Ruby and Rails Notes
Updating RubyGems:
$ sudo gem update --system
$ sudo gem install rubygems-update
$ sudo update_rubygems
Adding GitHub to RubyGems:
$ gem sources -a http://gems.github.com
Mysql gem under OSX:
Install:
$ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
Or, 64-bit:
$ sudo env ARCHFLAGS="-arch x86_64" gem install -V mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
Or, with MacPorts, mysql5 and mysql5-devel installed, this might work (or try 64-bit)
$ sudo gem install -V mysql -- --with-mysql-config=/opt/local/bin/mysql_config5
testing it:
$ irb
irb(main):001:0> require 'rubygems'
=> false
irb(main):002:0> require 'mysql_api'
=> true
irb(main):003:0> Mysql.get_client_info
=> "5.1.34"
irb(main):004:0> Mysql.get_client_version
=> 50134
Rest and Nested Routing
map.resources :categories do |categories|
categories.resources :products
end
You can continue to nest as much as you like. To access this you simple
use the following URL structure:
/categories
/categories/5
/categories/5/products
/categories/5/products/6
How to turn an Array or Object into a Hash:
a = [1,2,3,4]
Hash[*a]
=> { 1 => 2, 3 => 4 }
a = [1, 2, 3]
Hash[*a.collect { |v| [v, v*2]}.flatten]
=> { 1 => 1, 2 => 4, 3 => 6 }
class Event
attr_accessor :year, :month, :day
def date_hash
Hash[*[:year,:month,:day].collect{|k| [k=>self.send(k)] }.flatten]
end
end
c = Event.new({:year=>2007,:month=>7,:day=>4}).date_hash
=> {:year => 2007, :month => 7, :day => 4}
Using splat
# For parameters in a method definition:
def myfunc(*parms)
# params will be an array, or empty array if nothing sent
end
# for assignment
a, *b = "1,2,3,4".split(/,/) # a == [1], b == [2,3,4]
Hash initialization
# default to static values, instead of nil
h = Hash.new(0)
h[:test] # returns 0
# use a function!
h = Hash.new{|k,v| h[k] = "#{k} rocks"}
h["hashes"] # value is "hashes rock"
Profile your tests
rake log:clear; PROFILE=1 rake; ./script/profile_logs
Favorite Plugins
* query_trace
* acts_as_slugable
* betternestedset
* geokit
* paginating_find
* superdeploy
Script/Console Tricks
# mess w/ routes
>> include ActionController::UrlWriter
>> default_url_options[:host]="example.com"
>> comments_url("john","photo", 345)
=> 'http://example.com/comments/photo/345/john'
Using google analytics from ajax
/* js */
function trackPageHit(path){
pageTracker._trackPageview(path);
}
# in RJS
page.call 'trackPageHit', item_path(@item) # or any relative url
To install a specific version of a gem:
$ sudo gem install rails -v 1.2.6
To install a specific branch of a github plugin
$ script/plugin install http://github.com/activescaffold/active_scaffold.git -r rails-2.3
To generate a rails app using a specific installed gem version:
$ rails _1.2.6_ myapp
Note that this works with all gem binaries:
$ cap --version
Capistrano v2.2.0
$ cap _2.1.0_ --version
Capistrano v2.1.0
Finding Code
# open all your global helpers in all projects
$ find . -name "*helper.rb" | xargs mate
Regex Shortcut
Given a string text:
text = "file no 1234"
You can use a regex in several ways in Ruby. The “perl-ish” way is with ~= :
text =~ /file no (\d+)/
found = $1 # => "1234"
Another way is using match(), which returns MatchData objects:
text.match(/file no (\d+)/).matches[0] # => "1234"
However, if text could be nil
Still another way to extract matches is using the [] method and a regex:
text.to_s[/file no (\d+)/, 1] # => "1234"
Using to_s ensures that if text is nil you won’t get an exception.
But, perhaps the cleanest way is with scan:
test.scan(/file no (\d+)/) # => "1234"
Also, scan works with multiple matches where others do not:
text = "test here"
text.match(/(test|here)/)
=> #<MatchData "test" 1:"test"> # ONE match
text[/(test|here)] # => ONE match
text.scan(/(test|here)/) # => TWO matches: ["test", "here"]
Fix Readline, forward-delete and ~ (tilde) problem on OSX Snow Leopard:
# via http://snippets.aktagon.com/snippets/387-How-to-fix-irb-in-OSX-Snow-Leopard
$ sudo port install readline +universal
$ svn co http://svn.ruby-lang.org/repos/ruby/tags/v1_8_7_72/ext/readline/ readline
$ curl http://pastie.textmate.org/pastes/168767/download | patch readline/extconf.rb
$ cd readline
$ ruby extconf.rb
$ make
$ sudo make install
Rmagick
Version 2.13 is most stable, in my testing. It requires ImageMagick 6.
Install rmagick on OSX (be patient, the install can take 20m or more on a reasonably fast mac)
$ sudo port install ImageMagick @6.6.1-5_0+q16
$ sudo gem install rmagick -v 2.13.1
Install rmagick on debian:
$ sudo aptitude install libmagick++9-dev
$ sudo gem install rmagick -v 2.12.2
Test it (using a file called ‘mars.jpg’, can be any image):
$ irb
>> require 'rubygems'
=> false
>> require 'RMagick'
=> true
>> i=Magick::ImageList.new('mars.jpg')
=> [mars.jpg JPEG 2400x2400 2400x2400+0+0 DirectClass 8-bit 333kb]
>> i.scale!(0.1)
=> mars.jpg=>test.png JPEG 2400x2400=>240x240 240x240+0+0 DirectClass 8-bit 2982kb
>> i.write('test.png')
=> [mars.jpg=>test.png JPEG 2400x2400=>240x240 240x240+0+0 DirectClass 16-bit 155kb]
>> exit
$ identify test.png
test.png PNG 240x240 240x240+0+0 DirectClass 16-bit 155.221kb
Ruby Pitfall
A common Ruby pitfall…
>> i = b > a and b < c
false
>> i
true
fix:
>> i = (b > a and b < c)
false
>> i
false
Migrations
To make a BIGINT column:
t.integer :author_id, :limit => 8
Caching Tips
Via Rockstar Memcaching: Uses a generation number per user for content (their public pages).
class ApplicationController < ActionController::Base
before_filter :load_shop
after_filter :increment_generation
def load_shop
@current_shop = Shop.find_by_host(request.host)
end
def increment_generation
return if request.get?
@current_shop.increment_generation
end
end
class MyController < ApplicationController::Base
around_filter :cache
def index; end
def cache
if content = Rails.cache.get(cache_key)
render :text => content, :content_type => :html
else
yield
Rails.cache.set(cache_key, @response.body) if @response.code == 200
end
end
def cache_key
"#{request.url}/#{current_shop.generation}/#{cart.size}"
end
end
Mocha
In some cases under Rails 2.3 with Bundler, Mocha will not load right. This results is complaints about “Undefined method mock” etc. The solution is to have this in Gemfile:
gem 'mocha', '0.9.8', :require => false
The require => false bit prevents Mocha from loading until the test framework loads (which means require 'mocha' should be in test_helper.rb).
Internals
What launched rails?
ENV has:
>> ENV['_']
'script/server'
or
'/usr/bin/irb'
You can also check caller[-1] which will be the first program on the call stack. This might give you script/server:3 or it may be something less obvious like "/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/irb/workspace.rb:52".
If you want to know if the server is running, you can check for:
ENV['RACK_ENV'].present?
Bundler
$ bundle config build.mysql --with-mysql-config=/usr/local/mysql/bin/mysql_config
ActionView::MissingTemplate: Missing template
Add this to application.rb to handle errors with “smart” format detection.
Array Fun
Reduce:
>> (1..33).reduce([]){|r,v| r.tap{ r << v if v % 2 == 0} }
=> [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32]