Rails 3 hints

Preview:

DESCRIPTION

Some hints extracted from my experience porting rails 2 applications to rails 3

Citation preview

Rails 3Extra-hints

Summary● Rails 3

○ Deprecations for Rails 4○ ActionMailer○ Ajax in Rails 3○ General...

● Formtastic (2.2)○ Deprecations for V.3○ Custom Inputs

● Acts as Paranoid● Will Paginate● Rspec● ViewModels● Inherited Resources● Delayed Job● Ruby 1.9● Assorted...

Rails 3 > Deprecations

Plugins will cease to exist

Solutions:● get the gem● insert plugin folder in /lib● insert somewhere else and bundle them in your gemfile:

gem "view_models", "=2.0.1", :path => File.join(File.dirname(__FILE__), "/vendor/modified_gems/view_models-2.0.1")

Rails 3 > Deprecations

ActiveRecord Validations

Rails 2validates_presence_of :attrvalidates_length_of :attr, :within => 2..60validates_uniqueness_of :attr, :scope => [:assoc_id]

…Rails 3

validates :attr, :presence => true, :length => {:within => 2..60}, :uniqueness => {:scope => :assoc_id}, ...

Rails 3 > Deprecations

AR queries● Rails 2

ClassName.all(:conditions => ["blbla.id = ?", 1])...● Rails 3

○ Still supported, but it is just translating to ARel. Support in Rails 4 is still unknown.

ClassName.where("blbla.id = ?", 1)

Rails 3 > Deprecations

table names● Rails 2:ClassName < ActiveRecord::Base

set_table_name :bang...

end

● Rails 3:ClassName < ActiveRecord::Base

self.table_name = :bang...

end

Rails 3 > Deprecations

● link_to :confirm optionRails2: link_to ... :confirm => “Sure?” Rails3: link_to ... :data => {:confirm => “Sure?”}

● url helper accept :format option => :html, :js, :json, :xml, etc...

● render accepts two extra options: :formats and :handlerstemplate: app/views/homepage/index.html.hamlname: :index / “index” / “homepage/index” … (extension out!)formats: :htmlhandlers: :hamlrender :index, :formats => :html, :handlers => :haml

if called in respond_to html block:render :index, :handlers => :hamlif defined in environment that template engine is :haml:render :index

Rails 3 > Deprecations

composed_of will be probably removed. Still not officially deprecated, but:

http://blog.plataformatec.com.br/2012/06/about-the-composed_of-removal/

Rails 3 > ActionMailer

● mail delivery: created before delivered:

Mailer.deliver_registry #=> Mailer.registry.deliver

● template extensions changed:

template_name.text.plain.haml #=> template_name.text.hamltemplate_name.text.html.haml #=> template_name.html.haml

Rails 3 > Action Mailer

- template variables are not passed explicitly to the render

@vars[:books] = ...body {:vars => @vars ...}#=>@books = ...

● avoid "include_something" scopes -> just use ARel method “includes” directly

ClassName.include_books #=> ClassName.includes(:books)

● RJS is gone○ when writing ".js" templates, every ruby block

inserted has to be explicitly escaped (escape_javascript)

RAILS_ENV, RAILS_ROOT, RAILS_DEFAULT_LOGGER (Strings)Rails.env, Rails.root, Rails.logger (Pathnames)

Rails 3 > General

Rails 3 > General

● Rails.root can be joined directly

File.join(Rails.root,‘bang’) #=> Rails.root.join(‘bang’)

(note: Rails.root is from type Pathname. Quoting the documentation: "All functionality from File, FileTest, and some from Dir and FileUtils is included, in an unsurprising way. It is essentially a facade for all of these, and more".)

(Previous Rails 2 bug)

Ajax (xhr) != "Script" (Content-Type)● JQuery’s $.get() does an asynchronous html request. Rails will build the

response body in html format (assuming type is not given to $.get)

● JQuery’s $.getScript() does an asynchronous script request. Rails will build the response in the js format

