codedecoder

breaking into the unknown…


5 Comments

ruby block : difference between yield and &block

block, proc and lambda , known as closure in computer terminology is a very important aspect of ruby. Here, I will try to explain block, its usage and implementation. We will write our code in ruby_block_demo.rb file and execute it from irb.

Let us create a class BlockDemo and define a welcome method, which will welcome the peoples passed to it as argument.

class BlockDemo

  def welcome(*person) # spat parameter is used here, 
  you can pass argument separeted by comma, which get converted into array
    person.each do |p|
      puts "Hi #{p}...how are you ?"
    end
  end

end

Let us call this method from irb on terminal

1.9.3-p194 :005 > load “/home/arun/ruby_block_demo.rb” # give absolute path of your ruby_block_demo.rb. read here about load and require if you get “LoadError :  can not load such file”
=> true
1.9.3-p194 :006 > demo_object=BlockDemo.new
=> #
1.9.3-p194 :007 > demo_object.welcome(“arun”, “kapil”)
Hi arun…how are you ?
Hi kapil…how are you ?

Here , we have incorporated the welcome message in the welcome method itself. What if we want to greet people from punjab in some other way and from chennai in some other way or so on. We can decouple the welcome message from welcome method using block concept.

A block can we wrapped in { } for single line of code or within do end for multiple line of code. The block is passed just after the method call with all its argument. Let us pass a block of code to welcome method from irb.

=> block with single line of code, so wrap it in { }

1.9.3-p194 :008 > demo_object.welcome(“arun”, “kapil”){“This is welcome message from block”}
Hi arun…how are you ?
Hi kapil…how are you ?

=> block with multiple line of code, so wrap it in do end.

You can use do end for single line of code also but { } look good for that

1.9.3-p194 :009 > demo_object.welcome(“arun”, “kapil”) do
1.9.3-p194 :010 >     puts “this is first welcome message from the block”
1.9.3-p194 :011?>    puts “this is second welcome message from the block”
1.9.3-p194 :012?>   end
Hi arun…how are you ?
Hi kapil…how are you ?

So you can see that, passing block doesn’t made any difference to the output of welcome method. This is because, the receiver method must implement the block through yield or &block keywords. So Let us modify the ruby_block_demo.rb code to implement the passed block.

=> block execution through yield.

class BlockDemo

  def welcome(*person)
    person.each do |p|
      puts "Hi #{p}...how are you ?"
      yield # this will execute the block passed to it
    end
  end

end

=> block execution through &block .

class BlockDemo

  def welcome(*person, &message)
    person.each do |p|
      puts "Hi #{p}...how are you ?"
      message.call() # this will execute the block passed to it
    end
  end

end

NOTE :
=>It is the & , which make the difference, the name can be anything like &message as above or &block or &xyz etc.
=>& will make message a Proc object
=>The & Implementation take more memory and time as compared to yield . I will explain the difference between the two and when to use which one when need arises. untill need arises we will use yield.

You can write either of above code in ruby_block_demo.rb and check the output in irb.

1.9.3-p194 :018 > load “/home/arun/ruby_block_demo.rb” # reload the file as change made to it.
=> true
1.9.3-p194 :019 > demo_object=BlockDemo.new
=> #
1.9.3-p194 :020 > demo_object.welcome(“arun”, “kapil”) do
1.9.3-p194 :021 > puts “this is first welcome message from the block”
1.9.3-p194 :022?> puts “this is second welcome message from the block”
1.9.3-p194 :023?> end
Hi arun…how are you ?
this is first welcome message from the block
this is second welcome message from the block
Hi kapil…how are you ?
this is first welcome message from the block
this is second welcome message from the block

So, you can see that
=> lines within do end block get executed
=> the block will get executed every time it is called. here we have put it in loop so it get executed twice.
=> So now welcome message is decoupled and you can pass different message when you need it.see another call below

1.9.3-p194 :024 > demo_object.welcome(“arun”, “kapil”) do
1.9.3-p194 :025 > puts “I welcome you in punjabi”
1.9.3-p194 :026?> puts “sasriya kaal…. 🙂 “
1.9.3-p194 :027?> end
Hi arun…how are you ?
I welcome you in punjabi
sasriya kaal…. 🙂
Hi kapil…how are you ?
I welcome you in punjabi
sasriya kaal…. 🙂

So, you can see we have now welcomed in Punjabi. This demonstrate the use of decoupling code with block.

=> Now what happen, if block is not passed. Let us see

