Archive

Posts Tagged ‘rails’

rails current user

March 13th, 2010 6 comments

While playing with next app I thought a bit about so common current_user … so a lot of tutorials and descriptions uses this method in application controller.

After thinking a while I found other way to do this, and for me it looks a bit better, any thoughts on it ?

So get started with User model:

class User < ActiveRecord::Base
acts_as_authentic
cattr_reader :current
end
&#91;/sourcecode&#93;
Now in application controller add filter to set current user:
&#91;sourcecode lang="ruby"&#93;
class ApplicationController < ActionController::Base
before_filter :set_user
private
def set_user
current_user_session = UserSession.find
User.send :class_variable_set, :@@current, current_user_session > current_user_session.record
end
end

Now you can use whatever you want the same method to access current user:

User.current

Added 2010.03.14 :
Warning please do not use this method it is not thread safe – it is only good for single threaded applications.

Categories: Development Tags: , ,

rails acts_as_configurable file config

March 7th, 2010 1 comment

Working on my new app I thought it is necessary to get some data from configuration file, there was already plugin that had the necessary functionality – acts_as_configurable.

Unfortunately this plugin was not exactly what I was looking for It had possibility to write configuration in the class itself – I needed configuration in file.

So I did fork of the project, and now you have possibility to use rewritten version of the plugin which uses yaml file for storing configuration.

Here is example configuration file:

default:
  :first: 1st
Settings2:
  :first: 2nd
settings:
  :first: 3rd

With this configuration all the following definitions are valid:

class Settings1
  acts_as_configurable
end
class Settings2
  acts_as_configurable
end
class Settings30
  acts_as_configurable :for => 'settings'
end
class Settings31
  acts_as_configurable :for => :settings
end
class Settings4
  acts_as_configurable :for => Settings2
end
class Settings50
  acts_as_configurable 'conf'
end
class Settings51
  acts_as_configurable :conf
end
class Settings52
  acts_as_configurable :name=>'conf', :default=>'settings'
end
class Settings53
  acts_as_configurable :name=>:conf, :default=>'settings'
end
class Settings60
  acts_as_configurable :name=>'conf', :for => Settings1, :default=>'settings'
end
class Settings61
  acts_as_configurable :name=>'conf', :for => Settings1, :default=>:settings
end
class Settings7
  acts_as_configurable :name=>'conf', :for => Settings2, :default=>'settings'
end
class Settings8
  acts_as_configurable :name=>'conf1', :for => Settings1
  acts_as_configurable :name=>'conf2', :for => Settings2
end

and using following rspec code it will pass tests:

describe "ActsAsConfigurable" do
  it "should respond to 'configuration'" do
    Settings1.configuration.should_not be_nil
  end
  it "should contain default values" do
    Settings1 .configuration[:first].should eql '1st'
  end
  it "should contain values" do
    Settings2.configuration[:first].should eql "2nd"
  end
  it "should contain values for string" do
    Settings30.configuration[:first].should eql "3rd"
  end
  it "should contain values for symbol" do
    Settings31.configuration[:first].should eql "3rd"
  end
  it "should contain values for class" do
    Settings4.configuration[:first].should eql "2nd"
  end
  it "should contain values for object" do
    Settings2.new.configuration[:first].should eql "2nd"
  end
  it "should respond to 'conf' - passed as string" do
    Settings50 .conf[:first].should eql '1st'
  end
  it "should respond to 'conf' - passed as symbol" do
    Settings51 .conf[:first].should eql '1st'
  end
  it "should respond to 'conf' - passed as :name=>" do
    Settings52 .conf[:first].should eql '3rd'
  end
  it "should respond to 'conf' - passed as :name=>symbol" do
    Settings53 .conf[:first].should eql '3rd'
  end
  it "should work with other default - passed as string" do
    Settings60 .conf[:first].should eql '3rd'
  end
  it "should work with other default - passed as symbol" do
    Settings61 .conf[:first].should eql '3rd'
  end
  it "should work with other default but also exisitng for" do
    Settings7 .conf[:first].should eql '2nd'
  end
  it "should work with multiple definitions" do
    Settings8 .conf1[:first].should eql '1st'
    Settings8 .conf2[:first].should eql '2nd'
  end
end

Sources for the plugin are here http://github.com/mpapis/acts_as_configurable

Categories: Development Tags: , , ,

Cucumber multi-session

March 2nd, 2010 3 comments

This article is indirect translation of Cucumber – obsluga kilku sesji – polish version (from andrzejsliwa.com devblog).

Most of standard testing related tasks can be achieved in a simple way using the default steps from Cucumber. By assumption Cucumber is for functional testing, but it can also be used to implement integration test. This is especially useful when we want test interaction between users, when they should get on-line notifications – and this should be tested without login/logout functionality, like in shout box which was popular some time ago.

When using integration tests you could use block open_session:

def login(user)
  open_session do |sess|
    sess.extend(CustomDsl)
    u = users(user)
    sess.https!
    sess.post "/login", :username => u.username, :password => u.password
    assert_equal '/welcome', path
    sess.https!(false)
  end
end

But in the case of cucumber which is based on the steps it was necessary to find a solution that is suited to the form in which scenarios are created.

So I have asked my friend Andrzej Sliwa to write an example code, this is the example:

module ActionController
  module Integration
    class Session
      def switch_session_by_name(name)
        if @sessions_by_name.nil? 
          @sessions_by_name = { :default => @response.session.clone }
        end
        @sessions_by_name[name.to_sym] ||= @sessions_by_name[:default].clone
        @response.session = @sessions_by_name[name.to_sym]
      end
    end
  end
end

Given /^session name is "([^\"]*)"$/ do |name|
  switch_session_by_name(name)
