Introduction to Rails 4 by Kartik Sahoo
Agenda 1. Components of Rails (Overview) 2. Environment Setup for Rails 4 a. What is RVM? b. What is Bundler? c. How to Setup 3. What’s added to Rails 4 4. What’s removed from Rails 4 5. Model Changes a. Introduction of AREL b. Finders & Scopes c. Eager Loading d. ‘concerns’ directory inside app/model/ 6. Controller Changes a. Strong Parameters 7. View Changes a. Newly added form builder helper methods b. HTML code is escaped by default 8. Routing Changes a. Modified use of ‘match’ b. Introduction of PATCH verb c. Namespace, path, constraints, nested routes, shallow nesting d. Routing concerns 9. Migration Changes a. Reversible method b. ‘change’ method 10.Turbolinks
Components of Rails ● ActiveModel ● ActiveRecord ● ActiveResource ● ActiveSupport ● ActionPack ● ActionController ● ActionView ● ActionMailer
How to setup Environment ●RVM ●Bundler ●Gemfile, Gemfile.lock ●Setup orvm install 2.0.0 omkdir rails_4 ocd rails_4 orvm use 2.0.0@rails_4 --ruby-version --create
What’s new in Rails 4 1.Model a.AREL concept b.‘concerns’ directory 2.Controller a.Strong Parameter 3.Routes a.PATCH HTTP verb b.Routing concerns 4.Views a.Few form builder helpers b.HTML escaped by default 5.Reversible method in Migration 6.Turbolinks
What’s removed from Rails 4 ●Observers ●Protected model attributes ●AR Session store ●AR Resources ●Finder with conditions (Deprecated) What’s recommended ● before_action to before_filter ● update to update_attributes
Validation RAILS-2 class User validates_acceptance_of :terms_of_service validates_associated :books validates_format_of :email, :with => /A[a-zA-Z]+z/, :message => "Only letters allowed" validates_presence_of :login, :name, :address validates_exclusion_of :subdomain, :in => %w(www us ca jp) end RAILS-4 class User validates :terms_of_service, acceptance: true validates_associated :books validates :email, format: {with: /A[a-zA-Z]+z/, message: ‘Only letters allowed’} validates :login, :name, :address, presence: true validates :subdomain, exclusion: {in: %w(www us ca jp)} end
Introduction to AREL ● An expressive and efficient way of method chaining to query data through model. ● Gives a feeling of object-oriented design. ● Simplifies the generation of complex SQL queries. class Post has_many :comments end class Comment belongs_to :post end >Comment.all returns an array of ActiveRecord objects in Rails 2 > Comment.all returns an ActiveRecord::Relation object in Rails 4 > Comment.none returns an empty ActiveRecord::Relation object.
AREL continues..
ActiveRecord Finders RAILS-2 Comment.find(:all, :conditions => {:commenter => nil}) There are many finder methods defined in ActiveRecord to retrieve objects from the Database, such as: where, group, having, offset, take, limit, joins, includes, order, readonly, select etc. Each of these methods returns an instance of ActiveRecord::Relation class. RAILS-4 Comment.where(commenter: nil) Comment.find(:first, :conditions => [‘commenter IS NOT NULL’], :order => ‘created_at DESC’) Comment.where.not(commenter: nil).order(“created_at desc”).first Comment.find_all(:conditions => [‘commented_at >= ?’, 2.days.ago], :limit => 5) Comment.where(“commented_at >= ?”, 2.days.ago).limit(5) Comment.find_by_commenter(“Tukuna”) Comment.find_by_comenter(“Tukuna”) Post.find(:all, :conditions => [“likes_count > ?”, min_likes], :include => :comments) Post.where(“likes_count > ?”, min_likes).includes(:comments) Comment.find(:first, :order => “commented_at DESC”) Comment.order(:commented_at).last Comment.find_by(commenter: “Tukuna”, body: nil)
Finders Continues.. ● Model.take retrieves a record without any implicit ordering. Returns nil or array of AR objects. Comment.take => #<Comment id: 1, commenter: "Lifo"> Comment.take(2) =>[#<Comment id: 1, commenter: "Lifo">, #<Comment id: 3, commenter: "Donald">] ● Model.limit also retrieves records without any ordering. But it takes an arg and returns ActiveRecord::Relation object Comment.limit(2) => #<ActiveRecord::Relation [#<Comment id: 1, commenter: "Lifo">, #<Comment id: 2, commenter: "Donald">]> ● Model.first finds the first record ordered by the primary key. Also takes argument (e.g: Comment.first(10)) ● Model.last finds the last record ordered by the primary key. ● Model.find_by finds the first record matching some conditions. Comment.find_by(first_name: 'Lifo') => #<Comment id: 1, commenter: "Lifo"> Comment.find_by_first_name(‘Donald’) => #<Comment id: 2, commenter: "Donald"> Model.find_all_by_… method is deprecated
Model.all vs Model.find_each Comment.all ● instructs Active Record to fetch the entire table in a single pass, build a model object per row, and then keep the entire array of model objects in memory. ● It may exceed the amount of memory available. Comment.all.each do |c| c.post_to_facebook end Model.find_each ● Retrieves a batch of records and then yields each record to the block individually as a model. ● We can also specify batch size and starting point using :batch_size and :start parameters Comment.find_each(batch_size: 5000, start: 2000) do |c| c.post_to_facebook end
Scopes RAILS-2 class Comment named_scope :recent, :conditions => [“created_at > ? AND created_at < ?”, 2.days.ago, Time.now] named_scope :published, :conditions => { :state => :published } named_scope :published_after, lambda { |time| { :conditions => [“published_at > ?”, time] } } end RAILS-4 class Comment scope :recent, -> { where(“created_at > ? AND created_at < ?”, 2.days.ago, Time.now) } scope :published, -> { where(state: :published) } scope :published_after, -> (time) { where(“published_at > ?”, time) } end All scope methods will return an ActiveRecord::Relation object which will allow for further methods to be called on it. But why? If server has started 5 days ago then this 2.days.ago would give value equal to 7.days.ago This has been rectified in Rails-4 using lambda syntax: scope :published, -> { where => {'created_at >= ?', 1.day.ago} } This runs at run-time not class loading time.
Eager Loading Eager loading is the mechanism for loading the associated records of the objects returned by Model.find using as few queries as possible. comments = Comment.limit(10) comments.each do |comment| puts comment.post.description end Q: How many queries does it perform? Ans: 11 queries (1 for comments + 10 for fetching post from the comment) This is also known as N+1 queries problem. Solution: comments = Comment.includes(:post).limit(10) comments.each do |comment| puts comment.post.description end This solution creates following 2 queries. SELECT * FROM comments LIMIT 10; SELECT posts.* FROM comments WHERE post_id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Controller Changes ● Sessions are lazily loaded. If you don't access sessions in your action's code, they will not be loaded. Hence you will never need to disable sessions, just not accessing them will do the job ● application.rb file is renamed to application_controller.rb ● before_action should be used in place of before_filter Flash Message Can assign a flash message to :notice, :alert or :flash as part of the redirection. redirect_to post_url, notice: “Message is successfully created." redirect_to post_url, alert: "You're stuck here!" redirect_to post_url, flash: { referral_code: 1234 } <p class=”notice”> <%= flash[:notice] %> </p> X <p class=”notice”> <%= notice %> </p>
Strong Parameters ● With strong parameters, Action Controller parameters are forbidden to be used in Active Model mass assignments until they have been whitelisted. ● The basic idea is to move mass-assignment protection out of the model and into the controller where it belongs. class CommentsController < ApplicationController def create @comment = Comment.create(params[:comment]) end ………… end This will raise an ActiveModel::ForbiddenAttributes exception. class CommentsController < ApplicationController def create @comment = Comment.create(comment_params) end ………… private def comment_params params.require(:comment).permit(:commenter, :body) end end NOTE: comment_params is private. This new approach prevents an attacker from setting the model's attributes by manipulating the hash passed to the model.
Nested Parameters (Skip) params.permit(:name, { emails: [] }, friends: [:name, family: [:name], hobbies: [ ] ]) This declaration whitelists the name, emails and friends attributes. It is expected that ● emails will be an array ● friends will be an array of resources with specific attributes ○ a name attribute ○ a family attribute which is restricted to having a name ○ a hobbies attribute as an array
View Changes ● All strings are escaped by default. No need to use <%= h @post.description -%> ● <%= render partial: "comment", collection: @comments %> is same as <%= render @comments %> ● Rails determines the name of the partial to use by looking at the model name in the collection. ● In fact, we can even create a heterogeneous collection and render it this way, and Rails will choose the proper partial for each member of the collection. ● Spacer Template <%=render@products, spacer_template: "product_ruler"%> Rails will render the _product_ruler partial (with no data passed to it) between each pair of _product partials. ● Newly added Form Helpers (Details Later) ○ date_select, select_day, select_hour ○ collection_radio_buttons, collection_check_boxes ○ country_select, time_zone_select ○ video_tag, audio_tag ○ search_field, telephone_field, date_field, color_field
Partial Layout Partials now can have layouts of their own. posts/_post.html.erb <%= div_for(post) do %> <p><%= post.body %></p> <% end %> posts/show.html.erb <%= render partial: 'post', layout: 'box', locals: {post: @post} %> posts/_box.html.erb <div class='box'> <%= yield %> </div> OUTPUT: <div class='box'> <div id='post_1'> <p>Partial Layouts are cool!</p> </div> </div>
How form_for(@object) works.. ● When dealing with RESTful resources, calls to form_for can get significantly easier Creating a new comment # long-style: form_for(@comment, url: comments_path) # same thing, short-style (record identification gets used): form_for(@comment) Editing an existing comment # long-style: form_for(@comment, url: comment_path(@comment), html: { method: "patch" }) # short-style: form_for(@comment)
Routing Concerns Q: match method is not safe, why? A: Because it matches all kinds of requests (PUT, GET, POST, PATCH & DELETE). We should use the verbs instead. match '/items/:id/purchase', to: 'items#purchase' post '/items/:id/purchase', to: 'items#purchase' post '/items/:id/purchase', to: 'items#purchase', via: :post resources :messages do resources :comments resources :categories resources :tags end resources :posts do resources :comments resources :categories resources :tags end resources :items do resources :comments resources :categories resources :tags end Routing concerns allows us to declare common routes that can be reused inside others resources and routes. concern :sociable do resources :comments resources :categories resources :tags end resources :messages, concerns: :sociable resources :posts, concerns: :sociable resources :items, concerns: :sociable
Namespaced Routes
Nested Routes and Shallow Nesting
Named Helper, Constraints and Redirection
Change Method RAILS-2 class CreateProducts < ActiveRecord::Migration def up create_table :products do |t| t.string :name t.text :description t.timestamps end end def down drop_table :products end end RAILS- 3/4 class CreateProducts < ActiveRecord::Migration def change create_table :products do |t| t.string :name t.text :description t.timestamps end end end up and down still works in Rails 4.
Reversible Method
Why, How and Advantage