1.9.3-p194 :028 > demo_object.welcome(“arun”, “kapil”) # calling the welcome method without block
Hi arun…how are you ?
LocalJumpError: no block given (yield)
from /home/arun/Documents/books/learn_ruby/ruby_block_demo.rb:6:in `block in welcome’
from /home/arun/Documents/books/learn_ruby/ruby_block_demo.rb:4:in `each’
from /home/arun/Documents/books/learn_ruby/ruby_block_demo.rb:4:in `welcome’
from (irb):28
from /home/arun/.rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `<main>’

O.K so, it will complain if block is not passed. Let us correct the code to make the block optional i,e it should not complain , if the user forget to pass it.

It can be handled by using block_given? condition. See below the modified code of ruby_block_demo.rb

=> block made optional.

class BlockDemo

  def welcome(*person)
    person.each do |p|
      puts "Hi #{p}...how are you ?"
      yield if block_given? # yield will be called only if block is passed.
    end
  end

end

Again call the method without block and see what happen

1.9.3-p194 :029 > load “/home/arun/ruby_block_demo.rb”
=> true
1.9.3-p194 :030 > demo_object=BlockDemo.new
=> #
1.9.3-p194 :031 > demo_object.welcome(“arun”, “kapil”)
Hi arun…how are you ?
Hi kapil…how are you ?

So, It is not complaining if we forget to pass the block.
Now, what if we want to pass argument to the block. well you can do it with yield considering it like any other method.

=> passing argument to block.

def welcome(*person)
   person.each do |p|
   yield(p,"Tamil") if block_given? # person name and his state passed as argument.
  end
end

Here we have passed two argument to yield. When you pass block it should define two parameter under the pipe ||
See on irb how to call the block now.

1.9.3-p194 :043 > load “/home/arun/ruby_block_demo.rb”
=> true
1.9.3-p194 :044 > demo_object=BlockDemo.new
=> #<BlockDemo:0xa414b3c>
1.9.3-p194 :045 > demo_object.welcome(“arun”, “kapil”) do |person, region|
1.9.3-p194 :046 >       puts “Hi #{person}…..how are you”
1.9.3-p194 :047?>      puts “I welcome you according to tradition of #{region} “
1.9.3-p194 :048?>   end
Hi arun…..how are you
I welcome you according to tradition of Tamil
Hi kapil…..how are you
I welcome you according to tradition of Tamil

So you can take it like the variables within || i,e |person, region| is the parameter signature of the method yield. when calling yield you should pass two argument.

DIFFERENCE BETWEEN yield and &block :

=> When to use yield over &block

Yield have high performance over &block in term of time and memory usage. This is illustrated here on stackoverflow.

NOTE : use yield unless you explicitly need &block due to any of the below reasons.

=> When to use &block over yield

Case 1: You need to manipulate the block before calling it.

Here, you need to understand that, yield is not a class, so you can’t create an object out of it. But &block internally represent an instance of Proc class, so you can apply all the available method of proc class to it.

yield just execute the block of code passed to it, it do not belong to any class. Let demonstrate it

change the code.

class BlockDemo

  def welcome(*person)
    person.each do |p|
      yield(p, "Tamil") if block_given?
    end
    puts yield.class
  end

end

Now rerun the code in irb

1.9.3p194 :017 > load “/home/arun/ruby_block_demo.rb”
=> true
1.9.3p194 :018 > block_demo = BlockDemo.new
=> #
1.9.3p194 :019 > demo_object.welcome(“arun”, “kapil”) do |person, reagion|
1.9.3p194 :020 > puts “we will welocme you later…let us see yield class”
1.9.3p194 :021?> end
we will welocme you later…let us see yield class
we will welocme you later…let us see yield class
we will welocme you later…let us see yield class
NilClass

So ,you can see that it printed NilClass.

Let us change the code in terms of &block.

class BlockDemo

  def welcome(*person, &message)
    person.each do |p|
      message.call(p, "Tamil") if block_given?
    end
    puts message.class
  end

end

rerun it again

1.9.3p194 :022 > load “/home/arun/ruby_block_demo.rb”
=> true
1.9.3p194 :023 > block_demo = BlockDemo.new
=> #
1.9.3p194 :024 > demo_object.welcome(“arun”, “kapil”) do |person, reagion|
1.9.3p194 :025 > puts “we will welocme you later…let us see yield class”
1.9.3p194 :026?> end
we will welocme you later…let us see yield class
we will welocme you later…let us see yield class
Proc

So you can see that &block is basically internally represent an object of Proc class. One another difference you can see that, here block is executed when call method is applied to it, so only two message is output. But in case of yield it is executed every time yield is encountered, so when you did yield.class, it get executed and printed the message one more time.

Case 2: You want to pass argument to yield depending on parameter needed by the block.