end 

Use of this mechanism is trivially easy to perform the following step:

Given session name is "guest no 1 session"

g

In this case, a named session is created which is not dependent on others (including the default). Access to default session is called by using the default name:

Given session name is "default"

You can always get back to named session using the same name as before, the state of it will be persisted for you.

More details on Integration tests: http://guides.rubyonrails.org/testing.html#integration-testing-examples

Categories: Development Tags: , , ,

How to make rails migrations pretty

February 26th, 2010 5 comments

I have seen a lot of migrations, most of them was just doing what it had to do. But I found a nice way to make my migrations more pretty.

So I had an Article model, which had field :published of type :boolean , as comparing to other solutions, I found that it should be rather :publish_date of type date.
First I have created migration using “script/generate migration article_update” and got empty migration:

class UpdateArticles < ActiveRecord::Migration
  def self.up
  end
  def self.down
  end
end
&#91;/sourcecode&#93;
To be able to freely play with Fields we add our private implementation of article model, we change the fields and move data for them:
&#91;sourcecode language='ruby'&#93;
class UpdateArticles < ActiveRecord::Migration
  class Article < ActiveRecord::Base; end
  def self.up
    add_column :articles, :publish_date, :datetime, :null => true
    Article.reset_column_information
    Article.all.each { |article| article.update_attribute(:publish_date, :created_at) if article.publish }
    remove_column  :articles, :publish
  end
  def self.down
    add_column  :articles, :publish, :boolean
    Article.reset_column_information
    Article.all.each { |article| article.update_attribute(:publish, true) unless article.publish_date.nil? }
    remove_column :articles, :publish_date
  end
end

But this is not really what we wanted, if we have already a lot of data in Article tables then we will not know what is going on during update attribute action.
To make it display pretty we have to add say_with_time function:

class UpdateArticles < ActiveRecord::Migration
  class Article < ActiveRecord::Base; end

  def self.up
    add_column :articles, :publish_date, :datetime, :null => true
    say_with_time "Updating publish_date column" do
      Article.reset_column_information
      Article.all.each { |article| article.update_attribute(:publish_date, :created_at) if article.publish }
    end
    remove_column  :articles, :publish
  end

  def self.down
    add_column  :articles, :publish, :boolean
    say_with_time "Updating publish column" do
      Article.reset_column_information
      Article.all.each { |article| article.update_attribute(:publish, true) unless article.publish_date.nil? }
    end
    remove_column :articles, :publish_date
  end
end

And after running migration we will get he following result:

==  UpdateArticles: migrating =================================================
-- add_column(:articles, :publish_date, :datetime, {:null=>true})
-> 0.0007s
-- Updating publish_date column
-> 0.0034s
-- remove_column(:articles, :publish)
-> 0.0224s
==  UpdateArticles: migrated (0.0269s) ========================================

For me it was very small amount of data and the migration went fast, but if i would run it on production with thousands of records then it may take a bit more.

I know this exact code could be rewritten using SQL – and it would be almost always done in seconds, but even then It is nice to show progress, because person responsible for deployment might be confused seeing only blinking cursor.

Did You liked this post or maybe not, vote on it at dzone.

Categories: Development Tags: ,

complex associations in rails activerecord

January 30th, 2010 1 comment

During playing with CanCan from ryanb show in last railscasts I did planned some models and find me in quite strange situation – I knew the connection, but I could not easily get the result.

My models look something like:

class User < ActiveRecord::Base
  has_many :assigments
  has_many :groups, :through => :assigments
end
class Assigment < ActiveRecord::Base
  belongs_to :user
  belongs_to :group
end
class Group < ActiveRecord::Base
  has_many :assigments
  has_many :users, :through => :assigments
  has_many :responsibilities
  has_many :roles, :through => :responsibilities
end
class Responsibility < ActiveRecord::Base
  belongs_to :role
  belongs_to :group
end
class Role < ActiveRecord::Base
  has_many :responsibilities
  has_many :groups, :through => :responsibilities
end

So my first try was to get roles through groups manually:

User.first.groups.map{|g| 
  g.roles
}.flatten.map{|r|
  r.name.to_sym
}.uniq

But this solution is waste of resources, for system with a lot of possible roles it might be very inefficient, it takes first all groups of user and then ittereting through them gets all it’s roles. it will generate a lot of database queries.
So after some searching I got following code:

User.first.roles.map{|r|r.name.to_sym}.uniq

To make it working I used one quite nice feature of Active record – :finder_sql – so in user.rb I have added following line:

has_many :roles, :finder_sql => 'SELECT r.* FROM users u LEFT JOIN assigments a ON u.id=a.user_id LEFT JOIN responsibilities res ON a.group_id=res.group_id LEFT JOIN roles r ON res.role_id=r.id'

This makes it possible to get all the roles for a user just in one call, everything calculated on database side. Unfortunately there is one down side of this – the SQL code might be not portable to other databases, so use it only when You are sure You will stick to one database.
There is also other way around, it allows to get the same data in two sql calls, without using SQL queries, just on pure ActiveRecord usage:

Responsibility.find_all_by_group_id(
  u.assigments(:select=>'assigments.group_id').map{|a|a.group_id},
  :select=>'roles.name',
  :joins=>:role
).map{|r| r.name.to_sym }.uniq

There are many ways to archive the same goal, knowing them is only an part – knowing how they work, makes us aware how to chose the path.

on 2009-01-31 13:15 added:
Another way going out of the Group class:

Group.find(
  :all,
  :select=>'roles.name',
  :joins=>[:users,:roles],
  :conditions=>{:users=>{:id=>1}}
).map{|g|g.name.to_sym}.uniq

Did You liked this post or maybe not, vote on it at dzone.