Ruby on Rails Blog
This blog lists the latest tricks and tips of Ruby on Rails
Wednesday, 29 May 2013
Tuesday, 21 May 2013
NOTICE: Blog Migrated!! New Link
Hi guys,
I have decided to shift my blog.
From now on I'll be posting regular updates on http://railsifyme.wordpress.com.
Thanks,
Souvik
I have decided to shift my blog.
From now on I'll be posting regular updates on http://railsifyme.wordpress.com.
Thanks,
Souvik
Thursday, 6 December 2012
Delayed job, a real pain in production
There are so many ways to handle background jobs in Ruby on Rails. But the most simplest one is delayed job (DJ) as I showed you in the following post Handling delayed jobs. Simple in the sense that it requires very less effort to set up and in uses single table to handle the jobs.
It's a very good solution for development environment, but in production people starts complaining of problems such as:
I don't encourage this process.
Better solution will be to start delayed job using cron jobs.
There's a gem available for this purpose: whenever.
solution, but it surely helps in some cases.
It's a very good solution for development environment, but in production people starts complaining of problems such as:
- DJ is eating up my server resources.
- DJ is firing new daemon processes every now and then.
- Many DJ instances are running at a time.
And many more similar kind of issues.
To run delayed job daemon along with rails server, people often end up creating config/initializers/start_worker.rb, and put codes something like
Thread.new do
system("rake jobs:work")
end
I don't encourage this process.
Better solution will be to start delayed job using cron jobs.
There's a gem available for this purpose: whenever.
- Add this line to your gemfile: gem "whenever", :require => false
- $ bundle install
- $ wheneverize .
- This will create config/schedule.rb.
- Open up that file and put the following codes in it:
set :output, "/path_to_your_project/log/cron_log.log"
every :reboot do
command "cd /path_to_your_project && RAILS_ENV=production script/delayed_job start"
end
- This will start delayed job on every server boot. You may also write
every 10.hours do
command "cd /path_to_your_project && RAILS_ENV=production script/delayed_job restart"
end
This will restart delayed job after every 10 hours. Although point number 6 is not a very feasible solution, but it surely helps in some cases.
- $ whenever --update-crontab your_project_name.
- $ crontab -l
- To monitor whether delayed job is running you can try God or monit. You can get some ideas from the Railscasts / a blog on delayed job/ monit example
Wednesday, 17 October 2012
HAML indentation problem
Here's a sample code to assign a dynamic div for displaying companies, the ids should be assigned based on whether the user is logged in or not:
-
@companies.each
do |company|
-
if user_logged_in?
#sponsor_for_logged_users
.icon
= link_to company do
= image_tag company.file
%span{:class
=> 'classic'}
%p.name= company.name
%p= company.short_description
.benefits= company.benefits
-
else
#sponsor_for_unlogged_users
.icon
= link_to company do
= image_tag company.file
%span{:class
=> 'classic'}
%p.name= company.name
%p= company.short_description
.benefits= company.benefits
Too much repeatation :(
We
can't write:
-
@companies.each
do |company|
-
if user_logged_in?
#sponsor_for_logged_users
-
else
#sponsor_for_unlogged_users
.icon
= link_to company do
= image_tag company.file
%span{:class
=> 'classic'}
%p.name= company.name
%p=
company.short_description
.benefits= company.benefits
Because
HAML will take the yellow part as part of else.
So
what to do ???
Inside
controller's action let's decide which div to select
def
index
@div_class_sponsors
=
current_user.nil?
?
"sponsors_benefits_unlogged"
: "sponsors_benefits_logged"
end
The
view page above now becomes:
-
@companies.each
do |company|
%div{:id
=> @div_class_sponsors}
.icon
= link_to
company do
=
image_tag company.file
%span{:class
=> 'classic'}
%p.name=
company.name
%p=
company.short_description
.benefits=
company.benefits
Very
simple. Just an idea that came to my mind instead of using the ideas
available on other resources.
.
and # are used to represent div class and id resp.
But
we can always use %div.
Wednesday, 1 February 2012
Handling Background Jobs in Rails 3.2
Hi everybody,
What's up!
Today I am going to discuss about the background tasks management in rails.
Suppose we have a UserMailer which sends email to the users once they have successfully completed the registration process. Something like this:
class UserMailer < ActionMailer::Base
default :from => "admin@example.com"
And in the controller,
def create
@user = User.new(params[:user])
if @user.save
UserMailer.registration_confirmation(@user).deliver
redirect_to root_url, :notice => "Signed up! Please check confirmation mail!"
else
render "sign_up"
end
end
After the user hits the "Create User" button, he has to wait for some time while their mail is being delivered.
During this process, rails is busy and can't respond to any other requests.
This is a very bad user experience. After all why should one has to wait for some time unnecessarily.
Now there are many gems and plugins available to handle the background jobs. I prefer to use "delayed_job" gem.
Firstly, because it stores the jobs queue in the database table. Hence, no memory leak or loss of data.
Secondly, it's very easy to implement.
So, let's see what are the steps involved:
1) add: gem 'delayed_job_active record' to the gemfile and run 'bundle install'.
2) Next, we need the jobs table so run the commands:
$ rails generate delayed_job:active_record
$ rake db:migrate
3) Next go to the controller and just replace the line
UserMailer.registration_confirmation(@user).deliver
with,
UserMailer.delay.registration_confirmation(@user)
4) $ rake jobs:work
Step 4 will look for any new job in the "delayed_jobs" table and execute it in the background.
This was easy. We can add many more features to the "delayed_jobs" gem for which I suggest you to checkout this Github Link.
Thanks!!
What's up!
Today I am going to discuss about the background tasks management in rails.
Suppose we have a UserMailer which sends email to the users once they have successfully completed the registration process. Something like this:
class UserMailer < ActionMailer::Base
default :from => "admin@example.com"
def registration_confirmation(user)
mail(:to => user.email, :subject => "Registration Confirmation")
end
endAnd in the controller,
def create
@user = User.new(params[:user])
if @user.save
UserMailer.registration_confirmation(@user).deliver
redirect_to root_url, :notice => "Signed up! Please check confirmation mail!"
else
render "sign_up"
end
end
After the user hits the "Create User" button, he has to wait for some time while their mail is being delivered.
During this process, rails is busy and can't respond to any other requests.
This is a very bad user experience. After all why should one has to wait for some time unnecessarily.
Now there are many gems and plugins available to handle the background jobs. I prefer to use "delayed_job" gem.
Firstly, because it stores the jobs queue in the database table. Hence, no memory leak or loss of data.
Secondly, it's very easy to implement.
So, let's see what are the steps involved:
1) add: gem 'delayed_job_active record' to the gemfile and run 'bundle install'.
2) Next, we need the jobs table so run the commands:
$ rails generate delayed_job:active_record
$ rake db:migrate
3) Next go to the controller and just replace the line
UserMailer.registration_confirmation(@user).deliver
with,
UserMailer.delay.registration_confirmation(@user)
4) $ rake jobs:work
Step 4 will look for any new job in the "delayed_jobs" table and execute it in the background.
This was easy. We can add many more features to the "delayed_jobs" gem for which I suggest you to checkout this Github Link.
Thanks!!
Wednesday, 11 January 2012
Don't loop around active record !!!
Hello everyone, it's been a long time since I have posted here.
Today, I am going to show you a way to optimize the performance of your active record queries.
Lets say we have model definitions like this:
class User < ActiveRecord::Base
has_many :blogs
end
class Blog < ActiveRecord::Base
has_many :comments
belongs_to :user
end
class Comment < ActiveRecord::Base
belongs_to :blog
end
Now,let's say we want to display the content of the comments in the view regarding a particular user.
WRONG WAY
@user = User.where("email like ?","example@foobar.com")
@user.first.blogs.each do |blog|
blog.comments.each do |comments|
puts comments.content
end
end
Today, I am going to show you a way to optimize the performance of your active record queries.
Lets say we have model definitions like this:
class User < ActiveRecord::Base
has_many :blogs
end
class Blog < ActiveRecord::Base
has_many :comments
belongs_to :user
end
class Comment < ActiveRecord::Base
belongs_to :blog
end
Now,let's say we want to display the content of the comments in the view regarding a particular user.
WRONG WAY
@user = User.where("email like ?","example@foobar.com")
@user.first.blogs.each do |blog|
blog.comments.each do |comments|
puts comments.content
end
end
The Rails logfile will look something like this:
User Load (1.1ms) SELECT `users`.* FROM `users` WHERE (email like 'example@foobar.com') LIMIT 1
Blog Load (0.7ms) SELECT `blogs`.* FROM `blogs` WHERE `blogs`.`user_id` = 2
Comment Load (0.5ms) SELECT `comments`.* FROM `comments` WHERE `comments`.`blog_id` = 3
Comment Load (0.7ms) SELECT `comments`.* FROM `comments` WHERE `comments`.`blog_id` = 4
Comment Load (0.7ms) SELECT `comments`.* FROM `comments` WHERE `comments`.`blog_id` = 8
Comment Load (0.6ms) SELECT `comments`.* FROM `comments` WHERE `comments`.`blog_id` = 9
Comment Load (41.4ms) SELECT `comments`.* FROM `comments` WHERE `comments`.`blog_id` = 11
Comment Load (0.8ms) SELECT `comments`.* FROM `comments` WHERE `comments`.`blog_id` = 12
What happened? We started with a single user object, then issued a separate SQL command to retrieve the blogs for that user.
Additional SQL commands were required to retrieve the comments in each of those blogs.
In this case there were only a few blogs, but 6 queries were generated. A user with more blogs, more comments, or further nesting could easily result in dozens of SQL queries and a very slow
response.
RIGHT WAY
Fortunately, Rails 3.1 provides us with an easy way to deal with this problem as Active Record lets you specify in advance all the associations that are going to be loaded. This is possible by specifying the includes method of the Model.where / Model.find calls. With includes, Active Record ensures that all of the specified associations are loaded using the minimum possible number of queries.
Here's the way to do this:
@user =User.where("email like ?","example@foobar.com").includes(:blogs => :comments)
Looking into the log again and we see these 3 simple queries:
User Load (1.0ms) SELECT `users`.* FROM `users` WHERE (email like 'example@foobar.com')
@user.first.blogs.each do |blog|
blog.comments.each do |comments|
puts comments.content
end
end
In log what do we see??
NOTHING !!
Yes, it's really possible to minimize the database load to a huge extent.
This wraps up my post, hope it will help you write better code.
Blog Load (0.4ms) SELECT `blogs`.* FROM `blogs` WHERE `blogs`.`user_id` IN (2)
Comment Load (0.5ms) SELECT `comments`.* FROM `comments` WHERE `comments`.`blog_id` IN (3, 4, 8, 9, 11, 12)
And when we run the above loop again:
blog.comments.each do |comments|
puts comments.content
end
end
In log what do we see??
NOTHING !!
Yes, it's really possible to minimize the database load to a huge extent.
This wraps up my post, hope it will help you write better code.
Subscribe to:
Posts (Atom)