Just some notes after reading “Eloquent Ruby” chapter one.

indent

use two spaces per indent

comments

There are good reasons for adding comments to your code, the best being to explain how to use your software masterpiece. These kinds of “how to” comments should focus on exactly that: how to use the thing. Don’t explain why you wrote it, the algorithm that it uses, or how you got it to run faster than fast. Just tell me how to use the thing and remember that examples are always welcome.

Sometimes it’s also wise to include a “how it works” explanation of particularly complicated bits of code. Again, keep this kind of explanation separate from the “how to”.

The occasional in-line comment can also help.

Remember, good code is like a good joke: It needs no explanation.

Camels for Classes, Snakes Everywhere Else

  • Almost everything in this context means methods, arguments, and variables, including instance variables use snake case.
  • Class names are camel case.
  • Constants use all uppercase, punctuated by underscores.

Parenthess

both in method definitions and calls surround parenthess.

don’t surround parenthess:

  • conditions in control statements
  • defining or calling a method with no parameters
  • puts, instance_of?, and your right feeling

Look set.rb in your ruby lib path for more ruby conventions.

Naming

  • end the name of a method that answers a yes/no or true/false question with a question mark
  • reserve ! to adorn the names of methods that do something unexpected, or perhaps a bit dangerous

My vpn sp have twelve ip, so I want to choose the best ip.

#!/usr/bin/env ruby
#coding: utf-8

ping_count = 10

servers = (1..12).to_a.map { |el| "us#{'%03d' % el}.example.com" }

results = []

servers.each_with_index do |el, i|
  r = `ping -q -c #{ping_count} #{el}`
  puts "processing: #{'%2.2f%' % ((i+1)/12.0*100)}" if i < 11

  #The ping utility returns an exit status of zero 
  #if at least one response was heard from the specified host;
  #a status of two if the transmission was successful but no responses were received;
  packet_loss = r.scan(/d+.d+(?=%)/)[0]
  avg = r.scan(/d+.d*(?=/)/)[1]
  results << [el, [packet_loss.to_f, avg.to_f]] if $?.exitstatus == 0
end

puts "finished!"

puts results.min { |x, y| x[1][0] <=> y[1][0] and x[1][1] <=> y[1][1] }

Beware of RSpec’s before :all

Posted: March 14, 2011 in Ruby
Tags: , ,

When you add data in an RSpec before :all, it’s not inside the transactions RSpec places around each test. rake spec clears the database before running all the specs, but the data created in before :all will hang around after it runs. This introduces an order dependency between specs. A spec that assumes the database (or one table) is empty will run fine if it runs before the before :all, but can fail if it runs after. Your specs can start failing just because you reorganized them, which changed their run order.

Tags: Database schemas

Posted: March 14, 2011 in Architecture
Tags:

http://www.pui.ch/phred/archives/2005/04/tags-database-schemas.html

config.use_transactional_fixtures = true

Posted: March 13, 2011 in Rails
Tags:

if you set config.use_transactional_fixtures = false and you are using ActiveRecord with rspec, you will find that any operates of test database can not rollback.

So, make sure config.use_transactional_fixtures = true if you using ActiveRecord

http://stackoverflow.com/questions/3497135/why-isnt-factory-girl-operating-transactionally-for-me-rows-remain-in-databas/3498959#3498959

reload routes with spork each run

Posted: March 13, 2011 in Rails
Tags: ,

Default(spork -d) not reload routes. So add routes to each_run block.

Spork.each_run do
  # This code will be run each time you run your specs.
  require File.expand_path("../../config/routes", __FILE__)
end

Extending Rails with Plugins

Posted: March 12, 2011 in Uncategorized

Just some notes after reading “The Rails3 Way” chapter nineteen.

init your plugin

==> rails g plugin my_plugin
      create  vendor/plugins/my_plugin
      create  vendor/plugins/my_plugin/MIT-LICENSE
      create  vendor/plugins/my_plugin/README
      create  vendor/plugins/my_plugin/Rakefile
      create  vendor/plugins/my_plugin/init.rb
      create  vendor/plugins/my_plugin/install.rb
      create  vendor/plugins/my_plugin/uninstall.rb
      create  vendor/plugins/my_plugin/lib
      create  vendor/plugins/my_plugin/lib/my_plugin.rb
       error  rspec [not found]

bug: need rspec:plugin generator

Code that’s added to init.rb is run at startup. (That’s any sort of Rails commands, including server, console, and runner.) Most plugins have their require statements in init.rb.

