Finding the right stuff, an intro to Elasticsearch with Ruby/Rails
Intro to Rails 4
-
Upload
kartik-sahoo -
Category
Documents
-
view
106 -
download
3
Transcript of Intro to Rails 4
Introduction to Rails 4by Kartik Sahoo
Agenda1. 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 44. What’s removed from Rails 45. Model Changes
a. Introduction of ARELb. Finders & Scopesc. Eager Loadingd. ‘concerns’ directory inside app/model/
6. Controller Changesa. Strong Parameters
7. View Changesa. Newly added form builder helper methodsb. HTML code is escaped by default
8. Routing Changesa. Modified use of ‘match’b. Introduction of PATCH verbc. Namespace, path, constraints, nested routes, shallow nestingd. Routing concerns
9. Migration Changesa. Reversible methodb. ‘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
o rvm install 2.0.0omkdir rails_4ocd rails_4o rvm use 2.0.0@rails_4 --ruby-version --create
What’s new in Rails 41.Model
a.AREL conceptb.‘concerns’ directory
2.Controllera.Strong Parameter
3.Routesa.PATCH HTTP verbb.Routing concerns
4.Viewsa.Few form builder helpersb.HTML escaped by default
5.Reversible method in Migration6.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-2class 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-4class 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 :commentsend
class Comment belongs_to :postend
>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-2Comment.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-4Comment.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_eachComment.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-2class 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-4class 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 LoadingEager 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.descriptionend
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) endend
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 ConcernsQ: 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 :tagsend resources :posts do resources :comments resources :categories resources :tagsend resources :items do resources :comments resources :categories resources :tagsend
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 :tagsend
resources :messages, concerns: :sociableresources :posts, concerns: :sociableresources :items, concerns: :sociable
Namespaced Routes
Nested Routes and Shallow Nesting
Named Helper, Constraints and Redirection
Change Method
RAILS-2class 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 endend
RAILS-3/4class CreateProducts < ActiveRecord::Migration def change create_table :products do |t| t.string :name t.text :description t.timestamps end endend
up and down still works in Rails 4.
Reversible Method
Why, How and Advantage