diff options
118 files changed, 1194 insertions, 834 deletions
diff --git a/.travis.yml b/.travis.yml index 930fc8f583..18b0100c62 100644 --- a/.travis.yml +++ b/.travis.yml @@ -22,7 +22,7 @@ notifications: on_success: change on_failure: always rooms: - - secure: "qDX+Bw/tsg0ogYX0y/kTz5d326BL4JfjZQ66my2VdmRNwHyvv+mrONJ627Kt\n/U6HPA2FtSIMhtYWezi6qtGOKqZ3rns9B3gGGuaLgoCKLtCK2xzdR+OmNaNp\nXpuwGod2uABnpaDGDRYhsJ7X/efct1rk/C//S6s2b6rWKHNQd8k=" + - secure: "YA1alef1ESHWGFNVwvmVGCkMe4cUy4j+UcNvMUESraceiAfVyRMAovlQBGs6\n9kBRm7DHYBUXYC2ABQoJbQRLDr/1B5JPf/M8+Qd7BKu8tcDC03U01SMHFLpO\naOs/HLXcDxtnnpL07tGVsm0zhMc5N8tq4/L3SHxK7Vi+TacwQzI=" bundler_args: --path vendor/bundle matrix: allow_failures: diff --git a/RELEASING_RAILS.rdoc b/RELEASING_RAILS.rdoc index af1def223a..9af79f73e2 100644 --- a/RELEASING_RAILS.rdoc +++ b/RELEASING_RAILS.rdoc @@ -164,6 +164,9 @@ Today, do this stuff in this order: * Update RAILS_VERSION to remove the rc * Build and test the gem * Release the gems +* If releasing a new stable version: + - Trigger stable docs generation (see below) + - Update the version in the home page * Email security lists * Email general announcement lists diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 31005c9d9b..dcf13e927c 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -284,12 +284,12 @@ module ActionMailer # # = Callbacks # - # You can specify callbacks using before_filter and after_filter for configuring your messages. + # You can specify callbacks using before_action and after_action for configuring your messages. # This may be useful, for example, when you want to add default inline attachments for all # messages sent out by a certain mailer class: # # class Notifier < ActionMailer::Base - # before_filter :add_inline_attachment! + # before_action :add_inline_attachment! # # def welcome # mail @@ -306,8 +306,8 @@ module ActionMailer # can define and configure callbacks in the same manner that you would use callbacks in # classes that inherit from ActionController::Base. # - # Note that unless you have a specific reason to do so, you should prefer using before_filter - # rather than after_filter in your ActionMailer classes so that headers are parsed properly. + # Note that unless you have a specific reason to do so, you should prefer using before_action + # rather than after_action in your ActionMailer classes so that headers are parsed properly. # # = Configuration options # diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 3c21886502..4a608a9ad4 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -584,9 +584,9 @@ class BaseTest < ActiveSupport::TestCase assert_equal("Thanks for signing up this afternoon", mail.subject) end - test "modifying the mail message with a before_filter" do - class BeforeFilterMailer < ActionMailer::Base - before_filter :add_special_header! + test "modifying the mail message with a before_action" do + class BeforeActionMailer < ActionMailer::Base + before_action :add_special_header! def welcome ; mail ; end @@ -596,12 +596,12 @@ class BaseTest < ActiveSupport::TestCase end end - assert_equal('Wow, so special', BeforeFilterMailer.welcome['X-Special-Header'].to_s) + assert_equal('Wow, so special', BeforeActionMailer.welcome['X-Special-Header'].to_s) end - test "modifying the mail message with an after_filter" do - class AfterFilterMailer < ActionMailer::Base - after_filter :add_special_header! + test "modifying the mail message with an after_action" do + class AfterActionMailer < ActionMailer::Base + after_action :add_special_header! def welcome ; mail ; end @@ -611,12 +611,12 @@ class BaseTest < ActiveSupport::TestCase end end - assert_equal('Testing', AfterFilterMailer.welcome['X-Special-Header'].to_s) + assert_equal('Testing', AfterActionMailer.welcome['X-Special-Header'].to_s) end - test "adding an inline attachment using a before_filter" do + test "adding an inline attachment using a before_action" do class DefaultInlineAttachmentMailer < ActionMailer::Base - before_filter :add_inline_attachment! + before_action :add_inline_attachment! def welcome ; mail ; end diff --git a/actionmailer/test/fixtures/base_test/after_filter_mailer/welcome.html.erb b/actionmailer/test/fixtures/base_test/after_action_mailer/welcome.html.erb index e69de29bb2..e69de29bb2 100644 --- a/actionmailer/test/fixtures/base_test/after_filter_mailer/welcome.html.erb +++ b/actionmailer/test/fixtures/base_test/after_action_mailer/welcome.html.erb diff --git a/actionmailer/test/fixtures/base_test/before_filter_mailer/welcome.html.erb b/actionmailer/test/fixtures/base_test/before_action_mailer/welcome.html.erb index e69de29bb2..e69de29bb2 100644 --- a/actionmailer/test/fixtures/base_test/before_filter_mailer/welcome.html.erb +++ b/actionmailer/test/fixtures/base_test/before_action_mailer/welcome.html.erb diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index b57408ede3..9db30c8cf7 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,4 +1,31 @@ ## Rails 4.0.0 (unreleased) ## + +* Rename all action callbacks from *_filter to *_action to avoid the misconception that these + callbacks are only suited for transforming or halting the response. With the new style, + it's more inviting to use them as they were intended, like setting shared ivars for views. + + Example: + + class PeopleController < ActionController::Base + before_action :set_person, except: [ :index, :new, :create ] + before_action :ensure_permission, only: [ :edit, :update ] + + ... + + private + def set_person + @person = current_account.people.find(params[:id]) + end + + def ensure_permission + current_person.can_change?(@person) + end + end + + The old *_filter methods still work with no deprecation notice. + + *DHH* + * Add :if / :unless conditions to fragment cache: <%= cache @model, if: some_condition(@model) do %> diff --git a/actionpack/lib/abstract_controller/callbacks.rb b/actionpack/lib/abstract_controller/callbacks.rb index 02ac111392..599fff81c2 100644 --- a/actionpack/lib/abstract_controller/callbacks.rb +++ b/actionpack/lib/abstract_controller/callbacks.rb @@ -40,19 +40,22 @@ module AbstractController end end - # Skip before, after, and around filters matching any of the names + # Skip before, after, and around action callbacks matching any of the names + # Aliased as skip_filter. # # ==== Parameters # * <tt>names</tt> - A list of valid names that could be used for # callbacks. Note that skipping uses Ruby equality, so it's # impossible to skip a callback defined using an anonymous proc # using #skip_filter - def skip_filter(*names) - skip_before_filter(*names) - skip_after_filter(*names) - skip_around_filter(*names) + def skip_action_callback(*names) + skip_before_action(*names) + skip_after_action(*names) + skip_around_action(*names) end + alias_method :skip_filter, :skip_action_callback + # Take callback names and an optional callback proc, normalize them, # then call the block with each callback. This allows us to abstract # the normalization across several methods that use it. @@ -75,119 +78,138 @@ module AbstractController end ## - # :method: before_filter + # :method: before_action # - # :call-seq: before_filter(names, block) + # :call-seq: before_action(names, block) # - # Append a before filter. See _insert_callbacks for parameter details. + # Append a callback before actions. See _insert_callbacks for parameter details. + # Aliased as before_filter. ## - # :method: prepend_before_filter + # :method: prepend_before_action # - # :call-seq: prepend_before_filter(names, block) + # :call-seq: prepend_before_action(names, block) # - # Prepend a before filter. See _insert_callbacks for parameter details. + # Prepend a callback before actions. See _insert_callbacks for parameter details. + # Aliased as prepend_before_filter. ## - # :method: skip_before_filter + # :method: skip_before_action # - # :call-seq: skip_before_filter(names) + # :call-seq: skip_before_action(names) # - # Skip a before filter. See _insert_callbacks for parameter details. + # Skip a callback before actions. See _insert_callbacks for parameter details. + # Aliased as skip_before_filter. ## - # :method: append_before_filter + # :method: append_before_action # - # :call-seq: append_before_filter(names, block) + # :call-seq: append_before_action(names, block) # - # Append a before filter. See _insert_callbacks for parameter details. + # Append a callback before actions. See _insert_callbacks for parameter details. + # Aliased as append_before_filter. ## - # :method: after_filter + # :method: after_action # - # :call-seq: after_filter(names, block) + # :call-seq: after_action(names, block) # - # Append an after filter. See _insert_callbacks for parameter details. + # Append a callback after actions. See _insert_callbacks for parameter details. + # Aliased as after_filter. ## - # :method: prepend_after_filter + # :method: prepend_after_action # - # :call-seq: prepend_after_filter(names, block) + # :call-seq: prepend_after_action(names, block) # - # Prepend an after filter. See _insert_callbacks for parameter details. + # Prepend a callback after actions. See _insert_callbacks for parameter details. + # Aliased as prepend_after_filter. ## - # :method: skip_after_filter + # :method: skip_after_action # - # :call-seq: skip_after_filter(names) + # :call-seq: skip_after_action(names) # - # Skip an after filter. See _insert_callbacks for parameter details. + # Skip a callback after actions. See _insert_callbacks for parameter details. + # Aliased as skip_after_filter. ## - # :method: append_after_filter + # :method: append_after_action # - # :call-seq: append_after_filter(names, block) + # :call-seq: append_after_action(names, block) # - # Append an after filter. See _insert_callbacks for parameter details. + # Append a callback after actions. See _insert_callbacks for parameter details. + # Aliased as append_after_filter. ## - # :method: around_filter + # :method: around_action # - # :call-seq: around_filter(names, block) + # :call-seq: around_action(names, block) # - # Append an around filter. See _insert_callbacks for parameter details. + # Append a callback around actions. See _insert_callbacks for parameter details. + # Aliased as around_filter. ## - # :method: prepend_around_filter + # :method: prepend_around_action # - # :call-seq: prepend_around_filter(names, block) + # :call-seq: prepend_around_action(names, block) # - # Prepend an around filter. See _insert_callbacks for parameter details. + # Prepend a callback around actions. See _insert_callbacks for parameter details. + # Aliased as prepend_around_filter. ## - # :method: skip_around_filter + # :method: skip_around_action # - # :call-seq: skip_around_filter(names) + # :call-seq: skip_around_action(names) # - # Skip an around filter. See _insert_callbacks for parameter details. + # Skip a callback around actions. See _insert_callbacks for parameter details. + # Aliased as skip_around_filter. ## - # :method: append_around_filter + # :method: append_around_action # - # :call-seq: append_around_filter(names, block) + # :call-seq: append_around_action(names, block) # - # Append an around filter. See _insert_callbacks for parameter details. + # Append a callback around actions. See _insert_callbacks for parameter details. + # Aliased as append_around_filter. - # set up before_filter, prepend_before_filter, skip_before_filter, etc. + # set up before_action, prepend_before_action, skip_before_action, etc. # for each of before, after, and around. - [:before, :after, :around].each do |filter| + [:before, :after, :around].each do |callback| class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 - # Append a before, after or around filter. See _insert_callbacks + # Append a before, after or around callback. See _insert_callbacks # for details on the allowed parameters. - def #{filter}_filter(*names, &blk) # def before_filter(*names, &blk) - _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options| - set_callback(:process_action, :#{filter}, name, options) # set_callback(:process_action, :before, name, options) - end # end - end # end + def #{callback}_action(*names, &blk) # def before_action(*names, &blk) + _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options| + set_callback(:process_action, :#{callback}, name, options) # set_callback(:process_action, :before, name, options) + end # end + end # end + + alias_method :#{callback}_filter, :#{callback}_action - # Prepend a before, after or around filter. See _insert_callbacks + # Prepend a before, after or around callback. See _insert_callbacks # for details on the allowed parameters. - def prepend_#{filter}_filter(*names, &blk) # def prepend_before_filter(*names, &blk) - _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options| - set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true)) - end # end - end # end + def prepend_#{callback}_action(*names, &blk) # def prepend_before_action(*names, &blk) + _insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options| + set_callback(:process_action, :#{callback}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true)) + end # end + end # end + + alias_method :prepend_#{callback}_filter, :prepend_#{callback}_action - # Skip a before, after or around filter. See _insert_callbacks + # Skip a before, after or around callback. See _insert_callbacks # for details on the allowed parameters. - def skip_#{filter}_filter(*names) # def skip_before_filter(*names) - _insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options| - skip_callback(:process_action, :#{filter}, name, options) # skip_callback(:process_action, :before, name, options) - end # end - end # end - - # *_filter is the same as append_*_filter - alias_method :append_#{filter}_filter, :#{filter}_filter # alias_method :append_before_filter, :before_filter + def skip_#{callback}_action(*names) # def skip_before_action(*names) + _insert_callbacks(names) do |name, options| # _insert_callbacks(names) do |name, options| + skip_callback(:process_action, :#{callback}, name, options) # skip_callback(:process_action, :before, name, options) + end # end + end # end + + alias_method :skip_#{callback}_filter, :skip_#{callback}_action + + # *_action is the same as append_*_action + alias_method :append_#{callback}_action, :#{callback}_action # alias_method :append_before_action, :before_action + alias_method :append_#{callback}_filter, :#{callback}_action # alias_method :append_before_filter, :before_action RUBY_EVAL end end diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb index c38d8ccef3..f1e8714a86 100644 --- a/actionpack/lib/action_controller/metal/force_ssl.rb +++ b/actionpack/lib/action_controller/metal/force_ssl.rb @@ -32,14 +32,14 @@ module ActionController # ==== Options # * <tt>host</tt> - Redirect to a different host name # * <tt>only</tt> - The callback should be run only for this action - # * <tt>except</tt> - The callback should be run for all actions except this action + # * <tt>except</tt> - The callback should be run for all actions except this action # * <tt>if</tt> - A symbol naming an instance method or a proc; the callback # will be called only when it returns a true value. # * <tt>unless</tt> - A symbol naming an instance method or a proc; the callback # will be called only when it returns a false value. def force_ssl(options = {}) host = options.delete(:host) - before_filter(options) do + before_action(options) do force_ssl_redirect(host) end end diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index d3b5bafee1..283f6413ec 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -25,7 +25,7 @@ module ActionController # the regular HTML interface is protected by a session approach: # # class ApplicationController < ActionController::Base - # before_filter :set_account, :authenticate + # before_action :set_account, :authenticate # # protected # def set_account @@ -68,7 +68,7 @@ module ActionController module ClassMethods def http_basic_authenticate_with(options = {}) - before_filter(options.except(:name, :password, :realm)) do + before_action(options.except(:name, :password, :realm)) do authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password| name == options[:name] && password == options[:password] end @@ -124,7 +124,7 @@ module ActionController # USERS = {"dhh" => "secret", #plain text password # "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":"))} #ha1 digest password # - # before_filter :authenticate, except: [:index] + # before_action :authenticate, except: [:index] # # def index # render text: "Everyone can see me!" @@ -317,7 +317,7 @@ module ActionController # class PostsController < ApplicationController # TOKEN = "secret" # - # before_filter :authenticate, except: [ :index ] + # before_action :authenticate, except: [ :index ] # # def index # render text: "Everyone can see me!" @@ -340,7 +340,7 @@ module ActionController # the regular HTML interface is protected by a session approach: # # class ApplicationController < ActionController::Base - # before_filter :set_account, :authenticate + # before_action :set_account, :authenticate # # protected # def set_account diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 265ce5d6f3..c5db0cb0d4 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -19,7 +19,7 @@ module ActionController #:nodoc: # # class ApplicationController < ActionController::Base # protect_from_forgery - # skip_before_filter :verify_authenticity_token, if: :json_request? + # skip_before_action :verify_authenticity_token, if: :json_request? # # protected # @@ -66,15 +66,15 @@ module ActionController #:nodoc: # # You can disable csrf protection on controller-by-controller basis: # - # skip_before_filter :verify_authenticity_token + # skip_before_action :verify_authenticity_token # # It can also be disabled for specific controller actions: # - # skip_before_filter :verify_authenticity_token, except: [:create] + # skip_before_action :verify_authenticity_token, except: [:create] # # Valid Options: # - # * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified. + # * <tt>:only/:except</tt> - Passed to the <tt>before_action</tt> call. Set which actions are verified. # * <tt>:with</tt> - Set the method to handle unverified request. # # Valid unverified request handling methods are: @@ -84,7 +84,7 @@ module ActionController #:nodoc: def protect_from_forgery(options = {}) include protection_method_module(options[:with] || :null_session) self.request_forgery_protection_token ||= :authenticity_token - prepend_before_filter :verify_authenticity_token, options + prepend_before_action :verify_authenticity_token, options end private @@ -152,7 +152,7 @@ module ActionController #:nodoc: end protected - # The actual before_filter that is used. Modify this to change how you handle unverified requests. + # The actual before_action that is used. Modify this to change how you handle unverified requests. def verify_authenticity_token unless verified_request? logger.warn "Can't verify CSRF token authenticity" if logger diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb index 0f98e84788..57660e93c4 100644 --- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb +++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb @@ -68,7 +68,7 @@ module ActionDispatch # that are not controlled by the extension. # # class ApplicationController < ActionController::Base - # before_filter :adjust_format_for_iphone + # before_action :adjust_format_for_iphone # # private # def adjust_format_for_iphone @@ -87,7 +87,7 @@ module ActionDispatch # to the :html format. # # class ApplicationController < ActionController::Base - # before_filter :adjust_format_for_iphone_with_html_fallback + # before_action :adjust_format_for_iphone_with_html_fallback # # private # def adjust_format_for_iphone_with_html_fallback diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 17386a57b8..5710d1fc02 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -775,8 +775,8 @@ module ActionView # text_field(:post, :title, class: "create_input") # # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" /> # - # text_field(:session, :user, onchange: "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }") - # # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange = "if $('session[user]').value == 'admin' { alert('Your login can not be admin!'); }"/> + # text_field(:session, :user, onchange: "if $('#session_user').value == 'admin' { alert('Your login can not be admin!'); }") + # # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange = "if $('#session_user').value == 'admin' { alert('Your login can not be admin!'); }"/> # # text_field(:snippet, :code, size: 20, class: 'code_input') # # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" /> @@ -830,13 +830,25 @@ module ActionView # # Using this method inside a +form_for+ block will set the enclosing form's encoding to <tt>multipart/form-data</tt>. # + # ==== Options + # * Creates standard HTML attributes for the tag. + # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input. + # * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files. + # * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations. + # # ==== Examples # file_field(:user, :avatar) # # => <input type="file" id="user_avatar" name="user[avatar]" /> # + # file_field(:post, :image, :multiple => true) + # # => <input type="file" id="post_image" name="post[image]" multiple="true" /> + # # file_field(:post, :attached, accept: 'text/html') # # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" /> # + # file_field(:post, :image, accept: 'image/png,image/gif,image/jpeg') + # # => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" /> + # # file_field(:attachment, :file, class: 'file_input') # # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" /> def file_field(object_name, method, options = {}) diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index e298751062..ff83ef3ca1 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -233,6 +233,8 @@ module ActionView # ==== Options # * Creates standard HTML attributes for the tag. # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input. + # * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files. + # * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations. # # ==== Examples # file_field_tag 'attachment' diff --git a/actionpack/test/abstract/callbacks_test.rb b/actionpack/test/abstract/callbacks_test.rb index 5d1a703c55..1090af3060 100644 --- a/actionpack/test/abstract/callbacks_test.rb +++ b/actionpack/test/abstract/callbacks_test.rb @@ -28,9 +28,9 @@ module AbstractController end class Callback2 < ControllerWithCallbacks - before_filter :first - after_filter :second - around_filter :aroundz + before_action :first + after_action :second + around_action :aroundz def first @text = "Hello world" @@ -53,7 +53,7 @@ module AbstractController end class Callback2Overwrite < Callback2 - before_filter :first, :except => :index + before_action :first, except: :index end class TestCallbacks2 < ActiveSupport::TestCase @@ -61,22 +61,22 @@ module AbstractController @controller = Callback2.new end - test "before_filter works" do + test "before_action works" do @controller.process(:index) assert_equal "Hello world", @controller.response_body end - test "after_filter works" do + test "after_action works" do @controller.process(:index) assert_equal "Goodbye", @controller.instance_variable_get("@second") end - test "around_filter works" do + test "around_action works" do @controller.process(:index) assert_equal "FIRSTSECOND", @controller.instance_variable_get("@aroundz") end - test "before_filter with overwritten condition" do + test "before_action with overwritten condition" do @controller = Callback2Overwrite.new @controller.process(:index) assert_equal "", @controller.response_body @@ -84,11 +84,11 @@ module AbstractController end class Callback3 < ControllerWithCallbacks - before_filter do |c| + before_action do |c| c.instance_variable_set("@text", "Hello world") end - after_filter do |c| + after_action do |c| c.instance_variable_set("@second", "Goodbye") end @@ -102,20 +102,20 @@ module AbstractController @controller = Callback3.new end - test "before_filter works with procs" do + test "before_action works with procs" do @controller.process(:index) assert_equal "Hello world", @controller.response_body end - test "after_filter works with procs" do + test "after_action works with procs" do @controller.process(:index) assert_equal "Goodbye", @controller.instance_variable_get("@second") end end class CallbacksWithConditions < ControllerWithCallbacks - before_filter :list, :only => :index - before_filter :authenticate, :except => :index + before_action :list, :only => :index + before_action :authenticate, :except => :index def index self.response_body = @list.join(", ") @@ -141,25 +141,25 @@ module AbstractController @controller = CallbacksWithConditions.new end - test "when :only is specified, a before filter is triggered on that action" do + test "when :only is specified, a before action is triggered on that action" do @controller.process(:index) assert_equal "Hello, World", @controller.response_body end - test "when :only is specified, a before filter is not triggered on other actions" do + test "when :only is specified, a before action is not triggered on other actions" do @controller.process(:sekrit_data) assert_equal "true", @controller.response_body end - test "when :except is specified, an after filter is not triggered on that action" do + test "when :except is specified, an after action is not triggered on that action" do @controller.process(:index) assert !@controller.instance_variable_defined?("@authenticated") end end class CallbacksWithArrayConditions < ControllerWithCallbacks - before_filter :list, :only => [:index, :listy] - before_filter :authenticate, :except => [:index, :listy] + before_action :list, only: [:index, :listy] + before_action :authenticate, except: [:index, :listy] def index self.response_body = @list.join(", ") @@ -185,24 +185,24 @@ module AbstractController @controller = CallbacksWithArrayConditions.new end - test "when :only is specified with an array, a before filter is triggered on that action" do + test "when :only is specified with an array, a before action is triggered on that action" do @controller.process(:index) assert_equal "Hello, World", @controller.response_body end - test "when :only is specified with an array, a before filter is not triggered on other actions" do + test "when :only is specified with an array, a before action is not triggered on other actions" do @controller.process(:sekrit_data) assert_equal "true", @controller.response_body end - test "when :except is specified with an array, an after filter is not triggered on that action" do + test "when :except is specified with an array, an after action is not triggered on that action" do @controller.process(:index) assert !@controller.instance_variable_defined?("@authenticated") end end class ChangedConditions < Callback2 - before_filter :first, :only => :index + before_action :first, :only => :index def not_index @text ||= nil @@ -227,7 +227,7 @@ module AbstractController end class SetsResponseBody < ControllerWithCallbacks - before_filter :set_body + before_action :set_body def index self.response_body = "Fail" @@ -266,6 +266,50 @@ module AbstractController end end + class AliasedCallbacks < ControllerWithCallbacks + before_filter :first + after_filter :second + around_filter :aroundz + + def first + @text = "Hello world" + end + + def second + @second = "Goodbye" + end + + def aroundz + @aroundz = "FIRST" + yield + @aroundz << "SECOND" + end + def index + @text ||= nil + self.response_body = @text.to_s + end + end + + class TestAliasedCallbacks < ActiveSupport::TestCase + def setup + @controller = AliasedCallbacks.new + end + + test "before_filter works" do + @controller.process(:index) + assert_equal "Hello world", @controller.response_body + end + + test "after_filter works" do + @controller.process(:index) + assert_equal "Goodbye", @controller.instance_variable_get("@second") + end + + test "around_filter works" do + @controller.process(:index) + assert_equal "FIRSTSECOND", @controller.instance_variable_get("@aroundz") + end + end end end diff --git a/actionpack/test/controller/default_url_options_with_filter_test.rb b/actionpack/test/controller/default_url_options_with_before_action_test.rb index 9a9ab17fee..656fd0431e 100644 --- a/actionpack/test/controller/default_url_options_with_filter_test.rb +++ b/actionpack/test/controller/default_url_options_with_before_action_test.rb @@ -1,10 +1,10 @@ require 'abstract_unit' -class ControllerWithBeforeFilterAndDefaultUrlOptions < ActionController::Base +class ControllerWithBeforeActionAndDefaultUrlOptions < ActionController::Base - before_filter { I18n.locale = params[:locale] } - after_filter { I18n.locale = "en" } + before_action { I18n.locale = params[:locale] } + after_action { I18n.locale = "en" } def target render :text => "final response" @@ -19,11 +19,11 @@ class ControllerWithBeforeFilterAndDefaultUrlOptions < ActionController::Base end end -class ControllerWithBeforeFilterAndDefaultUrlOptionsTest < ActionController::TestCase +class ControllerWithBeforeActionAndDefaultUrlOptionsTest < ActionController::TestCase # This test has its roots in issue #1872 test "should redirect with correct locale :de" do get :redirect, :locale => "de" - assert_redirected_to "/controller_with_before_filter_and_default_url_options/target?locale=de" + assert_redirected_to "/controller_with_before_action_and_default_url_options/target?locale=de" end end diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb index 6414ba3994..9d4356f546 100644 --- a/actionpack/test/controller/flash_test.rb +++ b/actionpack/test/controller/flash_test.rb @@ -53,8 +53,8 @@ class FlashTest < ActionController::TestCase render :inline => "hello" end - # methods for test_sweep_after_halted_filter_chain - before_filter :halt_and_redir, :only => "filter_halting_action" + # methods for test_sweep_after_halted_action_chain + before_action :halt_and_redir, only: 'filter_halting_action' def std_action @flash_copy = {}.update(flash) @@ -159,7 +159,7 @@ class FlashTest < ActionController::TestCase assert_nil session["flash"] end - def test_sweep_after_halted_filter_chain + def test_sweep_after_halted_action_chain get :std_action assert_nil assigns["flash_copy"]["foo"] get :filter_halting_action diff --git a/actionpack/test/controller/http_basic_authentication_test.rb b/actionpack/test/controller/http_basic_authentication_test.rb index 2dcfda02a7..90548d4294 100644 --- a/actionpack/test/controller/http_basic_authentication_test.rb +++ b/actionpack/test/controller/http_basic_authentication_test.rb @@ -2,9 +2,9 @@ require 'abstract_unit' class HttpBasicAuthenticationTest < ActionController::TestCase class DummyController < ActionController::Base - before_filter :authenticate, :only => :index - before_filter :authenticate_with_request, :only => :display - before_filter :authenticate_long_credentials, :only => :show + before_action :authenticate, only: :index + before_action :authenticate_with_request, only: :display + before_action :authenticate_long_credentials, only: :show http_basic_authenticate_with :name => "David", :password => "Goliath", :only => :search diff --git a/actionpack/test/controller/http_digest_authentication_test.rb b/actionpack/test/controller/http_digest_authentication_test.rb index c4a94264c3..537de7a2dd 100644 --- a/actionpack/test/controller/http_digest_authentication_test.rb +++ b/actionpack/test/controller/http_digest_authentication_test.rb @@ -4,8 +4,8 @@ require 'active_support/key_generator' class HttpDigestAuthenticationTest < ActionController::TestCase class DummyDigestController < ActionController::Base - before_filter :authenticate, :only => :index - before_filter :authenticate_with_request, :only => :display + before_action :authenticate, only: :index + before_action :authenticate_with_request, only: :display USERS = { 'lifo' => 'world', 'pretty' => 'please', 'dhh' => ::Digest::MD5::hexdigest(["dhh","SuperSecret","secret"].join(":"))} diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb index ad4e743be8..8a409d6ed2 100644 --- a/actionpack/test/controller/http_token_authentication_test.rb +++ b/actionpack/test/controller/http_token_authentication_test.rb @@ -2,9 +2,9 @@ require 'abstract_unit' class HttpTokenAuthenticationTest < ActionController::TestCase class DummyController < ActionController::Base - before_filter :authenticate, :only => :index - before_filter :authenticate_with_request, :only => :display - before_filter :authenticate_long_credentials, :only => :show + before_action :authenticate, only: :index + before_action :authenticate_with_request, only: :display + before_action :authenticate_long_credentials, only: :show def index render :text => "Hello Secret" diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb index d8b971f963..929545fc10 100644 --- a/actionpack/test/controller/log_subscriber_test.rb +++ b/actionpack/test/controller/log_subscriber_test.rb @@ -13,7 +13,7 @@ module Another head :status => 406 end - before_filter :redirector, :only => :never_executed + before_action :redirector, only: :never_executed def never_executed end diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index d183b0be17..ed013e2185 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -1139,7 +1139,7 @@ end # For testing layouts which are set automatically class PostController < AbstractPostController - around_filter :with_iphone + around_action :with_iphone def index respond_to(:html, :iphone, :js) diff --git a/actionpack/test/controller/new_base/base_test.rb b/actionpack/test/controller/new_base/base_test.rb index ed244513a5..964f22eb03 100644 --- a/actionpack/test/controller/new_base/base_test.rb +++ b/actionpack/test/controller/new_base/base_test.rb @@ -3,7 +3,7 @@ require 'abstract_unit' # Tests the controller dispatching happy path module Dispatching class SimpleController < ActionController::Base - before_filter :authenticate + before_action :authenticate def index render :text => "success" diff --git a/actionpack/test/controller/new_base/render_context_test.rb b/actionpack/test/controller/new_base/render_context_test.rb index f41b14d5d6..177a1c088d 100644 --- a/actionpack/test/controller/new_base/render_context_test.rb +++ b/actionpack/test/controller/new_base/render_context_test.rb @@ -14,7 +14,7 @@ module RenderContext include ActionView::Context # 2) Call _prepare_context that will do the required initialization - before_filter :_prepare_context + before_action :_prepare_context def hello_world @value = "Hello" diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 859ed1466b..7640bc12a2 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -37,7 +37,7 @@ end class TestController < ActionController::Base protect_from_forgery - before_filter :set_variable_for_layout + before_action :set_variable_for_layout class LabellingFormBuilder < ActionView::Helpers::FormBuilder end @@ -137,7 +137,7 @@ class TestController < ActionController::Base def conditional_hello_with_bangs render :action => 'hello_world' end - before_filter :handle_last_modified_and_etags, :only=>:conditional_hello_with_bangs + before_action :handle_last_modified_and_etags, :only=>:conditional_hello_with_bangs def handle_last_modified_and_etags fresh_when(:last_modified => Time.now.utc.beginning_of_day, :etag => [ :foo, 123 ]) @@ -710,7 +710,7 @@ class TestController < ActionController::Base render :action => "calling_partial_with_layout", :layout => "layouts/partial_with_layout" end - before_filter :only => :render_with_filters do + before_action only: :render_with_filters do request.format = :xml end diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb index 48e2d6491e..4898b0c57f 100644 --- a/actionpack/test/controller/rescue_test.rb +++ b/actionpack/test/controller/rescue_test.rb @@ -68,9 +68,9 @@ class RescueController < ActionController::Base render :text => 'io error' end - before_filter(:only => :before_filter_raises) { raise 'umm nice' } + before_action(only: :before_action_raises) { raise 'umm nice' } - def before_filter_raises + def before_action_raises end def raises diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb index 718d06ef38..888791b874 100644 --- a/actionpack/test/controller/show_exceptions_test.rb +++ b/actionpack/test/controller/show_exceptions_test.rb @@ -5,7 +5,7 @@ module ShowExceptions use ActionDispatch::ShowExceptions, ActionDispatch::PublicExceptions.new("#{FIXTURE_LOAD_PATH}/public") use ActionDispatch::DebugExceptions - before_filter :only => :another_boom do + before_action only: :another_boom do request.env["action_dispatch.show_detailed_exceptions"] = true end diff --git a/actionpack/test/controller/view_paths_test.rb b/actionpack/test/controller/view_paths_test.rb index 40f6dc6f0f..c6e7a523b9 100644 --- a/actionpack/test/controller/view_paths_test.rb +++ b/actionpack/test/controller/view_paths_test.rb @@ -4,7 +4,7 @@ class ViewLoadPathsTest < ActionController::TestCase class TestController < ActionController::Base def self.controller_path() "test" end - before_filter :add_view_path, :only => :hello_world_at_request_time + before_action :add_view_path, only: :hello_world_at_request_time def hello_world() end def hello_world_at_request_time() render(:action => 'hello_world') end diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 57612bd3b8..b1988172cb 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,25 +1,53 @@ ## Rails 4.0.0 (unreleased) ## -* Add migration history to schema.rb dump. - Loading schema.rb with full migration history - restores the exact list of migrations that created - that schema (including names and fingerprints). This - avoids possible mistakes caused by assuming all - migrations with a lower version have been run when - loading schema.rb. Old schema.rb files without migration - history but with the :version setting still work as before. - - *Josh Susser* - -* Add metadata columns to schema_migrations table. - New columns are: migrated_at (timestamp), - fingerprint (md5 hash of migration source), and - name (filename minus version and extension) - - *Josh Susser* - -* Fix performance problem with primary_key method in PostgreSQL adapter when having many schemas. - Uses pg_constraint table instead of pg_depend table which has many records in general. +* Session variables can be set for the `mysql`, `mysql2`, and `postgresql` adapters + in the `variables: <hash>` parameter in `database.yml`. The key-value pairs of this + hash will be sent in a `SET key = value` query on new database connections. See also: + http://dev.mysql.com/doc/refman/5.0/en/set-statement.html + http://www.postgresql.org/docs/8.3/static/sql-set.html + + *Aaron Stone* + +* Allow `Relation#where` with no arguments to be chained with new `not` query method. + + Example: + + Developer.where.not(name: 'Aaron') + + *Akira Matsuda* + +* Unscope `update_column(s)` query to ignore default scope. + + When applying `default_scope` to a class with a where clause, using + `update_column(s)` could generate a query that would not properly update + the record due to the where clause from the `default_scope` being applied + to the update query. + + class User < ActiveRecord::Base + default_scope where(active: true) + end + + user = User.first + user.active = false + user.save! + + user.update_column(:active, true) # => false + + In this situation we want to skip the default_scope clause and just + update the record based on the primary key. With this change: + + user.update_column(:active, true) # => true + + Fixes #8436. + + *Carlos Antonio da Silva* + +* SQLite adapter no longer corrupts binary data if the data contains `%00`. + + *Chris Feist* + +* Fix performance problem with `primary_key` method in PostgreSQL adapter when having many schemas. + Uses `pg_constraint` table instead of `pg_depend` table which has many records in general. Fix #8414 *kennyj* @@ -31,8 +59,8 @@ *Yves Senn* * Add STI support to init and building associations. - Allows you to do BaseClass.new(:type => "SubClass") as well as - parent.children.build(:type => "SubClass") or parent.build_child + Allows you to do `BaseClass.new(:type => "SubClass")` as well as + `parent.children.build(:type => "SubClass")` or `parent.build_child` to initialize an STI subclass. Ensures that the class name is a valid class and that it is in the ancestors of the super class that the association is expecting. @@ -1077,11 +1105,11 @@ Note that you do not need to explicitly specify references in the following cases, as they can be automatically inferred: - Post.where(comments: { name: 'foo' }) - Post.where('comments.name' => 'foo') - Post.order('comments.name') + Post.includes(:comments).where(comments: { name: 'foo' }) + Post.includes(:comments).where('comments.name' => 'foo') + Post.includes(:comments).order('comments.name') - You also do not need to worry about this unless you are doing eager + You do not need to worry about this unless you are doing eager loading. Basically, don't worry unless you see a deprecation warning or (in future releases) an SQL error due to a missing JOIN. diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 7ec6abbc45..73012834c9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -190,16 +190,16 @@ module ActiveRecord # # What can be written like this with the regular calls to column: # - # create_table "products", force: true do |t| - # t.column "shop_id", :integer - # t.column "creator_id", :integer - # t.column "name", :string, default: "Untitled" - # t.column "value", :string, default: "Untitled" - # t.column "created_at", :datetime - # t.column "updated_at", :datetime + # create_table :products do |t| + # t.column :shop_id, :integer + # t.column :creator_id, :integer + # t.column :name, :string, default: "Untitled" + # t.column :value, :string, default: "Untitled" + # t.column :created_at, :datetime + # t.column :updated_at, :datetime # end # - # Can also be written as follows using the short-hand: + # can also be written as follows using the short-hand: # # create_table :products do |t| # t.integer :shop_id, :creator_id diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index a470e8de07..f1e42dfbbe 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -490,8 +490,8 @@ module ActiveRecord sm_table = ActiveRecord::Migrator.schema_migrations_table_name ActiveRecord::SchemaMigration.order('version').map { |sm| - "INSERT INTO #{sm_table} (version, migrated_at, fingerprint, name) VALUES ('#{sm.version}',LOCALTIMESTAMP,'#{sm.fingerprint}','#{sm.name}');" - }.join("\n\n") + "INSERT INTO #{sm_table} (version) VALUES ('#{sm.version}');" + }.join "\n\n" end # Should not be called normally, but this operation is non-destructive. @@ -512,7 +512,7 @@ module ActiveRecord end unless migrated.include?(version) - ActiveRecord::SchemaMigration.create!(:version => version, :migrated_at => Time.now) + execute "INSERT INTO #{sm_table} (version) VALUES ('#{version}')" end inserted = Set.new @@ -520,7 +520,7 @@ module ActiveRecord if inserted.include?(v) raise "Duplicate migration #{v}. Please renumber your migrations to resolve the conflict." elsif v < version - ActiveRecord::SchemaMigration.create!(:version => v, :migrated_at => Time.now) + execute "INSERT INTO #{sm_table} (version) VALUES ('#{v}')" inserted << v end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 84e73e6f0f..d37e489f5c 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -704,6 +704,45 @@ module ActiveRecord end column end + + def configure_connection + variables = @config[:variables] || {} + + # By default, MySQL 'where id is null' selects the last inserted id. + # Turn this off. http://dev.rubyonrails.org/ticket/6778 + variables[:sql_auto_is_null] = 0 + + # Increase timeout so the server doesn't disconnect us. + wait_timeout = @config[:wait_timeout] + wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum) + variables[:wait_timeout] = wait_timeout + + # Make MySQL reject illegal values rather than truncating or blanking them, see + # http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables + # If the user has provided another value for sql_mode, don't replace it. + if strict_mode? && !variables.has_key?(:sql_mode) + variables[:sql_mode] = 'STRICT_ALL_TABLES' + end + + # NAMES does not have an equals sign, see + # http://dev.mysql.com/doc/refman/5.0/en/set-statement.html#id944430 + # (trailing comma because variable_assignments will always have content) + encoding = "NAMES #{@config[:encoding]}, " if @config[:encoding] + + # Gather up all of the SET variables... + variable_assignments = variables.map do |k, v| + if v == ':default' || v == :default + "@@SESSION.#{k.to_s} = DEFAULT" # Sets the value to the global or compile default + elsif !v.nil? + "@@SESSION.#{k.to_s} = #{quote(v)}" + end + # or else nil; compact to clear nils out + end.compact.join(', ') + + # ...and send them all in one query + execute("SET #{encoding} #{variable_assignments}", :skip_logging) + end + end end end diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index f55d19393c..a6013f754a 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -251,27 +251,7 @@ module ActiveRecord def configure_connection @connection.query_options.merge!(:as => :array) - - # By default, MySQL 'where id is null' selects the last inserted id. - # Turn this off. http://dev.rubyonrails.org/ticket/6778 - variable_assignments = ['SQL_AUTO_IS_NULL=0'] - - # Make MySQL reject illegal values rather than truncating or - # blanking them. See - # http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html#sqlmode_strict_all_tables - variable_assignments << "SQL_MODE='STRICT_ALL_TABLES'" if strict_mode? - - encoding = @config[:encoding] - - # make sure we set the encoding - variable_assignments << "NAMES '#{encoding}'" if encoding - - # increase timeout so mysql server doesn't disconnect us - wait_timeout = @config[:wait_timeout] - wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum) - variable_assignments << "@@wait_timeout = #{wait_timeout}" - - execute("SET #{variable_assignments.join(', ')}", :skip_logging) + super end def version diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index e9677415cc..631f646f58 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -51,7 +51,8 @@ module ActiveRecord # * <tt>:database</tt> - The name of the database. No default, must be provided. # * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection. # * <tt>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html). - # * <tt>:strict</tt> - Defaults to true. Enable STRICT_ALL_TABLES. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html) + # * <tt>:strict</tt> - Defaults to true. Enable STRICT_ALL_TABLES. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html) + # * <tt>:variables</tt> - (Optional) A hash session variables to send as `SET @@SESSION.key = value` on each database connection. Use the value `:default` to set a variable to its DEFAULT value. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/set-statement.html). # * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection. # * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection. # * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection. @@ -535,18 +536,10 @@ module ActiveRecord configure_connection end + # Many Rails applications monkey-patch a replacement of the configure_connection method + # and don't call 'super', so leave this here even though it looks superfluous. def configure_connection - encoding = @config[:encoding] - execute("SET NAMES '#{encoding}'", :skip_logging) if encoding - - # By default, MySQL 'where id is null' selects the last inserted id. - # Turn this off. http://dev.rubyonrails.org/ticket/6778 - execute("SET SQL_AUTO_IS_NULL=0", :skip_logging) - - # Make MySQL reject illegal values rather than truncating or - # blanking them. See - # http://dev.mysql.com/doc/refman/5.5/en/server-sql-mode.html#sqlmode_strict_all_tables - execute("SET SQL_MODE='STRICT_ALL_TABLES'", :skip_logging) if strict_mode? + super end def select(sql, name = nil, binds = []) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index e18464fa35..e24ee1efdd 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -24,7 +24,7 @@ module ActiveRecord # Forward any unused config params to PGconn.connect. [:statement_limit, :encoding, :min_messages, :schema_search_path, :schema_order, :adapter, :pool, :checkout_timeout, :template, - :reaping_frequency, :insert_returning].each do |key| + :reaping_frequency, :insert_returning, :variables].each do |key| conn_params.delete key end conn_params.delete_if { |k,v| v.nil? } @@ -238,6 +238,8 @@ module ActiveRecord # <encoding></tt> call on the connection. # * <tt>:min_messages</tt> - An optional client min messages that is used in a # <tt>SET client_min_messages TO <min_messages></tt> call on the connection. + # * <tt>:variables</tt> - An optional hash of additional parameters that + # will be used in <tt>SET SESSION key = val</tt> calls on the connection. # * <tt>:insert_returning</tt> - An optional boolean to control the use or <tt>RETURNING</tt> for <tt>INSERT</tt> statements # defaults to true. # @@ -706,11 +708,24 @@ module ActiveRecord # If using Active Record's time zone support configure the connection to return # TIMESTAMP WITH ZONE types in UTC. + # (SET TIME ZONE does not use an equals sign like other SET variables) if ActiveRecord::Base.default_timezone == :utc execute("SET time zone 'UTC'", 'SCHEMA') elsif @local_tz execute("SET time zone '#{@local_tz}'", 'SCHEMA') end + + # SET statements from :variables config hash + # http://www.postgresql.org/docs/8.3/static/sql-set.html + variables = @config[:variables] || {} + variables.map do |k, v| + if v == ':default' || v == :default + # Sets the value to the global or compile default + execute("SET SESSION #{k.to_s} TO DEFAULT", 'SCHEMA') + elsif !v.nil? + execute("SET SESSION #{k.to_s} TO #{quote(v)}", 'SCHEMA') + end + end end # Returns the current ID of a table's sequence. diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 4ce276d4bf..22347fcaef 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1,6 +1,5 @@ require "active_support/core_ext/class/attribute_accessors" require 'set' -require 'digest/md5' module ActiveRecord # Exception that can be raised to stop migrations from going backwards. @@ -555,10 +554,6 @@ module ActiveRecord delegate :migrate, :announce, :write, :to => :migration - def fingerprint - @fingerprint ||= Digest::MD5.hexdigest(File.read(filename)) - end - private def migration @@ -729,7 +724,7 @@ module ActiveRecord raise UnknownMigrationVersionError.new(@target_version) if target.nil? unless (up? && migrated.include?(target.version.to_i)) || (down? && !migrated.include?(target.version.to_i)) target.migrate(@direction) - record_version_state_after_migrating(target) + record_version_state_after_migrating(target.version) end end @@ -752,7 +747,7 @@ module ActiveRecord begin ddl_transaction do migration.migrate(@direction) - record_version_state_after_migrating(migration) + record_version_state_after_migrating(migration.version) end rescue => e canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : "" @@ -810,18 +805,13 @@ module ActiveRecord raise DuplicateMigrationVersionError.new(version) if version end - def record_version_state_after_migrating(target) + def record_version_state_after_migrating(version) if down? - migrated.delete(target.version) - ActiveRecord::SchemaMigration.where(:version => target.version.to_s).delete_all + migrated.delete(version) + ActiveRecord::SchemaMigration.where(:version => version.to_s).delete_all else - migrated << target.version - ActiveRecord::SchemaMigration.create!( - :version => target.version.to_s, - :migrated_at => Time.now, - :fingerprint => target.fingerprint, - :name => File.basename(target.filename,'.rb').gsub(/^\d+_/,'') - ) + migrated << version + ActiveRecord::SchemaMigration.create!(:version => version.to_s) end end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 94c109e72b..4d1a9c94b7 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -259,7 +259,7 @@ module ActiveRecord verify_readonly_attribute(key.to_s) end - updated_count = self.class.where(self.class.primary_key => id).update_all(attributes) + updated_count = self.class.unscoped.where(self.class.primary_key => id).update_all(attributes) attributes.each do |k, v| raw_write_attribute(k, v) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index a480ddec9e..46c0d6206f 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -4,6 +4,51 @@ module ActiveRecord module QueryMethods extend ActiveSupport::Concern + # WhereChain objects act as placeholder for queries in which #where does not have any parameter. + # In this case, #where must be chained with either #not, #like, or #not_like to return a new relation. + class WhereChain + def initialize(scope) + @scope = scope + end + + # Returns a new relation expressing WHERE + NOT condition + # according to the conditions in the arguments. + # + # #not accepts conditions in one of these formats: String, Array, Hash. + # See #where for more details on each format. + # + # User.where.not("name = 'Jon'") + # # SELECT * FROM users WHERE NOT (name = 'Jon') + # + # User.where.not(["name = ?", "Jon"]) + # # SELECT * FROM users WHERE NOT (name = 'Jon') + # + # User.where.not(name: "Jon") + # # SELECT * FROM users WHERE name != 'Jon' + # + # User.where.not(name: nil) + # # SELECT * FROM users WHERE name IS NOT NULL + # + # User.where.not(name: %w(Ko1 Nobu)) + # # SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu') + def not(opts, *rest) + where_value = @scope.send(:build_where, opts, rest).map do |rel| + case rel + when Arel::Nodes::In + Arel::Nodes::NotIn.new(rel.left, rel.right) + when Arel::Nodes::Equality + Arel::Nodes::NotEqual.new(rel.left, rel.right) + when String + Arel::Nodes::Not.new(Arel::Nodes::SqlLiteral.new(rel)) + else + Arel::Nodes::Not.new(rel) + end + end + @scope.where_values += where_value + @scope + end + end + Relation::MULTI_VALUE_METHODS.each do |name| class_eval <<-CODE, __FILE__, __LINE__ + 1 def #{name}_values # def select_values @@ -370,18 +415,41 @@ module ActiveRecord # User.joins(:posts).where({ "posts.published" => true }) # User.joins(:posts).where({ posts: { published: true } }) # - # === empty condition + # === no argument + # + # If no argument is passed, #where returns a new instance of WhereChain, that + # can be chained with #not to return a new relation that negates the where clause. + # + # User.where.not(name: "Jon") + # # SELECT * FROM users WHERE name != 'Jon' + # + # See WhereChain for more details on #not. # - # If the condition returns true for blank?, then where is a no-op and returns the current relation. - def where(opts, *rest) - opts.blank? ? self : spawn.where!(opts, *rest) + # === blank condition + # + # If the condition is any blank-ish object, then #where is a no-op and returns + # the current relation. + def where(opts = :chain, *rest) + if opts == :chain + WhereChain.new(spawn) + elsif opts.blank? + self + else + spawn.where!(opts, *rest) + end end - def where!(opts, *rest) # :nodoc: - references!(PredicateBuilder.references(opts)) if Hash === opts + # #where! is identical to #where, except that instead of returning a new relation, it adds + # the condition to the existing relation. + def where!(opts = :chain, *rest) # :nodoc: + if opts == :chain + WhereChain.new(self) + else + references!(PredicateBuilder.references(opts)) if Hash === opts - self.where_values += build_where(opts, rest) - self + self.where_values += build_where(opts, rest) + self + end end # Allows to specify a HAVING clause. Note that you can't use HAVING diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index 44b7eb424b..3259dbbd80 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -39,45 +39,27 @@ module ActiveRecord end def define(info, &block) # :nodoc: - @using_deprecated_version_setting = info[:version].present? - SchemaMigration.drop_table - initialize_schema_migrations_table - instance_eval(&block) - # handle files from pre-4.0 that used :version option instead of dumping migration table - assume_migrated_upto_version(info[:version], migrations_paths) if @using_deprecated_version_setting + unless info[:version].blank? + initialize_schema_migrations_table + assume_migrated_upto_version(info[:version], migrations_paths) + end end # Eval the given block. All methods available to the current connection # adapter are available within the block, so you can easily use the # database definition DSL to build up your schema (+create_table+, # +add_index+, etc.). - def self.define(info={}, &block) - new.define(info, &block) - end - - # Create schema migration history. Include migration statements in a block to this method. - # - # migrations do - # migration 20121128235959, "44f1397e3b92442ca7488a029068a5ad", "add_horn_color_to_unicorns" - # migration 20121129235959, "4a1eb3965d94406b00002b370854eae8", "add_magic_power_to_unicorns" - # end - def migrations - raise(ArgumentError, "Can't set migrations while using :version option") if @using_deprecated_version_setting - yield - end - - # Add a migration to the ActiveRecord::SchemaMigration table. # - # The +version+ argument is an integer. - # The +fingerprint+ and +name+ arguments are required but may be empty strings. - # The migration's +migrated_at+ attribute is set to the current time, - # instead of being set explicitly as an argument to the method. + # The +info+ hash is optional, and if given is used to define metadata + # about the current schema (currently, only the schema's version): # - # migration 20121129235959, "4a1eb3965d94406b00002b370854eae8", "add_magic_power_to_unicorns" - def migration(version, fingerprint, name) - SchemaMigration.create!(version: version, migrated_at: Time.now, fingerprint: fingerprint, name: name) + # ActiveRecord::Schema.define(version: 20380119000001) do + # ... + # end + def self.define(info={}, &block) + new.define(info, &block) end end end diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 73c0f5b9eb..36bde44e7c 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -24,7 +24,6 @@ module ActiveRecord def dump(stream) header(stream) - migrations(stream) tables(stream) trailer(stream) stream @@ -45,7 +44,7 @@ module ActiveRecord stream.puts "# encoding: #{stream.external_encoding.name}" end - header_text = <<HEADER_RUBY + stream.puts <<HEADER # This file is auto-generated from the current state of the database. Instead # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. @@ -60,25 +59,13 @@ module ActiveRecord ActiveRecord::Schema.define(#{define_params}) do -HEADER_RUBY - stream.puts header_text +HEADER end def trailer(stream) stream.puts "end" end - def migrations(stream) - all_migrations = ActiveRecord::SchemaMigration.all.to_a - if all_migrations.any? - stream.puts(" migrations do") - all_migrations.each do |migration| - stream.puts(migration.schema_line(" ")) - end - stream.puts(" end") - end - end - def tables(stream) @connection.tables.sort.each do |tbl| next if ['schema_migrations', ignore_tables].flatten.any? do |ignored| diff --git a/activerecord/lib/active_record/schema_migration.rb b/activerecord/lib/active_record/schema_migration.rb index b2ae369eb6..9830abe7d8 100644 --- a/activerecord/lib/active_record/schema_migration.rb +++ b/activerecord/lib/active_record/schema_migration.rb @@ -14,38 +14,17 @@ module ActiveRecord end def self.create_table - if connection.table_exists?(table_name) - cols = connection.columns(table_name).collect { |col| col.name } - unless cols.include?("migrated_at") - connection.add_column(table_name, "migrated_at", :datetime) - q_table_name = connection.quote_table_name(table_name) - q_timestamp = connection.quoted_date(Time.now) - connection.update("UPDATE #{q_table_name} SET migrated_at = '#{q_timestamp}' WHERE migrated_at IS NULL") - connection.change_column(table_name, "migrated_at", :datetime, :null => false) - end - unless cols.include?("fingerprint") - connection.add_column(table_name, "fingerprint", :string, :limit => 32) - end - unless cols.include?("name") - connection.add_column(table_name, "name", :string) - end - else + unless connection.table_exists?(table_name) connection.create_table(table_name, :id => false) do |t| t.column :version, :string, :null => false - t.column :migrated_at, :datetime, :null => false - t.column :fingerprint, :string, :limit => 32 - t.column :name, :string end - connection.add_index(table_name, "version", :unique => true, :name => index_name) + connection.add_index table_name, :version, :unique => true, :name => index_name end - reset_column_information end def self.drop_table - if connection.index_exists?(table_name, "version", :unique => true, :name => index_name) - connection.remove_index(table_name, :name => index_name) - end if connection.table_exists?(table_name) + connection.remove_index table_name, :name => index_name connection.drop_table(table_name) end end @@ -53,17 +32,5 @@ module ActiveRecord def version super.to_i end - - # Construct ruby source to include in schema.rb dump for this migration. - # Pass a string of spaces as +indent+ to allow calling code to control how deeply indented the line is. - # The generated line includes the migration version, fingerprint, and name. Either fingerprint or name - # can be an empty string. - # - # Example output: - # - # migration 20121129235959, "ee4be703f9e6e2fc0f4baddebe6eb8f7", "add_magic_power_to_unicorns" - def schema_line(indent) - %Q(#{indent}migration %s, "%s", "%s") % [version, fingerprint, name] - end end end diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb index 534dc2c2df..ffd6904aec 100644 --- a/activerecord/test/cases/adapters/mysql/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql/connection_test.rb @@ -137,6 +137,23 @@ class MysqlConnectionTest < ActiveRecord::TestCase end end + def test_mysql_set_session_variable + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => 3}})) + session_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.DEFAULT_WEEK_FORMAT" + assert_equal 3, session_mode.rows.first.first.to_i + end + end + + def test_mysql_set_session_variable_to_default + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => :default}})) + global_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.DEFAULT_WEEK_FORMAT" + session_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.DEFAULT_WEEK_FORMAT" + assert_equal global_mode.rows, session_mode.rows + end + end + private def run_without_connection diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index 14c22d2519..1265cb927e 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -53,6 +53,23 @@ class MysqlConnectionTest < ActiveRecord::TestCase end end + def test_mysql_set_session_variable + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => 3}})) + session_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.DEFAULT_WEEK_FORMAT" + assert_equal 3, session_mode.rows.first.first.to_i + end + end + + def test_mysql_set_session_variable_to_default + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => :default}})) + global_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.DEFAULT_WEEK_FORMAT" + session_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.DEFAULT_WEEK_FORMAT" + assert_equal global_mode.rows, session_mode.rows + end + end + def test_logs_name_structure_dump @connection.structure_dump assert_equal "SCHEMA", @connection.logged[0][1] diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb index 1ff307c735..fa8f339f00 100644 --- a/activerecord/test/cases/adapters/postgresql/connection_test.rb +++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb @@ -154,5 +154,46 @@ module ActiveRecord end end + def test_set_session_variable_true + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:debug_print_plan => true}})) + set_true = ActiveRecord::Base.connection.exec_query "SHOW DEBUG_PRINT_PLAN" + assert_equal set_true.rows, [["on"]] + end + end + + def test_set_session_variable_false + run_without_connection do |orig_connection| + ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:debug_print_plan => false}})) + set_false = ActiveRecord::Base.connection.exec_query "SHOW DEBUG_PRINT_PLAN" + assert_equal set_false.rows, [["off"]] + end + end + + def test_set_session_variable_nil + run_without_connection do |orig_connection| + # This should be a no-op that does not raise an error + ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:debug_print_plan => nil}})) + end + end + + def test_set_session_variable_default + run_without_connection do |orig_connection| + # This should execute a query that does not raise an error + ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:debug_print_plan => :default}})) + end + end + + private + + def run_without_connection + original_connection = ActiveRecord::Base.remove_connection + begin + yield original_connection + ensure + ActiveRecord::Base.establish_connection(original_connection) + end + end + end end diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index bd47ba8741..b2eac0349b 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -46,55 +46,4 @@ if ActiveRecord::Base.connection.supports_migrations? end end - class ActiveRecordSchemaMigrationsTest < ActiveRecordSchemaTest - def setup - super - ActiveRecord::SchemaMigration.delete_all - end - - def test_migration_adds_row_to_migrations_table - schema = ActiveRecord::Schema.new - schema.migration(1001, "", "") - schema.migration(1002, "123456789012345678901234567890ab", "add_magic_power_to_unicorns") - - migrations = ActiveRecord::SchemaMigration.all.to_a - assert_equal 2, migrations.length - - assert_equal 1001, migrations[0].version - assert_match %r{^2\d\d\d-}, migrations[0].migrated_at.to_s(:db) - assert_equal "", migrations[0].fingerprint - assert_equal "", migrations[0].name - - assert_equal 1002, migrations[1].version - assert_match %r{^2\d\d\d-}, migrations[1].migrated_at.to_s(:db) - assert_equal "123456789012345678901234567890ab", migrations[1].fingerprint - assert_equal "add_magic_power_to_unicorns", migrations[1].name - end - - def test_define_clears_schema_migrations - assert_nothing_raised do - ActiveRecord::Schema.define do - migrations do - migration(123001, "", "") - end - end - ActiveRecord::Schema.define do - migrations do - migration(123001, "", "") - end - end - end - end - - def test_define_raises_if_both_version_and_explicit_migrations - assert_raise(ArgumentError) do - ActiveRecord::Schema.define(version: 123001) do - migrations do - migration(123001, "", "") - end - end - end - end - end - end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index d25aca760f..7e6c7d5862 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -298,12 +298,6 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 2, Firm.order(:id).find{|f| f.id > 0}.clients.length end - def test_find_with_blank_conditions - [[], {}, nil, ""].each do |blank| - assert_equal 2, Firm.all.merge!(:order => "id").first.clients.where(blank).to_a.size - end - end - def test_find_many_with_merged_options assert_equal 1, companies(:first_firm).limited_clients.size assert_equal 1, companies(:first_firm).limited_clients.to_a.size diff --git a/activerecord/test/cases/migration/logger_test.rb b/activerecord/test/cases/migration/logger_test.rb index 6e62c1fcbe..ee0c20747e 100644 --- a/activerecord/test/cases/migration/logger_test.rb +++ b/activerecord/test/cases/migration/logger_test.rb @@ -6,12 +6,10 @@ module ActiveRecord # mysql can't roll back ddl changes self.use_transactional_fixtures = false - Migration = Struct.new(:name, :version, :filename, :fingerprint) do + Migration = Struct.new(:name, :version) do def migrate direction # do nothing end - def filename; "anon.rb"; end - def fingerprint; "123456789012345678901234567890ab"; end end def setup diff --git a/activerecord/test/cases/migration/table_and_index_test.rb b/activerecord/test/cases/migration/table_and_index_test.rb new file mode 100644 index 0000000000..8fd770abd1 --- /dev/null +++ b/activerecord/test/cases/migration/table_and_index_test.rb @@ -0,0 +1,24 @@ +require "cases/helper" + +module ActiveRecord + class Migration + class TableAndIndexTest < ActiveRecord::TestCase + def test_add_schema_info_respects_prefix_and_suffix + conn = ActiveRecord::Base.connection + + conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name) + # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters + ActiveRecord::Base.table_name_prefix = 'p_' + ActiveRecord::Base.table_name_suffix = '_s' + conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name) + + conn.initialize_schema_migrations_table + + assert_equal "p_unique_schema_migrations_s", conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name] + ensure + ActiveRecord::Base.table_name_prefix = "" + ActiveRecord::Base.table_name_suffix = "" + end + end + end +end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 60e5a29965..c155f29973 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -59,21 +59,12 @@ class MigrationTest < ActiveRecord::TestCase def test_migrator_versions migrations_path = MIGRATIONS_ROOT + "/valid" ActiveRecord::Migrator.migrations_paths = migrations_path - m0_path = File.join(migrations_path, "1_valid_people_have_last_names.rb") - m0_fingerprint = Digest::MD5.hexdigest(File.read(m0_path)) ActiveRecord::Migrator.up(migrations_path) assert_equal 3, ActiveRecord::Migrator.current_version assert_equal 3, ActiveRecord::Migrator.last_version assert_equal false, ActiveRecord::Migrator.needs_migration? - rows = connection.select_all("SELECT * FROM #{connection.quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name)}") - assert_equal m0_fingerprint, rows[0]["fingerprint"] - assert_equal "valid_people_have_last_names", rows[0]["name"] - rows.each do |row| - assert_match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/, row["migrated_at"], "missing migrated_at") - end - ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid") assert_equal 0, ActiveRecord::Migrator.current_version assert_equal 3, ActiveRecord::Migrator.last_version @@ -346,7 +337,7 @@ class MigrationTest < ActiveRecord::TestCase assert_nothing_raised { Person.connection.create_table :binary_testings do |t| - t.column :data, :binary, :null => false + t.column "data", :binary, :null => false end } diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb index 0f0384382f..1e16addcf3 100644 --- a/activerecord/test/cases/migrator_test.rb +++ b/activerecord/test/cases/migrator_test.rb @@ -18,9 +18,6 @@ module ActiveRecord def up; @went_up = true; end def down; @went_down = true; end - # also used in place of a MigrationProxy - def filename; "anon.rb"; end - def fingerprint; "123456789012345678901234567890ab"; end end def setup @@ -105,7 +102,7 @@ module ActiveRecord end def test_finds_pending_migrations - ActiveRecord::SchemaMigration.create!(:version => '1', :name => "anon", :migrated_at => Time.now) + ActiveRecord::SchemaMigration.create!(:version => '1') migration_list = [ Migration.new('foo', 1), Migration.new('bar', 3) ] migrations = ActiveRecord::Migrator.new(:up, migration_list).pending_migrations @@ -155,7 +152,7 @@ module ActiveRecord end def test_current_version - ActiveRecord::SchemaMigration.create!(:version => '1000', :name => "anon", :migrated_at => Time.now) + ActiveRecord::SchemaMigration.create!(:version => '1000') assert_equal 1000, ActiveRecord::Migrator.current_version end @@ -323,7 +320,7 @@ module ActiveRecord def test_only_loads_pending_migrations # migrate up to 1 - ActiveRecord::SchemaMigration.create!(:version => '1', :name => "anon", :migrated_at => Time.now) + ActiveRecord::SchemaMigration.create!(:version => '1') calls, migrator = migrator_class(3) migrator.migrate("valid", nil) diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 02034c87b4..9e0423ab52 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -512,6 +512,14 @@ class PersistencesTest < ActiveRecord::TestCase assert_equal 'super_title', t.title end + def test_update_column_with_default_scope + developer = DeveloperCalledDavid.first + developer.name = 'John' + developer.save! + + assert developer.update_column(:name, 'Will'), 'did not update record due to default scope' + end + def test_update_columns topic = Topic.find(1) topic.update_columns({ "approved" => true, title: "Sebastian Topic" }) @@ -616,6 +624,14 @@ class PersistencesTest < ActiveRecord::TestCase assert_equal true, topic.update_columns(title: "New title") end + def test_update_columns_with_default_scope + developer = DeveloperCalledDavid.first + developer.name = 'John' + developer.save! + + assert developer.update_columns(name: 'Will'), 'did not update record due to default scope' + end + def test_update_attributes topic = Topic.find(1) assert !topic.approved? diff --git a/activerecord/test/cases/relation/where_chain_test.rb b/activerecord/test/cases/relation/where_chain_test.rb new file mode 100644 index 0000000000..8ce44636b4 --- /dev/null +++ b/activerecord/test/cases/relation/where_chain_test.rb @@ -0,0 +1,75 @@ +require 'cases/helper' +require 'models/post' +require 'models/comment' + +module ActiveRecord + class WhereChainTest < ActiveRecord::TestCase + fixtures :posts + + def test_not_eq + expected = Arel::Nodes::NotEqual.new(Post.arel_table[:title], 'hello') + relation = Post.where.not(title: 'hello') + assert_equal([expected], relation.where_values) + end + + def test_not_null + expected = Arel::Nodes::NotEqual.new(Post.arel_table[:title], nil) + relation = Post.where.not(title: nil) + assert_equal([expected], relation.where_values) + end + + def test_not_in + expected = Arel::Nodes::NotIn.new(Post.arel_table[:title], %w[hello goodbye]) + relation = Post.where.not(title: %w[hello goodbye]) + assert_equal([expected], relation.where_values) + end + + def test_association_not_eq + expected = Arel::Nodes::NotEqual.new(Comment.arel_table[:title], 'hello') + relation = Post.joins(:comments).where.not(comments: {title: 'hello'}) + assert_equal(expected.to_sql, relation.where_values.first.to_sql) + end + + def test_not_eq_with_preceding_where + relation = Post.where(title: 'hello').where.not(title: 'world') + + expected = Arel::Nodes::Equality.new(Post.arel_table[:title], 'hello') + assert_equal(expected, relation.where_values.first) + + expected = Arel::Nodes::NotEqual.new(Post.arel_table[:title], 'world') + assert_equal(expected, relation.where_values.last) + end + + def test_not_eq_with_succeeding_where + relation = Post.where.not(title: 'hello').where(title: 'world') + + expected = Arel::Nodes::NotEqual.new(Post.arel_table[:title], 'hello') + assert_equal(expected, relation.where_values.first) + + expected = Arel::Nodes::Equality.new(Post.arel_table[:title], 'world') + assert_equal(expected, relation.where_values.last) + end + + def test_not_eq_with_string_parameter + expected = Arel::Nodes::Not.new("title = 'hello'") + relation = Post.where.not("title = 'hello'") + assert_equal([expected], relation.where_values) + end + + def test_not_eq_with_array_parameter + expected = Arel::Nodes::Not.new("title = 'hello'") + relation = Post.where.not(['title = ?', 'hello']) + assert_equal([expected], relation.where_values) + end + + def test_chaining_multiple + relation = Post.where.not(author_id: [1, 2]).where.not(title: 'ruby on rails') + + expected = Arel::Nodes::NotIn.new(Post.arel_table[:author_id], [1, 2]) + assert_equal(expected, relation.where_values[0]) + + expected = Arel::Nodes::NotEqual.new(Post.arel_table[:title], 'ruby on rails') + assert_equal(expected, relation.where_values[1]) + end + end +end diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb index 9c0b139dbf..297e865308 100644 --- a/activerecord/test/cases/relation/where_test.rb +++ b/activerecord/test/cases/relation/where_test.rb @@ -85,5 +85,11 @@ module ActiveRecord def test_where_with_empty_hash_and_no_foreign_key assert_equal 0, Edge.where(:sink => {}).count end + + def test_where_with_blank_conditions + [[], {}, nil, ""].each do |blank| + assert_equal 4, Edge.where(blank).order("sink_id").to_a.size + end + end end end diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 9056b92d65..7ff0044bd4 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -1,5 +1,6 @@ require "cases/helper" + class SchemaDumperTest < ActiveRecord::TestCase def setup super @@ -17,15 +18,11 @@ class SchemaDumperTest < ActiveRecord::TestCase def test_dump_schema_information_outputs_lexically_ordered_versions versions = %w{ 20100101010101 20100201010101 20100301010101 } versions.reverse.each do |v| - ActiveRecord::SchemaMigration.create!( - :version => v, :migrated_at => Time.now, - :fingerprint => "123456789012345678901234567890ab", :name => "anon") + ActiveRecord::SchemaMigration.create!(:version => v) end schema_info = ActiveRecord::Base.connection.dump_schema_information assert_match(/20100201010101.*20100301010101/m, schema_info) - target_line = %q{INSERT INTO schema_migrations (version, migrated_at, fingerprint, name) VALUES ('20100101010101',LOCALTIMESTAMP,'123456789012345678901234567890ab','anon');} - assert_match target_line, schema_info end def test_magic_comment @@ -39,16 +36,6 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_no_match %r{create_table "schema_migrations"}, output end - def test_schema_dump_includes_migrations - ActiveRecord::SchemaMigration.delete_all - ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/always_safe") - - output = standard_dump - assert_match %r{migrations do}, output, "Missing migrations block" - assert_match %r{migration 1001, "[0-9a-f]{32}", "always_safe"}, output, "Missing migration line" - assert_match %r{migration 1002, "[0-9a-f]{32}", "still_safe"}, output, "Missing migration line" - end - def test_schema_dump_excludes_sqlite_sequence output = standard_dump assert_no_match %r{create_table "sqlite_sequence"}, output diff --git a/activerecord/test/cases/schema_migration_test.rb b/activerecord/test/cases/schema_migration_test.rb deleted file mode 100644 index 34b9fa520a..0000000000 --- a/activerecord/test/cases/schema_migration_test.rb +++ /dev/null @@ -1,54 +0,0 @@ -require "cases/helper" - -class SchemaMigrationTest < ActiveRecord::TestCase - def sm_table_name - ActiveRecord::SchemaMigration.table_name - end - - def connection - ActiveRecord::Base.connection - end - - def test_add_schema_info_respects_prefix_and_suffix - connection.drop_table(sm_table_name) if connection.table_exists?(sm_table_name) - # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters - ActiveRecord::Base.table_name_prefix = 'p_' - ActiveRecord::Base.table_name_suffix = '_s' - connection.drop_table(sm_table_name) if connection.table_exists?(sm_table_name) - - ActiveRecord::SchemaMigration.create_table - - assert_equal "p_unique_schema_migrations_s", connection.indexes(sm_table_name)[0][:name] - ensure - ActiveRecord::Base.table_name_prefix = "" - ActiveRecord::Base.table_name_suffix = "" - end - - def test_add_metadata_columns_to_exisiting_schema_migrations - # creates the old table schema from pre-Rails4.0, so we can test adding to it below - if connection.table_exists?(sm_table_name) - connection.drop_table(sm_table_name) - end - connection.create_table(sm_table_name, :id => false) do |schema_migrations_table| - schema_migrations_table.column("version", :string, :null => false) - end - - connection.insert "INSERT INTO #{connection.quote_table_name(sm_table_name)} (version) VALUES (100)" - connection.insert "INSERT INTO #{connection.quote_table_name(sm_table_name)} (version) VALUES (200)" - - ActiveRecord::SchemaMigration.create_table - - rows = connection.select_all("SELECT * FROM #{connection.quote_table_name(sm_table_name)}") - assert rows[0].has_key?("migrated_at"), "missing column `migrated_at`" - assert_match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/, rows[0]["migrated_at"]) - assert rows[0].has_key?("fingerprint"), "missing column `fingerprint`" - assert rows[0].has_key?("name"), "missing column `name`" - end - - def test_schema_migrations_columns - ActiveRecord::SchemaMigration.create_table - - columns = connection.columns(sm_table_name).collect(&:name) - %w[version migrated_at fingerprint name].each { |col| assert columns.include?(col), "missing column `#{col}`" } - end -end diff --git a/activerecord/test/migrations/always_safe/1001_always_safe.rb b/activerecord/test/migrations/always_safe/1001_always_safe.rb deleted file mode 100644 index 454b972507..0000000000 --- a/activerecord/test/migrations/always_safe/1001_always_safe.rb +++ /dev/null @@ -1,5 +0,0 @@ -class AlwaysSafe < ActiveRecord::Migration - def change - # do nothing to avoid side-effect conflicts from running multiple times - end -end diff --git a/activerecord/test/migrations/always_safe/1002_still_safe.rb b/activerecord/test/migrations/always_safe/1002_still_safe.rb deleted file mode 100644 index 7398ae27a2..0000000000 --- a/activerecord/test/migrations/always_safe/1002_still_safe.rb +++ /dev/null @@ -1,5 +0,0 @@ -class StillSafe < ActiveRecord::Migration - def change - # do nothing to avoid side-effect conflicts from running multiple times - end -end diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 450669968b..f5f2aa85ef 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,5 +1,11 @@ ## Rails 4.0.0 (unreleased) ## +* Deprecate `ActiveSupport::BasicObject` in favor of `ActiveSupport::ProxyObject`. + This class is used for proxy classes. It avoids confusion with Ruby's BasicObject + class. + + *Francesco Rodriguez* + * Patched Marshal#load to work with constant autoloading. Fixes autoloading with cache stores that relay on Marshal(MemCacheStore and FileStore). [fixes #8167] diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index 4e397ea110..b602686114 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -40,6 +40,7 @@ module ActiveSupport eager_autoload do autoload :BacktraceCleaner autoload :BasicObject + autoload :ProxyObject autoload :Benchmarkable autoload :Cache autoload :Callbacks diff --git a/activesupport/lib/active_support/basic_object.rb b/activesupport/lib/active_support/basic_object.rb index 6ccb0cd525..242b766b58 100644 --- a/activesupport/lib/active_support/basic_object.rb +++ b/activesupport/lib/active_support/basic_object.rb @@ -1,13 +1,7 @@ -module ActiveSupport - # A class with no predefined methods that behaves similarly to Builder's - # BlankSlate. Used for proxy classes. - class BasicObject < ::BasicObject - undef_method :== - undef_method :equal? +require 'active_support/deprecation' - # Let ActiveSupport::BasicObject at least raise exceptions. - def raise(*args) - ::Object.send(:raise, *args) - end - end +module ActiveSupport + # :nodoc: + # Deprecated in favor of ActiveSupport::ProxyObject + BasicObject = Deprecation::DeprecatedConstantProxy.new('ActiveSupport::BasicObject', 'ActiveSupport::ProxyObject') end diff --git a/activesupport/lib/active_support/core_ext/class/attribute.rb b/activesupport/lib/active_support/core_ext/class/attribute.rb index 1c3d26ead4..1504e18839 100644 --- a/activesupport/lib/active_support/core_ext/class/attribute.rb +++ b/activesupport/lib/active_support/core_ext/class/attribute.rb @@ -72,6 +72,9 @@ class Class instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true) instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true) + # We use class_eval here rather than define_method because class_attribute + # may be used in a performance sensitive context therefore the overhead that + # define_method introduces may become significant. attrs.each do |name| class_eval <<-RUBY, __FILE__, __LINE__ + 1 def self.#{name}() nil end diff --git a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb index 17e69c34a5..485dc91063 100644 --- a/activesupport/lib/active_support/deprecation/proxy_wrappers.rb +++ b/activesupport/lib/active_support/deprecation/proxy_wrappers.rb @@ -30,7 +30,7 @@ module ActiveSupport # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!") # @old_object = DeprecatedObjectProxy.new(Object.new, "Don't use this object anymore!", deprecator_instance) # - # When someone execute any method expect +inspect+ on proxy object this will + # When someone executes any method except +inspect+ on proxy object this will # trigger +warn+ method on +deprecator_instance+. # # Default deprecator is <tt>ActiveSupport::Deprecation</tt> diff --git a/activesupport/lib/active_support/duration.rb b/activesupport/lib/active_support/duration.rb index 7e99646117..2cb1f408b6 100644 --- a/activesupport/lib/active_support/duration.rb +++ b/activesupport/lib/active_support/duration.rb @@ -1,4 +1,4 @@ -require 'active_support/basic_object' +require 'active_support/proxy_object' require 'active_support/core_ext/array/conversions' require 'active_support/core_ext/object/acts_like' @@ -7,7 +7,7 @@ module ActiveSupport # Time#advance, respectively. It mainly supports the methods on Numeric. # # 1.month.ago # equivalent to Time.now.advance(months: -1) - class Duration < BasicObject + class Duration < ProxyObject attr_accessor :value, :parts def initialize(value, parts) #:nodoc: diff --git a/activesupport/lib/active_support/proxy_object.rb b/activesupport/lib/active_support/proxy_object.rb new file mode 100644 index 0000000000..a2bdf1d790 --- /dev/null +++ b/activesupport/lib/active_support/proxy_object.rb @@ -0,0 +1,13 @@ +module ActiveSupport + # A class with no predefined methods that behaves similarly to Builder's + # BlankSlate. Used for proxy classes. + class ProxyObject < ::BasicObject + undef_method :== + undef_method :equal? + + # Let ActiveSupport::BasicObject at least raise exceptions. + def raise(*args) + ::Object.send(:raise, *args) + end + end +end diff --git a/activesupport/test/core_ext/duration_test.rb b/activesupport/test/core_ext/duration_test.rb index c8312aa653..2826f51f2d 100644 --- a/activesupport/test/core_ext/duration_test.rb +++ b/activesupport/test/core_ext/duration_test.rb @@ -21,7 +21,7 @@ class DurationTest < ActiveSupport::TestCase assert ActiveSupport::Duration === 1.day assert !(ActiveSupport::Duration === 1.day.to_i) assert !(ActiveSupport::Duration === 'foo') - assert !(ActiveSupport::Duration === ActiveSupport::BasicObject.new) + assert !(ActiveSupport::Duration === ActiveSupport::ProxyObject.new) end def test_equals @@ -131,7 +131,7 @@ class DurationTest < ActiveSupport::TestCase assert_equal Time.local(2009,3,29,0,0,0) + 1.day, Time.local(2009,3,30,0,0,0) end end - + def test_delegation_with_block_works counter = 0 assert_nothing_raised do diff --git a/guides/Rakefile b/guides/Rakefile index 7881a3d9b3..d6dd950d01 100644 --- a/guides/Rakefile +++ b/guides/Rakefile @@ -13,6 +13,12 @@ namespace :guides do desc "Generate .mobi file. The kindlegen executable must be in your PATH. You can get it for free from http://www.amazon.com/kindlepublishing" task :kindle do + unless `kindlerb -v 2> /dev/null` =~ /kindlerb 0.1.1/ + abort "Please `gem install kindlerb`" + end + unless `convert` =~ /convert/ + abort "Please install ImageMagick`" + end ENV['KINDLE'] = '1' Rake::Task['guides:generate:html'].invoke end diff --git a/guides/rails_guides/generator.rb b/guides/rails_guides/generator.rb index 3b124ef236..af9c5b8372 100644 --- a/guides/rails_guides/generator.rb +++ b/guides/rails_guides/generator.rb @@ -84,7 +84,7 @@ module RailsGuides @warnings = ENV['WARNINGS'] == '1' @all = ENV['ALL'] == '1' @kindle = ENV['KINDLE'] == '1' - @version = ENV['RAILS_VERSION'] || `git rev-parse --short HEAD`.chomp + @version = ENV['RAILS_VERSION'] || 'local' @lang = ENV['GUIDES_LANGUAGE'] end @@ -112,11 +112,9 @@ module RailsGuides end def generate_mobi - opf = "#{output_dir}/rails_guides.opf" + require 'rails_guides/kindle' out = "#{output_dir}/kindlegen.out" - - system "kindlegen #{opf} -o #{mobi} > #{out} 2>&1" - puts "Guides compiled as Kindle book to #{mobi}" + Kindle.generate(output_dir, mobi, out) puts "(kindlegen log at #{out})." end diff --git a/guides/rails_guides/kindle.rb b/guides/rails_guides/kindle.rb new file mode 100644 index 0000000000..09eecd5634 --- /dev/null +++ b/guides/rails_guides/kindle.rb @@ -0,0 +1,119 @@ +#!/usr/bin/env ruby + +unless `which kindlerb` + abort "Please gem install kindlerb" +end + +require 'nokogiri' +require 'fileutils' +require 'yaml' +require 'date' + +module Kindle + extend self + + def generate(output_dir, mobi_outfile, logfile) + output_dir = File.absolute_path(output_dir) + Dir.chdir output_dir do + puts "=> Using output dir: #{output_dir}" + puts "=> Arranging html pages in document order" + toc = File.read("toc.ncx") + doc = Nokogiri::XML(toc).xpath("//ncx:content", 'ncx' => "http://www.daisy.org/z3986/2005/ncx/") + html_pages = doc.select {|c| c[:src]}.map {|c| c[:src]}.uniq + + generate_front_matter(html_pages) + + generate_sections(html_pages) + + generate_document_metadata(mobi_outfile) + + puts "Creating MOBI document with kindlegen. This make take a while." + cmd = "kindlerb . > #{File.absolute_path logfile} 2>&1" + puts cmd + system(cmd) + puts "MOBI document generated at #{File.expand_path(mobi_outfile, output_dir)}" + end + end + + def generate_front_matter(html_pages) + frontmatter = [] + html_pages.delete_if {|x| + if x =~ /(toc|welcome|credits|copyright).html/ + frontmatter << x unless x =~ /toc/ + true + end + } + html = frontmatter.map {|x| + Nokogiri::HTML(File.open(x)).at("body").inner_html + }.join("\n") + + fdoc = Nokogiri::HTML(html) + fdoc.search("h3").each do |h3| + h3.name = 'h4' + end + fdoc.search("h2").each do |h2| + h2.name = 'h3' + h2['id'] = h2.inner_text.gsub(/\s/, '-') + end + add_head_section fdoc, "Front Matter" + File.open("frontmatter.html",'w') {|f| f.puts fdoc.to_html} + html_pages.unshift "frontmatter.html" + end + + def generate_sections(html_pages) + FileUtils::rm_rf("sections/") + html_pages.each_with_index do |page, section_idx| + FileUtils::mkdir_p("sections/%03d" % section_idx) + doc = Nokogiri::HTML(File.open(page)) + title = doc.at("title").inner_text.gsub("Ruby on Rails Guides: ", '') + title = page.capitalize.gsub('.html', '') if title.strip == '' + File.open("sections/%03d/_section.txt" % section_idx, 'w') {|f| f.puts title} + doc.xpath("//h3[@id]").each_with_index do |h3,item_idx| + subsection = h3.inner_text + content = h3.xpath("./following-sibling::*").take_while {|x| x.name != "h3"}.map {|x| x.to_html} + item = Nokogiri::HTML(h3.to_html + content.join("\n")) + item_path = "sections/%03d/%03d.html" % [section_idx, item_idx] + add_head_section(item, subsection) + item.search("img").each do |img| + img['src'] = "#{Dir.pwd}/#{img['src']}" + end + item.xpath("//li/p").each {|p| p.swap(p.children); p.remove} + File.open(item_path, 'w') {|f| f.puts item.to_html} + end + end + end + + def generate_document_metadata(mobi_outfile) + puts "=> Generating _document.yml" + x = Nokogiri::XML(File.open("rails_guides.opf")).remove_namespaces! + cover_jpg = "#{Dir.pwd}/images/rails_guides_kindle_cover.jpg" + cover_gif = cover_jpg.sub(/jpg$/, 'gif') + puts `convert #{cover_jpg} #{cover_gif}` + document = { + 'doc_uuid' => x.at("package")['unique-identifier'], + 'title' => x.at("title").inner_text.gsub(/\(.*$/, " v2"), + 'publisher' => x.at("publisher").inner_text, + 'author' => x.at("creator").inner_text, + 'subject' => x.at("subject").inner_text, + 'date' => x.at("date").inner_text, + 'cover' => cover_gif, + 'masthead' => nil, + 'mobi_outfile' => mobi_outfile + } + puts document.to_yaml + File.open("_document.yml", 'w'){|f| f.puts document.to_yaml} + end + + def add_head_section(doc, title) + head = Nokogiri::XML::Node.new "head", doc + title_node = Nokogiri::XML::Node.new "title", doc + title_node.content = title + title_node.parent = head + css = Nokogiri::XML::Node.new "link", doc + css['rel'] = 'stylesheet' + css['type'] = 'text/css' + css['href'] = "#{Dir.pwd}/stylesheets/kindle.css" + css.parent = head + doc.at("body").before head + end +end diff --git a/guides/source/4_0_release_notes.md b/guides/source/4_0_release_notes.md index 42794b180e..dd57787111 100644 --- a/guides/source/4_0_release_notes.md +++ b/guides/source/4_0_release_notes.md @@ -143,7 +143,7 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/master/activ * `ActiveSupport::JSON::Variable` is deprecated. Define your own `#as_json` and `#encode_json` methods for custom JSON string literals. -* Deprecates the compatibility method Module#local_constant_names, use Module#local_constants instead (which returns symbols). +* Deprecates the compatibility method Module#local_constant_names, use Module#local_constants instead (which returns symbols). * BufferedLogger is deprecated. Use ActiveSupport::Logger, or the logger from Ruby stdlib. @@ -165,8 +165,48 @@ Please refer to the [Changelog](https://github.com/rails/rails/blob/master/railt ### Notable changes +* Adds some metadata columns to `schema_migrations` table. + + * `migrated_at` + * `fingerprint` - an md5 hash of the migration. + * `name` - the filename minus version and extension. + +* Adds PostgreSQL array type support. Any datatype can be used to create an array column, with full migration and schema dumper support. + +* Add `Relation#load` to explicitly load the record and return `self`. + +* `Model.all` now returns an `ActiveRecord::Relation`, rather than an array of records. Use `Relation#to_a` if you really want an array. In some specific cases, this may cause breakage when upgrading. + +* Added `ActiveRecord::Migration.check_pending!` that raises an error if migrations are pending. + +* Added custom coders support for `ActiveRecord::Store`. Now you can set your custom coder like this: + + store :settings, accessors: [ :color, :homepage ], coder: JSON + +* `mysql` and `mysql2` connections will set `SQL_MODE=STRICT_ALL_TABLES` by default to avoid silent data loss. This can be disabled by specifying `strict: false` in your `database.yml`. + +* Remove IdentityMap. + +* Adds `ActiveRecord::NullRelation` and `ActiveRecord::Relation#none` implementing the null object pattern for the Relation class. + +* Added `create_join_table` migration helper to create HABTM join tables. + +* Allows PostgreSQL hstore records to be created. + ### Deprecations +* Deprecated the old-style hash based finder API. This means that methods which previously accepted "finder options" no longer do. + +* All dynamic methods except for `find_by_...` and `find_by_...!` are deprecated. Here's + how you can rewrite the code: + + * `find_all_by_...` can be rewritten using `where(...)`. + * `find_last_by_...` can be rewritten using `where(...).last`. + * `scoped_by_...` can be rewritten using `where(...)`. + * `find_or_initialize_by_...` can be rewritten using `where(...).first_or_initialize`. + * `find_or_create_by_...` can be rewritten using `find_or_create_by(...)` or `where(...).first_or_create`. + * `find_or_create_by_...!` can be rewritten using `find_or_create_by!(...)` or `where(...).first_or_create!`. + Credits ------- diff --git a/guides/source/_welcome.html.erb b/guides/source/_welcome.html.erb index 9d2e9c1d68..a50961a0c7 100644 --- a/guides/source/_welcome.html.erb +++ b/guides/source/_welcome.html.erb @@ -1,4 +1,4 @@ -<h2>Ruby on Rails Guides (<%= @version %>)</h2> +<h2>Ruby on Rails Guides (<%= @edge ? @version[0, 7] : @version %>)</h2> <% if @edge %> <p> diff --git a/guides/source/action_controller_overview.md b/guides/source/action_controller_overview.md index 8bd28d5246..46ff9027fd 100644 --- a/guides/source/action_controller_overview.md +++ b/guides/source/action_controller_overview.md @@ -5,13 +5,13 @@ In this guide you will learn how controllers work and how they fit into the requ After reading this guide, you will know: -* Follow the flow of a request through a controller. -* Understand why and how to store data in the session or cookies. -* Work with filters to execute code during request processing. -* Use Action Controller's built-in HTTP authentication. -* Stream data directly to the user's browser. -* Filter sensitive parameters so they do not appear in the application's log. -* Deal with exceptions that may be raised during request processing. +* How to follow the flow of a request through a controller. +* Why and how to store data in the session or cookies. +* How to work with filters to execute code during request processing. +* How to use Action Controller's built-in HTTP authentication. +* How to stream data directly to the user's browser. +* How to filter sensitive parameters so they do not appear in the application's log. +* How to deal with exceptions that may be raised during request processing. -------------------------------------------------------------------------------- @@ -434,7 +434,7 @@ Filters are inherited, so if you set a filter on `ApplicationController`, it wil ```ruby class ApplicationController < ActionController::Base - before_filter :require_login + before_action :require_login private @@ -458,11 +458,11 @@ end The method simply stores an error message in the flash and redirects to the login form if the user is not logged in. If a "before" filter renders or redirects, the action will not run. If there are additional filters scheduled to run after that filter, they are also cancelled. -In this example the filter is added to `ApplicationController` and thus all controllers in the application inherit it. This will make everything in the application require the user to be logged in in order to use it. For obvious reasons (the user wouldn't be able to log in in the first place!), not all controllers or actions should require this. You can prevent this filter from running before particular actions with `skip_before_filter`: +In this example the filter is added to `ApplicationController` and thus all controllers in the application inherit it. This will make everything in the application require the user to be logged in in order to use it. For obvious reasons (the user wouldn't be able to log in in the first place!), not all controllers or actions should require this. You can prevent this filter from running before particular actions with `skip_before_action`: ```ruby class LoginsController < ApplicationController - skip_before_filter :require_login, only: [:new, :create] + skip_before_action :require_login, only: [:new, :create] end ``` @@ -480,7 +480,7 @@ For example, in a website where changes have an approval workflow an administrat ```ruby class ChangesController < ActionController::Base - around_filter :wrap_in_transaction, only: :show + around_action :wrap_in_transaction, only: :show private @@ -502,13 +502,13 @@ You can choose not to yield and build the response yourself, in which case the a ### Other Ways to Use Filters -While the most common way to use filters is by creating private methods and using *_filter to add them, there are two other ways to do the same thing. +While the most common way to use filters is by creating private methods and using *_action to add them, there are two other ways to do the same thing. -The first is to use a block directly with the *_filter methods. The block receives the controller as an argument, and the `require_login` filter from above could be rewritten to use a block: +The first is to use a block directly with the *_action methods. The block receives the controller as an argument, and the `require_login` filter from above could be rewritten to use a block: ```ruby class ApplicationController < ActionController::Base - before_filter do |controller| + before_action do |controller| redirect_to new_login_url unless controller.send(:logged_in?) end end @@ -520,7 +520,7 @@ The second way is to use a class (actually, any object that responds to the righ ```ruby class ApplicationController < ActionController::Base - before_filter LoginFilter + before_action LoginFilter end class LoginFilter @@ -648,7 +648,7 @@ HTTP digest authentication is superior to the basic authentication as it does no class AdminController < ApplicationController USERS = { "lifo" => "world" } - before_filter :authenticate + before_action :authenticate private @@ -828,7 +828,7 @@ end class ClientsController < ApplicationController # Check that the user has the right authorization to access clients. - before_filter :check_authorization + before_action :check_authorization # Note how the actions don't have to worry about all the auth stuff. def edit @@ -849,7 +849,7 @@ NOTE: Certain exceptions are only rescuable from the `ApplicationController` cla Force HTTPS protocol -------------------- -Sometime you might want to force a particular controller to only be accessible via an HTTPS protocol for security reasons. Since Rails 3.1 you can now use the `force_ssl` method in your controller to enforce that: +Sometime you might want to force a particular controller to only be accessible via an HTTPS protocol for security reasons. You can use the `force_ssl` method in your controller to enforce that: ```ruby class DinnerController @@ -857,7 +857,7 @@ class DinnerController end ``` -Just like the filter, you could also passing `:only` and `:except` to enforce the secure connection only to specific actions: +Just like the filter, you could also pass `:only` and `:except` to enforce the secure connection only to specific actions: ```ruby class DinnerController diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md index ddb0e438c9..aaf04f4256 100644 --- a/guides/source/action_mailer_basics.md +++ b/guides/source/action_mailer_basics.md @@ -5,6 +5,10 @@ This guide should provide you with all you need to get started in sending and re After reading this guide, you will know: +* How to send and receive email within a Rails application. +* How to generate and edit an Action Mailer class and mailer view. +* How to configure Action Mailer for your environment. +* How to test your Action Mailer classes. -------------------------------------------------------------------------------- Introduction @@ -105,7 +109,7 @@ When you call the `mail` method now, Action Mailer will detect the two templates #### Wire It Up So That the System Sends the Email When a User Signs Up -There are several ways to do this, some people create Rails Observers to fire off emails, others do it inside of the User Model. However, in Rails 3, mailers are really just another way to render a view. Instead of rendering a view and sending out the HTTP protocol, they are just sending it out through the Email protocols instead. Due to this, it makes sense to just have your controller tell the mailer to send an email when a user is successfully created. +There are several ways to do this, some people create Rails Observers to fire off emails, others do it inside of the User Model. However, mailers are really just another way to render a view. Instead of rendering a view and sending out the HTTP protocol, they are just sending it out through the Email protocols instead. Due to this, it makes sense to just have your controller tell the mailer to send an email when a user is successfully created. Setting this up is painfully simple. @@ -145,10 +149,6 @@ This provides a much simpler implementation that does not require the registerin The method `welcome_email` returns a `Mail::Message` object which can then just be told `deliver` to send itself out. -NOTE: In previous versions of Rails, you would call `deliver_welcome_email` or `create_welcome_email`. This has been deprecated in Rails 3.0 in favour of just calling the method name itself. - -WARNING: Sending out an email should only take a fraction of a second. If you are planning on sending out many emails, or you have a slow domain resolution service, you might want to investigate using a background process like Delayed Job. - ### Auto encoding header values Action Mailer now handles the auto encoding of multibyte characters inside of headers and bodies. @@ -447,17 +447,17 @@ end Action Mailer Callbacks --------------------------- -Action Mailer allows for you to specify a `before_filter`, `after_filter` and 'around_filter'. +Action Mailer allows for you to specify a `before_action`, `after_action` and 'around_action'. * Filters can be specified with a block or a symbol to a method in the mailer class similar to controllers. -* You could use a `before_filter` to prepopulate the mail object with defaults, delivery_method_options or insert default headers and attachments. +* You could use a `before_action` to prepopulate the mail object with defaults, delivery_method_options or insert default headers and attachments. -* You could use an `after_filter` to do similar setup as a `before_filter` but using instance variables set in your mailer action. +* You could use an `after_action` to do similar setup as a `before_action` but using instance variables set in your mailer action. ```ruby class UserMailer < ActionMailer::Base - after_filter :set_delivery_options, :prevent_delivery_to_guests, :set_business_headers + after_action :set_delivery_options, :prevent_delivery_to_guests, :set_business_headers def feedback_message(business, user) @business = business diff --git a/guides/source/action_view_overview.md b/guides/source/action_view_overview.md index c931b30bd3..6c2871d478 100644 --- a/guides/source/action_view_overview.md +++ b/guides/source/action_view_overview.md @@ -1263,10 +1263,8 @@ Creates a field set for grouping HTML form elements. Creates a file upload field. -Prior to Rails 3.1, if you are using file uploads, then you will need to set the multipart option for the form tag. Rails 3.1+ does this automatically. - ```html+erb -<%= form_tag {action: "post"}, {multipart: true} do %> +<%= form_tag {action: "post"} do %> <label for="file">File to Upload</label> <%= file_field_tag "file" %> <%= submit_tag %> <% end %> @@ -1486,7 +1484,7 @@ You can use the same technique to localize the rescue files in your public direc Since Rails doesn't restrict the symbols that you use to set I18n.locale, you can leverage this system to display different content depending on anything you like. For example, suppose you have some "expert" users that should see different pages from "normal" users. You could add the following to `app/controllers/application.rb`: ```ruby -before_filter :set_expert_locale +before_action :set_expert_locale def set_expert_locale I18n.locale = :expert if current_user.expert? diff --git a/guides/source/active_record_callbacks.md b/guides/source/active_record_callbacks.md index 02c1c46a5a..971c1cdb25 100644 --- a/guides/source/active_record_callbacks.md +++ b/guides/source/active_record_callbacks.md @@ -4,11 +4,11 @@ Active Record Callbacks This guide teaches you how to hook into the life cycle of your Active Record objects. -After reading this guide and trying out the presented concepts, we hope that you'll be able to: +After reading this guide, you will know: -* Understand the life cycle of Active Record objects -* Create callback methods that respond to events in the object life cycle -* Create special classes that encapsulate common behavior for your callbacks +* The life cycle of Active Record objects. +* How to create callback methods that respond to events in the object life cycle. +* How to create special classes that encapsulate common behavior for your callbacks. -------------------------------------------------------------------------------- diff --git a/guides/source/active_record_querying.md b/guides/source/active_record_querying.md index 9620270141..24f98f68ca 100644 --- a/guides/source/active_record_querying.md +++ b/guides/source/active_record_querying.md @@ -5,13 +5,13 @@ This guide covers different ways to retrieve data from the database using Active After reading this guide, you will know: -* Find records using a variety of methods and conditions. -* Specify the order, retrieved attributes, grouping, and other properties of the found records. -* Use eager loading to reduce the number of database queries needed for data retrieval. -* Use dynamic finders methods. -* Check for the existence of particular records. -* Perform various calculations on Active Record models. -* Run EXPLAIN on relations. +* How to find records using a variety of methods and conditions. +* How to specify the order, retrieved attributes, grouping, and other properties of the found records. +* How to use eager loading to reduce the number of database queries needed for data retrieval. +* How to use dynamic finders methods. +* How to check for the existence of particular records. +* How to perform various calculations on Active Record models. +* How to run EXPLAIN on relations. -------------------------------------------------------------------------------- @@ -505,6 +505,20 @@ This code will generate SQL like this: SELECT * FROM clients WHERE (clients.orders_count IN (1,3,5)) ``` +### NOT, LIKE, and NOT LIKE Conditions + +`NOT`, `LIKE`, and `NOT LIKE` SQL queries can be built by `where.not`, `where.like`, and `where.not_like` respectively. + +```ruby +Post.where.not(author: author) + +Author.where.like(name: 'Nari%') + +Developer.where.not_like(name: 'Tenderl%') +``` + +In other words, these sort of queries can be generated by calling `where` with no argument, then immediately chain with `not`, `like`, or `not_like` passing `where` conditions. + Ordering -------- @@ -1190,7 +1204,7 @@ class Client < ActiveRecord::Base end ``` -### Removing all scoping +### Removing All Scoping If we wish to remove scoping for any reason we can use the `unscoped` method. This is especially useful if a `default_scope` is specified in the model and should not be @@ -1222,9 +1236,7 @@ You can specify an exclamation point (`!`) on the end of the dynamic finders to If you want to find both by name and locked, you can chain these finders together by simply typing "`and`" between the fields. For example, `Client.find_by_first_name_and_locked("Ryan", true)`. -WARNING: Up to and including Rails 3.1, when the number of arguments passed to a dynamic finder method is lesser than the number of fields, say `Client.find_by_name_and_locked("Ryan")`, the behavior is to pass `nil` as the missing argument. This is **unintentional** and this behavior will be changed in Rails 3.2 to throw an `ArgumentError`. - -Find or build a new object +Find or Build a New Object -------------------------- It's common that you need to find a record or create it if it doesn't exist. You can do that with the `find_or_create_by` and `find_or_create_by!` methods. diff --git a/guides/source/active_record_validations.md b/guides/source/active_record_validations.md index 4642ef82f0..2e2f0e4ea9 100644 --- a/guides/source/active_record_validations.md +++ b/guides/source/active_record_validations.md @@ -6,9 +6,9 @@ the database using Active Record's validations feature. After reading this guide, you will know: -* Use the built-in Active Record validation helpers -* Create your own custom validation methods -* Work with the error messages generated by the validation process +* How to use the built-in Active Record validation helpers. +* How to create your own custom validation methods. +* How to work with the error messages generated by the validation process. -------------------------------------------------------------------------------- @@ -779,7 +779,7 @@ class Account < ActiveRecord::Base end ``` -### Grouping conditional validations +### Grouping Conditional validations Sometimes it is useful to have multiple validations use one condition, it can be easily achieved using `with_options`. @@ -796,7 +796,7 @@ end All validations inside of `with_options` block will have automatically passed the condition `if: :is_admin?` -### Combining validation conditions +### Combining Validation Conditions On the other hand, when multiple conditions define whether or not a validation should happen, an `Array` can be used. Moreover, you can apply both `:if` and diff --git a/guides/source/active_support_core_extensions.md b/guides/source/active_support_core_extensions.md index 775da2a85c..1d79ee2565 100644 --- a/guides/source/active_support_core_extensions.md +++ b/guides/source/active_support_core_extensions.md @@ -7,6 +7,11 @@ It offers a richer bottom-line at the language level, targeted both at the devel After reading this guide, you will know: +* What Core Extensions are. +* How to load all extensions. +* How to cherry-pick just the extensions you want. +* What extensions ActiveSupport provides. + -------------------------------------------------------------------------------- How to Load Core Extensions @@ -1120,8 +1125,6 @@ C.subclasses # => [B, D] The order in which these classes are returned is unspecified. -WARNING: This method is redefined in some Rails core classes but should be all compatible in Rails 3.1. - NOTE: Defined in `active_support/core_ext/class/subclasses.rb`. #### `descendants` @@ -1157,7 +1160,7 @@ Inserting data into HTML templates needs extra care. For example, you can't just #### Safe Strings -Active Support has the concept of <i>(html) safe</i> strings since Rails 3. A safe string is one that is marked as being insertable into HTML as is. It is trusted, no matter whether it has been escaped or not. +Active Support has the concept of <i>(html) safe</i> strings. A safe string is one that is marked as being insertable into HTML as is. It is trusted, no matter whether it has been escaped or not. Strings are considered to be <i>unsafe</i> by default: @@ -1194,10 +1197,10 @@ Safe arguments are directly appended: "".html_safe + "<".html_safe # => "<" ``` -These methods should not be used in ordinary views. In Rails 3 unsafe values are automatically escaped: +These methods should not be used in ordinary views. Unsafe values are automatically escaped: ```erb -<%= @review.title %> <%# fine in Rails 3, escaped if needed %> +<%= @review.title %> <%# fine, escaped if needed %> ``` To insert something verbatim use the `raw` helper rather than calling `html_safe`: diff --git a/guides/source/api_documentation_guidelines.md b/guides/source/api_documentation_guidelines.md index 0126fc94d1..d0499878da 100644 --- a/guides/source/api_documentation_guidelines.md +++ b/guides/source/api_documentation_guidelines.md @@ -5,6 +5,9 @@ This guide documents the Ruby on Rails API documentation guidelines. After reading this guide, you will know: +* How to write effective prose for documentation purposes. +* Style guidelines for documenting different kinds of Ruby code. + -------------------------------------------------------------------------------- RDoc diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index fa4e714950..ff6787dae5 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -1,15 +1,15 @@ The Asset Pipeline ================== -This guide covers the asset pipeline introduced in Rails 3.1. +This guide covers the asset pipeline. After reading this guide, you will know: -* Understand what the asset pipeline is and what it does. -* Properly organize your application assets. -* Understand the benefits of the asset pipeline. -* Add a pre-processor to the pipeline. -* Package assets with a gem. +* How to understand what the asset pipeline is and what it does. +* How to properly organize your application assets. +* How to understand the benefits of the asset pipeline. +* How to add a pre-processor to the pipeline. +* How to package assets with a gem. -------------------------------------------------------------------------------- @@ -18,11 +18,9 @@ What is the Asset Pipeline? The asset pipeline provides a framework to concatenate and minify or compress JavaScript and CSS assets. It also adds the ability to write these assets in other languages such as CoffeeScript, Sass and ERB. -Prior to Rails 3.1 these features were added through third-party Ruby libraries such as Jammit and Sprockets. Rails 3.1 is integrated with Sprockets through Action Pack which depends on the `sprockets` gem, by default. - Making the asset pipeline a core feature of Rails means that all developers can benefit from the power of having their assets pre-processed, compressed and minified by one central library, Sprockets. This is part of Rails' "fast by default" strategy as outlined by DHH in his keynote at RailsConf 2011. -In Rails 3.1, the asset pipeline is enabled by default. It can be disabled in `config/application.rb` by putting this line inside the application class definition: +The asset pipeline is enabled by default. It can be disabled in `config/application.rb` by putting this line inside the application class definition: ```ruby config.assets.enabled = false @@ -100,7 +98,24 @@ In production, Rails precompiles these files to `public/assets` by default. The When you generate a scaffold or a controller, Rails also generates a JavaScript file (or CoffeeScript file if the `coffee-rails` gem is in the `Gemfile`) and a Cascading Style Sheet file (or SCSS file if `sass-rails` is in the `Gemfile`) for that controller. -For example, if you generate a `ProjectsController`, Rails will also add a new file at `app/assets/javascripts/projects.js.coffee` and another at `app/assets/stylesheets/projects.css.scss`. You should put any JavaScript or CSS unique to a controller inside their respective asset files, as these files can then be loaded just for these controllers with lines such as `<%= javascript_include_tag params[:controller] %>` or `<%= stylesheet_link_tag params[:controller] %>`. +For example, if you generate a `ProjectsController`, Rails will also add a new file at `app/assets/javascripts/projects.js.coffee` and another at `app/assets/stylesheets/projects.css.scss`. You should put any JavaScript or CSS unique to a controller inside their respective asset files, as these files can then be loaded just for these controllers with lines such as `<%= javascript_include_tag params[:controller] %>` or `<%= stylesheet_link_tag params[:controller] %>`. Note that you have to set `config.assets.precompile` in `config/environments/production.rb` if you want to precomepile them and use in production mode. You can append them one by one or do something like this: + + # config/environments/production.rb + config.assets.precompile << Proc.new { |path| + if path =~ /\.(css|js)\z/ + full_path = Rails.application.assets.resolve(path).to_path + app_assets_path = Rails.root.join('app', 'assets').to_path + if full_path.starts_with? app_assets_path + puts "including asset: " + full_path + true + else + puts "excluding asset: " + full_path + false + end + else + false + end + } NOTE: You must have an [ExecJS](https://github.com/sstephenson/execjs#readme) supported runtime in order to use CoffeeScript. If you are using Mac OS X or Windows you have a JavaScript runtime installed in your operating system. Check [ExecJS](https://github.com/sstephenson/execjs#readme) documentation to know all supported JavaScript runtimes. @@ -114,7 +129,7 @@ Pipeline assets can be placed inside an application in one of three locations: ` * `vendor/assets` is for assets that are owned by outside entities, such as code for JavaScript plugins and CSS frameworks. -#### Search paths +#### Search Paths When a file is referenced from a manifest or a helper, Sprockets searches the three default asset locations for it. @@ -162,7 +177,7 @@ Paths are traversed in the order that they occur in the search path. By default, It is important to note that files you want to reference outside a manifest must be added to the precompile array or they will not be available in the production environment. -#### Using index files +#### Using Index Files Sprockets uses files named `index` (with the relevant extensions) for a special purpose. @@ -270,8 +285,6 @@ For example, a new Rails application includes a default `app/assets/javascripts/ In JavaScript files, the directives begin with `//=`. In this case, the file is using the `require` and the `require_tree` directives. The `require` directive is used to tell Sprockets the files that you wish to require. Here, you are requiring the files `jquery.js` and `jquery_ujs.js` that are available somewhere in the search path for Sprockets. You need not supply the extensions explicitly. Sprockets assumes you are requiring a `.js` file when done from within a `.js` file. -NOTE. In Rails 3.1 the `jquery-rails` gem provides the `jquery.js` and `jquery_ujs.js` files via the asset pipeline. You won't see them in the application tree. - The `require_tree` directive tells Sprockets to recursively include _all_ JavaScript files in the specified directory into the output. These paths must be specified relative to the manifest file. You can also use the `require_directory` directive which includes all JavaScript files only in the directory specified, without recursion. Directives are processed top to bottom, but the order in which files are included by `require_tree` is unspecified. You should not rely on any particular order among those. If you need to ensure some particular JavaScript ends up above some other in the concatenated file, require the prerequisite file first in the manifest. Note that the family of `require` directives prevents files from being included twice in the output. @@ -337,7 +350,7 @@ would generate this HTML: The `body` param is required by Sprockets. -### Turning Debugging off +### Turning Debugging Off You can turn off debug mode by updating `config/environments/development.rb` to include: @@ -462,7 +475,7 @@ The default location for the manifest is the root of the location specified in ` NOTE: If there are missing precompiled files in production you will get an `Sprockets::Helpers::RailsHelper::AssetPaths::AssetNotPrecompiledError` exception indicating the name of the missing file(s). -#### Far-future Expires header +#### Far-future Expires Header Precompiled assets exist on the filesystem and are served directly by your web server. They do not have far-future headers by default, so to get the benefit of fingerprinting you'll have to update your server configuration to add them. @@ -492,7 +505,7 @@ location ~ ^/assets/ { } ``` -#### GZip compression +#### GZip Compression When files are precompiled, Sprockets also creates a [gzipped](http://en.wikipedia.org/wiki/Gzip) (.gz) version of your assets. Web servers are typically configured to use a moderate compression ratio as a compromise, but since precompilation happens once, Sprockets uses the maximum compression ratio, thus reducing the size of the data transfer to the minimum. On the other hand, web servers can be configured to serve compressed content directly from disk, rather than deflating non-compressed files themselves. @@ -648,7 +661,7 @@ This can be changed to something else: config.assets.prefix = "/some_other_path" ``` -This is a handy option if you are updating an existing project (pre Rails 3.1) that already uses this path or you wish to use this path for a new resource. +This is a handy option if you are updating an older project that didn't use the asset pipeline and that already uses this path or you wish to use this path for a new resource. ### X-Sendfile Headers diff --git a/guides/source/association_basics.md b/guides/source/association_basics.md index 43d3b20165..95adb4ff0a 100644 --- a/guides/source/association_basics.md +++ b/guides/source/association_basics.md @@ -5,9 +5,9 @@ This guide covers the association features of Active Record. After reading this guide, you will know: -* Declare associations between Active Record models. -* Understand the various types of Active Record associations. -* Use the methods added to your models by creating associations. +* How to declare associations between Active Record models. +* How to understand the various types of Active Record associations. +* How to use the methods added to your models by creating associations. -------------------------------------------------------------------------------- @@ -452,7 +452,7 @@ class CreateAssemblyPartJoinTable < ActiveRecord::Migration end ``` -We pass `id: false` to `create_table` because that table does not represent a model. That's required for the association to work properly. If you observe any strange behavior in a `has_and_belongs_to_many` association like mangled models IDs, or exceptions about conflicting IDs chances are you forgot that bit. +We pass `id: false` to `create_table` because that table does not represent a model. That's required for the association to work properly. If you observe any strange behavior in a `has_and_belongs_to_many` association like mangled models IDs, or exceptions about conflicting IDs, chances are you forgot that bit. ### Controlling Association Scope diff --git a/guides/source/caching_with_rails.md b/guides/source/caching_with_rails.md index e737dcab83..773102400a 100644 --- a/guides/source/caching_with_rails.md +++ b/guides/source/caching_with_rails.md @@ -104,7 +104,7 @@ Let's say you only wanted authenticated users to call actions on `ProductsContro ```ruby class ProductsController < ActionController - before_filter :authenticate + before_action :authenticate caches_action :index def index diff --git a/guides/source/command_line.md b/guides/source/command_line.md index 0a4a704cd9..746226fa96 100644 --- a/guides/source/command_line.md +++ b/guides/source/command_line.md @@ -5,11 +5,11 @@ Rails comes with every command line tool you'll need to After reading this guide, you will know: -* Create a Rails application. -* Generate models, controllers, database migrations, and unit tests. -* Start a development server. -* Experiment with objects through an interactive shell. -* Profile and benchmark your new creation. +* How to create a Rails application. +* How to generate models, controllers, database migrations, and unit tests. +* How to start a development server. +* How to experiment with objects through an interactive shell. +* How to profile and benchmark your new creation. -------------------------------------------------------------------------------- diff --git a/guides/source/configuring.md b/guides/source/configuring.md index dba2be3b71..446f767f0c 100644 --- a/guides/source/configuring.md +++ b/guides/source/configuring.md @@ -5,8 +5,8 @@ This guide covers the configuration and initialization features available to Rai After reading this guide, you will know: -* Adjust the behavior of your Rails applications. -* Add additional code to be run at application start time. +* How to adjust the behavior of your Rails applications. +* How to add additional code to be run at application start time. -------------------------------------------------------------------------------- @@ -135,8 +135,6 @@ These configuration methods are to be called on a `Rails::Railtie` object, such ### Configuring Assets -Rails 3.1 and up, by default, is set up to use the `sprockets` gem to manage assets within an application. This gem concatenates and compresses assets in order to make serving them much less painful. - * `config.assets.enabled` a flag that controls whether the asset pipeline is enabled. It is explicitly initialized in `config/application.rb`. * `config.assets.compress` a flag that enables the compression of compiled assets. It is explicitly set to true in `config/production.rb`. @@ -165,7 +163,7 @@ Rails 3.1 and up, by default, is set up to use the `sprockets` gem to manage ass ### Configuring Generators -Rails 3 allows you to alter what generators are used with the `config.generators` method. This method takes a block: +Rails allows you to alter what generators are used with the `config.generators` method. This method takes a block: ```ruby config.generators do |g| diff --git a/guides/source/contributing_to_ruby_on_rails.md b/guides/source/contributing_to_ruby_on_rails.md index fa6c335088..ce2a5a4902 100644 --- a/guides/source/contributing_to_ruby_on_rails.md +++ b/guides/source/contributing_to_ruby_on_rails.md @@ -5,11 +5,11 @@ This guide covers ways in which _you_ can become a part of the ongoing developme After reading this guide, you will know: -* Using GitHub to report issues. -* Cloning master and running the test suite. -* Helping to resolve existing issues. -* Contributing to the Ruby on Rails documentation. -* Contributing to the Ruby on Rails code. +* How to use GitHub to report issues. +* How to clone master and run the test suite. +* How to help resolve existing issues. +* How to contribute to the Ruby on Rails documentation. +* How to contribute to the Ruby on Rails code. Ruby on Rails is not "someone else's framework." Over the years, hundreds of people have contributed to Ruby on Rails ranging from a single character to massive architectural changes or significant documentation — all with the goal of making Ruby on Rails better for everyone. Even if you don't feel up to writing code or documentation yet, there are a variety of other ways that you can contribute, from reporting issues to testing patches. @@ -91,7 +91,7 @@ You can invoke `test_jdbcmysql`, `test_jdbcsqlite3` or `test_jdbcpostgresql` als The test suite runs with warnings enabled. Ideally, Ruby on Rails should issue no warnings, but there may be a few, as well as some from third-party libraries. Please ignore (or fix!) them, if any, and submit patches that do not issue new warnings. -As of this writing (December, 2010) they are specially noisy with Ruby 1.9. If you are sure about what you are doing and would like to have a more clear output, there's a way to override the flag: +As of this writing (December, 2010) they are especially noisy with Ruby 1.9. If you are sure about what you are doing and would like to have a more clear output, there's a way to override the flag: ```bash $ RUBYOPT=-W0 bundle exec rake test @@ -205,7 +205,7 @@ TIP: Changes that are cosmetic in nature and do not add anything substantial to ### Follow the Coding Conventions -Rails follows a simple set of coding style conventions. +Rails follows a simple set of coding style conventions: * Two spaces, no tabs (for indentation). * No trailing whitespace. Blank lines should not have any spaces. diff --git a/guides/source/debugging_rails_applications.md b/guides/source/debugging_rails_applications.md index 96112da50f..addc3a63a8 100644 --- a/guides/source/debugging_rails_applications.md +++ b/guides/source/debugging_rails_applications.md @@ -5,10 +5,10 @@ This guide introduces techniques for debugging Ruby on Rails applications. After reading this guide, you will know: -* Understand the purpose of debugging. -* Track down problems and issues in your application that your tests aren't identifying. -* Learn the different ways of debugging. -* Analyze the stack trace. +* The purpose of debugging. +* How to track down problems and issues in your application that your tests aren't identifying. +* The different ways of debugging. +* How to analyze the stack trace. -------------------------------------------------------------------------------- diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md index 79d59859c8..db43d62fcf 100644 --- a/guides/source/development_dependencies_install.md +++ b/guides/source/development_dependencies_install.md @@ -145,6 +145,9 @@ We need first to delete `.bundle/config` because Bundler remembers in that file In order to be able to run the test suite against MySQL you need to create a user named `rails` with privileges on the test databases: ```bash +$ mysql -uroot -p + +mysql> CREATE USER 'rails'@'localhost'; mysql> GRANT ALL PRIVILEGES ON activerecord_unittest.* to 'rails'@'localhost'; mysql> GRANT ALL PRIVILEGES ON activerecord_unittest2.* diff --git a/guides/source/engines.md b/guides/source/engines.md index f1e2780eae..116a7e67cd 100644 --- a/guides/source/engines.md +++ b/guides/source/engines.md @@ -35,7 +35,7 @@ Finally, engines would not have been possible without the work of James Adam, Pi Generating an engine -------------------- -To generate an engine with Rails 3.2, you will need to run the plugin generator and pass it options as appropriate to the need. For the "blorgh" example, you will need to create a "mountable" engine, running this command in a terminal: +To generate an engine, you will need to run the plugin generator and pass it options as appropriate to the need. For the "blorgh" example, you will need to create a "mountable" engine, running this command in a terminal: ```bash $ rails plugin new blorgh --mountable diff --git a/guides/source/form_helpers.md b/guides/source/form_helpers.md index b89d776d87..ee563e72d5 100644 --- a/guides/source/form_helpers.md +++ b/guides/source/form_helpers.md @@ -5,13 +5,13 @@ Forms in web applications are an essential interface for user input. However, fo After reading this guide, you will know: -* Create search forms and similar kind of generic forms not representing any specific model in your application. -* Make model-centric forms for creation and editing of specific database records. -* Generate select boxes from multiple types of data. -* Understand the date and time helpers Rails provides. -* Learn what makes a file upload form different. -* Learn some cases of building forms to external resources. -* Find out how to build complex forms. +* How to create search forms and similar kind of generic forms not representing any specific model in your application. +* How to make model-centric forms for creation and editing of specific database records. +* How to generate select boxes from multiple types of data. +* The date and time helpers Rails provides. +* What makes a file upload form different. +* Some cases of building forms to external resources. +* How to build complex forms. -------------------------------------------------------------------------------- @@ -594,8 +594,6 @@ The following two forms both upload a file. <% end %> ``` -NOTE: Since Rails 3.1, forms rendered using `form_for` have their encoding set to `multipart/form-data` automatically once a `file_field` is used inside the block. Previous versions required you to set this explicitly. - Rails provides the usual pair of helpers: the barebones `file_field_tag` and the model oriented `file_field`. The only difference with other helpers is that you cannot set a default value for file inputs as this would have no meaning. As you would expect in the first case the uploaded file is in `params[:picture]` and in the second case in `params[:person][:picture]`. ### What Gets Uploaded diff --git a/guides/source/generators.md b/guides/source/generators.md index f83f5c6691..62de5a70bb 100644 --- a/guides/source/generators.md +++ b/guides/source/generators.md @@ -5,18 +5,16 @@ Rails generators are an essential tool if you plan to improve your workflow. Wit After reading this guide, you will know: -* Learn how to see which generators are available in your application. -* Create a generator using templates. -* Learn how Rails searches for generators before invoking them. -* Customize your scaffold by creating new generators. -* Customize your scaffold by changing generator templates. -* Learn how to use fallbacks to avoid overwriting a huge set of generators. -* Learn how to create an application template. +* How to see which generators are available in your application. +* How to create a generator using templates. +* How Rails searches for generators before invoking them. +* How to customize your scaffold by creating new generators. +* How to customize your scaffold by changing generator templates. +* How to use fallbacks to avoid overwriting a huge set of generators. +* How to create an application template. -------------------------------------------------------------------------------- -NOTE: This guide is about generators in Rails 3, previous versions are not covered. - First Contact ------------- diff --git a/guides/source/getting_started.md b/guides/source/getting_started.md index b1ca8da292..54200768e6 100644 --- a/guides/source/getting_started.md +++ b/guides/source/getting_started.md @@ -5,7 +5,7 @@ This guide covers getting up and running with Ruby on Rails. After reading this guide, you will know: -* Installing Rails, creating a new Rails application, and connecting your +* How to install Rails, create a new Rails application, and connect your application to a database. * The general layout of a Rails application. * The basic principles of MVC (Model, View, Controller) and RESTful design. @@ -77,7 +77,7 @@ TIP: The examples below use # and $ to denote superuser and regular user termina Open up a command line prompt. On Mac OS X open Terminal.app, on Windows choose "Run" from your Start menu and type 'cmd.exe'. Any commands prefaced with a -dollar sign `$` should be run in the command line. Verify sure you have a +dollar sign `$` should be run in the command line. Verify that you have a current version of Ruby installed: ```bash @@ -101,11 +101,11 @@ To verify that you have everything installed correctly, you should be able to ru $ rails --version ``` -If it says something like "Rails 3.2.9" you are ready to continue. +If it says something like "Rails 3.2.9", you are ready to continue. ### Creating the Blog Application -Rails comes with a number of generators that are designed to make your development life easier. One of these is the new application generator, which will provide you with the foundation of a Rails application so that you don't have to write it yourself. +Rails comes with a number of scripts called generators that are designed to make your development life easier by creating everything that's necessary to start working on a particular task. One of these is the new application generator, which will provide you with the foundation of a fresh Rails application so that you don't have to write it yourself. To use this generator, open a terminal, navigate to a directory where you have rights to create files, and type: diff --git a/guides/source/i18n.md b/guides/source/i18n.md index 1131b7f245..399a4963d7 100644 --- a/guides/source/i18n.md +++ b/guides/source/i18n.md @@ -134,10 +134,10 @@ However, you would probably like to **provide support for more locales** in your WARNING: You may be tempted to store the chosen locale in a _session_ or a <em>cookie</em>, however **do not do this**. The locale should be transparent and a part of the URL. This way you won't break people's basic assumptions about the web itself: if you send a URL to a friend, they should see the same page and content as you. A fancy word for this would be that you're being [<em>RESTful</em>](http://en.wikipedia.org/wiki/Representational_State_Transfer. Read more about the RESTful approach in [Stefan Tilkov's articles](http://www.infoq.com/articles/rest-introduction). Sometimes there are exceptions to this rule and those are discussed below. -The _setting part_ is easy. You can set the locale in a `before_filter` in the `ApplicationController` like this: +The _setting part_ is easy. You can set the locale in a `before_action` in the `ApplicationController` like this: ```ruby -before_filter :set_locale +before_action :set_locale def set_locale I18n.locale = params[:locale] || I18n.default_locale @@ -160,7 +160,7 @@ One option you have is to set the locale from the domain name where your applica You can implement it like this in your `ApplicationController`: ```ruby -before_filter :set_locale +before_action :set_locale def set_locale I18n.locale = extract_locale_from_tld || I18n.default_locale @@ -203,7 +203,7 @@ This solution has aforementioned advantages, however, you may not be able or may ### Setting the Locale from the URL Params -The most usual way of setting (and passing) the locale would be to include it in URL params, as we did in the `I18n.locale = params[:locale]` _before_filter_ in the first example. We would like to have URLs like `www.example.com/books?locale=ja` or `www.example.com/ja/books` in this case. +The most usual way of setting (and passing) the locale would be to include it in URL params, as we did in the `I18n.locale = params[:locale]` _before_action_ in the first example. We would like to have URLs like `www.example.com/books?locale=ja` or `www.example.com/ja/books` in this case. This approach has almost the same set of advantages as setting the locale from the domain name: namely that it's RESTful and in accord with the rest of the World Wide Web. It does require a little bit more work to implement, though. diff --git a/guides/source/index.html.erb b/guides/source/index.html.erb index 71fe94a870..a8e4525c67 100644 --- a/guides/source/index.html.erb +++ b/guides/source/index.html.erb @@ -9,9 +9,7 @@ Ruby on Rails Guides <% content_for :index_section do %> <div id="subCol"> <dl> - <dd class="kindle">Rails Guides are also available for Kindle and <%= link_to 'Free Kindle Reading Apps', 'http://www.amazon.com/gp/kindle/kcp' %> for the iPad, -iPhone, Mac, Android, etc. Download them from <%= link_to 'here', @mobi %>. - </dd> + <dd class="kindle">Rails Guides are also available for <%= link_to 'Kindle', @mobi %>.</dd> <dd class="work-in-progress">Guides marked with this icon are currently being worked on and will not be available in the Guides Index menu. While still useful, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections.</dd> </dl> </div> diff --git a/guides/source/initialization.md b/guides/source/initialization.md index 6a34125654..32df508f9c 100644 --- a/guides/source/initialization.md +++ b/guides/source/initialization.md @@ -6,7 +6,7 @@ as of Rails 4. It is an extremely in-depth guide and recommended for advanced Ra After reading this guide, you will know: -* Using `rails server`. +* How to use `rails server`. -------------------------------------------------------------------------------- diff --git a/guides/source/kindle/rails_guides.opf.erb b/guides/source/kindle/rails_guides.opf.erb index 4e07664fd0..547abcbc19 100644 --- a/guides/source/kindle/rails_guides.opf.erb +++ b/guides/source/kindle/rails_guides.opf.erb @@ -32,7 +32,7 @@ <item id="toc" media-type="application/x-dtbncx+xml" href="toc.ncx" /> - <item id="cover" media-type="image/jpeg" href="images/rails_guides_kindle_cover.jpg"/> + <item id="cover" media-type="image/jpg" href="images/rails_guides_kindle_cover.jpg"/> </manifest> <spine toc="toc"> diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md index 394ef794d3..dbaa3ec576 100644 --- a/guides/source/layouts_and_rendering.md +++ b/guides/source/layouts_and_rendering.md @@ -5,10 +5,10 @@ This guide covers the basic layout features of Action Controller and Action View After reading this guide, you will know: -* Use the various rendering methods built into Rails. -* Create layouts with multiple content sections. -* Use partials to DRY up your views. -* Use nested layouts (sub-templates). +* How to use the various rendering methods built into Rails. +* How to create layouts with multiple content sections. +* How to use partials to DRY up your views. +* How to use nested layouts (sub-templates). -------------------------------------------------------------------------------- @@ -160,21 +160,6 @@ def update end ``` -To be explicit, you can use `render` with the `:action` option (though this is no longer necessary in Rails 3.0): - -```ruby -def update - @book = Book.find(params[:id]) - if @book.update_attributes(params[:book]) - redirect_to(@book) - else - render action: "edit" - end -end -``` - -WARNING: Using `render` with `:action` is a frequent source of confusion for Rails newcomers. The specified action is used to determine which view to render, but Rails does _not_ run any of the code for that action in the controller. Any instance variables that you require in the view must be set up in the current action before calling `render`. - #### Rendering an Action's Template from Another Controller What if you want to render a template from an entirely different controller from the one that contains the action code? You can also do that with `render`, which accepts the full path (relative to `app/views`) of the template to render. For example, if you're running code in an `AdminProductsController` that lives in `app/controllers/admin`, you can render the results of an action to a template in `app/views/products` this way: @@ -674,7 +659,7 @@ There are three tag options available for the `auto_discovery_link_tag`: The `javascript_include_tag` helper returns an HTML `script` tag for each source provided. -If you are using Rails with the [Asset Pipeline](asset_pipeline.html) enabled, this helper will generate a link to `/assets/javascripts/` rather than `public/javascripts` which was used in earlier versions of Rails. This link is then served by the Sprockets gem, which was introduced in Rails 3.1. +If you are using Rails with the [Asset Pipeline](asset_pipeline.html) enabled, this helper will generate a link to `/assets/javascripts/` rather than `public/javascripts` which was used in earlier versions of Rails. This link is then served by the asset pipeline. A JavaScript file within a Rails application or Rails engine goes in one of three locations: `app/assets`, `lib/assets` or `vendor/assets`. These locations are explained in detail in the [Asset Organization section in the Asset Pipeline Guide](asset_pipeline.html#asset-organization) @@ -843,7 +828,7 @@ You can even use dynamic paths such as `cache/#{current_site}/main/display`. The `image_tag` helper builds an HTML `<img />` tag to the specified file. By default, files are loaded from `public/images`. -WARNING: Note that you must specify the extension of the image. Previous versions of Rails would allow you to just use the image name and would append `.png` if no extension was given but Rails 3.0 does not. +WARNING: Note that you must specify the extension of the image. ```erb <%= image_tag "header.png" %> @@ -1091,8 +1076,6 @@ Every partial also has a local variable with the same name as the partial (minus Within the `customer` partial, the `customer` variable will refer to `@new_customer` from the parent view. -WARNING: In previous versions of Rails, the default local variable would look for an instance variable with the same name as the partial in the parent. This behavior was deprecated in 2.3 and has been removed in Rails 3.0. - If you have an instance of a model to render into a partial, you can use a shorthand syntax: ```erb @@ -1120,7 +1103,7 @@ Partials are very useful in rendering collections. When you pass a collection to When a partial is called with a pluralized collection, then the individual instances of the partial have access to the member of the collection being rendered via a variable named after the partial. In this case, the partial is `_product`, and within the `_product` partial, you can refer to `product` to get the instance that is being rendered. -In Rails 3.0, there is also a shorthand for this. Assuming `@products` is a collection of `product` instances, you can simply write this in the `index.html.erb` to produce the same result: +There is also a shorthand for this. Assuming `@products` is a collection of `product` instances, you can simply write this in the `index.html.erb` to produce the same result: ```html+erb <h1>Products</h1> diff --git a/guides/source/migrations.md b/guides/source/migrations.md index 829cbf2873..9840e7694f 100644 --- a/guides/source/migrations.md +++ b/guides/source/migrations.md @@ -334,7 +334,7 @@ end removes the `description` and `name` columns, creates a `part_number` string column and adds an index on it. Finally it renames the `upccode` column. -### When Helpers Aren't Enough +### When Helpers aren't Enough If the helpers provided by Active Record aren't enough you can use the `execute` method to execute arbitrary SQL: @@ -585,8 +585,8 @@ Occasionally you will make a mistake when writing a migration. If you have already run the migration then you cannot just edit the migration and run the migration again: Rails thinks it has already run the migration and so will do nothing when you run `rake db:migrate`. You must rollback the migration (for -example with `rake db:rollback`), edit your migration and then run `rake -db:migrate` to run the corrected version. +example with `rake db:rollback`), edit your migration and then run +`rake db:migrate` to run the corrected version. In general, editing existing migrations is not a good idea. You will be creating extra work for yourself and your co-workers and cause major headaches diff --git a/guides/source/performance_testing.md b/guides/source/performance_testing.md index a07f64ec29..182f7eb0fd 100644 --- a/guides/source/performance_testing.md +++ b/guides/source/performance_testing.md @@ -6,12 +6,12 @@ application. After reading this guide, you will know: -* Understand the various types of benchmarking and profiling metrics. -* Generate performance and benchmarking tests. -* Install and use a GC-patched Ruby binary to measure memory usage and object +* The various types of benchmarking and profiling metrics. +* How to generate performance and benchmarking tests. +* How to install and use a GC-patched Ruby binary to measure memory usage and object allocation. -* Understand the benchmarking information provided by Rails inside the log files. -* Learn about various tools facilitating benchmarking and profiling. +* The benchmarking information provided by Rails inside the log files. +* Various tools facilitating benchmarking and profiling. Performance testing is an integral part of the development cycle. It is very important that you don't make your end users wait for too long before the page diff --git a/guides/source/plugins.md b/guides/source/plugins.md index c7c843ec3a..f8f04c3c67 100644 --- a/guides/source/plugins.md +++ b/guides/source/plugins.md @@ -9,8 +9,8 @@ A Rails plugin is either an extension or a modification of the core framework. P After reading this guide, you will know: -* Creating a plugin from scratch. -* Writing and running tests for the plugin. +* How to create a plugin from scratch. +* How to write and run tests for the plugin. This guide describes how to build a test-driven plugin that will: @@ -27,16 +27,13 @@ goodness. Setup ----- -_"vendored plugins"_ were available in previous versions of Rails, but they are deprecated in -Rails 3.2, and will not be available in the future. - Currently, Rails plugins are built as gems, _gemified plugins_. They can be shared across different rails applications using RubyGems and Bundler if desired. ### Generate a gemified plugin. -Rails 3.1 ships with a `rails plugin new` command which creates a +Rails ships with a `rails plugin new` command which creates a skeleton for developing any kind of Rails extension with the ability to run integration tests using a dummy Rails application. See usage and options by asking for help: diff --git a/guides/source/rails_application_templates.md b/guides/source/rails_application_templates.md index 83c563ac78..9e694acb98 100644 --- a/guides/source/rails_application_templates.md +++ b/guides/source/rails_application_templates.md @@ -5,8 +5,8 @@ Application templates are simple Ruby files containing DSL for adding gems/initi After reading this guide, you will know: -* Use templates to generate/customize Rails applications. -* Write your own reusable application templates using the Rails template API. +* How to use templates to generate/customize Rails applications. +* How to write your own reusable application templates using the Rails template API. -------------------------------------------------------------------------------- diff --git a/guides/source/rails_on_rack.md b/guides/source/rails_on_rack.md index 531b35a973..1fac6d9883 100644 --- a/guides/source/rails_on_rack.md +++ b/guides/source/rails_on_rack.md @@ -5,10 +5,10 @@ This guide covers Rails integration with Rack and interfacing with other Rack co After reading this guide, you will know: -* Create Rails Metal applications. -* Use Rack Middlewares in your Rails applications. -* Understand Action Pack's internal Middleware stack. -* Define a custom Middleware stack. +* How to create Rails Metal applications. +* How to use Rack Middlewares in your Rails applications. +* Action Pack's internal Middleware stack. +* How to define a custom Middleware stack. -------------------------------------------------------------------------------- diff --git a/guides/source/routing.md b/guides/source/routing.md index 714c0b609e..241a7cfec6 100644 --- a/guides/source/routing.md +++ b/guides/source/routing.md @@ -5,11 +5,11 @@ This guide covers the user-facing features of Rails routing. After reading this guide, you will know: -* Understand the code in `routes.rb`. -* Construct your own routes, using either the preferred resourceful style or the `match` method. -* Identify what parameters to expect an action to receive. -* Automatically create paths and URLs using route helpers. -* Use advanced techniques such as constraints and Rack endpoints. +* How to interpret the code in `routes.rb`. +* How to construct your own routes, using either the preferred resourceful style or the `match` method. +* What parameters to expect an action to receive. +* How to automatically create paths and URLs using route helpers. +* Advanced techniques such as constraints and Rack endpoints. -------------------------------------------------------------------------------- @@ -733,12 +733,6 @@ get '*a/foo/*b', to: 'test#index' would match `zoo/woo/foo/bar/baz` with `params[:a]` equals `'zoo/woo'`, and `params[:b]` equals `'bar/baz'`. -NOTE: Starting from Rails 3.1, wildcard segments will always match the optional format segment by default. For example if you have this route: - -```ruby -get '*pages', to: 'pages#show' -``` - NOTE: By requesting `'/foo/bar.json'`, your `params[:pages]` will be equals to `'foo/bar'` with the request format of JSON. If you want the old 3.0.x behavior back, you could supply `format: false` like this: ```ruby diff --git a/guides/source/ruby_on_rails_guides_guidelines.md b/guides/source/ruby_on_rails_guides_guidelines.md index 2c3bc686ef..a78711f4b2 100644 --- a/guides/source/ruby_on_rails_guides_guidelines.md +++ b/guides/source/ruby_on_rails_guides_guidelines.md @@ -5,6 +5,9 @@ This guide documents guidelines for writing Ruby on Rails Guides. This guide fol After reading this guide, you will know: +* About the conventions to be used in Rails documentation. +* How to generate guides locally. + -------------------------------------------------------------------------------- Markdown diff --git a/guides/source/security.md b/guides/source/security.md index 6c32a8ff5b..532a1ae5cc 100644 --- a/guides/source/security.md +++ b/guides/source/security.md @@ -688,7 +688,7 @@ NOTE: _When sanitizing, protecting or verifying something, whitelists over black A blacklist can be a list of bad e-mail addresses, non-public actions or bad HTML tags. This is opposed to a whitelist which lists the good e-mail addresses, public actions, good HTML tags and so on. Although sometimes it is not possible to create a whitelist (in a SPAM filter, for example), _prefer to use whitelist approaches_: -* Use before_filter only: [...] instead of except: [...]. This way you don't forget to turn it off for newly added actions. +* Use before_action only: [...] instead of except: [...]. This way you don't forget to turn it off for newly added actions. * Use attr_accessible instead of attr_protected. See the mass-assignment section for details * Allow <strong> instead of removing <script> against Cross-Site Scripting (XSS). See below for details. * Don't try to correct user input by blacklists: diff --git a/guides/source/testing.md b/guides/source/testing.md index 9ce94f4c53..a9bce04522 100644 --- a/guides/source/testing.md +++ b/guides/source/testing.md @@ -6,9 +6,9 @@ application. After reading this guide, you will know: -* Understand Rails testing terminology. -* Write unit, functional, and integration tests for your application. -* Identify other popular testing approaches and plugins. +* Rails testing terminology. +* How to write unit, functional, and integration tests for your application. +* Other popular testing approaches and plugins. -------------------------------------------------------------------------------- diff --git a/guides/source/working_with_javascript_in_rails.md b/guides/source/working_with_javascript_in_rails.md index 26e3403ff9..0f5acfba3c 100644 --- a/guides/source/working_with_javascript_in_rails.md +++ b/guides/source/working_with_javascript_in_rails.md @@ -7,10 +7,10 @@ ease! After reading this guide, you will know: -* Quick introduction to Ajax. +* The basics of Ajax. * Unobtrusive JavaScript. * How Rails' built-in helpers assist you. -* Handling Ajax on the server side. +* How to handle Ajax on the server side. * The Turbolinks gem. ------------------------------------------------------------------------------- diff --git a/railties/lib/rails.rb b/railties/lib/rails.rb index 6bf2d8db20..b6a9eccdb6 100644 --- a/railties/lib/rails.rb +++ b/railties/lib/rails.rb @@ -24,13 +24,7 @@ module Rails autoload :InfoController, 'rails/info_controller' class << self - def application - @application ||= nil - end - - def application=(application) - @application = application - end + attr_accessor :application, :cache, :logger # The Configuration instance used to configure the Rails environment def configuration @@ -64,14 +58,6 @@ module Rails application.initialized? end - def logger - @logger ||= nil - end - - def logger=(logger) - @logger = logger - end - def backtrace_cleaner @backtrace_cleaner ||= begin # Relies on Active Support, so we have to lazy load to postpone definition until AS has been loaded @@ -95,14 +81,6 @@ module Rails @_env = ActiveSupport::StringInquirer.new(environment) end - def cache - @cache ||= nil - end - - def cache=(cache) - @cache = cache - end - # Returns all rails groups for loading based on: # # * The Rails environment; diff --git a/railties/lib/rails/code_statistics.rb b/railties/lib/rails/code_statistics.rb index 1aed2796c1..039360fcf6 100644 --- a/railties/lib/rails/code_statistics.rb +++ b/railties/lib/rails/code_statistics.rb @@ -1,6 +1,12 @@ class CodeStatistics #:nodoc: - TEST_TYPES = %w(Units Functionals Unit\ tests Functional\ tests Integration\ tests) + TEST_TYPES = ['Controller tests', + 'Helper tests', + 'Model tests', + 'Mailer tests', + 'Integration tests', + 'Functional tests (old)', + 'Unit tests (old)'] def initialize(*pairs) @pairs = pairs diff --git a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb index d6bce40b0c..08fe8a08e3 100644 --- a/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb +++ b/railties/lib/rails/generators/rails/scaffold_controller/templates/controller.rb @@ -4,6 +4,8 @@ require_dependency "<%= namespaced_file_path %>/application_controller" <% end -%> <% module_namespacing do -%> class <%= controller_class_name %>Controller < ApplicationController + before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy] + # GET <%= route_url %> # GET <%= route_url %>.json def index @@ -18,8 +20,6 @@ class <%= controller_class_name %>Controller < ApplicationController # GET <%= route_url %>/1 # GET <%= route_url %>/1.json def show - @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> - respond_to do |format| format.html # show.html.erb format.json { render json: <%= "@#{singular_table_name}" %> } @@ -39,7 +39,6 @@ class <%= controller_class_name %>Controller < ApplicationController # GET <%= route_url %>/1/edit def edit - @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> end # POST <%= route_url %> @@ -61,8 +60,6 @@ class <%= controller_class_name %>Controller < ApplicationController # PATCH/PUT <%= route_url %>/1 # PATCH/PUT <%= route_url %>/1.json def update - @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> - respond_to do |format| if @<%= orm_instance.update_attributes("#{singular_table_name}_params") %> format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> } @@ -77,7 +74,6 @@ class <%= controller_class_name %>Controller < ApplicationController # DELETE <%= route_url %>/1 # DELETE <%= route_url %>/1.json def destroy - @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> @<%= orm_instance.destroy %> respond_to do |format| @@ -87,6 +83,10 @@ class <%= controller_class_name %>Controller < ApplicationController end private + # Use callbacks to share common setup or constraints between actions. + def set_<%= singular_table_name %> + @<%= singular_table_name %> = <%= orm_class.find(class_name, "params[:id]") %> + end # Use this method to whitelist the permissible parameters. Example: params.require(:person).permit(:name, :age) # Also, you can specialize this method with per-user checking of permissible attributes. diff --git a/railties/lib/rails/generators/test_case.rb b/railties/lib/rails/generators/test_case.rb index 24308dcf6c..85a8914ccc 100644 --- a/railties/lib/rails/generators/test_case.rb +++ b/railties/lib/rails/generators/test_case.rb @@ -163,8 +163,8 @@ module Rails # end # end def assert_instance_method(method, content) - assert content =~ /def #{method}(\(.+\))?(.*?)\n end/m, "Expected to have method #{method}" - yield $2.strip if block_given? + assert content =~ /(\s+)def #{method}(\(.+\))?(.*?)\n\1end/m, "Expected to have method #{method}" + yield $3.strip if block_given? end alias :assert_method :assert_instance_method diff --git a/railties/test/abstract_unit.rb b/railties/test/abstract_unit.rb index 2ea1d2aff4..ecd5e03978 100644 --- a/railties/test/abstract_unit.rb +++ b/railties/test/abstract_unit.rb @@ -7,7 +7,6 @@ require 'minitest/autorun' require 'fileutils' require 'active_support' - require 'action_controller' require 'rails/all' diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index 03798d572a..ccb47663d4 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -55,8 +55,8 @@ module ApplicationTests def db_migrate_and_status Dir.chdir(app_path) do - `rails generate model book title:string` - `bundle exec rake db:migrate` + `rails generate model book title:string; + bundle exec rake db:migrate` output = `bundle exec rake db:migrate:status` assert_match(/database:\s+\S+#{expected[:database]}/, output) assert_match(/up\s+\d{14}\s+Create books/, output) @@ -78,9 +78,8 @@ module ApplicationTests def db_schema_dump Dir.chdir(app_path) do - `rails generate model book title:string` - `rake db:migrate` - `rake db:schema:dump` + `rails generate model book title:string; + rake db:migrate db:schema:dump` schema_dump = File.read("db/schema.rb") assert_match(/create_table \"books\"/, schema_dump) end @@ -97,9 +96,8 @@ module ApplicationTests def db_fixtures_load Dir.chdir(app_path) do - `rails generate model book title:string` - `bundle exec rake db:migrate` - `bundle exec rake db:fixtures:load` + `rails generate model book title:string; + bundle exec rake db:migrate db:fixtures:load` assert_match(/#{expected[:database]}/, ActiveRecord::Base.connection_config[:database]) require "#{app_path}/app/models/book" @@ -122,13 +120,11 @@ module ApplicationTests def db_structure_dump_and_load Dir.chdir(app_path) do - `rails generate model book title:string` - `bundle exec rake db:migrate` - `bundle exec rake db:structure:dump` + `rails generate model book title:string; + bundle exec rake db:migrate db:structure:dump` structure_dump = File.read("db/structure.sql") assert_match(/CREATE TABLE \"books\"/, structure_dump) - `bundle exec rake db:drop` - `bundle exec rake db:structure:load` + `bundle exec rake db:drop db:structure:load` assert_match(/#{expected[:database]}/, ActiveRecord::Base.connection_config[:database]) require "#{app_path}/app/models/book" @@ -152,10 +148,8 @@ module ApplicationTests def db_test_load_structure Dir.chdir(app_path) do - `rails generate model book title:string` - `bundle exec rake db:migrate` - `bundle exec rake db:structure:dump` - `bundle exec rake db:test:load_structure` + `rails generate model book title:string; + bundle exec rake db:migrate db:structure:dump db:test:load_structure` ActiveRecord::Base.configurations = Rails.application.config.database_configuration ActiveRecord::Base.establish_connection 'test' require "#{app_path}/app/models/book" @@ -178,4 +172,4 @@ module ApplicationTests end end end -end
\ No newline at end of file +end diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb index be520f2534..f65b5e2f2d 100644 --- a/railties/test/application/runner_test.rb +++ b/railties/test/application/runner_test.rb @@ -1,8 +1,10 @@ require 'isolation/abstract_unit' +require 'env_helpers' module ApplicationTests class RunnerTest < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation + include EnvHelpers def setup build_app @@ -73,21 +75,15 @@ module ApplicationTests end def test_environment_with_rails_env - orig = ENV['RAILS_ENV'] - ENV['RAILS_ENV'] = "production" - assert_match "production", Dir.chdir(app_path) { `bundle exec rails runner "puts Rails.env"` } - ensure - ENV['RAILS_ENV'] = orig + with_rails_env "production" do + assert_match "production", Dir.chdir(app_path) { `bundle exec rails runner "puts Rails.env"` } + end end def test_environment_with_rack_env - rack, rails = ENV['RACK_ENV'], ENV['RAILS_ENV'] - ENV['RACK_ENV'] = "production" - ENV['RAILS_ENV'] = nil - assert_match "production", Dir.chdir(app_path) { `bundle exec rails runner "puts Rails.env"` } - ensure - ENV['RAILS_ENV'] = rails - ENV['RACK_ENV'] = rack + with_rack_env "production" do + assert_match "production", Dir.chdir(app_path) { `bundle exec rails runner "puts Rails.env"` } + end end end end diff --git a/railties/test/commands/console_test.rb b/railties/test/commands/console_test.rb index 4062905c16..9e449856f4 100644 --- a/railties/test/commands/console_test.rb +++ b/railties/test/commands/console_test.rb @@ -1,14 +1,14 @@ require 'abstract_unit' +require 'env_helpers' require 'rails/commands/console' class Rails::ConsoleTest < ActiveSupport::TestCase + include EnvHelpers + class FakeConsole def self.start; end end - def setup - end - def test_sandbox_option console = Rails::Console.new(app, parse_arguments(["--sandbox"])) assert console.sandbox? @@ -85,7 +85,7 @@ class Rails::ConsoleTest < ActiveSupport::TestCase assert_match(/\sproduction\s/, output) end end - + def test_e_option start ['-e', 'special-production'] assert_match(/\sspecial-production\s/, output) @@ -133,20 +133,4 @@ class Rails::ConsoleTest < ActiveSupport::TestCase def parse_arguments(args) Rails::Console.parse_arguments(args) end - - def with_rails_env(env) - rails = ENV['RAILS_ENV'] - ENV['RAILS_ENV'] = env - yield - ensure - ENV['RAILS_ENV'] = rails - end - - def with_rack_env(env) - rack = ENV['RACK_ENV'] - ENV['RACK_ENV'] = env - with_rails_env(nil) { yield } - ensure - ENV['RACK_ENV'] = rack - end end diff --git a/railties/test/commands/server_test.rb b/railties/test/commands/server_test.rb index 6a75207ebb..cb57b3c0cd 100644 --- a/railties/test/commands/server_test.rb +++ b/railties/test/commands/server_test.rb @@ -1,7 +1,9 @@ require 'abstract_unit' +require 'env_helpers' require 'rails/commands/server' class Rails::ServerTest < ActiveSupport::TestCase + include EnvHelpers def test_environment_with_server_option args = ["thin", "-e", "production"] @@ -25,22 +27,16 @@ class Rails::ServerTest < ActiveSupport::TestCase end def test_environment_with_rails_env - rails = ENV['RAILS_ENV'] - ENV['RAILS_ENV'] = 'production' - server = Rails::Server.new - assert_equal 'production', server.options[:environment] - ensure - ENV['RAILS_ENV'] = rails + with_rails_env 'production' do + server = Rails::Server.new + assert_equal 'production', server.options[:environment] + end end def test_environment_with_rack_env - rack, rails = ENV['RACK_ENV'], ENV['RAILS_ENV'] - ENV['RAILS_ENV'] = nil - ENV['RACK_ENV'] = 'production' - server = Rails::Server.new - assert_equal 'production', server.options[:environment] - ensure - ENV['RACK_ENV'] = rack - ENV['RAILS_ENV'] = rails + with_rack_env 'production' do + server = Rails::Server.new + assert_equal 'production', server.options[:environment] + end end end diff --git a/railties/test/env_helpers.rb b/railties/test/env_helpers.rb new file mode 100644 index 0000000000..6223c85bbf --- /dev/null +++ b/railties/test/env_helpers.rb @@ -0,0 +1,26 @@ +module EnvHelpers + private + + def with_rails_env(env) + switch_env 'RAILS_ENV', env do + switch_env 'RACK_ENV', nil do + yield + end + end + end + + def with_rack_env(env) + switch_env 'RACK_ENV', env do + switch_env 'RAILS_ENV', nil do + yield + end + end + end + + def switch_env(key, value) + old, ENV[key] = ENV[key], value + yield + ensure + ENV[key] = old + end +end diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb index 8af92479c3..54734ed260 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -1,8 +1,11 @@ require 'generators/generators_test_helper' require 'rails/generators/rails/app/app_generator' +require 'env_helpers' class ActionsTest < Rails::Generators::TestCase include GeneratorsTestHelper + include EnvHelpers + tests Rails::Generators::AppGenerator arguments [destination_root] @@ -154,10 +157,9 @@ class ActionsTest < Rails::Generators::TestCase def test_rake_should_run_rake_command_with_default_env generator.expects(:run).once.with("rake log:clear RAILS_ENV=development", verbose: false) - old_env, ENV['RAILS_ENV'] = ENV["RAILS_ENV"], nil - action :rake, 'log:clear' - ensure - ENV["RAILS_ENV"] = old_env + with_rails_env nil do + action :rake, 'log:clear' + end end def test_rake_with_env_option_should_run_rake_command_in_env @@ -167,26 +169,23 @@ class ActionsTest < Rails::Generators::TestCase def test_rake_with_rails_env_variable_should_run_rake_command_in_env generator.expects(:run).once.with('rake log:clear RAILS_ENV=production', verbose: false) - old_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "production" - action :rake, 'log:clear' - ensure - ENV["RAILS_ENV"] = old_env + with_rails_env "production" do + action :rake, 'log:clear' + end end def test_env_option_should_win_over_rails_env_variable_when_running_rake generator.expects(:run).once.with('rake log:clear RAILS_ENV=production', verbose: false) - old_env, ENV["RAILS_ENV"] = ENV["RAILS_ENV"], "staging" - action :rake, 'log:clear', env: 'production' - ensure - ENV["RAILS_ENV"] = old_env + with_rails_env "staging" do + action :rake, 'log:clear', env: 'production' + end end def test_rake_with_sudo_option_should_run_rake_command_with_sudo generator.expects(:run).once.with("sudo rake log:clear RAILS_ENV=development", verbose: false) - old_env, ENV['RAILS_ENV'] = ENV["RAILS_ENV"], nil - action :rake, 'log:clear', sudo: true - ensure - ENV["RAILS_ENV"] = old_env + with_rails_env nil do + action :rake, 'log:clear', sudo: true + end end def test_capify_should_run_the_capify_command diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb index 8cacca668f..2a88dac635 100644 --- a/railties/test/generators/scaffold_controller_generator_test.rb +++ b/railties/test/generators/scaffold_controller_generator_test.rb @@ -20,17 +20,13 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase assert_match(/@users = User\.all/, m) end - assert_instance_method :show, content do |m| - assert_match(/@user = User\.find\(params\[:id\]\)/, m) - end + assert_instance_method :show, content assert_instance_method :new, content do |m| assert_match(/@user = User\.new/, m) end - assert_instance_method :edit, content do |m| - assert_match(/@user = User\.find\(params\[:id\]\)/, m) - end + assert_instance_method :edit, content assert_instance_method :create, content do |m| assert_match(/@user = User\.new\(user_params\)/, m) @@ -39,16 +35,18 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase end assert_instance_method :update, content do |m| - assert_match(/@user = User\.find\(params\[:id\]\)/, m) assert_match(/@user\.update_attributes\(user_params\)/, m) assert_match(/@user\.errors/, m) end assert_instance_method :destroy, content do |m| - assert_match(/@user = User\.find\(params\[:id\]\)/, m) assert_match(/@user\.destroy/, m) end + assert_instance_method :set_user, content do |m| + assert_match(/@user = User\.find\(params\[:id\]\)/, m) + end + assert_match(/def user_params/, content) assert_match(/params\.require\(:user\)\.permit\(:name, :age\)/, content) end diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index 54d5a9db6f..7fcc0a7409 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -30,17 +30,13 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase assert_match(/@product_lines = ProductLine\.all/, m) end - assert_instance_method :show, content do |m| - assert_match(/@product_line = ProductLine\.find\(params\[:id\]\)/, m) - end + assert_instance_method :show, content assert_instance_method :new, content do |m| assert_match(/@product_line = ProductLine\.new/, m) end - assert_instance_method :edit, content do |m| - assert_match(/@product_line = ProductLine\.find\(params\[:id\]\)/, m) - end + assert_instance_method :edit, content assert_instance_method :create, content do |m| assert_match(/@product_line = ProductLine\.new\(product_line_params\)/, m) @@ -49,15 +45,17 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end assert_instance_method :update, content do |m| - assert_match(/@product_line = ProductLine\.find\(params\[:id\]\)/, m) assert_match(/@product_line\.update_attributes\(product_line_params\)/, m) assert_match(/@product_line\.errors/, m) end assert_instance_method :destroy, content do |m| - assert_match(/@product_line = ProductLine\.find\(params\[:id\]\)/, m) assert_match(/@product_line\.destroy/, m) end + + assert_instance_method :set_product_line, content do |m| + assert_match(/@product_line = ProductLine\.find\(params\[:id\]\)/, m) + end end assert_file "test/controllers/product_lines_controller_test.rb" do |test| @@ -149,17 +147,13 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase assert_match(/@admin_roles = Admin::Role\.all/, m) end - assert_instance_method :show, content do |m| - assert_match(/@admin_role = Admin::Role\.find\(params\[:id\]\)/, m) - end + assert_instance_method :show, content assert_instance_method :new, content do |m| assert_match(/@admin_role = Admin::Role\.new/, m) end - assert_instance_method :edit, content do |m| - assert_match(/@admin_role = Admin::Role\.find\(params\[:id\]\)/, m) - end + assert_instance_method :edit, content assert_instance_method :create, content do |m| assert_match(/@admin_role = Admin::Role\.new\(admin_role_params\)/, m) @@ -168,15 +162,17 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end assert_instance_method :update, content do |m| - assert_match(/@admin_role = Admin::Role\.find\(params\[:id\]\)/, m) assert_match(/@admin_role\.update_attributes\(admin_role_params\)/, m) assert_match(/@admin_role\.errors/, m) end assert_instance_method :destroy, content do |m| - assert_match(/@admin_role = Admin::Role\.find\(params\[:id\]\)/, m) assert_match(/@admin_role\.destroy/, m) end + + assert_instance_method :set_admin_role, content do |m| + assert_match(/@admin_role = Admin::Role\.find\(params\[:id\]\)/, m) + end end assert_file "test/controllers/admin/roles_controller_test.rb", |