This case is just an illustration of benefit of &block being an object. Since you have all the method of Proc class available to your block, you can use them. We will take example of arity method

with arity you can decide, how many argument is needed by passed block, so you can pass the argument accordingly or do some other stuff before calling the block.

Let change the code as below :

class BlockDemo
  def welcome(*person, &message)
    no_of_argument = message.arity
    case no_of_argument
    when 1
      puts "I am sad only one parameter is passed"
      person.each do |p|
        message.call(p, "Tamil") if block_given?
      end
    when 2
      puts "wao..all the required parameter are passed"
      person.each do |p|
        message.call(p, "Tamil") if block_given?
      end
    else
      raise "invalid no of parameter"
    end
  end
end

Let us run the above code in irb with different number of argument.

1.9.3p194 :027 > load “/home/arun/ruby_block_demo.rb”
=> true
1.9.3p194 :028 > block_demo = BlockDemo.new
1.9.3p194 :031 > demo_object.welcome(“arun”, “kapil”) do |person, reagion|
1.9.3p194 :032 > puts “Hi #{person}…how are you”
1.9.3p194 :033?> end
wao..all the required parameter are passed
Hi arun…how are you
Hi kapil…how are you

1.9.3p194 :034 > demo_object.welcome(“arun”, “kapil”) do |person|
1.9.3p194 :035 > puts “Hi #{person}…how are you”
1.9.3p194 :036?> end
I am sad only one parameter is passed
Hi arun…how are you
Hi kapil…how are you

1.9.3p194 :063 > demo_object.welcome(“arun”, “kapil”) do
1.9.3p194 :064 > puts “Hi #{person}…how are you”
1.9.3p194 :065?> end
RuntimeError: invalid no of parameter
from /home/arun/Documents/books/learn_ruby/ruby_block_demo.rb:17:in `welcome’
from (irb):63
from /home/arun/.rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `’

So you can see that we have handled different block in different way depending on there parameters.

Case 3: You want to pass the block to some other method

This is case, where yield will not work and you have to use & block.
NOTE : yield cannot pass block to an other method

Let us modify our above code so that, the block pass only welcome message (not use puts as above while running in irb)and we will make another method to print the message.

class BlockDemo

  def welcome(*person, &message)
    no_of_argument = message.arity
    case no_of_argument
    when 1
      puts "I am sad only one parameter is passed"
      person.each do |p|
        print_message(&message)
      end
    when 2
      puts "wao..all the required parameter are passed"
      person.each do |p|
        print_message(&message)
      end
    else
      raise "ivalid no of parameter"
    end
  end
end

def print_message
  puts "welcome message will be printed by this method"
  puts yield("arun") if block_given?
end

Now, run this code on irb

1.9.3p194 :076 > load “/home/arun/ruby_block_demo.rb”
=> true
1.9.3p194 :077 > block_demo = BlockDemo.new
=> #
1.9.3p194 :078 > demo_object.welcome(“arun”) do |person|
1.9.3p194 :079 > puts “Hi #{person}…how are you”
1.9.3p194 :080?> end
I am sad only one parameter is passed
welcome message will be printed by this method
Hi arun…how are you

So here, block passed to first method, do not execute it itself but pass it to a second method. In such cases, you always need to use &block .

REFERENCES:

http://innig.net/software/ruby/closures-in-ruby

Advertisements


Leave a comment

passenger.conf file location on nginx or apache

Recently, we have observed a strange behaviour on the staging server(nginx + passenger) , we have created for testing. It is found that, when a set of 10 or 15 users try to access the home page one after other, the first user took relatively more time (50 seconds against the average of 10 second) . We get clue about the problem here on stackoverflow .

PROBLEM : Passenger spawning new Rails instance for some request causing high load time for the request  .

The problem is that, our site remain idle for long time, since we are using it only for testing. The default passenger configuration for passenger_pool_idle_time allow idle time of 300 seconds, after which it shut down itself. It restart rails instance again ,when new request come, so basically it loading all the configuration, database.yml, initialization etc, which results in more loading time for the user visiting first. Since, Rails instance got restarted the subsequent user visiting the site witness normal time of 10 seconds.

SOLUTION :  set passenger_pool_idle_time to 0

It will make passenger to never shut down it self even if the server is idle for long time. It cost you memory as inactive process never get killed.

So, if you are aware of traffic on your site, you can adjust accordingly. Say, one user visit your site every 10 minute. so you can set the value to 2×10 i,e 20 minutes.

passenger_pool_idle_time 1200 #the time should be in integre

You can get complete list of passenger configuration here .  Now the question is where we can write all these available options for passenger configuration. The Answer is that : All passenger configuration goes in nginx.conf or apache.conf file itself .

