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
Other Examples:

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
With ActiveRecord
class User < ActiveRecord::Base
  include HasConstant
  include HasConstant::Orm::ActiveRecord

  has_constant :industries, ['IT/Development', 'Marketing']
end

User.industries #=> [‘IT/Development’, ‘Marketing’]
Without a database
class User
  include HasConstant

  has_constant :job_roles, ['Junior Developer', 'Developer', 'CTO', 'Other']
end

User.job_roles #=> [‘Junior Developer’, ‘Developer’, ‘CTO’, ‘Other’]
With a lambda or a proc
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

Published on Mon, 26 Jul 2010 22:55
0 comments

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.

Published on Wed, 21 Jul 2010 19:39
0 comments

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.
Published on Sun, 04 Jul 2010 08:18
0 comments

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
Published on Sun, 04 Jul 2010 07:40
7 comments

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
Then in the application controller:
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
Now, by default all actions will require a subdomain for a valid site. On any actions where there is no need for a subdomain I can skip the site_required before filter, like in my static controller:
class StaticController < ApplicationController
  skip_before_filter :site_required, :only => [ :no_site_index  ]

  def no_site_index
  end
end
Published on Sat, 03 Jul 2010 14:38
1 comment

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) }
Published on Wed, 16 Jun 2010 08:08
0 comments

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...

Published on Sat, 12 Jun 2010 07:54
0 comments

RSS