● JQuery’s $.getJSON() does an asynchronous json request. Rails will build the response in the json format

● ...

● you get the idea

● if you want to test if the request was asynchronous in the controller, use request.xhr?

Rails 3 - General

Rails 3 > General

● rendering flush logic changed: template calls with blocks that render something have to be marked with “=” ; others with “-” (Haml notation):

in ERB:<%= form_for(...) do %>...<% @elements.each do |element| %>

● URL helpers: clear distinction from url elements on explicit assignment

○ host : only host! ■ restorm.dev:3000 -> host is restorm.dev

○ subdomain : only subdomain!■ choco.restorm.com -> subdomain is choco■ us.members.restorm.com -> subdomain is us.

members○ port : only port!

■ restorm.dev:3000 -> port is 3000○ user ; password; ... you get the idea

Rails 3 > General

● ActiveRecord errors handled differently:errors.on(:attr) #=> errors[:attr]

● full asset paths: compute_asset_url(“/images/image.jpg”) #=> image_path(“/images/image.jpg”)

● form calls where we want to define the variable name explicitly works differently in Rails 3:form_for :song, elem #=> form_for elem, :as => :song

● Array “random” function was removed; use “sample” instead:[1, 2, 3].random #=> [1, 2, 3].sample

Rails 3 > General

Rails 3 > General

Arel and Scopes

● everything is concatenated, not only the conditions (as in Rails 2)

● avoid defining a condition like select(“table_name.*”); it does that already by default

● (related to above) avoid defining “select” calls in scopes whenever possible; everytime you concatenate it with another “select” call, they will be joined “SELECT *, id FROM...“ #=> WRONG!

● same thing for “order” calls, “group” calls, etc....

Rails 3 > General

Arel and Scopes

● Rails 3 ARel bug: if you have a “group” call in your ARel/scope definition and call “count” at the end, it will not return an Integer, but an OrderedHash (?), careful with that

Rightclearing::Song.count #=> 15269Rightclearing::Song.group_by(:album_title).count #=> {""=>2410, " Little pieces for guitars and other instruments - Part I"=>15, " so black, so bright-instrumental"=>6, ... }

Rails 3 > General

ModulesRails 2:

module Bla

def self.included(base)

base.has_many ….

base.send :include, InstanceMethods

base.extend ClassMethods

end

module ClassMethods

end

module InstanceMethods

def bang

end

end

end

Rails 3 > General

ModulesRails 3:

module Bla

extend ActiveSupport::Concern

included do

has_many

# include InstanceMethods, but, it does this implicitly

extend ClassMethods

end

module ClassMethods

end

def bang

end

end

Rails 3 > General● included from Concern module executes the code in the

block in the scope of the element where the module is included (no more need for the “send :included” call that breaks visibility);

● different order of inclusion (may be an issue in certain cases where we have hacked stuff for inherited resources and other gems...)

● functions defined directly in the module will be instance methods implicitly in the class where the module is included (no more need for the explicit module InstanceMethods)

Formtastic > Deprecations

● explicit value for input now inside input_htmlf.input :attr, :value => 3 f.input :attr, :input_html => { :value => 3 }

● input for dates is now called :date_select● buttons defined differently:

f.buttons do... #=> f.actions do ...f.commit_button #=> f.action :submit #=> <input type=submit...../>f.commit_button :button_html =>... #=> f.action :submit, :as => :button, :button_html => ...

Formtastic - Hints

● nested “inputs” calls on the same builder do not work as before anymore:f.inputs do

%lif.inputs do

….WRONG!

● Notes:

● does not produce invalid HTML as before (good)

● produces nevertheless HTML, though unexpected (so so...)

● does not produce warning message (bad)

Formtastic - Hints

● if an attribute is of type Integer, the resulting input will be of type number (HTML5)

Main Issues with this:○ Some JS code was dependent of the input type

being "text"

Ways to avoid it:○ Provide a context to the element instead of