depending on the way, you have installed apache or nginx, there respective conf file may be present at below location:

/usr/local/nginx/conf/nginx.conf

or

/etc/nginx/conf/nginx.conf

or

/opt/nginx/conf/nginx.conf

path will remain same for apache.conf also . So we have hold on the respective conf file. Now you can add passenger related configuration in below two ways.

METHOD 1: Add the passenger configuration in nginx.conf file directly.

You can add passenger configuration in http , server or location block. I have added it to http block

worker_processes  4;
events {
    worker_connections  1024;
}

worker_rlimit_nofile    1200;

http {
      passenger_root /usr/local/rvm/gems/ruby-1.9.3-p125@new_customer_portal/gems/passenger-3.0.17;
      passenger_ruby /usr/local/rvm/wrappers/ruby-1.9.3-p125@new_customer_portal/ruby;
      passenger_pool_idle_time 0;

      include	  mime.types;
      default_type  application/octet-stream;
      underscores_in_headers on;

      sendfile        on;
      keepalive_timeout  65;

     server {
	          listen       80;        
            server_name  barcelona-portal.thirdpillar.com;
            root "/var/www/sunpower.com/current/public";
            passenger_enabled on;
            rails_env staging;
            error_page   500 502 503 504  /50x.html;

            location = /50x.html {
                   root   html;
             }

          }
     index  index.html index.htm;
}

The orange marked part is passenger configuration added to nginx.conf . But as your passenger configuration increase it become cumbersome to maintain. It is better to write all passenger configuration in passenger.conf file and include it in nginx.conf file. we will do it in next Method.

METHOD 2: Add passenger configuration in passenger.conf file and include it in nginx.conf file .

create passenger.conf file in the same folder where nginx.conf file present i,e say /etc/nginx/conf (you may create it anywhere, but it is a convention) folder and add passenger related configuration to it.

      passenger_root /usr/local/rvm/gems/ruby-1.9.3-p125@new_customer_portal/gems/passenger-3.0.17;
      passenger_ruby /usr/local/rvm/wrappers/ruby-1.9.3-p125@new_customer_portal/ruby;
      passenger_pool_idle_time 0;

Now include this file in nginx.conf file. So the configuration in Method 1, will now look like

worker_processes  4;
events {
    worker_connections  1024;
}

worker_rlimit_nofile    1200;

http {
      include /etc/nginx/conf/passenger.conf; # it load passenger.conf file

      include	  mime.types;
      default_type  application/octet-stream;
      underscores_in_headers on;

      sendfile        on;
      keepalive_timeout  65;

     server {
	          listen       80;        
            server_name  barcelona-portal.thirdpillar.com;
            root "/var/www/sunpower.com/current/public";
            passenger_enabled on;
            rails_env staging;
            error_page   500 502 503 504  /50x.html;

            location = /50x.html {
                   root   html;
             }

          }
     index  index.html index.htm;
}

REFERENCES:

https://www.phusionpassenger.com/documentation_and_support#documentation # passenger documentation

http://www.modrails.com/documentation/Users%20guide%20Nginx.html #passenger with nginx

http://www.modrails.com/documentation/Users%20guide%20Apache.html # passenger with apache


5 Comments

require and load in ruby : LoadError cannot load such file

require is used to call one file into another file. The detail about require is available here and about load is  available here on ruby doc. There purpose is same, but are different in approach. I will explain the concept in terms of require, which hold true for load also. Towards the end I will list the difference between the two.

Let I have a ruby file myprogram.rb in  Document/projects folder. I need to call the method contained in it from irb. We know that to access any method within a file or library we need to require or load that file/library in the current scope (irb in this case). So, I will try to require myprogram.rb file.

$ irb
1.9.3-p194 :001 > require "myprogram"
LoadError: cannot load such file -- myprogram

Its not working, you may think that we have not provided the .rb extension, that’s why it is not loading. Let us try again.

1.9.3-p194 :001 > require "myprogram.rb"
LoadError: cannot load such file -- myprogram

Still the same error, So absence of extension is not a problem . Basically ruby is smart enough to add the extension by own. If the filename has the extension “.rb”, it is loaded as a source file; if the extension is “.so”, “.o”, or “.dll”, or the default shared library extension on the current platform, Ruby loads the shared library as a Ruby extension. Otherwise, Ruby tries adding “.rb”, “.so”, and so on to the name until found. If the file named cannot be found, a LoadError will be raised.

But, why it is happening. We have seen this syntax working many time in case of gems and files placed in lib folder of a Rails app.

