HasConstant
A long time ago a guy I worked with (dancingtext.com) had an idea for a way of defining a constant in a ruby class, and only allowing an accessor to use those values. I liked the idea, and wrote a piece of code named has_constant. After using it in loads of projects I finally decided to take the time to make it into a gem for other people to improve on it (and there are a lot of improvements to be made). I've found this particularly useful for models in multi-lingual sites, where the values need to translate. It also works well with my locale_around hack (http://mattbeedle.com/2010/06/16/i18n-locale_around). Imagine for example, you need to display a user form where they choose their salutation, and that on the users proflie page, their salutation is displayed in the correct locale:
class User include Mongoid::Document include HasConstant include HasConstant::Orm::Mongoid field :salutation, :type => Integer has_constant :salutations, lambda { I18n.t(:salutations) } end
Then in the form, this will display the correct salutations whatever the locale:
form.select :salutation, User.salutations
And on the user profile page will always return the translated salutation
@user.salutation
With locale around
# Finding all users with salutation == 'Mr' when the locale is not en I18n.locale = :de I18n.locale_around(:en) do @users = User.salutation_is('Mr') end
class User < ActiveRecord::Base include HasConstant include HasConstant::Orm::ActiveRecord has_constant :industries, ['IT/Development', 'Marketing'] end User.industries #=> [‘IT/Development’, ‘Marketing’]
class User include HasConstant has_constant :job_roles, ['Junior Developer', 'Developer', 'CTO', 'Other'] end User.job_roles #=> [‘Junior Developer’, ‘Developer’, ‘CTO’, ‘Other’]
class User include HasConstant has_constant :salutation, lambda { I18n.t(:salutations) } has_constant :industries, Proc.new { I18n.t(:industries) } end #assuming the correct translations exist I18n.locale = :en User.salutations #=> [‘Mr’, ‘Mrs’] I18n.locale = :de User.salutations #=> [‘Herr’, ‘Frau’] u = User.new(:salutation => ‘Herr’) u.salutation #=> ‘Herr’ I18n.locale = :en u.salutation #=> ‘Mr’
It's on gemcutter now, so to install is simply:
gem install has_constant
Please fork this on github if you want to change it: http://github.com/mattbeedle/has_constant
Deploying Rails 3 Applications Dependent on Bundler 1.0.0.beta* to Heroku
If like me you need to deploy your Rails 3 application to Heroku, and can't because the latest Rails depends on a beta version of bundler you're probably getting a bit frustrated. Here's how to fix it:
First, you need to fork rails on github. Then change your bundler dependency in the rails.gemspec file:
s.add_dependency('bundler', '>= 0.9.26')
Push your changes back to github.
Then, in the project that you are trying to deploy to heroku, make sure that you are using your fork of rails in the Gemfile:
#gem 'rails', :git => 'git://github.com/rails/rails.git' gem 'rails', :git => 'git://github.com/mattbeedle/rails.git'
Now you should be able to deploy fine.
Useful Cucumber Steps #1
One cucumber step I find myself defining over and over again is:Given /^I execute "([^"]*)"$/ do |arg1| eval arg1 end
Now I can easily run additional commands in any test to check on different variables, etc like so:
Scenario: Some Scenario Given a user exists And I execute "throw User.first" ...
Obviously this is a bit of a simplistic example, but you get the idea.
Testing subdomains with rails 3, cucumber, pickle, cabybara and rack-test
In my old rails 2.3.x applications, in cucumber setting the subdomain was simply a matter of setting the host. In my white label app I used to have this step:Given /^I visit the subdomain for #{capture_model}$/ do |site| m = model!(site) host! "#{m.subdomain}.example.com" end
I tried this in rails 3 however, and although no errors where raised, the tests failed and the subdomain was never set. After a bit of googling, it seems the answer is to use Capybara.default_host:
Given /^I visit the subdomain for #{capture_model}$/ do |site| m = model!(site) Capybara.default_host = "#{m.subdomain}.example.com" Capybara.app_host = "http://#{m.subdomain}.example.com:9887" if Capybara.current_driver == :culerity end
Now though, you may find that all of your tests are running with that subdomain. To fix this, just use a Before block in your features/support/env.rb:
Before do Capybara.default_host = 'example.com' end
Rails3 routing and subdomains
I've been playing around with a white labeling site recently where different whitelabelled versions are hosted on subdomains. I needed to setup an index page for when there is no subdomain specified, and an index page for when a subdomain is specified. After playing around with rails3 routing and subdomain-fu for a while I finally got it working with this:class SubDomain def self.matches?( request ) if Site.first(:conditions => { :subdomain => /#{request.subdomain}/i }) true else false end end end class NoSubDomain def self.matches?( request ) if request.subdomain == '' || Site.first(:conditions => { :subdomain => /#{request.subdomain}/i }).nil? true else false end end end MySite::Application.routes.draw do devise_for :publishers, :users constraints(NoSubDomain) do root :to => 'static#no_site_index' end resources :users resources :publishers constraints(SubDomain) do root :to => 'static#index' end end
class ApplicationController < ActionController::Base protect_from_forgery layout 'application' before_filter :site_required def current_site @current_site ||= Site.first(:conditions => { :subdomain => /request.subdomains/i }) if request.subdomain end protected def site_required if current_site.blank? redirect_to root_url(nil, :subdomain => nil) return false end end # http://bcardarella.com/post/716951242/custom-subdomains-in-rails-3 def url_for(options = nil) case options when Hash if subdomain = options.delete(:subdomain) if request.subdomain.empty? options[:host] = "#{subdomain}.#{request.host_with_port}" else options[:host] = request.host_with_port.sub(request.subdomain, subdomain) end end end super end end
class StaticController < ApplicationController skip_before_filter :site_required, :only => [ :no_site_index ] def no_site_index end end
I18n.locale_around
In a few of my recent rails applications I've found myself needing to run just certain code in a view in a particular locale, so I ended up monkey patching I18n module:
module I18n class << self def locale_around( locale, &block ) begin original_locale = I18n.locale I18n.locale = locale return block.call ensure I18n.locale = original_locale end end end
Then in the view (or anywhere you want really):
I18n.locale_around(:en) { t(:something_in_en) }
rvm ruby 1.9.2-head, and 1.9.1-head bison problems
If you are trying to install ruby 1.9.1-head or 1.9.2-head with rvm and keep getting the following error:
Installing Ruby from source to: /home/matt/.rvm/rubies/ruby-1.9.2-head
Updating ruby from http://svn.ruby-lang.org/repos/ruby/branches/ruby_1_9_2
Configuring ruby-1.9.2-head, this may take a while depending on your cpu(s)...
Compiling ruby-1.9.2-head, this may take a while, depending on your cpu(s)...
*** glibc detected *** bison: double free or corruption (!prev): 0x0000000001562760 ***
Then like me, you managed to accidentally install bison++ package instead of bison. Easy fix.
sudo apt-get install bison
then try again and all should work fine...