depending of it directly; mark elements with a class.

Formtastic - Custom Inputs

● Input Structure changed - Focus on modularity

● All Standard Inputs include the Base Module● All Standard Inputs define the to_html which

renders the input● Separate Modules defining input validations,

labelling, content wrapping, errors, collection, etc...

● Everything can be overwritten/redefined

Formtastic - Custom Inputs

Country Select Box

● Inherits From SelectInput, because it is a select box

● Additionally caches the countries and calls them in the "collection" method, which is used by the parent to render the select box

...Et Voilà!

Acts As Paranoid

● Different Gem● Different Developers● Different code-basis! ● More or less the same API

note: in order to use :with_deleted option for associations in a class which is not paranoid itself, include ActsAsParanoid::Associations (see change_tracking/change.rb model for an implementation)

Will Paginate

● array pagination is not available implicitly; if you need it, you’ll have to require ‘will_paginate/array’ explicitly in the file using it

● https://github.com/mislav/will_paginate/wiki/Backwards-incompatibility

RSpec

● ‘double’ recommended instead of ‘mock’● Factory(:element) returns a stubbed model.

They neither have an id anymore nor they can be saved anymore.

● One cannot stub :id calls:

○ Factory(:model, :id => 23) is not valid!

ViewModels

● module to include for view_model_for access changed:

include ViewModels::Helpers::Railsinclude ViewModels::Helpers::Mapping

● ViewModels gem is not being developed further... use a new gem in RC instead? (possible candidate: decorators)

Inherited Resources

● loading helpers on server start; not reloading them on update; everytime you change something in the helpers, you’ll have to restart the server (Major downer...)

● Deprecated; Gem creator recommends using the new Rails 3 controllers ; consider removal in RC?

Delayed Job

● called differently: ○ send_later :bang #=> delay.bang

Ruby 1.9

● the new file character encoding system. new to anyone? in new files that might not have been marked with the utf8 tag, please do.# -*- encoding : utf-8 -*-

● to_a removed from the Object APIsomething.to_a => Array(something)

Ruby 1.9

● “require” calls must be made using the full path (or relative to the file where it is being called)

● when you return something from a Proc, you are not returning it from the Proc, but instead from the scope where the Proc is being executed ○ no scope is returning now

Ruby 1.9

● Array#to_s works differently:○ Ruby 1.8 - [1, 2].to_s #=> “12”○ Ruby 1.9 - [1.2].to_s #=> “[1, 2]”○ use.join instead

● procs now support splat arguments and default values for them:○ proc {|id, args*, opts={}|...}

Assorted

● forms that involve upload or possible upload (more explicitly contain files, non-ASCII data, and binary data) should be marked as multipart.

● in cases where the method arguments are defined with a pointer (“def func(*args)”) and we want to extract the options hash from the last argument:opts = args.extract_options!opts = args.last.is_a?(Hash) ? args.pop : {}

Assorted

● if there are functions that need to be shared between the controller and its respective template rendering scope (templates, helpers), use the helper_method method and define it only one time in the controller.

● Avoid inclusion of helpers in controllers; either they are helpers and therefore should support the rendering, or they are controller mixins that are responsible for handling controller code (and may define helper functions using the helper_method method mentioned above)

● use always t(“a.b.c.d”) instead of t(:d => :scope => [:a, :b, :c]) it is better to look for..and use always “” and not ‘’

Assorted

● Avoid writing explicit scopes (unless you really really have too...)

Class.where(“table_name.attribute = 1”) Class.where(:attribute => 1)

● Why? ○ ARel sanitizes everything ○ ARel gets the table names and attribute names from

the table structure by itself○ (very important!) ARel quotes the table name,

attribute name and value all by himself (something we seldom do).

● Implicitly protects us from SQL injection, table name changing and usage of SQL reserved words.

Questions?

Check the documentation, and more importantly, pay attention to your development log noise.

More Hints?

Recommended