The reason is that ruby directly search for the file to require in all the directory listed in $LOAD_PATH. You can find the directory included in ruby search path, by typing $LOAD_APTH or simply $:

1.9.3-p194 :005 > $LOAD_PATH
 => ["/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby/1.9.1",
 "/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby/1.9.1/i686-linux",
 "/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby", 
"/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/vendor_ruby/1.9.1", 
"/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/vendor_ruby/1.9.1/i686-linux", 
"/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/vendor_ruby", 
"/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1", 
"/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/i686-linux"] 

So, ruby will search the file you are specifying in above folder. You can see that the folder Document/projects is not listed in load path so the require syntax we are using is not working.

NOTE : ruby first try to find a file by its absolute path. If ruby do not able to find the file by its absolute path, it try to find it in all the directory listed in $LOAD_PATH. If it is not found there also, It will raise LoadError: cannot load such file

So we can use any of the below solution.

SOLUTION 1: specify the absolute path of the file you want to require.

1.9.3-p194 :001 > require "Document/projects/myprogram.rb"
LoadError: cannot load such file -- myprogram

Again, it is not working. this is because you have specified the absolute path wrongly. It should be start from your home directory. below will work.

1.9.3-p194 :001 > require "/home/arun/Document/projects/myprogram.rb"
=> true

So, now it worked. But specifying absolute path is tedious. The second solution provide a better approach.

SOLUTION 2: Add the folder containing your program to the ruby $LOAD_PATH.
If you are working in any RAILS application you can specify it in config/application.rb file. It always include lib folder by default. That is the reason why, you just need to specify the file name with require if it is placed in lib folder, not the absolute path. The default line look like this.

Auto Loading custom folder in rails application