Intro to Rails 4

  • 1.
    Introduction to Rails4 by Kartik Sahoo
  • 2.
    Agenda 1. Components ofRails (Overview) 2. Environment Setup for Rails 4 a. What is RVM? b. What is Bundler? c. How to Setup 3. What’s added to Rails 4 4. What’s removed from Rails 4 5. Model Changes a. Introduction of AREL b. Finders & Scopes c. Eager Loading d. ‘concerns’ directory inside app/model/ 6. Controller Changes a. Strong Parameters 7. View Changes a. Newly added form builder helper methods b. HTML code is escaped by default 8. Routing Changes a. Modified use of ‘match’ b. Introduction of PATCH verb c. Namespace, path, constraints, nested routes, shallow nesting d. Routing concerns 9. Migration Changes a. Reversible method b. ‘change’ method 10.Turbolinks
  • 3.
    Components of Rails ●ActiveModel ● ActiveRecord ● ActiveResource ● ActiveSupport ● ActionPack ● ActionController ● ActionView ● ActionMailer
  • 4.
    How to setupEnvironment ●RVM ●Bundler ●Gemfile, Gemfile.lock ●Setup orvm install 2.0.0 omkdir rails_4 ocd rails_4 orvm use 2.0.0@rails_4 --ruby-version --create
  • 5.
    What’s new inRails 4 1.Model a.AREL concept b.‘concerns’ directory 2.Controller a.Strong Parameter 3.Routes a.PATCH HTTP verb b.Routing concerns 4.Views a.Few form builder helpers b.HTML escaped by default 5.Reversible method in Migration 6.Turbolinks
  • 6.
    What’s removed fromRails 4 ●Observers ●Protected model attributes ●AR Session store ●AR Resources ●Finder with conditions (Deprecated) What’s recommended ● before_action to before_filter ● update to update_attributes
  • 8.
    Validation RAILS-2 class User validates_acceptance_of :terms_of_service validates_associated:books validates_format_of :email, :with => /A[a-zA-Z]+z/, :message => "Only letters allowed" validates_presence_of :login, :name, :address validates_exclusion_of :subdomain, :in => %w(www us ca jp) end RAILS-4 class User validates :terms_of_service, acceptance: true validates_associated :books validates :email, format: {with: /A[a-zA-Z]+z/, message: ‘Only letters allowed’} validates :login, :name, :address, presence: true validates :subdomain, exclusion: {in: %w(www us ca jp)} end
  • 9.
    Introduction to AREL ●An expressive and efficient way of method chaining to query data through model. ● Gives a feeling of object-oriented design. ● Simplifies the generation of complex SQL queries. class Post has_many :comments end class Comment belongs_to :post end >Comment.all returns an array of ActiveRecord objects in Rails 2 > Comment.all returns an ActiveRecord::Relation object in Rails 4 > Comment.none returns an empty ActiveRecord::Relation object.
  • 10.
  • 11.
    ActiveRecord Finders RAILS-2 Comment.find(:all, :conditions=> {:commenter => nil}) There are many finder methods defined in ActiveRecord to retrieve objects from the Database, such as: where, group, having, offset, take, limit, joins, includes, order, readonly, select etc. Each of these methods returns an instance of ActiveRecord::Relation class. RAILS-4 Comment.where(commenter: nil) Comment.find(:first, :conditions => [‘commenter IS NOT NULL’], :order => ‘created_at DESC’) Comment.where.not(commenter: nil).order(“created_at desc”).first Comment.find_all(:conditions => [‘commented_at >= ?’, 2.days.ago], :limit => 5) Comment.where(“commented_at >= ?”, 2.days.ago).limit(5) Comment.find_by_commenter(“Tukuna”) Comment.find_by_comenter(“Tukuna”) Post.find(:all, :conditions => [“likes_count > ?”, min_likes], :include => :comments) Post.where(“likes_count > ?”, min_likes).includes(:comments) Comment.find(:first, :order => “commented_at DESC”) Comment.order(:commented_at).last Comment.find_by(commenter: “Tukuna”, body: nil)
  • 12.
    Finders Continues.. ● Model.takeretrieves a record without any implicit ordering. Returns nil or array of AR objects. Comment.take => #<Comment id: 1, commenter: "Lifo"> Comment.take(2) =>[#<Comment id: 1, commenter: "Lifo">, #<Comment id: 3, commenter: "Donald">] ● Model.limit also retrieves records without any ordering. But it takes an arg and returns ActiveRecord::Relation object Comment.limit(2) => #<ActiveRecord::Relation [#<Comment id: 1, commenter: "Lifo">, #<Comment id: 2, commenter: "Donald">]> ● Model.first finds the first record ordered by the primary key. Also takes argument (e.g: Comment.first(10)) ● Model.last finds the last record ordered by the primary key. ● Model.find_by finds the first record matching some conditions. Comment.find_by(first_name: 'Lifo') => #<Comment id: 1, commenter: "Lifo"> Comment.find_by_first_name(‘Donald’) => #<Comment id: 2, commenter: "Donald"> Model.find_all_by_… method is deprecated
  • 13.
    Model.all vs Model.find_each Comment.all ●instructs Active Record to fetch the entire table in a single pass, build a model object per row, and then keep the entire array of model objects in memory. ● It may exceed the amount of memory available. Comment.all.each do |c| c.post_to_facebook end Model.find_each ● Retrieves a batch of records and then yields each record to the block individually as a model. ● We can also specify batch size and starting point using :batch_size and :start parameters Comment.find_each(batch_size: 5000, start: 2000) do |c| c.post_to_facebook end
  • 14.
    Scopes RAILS-2 class Comment named_scope :recent,:conditions => [“created_at > ? AND created_at < ?”, 2.days.ago, Time.now] named_scope :published, :conditions => { :state => :published } named_scope :published_after, lambda { |time| { :conditions => [“published_at > ?”, time] } } end RAILS-4 class Comment scope :recent, -> { where(“created_at > ? AND created_at < ?”, 2.days.ago, Time.now) } scope :published, -> { where(state: :published) } scope :published_after, -> (time) { where(“published_at > ?”, time) } end All scope methods will return an ActiveRecord::Relation object which will allow for further methods to be called on it. But why? If server has started 5 days ago then this 2.days.ago would give value equal to 7.days.ago This has been rectified in Rails-4 using lambda syntax: scope :published, -> { where => {'created_at >= ?', 1.day.ago} } This runs at run-time not class loading time.
  • 15.
    Eager Loading Eager loadingis the mechanism for loading the associated records of the objects returned by Model.find using as few queries as possible. comments = Comment.limit(10) comments.each do |comment| puts comment.post.description end Q: How many queries does it perform? Ans: 11 queries (1 for comments + 10 for fetching post from the comment) This is also known as N+1 queries problem. Solution: comments = Comment.includes(:post).limit(10) comments.each do |comment| puts comment.post.description end This solution creates following 2 queries. SELECT * FROM comments LIMIT 10; SELECT posts.* FROM comments WHERE post_id IN (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
  • 17.
    Controller Changes ● Sessionsare lazily loaded. If you don't access sessions in your action's code, they will not be loaded. Hence you will never need to disable sessions, just not accessing them will do the job ● application.rb file is renamed to application_controller.rb ● before_action should be used in place of before_filter Flash Message Can assign a flash message to :notice, :alert or :flash as part of the redirection. redirect_to post_url, notice: “Message is successfully created." redirect_to post_url, alert: "You're stuck here!" redirect_to post_url, flash: { referral_code: 1234 } <p class=”notice”> <%= flash[:notice] %> </p> X <p class=”notice”> <%= notice %> </p>
  • 18.
    Strong Parameters ● Withstrong parameters, Action Controller parameters are forbidden to be used in Active Model mass assignments until they have been whitelisted. ● The basic idea is to move mass-assignment protection out of the model and into the controller where it belongs. class CommentsController < ApplicationController def create @comment = Comment.create(params[:comment]) end ………… end This will raise an ActiveModel::ForbiddenAttributes exception. class CommentsController < ApplicationController def create @comment = Comment.create(comment_params) end ………… private def comment_params params.require(:comment).permit(:commenter, :body) end end NOTE: comment_params is private. This new approach prevents an attacker from setting the model's attributes by manipulating the hash passed to the model.
  • 19.
    Nested Parameters (Skip) params.permit(:name, {emails: [] }, friends: [:name, family: [:name], hobbies: [ ] ]) This declaration whitelists the name, emails and friends attributes. It is expected that ● emails will be an array ● friends will be an array of resources with specific attributes ○ a name attribute ○ a family attribute which is restricted to having a name ○ a hobbies attribute as an array
  • 21.
    View Changes ● Allstrings are escaped by default. No need to use <%= h @post.description -%> ● <%= render partial: "comment", collection: @comments %> is same as <%= render @comments %> ● Rails determines the name of the partial to use by looking at the model name in the collection. ● In fact, we can even create a heterogeneous collection and render it this way, and Rails will choose the proper partial for each member of the collection. ● Spacer Template <%=render@products, spacer_template: "product_ruler"%> Rails will render the _product_ruler partial (with no data passed to it) between each pair of _product partials. ● Newly added Form Helpers (Details Later) ○ date_select, select_day, select_hour ○ collection_radio_buttons, collection_check_boxes ○ country_select, time_zone_select ○ video_tag, audio_tag ○ search_field, telephone_field, date_field, color_field
  • 22.
    Partial Layout Partials nowcan have layouts of their own. posts/_post.html.erb <%= div_for(post) do %> <p><%= post.body %></p> <% end %> posts/show.html.erb <%= render partial: 'post', layout: 'box', locals: {post: @post} %> posts/_box.html.erb <div class='box'> <%= yield %> </div> OUTPUT: <div class='box'> <div id='post_1'> <p>Partial Layouts are cool!</p> </div> </div>
  • 23.
    How form_for(@object) works.. ●When dealing with RESTful resources, calls to form_for can get significantly easier Creating a new comment # long-style: form_for(@comment, url: comments_path) # same thing, short-style (record identification gets used): form_for(@comment) Editing an existing comment # long-style: form_for(@comment, url: comment_path(@comment), html: { method: "patch" }) # short-style: form_for(@comment)
  • 25.
    Routing Concerns Q: matchmethod is not safe, why? A: Because it matches all kinds of requests (PUT, GET, POST, PATCH & DELETE). We should use the verbs instead. match '/items/:id/purchase', to: 'items#purchase' post '/items/:id/purchase', to: 'items#purchase' post '/items/:id/purchase', to: 'items#purchase', via: :post resources :messages do resources :comments resources :categories resources :tags end resources :posts do resources :comments resources :categories resources :tags end resources :items do resources :comments resources :categories resources :tags end Routing concerns allows us to declare common routes that can be reused inside others resources and routes. concern :sociable do resources :comments resources :categories resources :tags end resources :messages, concerns: :sociable resources :posts, concerns: :sociable resources :items, concerns: :sociable
  • 26.
  • 27.
    Nested Routes andShallow Nesting
  • 28.
  • 30.
    Change Method RAILS-2 class CreateProducts< ActiveRecord::Migration def up create_table :products do |t| t.string :name t.text :description t.timestamps end end def down drop_table :products end end RAILS- 3/4 class CreateProducts < ActiveRecord::Migration def change create_table :products do |t| t.string :name t.text :description t.timestamps end end end up and down still works in Rails 4.
  • 31.
  • 33.
    Why, How andAdvantage