The lib directory of your plugin is added to Ruby’s load path before init.rb is run.

In addition to opening existing classes to add or modify behavior, there are at least three other ways used by plugins to extend Rails functionality:

  • Mixins, which describes inclusion of modules into existing classes
  • Dynamic extension through Ruby’s callbacks and hooks such as method_missing, const_missing, and included
  • Dynamic extension through Ruby’s callbacks and hooks such as method_missing, const_missing, and included Dynamic extension using runtime evaluation with methods such as eval, class_eval, and instance_eval

Most plugins follow a pattern used internally in Rails and split their methods into two modules, one each for class and instance methods.

vendor/plugins/my_plugin/lib/my_plugin.rb

# MyPlugin
#class ActiveRecord::Base 
  #def self.[](id)
    #find(id)
  #end
#end

module MyPlugin
  extend ActiveSupport::Concern

  #extending ActiveSupport::Concern automatically does the following 
  #def self.included(base)
  #base.extend(ClassMethods)
  #base.send(:include, InstanceMethods)
  #end

  module ClassMethods 

    def [](id)
      find(id) 
    end

  end

  module InstanceMethods 

    def to_param
      has_name? ? "#{id}-#{name.gsub(/[^a-z0-9]+/i, '-')}" : super
    end

    private

    def has_name?
      respond_to?(:name) and persisted?
    end

  end

end

vendor/plugins/my_plugin/init.rb

# Include hook code here
#puts "Current Rails version: #{Rails::VERSION::STRING}"
require 'my_plugin'

ActiveRecord::Base.send(:include, MyPlugin)
#ActiveRecord::Base.class_eval do 
  #include MyPlugin
#end

My previous entry Ruby’s Object Model maybe useful if you want to become a master of Ruby.

vendor/plugins/my_plugin/install.rb

  • Copying asset files (HTML, CSS, and so on) into the public directory
  • Checking for the existence of dependencies (for example, RMagick)
  • Installing other requisite plugins
  • Displaying documentation

The rails plugin remove command checks for the presence of a file called uninstall.rb when removing a plugin. I

If as a plugin writer you are concerned about making sure that your install and/or uninstall scripts are actually executed, it’s probably worthwile to stress the point in your announcements to the community and within the plugin documentation itself, such as the README file.

For javascript, you will simply need to register a javascript expansion using ActionView::Helpers::AssetTagHelper. In the following example we add two javascript files from our plugin under the namespace my_plugin. Note that the source files in the plugin must also reside in public/javascripts.

ActionView::Helpers::AssetTagHelper.register_javascript_expansion :my_plugin => ["core", "ext"]

This javascript can then be loaded into the application through the standard javascript include tag, and passing the name defined by the plugin.

javascript_include_tag :my_plugin

Railties

Railties are classes that extend from Rails::Railtie and provide hooks into Rails ini- tialization for add-on libraries. This is extremely useful for gems which want to seemlessly integrate with Rails.

Railties provide hooks for libraries to add the following functionality:

  • Creating initializers
  • Providing Rake tasks
  • Adding generators
  • Registering event subscribers (for logging)

To create a Railtie, create a class called Railtie in your project’s namespace that inherits from Rails::Railtie. Make sure you require ‘rails’ and your own gem in the file as well.

require 'my_gem' 
require 'rails'
module MyGem
  class Railtie < Rails::Railtie

    initializer "setup" do 
      # Some initialization code here, like setting up a 
      # database connection.
    end

    initializer "verify setup" do
      config.after_initialize do
        # Do some verification on the setup.
      end
    end

    rake_tasks do
      load 'my_gem/railties/tasks.rake'
    end

    generators do 
      require 'my_gem/rails/generators/my_generator'
    end

    log_subscriber :my_gem, MyGem::Railtie::Subscriber.new

  end
end

More about Railtie

Caching and Performance

Posted: March 11, 2011 in Rails
Tags: ,

Just some notes after reading “The Rails3 Way” chapter seventeen and Caching with Rails.

There are three types of view caching in Rails:

  • Page caching: The output of an entire controller action is cached to disk, with no further involvement by the Rails dispatcher.
  • Action caching: The output of an entire controller action is cached to disk, but the Rails dispatcher is still involved in subsequent requests, and controller filters are executed.
  • Fragment caching: Arbitrary bits and pieces of your page’s output can be cached to disk to save the time of having to render them in the future.

