diff options
83 files changed, 969 insertions, 459 deletions
diff --git a/.travis.yml b/.travis.yml index 68d5c594ae..674d41c88f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,6 @@ script: 'ci/travis.rb' +before_install: + - gem install bundler rvm: - 1.9.3 env: diff --git a/actionmailer/CHANGELOG.md b/actionmailer/CHANGELOG.md index da5d5c4086..be7b82eecc 100644 --- a/actionmailer/CHANGELOG.md +++ b/actionmailer/CHANGELOG.md @@ -1,6 +1,11 @@ -## Rails 4.0.0 (unreleased) ## +## Rails 3.2.3 (unreleased) ## -* No changes +* Upgrade mail version to 2.4.3 *ML* + + +## Rails 3.2.2 (March 1, 2012) ## + +* No changes. ## Rails 3.2.1 (January 26, 2012) ## @@ -128,6 +133,7 @@ * Mail does not have "quoted_body", "quoted_subject" etc. All of these are accessed via body.encoded, subject.encoded etc * Every object in a Mail object returns an object, never a string. So Mail.body returns a Mail::Body class object, need to call #encoded or #decoded to get the string you want. + * Mail::Message#set_content_type does not exist, it is simply Mail::Message#content_type * Every mail message gets a unique message_id unless you specify one, had to change all the tests that check for equality with expected.encoded == actual.encoded to first replace their message_ids with control values diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 1800ff5839..64f82f24a2 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -267,6 +267,33 @@ module ActionMailer #:nodoc: # set something in the defaults using a proc, and then set the same thing inside of your # mailer method, it will get over written by the mailer method. # + # = Callbacks + # + # You can specify callbacks using before_filter and after_filter 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! + # + # def welcome + # mail + # end + # + # private + # + # def add_inline_attachment! + # attachments.inline["footer.jpg"] = File.read('/path/to/filename.jpg') + # end + # end + # + # Callbacks in ActionMailer are implemented using AbstractController::Callbacks, so you + # 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. + # # = Configuration options # # These options are specified on the class level, like @@ -330,6 +357,7 @@ module ActionMailer #:nodoc: include AbstractController::Helpers include AbstractController::Translation include AbstractController::AssetPaths + include AbstractController::Callbacks self.protected_instance_variables = [:@_action_has_layout] diff --git a/actionmailer/test/base_test.rb b/actionmailer/test/base_test.rb index 65550ab505..aed3ee1874 100644 --- a/actionmailer/test/base_test.rb +++ b/actionmailer/test/base_test.rb @@ -1,5 +1,7 @@ # encoding: utf-8 require 'abstract_unit' +require 'set' + require 'active_support/time' require 'mailers/base_mailer' @@ -550,6 +552,52 @@ 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! + + def welcome ; mail ; end + + private + def add_special_header! + headers('X-Special-Header' => 'Wow, so special') + end + end + + assert_equal('Wow, so special', BeforeFilterMailer.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! + + def welcome ; mail ; end + + private + def add_special_header! + headers('X-Special-Header' => 'Testing') + end + end + + assert_equal('Testing', AfterFilterMailer.welcome['X-Special-Header'].to_s) + end + + test "adding an inline attachment using a before_filter" do + class DefaultInlineAttachmentMailer < ActionMailer::Base + before_filter :add_inline_attachment! + + def welcome ; mail ; end + + private + def add_inline_attachment! + attachments.inline["footer.jpg"] = 'hey there' + end + end + + mail = DefaultInlineAttachmentMailer.welcome + assert_equal('image/jpeg; filename=footer.jpg', mail.attachments.inline.first['Content-Type'].to_s) + end + test "action methods should be refreshed after defining new method" do class FooMailer < ActionMailer::Base # this triggers action_methods @@ -559,7 +607,7 @@ class BaseTest < ActiveSupport::TestCase end end - assert_equal ["notify"], FooMailer.action_methods + assert_equal Set.new(["notify"]), FooMailer.action_methods end protected diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 78ac05389c..ebaf2d407c 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,12 +1,18 @@ ## Rails 4.0.0 (unreleased) ## -* Adds support for layouts when rendering a partial with a given collection. *serabe* +* Add `include_hidden` option to select tag. With `:include_hidden => true` select with `multiple` attribute doesn't generate hidden input with blank value. *Vasiliy Ermolovich* -* Allows the route helper `root` to take a string argument. For example, `root 'pages#main'`. *bcardarella* +* Removed default `size` option from the `text_field`, `search_field`, `telephone_field`, `url_field`, `email_field` helpers. *Philip Arndt* -* Forms of persisted records use always PATCH (via the `_method` hack). *fxn* +* Removed default `cols` and `rows` options from the `text_area` helper. *Philip Arndt* -* For resources, both PATCH and PUT are routed to the `update` action. *fxn* +* Adds support for layouts when rendering a partial with a given collection. *serabe* + +* Allows the route helper `root` to take a string argument. For example, `root 'pages#main'`. *bcardarella* + +* Forms of persisted records use always PATCH (via the `_method` hack). *fxn* + +* For resources, both PATCH and PUT are routed to the `update` action. *fxn* * Don't ignore `force_ssl` in development. This is a change of behavior - use a `:if` condition to recreate the old behavior. @@ -115,6 +121,21 @@ HTML5 `mark` element. *Brian Cardarella* +## Rails 3.2.3 (unreleased) ## + +* Upgrade rack-cache to 1.2. *José Valim* + +* ActionController::SessionManagement is removed. *Santiago Pastorino* + +* Since the router holds references to many parts of the system like engines, controllers and the application itself, inspecting the route set can actually be really slow, therefore we default alias inspect to to_s. *José Valim* + +* Add a new line after the textarea opening tag. Closes #393 *Rafael Mendonça França* + +* Always pass a respond block from to responder. We should let the responder decide what to do with the given overridden response block, and not short circuit it. *Prem Sichanugrist* + +* Fixes layout rendering regression from 3.2.2. *José Valim* + + ## Rails 3.2.2 (March 1, 2012) ## * Format lookup for partials is derived from the format in which the template is being rendered. Closes #5025 part 2 *Santiago Pastorino* @@ -305,6 +326,7 @@ returned by the class method attribute_names will be wrapped. This fixes the wrapping of nested attributes by adding them to attr_accessible. + ## Rails 3.1.4 (March 1, 2012) ## * Skip assets group in Gemfile and all assets configurations options @@ -321,6 +343,7 @@ * Assets should use the request protocol by default or default to relative if no request is available *Jonathan del Strother* + ## Rails 3.1.3 (November 20, 2011) ## * Downgrade sprockets to ~> 2.0.3. Using 2.1.0 caused regressions. @@ -330,6 +353,7 @@ *Jon Leighton* + ## Rails 3.1.2 (November 18, 2011) ## * Fix XSS security vulnerability in the `translate` helper method. When using interpolation @@ -371,6 +395,7 @@ * Ensure users upgrading from 3.0.x to 3.1.x will properly upgrade their flash object in session (issues #3298 and #2509) + ## Rails 3.1.1 (October 07, 2011) ## * javascript_path and stylesheet_path now refer to /assets if asset pipelining diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index 43cea3b79e..b068846a13 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -1,4 +1,5 @@ require 'erubis' +require 'set' require 'active_support/configurable' require 'active_support/descendants_tracker' require 'active_support/core_ext/module/anonymous' @@ -59,7 +60,7 @@ module AbstractController # itself. Finally, #hidden_actions are removed. # # ==== Returns - # * <tt>array</tt> - A list of all methods that should be considered actions. + # * <tt>set</tt> - A set of all methods that should be considered actions. def action_methods @action_methods ||= begin # All public instance methods of this class, including ancestors @@ -72,7 +73,7 @@ module AbstractController hidden_actions.to_a # Clear out AS callback method pollution - methods.reject { |method| method =~ /_one_time_conditions/ } + Set.new(methods.reject { |method| method =~ /_one_time_conditions/ }) end end diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 95de595f4f..6ce78f34b5 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -174,8 +174,8 @@ module ActionController # Shortcut helper that returns all the ActionController modules except the ones passed in the argument: # # class MetalController - # ActionController::Base.without_modules(:ParamsWrapper, :Streaming).each do |module| - # include module + # ActionController::Base.without_modules(:ParamsWrapper, :Streaming).each do |left| + # include left # end # end # diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index f413a2f318..73e044a092 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -259,8 +259,12 @@ module ActionController #:nodoc: end end - # Collects mimes and return the response for the negotiated format. Returns - # nil if :not_acceptable was sent to the client. + # Returns a Collector object containing the appropriate mime-type response + # for the current request, based on the available responses defined by a block. + # In typical usage this is the block passed to +respond_with+ or +respond_to+. + # + # Sends :not_acceptable to the client and returns nil if no suitable format + # is available. # def retrieve_collector_from_mimes(mimes=nil, &block) #:nodoc: mimes ||= collect_mimes_from_class_level diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index afa9243f02..3081c14c09 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -37,6 +37,10 @@ module ActionController #:nodoc: config_accessor :request_forgery_protection_token self.request_forgery_protection_token ||= :authenticity_token + # Controls how unverified request will be handled + config_accessor :request_forgery_protection_method + self.request_forgery_protection_method ||= :reset_session + # Controls whether request forgery protection is turned on or not. Turned off by default only in test mode. config_accessor :allow_forgery_protection self.allow_forgery_protection = true if allow_forgery_protection.nil? @@ -64,8 +68,10 @@ module ActionController #:nodoc: # Valid Options: # # * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified. + # * <tt>:with</tt> - Set the method to handle unverified request. Valid values: <tt>:exception</tt> and <tt>:reset_session</tt> (default). def protect_from_forgery(options = {}) self.request_forgery_protection_token ||= :authenticity_token + self.request_forgery_protection_method = options.delete(:with) if options.key?(:with) prepend_before_filter :verify_authenticity_token, options end end @@ -80,9 +86,19 @@ module ActionController #:nodoc: end # This is the method that defines the application behavior when a request is found to be unverified. - # By default, \Rails resets the session when it finds an unverified request. + # By default, \Rails uses <tt>request_forgery_protection_method</tt> when it finds an unverified request: + # + # * <tt>:reset_session</tt> - Resets the session. + # * <tt>:exception</tt>: - Raises ActionController::InvalidAuthenticityToken exception. def handle_unverified_request - reset_session + case request_forgery_protection_method + when :exception + raise ActionController::InvalidAuthenticityToken + when :reset_session + reset_session + else + raise ArgumentError, 'Invalid request forgery protection method, use :exception or :reset_session' + end end # Returns true or false if a request is verified. Checks: diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index e9a3ec5a22..7af30ed690 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -56,6 +56,9 @@ module ActionController # # assert that the "new" view template was rendered # assert_template "new" # + # # assert that the exact template "admin/posts/new" was rendered + # assert_template %r{\Aadmin/posts/new\Z} + # # # assert that the "_customer" partial was rendered twice # assert_template :partial => '_customer', :count => 2 # @@ -74,11 +77,11 @@ module ActionController response.body case options - when NilClass, String, Symbol + when NilClass, String, Symbol, Regexp options = options.to_s if Symbol === options rendered = @templates msg = message || sprintf("expecting <%s> but rendering with <%s>", - options, rendered.keys) + options.inspect, rendered.keys) assert_block(msg) do if options rendered.any? { |t,num| t.match(options) } @@ -121,6 +124,8 @@ module ActionController assert @partials.empty?, "Expected no partials to be rendered" end + else + raise ArgumentError, "assert_template only accepts a String, Symbol, Hash, Regexp, or nil" end end end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 54e3032298..43d5cf1471 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -56,10 +56,10 @@ module ActionView # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" /> # </div> # <label for="person_first_name">First name</label>: - # <input id="person_first_name" name="person[first_name]" size="30" type="text" /><br /> + # <input id="person_first_name" name="person[first_name]" type="text" /><br /> # # <label for="person_last_name">Last name</label>: - # <input id="person_last_name" name="person[last_name]" size="30" type="text" /><br /> + # <input id="person_last_name" name="person[last_name]" type="text" /><br /> # # <input name="commit" type="submit" value="Create Person" /> # </form> @@ -87,10 +87,10 @@ module ActionView # <input name="authenticity_token" type="hidden" value="NrOp5bsjoLRuK8IW5+dQEYjKGUJDe7TQoZVvq95Wteg=" /> # </div> # <label for="person_first_name">First name</label>: - # <input id="person_first_name" name="person[first_name]" size="30" type="text" value="John" /><br /> + # <input id="person_first_name" name="person[first_name]" type="text" value="John" /><br /> # # <label for="person_last_name">Last name</label>: - # <input id="person_last_name" name="person[last_name]" size="30" type="text" value="Smith" /><br /> + # <input id="person_last_name" name="person[last_name]" type="text" value="Smith" /><br /> # # <input name="commit" type="submit" value="Update Person" /> # </form> @@ -932,20 +932,20 @@ module ActionView # ==== Examples # # search_field(:user, :name) - # # => <input id="user_name" name="user[name]" size="30" type="search" /> + # # => <input id="user_name" name="user[name]" type="search" /> # search_field(:user, :name, :autosave => false) - # # => <input autosave="false" id="user_name" name="user[name]" size="30" type="search" /> + # # => <input autosave="false" id="user_name" name="user[name]" type="search" /> # search_field(:user, :name, :results => 3) - # # => <input id="user_name" name="user[name]" results="3" size="30" type="search" /> + # # => <input id="user_name" name="user[name]" results="3" type="search" /> # # Assume request.host returns "www.example.com" # search_field(:user, :name, :autosave => true) - # # => <input autosave="com.example.www" id="user_name" name="user[name]" results="10" size="30" type="search" /> + # # => <input autosave="com.example.www" id="user_name" name="user[name]" results="10" type="search" /> # search_field(:user, :name, :onsearch => true) - # # => <input id="user_name" incremental="true" name="user[name]" onsearch="true" size="30" type="search" /> + # # => <input id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" /> # search_field(:user, :name, :autosave => false, :onsearch => true) - # # => <input autosave="false" id="user_name" incremental="true" name="user[name]" onsearch="true" size="30" type="search" /> + # # => <input autosave="false" id="user_name" incremental="true" name="user[name]" onsearch="true" type="search" /> # search_field(:user, :name, :autosave => true, :onsearch => true) - # # => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" size="30" type="search" /> + # # => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" type="search" /> # def search_field(object_name, method, options = {}) Tags::SearchField.new(object_name, method, self, options).render @@ -954,7 +954,7 @@ module ActionView # Returns a text_field of type "tel". # # telephone_field("user", "phone") - # # => <input id="user_phone" name="user[phone]" size="30" type="tel" /> + # # => <input id="user_phone" name="user[phone]" type="tel" /> # def telephone_field(object_name, method, options = {}) Tags::TelField.new(object_name, method, self, options).render @@ -982,7 +982,7 @@ module ActionView # Returns a text_field of type "url". # # url_field("user", "homepage") - # # => <input id="user_homepage" size="30" name="user[homepage]" type="url" /> + # # => <input id="user_homepage" name="user[homepage]" type="url" /> # def url_field(object_name, method, options = {}) Tags::UrlField.new(object_name, method, self, options).render @@ -991,7 +991,7 @@ module ActionView # Returns a text_field of type "email". # # email_field("user", "address") - # # => <input id="user_address" size="30" name="user[address]" type="email" /> + # # => <input id="user_address" name="user[address]" type="email" /> # def email_field(object_name, method, options = {}) Tags::EmailField.new(object_name, method, self, options).render diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index 5be3da9b94..d61c2bbee2 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -153,6 +153,8 @@ module ActionView # form, and parameters extraction gets the last occurrence of any repeated # key in the query string, that works for ordinary forms. # + # In case if you don't want the helper to generate this hidden field you can specify <tt>:include_blank => false</tt> option. + # def select(object, method, choices, options = {}, html_options = {}) Tags::Select.new(object, method, self, choices, options, html_options).render end diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index ac9e530f01..d88f5babb9 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -14,6 +14,8 @@ module ActionView } JS_ESCAPE_MAP["\342\200\250".force_encoding('UTF-8').encode!] = '
' + JS_ESCAPE_MAP["\342\200\251".force_encoding('UTF-8').encode!] = '
' + # Escapes carriage returns and single and double quotes for JavaScript segments. # @@ -22,7 +24,7 @@ module ActionView # $('some_element').replaceWith('<%=j render 'some/element_template' %>'); def escape_javascript(javascript) if javascript - result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] } + result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"'])/u) {|match| JS_ESCAPE_MAP[match] } javascript.html_safe? ? result.html_safe : result else '' diff --git a/actionpack/lib/action_view/helpers/tags/base.rb b/actionpack/lib/action_view/helpers/tags/base.rb index 22c16de057..7c2f12d8e7 100644 --- a/actionpack/lib/action_view/helpers/tags/base.rb +++ b/actionpack/lib/action_view/helpers/tags/base.rb @@ -5,8 +5,6 @@ module ActionView include Helpers::ActiveModelInstanceTag, Helpers::TagHelper, Helpers::FormTagHelper include FormOptionsHelper - DEFAULT_FIELD_OPTIONS = { "size" => 30 } - attr_reader :object def initialize(object_name, method_name, template_object, options = {}) @@ -124,7 +122,8 @@ module ActionView html_options = html_options.stringify_keys add_default_name_and_id(html_options) select = content_tag("select", add_options(option_tags, options, value(object)), html_options) - if html_options["multiple"] + + if html_options["multiple"] && options.fetch(:include_hidden) { true } tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select else select diff --git a/actionpack/lib/action_view/helpers/tags/text_area.rb b/actionpack/lib/action_view/helpers/tags/text_area.rb index 461a049fc2..2e48850d5c 100644 --- a/actionpack/lib/action_view/helpers/tags/text_area.rb +++ b/actionpack/lib/action_view/helpers/tags/text_area.rb @@ -2,10 +2,8 @@ module ActionView module Helpers module Tags class TextArea < Base #:nodoc: - DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 } - def render - options = DEFAULT_TEXT_AREA_OPTIONS.merge(@options.stringify_keys) + options = @options.stringify_keys add_default_name_and_id(options) if size = options.delete("size") diff --git a/actionpack/lib/action_view/helpers/tags/text_field.rb b/actionpack/lib/action_view/helpers/tags/text_field.rb index ce5182d20f..024a1a8af2 100644 --- a/actionpack/lib/action_view/helpers/tags/text_field.rb +++ b/actionpack/lib/action_view/helpers/tags/text_field.rb @@ -4,8 +4,7 @@ module ActionView class TextField < Base #:nodoc: def render options = @options.stringify_keys - options["size"] = options["maxlength"] || DEFAULT_FIELD_OPTIONS["size"] unless options.key?("size") - options = DEFAULT_FIELD_OPTIONS.merge(options) + options["size"] = options["maxlength"] unless options.key?("size") options["type"] ||= field_type options["value"] = options.fetch("value"){ value_before_type_cast(object) } unless field_type == "file" options["value"] &&= ERB::Util.html_escape(options["value"]) diff --git a/actionpack/test/abstract/abstract_controller_test.rb b/actionpack/test/abstract/abstract_controller_test.rb index bf068aedcd..30d6a7ae14 100644 --- a/actionpack/test/abstract/abstract_controller_test.rb +++ b/actionpack/test/abstract/abstract_controller_test.rb @@ -1,4 +1,5 @@ require 'abstract_unit' +require 'set' module AbstractController module Testing @@ -254,7 +255,7 @@ module AbstractController class TestActionMethodsReloading < ActiveSupport::TestCase test "action_methods should be reloaded after defining a new method" do - assert_equal ["index"], Me6.action_methods + assert_equal Set.new(["index"]), Me6.action_methods end end diff --git a/actionpack/test/controller/base_test.rb b/actionpack/test/controller/base_test.rb index 7d0609751f..03d9873fc7 100644 --- a/actionpack/test/controller/base_test.rb +++ b/actionpack/test/controller/base_test.rb @@ -195,7 +195,6 @@ class UrlOptionsTest < ActionController::TestCase match "account/overview" end - @controller.class.send(:include, set.url_helpers) assert !@controller.class.action_methods.include?("account_overview_path") end end diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index 64ed7f667f..ef795dad89 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -43,7 +43,7 @@ class RequestForgeryProtectionController < ActionController::Base protect_from_forgery :only => %w(index meta) end -class RequestForgeryProtectionControllerUsingOldBehaviour < ActionController::Base +class RequestForgeryProtectionControllerUsingException < ActionController::Base include RequestForgeryProtectionActions protect_from_forgery :only => %w(index meta) @@ -215,7 +215,7 @@ class RequestForgeryProtectionControllerTest < ActionController::TestCase end end -class RequestForgeryProtectionControllerUsingOldBehaviourTest < ActionController::TestCase +class RequestForgeryProtectionControllerUsingExceptionTest < ActionController::TestCase include RequestForgeryProtectionTests def assert_blocked assert_raises(ActionController::InvalidAuthenticityToken) do diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index 36884846be..3af17f495c 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -23,10 +23,6 @@ class SendFileController < ActionController::Base def data send_data(file_data, options) end - - def multibyte_text_data - send_data("Кирилица\n祝您好運.", options) - end end class SendFileTest < ActionController::TestCase diff --git a/actionpack/test/template/active_model_helper_test.rb b/actionpack/test/template/active_model_helper_test.rb index 66a7bce71e..18468ee91a 100644 --- a/actionpack/test/template/active_model_helper_test.rb +++ b/actionpack/test/template/active_model_helper_test.rb @@ -29,14 +29,14 @@ class ActiveModelHelperTest < ActionView::TestCase def test_text_area_with_errors assert_dom_equal( - %(<div class="field_with_errors"><textarea cols="40" id="post_body" name="post[body]" rows="20">\nBack to the hill and over it again!</textarea></div>), + %(<div class="field_with_errors"><textarea id="post_body" name="post[body]">\nBack to the hill and over it again!</textarea></div>), text_area("post", "body") ) end def test_text_field_with_errors assert_dom_equal( - %(<div class="field_with_errors"><input id="post_author_name" name="post[author_name]" size="30" type="text" value="" /></div>), + %(<div class="field_with_errors"><input id="post_author_name" name="post[author_name]" type="text" value="" /></div>), text_field("post", "author_name") ) end @@ -76,7 +76,7 @@ class ActiveModelHelperTest < ActionView::TestCase end assert_dom_equal( - %(<div class="field_with_errors"><input id="post_author_name" name="post[author_name]" size="30" type="text" value="" /> <span class="error">can't be empty</span></div>), + %(<div class="field_with_errors"><input id="post_author_name" name="post[author_name]" type="text" value="" /> <span class="error">can't be empty</span></div>), text_field("post", "author_name") ) ensure diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 4eed5615f6..bc42e14b8a 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -249,35 +249,35 @@ class FormHelperTest < ActionView::TestCase end def test_label_with_block_in_erb - assert_equal "<label for=\"post_message\">\n Message\n <input id=\"post_message\" name=\"post[message]\" size=\"30\" type=\"text\" />\n</label>", view.render("test/label_with_block") + assert_equal "<label for=\"post_message\">\n Message\n <input id=\"post_message\" name=\"post[message]\" type=\"text\" />\n</label>", view.render("test/label_with_block") end def test_text_field assert_dom_equal( - '<input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />', text_field("post", "title") + '<input id="post_title" name="post[title]" type="text" value="Hello World" />', text_field("post", "title") ) assert_dom_equal( - '<input id="post_title" name="post[title]" size="30" type="password" />', password_field("post", "title") + '<input id="post_title" name="post[title]" type="password" />', password_field("post", "title") ) assert_dom_equal( - '<input id="post_title" name="post[title]" size="30" type="password" value="Hello World" />', password_field("post", "title", :value => @post.title) + '<input id="post_title" name="post[title]" type="password" value="Hello World" />', password_field("post", "title", :value => @post.title) ) assert_dom_equal( - '<input id="person_name" name="person[name]" size="30" type="password" />', password_field("person", "name") + '<input id="person_name" name="person[name]" type="password" />', password_field("person", "name") ) end def test_text_field_with_escapes @post.title = "<b>Hello World</b>" assert_dom_equal( - '<input id="post_title" name="post[title]" size="30" type="text" value="<b>Hello World</b>" />', text_field("post", "title") + '<input id="post_title" name="post[title]" type="text" value="<b>Hello World</b>" />', text_field("post", "title") ) end def test_text_field_with_html_entities @post.title = "The HTML Entity for & is &" assert_dom_equal( - '<input id="post_title" name="post[title]" size="30" type="text" value="The HTML Entity for & is &amp;" />', + '<input id="post_title" name="post[title]" type="text" value="The HTML Entity for & is &amp;" />', text_field("post", "title") ) end @@ -301,18 +301,18 @@ class FormHelperTest < ActionView::TestCase end def test_text_field_with_nil_value - expected = '<input id="post_title" name="post[title]" size="30" type="text" />' + expected = '<input id="post_title" name="post[title]" type="text" />' assert_dom_equal expected, text_field("post", "title", :value => nil) end def test_text_field_with_nil_name - expected = '<input id="post_title" size="30" type="text" value="Hello World" />' + expected = '<input id="post_title" type="text" value="Hello World" />' assert_dom_equal expected, text_field("post", "title", :name => nil) end def test_text_field_doesnt_change_param_values object_name = 'post[]' - expected = '<input id="post_123_title" name="post[123][title]" size="30" type="text" value="Hello World" />' + expected = '<input id="post_123_title" name="post[123][title]" type="text" value="Hello World" />' assert_equal expected, text_field(object_name, "title") assert_equal object_name, "post[]" end @@ -346,7 +346,7 @@ class FormHelperTest < ActionView::TestCase end def test_text_field_with_custom_type - assert_dom_equal '<input id="user_email" size="30" name="user[email]" type="email" />', + assert_dom_equal '<input id="user_email" name="user[email]" type="email" />', text_field("user", "email", :type => "email") end @@ -474,7 +474,7 @@ class FormHelperTest < ActionView::TestCase def test_text_area assert_dom_equal( - %{<textarea cols="40" id="post_body" name="post[body]" rows="20">\nBack to the hill and over it again!</textarea>}, + %{<textarea id="post_body" name="post[body]">\nBack to the hill and over it again!</textarea>}, text_area("post", "body") ) end @@ -482,14 +482,14 @@ class FormHelperTest < ActionView::TestCase def test_text_area_with_escapes @post.body = "Back to <i>the</i> hill and over it again!" assert_dom_equal( - %{<textarea cols="40" id="post_body" name="post[body]" rows="20">\nBack to <i>the</i> hill and over it again!</textarea>}, + %{<textarea id="post_body" name="post[body]">\nBack to <i>the</i> hill and over it again!</textarea>}, text_area("post", "body") ) end def test_text_area_with_alternate_value assert_dom_equal( - %{<textarea cols="40" id="post_body" name="post[body]" rows="20">\nTesting alternate values.</textarea>}, + %{<textarea id="post_body" name="post[body]">\nTesting alternate values.</textarea>}, text_area("post", "body", :value => 'Testing alternate values.') ) end @@ -497,7 +497,7 @@ class FormHelperTest < ActionView::TestCase def test_text_area_with_html_entities @post.body = "The HTML Entity for & is &" assert_dom_equal( - %{<textarea cols="40" id="post_body" name="post[body]" rows="20">\nThe HTML Entity for & is &amp;</textarea>}, + %{<textarea id="post_body" name="post[body]">\nThe HTML Entity for & is &amp;</textarea>}, text_area("post", "body") ) end @@ -510,12 +510,12 @@ class FormHelperTest < ActionView::TestCase end def test_search_field - expected = %{<input id="contact_notes_query" size="30" name="contact[notes_query]" type="search" />} + expected = %{<input id="contact_notes_query" name="contact[notes_query]" type="search" />} assert_dom_equal(expected, search_field("contact", "notes_query")) end def test_telephone_field - expected = %{<input id="user_cell" size="30" name="user[cell]" type="tel" />} + expected = %{<input id="user_cell" name="user[cell]" type="tel" />} assert_dom_equal(expected, telephone_field("user", "cell")) end @@ -546,12 +546,12 @@ class FormHelperTest < ActionView::TestCase end def test_url_field - expected = %{<input id="user_homepage" size="30" name="user[homepage]" type="url" />} + expected = %{<input id="user_homepage" name="user[homepage]" type="url" />} assert_dom_equal(expected, url_field("user", "homepage")) end def test_email_field - expected = %{<input id="user_address" size="30" name="user[address]" type="email" />} + expected = %{<input id="user_address" name="user[address]" type="email" />} assert_dom_equal(expected, email_field("user", "address")) end @@ -571,10 +571,10 @@ class FormHelperTest < ActionView::TestCase def test_explicit_name assert_dom_equal( - '<input id="post_title" name="dont guess" size="30" type="text" value="Hello World" />', text_field("post", "title", "name" => "dont guess") + '<input id="post_title" name="dont guess" type="text" value="Hello World" />', text_field("post", "title", "name" => "dont guess") ) assert_dom_equal( - %{<textarea cols="40" id="post_body" name="really!" rows="20">\nBack to the hill and over it again!</textarea>}, + %{<textarea id="post_body" name="really!">\nBack to the hill and over it again!</textarea>}, text_area("post", "body", "name" => "really!") ) assert_dom_equal( @@ -591,10 +591,10 @@ class FormHelperTest < ActionView::TestCase def test_explicit_id assert_dom_equal( - '<input id="dont guess" name="post[title]" size="30" type="text" value="Hello World" />', text_field("post", "title", "id" => "dont guess") + '<input id="dont guess" name="post[title]" type="text" value="Hello World" />', text_field("post", "title", "id" => "dont guess") ) assert_dom_equal( - %{<textarea cols="40" id="really!" name="post[body]" rows="20">\nBack to the hill and over it again!</textarea>}, + %{<textarea id="really!" name="post[body]">\nBack to the hill and over it again!</textarea>}, text_area("post", "body", "id" => "really!") ) assert_dom_equal( @@ -611,10 +611,10 @@ class FormHelperTest < ActionView::TestCase def test_nil_id assert_dom_equal( - '<input name="post[title]" size="30" type="text" value="Hello World" />', text_field("post", "title", "id" => nil) + '<input name="post[title]" type="text" value="Hello World" />', text_field("post", "title", "id" => nil) ) assert_dom_equal( - %{<textarea cols="40" name="post[body]" rows="20">\nBack to the hill and over it again!</textarea>}, + %{<textarea name="post[body]">\nBack to the hill and over it again!</textarea>}, text_area("post", "body", "id" => nil) ) assert_dom_equal( @@ -641,11 +641,11 @@ class FormHelperTest < ActionView::TestCase def test_index assert_dom_equal( - '<input name="post[5][title]" size="30" id="post_5_title" type="text" value="Hello World" />', + '<input name="post[5][title]" id="post_5_title" type="text" value="Hello World" />', text_field("post", "title", "index" => 5) ) assert_dom_equal( - %{<textarea cols="40" name="post[5][body]" id="post_5_body" rows="20">\nBack to the hill and over it again!</textarea>}, + %{<textarea name="post[5][body]" id="post_5_body">\nBack to the hill and over it again!</textarea>}, text_area("post", "body", "index" => 5) ) assert_dom_equal( @@ -668,11 +668,11 @@ class FormHelperTest < ActionView::TestCase def test_index_with_nil_id assert_dom_equal( - '<input name="post[5][title]" size="30" type="text" value="Hello World" />', + '<input name="post[5][title]" type="text" value="Hello World" />', text_field("post", "title", "index" => 5, 'id' => nil) ) assert_dom_equal( - %{<textarea cols="40" name="post[5][body]" rows="20">\nBack to the hill and over it again!</textarea>}, + %{<textarea name="post[5][body]">\nBack to the hill and over it again!</textarea>}, text_area("post", "body", "index" => 5, 'id' => nil) ) assert_dom_equal( @@ -700,10 +700,10 @@ class FormHelperTest < ActionView::TestCase label("post[]", "title") ) assert_dom_equal( - "<input id=\"post_#{pid}_title\" name=\"post[#{pid}][title]\" size=\"30\" type=\"text\" value=\"Hello World\" />", text_field("post[]","title") + "<input id=\"post_#{pid}_title\" name=\"post[#{pid}][title]\" type=\"text\" value=\"Hello World\" />", text_field("post[]","title") ) assert_dom_equal( - "<textarea cols=\"40\" id=\"post_#{pid}_body\" name=\"post[#{pid}][body]\" rows=\"20\">\nBack to the hill and over it again!</textarea>", + "<textarea id=\"post_#{pid}_body\" name=\"post[#{pid}][body]\">\nBack to the hill and over it again!</textarea>", text_area("post[]", "body") ) assert_dom_equal( @@ -722,11 +722,11 @@ class FormHelperTest < ActionView::TestCase def test_auto_index_with_nil_id pid = 123 assert_dom_equal( - "<input name=\"post[#{pid}][title]\" size=\"30\" type=\"text\" value=\"Hello World\" />", + "<input name=\"post[#{pid}][title]\" type=\"text\" value=\"Hello World\" />", text_field("post[]","title", :id => nil) ) assert_dom_equal( - "<textarea cols=\"40\" name=\"post[#{pid}][body]\" rows=\"20\">\nBack to the hill and over it again!</textarea>", + "<textarea name=\"post[#{pid}][body]\">\nBack to the hill and over it again!</textarea>", text_area("post[]", "body", :id => nil) ) assert_dom_equal( @@ -760,8 +760,8 @@ class FormHelperTest < ActionView::TestCase expected = whole_form("/posts/123", "create-post" , "edit_post", :method => 'patch') do "<label for='post_title'>The Title</label>" + - "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + - "<textarea name='post[body]' id='post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + + "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[secret]' type='hidden' value='0' />" + "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" + "<input name='commit' type='submit' value='Create post' />" + @@ -859,7 +859,7 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form("/posts/44", "edit_post_44" , "edit_post", :method => 'patch') do - "<input name='post[title]' size='30' type='text' id='post_title' value='And his name will be forty and four.' />" + + "<input name='post[title]' type='text' id='post_title' value='And his name will be forty and four.' />" + "<input name='commit' type='submit' value='Edit post' />" end @@ -877,8 +877,8 @@ class FormHelperTest < ActionView::TestCase expected = whole_form("/posts/123", "create-post", "edit_other_name", :method => 'patch') do "<label for='other_name_title' class='post_title'>Title</label>" + - "<input name='other_name[title]' size='30' id='other_name_title' value='Hello World' type='text' />" + - "<textarea name='other_name[body]' id='other_name_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='other_name[title]' id='other_name_title' value='Hello World' type='text' />" + + "<textarea name='other_name[body]' id='other_name_body'>\nBack to the hill and over it again!</textarea>" + "<input name='other_name[secret]' value='0' type='hidden' />" + "<input name='other_name[secret]' checked='checked' id='other_name_secret' value='1' type='checkbox' />" + "<input name='commit' value='Create post' type='submit' />" @@ -895,8 +895,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form("/", "create-post", "edit_post", "delete") do - "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + - "<textarea name='post[body]' id='post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + + "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[secret]' type='hidden' value='0' />" + "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" end @@ -912,8 +912,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form("/", "create-post", "edit_post", "delete") do - "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + - "<textarea name='post[body]' id='post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + + "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[secret]' type='hidden' value='0' />" + "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" end @@ -929,7 +929,7 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form("/search", "search-post", "new_post", "get") do - "<input name='post[title]' size='30' type='search' id='post_title' />" + "<input name='post[title]' type='search' id='post_title' />" end assert_dom_equal expected, output_buffer @@ -943,8 +943,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form("/", "create-post", "edit_post", :method => 'patch', :remote => true) do - "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + - "<textarea name='post[body]' id='post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + + "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[secret]' type='hidden' value='0' />" + "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" end @@ -960,8 +960,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form("/", "create-post", "edit_post", :method => 'patch', :remote => true) do - "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + - "<textarea name='post[body]' id='post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + + "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[secret]' type='hidden' value='0' />" + "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" end @@ -979,8 +979,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form("/posts", 'new_post', 'new_post', :remote => true) do - "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + - "<textarea name='post[body]' id='post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + + "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[secret]' type='hidden' value='0' />" + "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" end @@ -996,8 +996,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form("/", "create-post") do - "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + - "<textarea name='post[body]' id='post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + + "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[secret]' type='hidden' value='0' />" + "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" end @@ -1015,8 +1015,8 @@ class FormHelperTest < ActionView::TestCase expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do "<label for='post_123_title'>Title</label>" + - "<input name='post[123][title]' size='30' type='text' id='post_123_title' value='Hello World' />" + - "<textarea name='post[123][body]' id='post_123_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[123][title]' type='text' id='post_123_title' value='Hello World' />" + + "<textarea name='post[123][body]' id='post_123_body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[123][secret]' type='hidden' value='0' />" + "<input name='post[123][secret]' checked='checked' type='checkbox' id='post_123_secret' value='1' />" end @@ -1032,8 +1032,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do - "<input name='post[][title]' size='30' type='text' id='post__title' value='Hello World' />" + - "<textarea name='post[][body]' id='post__body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[][title]' type='text' id='post__title' value='Hello World' />" + + "<textarea name='post[][body]' id='post__body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[][secret]' type='hidden' value='0' />" + "<input name='post[][secret]' checked='checked' type='checkbox' id='post__secret' value='1' />" end @@ -1049,8 +1049,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', 'patch') do - "<input name='post[title]' size='30' type='text' id='namespace_post_title' value='Hello World' />" + - "<textarea name='post[body]' id='namespace_post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[title]' type='text' id='namespace_post_title' value='Hello World' />" + + "<textarea name='post[body]' id='namespace_post_body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[secret]' type='hidden' value='0' />" + "<input name='post[secret]' checked='checked' type='checkbox' id='namespace_post_secret' value='1' />" end @@ -1066,7 +1066,7 @@ class FormHelperTest < ActionView::TestCase expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', 'patch') do "<label for='namespace_post_title'>Title</label>" + - "<input name='post[title]' size='30' type='text' id='namespace_post_title' value='Hello World' />" + "<input name='post[title]' type='text' id='namespace_post_title' value='Hello World' />" end assert_dom_equal expected, output_buffer @@ -1080,7 +1080,7 @@ class FormHelperTest < ActionView::TestCase expected_1 = whole_form('/posts/123', 'namespace_1_edit_post_123', 'edit_post', 'patch') do "<label for='namespace_1_post_title'>Title</label>" + - "<input name='post[title]' size='30' type='text' id='namespace_1_post_title' value='Hello World' />" + "<input name='post[title]' type='text' id='namespace_1_post_title' value='Hello World' />" end assert_dom_equal expected_1, output_buffer @@ -1092,7 +1092,7 @@ class FormHelperTest < ActionView::TestCase expected_2 = whole_form('/posts/123', 'namespace_2_edit_post_123', 'edit_post', 'patch') do "<label for='namespace_2_post_title'>Title</label>" + - "<input name='post[title]' size='30' type='text' id='namespace_2_post_title' value='Hello World' />" + "<input name='post[title]' type='text' id='namespace_2_post_title' value='Hello World' />" end assert_dom_equal expected_2, output_buffer @@ -1109,9 +1109,9 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', 'patch') do - "<input name='post[title]' size='30' type='text' id='namespace_post_title' value='Hello World' />" + - "<textarea name='post[body]' id='namespace_post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + - "<input name='post[comment][body]' size='30' type='text' id='namespace_post_comment_body' value='Hello World' />" + "<input name='post[title]' type='text' id='namespace_post_title' value='Hello World' />" + + "<textarea name='post[body]' id='namespace_post_body'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[comment][body]' type='text' id='namespace_post_comment_body' value='Hello World' />" end assert_dom_equal expected, output_buffer @@ -1192,7 +1192,7 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - "<input name='post[comment][body]' size='30' type='text' id='post_comment_body' value='Hello World' />" + "<input name='post[comment][body]' type='text' id='post_comment_body' value='Hello World' />" end assert_dom_equal expected, output_buffer @@ -1207,8 +1207,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do - "<input name='post[123][title]' size='30' type='text' id='post_123_title' value='Hello World' />" + - "<input name='post[123][comment][][name]' size='30' type='text' id='post_123_comment__name' value='new comment' />" + "<input name='post[123][title]' type='text' id='post_123_title' value='Hello World' />" + + "<input name='post[123][comment][][name]' type='text' id='post_123_comment__name' value='new comment' />" end assert_dom_equal expected, output_buffer @@ -1223,8 +1223,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'patch') do - "<input name='post[1][title]' size='30' type='text' id='post_1_title' value='Hello World' />" + - "<input name='post[1][comment][1][name]' size='30' type='text' id='post_1_comment_1_name' value='new comment' />" + "<input name='post[1][title]' type='text' id='post_1_title' value='Hello World' />" + + "<input name='post[1][comment][1][name]' type='text' id='post_1_comment_1_name' value='new comment' />" end assert_dom_equal expected, output_buffer @@ -1238,7 +1238,7 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'patch') do - "<input name='post[1][comment][title]' size='30' type='text' id='post_1_comment_title' value='Hello World' />" + "<input name='post[1][comment][title]' type='text' id='post_1_comment_title' value='Hello World' />" end assert_dom_equal expected, output_buffer @@ -1252,7 +1252,7 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'patch') do - "<input name='post[1][comment][5][title]' size='30' type='text' id='post_1_comment_5_title' value='Hello World' />" + "<input name='post[1][comment][5][title]' type='text' id='post_1_comment_5_title' value='Hello World' />" end assert_dom_equal expected, output_buffer @@ -1266,7 +1266,7 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do - "<input name='post[123][comment][title]' size='30' type='text' id='post_123_comment_title' value='Hello World' />" + "<input name='post[123][comment][title]' type='text' id='post_123_comment_title' value='Hello World' />" end assert_dom_equal expected, output_buffer @@ -1294,7 +1294,7 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do - "<input name='post[123][comment][123][title]' size='30' type='text' id='post_123_comment_123_title' value='Hello World' />" + "<input name='post[123][comment][123][title]' type='text' id='post_123_comment_123_title' value='Hello World' />" end assert_dom_equal expected, output_buffer @@ -1314,9 +1314,9 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do - "<input name='post[123][comment][5][title]' size='30' type='text' id='post_123_comment_5_title' value='Hello World' />" + "<input name='post[123][comment][5][title]' type='text' id='post_123_comment_5_title' value='Hello World' />" end + whole_form('/posts/123', 'edit_post', 'edit_post', 'patch') do - "<input name='post[1][comment][123][title]' size='30' type='text' id='post_1_comment_123_title' value='Hello World' />" + "<input name='post[1][comment][123][title]' type='text' id='post_1_comment_123_title' value='Hello World' />" end assert_dom_equal expected, output_buffer @@ -1333,8 +1333,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="new author" />' + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="new author" />' end assert_dom_equal expected, output_buffer @@ -1360,8 +1360,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="author #321" />' + + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' + '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' end @@ -1379,8 +1379,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="author #321" />' + + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' + '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' end @@ -1398,8 +1398,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="author #321" />' + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' end assert_dom_equal expected, output_buffer @@ -1416,8 +1416,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="author #321" />' + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' end assert_dom_equal expected, output_buffer @@ -1434,8 +1434,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="author #321" />' + + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' + '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' end @@ -1454,9 +1454,9 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' + - '<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="author #321" />' + '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' end assert_dom_equal expected, output_buffer @@ -1475,10 +1475,10 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' + + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' + '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' + - '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' + + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />' + '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />' end @@ -1502,11 +1502,11 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="author #321" />' + + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' + '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' + - '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' + - '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' + + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />' end assert_dom_equal expected, output_buffer @@ -1529,10 +1529,10 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="author #321" />' + - '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' + - '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' + + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />' end assert_dom_equal expected, output_buffer @@ -1555,11 +1555,11 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="author #321" />' + + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="author #321" />' + '<input id="post_author_attributes_id" name="post[author_attributes][id]" type="hidden" value="321" />' + - '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' + - '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' + + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />' end assert_dom_equal expected, output_buffer @@ -1578,10 +1578,10 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' + + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' + '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' + - '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' + + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />' + '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />' end @@ -1602,11 +1602,11 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' + - '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' + '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />' + - '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />' end assert_dom_equal expected, output_buffer @@ -1625,9 +1625,9 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="new comment" />' + - '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" />' + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="new comment" />' + + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="new comment" />' end assert_dom_equal expected, output_buffer @@ -1646,10 +1646,10 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #321" />' + + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #321" />' + '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' + - '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" />' + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="new comment" />' end assert_dom_equal expected, output_buffer @@ -1664,7 +1664,7 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' end assert_dom_equal expected, output_buffer @@ -1681,10 +1681,10 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' + + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' + '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' + - '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' + + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />' + '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />' end @@ -1702,10 +1702,10 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' + + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' + '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' + - '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' + + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />' + '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />' end @@ -1724,10 +1724,10 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #1" />' + + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #1" />' + '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="1" />' + - '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="comment #2" />' + + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="comment #2" />' + '<input id="post_comments_attributes_1_id" name="post[comments_attributes][1][id]" type="hidden" value="2" />' end @@ -1747,10 +1747,10 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input name="post[title]" size="30" type="text" id="post_title" value="Hello World" />' + - '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #321" />' + + '<input name="post[title]" type="text" id="post_title" value="Hello World" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #321" />' + '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' + - '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" size="30" type="text" value="new comment" />' + '<input id="post_comments_attributes_1_name" name="post[comments_attributes][1][name]" type="text" value="new comment" />' end assert_dom_equal expected, output_buffer @@ -1767,7 +1767,7 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input id="post_comments_attributes_abc_name" name="post[comments_attributes][abc][name]" size="30" type="text" value="comment #321" />' + + '<input id="post_comments_attributes_abc_name" name="post[comments_attributes][abc][name]" type="text" value="comment #321" />' + '<input id="post_comments_attributes_abc_id" name="post[comments_attributes][abc][id]" type="hidden" value="321" />' end @@ -1803,16 +1803,16 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" size="30" type="text" value="comment #321" />' + - '<input id="post_comments_attributes_0_relevances_attributes_0_value" name="post[comments_attributes][0][relevances_attributes][0][value]" size="30" type="text" value="commentrelevance #314" />' + + '<input id="post_comments_attributes_0_name" name="post[comments_attributes][0][name]" type="text" value="comment #321" />' + + '<input id="post_comments_attributes_0_relevances_attributes_0_value" name="post[comments_attributes][0][relevances_attributes][0][value]" type="text" value="commentrelevance #314" />' + '<input id="post_comments_attributes_0_relevances_attributes_0_id" name="post[comments_attributes][0][relevances_attributes][0][id]" type="hidden" value="314" />' + '<input id="post_comments_attributes_0_id" name="post[comments_attributes][0][id]" type="hidden" value="321" />' + - '<input id="post_tags_attributes_0_value" name="post[tags_attributes][0][value]" size="30" type="text" value="tag #123" />' + - '<input id="post_tags_attributes_0_relevances_attributes_0_value" name="post[tags_attributes][0][relevances_attributes][0][value]" size="30" type="text" value="tagrelevance #3141" />' + + '<input id="post_tags_attributes_0_value" name="post[tags_attributes][0][value]" type="text" value="tag #123" />' + + '<input id="post_tags_attributes_0_relevances_attributes_0_value" name="post[tags_attributes][0][relevances_attributes][0][value]" type="text" value="tagrelevance #3141" />' + '<input id="post_tags_attributes_0_relevances_attributes_0_id" name="post[tags_attributes][0][relevances_attributes][0][id]" type="hidden" value="3141" />' + '<input id="post_tags_attributes_0_id" name="post[tags_attributes][0][id]" type="hidden" value="123" />' + - '<input id="post_tags_attributes_1_value" name="post[tags_attributes][1][value]" size="30" type="text" value="tag #456" />' + - '<input id="post_tags_attributes_1_relevances_attributes_0_value" name="post[tags_attributes][1][relevances_attributes][0][value]" size="30" type="text" value="tagrelevance #31415" />' + + '<input id="post_tags_attributes_1_value" name="post[tags_attributes][1][value]" type="text" value="tag #456" />' + + '<input id="post_tags_attributes_1_relevances_attributes_0_value" name="post[tags_attributes][1][relevances_attributes][0][value]" type="text" value="tagrelevance #31415" />' + '<input id="post_tags_attributes_1_relevances_attributes_0_id" name="post[tags_attributes][1][relevances_attributes][0][id]" type="hidden" value="31415" />' + '<input id="post_tags_attributes_1_id" name="post[tags_attributes][1][id]" type="hidden" value="456" />' end @@ -1830,7 +1830,7 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - '<input id="post_author_attributes_name" name="post[author_attributes][name]" size="30" type="text" value="hash backed author" />' + '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="hash backed author" />' end assert_dom_equal expected, output_buffer @@ -1844,8 +1844,8 @@ class FormHelperTest < ActionView::TestCase end expected = - "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + - "<textarea name='post[body]' id='post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + + "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[secret]' type='hidden' value='0' />" + "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" @@ -1860,8 +1860,8 @@ class FormHelperTest < ActionView::TestCase end expected = - "<input name='post[123][title]' size='30' type='text' id='post_123_title' value='Hello World' />" + - "<textarea name='post[123][body]' id='post_123_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[123][title]' type='text' id='post_123_title' value='Hello World' />" + + "<textarea name='post[123][body]' id='post_123_body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[123][secret]' type='hidden' value='0' />" + "<input name='post[123][secret]' checked='checked' type='checkbox' id='post_123_secret' value='1' />" @@ -1876,8 +1876,8 @@ class FormHelperTest < ActionView::TestCase end expected = - "<input name='post[][title]' size='30' type='text' id='post__title' value='Hello World' />" + - "<textarea name='post[][body]' id='post__body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[][title]' type='text' id='post__title' value='Hello World' />" + + "<textarea name='post[][body]' id='post__body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[][secret]' type='hidden' value='0' />" + "<input name='post[][secret]' checked='checked' type='checkbox' id='post__secret' value='1' />" @@ -1892,8 +1892,8 @@ class FormHelperTest < ActionView::TestCase end expected = - "<input name='post[abc][title]' size='30' type='text' id='post_abc_title' value='Hello World' />" + - "<textarea name='post[abc][body]' id='post_abc_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[abc][title]' type='text' id='post_abc_title' value='Hello World' />" + + "<textarea name='post[abc][body]' id='post_abc_body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[abc][secret]' type='hidden' value='0' />" + "<input name='post[abc][secret]' checked='checked' type='checkbox' id='post_abc_secret' value='1' />" @@ -1908,8 +1908,8 @@ class FormHelperTest < ActionView::TestCase end expected = - "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + - "<textarea name='post[body]' id='post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + + "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[secret]' type='hidden' value='0' />" + "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" @@ -1924,8 +1924,8 @@ class FormHelperTest < ActionView::TestCase end expected = - "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + - "<textarea name='post[body]' id='post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + + "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" + "<input name='post[secret]' type='hidden' value='0' />" + "<input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' />" @@ -1939,7 +1939,7 @@ class FormHelperTest < ActionView::TestCase end assert_dom_equal "<label for=\"author_post_title\">Title</label>" + - "<input name='author[post][title]' size='30' type='text' id='author_post_title' value='Hello World' />", + "<input name='author[post][title]' type='text' id='author_post_title' value='Hello World' />", output_buffer end @@ -1950,7 +1950,7 @@ class FormHelperTest < ActionView::TestCase end assert_dom_equal "<label for=\"author_post_1_title\">Title</label>" + - "<input name='author[post][1][title]' size='30' type='text' id='author_post_1_title' value='Hello World' />", + "<input name='author[post][1][title]' type='text' id='author_post_1_title' value='Hello World' />", output_buffer end @@ -1969,8 +1969,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'create-post', 'edit_post', :method => 'patch') do - "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + - "<textarea name='post[body]' id='post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + + "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" + "<input name='parent_post[secret]' type='hidden' value='0' />" + "<input name='parent_post[secret]' checked='checked' type='checkbox' id='parent_post_secret' value='1' />" end @@ -1989,9 +1989,9 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'create-post', 'edit_post', :method => 'patch') do - "<input name='post[title]' size='30' type='text' id='post_title' value='Hello World' />" + - "<textarea name='post[body]' id='post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea>" + - "<input name='post[comment][name]' type='text' id='post_comment_name' value='new comment' size='30' />" + "<input name='post[title]' type='text' id='post_title' value='Hello World' />" + + "<textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea>" + + "<input name='post[comment][name]' type='text' id='post_comment_name' value='new comment' />" end assert_dom_equal expected, output_buffer @@ -2005,7 +2005,7 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'patch') do - "<input name='post[category][name]' type='text' size='30' id='post_category_name' />" + "<input name='post[category][name]' type='text' id='post_category_name' />" end assert_dom_equal expected, output_buffer @@ -2029,8 +2029,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - "<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>" + - "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea><br/>" + + "<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>" + + "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea><br/>" + "<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' /><br/>" end @@ -2048,8 +2048,8 @@ class FormHelperTest < ActionView::TestCase end expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do - "<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>" + - "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea><br/>" + + "<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>" + + "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea><br/>" + "<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' /><br/>" end @@ -2066,8 +2066,8 @@ class FormHelperTest < ActionView::TestCase end expected = - "<label for='title'>Title:</label> <input name='post[title]' size='30' type='text' id='post_title' value='Hello World' /><br/>" + - "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body' rows='20' cols='40'>\nBack to the hill and over it again!</textarea><br/>" + + "<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>" + + "<label for='body'>Body:</label> <textarea name='post[body]' id='post_body'>\nBack to the hill and over it again!</textarea><br/>" + "<label for='secret'>Secret:</label> <input name='post[secret]' type='hidden' value='0' /><input name='post[secret]' checked='checked' type='checkbox' id='post_secret' value='1' /><br/>" assert_dom_equal expected, output_buffer diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb index 606d454cb3..2c0da8473a 100644 --- a/actionpack/test/template/form_options_helper_test.rb +++ b/actionpack/test/template/form_options_helper_test.rb @@ -529,6 +529,14 @@ class FormOptionsHelperTest < ActionView::TestCase ) end + def test_select_with_multiple_and_without_hidden_input + output_buffer = select(:post, :category, "", {:include_hidden => false}, :multiple => true) + assert_dom_equal( + "<select multiple=\"multiple\" id=\"post_category\" name=\"post[category][]\"></select>", + output_buffer + ) + end + def test_select_with_multiple_and_disabled_to_add_disabled_hidden_input output_buffer = select(:post, :category, "", {}, :multiple => true, :disabled => true) assert_dom_equal( diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb index 9441bd6b38..64898f7ad1 100644 --- a/actionpack/test/template/javascript_helper_test.rb +++ b/actionpack/test/template/javascript_helper_test.rb @@ -28,6 +28,8 @@ class JavaScriptHelperTest < ActionView::TestCase assert_equal %(backslash\\\\test), escape_javascript( %(backslash\\test) ) assert_equal %(dont <\\/close> tags), escape_javascript(%(dont </close> tags)) assert_equal %(unicode 
 newline), escape_javascript(%(unicode \342\200\250 newline).force_encoding('UTF-8').encode!) + assert_equal %(unicode 
 newline), escape_javascript(%(unicode \342\200\251 newline).force_encoding('UTF-8').encode!) + assert_equal %(dont <\\/close> tags), j(%(dont </close> tags)) end diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb index 37858c1ba2..f2ed2ec609 100644 --- a/actionpack/test/template/test_case_test.rb +++ b/actionpack/test/template/test_case_test.rb @@ -277,6 +277,12 @@ module ActionView end class RenderTemplateTest < ActionView::TestCase + test "supports specifying templates with a Regexp" do + controller.controller_path = "fun" + render(:template => "fun/games/hello_world") + assert_template %r{\Afun/games/hello_world\Z} + end + test "supports specifying partials" do controller.controller_path = "test" render(:template => "test/calling_partial_with_layout") diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index 42adbec8b9..c6d8eae46c 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -1,13 +1,22 @@ +## Rails 4.0.0 (unreleased) ## + * Added ActiveModel::Model, a mixin to make Ruby objects work with AP out of box *Guillermo Iguaran* * `AM::Errors#to_json`: support `:full_messages` parameter *Bogdan Gusiev* * Trim down Active Model API by removing `valid?` and `errors.full_messages` *José Valim* + +## Rails 3.2.2 (March 1, 2012) ## + +* No changes. + + ## Rails 3.2.1 (January 26, 2012) ## * No changes. + ## Rails 3.2.0 (January 20, 2012) ## * Deprecated `define_attr_method` in `ActiveModel::AttributeMethods`, because this only existed to @@ -23,14 +32,17 @@ * Provide mass_assignment_sanitizer as an easy API to replace the sanitizer behavior. Also support both :logger (default) and :strict sanitizer behavior *Bogdan Gusiev* + ## Rails 3.1.3 (November 20, 2011) ## * No changes + ## Rails 3.1.2 (November 18, 2011) ## * No changes + ## Rails 3.1.1 (October 7, 2011) ## * Remove hard dependency on bcrypt-ruby to avoid make ActiveModel dependent on a binary library. @@ -40,6 +52,7 @@ See GH #2687. *Guillermo Iguaran* + ## Rails 3.1.0 (August 30, 2011) ## * Alternate I18n namespace lookup is no longer supported. diff --git a/activemodel/lib/active_model/conversion.rb b/activemodel/lib/active_model/conversion.rb index c7c805f1a2..d7f30f0920 100644 --- a/activemodel/lib/active_model/conversion.rb +++ b/activemodel/lib/active_model/conversion.rb @@ -21,7 +21,7 @@ module ActiveModel # cm.to_model == self # => true # cm.to_key # => nil # cm.to_param # => nil - # cm.to_path # => "contact_messages/contact_message" + # cm.to_partial_path # => "contact_messages/contact_message" # module Conversion extend ActiveSupport::Concern @@ -57,7 +57,7 @@ module ActiveModel end module ClassMethods #:nodoc: - # Provide a class level cache for the to_path. This is an + # Provide a class level cache for #to_partial_path. This is an # internal method and should not be accessed directly. def _to_partial_path #:nodoc: @_to_partial_path ||= begin diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb index 755e54efcd..fd0bc4e8e9 100644 --- a/activemodel/lib/active_model/naming.rb +++ b/activemodel/lib/active_model/naming.rb @@ -2,6 +2,7 @@ require 'active_support/inflector' require 'active_support/core_ext/hash/except' require 'active_support/core_ext/module/introspection' require 'active_support/core_ext/module/deprecation' +require 'active_support/core_ext/object/blank' module ActiveModel class Name < String diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb index 51f078e662..4323ee1e09 100644 --- a/activemodel/lib/active_model/serialization.rb +++ b/activemodel/lib/active_model/serialization.rb @@ -1,7 +1,5 @@ require 'active_support/core_ext/hash/except' require 'active_support/core_ext/hash/slice' -require 'active_support/core_ext/array/wrap' - module ActiveModel # == Active Model Serialization @@ -11,7 +9,6 @@ module ActiveModel # A minimal implementation could be: # # class Person - # # include ActiveModel::Serialization # # attr_accessor :name @@ -19,7 +16,6 @@ module ActiveModel # def attributes # {'name' => nil} # end - # # end # # Which would provide you with: @@ -43,7 +39,6 @@ module ActiveModel # So a minimal implementation including XML and JSON would be: # # class Person - # # include ActiveModel::Serializers::JSON # include ActiveModel::Serializers::Xml # @@ -52,7 +47,6 @@ module ActiveModel # def attributes # {'name' => nil} # end - # # end # # Which would provide you with: @@ -88,7 +82,7 @@ module ActiveModel method_names.each { |n| hash[n.to_s] = send(n) } serializable_add_includes(options) do |association, records, opts| - hash[association] = if records.is_a?(Enumerable) + hash[association.to_s] = if records.is_a?(Enumerable) records.map { |a| a.serializable_hash(opts) } else records.serializable_hash(opts) @@ -126,13 +120,13 @@ module ActiveModel # +records+ - the association record(s) to be serialized # +opts+ - options for the association records def serializable_add_includes(options = {}) #:nodoc: - return unless include = options[:include] + return unless includes = options[:include] - unless include.is_a?(Hash) - include = Hash[Array.wrap(include).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }] + unless includes.is_a?(Hash) + includes = Hash[Array(includes).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }] end - include.each do |association, opts| + includes.each do |association, opts| if records = send(association) yield association, records, opts end diff --git a/activemodel/test/cases/conversion_test.rb b/activemodel/test/cases/conversion_test.rb index 24552bcaf2..d679ad41aa 100644 --- a/activemodel/test/cases/conversion_test.rb +++ b/activemodel/test/cases/conversion_test.rb @@ -24,7 +24,7 @@ class ConversionTest < ActiveModel::TestCase assert_equal "1", Contact.new(:id => 1).to_param end - test "to_path default implementation returns a string giving a relative path" do + test "to_partial_path default implementation returns a string giving a relative path" do assert_equal "contacts/contact", Contact.new.to_partial_path assert_equal "helicopters/helicopter", Helicopter.new.to_partial_path, "ActiveModel::Conversion#to_partial_path caching should be class-specific" diff --git a/activemodel/test/cases/serialization_test.rb b/activemodel/test/cases/serialization_test.rb index 3b201a70f5..66b18d65e5 100644 --- a/activemodel/test/cases/serialization_test.rb +++ b/activemodel/test/cases/serialization_test.rb @@ -88,62 +88,62 @@ class SerializationTest < ActiveModel::TestCase def test_include_option_with_singular_association expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com", - :address=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111}} + "address"=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111}} assert_equal expected, @user.serializable_hash(:include => :address) end def test_include_option_with_plural_association expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", - :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'}, + "friends"=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'}, {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]} assert_equal expected, @user.serializable_hash(:include => :friends) end def test_include_option_with_empty_association @user.friends = [] - expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", :friends=>[]} + expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", "friends"=>[]} assert_equal expected, @user.serializable_hash(:include => :friends) end def test_multiple_includes expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", - :address=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111}, - :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'}, + "address"=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111}, + "friends"=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'}, {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]} assert_equal expected, @user.serializable_hash(:include => [:address, :friends]) end def test_include_with_options expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", - :address=>{"street"=>"123 Lane"}} + "address"=>{"street"=>"123 Lane"}} assert_equal expected, @user.serializable_hash(:include => {:address => {:only => "street"}}) end def test_nested_include @user.friends.first.friends = [@user] expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", - :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male', - :friends => [{"email"=>"david@example.com", "gender"=>"male", "name"=>"David"}]}, - {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female', :friends => []}]} + "friends"=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male', + "friends"=> [{"email"=>"david@example.com", "gender"=>"male", "name"=>"David"}]}, + {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female', "friends"=> []}]} assert_equal expected, @user.serializable_hash(:include => {:friends => {:include => :friends}}) end def test_only_include - expected = {"name"=>"David", :friends => [{"name" => "Joe"}, {"name" => "Sue"}]} + expected = {"name"=>"David", "friends" => [{"name" => "Joe"}, {"name" => "Sue"}]} assert_equal expected, @user.serializable_hash(:only => :name, :include => {:friends => {:only => :name}}) end def test_except_include expected = {"name"=>"David", "email"=>"david@example.com", - :friends => [{"name" => 'Joe', "email" => 'joe@example.com'}, + "friends"=> [{"name" => 'Joe', "email" => 'joe@example.com'}, {"name" => "Sue", "email" => 'sue@example.com'}]} assert_equal expected, @user.serializable_hash(:except => :gender, :include => {:friends => {:except => :gender}}) end def test_multiple_includes_with_options expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", - :address=>{"street"=>"123 Lane"}, - :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'}, + "address"=>{"street"=>"123 Lane"}, + "friends"=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'}, {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]} assert_equal expected, @user.serializable_hash(:include => [{:address => {:only => "street"}}, :friends]) end diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index d7823855a3..6fd86b1f32 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -153,6 +153,28 @@ * PostgreSQL hstore types are automatically deserialized from the database. +## Rails 3.2.3 (unreleased) ## + +* Added find_or_create_by_{attribute}! dynamic method. *Andrew White* + +* Whitelist all attribute assignment by default. Change the default for newly generated applications to whitelist all attribute assignment. Also update the generated model classes so users are reminded of the importance of attr_accessible. *NZKoz* + +* Update ActiveRecord::AttributeMethods#attribute_present? to return false for empty strings. *Jacobkg* + +* Fix associations when using per class databases. *larskanis* + +* Revert setting NOT NULL constraints in add_timestamps *fxn* + +* Fix mysql to use proper text types. Fixes #3931. *kennyj* + +* Fix #5069 - Protect foreign key from mass assignment through association builder. *byroot* + + +## Rails 3.2.2 (March 1, 2012) ## + +* No changes. + + ## Rails 3.2.1 (January 26, 2012) ## * The threshold for auto EXPLAIN is ignored if there's no logger. *fxn* @@ -343,6 +365,7 @@ *Aaron Christy* + ## Rails 3.1.4 (March 1, 2012) ## * Fix a custom primary key regression *GH 3987* @@ -370,6 +393,7 @@ *Julius de Bruijn* + ### Rails 3.1.3 (November 20, 2011) ## * Perf fix: If we're deleting all records in an association, don't add a IN(..) clause @@ -383,6 +407,7 @@ *Christos Zisopoulos and Kenny J* + ### Rails 3.1.2 (November 18, 2011) ## * Fix bug with PostgreSQLAdapter#indexes. When the search path has multiple schemas, spaces @@ -430,6 +455,7 @@ *Kenny J* + ## Rails 3.1.1 (October 7, 2011) ## * Add deprecation for the preload_associations method. Fixes #3022. diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index b2136605e1..da4c311bce 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -544,7 +544,7 @@ module ActiveRecord # If using a custom finder_sql, #find scans the entire collection. def find_by_scan(*args) expects_array = args.first.kind_of?(Array) - ids = args.flatten.compact.uniq.map { |arg| arg.to_i } + ids = args.flatten.compact.map{ |arg| arg.to_i }.uniq if ids.size == 1 id = ids.first diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 93c243e7b1..bebf6a1a05 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -214,37 +214,64 @@ module ActiveRecord value end - # Returns a copy of the attributes hash where all the values have been safely quoted for use in - # an Arel insert/update method. - def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys) - attrs = {} - klass = self.class - arel_table = klass.arel_table + def arel_attributes_with_values_for_create(pk_attribute_allowed) + arel_attributes_with_values(attributes_for_create(pk_attribute_allowed)) + end - attribute_names.each do |name| - if (column = column_for_attribute(name)) && (include_primary_key || !column.primary) + def arel_attributes_with_values_for_update(attribute_names) + arel_attributes_with_values(attributes_for_update(attribute_names)) + end - if include_readonly_attributes || !self.class.readonly_attributes.include?(name) + def attribute_method?(attr_name) + defined?(@attributes) && @attributes.include?(attr_name) + end - value = if klass.serialized_attributes.include?(name) - @attributes[name].serialized_value - else - # FIXME: we need @attributes to be used consistently. - # If the values stored in @attributes were already type - # casted, this code could be simplified - read_attribute(name) - end + private - attrs[arel_table[name]] = value - end - end - end + # Returns a Hash of the Arel::Attributes and attribute values that have been + # type casted for use in an Arel insert/update method. + def arel_attributes_with_values(attribute_names) + attrs = {} + arel_table = self.class.arel_table + attribute_names.each do |name| + attrs[arel_table[name]] = typecasted_attribute_value(name) + end attrs end - def attribute_method?(attr_name) - defined?(@attributes) && @attributes.include?(attr_name) + # Filters the primary keys and readonly attributes from the attribute names. + def attributes_for_update(attribute_names) + attribute_names.select do |name| + column_for_attribute(name) && !pk_attribute?(name) && !readonly_attribute?(name) + end + end + + # Filters out the primary keys, from the attribute names, when the primary + # key is to be generated (e.g. the id attribute has no value). + def attributes_for_create(pk_attribute_allowed) + @attributes.keys.select do |name| + column_for_attribute(name) && (pk_attribute_allowed || !pk_attribute?(name)) + end + end + + def readonly_attribute?(name) + self.class.readonly_attributes.include?(name) + end + + def pk_attribute?(name) + column_for_attribute(name).primary + end + + def typecasted_attribute_value(name) + if self.class.serialized_attributes.include?(name) + @attributes[name].serialized_value + else + # FIXME: we need @attributes to be used consistently. + # If the values stored in @attributes were already typecasted, this code + # could be simplified + read_attribute(name) + end end end end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index d4d0220fb7..d25a821688 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -201,6 +201,9 @@ module ActiveRecord #:nodoc: # # Now 'Bob' exist and is an 'admin' # User.find_or_create_by_name('Bob', :age => 40) { |u| u.admin = true } # + # Adding an exclamation point (!) on to the end of <tt>find_or_create_by_</tt> will + # raise an <tt>ActiveRecord::RecordInvalid</tt> error if the new record is invalid. + # # Use the <tt>find_or_initialize_by_</tt> finder if you want to return a new record without # saving it first. Protected attributes won't be set unless they are given in a block. # diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 37a9d216df..561e48d52e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -128,10 +128,11 @@ module ActiveRecord @reserved_connections[current_connection_id] ||= checkout end - # Check to see if there is an active connection in this connection - # pool. + # Is there an open connection that is being used for the current thread? def active_connection? - active_connections.any? + @reserved_connections.fetch(current_connection_id) { + return false + }.in_use? end # Signal that the thread is finished with the current connection. @@ -185,8 +186,9 @@ module ActiveRecord end def clear_stale_cached_connections! # :nodoc: + reap end - deprecate :clear_stale_cached_connections! + deprecate :clear_stale_cached_connections! => "Please use #reap instead" # Check-out a database connection from the pool, indicating that you want # to use it. You should call #checkin when you no longer need this. @@ -233,6 +235,8 @@ module ActiveRecord conn.run_callbacks :checkin do conn.expire end + + release conn end end @@ -244,10 +248,7 @@ module ActiveRecord # FIXME: we might want to store the key on the connection so that removing # from the reserved hash will be a little easier. - thread_id = @reserved_connections.keys.find { |k| - @reserved_connections[k] == conn - } - @reserved_connections.delete thread_id if thread_id + release conn end end @@ -265,6 +266,20 @@ module ActiveRecord private + def release(conn) + thread_id = nil + + if @reserved_connections[current_connection_id] == conn + thread_id = current_connection_id + else + thread_id = @reserved_connections.keys.find { |k| + @reserved_connections[k] == conn + } + end + + @reserved_connections.delete thread_id if thread_id + end + def new_connection ActiveRecord::Base.send(spec.adapter_method, spec.config) end @@ -288,10 +303,6 @@ module ActiveRecord end c end - - def active_connections - @connections.find_all { |c| c.in_use? } - end end # ConnectionHandler is a collection of ConnectionPool objects. It is used diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index e75a2a1cb4..46aababfb6 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -130,7 +130,7 @@ module ActiveRecord end def arel_engine - @arel_engine ||= connection_handler.connection_pools[name] ? self : active_record_super.arel_engine + @arel_engine ||= connection_handler.retrieve_connection_pool(self) ? self : active_record_super.arel_engine end private diff --git a/activerecord/lib/active_record/dynamic_finder_match.rb b/activerecord/lib/active_record/dynamic_finder_match.rb index 38dbbef5fc..0473d6aafc 100644 --- a/activerecord/lib/active_record/dynamic_finder_match.rb +++ b/activerecord/lib/active_record/dynamic_finder_match.rb @@ -7,7 +7,7 @@ module ActiveRecord class DynamicFinderMatch def self.match(method) method = method.to_s - klass = [FindBy, FindByBang, FindOrInitializeCreateBy].find do |_klass| + klass = klasses.find do |_klass| _klass.matches?(method) end klass.new(method) if klass @@ -17,6 +17,10 @@ module ActiveRecord method =~ self::METHOD_PATTERN end + def self.klasses + [FindBy, FindByBang, FindOrInitializeCreateBy, FindOrCreateByBang] + end + def initialize(method) @finder = :first @instantiator = nil @@ -47,6 +51,14 @@ module ActiveRecord arguments.size >= @attribute_names.size end + def save_record? + @instantiator == :create + end + + def save_method + bang? ? :save! : :save + end + private def initialize_from_match_data(match_data) @@ -81,4 +93,16 @@ module ActiveRecord arguments.size == 1 && arguments.first.is_a?(Hash) || super end end + + class FindOrCreateByBang < DynamicFinderMatch + METHOD_PATTERN = /^find_or_create_by_([_a-zA-Z]\w*)\!$/ + + def initialize_from_match_data(match_data) + @instantiator = :create + end + + def bang? + true + end + end end diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index 9b2dc096a0..8266427b71 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -84,7 +84,7 @@ module ActiveRecord relation.table[self.class.primary_key].eq(id).and( relation.table[lock_col].eq(quote_value(previous_lock_value)) ) - ).arel.compile_update(arel_attributes_values(false, false, attribute_names)) + ).arel.compile_update(arel_attributes_with_values_for_update(attribute_names)) affected_rows = connection.update stmt diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index c4bce87311..09502bb52c 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -350,7 +350,7 @@ module ActiveRecord # Updates the associated record with values matching those of the instance attributes. # Returns the number of affected rows. def update(attribute_names = @attributes.keys) - attributes_with_values = arel_attributes_values(false, false, attribute_names) + attributes_with_values = arel_attributes_with_values_for_update(attribute_names) return 0 if attributes_with_values.empty? klass = self.class stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id)).arel.compile_update(attributes_with_values) @@ -360,7 +360,7 @@ module ActiveRecord # Creates a record with values matching those of the instance attributes # and returns its id. def create - attributes_values = arel_attributes_values(!id.nil?) + attributes_values = arel_attributes_with_values_for_create(!id.nil?) new_id = self.class.unscoped.insert attributes_values diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 4cd703e0a5..adfacf37ee 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -290,7 +290,7 @@ module ActiveRecord r.assign_attributes(unprotected_attributes_for_create, :without_protection => true) end yield(record) if block_given? - record.save if match.instantiator == :create + record.send(match.save_method) if match.save_record? end record diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 87dd513880..d737b34115 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -329,7 +329,7 @@ module ActiveRecord arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty? arel.take(connection.sanitize_limit(@limit_value)) if @limit_value - arel.skip(@offset_value) if @offset_value + arel.skip(@offset_value.to_i) if @offset_value arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty? diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 02a7f6af78..6a4f972356 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -378,6 +378,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 1, Firm.find(:first, :order => "id").clients_using_sql.size end + def test_finding_using_sql_take_into_account_only_uniq_ids + firm = Firm.find(:first, :order => "id") + client = firm.clients_using_sql.first + assert_equal client, firm.clients_using_sql.find(client.id, client.id) + assert_equal client, firm.clients_using_sql.find(client.id, client.id.to_s) + end + def test_counting_using_sql assert_equal 1, Firm.find(:first, :order => "id").clients_using_counter_sql.size assert Firm.find(:first, :order => "id").clients_using_counter_sql.any? diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index 2c69bfde5b..da93500ce3 100644 --- a/activerecord/test/cases/connection_pool_test.rb +++ b/activerecord/test/cases/connection_pool_test.rb @@ -3,6 +3,8 @@ require "cases/helper" module ActiveRecord module ConnectionAdapters class ConnectionPoolTest < ActiveRecord::TestCase + attr_reader :pool + def setup super @@ -25,6 +27,67 @@ module ActiveRecord @pool.disconnect! end + def active_connections(pool) + pool.connections.find_all(&:in_use?) + end + + def test_checkout_after_close + connection = pool.connection + assert connection.in_use? + + connection.close + assert !connection.in_use? + + assert pool.connection.in_use? + end + + def test_released_connection_moves_between_threads + thread_conn = nil + + Thread.new { + pool.with_connection do |conn| + thread_conn = conn + end + }.join + + assert thread_conn + + Thread.new { + pool.with_connection do |conn| + assert_equal thread_conn, conn + end + }.join + end + + def test_with_connection + assert_equal 0, active_connections(pool).size + + main_thread = pool.connection + assert_equal 1, active_connections(pool).size + + Thread.new { + pool.with_connection do |conn| + assert conn + assert_equal 2, active_connections(pool).size + end + assert_equal 1, active_connections(pool).size + }.join + + main_thread.close + assert_equal 0, active_connections(pool).size + end + + def test_active_connection_in_use + assert !pool.active_connection? + main_thread = pool.connection + + assert pool.active_connection? + + main_thread.close + + assert !pool.active_connection? + end + def test_full_pool_exception assert_raises(PoolFullError) do (@pool.size + 1).times do diff --git a/activerecord/test/cases/dynamic_finder_match_test.rb b/activerecord/test/cases/dynamic_finder_match_test.rb index e576870317..db619faa83 100644 --- a/activerecord/test/cases/dynamic_finder_match_test.rb +++ b/activerecord/test/cases/dynamic_finder_match_test.rb @@ -83,6 +83,14 @@ module ActiveRecord assert_equal :create, m.instantiator end + def test_find_or_create! + m = DynamicFinderMatch.match(:find_or_create_by_foo!) + assert_equal :first, m.finder + assert m.bang?, 'should be banging' + assert_equal %w{ foo }, m.attribute_names + assert_equal :create, m.instantiator + end + def test_find_or_initialize m = DynamicFinderMatch.match(:find_or_initialize_by_foo) assert_equal :first, m.finder diff --git a/activerecord/test/cases/finder_respond_to_test.rb b/activerecord/test/cases/finder_respond_to_test.rb index 235805a67c..810c1500cc 100644 --- a/activerecord/test/cases/finder_respond_to_test.rb +++ b/activerecord/test/cases/finder_respond_to_test.rb @@ -56,6 +56,16 @@ class FinderRespondToTest < ActiveRecord::TestCase assert_respond_to Topic, :find_or_create_by_title_and_author_name end + def test_should_respond_to_find_or_create_from_one_attribute_bang + ensure_topic_method_is_not_cached(:find_or_create_by_title!) + assert_respond_to Topic, :find_or_create_by_title! + end + + def test_should_respond_to_find_or_create_from_two_attributes_bang + ensure_topic_method_is_not_cached(:find_or_create_by_title_and_author_name!) + assert_respond_to Topic, :find_or_create_by_title_and_author_name! + end + def test_should_not_respond_to_find_by_one_missing_attribute assert !Topic.respond_to?(:find_by_undertitle) end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 76c041397a..96c8eb6417 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -862,6 +862,28 @@ class FinderTest < ActiveRecord::TestCase assert another.persisted? end + def test_find_or_create_from_one_attribute_bang + number_of_companies = Company.count + assert_raises(ActiveRecord::RecordInvalid) { Company.find_or_create_by_name!("") } + assert_equal number_of_companies, Company.count + sig38 = Company.find_or_create_by_name!("38signals") + assert_equal number_of_companies + 1, Company.count + assert_equal sig38, Company.find_or_create_by_name!("38signals") + assert sig38.persisted? + end + + def test_find_or_create_from_two_attributes_bang + number_of_companies = Company.count + assert_raises(ActiveRecord::RecordInvalid) { Company.find_or_create_by_name_and_firm_id!("", 17) } + assert_equal number_of_companies, Company.count + sig38 = Company.find_or_create_by_name_and_firm_id!("38signals", 17) + assert_equal number_of_companies + 1, Company.count + assert_equal sig38, Company.find_or_create_by_name_and_firm_id!("38signals", 17) + assert sig38.persisted? + assert_equal "38signals", sig38.name + assert_equal 17, sig38.firm_id + end + def test_find_or_create_from_two_attributes_with_one_being_an_aggregate number_of_customers = Customer.count created_customer = Customer.find_or_create_by_balance_and_name(Money.new(123), "Elizabeth") @@ -1185,6 +1207,10 @@ class FinderTest < ActiveRecord::TestCase end end + def test_finder_with_offset_string + assert_nothing_raised(ActiveRecord::StatementInvalid) { Topic.find(:all, :offset => "3") } + end + protected def bind(statement, *vars) if vars.first.is_a?(Hash) diff --git a/activerecord/test/cases/multiple_db_test.rb b/activerecord/test/cases/multiple_db_test.rb index 917a03bb34..06d6596725 100644 --- a/activerecord/test/cases/multiple_db_test.rb +++ b/activerecord/test/cases/multiple_db_test.rb @@ -86,7 +86,13 @@ class MultipleDbTest < ActiveRecord::TestCase end def test_arel_table_engines - assert_equal Entrant.arel_engine, Bird.arel_engine + assert_not_equal Entrant.arel_engine, Bird.arel_engine + assert_not_equal Entrant.arel_engine, Course.arel_engine + end + + def test_connection + assert_equal Entrant.arel_engine.connection, Bird.arel_engine.connection + assert_not_equal Entrant.arel_engine.connection, Course.arel_engine.connection end unless in_memory_db? diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 7b1d65c6db..acec230c72 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -462,6 +462,18 @@ class RelationTest < ActiveRecord::TestCase assert_equal authors(:david), authors.find_or_create_by_name(:name => 'David') end + def test_dynamic_find_or_create_by_attributes_bang + authors = Author.scoped + + assert_raises(ActiveRecord::RecordInvalid) { authors.find_or_create_by_name!('') } + + lifo = authors.find_or_create_by_name!('Lifo') + assert_equal "Lifo", lifo.name + assert lifo.persisted? + + assert_equal authors(:david), authors.find_or_create_by_name!(:name => 'David') + end + def test_find_id authors = Author.scoped diff --git a/activeresource/CHANGELOG.md b/activeresource/CHANGELOG.md index f305c3fd4a..670cb19f57 100644 --- a/activeresource/CHANGELOG.md +++ b/activeresource/CHANGELOG.md @@ -3,6 +3,11 @@ * Adds support for PATCH requests. *dlee* +## Rails 3.2.2 (March 1, 2012) ## + +* No changes. + + ## Rails 3.2.1 (January 26, 2012) ## * Documentation fixes. diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 09ec4ed618..8165b89cde 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -27,6 +27,11 @@ * Unicode database updated to 6.1.0. +## Rails 3.2.2 (March 1, 2012) ## + +* No changes. + + ## Rails 3.2.1 (January 26, 2012) ## * Documentation fixes and improvements. diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index d7408eff9f..b9f196d7a9 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -91,6 +91,7 @@ module ActiveSupport case when key.respond_to?(:cache_key) then key.cache_key when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param + when key.respond_to?(:to_a) then retrieve_cache_key(key.to_a) else key.to_param end.to_s end diff --git a/activesupport/lib/active_support/dependencies/autoload.rb b/activesupport/lib/active_support/dependencies/autoload.rb index 4c771da096..a1626ebeba 100644 --- a/activesupport/lib/active_support/dependencies/autoload.rb +++ b/activesupport/lib/active_support/dependencies/autoload.rb @@ -9,13 +9,16 @@ module ActiveSupport @@eager_autoload = false def autoload(const_name, path = @@at_path) - full = [self.name, @@under_path, const_name.to_s, path].compact.join("::") - location = path || Inflector.underscore(full) + unless path + full = [name, @@under_path, const_name.to_s, path].compact.join("::") + path = Inflector.underscore(full) + end if @@eager_autoload - @@autoloads[const_name] = location + @@autoloads[const_name] = path end - super const_name, location + + super const_name, path end def autoload_under(path) diff --git a/activesupport/lib/active_support/tagged_logging.rb b/activesupport/lib/active_support/tagged_logging.rb index f6ad861353..88fd438448 100644 --- a/activesupport/lib/active_support/tagged_logging.rb +++ b/activesupport/lib/active_support/tagged_logging.rb @@ -13,7 +13,7 @@ module ActiveSupport # This is used by the default Rails.logger as configured by Railties to make it easy to stamp log lines # with subdomains, request ids, and anything else to aid debugging of multi-user production applications. module TaggedLogging - class Formatter < ActiveSupport::Logger::SimpleFormatter # :nodoc: + module Formatter # :nodoc: # This method is invoked when a log event occurs def call(severity, timestamp, progname, msg) super(severity, timestamp, progname, "#{tags_text}#{msg}") @@ -37,7 +37,7 @@ module ActiveSupport end def self.new(logger) - logger.formatter = Formatter.new + logger.formatter.extend Formatter logger.extend(self) end diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index b03865da93..ba027f1ff0 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -69,6 +69,10 @@ class CacheKeyTest < ActiveSupport::TestCase def test_expand_cache_key_of_true assert_equal 'true', ActiveSupport::Cache.expand_cache_key(true) end + + def test_expand_cache_key_of_array_like_object + assert_equal 'foo/bar/baz', ActiveSupport::Cache.expand_cache_key(%w{foo bar baz}.to_enum) + end end class CacheStoreSettingTest < ActiveSupport::TestCase diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb index d71e3f968f..4544edf0dd 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -490,7 +490,7 @@ class HashExtTest < ActiveSupport::TestCase original = {:a => 1, :b => 2, :c => 3, :d => 4} expected = {:a => 1, :b => 2} - assert_equal expected, {:a => 1, :b => 2, :c => 3, :d => 4}.extract!(:a, :b) + assert_equal expected, original.extract!(:a, :b) end def test_except diff --git a/rails.gemspec b/rails.gemspec index cd7c5d1ee9..b55d0e6859 100644 --- a/rails.gemspec +++ b/rails.gemspec @@ -23,5 +23,5 @@ Gem::Specification.new do |s| s.add_dependency('activeresource', version) s.add_dependency('actionmailer', version) s.add_dependency('railties', version) - s.add_dependency('bundler', '~> 1.0') + s.add_dependency('bundler', '~> 1.1') end diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 960b1ed8ca..e7994e45c6 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -20,6 +20,11 @@ * Rails::Plugin has gone. Instead of adding plugins to vendor/plugins use gems or bundler with path or git dependencies. *Santiago Pastorino* +## Rails 3.2.2 (March 1, 2012) ## + +* No changes. + + ## Rails 3.2.1 (January 26, 2012) ## * Documentation fixes. diff --git a/railties/guides/source/action_view_overview.textile b/railties/guides/source/action_view_overview.textile index f007629207..42120e9bad 100644 --- a/railties/guides/source/action_view_overview.textile +++ b/railties/guides/source/action_view_overview.textile @@ -431,11 +431,11 @@ form("post") <form action='/posts/create' method='post'> <p> <label for="post_title">Title</label><br /> - <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /> + <input id="post_title" name="post[title]" type="text" value="Hello World" /> </p> <p> <label for="post_body">Body</label><br /> - <textarea cols="40" id="post_body" name="post[body]" rows="20"></textarea> + <textarea id="post_body" name="post[body]"></textarea> </p> <input name="commit" type="submit" value="Create" /> </form> @@ -451,7 +451,7 @@ For example, if +@post+ has an attribute +title+ mapped to a +String+ column tha <ruby> input("post", "title") # => - <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" /> + <input id="post_title" name="post[title]" type="text" value="Hello World" /> </ruby> h4. RecordTagHelper @@ -987,8 +987,8 @@ The HTML generated for this would be: <html> <form action="/persons/create" method="post"> - <input id="person_first_name" name="person[first_name]" size="30" type="text" /> - <input id="person_last_name" name="person[last_name]" size="30" type="text" /> + <input id="person_first_name" name="person[first_name]" type="text" /> + <input id="person_last_name" name="person[last_name]" type="text" /> <input name="commit" type="submit" value="Create" /> </form> </html> diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile index d9a62f7574..8e23a577e2 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -1133,6 +1133,8 @@ Client.where(:first_name => 'Andy').first_or_create!(:locked => false) # => ActiveRecord::RecordInvalid: Validation failed: Orders count can't be blank </ruby> +As with +first_or_create+ there is a +find_or_create_by!+ method but the +first_or_create!+ method is preferred for clarity. + h4. +first_or_initialize+ The +first_or_initialize+ method will work just like +first_or_create+ but it will not call +create+ but +new+. This means that a new model instance will be created in memory but won't be saved to the database. Continuing with the +first_or_create+ example, we now want the client named 'Nick': diff --git a/railties/guides/source/api_app.textile b/railties/guides/source/api_app.textile index f2d00c5768..9bbf7b36b1 100644 --- a/railties/guides/source/api_app.textile +++ b/railties/guides/source/api_app.textile @@ -13,15 +13,14 @@ endprologue. h3. What is an API app? -Traditionally, when people said that they used Rails as an "API", they meant -providing a programmatically accessible API alongside their web application. +Traditionally, when people said that they used Rails as an "API", they meant providing a programmatically accessible API alongside their web application. For example, GitHub provides "an API":http://developer.github.com that you can use from your own custom clients. With the advent of client-side frameworks, more developers are using Rails to build a backend that is shared between their web application and other native applications. For example, Twitter uses its "public API":https://dev.twitter.com in its web application, which is built as a static site that consumes JSON resources. -Instead of using Rails to generate dynamic HTML that will communicate with the server through forms and links, many developers are treating their web application as just another client, delivered as static HTML, CSS and JavaScript, and consuming a simple JSON API +Instead of using Rails to generate dynamic HTML that will communicate with the server through forms and links, many developers are treating their web application as just another client, delivered as static HTML, CSS and JavaScript, and consuming a simple JSON API This guide covers building a Rails application that serves JSON resources to an API client *or* client-side framework. @@ -72,13 +71,13 @@ If you're building a Rails application that will be an API server first and fore You can generate a new bare Rails app: <shell> -$ rails new my_api --api +$ rails new my_api --http </shell> This will do three main things for you: * Configure your application to start with a more limited set of middleware than normal. Specifically, it will not include any middleware primarily useful for browser applications (like cookie support) by default. -* Make +ApplicationController+ inherit from +ActionController::API+ instead of +ActionController::Base+. As with middleware, this will leave out any +ActionController+ modules that provide functionality primarily used by browser applications. +* Make +ApplicationController+ inherit from +ActionController::HTTP+ instead of +ActionController::Base+. As with middleware, this will leave out any +ActionController+ modules that provide functionality primarily used by browser applications. * Configure the generators to skip generating views, helpers and assets when you generate a new resource. If you want to take an existing app and make it an API app, follow the following steps. @@ -86,8 +85,8 @@ If you want to take an existing app and make it an API app, follow the following In +config/application.rb+ add the following lines at the top of the +Application+ class: <ruby> -config.middleware.api_only! -config.generators.api_only! +config.middleware.http_only! +config.generators.http_only! </ruby> Change +app/controllers/application_controller.rb+: @@ -98,7 +97,7 @@ class ApplicationController < ActionController::Base end # do -class ApplicationController < ActionController::API +class ApplicationController < ActionController::HTTP end </ruby> @@ -240,7 +239,7 @@ Keep in mind that removing these features may remove support for certain feature h3. Choosing Controller Modules -An API application (using +ActionController::API+) comes with the following controller modules by default: +An API application (using +ActionController::HTTP+) comes with the following controller modules by default: * +AbstractController::Translation+: Support for the +l+ and +t+ localization and translation methods. These delegate to +I18n.translate+ and +I18n.localize+. * +ActionController::UrlFor+: Makes +url_for+ and friends available. @@ -253,11 +252,11 @@ An API application (using +ActionController::API+) comes with the following cont * +AbstractController::Callbacks+: Support for +before_filter+ and friends * +ActionController::Instrumentation+: Support for the instrumentation hooks defined by +ActionController+ (see "the source":https://github.com/rails/rails/blob/master/actionpack/lib/action_controller/metal/instrumentation.rb for more). -Other plugins may add additional modules. You can get a list of all modules included into +ActionController::API+ in the rails console: +Other plugins may add additional modules. You can get a list of all modules included into +ActionController::HTTP+ in the rails console: <shell> $ irb ->> ActionController::API.ancestors - ActionController::Metal.ancestors +>> ActionController::HTTP.ancestors - ActionController::Metal.ancestors </shell> h4. Adding Other Modules diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 7a93a30702..d6e500fc4b 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -248,6 +248,14 @@ They can also be removed from the stack completely: config.middleware.delete ActionDispatch::BestStandardsSupport </ruby> +In addition to these methods to handle the stack, if your application is going to be used as an API endpoint only, the middleware stack can be configured like this: + +<ruby> +config.middleware.http_only! +</ruby> + +By doing this, Rails will create a smaller middleware stack, by not adding some middlewares that are usually useful for browser access only, such as Cookies, Session and Flash, BestStandardsSupport, and MethodOverride. You can always add any of them later manually if you want. Refer to the "API App docs":api_app.html for more info on how to setup your application for API only apps. + h4. Configuring i18n * +config.i18n.default_locale+ sets the default locale of an application used for i18n. Defaults to +:en+. diff --git a/railties/guides/source/contributing_to_ruby_on_rails.textile b/railties/guides/source/contributing_to_ruby_on_rails.textile index dd993cc3b9..df8b16eb9e 100644 --- a/railties/guides/source/contributing_to_ruby_on_rails.textile +++ b/railties/guides/source/contributing_to_ruby_on_rails.textile @@ -309,7 +309,7 @@ Rails follows a simple set of coding style conventions. * Two spaces, no tabs. * No trailing whitespace. Blank lines should not have any space. -* Outdent private/protected from method definitions. Same indentation as the class/module. +* Indent after private/protected. * Prefer +&&+/+||+ over +and+/+or+. * Prefer class << self block over self.method for class methods. * +MyClass.my_method(my_arg)+ not +my_method( my_arg )+ or +my_method my_arg+. diff --git a/railties/guides/source/form_helpers.textile b/railties/guides/source/form_helpers.textile index 9f91d12a54..8934667c5e 100644 --- a/railties/guides/source/form_helpers.textile +++ b/railties/guides/source/form_helpers.textile @@ -169,11 +169,11 @@ Output: <textarea id="message" name="message" cols="24" rows="6">Hi, nice site</textarea> <input id="password" name="password" type="password" /> <input id="parent_id" name="parent_id" type="hidden" value="5" /> -<input id="user_name" name="user[name]" size="30" type="search" /> -<input id="user_phone" name="user[phone]" size="30" type="tel" /> +<input id="user_name" name="user[name]" type="search" /> +<input id="user_phone" name="user[phone]" type="tel" /> <input id="user_born_on" name="user[born_on]" type="date" /> -<input id="user_homepage" size="30" name="user[homepage]" type="url" /> -<input id="user_address" size="30" name="user[address]" type="email" /> +<input id="user_homepage" name="user[homepage]" type="url" /> +<input id="user_address" name="user[address]" type="email" /> </html> Hidden inputs are not shown to the user but instead hold data like any textual input. Values inside them can be changed with JavaScript. @@ -239,7 +239,7 @@ The resulting HTML is: <html> <form accept-charset="UTF-8" action="/articles/create" method="post" class="nifty_form"> - <input id="article_title" name="article[title]" size="30" type="text" /> + <input id="article_title" name="article[title]" type="text" /> <textarea id="article_body" name="article[body]" cols="60" rows="12"></textarea> <input name="commit" type="submit" value="Create" /> </form> @@ -264,8 +264,8 @@ which produces the following output: <html> <form accept-charset="UTF-8" action="/people/create" class="new_person" id="new_person" method="post"> - <input id="person_name" name="person[name]" size="30" type="text" /> - <input id="contact_detail_phone_number" name="contact_detail[phone_number]" size="30" type="text" /> + <input id="person_name" name="person[name]" type="text" /> + <input id="contact_detail_phone_number" name="contact_detail[phone_number]" type="text" /> </form> </html> @@ -714,9 +714,9 @@ Assuming the person had two addresses, with ids 23 and 45 this would create outp <html> <form accept-charset="UTF-8" action="/people/1" class="edit_person" id="edit_person_1" method="post"> - <input id="person_name" name="person[name]" size="30" type="text" /> - <input id="person_address_23_city" name="person[address][23][city]" size="30" type="text" /> - <input id="person_address_45_city" name="person[address][45][city]" size="30" type="text" /> + <input id="person_name" name="person[name]" type="text" /> + <input id="person_address_23_city" name="person[address][23][city]" type="text" /> + <input id="person_address_45_city" name="person[address][45][city]" type="text" /> </form> </html> @@ -739,7 +739,7 @@ To create more intricate nestings, you can specify the first part of the input n will create inputs like <html> -<input id="person_address_primary_1_city" name="person[address][primary][1][city]" size="30" type="text" value="bologna" /> +<input id="person_address_primary_1_city" name="person[address][primary][1][city]" type="text" value="bologna" /> </html> As a general rule the final input name is the concatenation of the name given to +fields_for+/+form_for+, the index value and the name of the attribute. You can also pass an +:index+ option directly to helpers such as +text_field+, but it is usually less repetitive to specify this at the form builder level rather than on individual input controls. diff --git a/railties/guides/source/nested_model_forms.textile b/railties/guides/source/nested_model_forms.textile index 4b1fd2e0ac..82c9ab9d36 100644 --- a/railties/guides/source/nested_model_forms.textile +++ b/railties/guides/source/nested_model_forms.textile @@ -131,7 +131,7 @@ This will generate the following html: <html> <form action="/people" class="new_person" id="new_person" method="post"> - <input id="person_name" name="person[name]" size="30" type="text" /> + <input id="person_name" name="person[name]" type="text" /> </form> </html> @@ -153,9 +153,9 @@ This generates: <html> <form action="/people" class="new_person" id="new_person" method="post"> - <input id="person_name" name="person[name]" size="30" type="text" /> + <input id="person_name" name="person[name]" type="text" /> - <input id="person_address_attributes_street" name="person[address_attributes][street]" size="30" type="text" /> + <input id="person_address_attributes_street" name="person[address_attributes][street]" type="text" /> </form> </html> @@ -194,10 +194,10 @@ Which generates: <html> <form action="/people" class="new_person" id="new_person" method="post"> - <input id="person_name" name="person[name]" size="30" type="text" /> + <input id="person_name" name="person[name]" type="text" /> - <input id="person_projects_attributes_0_name" name="person[projects_attributes][0][name]" size="30" type="text" /> - <input id="person_projects_attributes_1_name" name="person[projects_attributes][1][name]" size="30" type="text" /> + <input id="person_projects_attributes_0_name" name="person[projects_attributes][0][name]" type="text" /> + <input id="person_projects_attributes_1_name" name="person[projects_attributes][1][name]" type="text" /> </form> </html> diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 10fa63c303..3191fe68a7 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -239,7 +239,7 @@ module Rails middleware.use ::Rack::Lock unless config.allow_concurrency middleware.use ::Rack::Runtime - middleware.use ::Rack::MethodOverride unless config.middleware.api_only? + middleware.use ::Rack::MethodOverride unless config.middleware.http_only? middleware.use ::ActionDispatch::RequestId middleware.use ::Rails::Rack::Logger, config.log_tags # must come after Rack::MethodOverride to properly log overridden methods middleware.use ::ActionDispatch::ShowExceptions, config.exceptions_app || ActionDispatch::PublicExceptions.new(Rails.public_path) @@ -252,9 +252,9 @@ module Rails end middleware.use ::ActionDispatch::Callbacks - middleware.use ::ActionDispatch::Cookies unless config.middleware.api_only? + middleware.use ::ActionDispatch::Cookies unless config.middleware.http_only? - if !config.middleware.api_only? && config.session_store + if !config.middleware.http_only? && config.session_store if config.force_ssl && !config.session_options.key?(:secure) config.session_options[:secure] = true end @@ -267,7 +267,7 @@ module Rails middleware.use ::Rack::ConditionalGet middleware.use ::Rack::ETag, "no-cache" - if !config.middleware.api_only? && config.action_dispatch.best_standards_support + if !config.middleware.http_only? && config.action_dispatch.best_standards_support middleware.use ::ActionDispatch::BestStandardsSupport, config.action_dispatch.best_standards_support end end diff --git a/railties/lib/rails/application/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb index 93a0fba10b..e567df7162 100644 --- a/railties/lib/rails/application/bootstrap.rb +++ b/railties/lib/rails/application/bootstrap.rb @@ -32,9 +32,9 @@ module Rails f.binmode f.sync = config.autoflush_log # if true make sure every write flushes - logger = ActiveSupport::TaggedLogging.new( - ActiveSupport::Logger.new(f) - ) + logger = ActiveSupport::Logger.new f + logger.formatter = config.log_formatter + logger = ActiveSupport::TaggedLogging.new(logger) logger.level = ActiveSupport::Logger.const_get(config.log_level.to_s.upcase) logger rescue StandardError diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 0f5fc2b7bc..1cfcd30c5b 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -8,7 +8,7 @@ module Rails attr_accessor :allow_concurrency, :asset_host, :asset_path, :assets, :autoflush_log, :cache_classes, :cache_store, :consider_all_requests_local, :console, :dependency_loading, :exceptions_app, :file_watcher, :filter_parameters, - :force_ssl, :helpers_paths, :logger, :log_tags, :preload_frameworks, + :force_ssl, :helpers_paths, :logger, :log_formatter, :log_tags, :preload_frameworks, :railties_order, :relative_url_root, :secret_token, :serve_static_assets, :ssl_options, :static_cache_control, :session_options, :time_zone, :reload_classes_only_on_change, :use_schema_cache_dump @@ -41,6 +41,7 @@ module Rails @file_watcher = ActiveSupport::FileUpdateChecker @exceptions_app = nil @autoflush_log = true + @log_formatter = ActiveSupport::Logger::SimpleFormatter.new @use_schema_cache_dump = true @assets = ActiveSupport::OrderedOptions.new diff --git a/railties/lib/rails/commands/server.rb b/railties/lib/rails/commands/server.rb index 0b757cbe28..a608693ca4 100644 --- a/railties/lib/rails/commands/server.rb +++ b/railties/lib/rails/commands/server.rb @@ -71,6 +71,8 @@ module Rails wrapped_app # touch the app so the logger is set up console = ActiveSupport::Logger.new($stdout) + console.formatter = Rails.logger.formatter + Rails.logger.extend(ActiveSupport::Logger.broadcast(console)) end diff --git a/railties/lib/rails/configuration.rb b/railties/lib/rails/configuration.rb index 0efa21d82c..d8185bcb34 100644 --- a/railties/lib/rails/configuration.rb +++ b/railties/lib/rails/configuration.rb @@ -6,17 +6,54 @@ require 'rails/rack' module Rails module Configuration - class MiddlewareStackProxy #:nodoc: + # MiddlewareStackProxy is a proxy for the Rails middleware stack that allows + # you to configure middlewares in your application. It works basically as a + # command recorder, saving each command to be applied after initialization + # over the default middleware stack, so you can add, swap, or remove any + # middleware in Rails. + # + # You can add your own middlewares by using the +config.middleware.use+ method: + # + # config.middleware.use Magical::Unicorns + # + # This will put the +Magical::Unicorns+ middleware on the end of the stack. + # You can use +insert_before+ if you wish to add a middleware before another: + # + # config.middleware.insert_before ActionDispatch::Head, Magical::Unicorns + # + # There's also +insert_after+ which will insert a middleware after another: + # + # config.middleware.insert_after ActionDispatch::Head, Magical::Unicorns + # + # Middlewares can also be completely swapped out and replaced with others: + # + # config.middleware.swap ActionDispatch::BestStandardsSupport, Magical::Unicorns + # + # And finally they can also be removed from the stack completely: + # + # config.middleware.delete ActionDispatch::BestStandardsSupport + # + # In addition to these methods to handle the stack, if your application is + # going to be used as an API endpoint only, the middleware stack can be + # configured like this: + # + # config.middleware.http_only! + # + # By doing this, Rails will create a smaller middleware stack, by not adding + # some middlewares that are usually useful for browser access only, such as + # Cookies, Session and Flash, BestStandardsSupport, and MethodOverride. You + # can always add any of them later manually if you want. + class MiddlewareStackProxy def initialize @operations = [] - @api_only = false + @http_only = false end - attr_reader :api_only - alias :api_only? :api_only + attr_reader :http_only + alias :http_only? :http_only - def api_only! - @api_only = true + def http_only! + @http_only = true end def insert_before(*args, &block) @@ -41,7 +78,7 @@ module Rails @operations << [:delete, args, block] end - def merge_into(other) + def merge_into(other) #:nodoc: @operations.each do |operation, args, block| other.send(operation, *args, &block) end diff --git a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb index e8065d9505..b3d6adad2a 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb +++ b/railties/lib/rails/generators/rails/app/templates/app/controllers/application_controller.rb @@ -1,3 +1,5 @@ class ApplicationController < ActionController::Base - protect_from_forgery + # prevent CSRF attacks by raising an exception, + # if your application has an API, you'll probably need to use :reset_session + protect_from_forgery :with => :exception end diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt index 7041550fd0..d0d9083c37 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt @@ -73,4 +73,7 @@ # Disable automatic flushing of the log to improve performance. # config.autoflush_log = false + + # Use default logging formatter so that PID and timestamp are not suppressed + config.log_formatter = ::Logger::Formatter.new end diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb index dcd3b276e3..8d45b2bbd9 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb +++ b/railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb @@ -8,3 +8,8 @@ Rails.backtrace_cleaner.remove_silencers! # Load support files Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f } + +# Load fixtures from the engine +if ActiveSupport::TestCase.method_defined?(:fixture_path) + ActiveSupport::TestCase.fixture_path = File.expand_path("../fixtures", __FILE__) +end