config.autoload_paths += %W(#{config.root}/lib)

So this line will add lib folder of your application to the ruby $LOAD_PATH. let us modify the above line to include, say extra_program folder in the load path.

config.autoload_paths += %W(#{config.root}/lib #{config.root}/extra_program)

So any file in extra_program folder of your rails app will also be added to auto load path.

Outside Rails app, like in above case where the file is located in /home/arunDocument/projects folder, we can add it to $LOAD_PATH as below from the irb or rails console.

1.9.3p194 :001 > $LOAD_PATH.unshift File.expand_path('/home/arunDocument/projects', __FILE__)
 => ["/home/arunDocument/projects", 
"/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby/1.9.1",
 "/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby/1.9.1/i686-linux", 
"/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby", 
"/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/vendor_ruby/1.9.1", 
"/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/vendor_ruby/1.9.1/i686-linux",
 "/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/vendor_ruby", 
"/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1",
 "/home/arun/.rvm/rubies/ruby-1.9.3-p194/lib/ruby/1.9.1/i686-linux"] 
1.9.3p194 :002 > require "myprogram"
 => true

So we can see that /home/arunDocument/projects added to auto load path and the simple require syntax is working now.

load can be used exactly as require, but have below differences:

DIFFERENCE BETWEEN LOAD AND REQUIRE:

1=> File Extension need to be specified with load but not with require

1.9.3p194 :002 > require "myprogram.rb" # work without extension
 => true 
1.9.3p194 :003 > load "myprogram" # load do not work without extension
LoadError: cannot load such file -- ruby_block
	from (irb):3:in `load'
	from (irb):3
	from /home/arun/.rvm/rubies/ruby-1.9.3-p194/bin/irb:16:in `'
1.9.3p194 :004 > load "myprogram.rb" # working with extension
 => true

2=> in any current scope, require compile the file only first time it is encountered, load do it every time.


say we are working in irb, so our current scope is irb. So on irb, if you require the myprogram.rb once then you make some changes and again require the file, your changes will not reflect, as it is not compiled again. To make your changes reflect, you need to exit irb and restart again to make require to compile your new changes. But load do it every time it is encountered. So if any file content keep changing with time, you should better use load instead of require.

REFERENCES :

http://www.ruby-doc.org/core-2.0.0/Kernel.html


Leave a comment

ArgumentError: invalid byte sequence in US-ASCII

I have automated my rails deployment with Capistrano. It was working fine. I have added a new gem useragent to my Gemfile as below

gem 'useragent', '0.2.3', :git => "http://github.com/jilion/useragent"

I committed the code and when try to deploy the code again got below error

2013-09-17 16:50:35 executing `bundle:install'
    [root@10.1.5.127] executing command
 ** [out :: root@10.1.5.127] ArgumentError: invalid byte sequence in US-ASCII
 ** [out :: root@10.1.5.127] An error occurred while installing useragent (0.2.3),
 ** [out :: root@10.1.5.127]  and Bundler cannot continue.
 ** [out :: root@10.1.5.127] Make sure that `gem install useragent -v '0.2.3'`
    succeeds before bundling.

The issue is discussed in length here at bundler issue tracking .

The reason is that by default ruby set to evaluate any file as US-ASCII encoding. But useragent gem is having some non ASCII characters in it, so throwing the error. It can be solved by setting the default language to utf-8 which support much wider range of encoding. The solution is to add below line to Gemfile at the top.

if RUBY_VERSION =~ /1.9/ # assuming you're running Ruby ~1.9
  Encoding.default_external = Encoding::UTF_8
  Encoding.default_internal = Encoding::UTF_8
end


Leave a comment

customize error message with locales

locales allow you to add multiple language to your application. It is based on rails I18n API , the i18n wiki is available here . From rails 2.2 onward the I18n API is bundled as a dependency gem for rails. It is always a good practice to separate out, constant stuffs exposed to user ,  like validation error messages , flash notices etc in locales. If you structure your code like this from beginning, it will be easy for you to display the message in any language, the default is English. So by default rails will load all the messages from en.yml file. If say your site is in Germany and you want to show the validation message in German language. Then you will write all the validation message in de.yml file and set the default language to :de in application.rb file with the below line

config.i18n.default_locale = :de

So now rails will try to look for the message in de.yml file. At any point, you can make rails to load a specific language file any time by passing locales parameter in URL

Example : 

http://my_xyz.com/sigin?locales=en # it will load all the locales from 
en.yml extensions like devise.en.yml, rest.en.yml etc
http://my_xyz.com/sigin?locales=de # it will load all the locales from 
de.yml extensions like devise.de.yml, rest.de.yml etc

The best way is to provide the extension in domain name itself

Example:
http://my_xyz.com.en 
http://my_xyz.com.de

Above explanation is just a Introduction of locales and I will not get into more detail here. You may read the reference provided below. Let us move our validation error message to locals.

Below, is the current validation

class User < ActiveRecord::Base
validates :source_system, :source_system_id
validates_uniqueness_of :source_system_id, :message => "ID has already been taken"
validates :entity_type, :inclusion =>  {:in => %w(Customer User), :message => "should be Customer or User"}
validates :target_system, :inclusion =>  {:in => %w(LOCAL OPENAM), :message => "should be LOCAL or OPENAM"}
end

We can move these validation message to en.yml file locales. I have also shown providing custom name to the model and its attributes.

But let us first remove the message from the user model, we do not need them as we are now setting the message in the locals

class User < ActiveRecord::Base
validates :source_system, :source_system_id
validates_uniqueness_of :source_system_id
validates :entity_type, :inclusion =>  {:in => %w(Customer User)}
validates :target_system, :inclusion =>  {:in => %w(LOCAL OPENAM)}
end

Below is the code in config/locales/en.yml file

en:
  activerecord:
    models: 
      user: "Customer"
    attributes:
      user:
        email: "Email address"
    errors:
      models:
        user:
          attributes:
            source_system_id:
              blank: "must fill %{attribute}" 
              taken: " Id User already provisioned"
            entity_type:
              inclusion: "should be Customer or User"
            target_system:
              inclusion: "should be LOCAL or OPENAM"

Note, that the validation to be used in locals is not exactly same as we use in model, for example in model we have checked uniqueness for source_system_id, but in local above we have used :taken, similarly when we use :presence => true in model, in locals we used blank. Also see the way we have used attribute in the message, thus the message will become must fill source_system_id.

The complete mapping of active record validation with corresponding validation to be used in locals is as below.

list of ActiveRecord Validation maped to corrosponding validation to be used in locals

list of ActiveRecord Validation mapped to corresponding validation to be used in locals

Reference:
http://ruby-i18n.org/wiki
http://guides.rubyonrails.org/i18n.html
http://stackoverflow.com/questions/808547/fully-custom-validation-error-message-with-rails


1 Comment

TimeZone in rails

Recently, I get a chance to look deeper in Time zone concept. Actually, The site I was working have its target audience in USA, but the server is located in India. QA guys have reported that, the login time or any other time based data is not having actual value of the time the user accessing the site from USA.  A Ticket is raised to fix the issue. There exact requirement is to fix the time to USA pacific time.

Rails provide a very easy way to do this. But the main problem is to we aware of the correct use of Time class in your code. I will go step by step

STEP 1: knowing different time zone supported by rails.

TimeZone is part of active support provided by rails. You can get more detail on it here . You can find all the detail about zone by running below rake command on your project root.
listing all the time zone.

$ rake time:zones:all

* UTC -07:00 *
Mountain Time (US & Canada)

* UTC -06:00 *
Central America
Central Time (US & Canada)

* UTC -05:00 *
Bogota
Eastern Time (US & Canada)
Indiana (East)

* UTC -02:00 *
Mid-Atlantic

* UTC +01:00 *
Amsterdam
Belgrade

* UTC +02:00 *
Athens
Bucharest
Cairo

* UTC +03:00 *
Baghdad
Kuwait

* UTC +05:00 *
Islamabad
Karachi
Tashkent

* UTC +05:30 *
Chennai
Kolkata
Mumbai
New Delhi

* UTC +05:45 *
Kathmandu

* UTC +10:00 *
Brisbane
Canberra

* UTC +11:00 *
New Caledonia
Vladivostok

* UTC +12:00 *
Auckland
Fiji

It will give you all the available time zone. You can filter the result by giving hour offset from GMT. In the above listing +12.00 mean the UTC(Universal Time Coordinated) time is 12 hour ahead of GMT(Greenwich Mean Time).

$ rake time:zones:all OFFSET=+12
* UTC +12:00 *
Auckland
Fiji
Kamchatka
Magadan
Marshall Is.
Solomon Is.
Wellington

So you can see that it has listed only those time zone which is 12 hour ahed of GMT
There is also rake task to get time zones within USA

$ rake time:zones:us
* UTC -10:00 *
Hawaii

* UTC -09:00 *
Alaska

* UTC -08:00 *
Pacific Time (US & Canada)

* UTC -07:00 *
Arizona
Mountain Time (US & Canada)

* UTC -06:00 *
Central Time (US & Canada)

* UTC -05:00 *
Eastern Time (US & Canada)
Indiana (East)

You can also find loacl time zone i,e time zones within the country your computer is located.

$ rake time:zones:local
* UTC +05:30 *
Chennai
Kolkata
Mumbai
New Delhi

O.K..so we know have list of all the available time zone, in next step we will configure rails to use the zone we specify.

STEP 2: setting time zone

By default rails assume time zone to be GMT. you can find it by going to your rails console

1.9.3p194 :001 > Time.zone
 => (GMT+00:00) UTC

O.k let us set it to Pacific Time (US & Canada). It can be set in config/application.rb file, there you can find below lines, suggesting how to set time zone

    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
    # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
    # config.time_zone = 'Central Time (US & Canada)'

So just uncomment the last line and put your required time zone

    config.time_zone = "Pacific Time (US & Canada)"

Now restart your rails console and check the time zone again

1.9.3p194 :001 > Time.zone
 => (GMT-08:00) Pacific Time (US & Canada)

So, now our rails loading the zone we have specified in application.rb file. In next step we will see how it affect time

STEP 3: working with Time and TimeZone

So, above we have set time zone to (GMT-08:00) Pacific Time (US & Canada) . It is 8 hour behind GMT. I’ am in india. Let us see what time is returned on the console.

1.9.3p194 :001 > Time.zone
 => (GMT-08:00) Pacific Time (US & Canada) 
1.9.3p194 :002 > Time.now
 => 2013-09-09 18:28:23 +0530

But what is this…zone is showing properly, but time is still the time my computer is showing.
This is because, Time and DateTime are ruby classes, they know nothing about rails. ruby Time object derive its time from the machine on which it is working, so If your machine or server is in India, it will show current time of India, if in Australia , it will show current time of Australia and so on. So we can summarize the problem as “Rails Time not inheriting from application.rb configuration” .

The solution here is to use zone method provided by TimeZone class . zone method will basically, wrap the timeobject in the TimeZone provided in the setting. So you should use Time.zone.now instead of Time.now

NOTE : make it a practice to use Time.zone.now instead of Time.now

1.9.3p194 :001 > Time.zone.now
 => Mon, 09 Sep 2013 07:20:41 PDT -07:00

So now it is giving correct time based on configured time zone. I was using Time.now in around 20 files and have to change them to Time.zone.now. So, made it as a practice to use Time.zone.now instead of Time.now to retrieve the current time.

You can also, override setting in application.rb at anywhere in your code by Time.zone= method

1.9.3p194 :009 > Time.zone = "Kuwait" # it will set time zone to kuwait
 => "Kuwait" 
1.9.3p194 :010 > Time.zone.now
 => Mon, 09 Sep 2013 16:16:01 AST +03:00

STEP 4: converting Time between different TimeZone

The above steps tell you how to configure your application to render time as per a perticuler timeZone you want. But what If, your application work with third Party Services and API which retrun date as string in some other time zone. Say, one of the API Iam working return “2013-11-13T23:26:13.655-08:00” as the date of uploaded pic.

Since here, we do not get a Time object, we should first convert it into a Time object. You can use either to_time or parse method

1.9.3p194 :009 > time = Time.parse("2013-11-13T23:26:13.655-08:00")
 => 2013-11-14 12:56:13 +0530
1.9.3p194 :015 > time.strftime("%d-%b-%Y %H:%M")
 => "14-Nov-2013 12:56"
1.9.3p194 :011 > time="2013-11-13T23:26:13.655-08:00".to_time
 => 2013-11-14 07:26:13 UTC 
1.9.3p194 :017 > time.strftime("%d-%b-%Y %H:%M")
 => "14-Nov-2013 07:26"

Now you have time object.You can see that the parse method return time as offset from UTC while the to_time method return time as per UTC. Anyway now we have time object and so now we can convert it to corrosponding time as per any timezone, by using in_time_zone method .

1.9.3p194 :019 > pacific_time = time.in_time_zone("Pacific Time (US & Canada)")
 => Wed, 13 Nov 2013 23:26:13 PST -08:00 
1.9.3p194 :020 > pacific_time.strftime("%d-%b-%Y %H:%M")
 => "13-Nov-2013 23:26"

So you can see that the time as per pacific time zone is 23:26

References :

http://api.rubyonrails.org/classes/ActiveSupport/TimeZone.html

http://www.databasically.com/2010/10/22/what-time-is-it-or-handling-timezones-in-rails

http://danilenko.org/2012/7/6/rails_timezones

http://approache.com/blog/testing-rails-across-time-zones


Leave a comment

loging user agent details

Recently, our application throwing some exception in production, when request trigger from certain browser on certain OS. For example a javascript code working on IE8 + window,  when Iam testing in India, but QA person form USA reporting error in IE8. To reproduce the issue, I have to login to there machine remotely and find that they are using different build of IE8. So it is decided to log the browser and OS detail from which user is accessing our application, so that we can prioritize the bug to be fixed.

You can do it by capturing the request when a user login to your application. request a object in itself in rails and available in all the controller.

You can get list of all method available to this request object by calling methods method on it as available for any object in ruby.

request.methods #return a array of methods

[:ignore_accept_header, :ignore_accept_header=, :auth_type, :gateway_interface, :path_translated, :remote_host, :remote_ident, :remote_user, :remote_addr, :server_name, :server_protocol, :accept, :accept_charset, :accept_encoding, :accept_language, :cache_control, :from, :negotiate, :pragma, :key?, :request_method, :request_method_symbol, :method, :method_symbol, :get?, :post?, :put?, :delete?, :head?, :headers, :original_fullpath, :fullpath, :original_url, :media_type, :content_length, :xml_http_request?, :xhr?, :ip, :remote_ip, :uuid, :server_software, :raw_post, :body, :form_data?, :body_stream, :reset_session, :session=, :session_options=, :GET, :query_parameters, :POST, :request_parameters, :authorization, :local?, :deep_munge,  :trace?, :parseable_data?, :[], :[]=, :values_at, :referer, :referrer, :user_agent, :cookies, :base_url, :path, :trusted_proxy?, :parse_multipart]

The method of our concern is user_agent . It will return the user’s browser and os detail from which he is making the request to your application.

Some of the sample output is as below :

request.user_agent # below is the output for different browser

From firefox

“Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:18.0) Gecko/20100101 Firefox/18.0”

from chrome

“Mozilla/5.0 (X11; Linux i686) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.52 Safari/536.5”

So you can see that, it has all the detail of browser, its version and Operating system. you just need to parse it to retrieve all the detail.

Well, you do not need to do that also, as user_agent gem is available for that. Add below to your Gemfile and run bundle install

gem 'useragent', '0.2.3', :git => "http://github.com/jilion/useragent" # I have specified 
the git url as I found that there is also another gem with the same name, and I do not want 
to get into any conflict


Now you can retrieve various detail as below :

user_agent = request.user_agent # It will return the user agent

“Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:18.0) Gecko/20100101 Firefox/18.0”

parsed_user_agent = UserAgent.parse(user_agent) #the gem will parse above string for you returning value you need

parsed_user_agent.browser
“Firefox”
parsed_user_agent.version
“18.0”
parsed_user_agent.platform
“Linux”
parsed_user_agent.os
“Ubuntu”

So, once you get all these detail, you can just save it in login_histories table as below.

    LoginHistory.create(:user_id=> self.id, :login_time => DateTime.now, :login_ip => request.remote_ip,
      :browser => parsed_user_agent.browser, :browser_version => parsed_user_agent.version,
      :platform => parsed_user_agent.platform, :os => parsed_user_agent.os)