By default, the page cache directory is set to Rails.public_path (which is usually set to the public folder) and this can be configured by changing the configuration setting config.action_controller.page_cache_directory. Changing the default from public helps avoid naming conflicts, since you may want to put other static html in public, but changing this will require web server reconfiguration to let the web server know where to serve the cached files from, other than you will make rails rewriting the cache (Means don’t have any cache!).

The Page Caching mechanism will automatically add a .html extension to requests for pages that do not have an extension to make it easy for the webserver to find those pages and this can be configured by changing the configuration setting config.action_controller.page_cache_extension.

Note: Page caching ignores all parameters. For example /products?page=1 will be written out to the filesystem as products.html with no reference to the page parameter. Thus, if someone requests /products?page=2 later, they will get the cached first page. Be careful when page caching GET parameters in the URL!

Action Caching works like Page Caching except for the fact that the incoming web request does go from the webserver to the Rails stack and Action Pack so that before filters can be run on it before the cache is served. This allows authentication and other restriction to be run while still serving the result of the output from a cached copy.

Fragment Caching allows a fragment of view logic to be wrapped in a cache block and served out of the cache store when the next request comes in.

Action Cache and Fragment Cache default path: your_project/tmp/cache (ActiveSupport::Cache::FileStore)

ActionController::Caching::Actions

Cache sweeping is a mechanism which allows you to get around having a ton of expire_{page,action,fragment} calls in your code. It does this by moving all the work required to expire cached content into an ActionController::Caching::Sweeper subclass.

It’s important to note that SQL query caches are created at the start of an action and destroyed at the end of that action and thus persist only for the duration of the action.

You can access these cache stores at a low level for storing queries and other objects.

Rails.cache.read("city")   # => nil
Rails.cache.write("city", "Duckburgh")
Rails.cache.read("city")   # => "Duckburgh"

Conditional GET support

class ProductsController < ApplicationController

  def show
    @product = Product.find(params[:id])

    # If the request is stale according to the given timestamp and etag value
    # (i.e. it needs to be processed again) then execute this block
    if stale?(:last_modified => @product.updated_at.utc, :etag => @product)
      respond_to do |wants|
        # ... normal response processing
      end
    end

    # If the request is fresh (i.e. it's not modified) then you don't need to do
    # anything. The default render checks for this using the parameters
    # used in the previous call to stale? and will automatically send a
    # :not_modified.  So that's it, you're done.
end

class ProductsController < ApplicationController

  # This will automatically send back a :not_modified if the request is fresh,
  # and will render the default template (product.*) if it's stale.

  def show
    @product = Product.find(params[:id])
    fresh_when :last_modified => @product.published_at.utc, :etag => @product
  end
end

Action Mailer

Action Mailer Configuration for sendmail

config.action_mailer.delivery_method = :sendmail
# Defaults to:
# config.action_mailer.sendmail_settings = {
#   :location => '/usr/sbin/sendmail',
#   :arguments => '-i -t'
# }
config.action_mailer.perform_deliveries = true
config.action_mailer.raise_delivery_errors = true

Action Mailer Configuration for GMail

config.action_mailer.delivery_method = :smtp
config.action_mailer.smtp_settings = {
  :address              => "smtp.gmail.com",
  :port                 => 587,
  :domain               => 'baci.lindsaar.net',
  :user_name            => '<username>',
  :password             => '<password>',
  :authentication       => 'plain',
  :enable_starttls_auto => true  }

Model User should devise :confirmable

class User < ActiveRecord::Base
  devise :database_authenticatable, :confirmable, :lockable, :recoverable,
         :rememberable, :registerable, :trackable, :timeoutable, :validatable,
         :token_authenticatable

  attr_accessible :email, :password, :password_confirmation
end

task :setup => ['db:drop', 'db:create', 'db:migrate', 'environment'] do
  user = User.create! do |u|
    u.email = 'user@test.com'
    u.password = 'user123'
    u.password_confirmation = 'user123'
    u.ensure_authentication_token!
  end
  user.confirm!
  puts 'New user created!'
  puts 'Email   : ' << user.email
  puts 'Password: ' << user.password

  admin = Admin.create! do |u|
    u.email = 'admin@test.com'
    u.password = 'admin123'
    u.password_confirmation = 'admin123'
  end
  #admin.confirm!
  puts 'New admin created!'
  puts 'Email   : ' << admin.email
  puts 'Password: ' << admin.password
end

http://stackoverflow.com/questions/3551296/feasability-of-running-mongodb-on-…

MongoDB uses a mapped memory storage engine, which means it relies on the OS system cache to keep frequently used data in memory.