\ No newline at end of file diff --git a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb index f7e907a017..4c07baae36 100644 --- a/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/scaffold_generator.rb @@ -8,10 +8,31 @@ module TestUnit check_class_collision :suffix => "ControllerTest" + argument :attributes, :type => :array, :default => [], :banner => "field:type field:type" + def create_test_files template 'functional_test.rb', File.join('test/functional', controller_class_path, "#{controller_file_name}_controller_test.rb") end + + private + + def resource_attributes + key_value singular_table_name, "{ #{attributes_hash} }" + end + + def attributes_hash + return if accessible_attributes.empty? + + accessible_attributes.map do |a| + name = a.name + "#{name}: @#{singular_table_name}.#{name}" + end.sort.join(', ') + end + + def accessible_attributes + attributes.reject(&:reference?) + end end end end diff --git a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb index 9ec2e34545..19894443d5 100644 --- a/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb +++ b/railties/lib/rails/generators/test_unit/scaffold/templates/functional_test.rb @@ -19,7 +19,7 @@ class <%= controller_class_name %>ControllerTest < ActionController::TestCase test "should create <%= singular_table_name %>" do assert_difference('<%= class_name %>.count') do - post :create, <%= key_value singular_table_name, "@#{singular_table_name}.attributes" %> + post :create, <%= resource_attributes %> end assert_redirected_to <%= singular_table_name %>_path(assigns(:<%= singular_table_name %>)) @@ -36,7 +36,7 @@ class <%= controller_class_name %>ControllerTest < ActionController::TestCase end test "should update <%= singular_table_name %>" do - put :update, <%= key_value :id, "@#{singular_table_name}" %>, <%= key_value singular_table_name, "@#{singular_table_name}.attributes" %> + put :update, <%= key_value :id, "@#{singular_table_name}" %>, <%= resource_attributes %> assert_redirected_to <%= singular_table_name %>_path(assigns(:<%= singular_table_name %>)) end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index c9310aff87..ac5ac2b93e 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -275,19 +275,23 @@ module ApplicationTests require "#{app_path}/config/environment" + token = "cf50faa3fe97702ca1ae" + PostsController.any_instance.stubs(:form_authenticity_token).returns(token) + params = {:authenticity_token => token} + get "/posts/1" assert_match /patch/, last_response.body - patch "/posts/1" + patch "/posts/1", params assert_match /update/, last_response.body - patch "/posts/1" + patch "/posts/1", params assert_equal 200, last_response.status - put "/posts/1" + put "/posts/1", params assert_match /update/, last_response.body - put "/posts/1" + put "/posts/1", params assert_equal 200, last_response.status end @@ -528,6 +532,12 @@ module ApplicationTests end RUBY + app_file 'app/controllers/application_controller.rb', <<-RUBY + class ApplicationController < ActionController::Base + protect_from_forgery :with => :reset_session # as we are testing API here + end + RUBY + app_file 'app/controllers/posts_controller.rb', <<-RUBY class PostsController < ApplicationController def create diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index a0e88cd0f0..a08e5b2374 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -1,4 +1,5 @@ require "isolation/abstract_unit" +require 'set' module ApplicationTests class FrameworksTest < ActiveSupport::TestCase @@ -66,7 +67,7 @@ module ApplicationTests require "#{app_path}/config/environment" assert Foo.method_defined?(:foo_path) assert Foo.method_defined?(:main_app) - assert_equal ["notify"], Foo.action_methods + assert_equal Set.new(["notify"]), Foo.action_methods end test "allows to not load all helpers for controllers" do @@ -196,9 +197,8 @@ module ApplicationTests test "use schema cache dump" do Dir.chdir(app_path) do - `rails generate model post title:string` - `bundle exec rake db:migrate` - `bundle exec rake db:schema:cache:dump` + `rails generate model post title:string; + bundle exec rake db:migrate db:schema:cache:dump` end require "#{app_path}/config/environment" ActiveRecord::Base.connection.drop_table("posts") # force drop posts table for test. @@ -207,17 +207,13 @@ module ApplicationTests test "expire schema cache dump" do Dir.chdir(app_path) do - `rails generate model post title:string` - `bundle exec rake db:migrate` - `bundle exec rake db:schema:cache:dump` - - `bundle exec rake db:rollback` + `rails generate model post title:string; + bundle exec rake db:migrate db:schema:cache:dump db:rollback` end silence_warnings { require "#{app_path}/config/environment" assert !ActiveRecord::Base.connection.schema_cache.tables["posts"] } end - end end diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb index a190a31fc7..712a555c4a 100644 --- a/railties/test/application/middleware_test.rb +++ b/railties/test/application/middleware_test.rb @@ -53,7 +53,7 @@ module ApplicationTests end test "api middleware stack" do - add_to_config "config.middleware.api_only!" + add_to_config "config.middleware.http_only!" add_to_config "config.force_ssl = true" add_to_config "config.action_dispatch.x_sendfile_header = 'X-Sendfile'" @@ -216,7 +216,6 @@ module ApplicationTests assert_equal etag, last_response.headers["Etag"] get "/?nothing=true" - puts last_response.body assert_equal 200, last_response.status assert_equal "", last_response.body assert_equal "text/html; charset=utf-8", last_response.headers["Content-Type"] diff --git a/railties/test/application/rake/migrations_test.rb b/railties/test/application/rake/migrations_test.rb index c94334f189..0a47fd014c 100644 --- a/railties/test/application/rake/migrations_test.rb +++ b/railties/test/application/rake/migrations_test.rb @@ -16,44 +16,45 @@ module ApplicationTests test 'running migrations with given scope' do Dir.chdir(app_path) do `rails generate model user username:string password:string` - end - app_file "db/migrate/01_a_migration.bukkits.rb", <<-MIGRATION - class AMigration < ActiveRecord::Migration - end - MIGRATION - output = Dir.chdir(app_path) { `rake db:migrate SCOPE=bukkits` } - assert_no_match(/create_table\(:users\)/, output) - assert_no_match(/CreateUsers/, output) - assert_no_match(/add_column\(:users, :email, :string\)/, output) + app_file "db/migrate/01_a_migration.bukkits.rb", <<-MIGRATION + class AMigration < ActiveRecord::Migration + end + MIGRATION + + output = `rake db:migrate SCOPE=bukkits` + assert_no_match(/create_table\(:users\)/, output) + assert_no_match(/CreateUsers/, output) + assert_no_match(/add_column\(:users, :email, :string\)/, output) - assert_match(/AMigration: migrated/, output) + assert_match(/AMigration: migrated/, output) - output = Dir.chdir(app_path) { `rake db:migrate SCOPE=bukkits VERSION=0` } - assert_no_match(/drop_table\(:users\)/, output) - assert_no_match(/CreateUsers/, output) - assert_no_match(/remove_column\(:users, :email\)/, output) + output = `rake db:migrate SCOPE=bukkits VERSION=0` + assert_no_match(/drop_table\(:users\)/, output) + assert_no_match(/CreateUsers/, output) + assert_no_match(/remove_column\(:users, :email\)/, output) - assert_match(/AMigration: reverted/, output) + assert_match(/AMigration: reverted/, output) + end end test 'model and migration generator with change syntax' do Dir.chdir(app_path) do - `rails generate model user username:string password:string` - `rails generate migration add_email_to_users email:string` + `rails generate model user username:string password:string; + rails generate migration add_email_to_users email:string` + + output = `rake db:migrate` + assert_match(/create_table\(:users\)/, output) + assert_match(/CreateUsers: migrated/, output) + assert_match(/add_column\(:users, :email, :string\)/, output) + assert_match(/AddEmailToUsers: migrated/, output) + + output = `rake db:rollback STEP=2` + assert_match(/drop_table\("users"\)/, output) + assert_match(/CreateUsers: reverted/, output) + assert_match(/remove_column\("users", :email\)/, output) + assert_match(/AddEmailToUsers: reverted/, output) end - - output = Dir.chdir(app_path){ `rake db:migrate` } - assert_match(/create_table\(:users\)/, output) - assert_match(/CreateUsers: migrated/, output) - assert_match(/add_column\(:users, :email, :string\)/, output) - assert_match(/AddEmailToUsers: migrated/, output) - - output = Dir.chdir(app_path){ `rake db:rollback STEP=2` } - assert_match(/drop_table\("users"\)/, output) - assert_match(/CreateUsers: reverted/, output) - assert_match(/remove_column\("users", :email\)/, output) - assert_match(/AddEmailToUsers: reverted/, output) end test 'migration status when schema migrations table is not present' do @@ -63,94 +64,94 @@ module ApplicationTests test 'test migration status' do Dir.chdir(app_path) do - `rails generate model user username:string password:string` - `rails generate migration add_email_to_users email:string` - end + `rails generate model user username:string password:string; + rails generate migration add_email_to_users email:string; + rake db:migrate` - Dir.chdir(app_path) { `rake db:migrate` } - output = Dir.chdir(app_path) { `rake db:migrate:status` } + output = `rake db:migrate:status` - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) - Dir.chdir(app_path) { `rake db:rollback STEP=1` } - output = Dir.chdir(app_path) { `rake db:migrate:status` } + `rake db:rollback STEP=1` + output = `rake db:migrate:status` - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/down\s+\d{14}\s+Add email to users/, output) + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/down\s+\d{14}\s+Add email to users/, output) + end end test 'migration status without timestamps' do add_to_config('config.active_record.timestamped_migrations = false') Dir.chdir(app_path) do - `rails generate model user username:string password:string` - `rails generate migration add_email_to_users email:string` - end + `rails generate model user username:string password:string; + rails generate migration add_email_to_users email:string; + rake db:migrate` - Dir.chdir(app_path) { `rake db:migrate` } - output = Dir.chdir(app_path) { `rake db:migrate:status` } + output = `rake db:migrate:status` - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/up\s+\d{3,}\s+Add email to users/, output) + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/up\s+\d{3,}\s+Add email to users/, output) - Dir.chdir(app_path) { `rake db:rollback STEP=1` } - output = Dir.chdir(app_path) { `rake db:migrate:status` } + `rake db:rollback STEP=1` + output = `rake db:migrate:status` - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/down\s+\d{3,}\s+Add email to users/, output) + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/down\s+\d{3,}\s+Add email to users/, output) + end end test 'test migration status after rollback and redo' do Dir.chdir(app_path) do - `rails generate model user username:string password:string` - `rails generate migration add_email_to_users email:string` - end + `rails generate model user username:string password:string; + rails generate migration add_email_to_users email:string; + rake db:migrate` - Dir.chdir(app_path) { `rake db:migrate` } - output = Dir.chdir(app_path) { `rake db:migrate:status` } + output = `rake db:migrate:status` - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) - Dir.chdir(app_path) { `rake db:rollback STEP=2` } - output = Dir.chdir(app_path) { `rake db:migrate:status` } + `rake db:rollback STEP=2` + output = `rake db:migrate:status` - assert_match(/down\s+\d{14}\s+Create users/, output) - assert_match(/down\s+\d{14}\s+Add email to users/, output) + assert_match(/down\s+\d{14}\s+Create users/, output) + assert_match(/down\s+\d{14}\s+Add email to users/, output) - Dir.chdir(app_path) { `rake db:migrate:redo` } - output = Dir.chdir(app_path) { `rake db:migrate:status` } + `rake db:migrate:redo` + output = `rake db:migrate:status` - assert_match(/up\s+\d{14}\s+Create users/, output) - assert_match(/up\s+\d{14}\s+Add email to users/, output) + assert_match(/up\s+\d{14}\s+Create users/, output) + assert_match(/up\s+\d{14}\s+Add email to users/, output) + end end test 'migration status after rollback and redo without timestamps' do add_to_config('config.active_record.timestamped_migrations = false') Dir.chdir(app_path) do - `rails generate model user username:string password:string` - `rails generate migration add_email_to_users email:string` - end + `rails generate model user username:string password:string; + rails generate migration add_email_to_users email:string; + rake db:migrate` - Dir.chdir(app_path) { `rake db:migrate` } - output = Dir.chdir(app_path) { `rake db:migrate:status` } + output = `rake db:migrate:status` - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/up\s+\d{3,}\s+Add email to users/, output) + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/up\s+\d{3,}\s+Add email to users/, output) - Dir.chdir(app_path) { `rake db:rollback STEP=2` } - output = Dir.chdir(app_path) { `rake db:migrate:status` } + `rake db:rollback STEP=2` + output = `rake db:migrate:status` - assert_match(/down\s+\d{3,}\s+Create users/, output) - assert_match(/down\s+\d{3,}\s+Add email to users/, output) + assert_match(/down\s+\d{3,}\s+Create users/, output) + assert_match(/down\s+\d{3,}\s+Add email to users/, output) - Dir.chdir(app_path) { `rake db:migrate:redo` } - output = Dir.chdir(app_path) { `rake db:migrate:status` } + `rake db:migrate:redo` + output = `rake db:migrate:status` - assert_match(/up\s+\d{3,}\s+Create users/, output) - assert_match(/up\s+\d{3,}\s+Add email to users/, output) + assert_match(/up\s+\d{3,}\s+Create users/, output) + assert_match(/up\s+\d{3,}\s+Add email to users/, output) + end end end end diff --git a/railties/test/application/rake/notes_test.rb b/railties/test/application/rake/notes_test.rb index 4ab20afc47..b66433f64d 100644 --- a/railties/test/application/rake/notes_test.rb +++ b/railties/test/application/rake/notes_test.rb @@ -3,7 +3,7 @@ require "isolation/abstract_unit" module ApplicationTests module RakeTests class RakeNotesTest < ActiveSupport::TestCase - def setup + def setup build_app require "rails/all" end @@ -13,7 +13,6 @@ module ApplicationTests end test 'notes' do - app_file "app/views/home/index.html.erb", "<% # TODO: note in erb %>" app_file "app/views/home/index.html.haml", "-# TODO: note in haml" app_file "app/views/home/index.html.slim", "/ TODO: note in slim" diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb index 9515e40b6e..fed9dce8e1 100644 --- a/railties/test/application/rake_test.rb +++ b/railties/test/application/rake_test.rb @@ -107,9 +107,9 @@ module ApplicationTests def test_loading_specific_fixtures Dir.chdir(app_path) do - `rails generate model user username:string password:string` - `rails generate model product name:string` - `rake db:migrate` + `rails generate model user username:string password:string; + rails generate model product name:string; + rake db:migrate` end require "#{rails_root}/config/environment" @@ -124,8 +124,8 @@ module ApplicationTests def test_scaffold_tests_pass_by_default content = Dir.chdir(app_path) do - `rails generate scaffold user username:string password:string` - `bundle exec rake db:migrate db:test:clone test` + `rails generate scaffold user username:string password:string; + bundle exec rake db:migrate db:test:clone test` end assert_match(/\d+ tests, \d+ assertions, 0 failures, 0 errors/, content) @@ -133,29 +133,26 @@ module ApplicationTests def test_rake_dump_structure_should_respect_db_structure_env_variable Dir.chdir(app_path) do - `bundle exec rake db:migrate` # ensure we have a schema_migrations table to dump - `bundle exec rake db:structure:dump DB_STRUCTURE=db/my_structure.sql` + # ensure we have a schema_migrations table to dump + `bundle exec rake db:migrate db:structure:dump DB_STRUCTURE=db/my_structure.sql` end assert File.exists?(File.join(app_path, 'db', 'my_structure.sql')) end def test_rake_dump_schema_cache Dir.chdir(app_path) do - `rails generate model post title:string` - `rails generate model product name:string` - `bundle exec rake db:migrate` - `bundle exec rake db:schema:cache:dump` + `rails generate model post title:string; + rails generate model product name:string; + bundle exec rake db:migrate db:schema:cache:dump` end assert File.exists?(File.join(app_path, 'db', 'schema_cache.dump')) end def test_rake_clear_schema_cache Dir.chdir(app_path) do - `bundle exec rake db:schema:cache:dump` - `bundle exec rake db:schema:cache:clear` + `bundle exec rake db:schema:cache:dump db:schema:cache:clear` end assert !File.exists?(File.join(app_path, 'db', 'schema_cache.dump')) end - end end diff --git a/railties/test/generators/scaffold_controller_generator_test.rb b/railties/test/generators/scaffold_controller_generator_test.rb index 1382133d7b..03a753151d 100644 --- a/railties/test/generators/scaffold_controller_generator_test.rb +++ b/railties/test/generators/scaffold_controller_generator_test.rb @@ -75,6 +75,19 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase assert_file "test/functional/users_controller_test.rb" do |content| assert_match(/class UsersControllerTest < ActionController::TestCase/, content) assert_match(/test "should get index"/, content) + assert_match(/post :create, user: { age: @user.age, name: @user.name }/, content) + assert_match(/put :update, id: @user, user: { age: @user.age, name: @user.name }/, content) + end + end + + def test_functional_tests_without_attributes + run_generator ["User"] + + assert_file "test/functional/users_controller_test.rb" do |content| + assert_match(/class UsersControllerTest < ActionController::TestCase/, content) + assert_match(/test "should get index"/, content) + assert_match(/post :create, user: { }/, content) + assert_match(/put :update, id: @user, user: { }/, content) end end diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index 2db8090621..f4f0147156 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -62,8 +62,11 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase end end - assert_file "test/functional/product_lines_controller_test.rb", - /class ProductLinesControllerTest < ActionController::TestCase/ + assert_file "test/functional/product_lines_controller_test.rb" do |test| + assert_match(/class ProductLinesControllerTest < ActionController::TestCase/, test) + assert_match(/post :create, product_line: { title: @product_line.title }/, test) + assert_match(/put :update, id: @product_line, product_line: { title: @product_line.title }/, test) + end # Views %w( @@ -85,6 +88,17 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase assert_file "app/assets/stylesheets/product_lines.css" end + def test_functional_tests_without_attributes + run_generator ["product_line"] + + assert_file "test/functional/product_lines_controller_test.rb" do |content| + assert_match(/class ProductLinesControllerTest < ActionController::TestCase/, content) + assert_match(/test "should get index"/, content) + assert_match(/post :create, product_line: { }/, content) + assert_match(/put :update, id: @product_line, product_line: { }/, content) + end + end + def test_scaffold_on_revoke run_generator run_generator ["product_line"], :behavior => :revoke |