diff options
129 files changed, 1548 insertions, 651 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/actionmailer.gemspec b/actionmailer/actionmailer.gemspec index c605f1ff04..f03240acb1 100644 --- a/actionmailer/actionmailer.gemspec +++ b/actionmailer/actionmailer.gemspec @@ -17,5 +17,5 @@ Gem::Specification.new do |s| s.requirements << 'none' s.add_dependency('actionpack', version) - s.add_dependency('mail', '~> 2.4.1') + s.add_dependency('mail', '~> 2.4.3') end 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 869d4704dd..c50cb5e927 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,10 +1,16 @@ ## Rails 4.0.0 (unreleased) ## -* 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. @@ -113,6 +119,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* @@ -303,6 +324,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 @@ -319,6 +341,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. @@ -328,6 +351,7 @@ *Jon Leighton* + ## Rails 3.1.2 (November 18, 2011) ## * Fix XSS security vulnerability in the `translate` helper method. When using interpolation @@ -369,6 +393,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/actionpack.gemspec b/actionpack/actionpack.gemspec index b205acdb79..089913be3c 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -18,7 +18,7 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('activemodel', version) - s.add_dependency('rack-cache', '~> 1.1') + s.add_dependency('rack-cache', '~> 1.2') s.add_dependency('builder', '~> 3.0.0') s.add_dependency('rack', '~> 1.4.1') s.add_dependency('rack-test', '~> 0.6.1') 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/abstract_controller/url_for.rb b/actionpack/lib/abstract_controller/url_for.rb index e4261068d8..4a95e1f276 100644 --- a/actionpack/lib/abstract_controller/url_for.rb +++ b/actionpack/lib/abstract_controller/url_for.rb @@ -1,10 +1,10 @@ -# Includes +url_for+ into the host class (e.g. an abstract controller or mailer). The class -# has to provide a +RouteSet+ by implementing the <tt>_routes</tt> methods. Otherwise, an -# exception will be raised. -# -# Note that this module is completely decoupled from HTTP - the only requirement is a valid -# <tt>_routes</tt> implementation. module AbstractController + # Includes +url_for+ into the host class (e.g. an abstract controller or mailer). The class + # has to provide a +RouteSet+ by implementing the <tt>_routes</tt> methods. Otherwise, an + # exception will be raised. + # + # Note that this module is completely decoupled from HTTP - the only requirement is a valid + # <tt>_routes</tt> implementation. module UrlFor extend ActiveSupport::Concern include ActionDispatch::Routing::UrlFor diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index a0c54dbd84..7c10fcbb8a 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -31,7 +31,6 @@ module ActionController autoload :RequestForgeryProtection autoload :Rescue autoload :Responder - autoload :SessionManagement autoload :Streaming autoload :Testing autoload :UrlFor diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 3b82231b15..95de595f4f 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -171,6 +171,16 @@ module ActionController class Base < Metal abstract! + # 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 + # end + # end + # + # This gives better control over what you want to exclude and makes it easier + # to create a bare controller class, instead of listing the modules required manually. def self.without_modules(*modules) modules = modules.map do |m| m.is_a?(Symbol) ? ActionController.const_get(m) : m @@ -192,7 +202,6 @@ module ActionController Renderers::All, ConditionalGet, RackDelegation, - SessionManagement, Caching, MimeResponds, ImplicitRender, diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index cb59af4f85..800752975c 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -6,8 +6,6 @@ module ActionController #:nodoc: module MimeResponds extend ActiveSupport::Concern - include ActionController::ImplicitRender - included do class_attribute :responder, :mimes_for_respond_to self.responder = ActionController::Responder @@ -193,7 +191,7 @@ module ActionController #:nodoc: if collector = retrieve_collector_from_mimes(mimes, &block) response = collector.response - response ? response.call : default_render({}) + response ? response.call : render({}) end end @@ -235,16 +233,8 @@ module ActionController #:nodoc: if collector = retrieve_collector_from_mimes(&block) options = resources.size == 1 ? {} : resources.extract_options! - - if defined_response = collector.response - if action = options.delete(:action) - render :action => action - else - defined_response.call - end - else - (options.delete(:responder) || self.class.responder).call(self, resources, options) - end + options[:default_response] = collector.response + (options.delete(:responder) || self.class.responder).call(self, resources, options) end end @@ -281,6 +271,7 @@ module ActionController #:nodoc: if format self.content_type ||= format.to_s lookup_context.formats = [format.to_sym] + lookup_context.rendered_format = lookup_context.formats.first collector else head :not_acceptable 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/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb index ccda01ed44..1e8990495c 100644 --- a/actionpack/lib/action_controller/metal/responder.rb +++ b/actionpack/lib/action_controller/metal/responder.rb @@ -130,6 +130,7 @@ module ActionController #:nodoc: @resources = resources @options = options @action = options.delete(:action) + @default_response = options.delete(:default_response) end delegate :head, :render, :redirect_to, :to => :controller @@ -172,7 +173,7 @@ module ActionController #:nodoc: # responds to :to_format and display it. # def to_format - if get? || !has_errors? + if get? || !has_errors? || response_overridden? default_render else display_errors @@ -226,7 +227,11 @@ module ActionController #:nodoc: # controller. # def default_render - controller.default_render(options) + if @default_response + @default_response.call(options) + else + controller.default_render(options) + end end # Display is just a shortcut to render a resource with the current format. @@ -274,5 +279,9 @@ module ActionController #:nodoc: def json_resource_errors {:errors => resource.errors} end + + def response_overridden? + @default_response.present? + end end end diff --git a/actionpack/lib/action_controller/metal/session_management.rb b/actionpack/lib/action_controller/metal/session_management.rb deleted file mode 100644 index 91d89ff9a4..0000000000 --- a/actionpack/lib/action_controller/metal/session_management.rb +++ /dev/null @@ -1,9 +0,0 @@ -module ActionController #:nodoc: - module SessionManagement #:nodoc: - extend ActiveSupport::Concern - - module ClassMethods - - end - end -end diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb index 1177a703b3..8e7b56dbcc 100644 --- a/actionpack/lib/action_controller/metal/url_for.rb +++ b/actionpack/lib/action_controller/metal/url_for.rb @@ -1,25 +1,25 @@ -# Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing -# the <tt>_routes</tt> method. Otherwise, an exception will be raised. -# -# In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define -# url options like the +host+. In order to do so, this module requires the host class -# to implement +env+ and +request+, which need to be a Rack-compatible. -# -# Example: -# -# class RootUrl -# include ActionController::UrlFor -# include Rails.application.routes.url_helpers -# -# delegate :env, :request, :to => :controller -# -# def initialize(controller) -# @controller = controller -# @url = root_path # named route from the application. -# end -# end -# module ActionController + # Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing + # the <tt>_routes</tt> method. Otherwise, an exception will be raised. + # + # In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define + # url options like the +host+. In order to do so, this module requires the host class + # to implement +env+ and +request+, which need to be a Rack-compatible. + # + # Example: + # + # class RootUrl + # include ActionController::UrlFor + # include Rails.application.routes.url_helpers + # + # delegate :env, :request, :to => :controller + # + # def initialize(controller) + # @controller = controller + # @url = root_path # named route from the application. + # end + # end + # module UrlFor extend ActiveSupport::Concern 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_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb index 5ab99d1061..c4a915d1ad 100644 --- a/actionpack/lib/action_dispatch/http/upload.rb +++ b/actionpack/lib/action_dispatch/http/upload.rb @@ -4,11 +4,12 @@ module ActionDispatch attr_accessor :original_filename, :content_type, :tempfile, :headers def initialize(hash) + @tempfile = hash[:tempfile] + raise(ArgumentError, ':tempfile is required') unless @tempfile + @original_filename = encode_filename(hash[:filename]) @content_type = hash[:type] @headers = hash[:head] - @tempfile = hash[:tempfile] - raise(ArgumentError, ':tempfile is required') unless @tempfile end def read(*args) @@ -19,15 +20,12 @@ module ActionDispatch [:open, :path, :rewind, :size].each do |method| class_eval "def #{method}; @tempfile.#{method}; end" end - + private + def encode_filename(filename) # Encode the filename in the utf8 encoding, unless it is nil - if filename - filename.force_encoding("UTF-8").encode! - else - filename - end + filename.force_encoding("UTF-8").encode! if filename end end diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb index 2a28e780bf..add8d94b70 100644 --- a/actionpack/lib/action_view/asset_paths.rb +++ b/actionpack/lib/action_view/asset_paths.rb @@ -117,7 +117,7 @@ module ActionView end def relative_url_root - config.relative_url_root + config.relative_url_root || current_request.try(:script_name) end def asset_host_config diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 7492ef56c6..835111eb95 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -45,10 +45,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> @@ -76,10 +76,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> @@ -860,20 +860,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 @@ -882,7 +882,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 @@ -910,7 +910,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 @@ -919,7 +919,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/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..14323a3891 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 = {}) 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/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb index 3628b935b7..245a19deec 100644 --- a/actionpack/lib/action_view/renderer/partial_renderer.rb +++ b/actionpack/lib/action_view/renderer/partial_renderer.rb @@ -158,6 +158,43 @@ module ActionView # Name: <%= user.name %> # </div> # + # If a collection is given, the layout will be rendered once for each item in the collection. Just think + # these two snippets have the same output: + # + # <%# app/views/users/_user.html.erb %> + # Name: <%= user.name %> + # + # <%# app/views/users/index.html.erb %> + # <%# This does not use layouts %> + # <ul> + # <% users.each do |user| -%> + # <li> + # <%= render :partial => "user", :locals => { :user => user } %> + # </li> + # <% end -%> + # </ul> + # + # <%# app/views/users/_li_layout.html.erb %> + # <li> + # <%= yield %> + # </li> + # + # <%# app/views/users/index.html.erb %> + # <ul> + # <%= render :partial => "user", :layout => "li_layout", :collection => users %> + # </ul> + # + # Given two users whose names are Alice and Bob, these snippets return: + # + # <ul> + # <li> + # Name: Alice + # </li> + # <li> + # Name: Bob + # </li> + # </ul> + # # You can also apply a layout to a block within any template: # # <%# app/views/users/_chief.html.erb &> @@ -238,7 +275,14 @@ module ActionView spacer = find_template(@options[:spacer_template]).render(@view, @locals) end + if layout = @options[:layout] + layout = find_template(layout) + end + result = @template ? collection_with_template : collection_without_template + + result.map!{|content| layout.render(@view, @locals) { content } } if layout + result.join(spacer).html_safe end @@ -342,9 +386,10 @@ module ActionView locals[as] = object segments << template.render(@view, locals) end - + segments end + def collection_without_template segments, locals, collection_data = [], @locals, @collection_data diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb index 20f75fba45..f7df9a6322 100644 --- a/actionpack/lib/action_view/renderer/template_renderer.rb +++ b/actionpack/lib/action_view/renderer/template_renderer.rb @@ -6,8 +6,13 @@ module ActionView @view = context @details = extract_details(options) template = determine_template(options) - @lookup_context.rendered_format ||= template.formats.first - @lookup_context.formats = template.formats + context = @lookup_context + + unless context.rendered_format + context.rendered_format = template.formats.first + context.formats = template.formats + end + render_template(template, options[:layout], options[:locals]) end 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/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index ae368842b5..0e4099ddc6 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -132,7 +132,6 @@ class RespondToController < ActionController::Base end end - def iphone_with_html_response_type request.format = :iphone if request.env["HTTP_ACCEPT"] == "text/iphone" @@ -1135,7 +1134,7 @@ class PostController < AbstractPostController around_filter :with_iphone def index - respond_to(:html, :iphone) + respond_to(:html, :iphone, :js) end protected @@ -1182,4 +1181,45 @@ class MimeControllerLayoutsTest < ActionController::TestCase get :index assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body end + + def test_non_navigational_format_with_no_template_fallbacks_to_html_template_with_no_layout + get :index, :format => :js + assert_equal "Hello Firefox", @response.body + end +end + +class FlashResponder < ActionController::Responder + def initialize(controller, resources, options={}) + super + end + + def to_html + controller.flash[:notice] = 'Success' + super + end +end + +class FlashResponderController < ActionController::Base + self.responder = FlashResponder + respond_to :html + + def index + respond_with Object.new do |format| + format.html { render :text => 'HTML' } + end + end +end + +class FlashResponderControllerTest < ActionController::TestCase + tests FlashResponderController + + def test_respond_with_block_executed + get :index + assert_equal 'HTML', @response.body + end + + def test_flash_responder_executed + get :index + assert_equal 'Success', flash[:notice] + 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/dispatch/uploaded_file_test.rb b/actionpack/test/dispatch/uploaded_file_test.rb index 0b95291e18..1e3f720fa7 100644 --- a/actionpack/test/dispatch/uploaded_file_test.rb +++ b/actionpack/test/dispatch/uploaded_file_test.rb @@ -12,7 +12,7 @@ module ActionDispatch uf = Http::UploadedFile.new(:filename => 'foo', :tempfile => Object.new) assert_equal 'foo', uf.original_filename end - + def test_filename_should_be_in_utf_8 uf = Http::UploadedFile.new(:filename => 'foo', :tempfile => Object.new) assert_equal "UTF-8", uf.original_filename.encoding.to_s diff --git a/actionpack/test/fixtures/test/_b_layout_for_partial.html.erb b/actionpack/test/fixtures/test/_b_layout_for_partial.html.erb new file mode 100644 index 0000000000..e918ba8f83 --- /dev/null +++ b/actionpack/test/fixtures/test/_b_layout_for_partial.html.erb @@ -0,0 +1 @@ +<b><%= yield %></b>
\ No newline at end of file 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/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 3d2eeea503..58ff055fc2 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -38,6 +38,7 @@ class AssetTagHelperTest < ActionView::TestCase @controller = BasicController.new @request = Class.new do + attr_accessor :script_name def protocol() 'http://' end def ssl?() false end def host_with_port() 'localhost' end @@ -647,6 +648,13 @@ class AssetTagHelperTest < ActionView::TestCase assert_equal %(<img alt="Rails" src="#{@controller.config.relative_url_root}/images/rails.png?#{expected_time}" />), image_tag("rails.png") end + # Same as above, but with script_name + def test_timebased_asset_id_with_script_name + @request.script_name = "/collaboration/hieraki" + expected_time = File.mtime(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).to_i.to_s + assert_equal %(<img alt="Rails" src="#{@request.script_name}/images/rails.png?#{expected_time}" />), image_tag("rails.png") + end + def test_should_skip_asset_id_on_complete_url assert_equal %(<img alt="Rails" src="http://www.example.com/rails.png" />), image_tag("http://www.example.com/rails.png") end @@ -897,6 +905,31 @@ class AssetTagHelperTest < ActionView::TestCase FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js')) end + # Same as above, but with script_name + def test_caching_javascript_include_tag_with_script_name + ENV["RAILS_ASSET_ID"] = "" + @request.script_name = "/collaboration/hieraki" + config.perform_caching = true + + assert_dom_equal( + %(<script src="/collaboration/hieraki/javascripts/all.js" type="text/javascript"></script>), + javascript_include_tag(:all, :cache => true) + ) + + assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js')) + + assert_dom_equal( + %(<script src="/collaboration/hieraki/javascripts/money.js" type="text/javascript"></script>), + javascript_include_tag(:all, :cache => "money") + ) + + assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js')) + + ensure + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js')) + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js')) + end + def test_caching_javascript_include_tag_with_named_paths_and_relative_url_root_when_caching_off ENV["RAILS_ASSET_ID"] = "" @controller.config.relative_url_root = "/collaboration/hieraki" @@ -917,6 +950,27 @@ class AssetTagHelperTest < ActionView::TestCase assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js')) end + # Same as above, but with script_name + def test_caching_javascript_include_tag_with_named_paths_and_script_name_when_caching_off + ENV["RAILS_ASSET_ID"] = "" + @request.script_name = "/collaboration/hieraki" + config.perform_caching = false + + assert_dom_equal( + %(<script src="/collaboration/hieraki/javascripts/robber.js" type="text/javascript"></script>), + javascript_include_tag('robber', :cache => true) + ) + + assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js')) + + assert_dom_equal( + %(<script src="/collaboration/hieraki/javascripts/robber.js" type="text/javascript"></script>), + javascript_include_tag('robber', :cache => "money", :recursive => true) + ) + + assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js')) + end + def test_caching_javascript_include_tag_when_caching_off ENV["RAILS_ASSET_ID"] = "" config.perform_caching = false @@ -1144,6 +1198,33 @@ class AssetTagHelperTest < ActionView::TestCase FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) end + # Same as above, but with script_name + def test_caching_stylesheet_link_tag_with_script_name + ENV["RAILS_ASSET_ID"] = "" + @request.script_name = "/collaboration/hieraki" + config.perform_caching = true + + assert_dom_equal( + %(<link href="/collaboration/hieraki/stylesheets/all.css" media="screen" rel="stylesheet" type="text/css" />), + stylesheet_link_tag(:all, :cache => true) + ) + + files_to_be_joined = Dir["#{ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR}/[^all]*.css"] + + expected_mtime = files_to_be_joined.map { |p| File.mtime(p) }.max + assert_equal expected_mtime, File.mtime(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) + + assert_dom_equal( + %(<link href="/collaboration/hieraki/stylesheets/money.css" media="screen" rel="stylesheet" type="text/css" />), + stylesheet_link_tag(:all, :cache => "money") + ) + + assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) + ensure + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) + FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) + end + def test_caching_stylesheet_link_tag_with_named_paths_and_relative_url_root_when_caching_off ENV["RAILS_ASSET_ID"] = "" @@ -1165,6 +1246,27 @@ class AssetTagHelperTest < ActionView::TestCase assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) end + # Same as above, but with script_name + def test_caching_stylesheet_link_tag_with_named_paths_and_script_name_when_caching_off + ENV["RAILS_ASSET_ID"] = "" + @request.script_name = "/collaboration/hieraki" + config.perform_caching = false + + assert_dom_equal( + %(<link href="/collaboration/hieraki/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />), + stylesheet_link_tag('robber', :cache => true) + ) + + assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css')) + + assert_dom_equal( + %(<link href="/collaboration/hieraki/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />), + stylesheet_link_tag('robber', :cache => "money") + ) + + assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css')) + end + 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/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/render_test.rb b/actionpack/test/template/render_test.rb index 7347e15373..122b07d348 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -238,6 +238,10 @@ module RenderTestCases def test_render_partial_with_nil_values_in_collection assert_equal "Hello: davidHello: Anonymous", @view.render(:partial => "test/customer", :collection => [ Customer.new("david"), nil ]) end + + def test_render_partial_with_layout_using_collection_and_template + assert_equal "<b>Hello: Amazon</b><b>Hello: Yahoo</b>", @view.render(:partial => "test/customer", :layout => 'test/b_layout_for_partial', :collection => [ Customer.new("Amazon"), Customer.new("Yahoo") ]) + end def test_render_partial_with_empty_array_should_return_nil assert_nil @view.render(:partial => []) diff --git a/actionpack/test/template/sprockets_helper_test.rb b/actionpack/test/template/sprockets_helper_test.rb index 1c591bdcc2..d4d2dedfb6 100644 --- a/actionpack/test/template/sprockets_helper_test.rb +++ b/actionpack/test/template/sprockets_helper_test.rb @@ -12,6 +12,7 @@ class SprocketsHelperTest < ActionView::TestCase def protocol() 'http://' end def ssl?() false end def host_with_port() 'localhost' end + def script_name() nil end end def setup 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/mass_assignment_security.rb b/activemodel/lib/active_model/mass_assignment_security.rb index 95de039676..5e5405fe27 100644 --- a/activemodel/lib/active_model/mass_assignment_security.rb +++ b/activemodel/lib/active_model/mass_assignment_security.rb @@ -85,7 +85,7 @@ module ActiveModel # end # end # - # When using the :default role : + # When using the :default role: # # customer = Customer.new # customer.assign_attributes({ "name" => "David", "email" => "a@b.com", :logins_count => 5 }, :as => :default) @@ -93,7 +93,7 @@ module ActiveModel # customer.email # => "a@b.com" # customer.logins_count # => nil # - # And using the :admin role : + # And using the :admin role: # # customer = Customer.new # customer.assign_attributes({ "name" => "David", "email" => "a@b.com", :logins_count => 5}, :as => :admin) @@ -107,8 +107,9 @@ module ActiveModel # To start from an all-closed default and enable attributes as needed, # have a look at +attr_accessible+. # - # Note that using <tt>Hash#except</tt> or <tt>Hash#slice</tt> in place of +attr_protected+ - # to sanitize attributes won't provide sufficient protection. + # Note that using <tt>Hash#except</tt> or <tt>Hash#slice</tt> in place of + # +attr_protected+ to sanitize attributes provides basically the same + # functionality, but it makes a bit tricky to deal with nested attributes. def attr_protected(*args) options = args.extract_options! role = options[:as] || :default @@ -152,7 +153,7 @@ module ActiveModel # end # end # - # When using the :default role : + # When using the :default role: # # customer = Customer.new # customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :default) @@ -162,15 +163,16 @@ module ActiveModel # customer.credit_rating = "Average" # customer.credit_rating # => "Average" # - # And using the :admin role : + # And using the :admin role: # # customer = Customer.new # customer.assign_attributes({ "name" => "David", "credit_rating" => "Excellent", :last_login => 1.day.ago }, :as => :admin) # customer.name # => "David" # customer.credit_rating # => "Excellent" # - # Note that using <tt>Hash#except</tt> or <tt>Hash#slice</tt> in place of +attr_accessible+ - # to sanitize attributes won't provide sufficient protection. + # Note that using <tt>Hash#except</tt> or <tt>Hash#slice</tt> in place of + # +attr_accessible+ to sanitize attributes provides basically the same + # functionality, but it makes a bit tricky to deal with nested attributes. def attr_accessible(*args) options = args.extract_options! role = options[:as] || :default 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 92e0b47469..6fd86b1f32 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,28 @@ ## Rails 4.0.0 (unreleased) ## +* Added the schema cache dump feature. + + `Schema cache dump` feature was implemetend. This feature can dump/load internal state of `SchemaCache` instance + because we want to boot rails more quickly when we have many models. + + Usage notes: + + 1) execute rake task. + RAILS_ENV=production bundle exec rake db:schema:cache:dump + => generate db/schema_cache.dump + + 2) add config.use_schema_cache_dump = true in config/production.rb. BTW, true is default. + + 3) boot rails. + RAILS_ENV=production bundle exec rails server + => use db/schema_cache.dump + + 4) If you remove clear dumped cache, execute rake task. + RAILS_ENV=production bundle exec rake db:schema:cache:clear + => remove db/schema_cache.dump + + *kennyj* + * Added support for partial indices to PostgreSQL adapter The `add_index` method now supports a `where` option that receives a @@ -130,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* @@ -320,6 +365,7 @@ *Aaron Christy* + ## Rails 3.1.4 (March 1, 2012) ## * Fix a custom primary key regression *GH 3987* @@ -347,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 @@ -360,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 @@ -407,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.rb b/activerecord/lib/active_record/associations.rb index 958821add6..b1a5110e2d 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1512,8 +1512,8 @@ module ActiveRecord # * <tt>Developer#projects.size</tt> # * <tt>Developer#projects.find(id)</tt> # * <tt>Developer#projects.exists?(...)</tt> - # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("project_id" => id)</tt>) - # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("project_id" => id); c.save; c</tt>) + # * <tt>Developer#projects.build</tt> (similar to <tt>Project.new("developer_id" => id)</tt>) + # * <tt>Developer#projects.create</tt> (similar to <tt>c = Project.new("developer_id" => id); c.save; c</tt>) # The declaration may include an options hash to specialize the behavior of the association. # # === Options 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/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 9657cb081d..53d49fef2e 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -73,7 +73,9 @@ module ActiveRecord # association def build_through_record(record) @through_records[record.object_id] ||= begin - through_record = through_association.build(construct_join_attributes(record)) + ensure_mutable + + through_record = through_association.build through_record.send("#{source_reflection.name}=", record) through_record end diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb index f95e5337c2..fd0e90aaf0 100644 --- a/activerecord/lib/active_record/associations/through_association.rb +++ b/activerecord/lib/active_record/associations/through_association.rb @@ -37,9 +37,7 @@ module ActiveRecord # situation it is more natural for the user to just create or modify their join records # directly as required. def construct_join_attributes(*records) - if source_reflection.macro != :belongs_to - raise HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(owner, reflection) - end + ensure_mutable join_attributes = { source_reflection.foreign_key => @@ -73,6 +71,12 @@ module ActiveRecord !owner[through_reflection.foreign_key].nil? end + def ensure_mutable + if source_reflection.macro != :belongs_to + raise HasManyThroughCantAssociateThroughHasOneOrManyReflection.new(owner, reflection) + end + end + def ensure_not_nested if reflection.nested? raise HasManyThroughNestedAssociationsAreReadonly.new(owner, reflection) diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 4b0925a9ec..bebf6a1a05 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -189,7 +189,7 @@ module ActiveRecord # nil nor empty? (the latter only applies to objects that respond to empty?, most notably Strings). def attribute_present?(attribute) value = read_attribute(attribute) - !value.nil? || (value.respond_to?(:empty?) && !value.empty?) + !value.nil? && !(value.respond_to?(:empty?) && value.empty?) end # Returns the column object for the named attribute. 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/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 594649189d..767c99de4c 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -86,6 +86,11 @@ module ActiveRecord end end + def schema_cache=(cache) + cache.connection = self + @schema_cache = cache + end + def expire @in_use = false end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index d2126a3e19..5b7fa029da 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -978,6 +978,27 @@ module ActiveRecord end_sql end + # Returns an array of schema names. + def schema_names + query(<<-SQL).flatten + SELECT nspname + FROM pg_namespace + WHERE nspname !~ '^pg_.*' + AND nspname NOT IN ('information_schema') + ORDER by nspname; + SQL + end + + # Creates a schema for the given schema name. + def create_schema schema_name + execute "CREATE SCHEMA #{schema_name}" + end + + # Drops the schema for the given schema name. + def drop_schema schema_name + execute "DROP SCHEMA #{schema_name} CASCADE" + end + # Sets the schema search path to a string of comma-separated schema names. # Names beginning with $ have to be quoted (e.g. $user => '$user'). # See: http://www.postgresql.org/docs/current/static/ddl-schemas.html diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb index 962718da56..aad1f9a7ef 100644 --- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb @@ -1,26 +1,17 @@ module ActiveRecord module ConnectionAdapters class SchemaCache - attr_reader :columns, :columns_hash, :primary_keys, :tables - attr_reader :connection + attr_reader :columns, :columns_hash, :primary_keys, :tables, :version + attr_accessor :connection def initialize(conn) @connection = conn - @tables = {} - @columns = Hash.new do |h, table_name| - h[table_name] = conn.columns(table_name) - end - - @columns_hash = Hash.new do |h, table_name| - h[table_name] = Hash[columns[table_name].map { |col| - [col.name, col] - }] - end - - @primary_keys = Hash.new do |h, table_name| - h[table_name] = table_exists?(table_name) ? conn.primary_key(table_name) : nil - end + @columns = {} + @columns_hash = {} + @primary_keys = {} + @tables = {} + prepare_default_proc end # A cached lookup for table existence. @@ -30,12 +21,22 @@ module ActiveRecord @tables[name] = connection.table_exists?(name) end + # Add internal cache for table with +table_name+. + def add(table_name) + if table_exists?(table_name) + @primary_keys[table_name] + @columns[table_name] + @columns_hash[table_name] + end + end + # Clears out internal caches def clear! @columns.clear @columns_hash.clear @primary_keys.clear @tables.clear + @version = nil end # Clear out internal caches for table with +table_name+. @@ -45,6 +46,37 @@ module ActiveRecord @primary_keys.delete table_name @tables.delete table_name end + + def marshal_dump + # if we get current version during initialization, it happens stack over flow. + @version = ActiveRecord::Migrator.current_version + [@version] + [:@columns, :@columns_hash, :@primary_keys, :@tables].map do |val| + self.instance_variable_get(val).inject({}) { |h, v| h[v[0]] = v[1]; h } + end + end + + def marshal_load(array) + @version, @columns, @columns_hash, @primary_keys, @tables = array + prepare_default_proc + end + + private + + def prepare_default_proc + @columns.default_proc = Proc.new do |h, table_name| + h[table_name] = connection.columns(table_name) + end + + @columns_hash.default_proc = Proc.new do |h, table_name| + h[table_name] = Hash[columns[table_name].map { |col| + [col.name, col] + }] + end + + @primary_keys.default_proc = Proc.new do |h, table_name| + h[table_name] = table_exists?(table_name) ? connection.primary_key(table_name) : nil + end + end end end end 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/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index 24896801d4..99847ac161 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -118,7 +118,7 @@ module ActiveRecord if defined?(@table_name) return if value == @table_name - reset_column_information + reset_column_information if connected? end @table_name = value diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 058dd58efb..516c450f85 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -107,7 +107,7 @@ module ActiveRecord config.watchable_files.concat ["#{app.root}/db/schema.rb", "#{app.root}/db/structure.sql"] end - config.after_initialize do + config.after_initialize do |app| ActiveSupport.on_load(:active_record) do ActiveRecord::Base.instantiate_observers @@ -115,6 +115,21 @@ module ActiveRecord ActiveRecord::Base.instantiate_observers end end + + ActiveSupport.on_load(:active_record) do + if app.config.use_schema_cache_dump + filename = File.join(app.config.paths["db"].first, "schema_cache.dump") + if File.file?(filename) + cache = Marshal.load(open(filename, 'rb') { |f| f.read }) + if cache.version == ActiveRecord::Migrator.current_version + ActiveRecord::Base.connection.schema_cache = cache + else + warn "schema_cache.dump is expired. Current version is #{ActiveRecord::Migrator.current_version}, but cache version is #{cache.version}." + end + end + end + end + end end end diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 7e74fe7d13..f26989ae57 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -372,6 +372,25 @@ db_namespace = namespace :db do task :load_if_ruby => 'db:create' do db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby end + + namespace :cache do + desc 'Create a db/schema_cache.dump file.' + task :dump => :environment do + con = ActiveRecord::Base.connection + filename = File.join(Rails.application.config.paths["db"].first, "schema_cache.dump") + + con.schema_cache.clear! + con.tables.each { |table| con.schema_cache.add(table) } + open(filename, 'wb') { |f| f.write(Marshal.dump(con.schema_cache)) } + end + + desc 'Clear a db/schema_cache.dump file.' + task :clear => :environment do + filename = File.join(Rails.application.config.paths["db"].first, "schema_cache.dump") + FileUtils.rm(filename) if File.exists?(filename) + end + end + end namespace :structure do @@ -458,7 +477,7 @@ db_namespace = namespace :db do db_namespace["test:load_schema"].invoke when :sql db_namespace["test:load_structure"].invoke - end + end end # desc "Recreate the test database from an existent structure.sql file" diff --git a/activerecord/lib/active_record/railties/jdbcmysql_error.rb b/activerecord/lib/active_record/railties/jdbcmysql_error.rb index 0b75983484..6a38211bff 100644 --- a/activerecord/lib/active_record/railties/jdbcmysql_error.rb +++ b/activerecord/lib/active_record/railties/jdbcmysql_error.rb @@ -1,6 +1,6 @@ #FIXME Remove if ArJdbcMysql will give. module ArJdbcMySQL #:nodoc: - class Error < StandardError + class Error < StandardError #:nodoc: attr_accessor :error_number, :sql_state def initialize msg 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/adapters/mysql/mysql_adapter_test.rb b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb index 5d6de90889..5aa4743e7b 100644 --- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb +++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb @@ -52,7 +52,7 @@ module ActiveRecord flunk rescue => e # assertion for *quoted* database properly - assert_match(/Unknown database 'foo-bar': SHOW TABLES IN `foo-bar`/, e.inspect) + assert_match(/database 'foo-bar'/, e.inspect) end end diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb index 10fdf88592..de4d9c2b33 100644 --- a/activerecord/test/cases/adapters/mysql2/schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb @@ -42,7 +42,7 @@ module ActiveRecord flunk rescue => e # assertion for *quoted* database properly - assert_match(/Unknown database 'foo-bar': SHOW TABLES IN `foo-bar`/, e.inspect) + assert_match(/database 'foo-bar'/, e.inspect) end end diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index 18c81d2b09..9208f53997 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -71,6 +71,45 @@ class SchemaTest < ActiveRecord::TestCase @connection.execute "DROP SCHEMA #{SCHEMA_NAME} CASCADE" end + def test_schema_names + assert_equal ["public", "test_schema", "test_schema2"], @connection.schema_names + end + + def test_create_schema + begin + @connection.create_schema "test_schema3" + assert @connection.schema_names.include? "test_schema3" + ensure + @connection.drop_schema "test_schema3" + end + end + + def test_raise_create_schema_with_existing_schema + begin + @connection.create_schema "test_schema3" + assert_raises(ActiveRecord::StatementInvalid) do + @connection.create_schema "test_schema3" + end + ensure + @connection.drop_schema "test_schema3" + end + end + + def test_drop_schema + begin + @connection.create_schema "test_schema3" + ensure + @connection.drop_schema "test_schema3" + end + assert !@connection.schema_names.include?("test_schema3") + end + + def test_raise_drop_schema_with_nonexisting_schema + assert_raises(ActiveRecord::StatementInvalid) do + @connection.drop_schema "test_schema3" + end + end + def test_schema_change_with_prepared_stmt altered = false @connection.exec_query "select * from developers where id = $1", 'sql', [[nil, 1]] @@ -183,13 +222,13 @@ class SchemaTest < ActiveRecord::TestCase end def test_raise_on_unquoted_schema_name - assert_raise(ActiveRecord::StatementInvalid) do + assert_raises(ActiveRecord::StatementInvalid) do with_schema_search_path '$user,public' end end def test_without_schema_search_path - assert_raise(ActiveRecord::StatementInvalid) { columns(TABLE_NAME) } + assert_raises(ActiveRecord::StatementInvalid) { columns(TABLE_NAME) } end def test_ignore_nil_schema_search_path 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/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 9cc09194dc..e9b930204f 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -44,17 +44,33 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase end def test_associate_existing - posts(:thinking); people(:david) # Warm cache + post = posts(:thinking) + person = people(:david) assert_queries(1) do - posts(:thinking).people << people(:david) + post.people << person end assert_queries(1) do - assert posts(:thinking).people.include?(people(:david)) + assert post.people.include?(person) end - assert posts(:thinking).reload.people(true).include?(people(:david)) + assert post.reload.people(true).include?(person) + end + + def test_associate_existing_with_strict_mass_assignment_sanitizer + SecureReader.mass_assignment_sanitizer = :strict + + SecureReader.new + + post = posts(:thinking) + person = people(:david) + + assert_queries(1) do + post.secure_people << person + end + ensure + SecureReader.mass_assignment_sanitizer = :logger end def test_associate_existing_record_twice_should_add_to_target_twice @@ -688,6 +704,17 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert author.comments.include?(comment) end + def test_through_association_readonly_should_be_false + assert !people(:michael).posts.first.readonly? + assert !people(:michael).posts.all.first.readonly? + end + + def test_can_update_through_association + assert_nothing_raised do + people(:michael).posts.first.update_attributes!(:title => "Can write") + end + end + def test_has_many_through_polymorphic_with_primary_key_option assert_equal [categories(:general)], authors(:david).essay_categories diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 23bdb09514..ef01476ae4 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -30,9 +30,12 @@ class AttributeMethodsTest < ActiveRecord::TestCase t = Topic.new t.title = "hello there!" t.written_on = Time.now + t.author_name = "" assert t.attribute_present?("title") assert t.attribute_present?("written_on") assert !t.attribute_present?("content") + assert !t.attribute_present?("author_name") + end def test_attribute_present_with_booleans diff --git a/activerecord/test/cases/connection_adapters/schema_cache_test.rb b/activerecord/test/cases/connection_adapters/schema_cache_test.rb index 42e39d534c..541e983758 100644 --- a/activerecord/test/cases/connection_adapters/schema_cache_test.rb +++ b/activerecord/test/cases/connection_adapters/schema_cache_test.rb @@ -39,6 +39,21 @@ module ActiveRecord assert_equal 0, @cache.tables.size assert_equal 0, @cache.primary_keys.size end + + def test_dump_and_load + @cache.columns['posts'] + @cache.columns_hash['posts'] + @cache.tables['posts'] + @cache.primary_keys['posts'] + + @cache = Marshal.load(Marshal.dump(@cache)) + + assert_equal 12, @cache.columns['posts'].size + assert_equal 12, @cache.columns_hash['posts'].size + assert @cache.tables['posts'] + assert_equal 'id', @cache.primary_keys['posts'] + end + end end end 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/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/activerecord/test/models/person.rb b/activerecord/test/models/person.rb index d2a0c6b40c..84bc901b5e 100644 --- a/activerecord/test/models/person.rb +++ b/activerecord/test/models/person.rb @@ -1,8 +1,10 @@ class Person < ActiveRecord::Base has_many :readers + has_many :secure_readers has_one :reader has_many :posts, :through => :readers + has_many :secure_posts, :through => :secure_readers has_many :posts_with_no_comments, :through => :readers, :source => :post, :include => :comments, :conditions => 'comments.id is null', :references => :comments diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 1cab78d8c7..0fc22ac6a3 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -115,8 +115,10 @@ class Post < ActiveRecord::Base has_many :named_categories, :through => :standard_categorizations has_many :readers + has_many :secure_readers has_many :readers_with_person, :include => :person, :class_name => "Reader" has_many :people, :through => :readers + has_many :secure_people, :through => :secure_readers has_many :single_people, :through => :readers has_many :people_with_callbacks, :source=>:person, :through => :readers, :before_add => lambda {|owner, reader| log(:added, :before, reader.first_name) }, diff --git a/activerecord/test/models/reader.rb b/activerecord/test/models/reader.rb index 0207a2bd92..59005ac604 100644 --- a/activerecord/test/models/reader.rb +++ b/activerecord/test/models/reader.rb @@ -3,3 +3,12 @@ class Reader < ActiveRecord::Base belongs_to :person, :inverse_of => :readers belongs_to :single_person, :class_name => 'Person', :foreign_key => :person_id, :inverse_of => :reader end + +class SecureReader < ActiveRecord::Base + self.table_name = "readers" + + belongs_to :secure_post, :class_name => "Post", :foreign_key => "post_id" + belongs_to :secure_person, :inverse_of => :secure_readers, :class_name => "Person", :foreign_key => "person_id" + + attr_accessible nil +end 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/core_ext/hash/slice.rb b/activesupport/lib/active_support/core_ext/hash/slice.rb index 0484d8e5d8..fbc6e538d5 100644 --- a/activesupport/lib/active_support/core_ext/hash/slice.rb +++ b/activesupport/lib/active_support/core_ext/hash/slice.rb @@ -13,17 +13,15 @@ class Hash # valid_keys = [:mass, :velocity, :time] # search(options.slice(*valid_keys)) def slice(*keys) - keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) - hash = self.class.new - keys.each { |k| hash[k] = self[k] if has_key?(k) } - hash + keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) + keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) } end # Replaces the hash with only the given keys. # Returns a hash contained the removed key/value pairs # {:a => 1, :b => 2, :c => 3, :d => 4}.slice!(:a, :b) # => {:c => 3, :d => 4} def slice!(*keys) - keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) + keys.map! { |key| convert_key(key) } if respond_to?(:convert_key) omit = slice(*self.keys - keys) hash = slice(*keys) replace(hash) @@ -33,8 +31,6 @@ class Hash # Removes and returns the key/value pairs matching the given keys. # {:a => 1, :b => 2, :c => 3, :d => 4}.extract!(:a, :b) # => {:a => 1, :b => 2} def extract!(*keys) - result = {} - keys.each {|key| result[key] = delete(key) } - result + keys.each_with_object({}) {|key, result| result[key] = delete(key) } end 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/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb index 674e4acfd6..e4a13870d7 100644 --- a/activesupport/lib/active_support/hash_with_indifferent_access.rb +++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb @@ -1,10 +1,9 @@ require 'active_support/core_ext/hash/keys' -# This class has dubious semantics and we only have it so that -# people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt> -# and they get the same value for both keys. - module ActiveSupport + # This class has dubious semantics and we only have it so that + # people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt> + # and they get the same value for both keys. class HashWithIndifferentAccess < Hash # Always returns true, so that <tt>Array#extract_options!</tt> finds members of this class. diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb index 82507c1e03..c167efc1a7 100644 --- a/activesupport/lib/active_support/lazy_load_hooks.rb +++ b/activesupport/lib/active_support/lazy_load_hooks.rb @@ -1,32 +1,32 @@ -# lazy_load_hooks allows rails to lazily load a lot of components and thus making the app boot faster. Because of -# this feature now there is no need to require <tt>ActiveRecord::Base</tt> at boot time purely to apply configuration. Instead -# a hook is registered that applies configuration once <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is used -# as example but this feature can be applied elsewhere too. -# -# Here is an example where +on_load+ method is called to register a hook. -# -# initializer "active_record.initialize_timezone" do -# ActiveSupport.on_load(:active_record) do -# self.time_zone_aware_attributes = true -# self.default_timezone = :utc -# end -# end -# -# When the entirety of +activerecord/lib/active_record/base.rb+ has been evaluated then +run_load_hooks+ is invoked. -# The very last line of +activerecord/lib/active_record/base.rb+ is: -# -# ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base) -# module ActiveSupport - @load_hooks = Hash.new {|h,k| h[k] = [] } - @loaded = {} + # lazy_load_hooks allows rails to lazily load a lot of components and thus making the app boot faster. Because of + # this feature now there is no need to require <tt>ActiveRecord::Base</tt> at boot time purely to apply configuration. Instead + # a hook is registered that applies configuration once <tt>ActiveRecord::Base</tt> is loaded. Here <tt>ActiveRecord::Base</tt> is used + # as example but this feature can be applied elsewhere too. + # + # Here is an example where +on_load+ method is called to register a hook. + # + # initializer "active_record.initialize_timezone" do + # ActiveSupport.on_load(:active_record) do + # self.time_zone_aware_attributes = true + # self.default_timezone = :utc + # end + # end + # + # When the entirety of +activerecord/lib/active_record/base.rb+ has been evaluated then +run_load_hooks+ is invoked. + # The very last line of +activerecord/lib/active_record/base.rb+ is: + # + # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base) + # + @load_hooks = Hash.new { |h,k| h[k] = [] } + @loaded = Hash.new { |h,k| h[k] = [] } def self.on_load(name, options = {}, &block) - if base = @loaded[name] + @loaded[name].each do |base| execute_hook(base, options, block) - else - @load_hooks[name] << [block, options] end + + @load_hooks[name] << [block, options] end def self.execute_hook(base, options, block) @@ -38,7 +38,7 @@ module ActiveSupport end def self.run_load_hooks(name, base = Object) - @loaded[name] = base + @loaded[name] << base @load_hooks[name].each do |hook, options| execute_hook(base, options, hook) end 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/lib/active_support/values/time_zone.rb b/activesupport/lib/active_support/values/time_zone.rb index 35f400f9df..bfbb838ea7 100644 --- a/activesupport/lib/active_support/values/time_zone.rb +++ b/activesupport/lib/active_support/values/time_zone.rb @@ -1,28 +1,28 @@ require 'active_support/core_ext/object/blank' require 'active_support/core_ext/object/try' -# The TimeZone class serves as a wrapper around TZInfo::Timezone instances. It allows us to do the following: -# -# * Limit the set of zones provided by TZInfo to a meaningful subset of 142 zones. -# * Retrieve and display zones with a friendlier name (e.g., "Eastern Time (US & Canada)" instead of "America/New_York"). -# * Lazily load TZInfo::Timezone instances only when they're needed. -# * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+, +parse+, +at+ and +now+ methods. -# -# If you set <tt>config.time_zone</tt> in the Rails Application, you can access this TimeZone object via <tt>Time.zone</tt>: -# -# # application.rb: -# class Application < Rails::Application -# config.time_zone = "Eastern Time (US & Canada)" -# end -# -# Time.zone # => #<TimeZone:0x514834...> -# Time.zone.name # => "Eastern Time (US & Canada)" -# Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00 -# -# The version of TZInfo bundled with Active Support only includes the definitions necessary to support the zones -# defined by the TimeZone class. If you need to use zones that aren't defined by TimeZone, you'll need to install the TZInfo gem -# (if a recent version of the gem is installed locally, this will be used instead of the bundled version.) module ActiveSupport + # The TimeZone class serves as a wrapper around TZInfo::Timezone instances. It allows us to do the following: + # + # * Limit the set of zones provided by TZInfo to a meaningful subset of 142 zones. + # * Retrieve and display zones with a friendlier name (e.g., "Eastern Time (US & Canada)" instead of "America/New_York"). + # * Lazily load TZInfo::Timezone instances only when they're needed. + # * Create ActiveSupport::TimeWithZone instances via TimeZone's +local+, +parse+, +at+ and +now+ methods. + # + # If you set <tt>config.time_zone</tt> in the Rails Application, you can access this TimeZone object via <tt>Time.zone</tt>: + # + # # application.rb: + # class Application < Rails::Application + # config.time_zone = "Eastern Time (US & Canada)" + # end + # + # Time.zone # => #<TimeZone:0x514834...> + # Time.zone.name # => "Eastern Time (US & Canada)" + # Time.zone.now # => Sun, 18 May 2008 14:30:44 EDT -04:00 + # + # The version of TZInfo bundled with Active Support only includes the definitions necessary to support the zones + # defined by the TimeZone class. If you need to use zones that aren't defined by TimeZone, you'll need to install the TZInfo gem + # (if a recent version of the gem is installed locally, this will be used instead of the bundled version.) class TimeZone # Keys are Rails TimeZone names, values are TZInfo identifiers MAPPING = { diff --git a/activesupport/lib/active_support/xml_mini/jdom.rb b/activesupport/lib/active_support/xml_mini/jdom.rb index dbb6c71907..4551dd2f2d 100644 --- a/activesupport/lib/active_support/xml_mini/jdom.rb +++ b/activesupport/lib/active_support/xml_mini/jdom.rb @@ -12,7 +12,6 @@ java_import org.xml.sax.InputSource unless defined? InputSource java_import org.xml.sax.Attributes unless defined? Attributes java_import org.w3c.dom.Node unless defined? Node -# = XmlMini JRuby JDOM implementation module ActiveSupport module XmlMini_JDOM #:nodoc: extend self diff --git a/activesupport/lib/active_support/xml_mini/libxml.rb b/activesupport/lib/active_support/xml_mini/libxml.rb index 16570c6aea..26556598fd 100644 --- a/activesupport/lib/active_support/xml_mini/libxml.rb +++ b/activesupport/lib/active_support/xml_mini/libxml.rb @@ -2,7 +2,6 @@ require 'libxml' require 'active_support/core_ext/object/blank' require 'stringio' -# = XmlMini LibXML implementation module ActiveSupport module XmlMini_LibXML #:nodoc: extend self diff --git a/activesupport/lib/active_support/xml_mini/libxmlsax.rb b/activesupport/lib/active_support/xml_mini/libxmlsax.rb index 2536b1f33e..acc018fd2d 100644 --- a/activesupport/lib/active_support/xml_mini/libxmlsax.rb +++ b/activesupport/lib/active_support/xml_mini/libxmlsax.rb @@ -2,9 +2,8 @@ require 'libxml' require 'active_support/core_ext/object/blank' require 'stringio' -# = XmlMini LibXML implementation using a SAX-based parser module ActiveSupport - module XmlMini_LibXMLSAX + module XmlMini_LibXMLSAX #:nodoc: extend self # Class that will build the hash while the XML document diff --git a/activesupport/lib/active_support/xml_mini/nokogiri.rb b/activesupport/lib/active_support/xml_mini/nokogiri.rb index 04ec9e8ab8..bb0a52bdcf 100644 --- a/activesupport/lib/active_support/xml_mini/nokogiri.rb +++ b/activesupport/lib/active_support/xml_mini/nokogiri.rb @@ -7,7 +7,6 @@ end require 'active_support/core_ext/object/blank' require 'stringio' -# = XmlMini Nokogiri implementation module ActiveSupport module XmlMini_Nokogiri #:nodoc: extend self diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb index 93fd3dfe57..30b94aac47 100644 --- a/activesupport/lib/active_support/xml_mini/nokogirisax.rb +++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb @@ -7,9 +7,8 @@ end require 'active_support/core_ext/object/blank' require 'stringio' -# = XmlMini Nokogiri implementation using a SAX-based parser module ActiveSupport - module XmlMini_NokogiriSAX + module XmlMini_NokogiriSAX #:nodoc: extend self # Class that will build the hash while the XML document diff --git a/activesupport/lib/active_support/xml_mini/rexml.rb b/activesupport/lib/active_support/xml_mini/rexml.rb index a13ad10118..a2a87337a6 100644 --- a/activesupport/lib/active_support/xml_mini/rexml.rb +++ b/activesupport/lib/active_support/xml_mini/rexml.rb @@ -2,7 +2,6 @@ require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/object/blank' require 'stringio' -# = XmlMini ReXML implementation module ActiveSupport module XmlMini_REXML #:nodoc: extend self 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 38cdda6c5c..4544edf0dd 100644 --- a/activesupport/test/core_ext/hash_ext_test.rb +++ b/activesupport/test/core_ext/hash_ext_test.rb @@ -486,6 +486,13 @@ class HashExtTest < ActiveSupport::TestCase assert_equal 'bender', slice['login'] end + def test_extract + original = {:a => 1, :b => 2, :c => 3, :d => 4} + expected = {:a => 1, :b => 2} + + assert_equal expected, original.extract!(:a, :b) + end + def test_except original = { :a => 'x', :b => 'y', :c => 10 } expected = { :a => 'x', :b => 'y' } diff --git a/activesupport/test/lazy_load_hooks_test.rb b/activesupport/test/lazy_load_hooks_test.rb index 58ccc14324..7851634dbf 100644 --- a/activesupport/test/lazy_load_hooks_test.rb +++ b/activesupport/test/lazy_load_hooks_test.rb @@ -8,6 +8,16 @@ class LazyLoadHooksTest < ActiveSupport::TestCase assert_equal 1, i end + def test_basic_hook_with_two_registrations + i = 0 + ActiveSupport.on_load(:basic_hook_with_two) { i += incr } + assert_equal 0, i + ActiveSupport.run_load_hooks(:basic_hook_with_two, FakeContext.new(2)) + assert_equal 2, i + ActiveSupport.run_load_hooks(:basic_hook_with_two, FakeContext.new(5)) + assert_equal 7, i + end + def test_hook_registered_after_run i = 0 ActiveSupport.run_load_hooks(:registered_after) @@ -16,6 +26,25 @@ class LazyLoadHooksTest < ActiveSupport::TestCase assert_equal 1, i end + def test_hook_registered_after_run_with_two_registrations + i = 0 + ActiveSupport.run_load_hooks(:registered_after_with_two, FakeContext.new(2)) + ActiveSupport.run_load_hooks(:registered_after_with_two, FakeContext.new(5)) + assert_equal 0, i + ActiveSupport.on_load(:registered_after_with_two) { i += incr } + assert_equal 7, i + end + + def test_hook_registered_interleaved_run_with_two_registrations + i = 0 + ActiveSupport.run_load_hooks(:registered_interleaved_with_two, FakeContext.new(2)) + assert_equal 0, i + ActiveSupport.on_load(:registered_interleaved_with_two) { i += incr } + assert_equal 2, i + ActiveSupport.run_load_hooks(:registered_interleaved_with_two, FakeContext.new(5)) + assert_equal 7, i + end + def test_hook_receives_a_context i = 0 ActiveSupport.on_load(:contextual) { i += incr } 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/code/getting_started/config/database.yml b/railties/guides/code/getting_started/config/database.yml index 32a998ad72..51a4dd459d 100644 --- a/railties/guides/code/getting_started/config/database.yml +++ b/railties/guides/code/getting_started/config/database.yml @@ -6,9 +6,7 @@ development: adapter: sqlite3 database: db/development.sqlite3 - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 timeout: 5000 # Warning: The database defined as "test" will be erased and @@ -17,15 +15,11 @@ development: test: adapter: sqlite3 database: db/test.sqlite3 - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 timeout: 5000 production: adapter: sqlite3 database: db/production.sqlite3 - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 timeout: 5000 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 21bbc64255..8e23a577e2 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -94,7 +94,7 @@ client = Client.find(10) The SQL equivalent of the above is: <sql> -SELECT * FROM clients WHERE (clients.id = 10) +SELECT * FROM clients WHERE (clients.id = 10) LIMIT 1 </sql> <tt>Model.find(primary_key)</tt> will raise an +ActiveRecord::RecordNotFound+ exception if no matching record is found. @@ -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/active_support_instrumentation.textile b/railties/guides/source/active_support_instrumentation.textile new file mode 100644 index 0000000000..f9452400ad --- /dev/null +++ b/railties/guides/source/active_support_instrumentation.textile @@ -0,0 +1,39 @@ +h2. Active Support Instrumentation + +Active Support is a part of core Rails that provides Ruby language extensions, utilities and other things. One of the things it includes is an instrumentation API that can be used inside an application to measure certain actions that occur within Ruby code, such as that inside a Rails application or the framework itself. It is not limited to Rails, however. It can be used independently in other Ruby scripts if it is so desired. + +In this guide, you will learn how to use the instrumentation API inside of ActiveSupport to measure events inside of Rails and other Ruby code. We cover: + +* What instrumentation can provide +* The hooks inside the Rails framework for instrumentation +* Adding a subscriber to a hook +* Building a custom instrumentation implementation + +endprologue. + +h3. Introduction to instrumentation + +The instrumentation API provided by ActiveSupport allows developers to provide hooks which other developers may hook into. There are several of these within the Rails framework, as described below in <TODO: link to section detailing each hook point>. With this API, developers can choose to be notified when certain events occur inside their application or another piece of Ruby code. + +For example, there is a hook provided within Active Record that is called every time Active Record uses a SQL query on a database. This hook could be *subscribed* to, and used to track the number of queries during a certain action. There's another hook around the processing of an action of a controller. This could be used, for instance, to track how long a specific action has taken. + +You are even able to create your own events inside your application which you can later subscribe to. + +h3. Rails framework hooks + +h4. Action Mailer + +h4. Action Controller + +h4. Action View + +h4. Active Record + +h4. Active Resource + +h4. Active Support + +h3. Subscribing to an event + +h3. Creating custom events + diff --git a/railties/guides/source/ajax_on_rails.textile b/railties/guides/source/ajax_on_rails.textile index 5913a472fd..cda9c64460 100644 --- a/railties/guides/source/ajax_on_rails.textile +++ b/railties/guides/source/ajax_on_rails.textile @@ -134,7 +134,7 @@ If the server returns 200, the output of the above example is equivalent to our ** *position* By default (i.e. when not specifying this option, like in the examples before) the response is injected into the element with the specified DOM id, replacing the original content of the element (if there was any). You might want to alter this behavior by keeping the original content - the only question is where to place the new content? This can specified by the +position+ parameter, with four possibilities: *** +:before+ Inserts the response text just before the target element. More precisely, it creates a text node from the response and inserts it as the left sibling of the target element. *** +:after+ Similar behavior to +:before+, but in this case the response is inserted after the target element. -*** +:top+ Inserts the text into the target element, before it's original content. If the target element was empty, this is equivalent with not specifying +:position+ at all. +*** +:top+ Inserts the text into the target element, before its original content. If the target element was empty, this is equivalent with not specifying +:position+ at all. *** +:bottom+ The counterpart of +:top+: the response is inserted after the target element's original content. A typical example of using +:bottom+ is inserting a new <li> element into an existing list: @@ -174,7 +174,7 @@ link_to_remote "Update record", This generates a remote link which adds 2 parameters to the standard URL generated by Rails, taken from the page (contained in the elements matched by the 'status' and 'completed' DOM id). -** *Callbacks* Since an AJAX call is typically asynchronous, as it's name suggests (this is not a rule, and you can fire a synchronous request - see the last option, +:type+) your only way of communicating with a request once it is fired is via specifying callbacks. There are six options at your disposal (in fact 508, counting all possible response types, but these six are the most frequent and therefore specified by a constant): +** *Callbacks* Since an AJAX call is typically asynchronous, as its name suggests (this is not a rule, and you can fire a synchronous request - see the last option, +:type+) your only way of communicating with a request once it is fired is via specifying callbacks. There are six options at your disposal (in fact 508, counting all possible response types, but these six are the most frequent and therefore specified by a constant): *** +:loading:+ => +code+ The request is in the process of receiving the data, but the transfer is not completed yet. *** +:loaded:+ => +code+ The transfer is completed, but the data is not processed and returned yet *** +:interactive:+ => +code+ One step after +:loaded+: The data is fully received and being processed @@ -203,7 +203,7 @@ link_to_remote "Add new item", ** *:type* If you want to fire a synchronous request for some obscure reason (blocking the browser while the request is processed and doesn't return a status code), you can use the +:type+ option with the value of +:synchronous+. * Finally, using the +html_options+ parameter you can add HTML attributes to the generated tag. It works like the same parameter of the +link_to+ helper. There are interesting side effects for the +href+ and +onclick+ parameters though: ** If you specify the +href+ parameter, the AJAX link will degrade gracefully, i.e. the link will point to the URL even if JavaScript is disabled in the client browser -** +link_to_remote+ gains it's AJAX behavior by specifying the remote call in the onclick handler of the link. If you supply +html_options[:onclick]+ you override the default behavior, so use this with care! +** +link_to_remote+ gains its AJAX behavior by specifying the remote call in the onclick handler of the link. If you supply +html_options[:onclick]+ you override the default behavior, so use this with care! We are finished with +link_to_remote+. I know this is quite a lot to digest for one helper function, but remember, these options are common for all the rest of the Rails view helpers, so we will take a look at the differences / additional parameters in the next sections. @@ -211,8 +211,8 @@ h4. AJAX Forms There are three different ways of adding AJAX forms to your view using Rails Prototype helpers. They are slightly different, but striving for the same goal: instead of submitting the form using the standard HTTP request/response cycle, it is submitted asynchronously, thus not reloading the page. These methods are the following: -* +remote_form_for+ (and it's alias +form_remote_for+) is tied to Rails most tightly of the three since it takes a resource, model or array of resources (in case of a nested resource) as a parameter. -* +form_remote_tag+ AJAXifies the form by serializing and sending it's data in the background +* +remote_form_for+ (and its alias +form_remote_for+) is tied to Rails most tightly of the three since it takes a resource, model or array of resources (in case of a nested resource) as a parameter. +* +form_remote_tag+ AJAXifies the form by serializing and sending its data in the background * +submit_to_remote+ and +button_to_remote+ is more rarely used than the previous two. Rather than creating an AJAX form, you add a button/input Let's see them in action one by one! diff --git a/railties/guides/source/api_app.textile b/railties/guides/source/api_app.textile index d51fcb2d58..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 @@ -270,7 +269,6 @@ Some common modules you might want to add: * +ActionController::Rendering+: Support for templating and +ActionView+. * +AbstractController::Layouts+: Support for layouts when rendering. * +ActionController::Renderers::XML+: Support for +render :xml+. -* +ActionController::SessionManagement+: Support for +session+. This requires a session middleware. * +ActionController::Cookies+: Support for +cookies+, which includes support for signed and encrypted cookies. This requires the cookie middleware. * +ActionController::Rescue+: Support for +rescue_from+. diff --git a/railties/guides/source/asset_pipeline.textile b/railties/guides/source/asset_pipeline.textile index a061c1fc16..b1b1d21c2d 100644 --- a/railties/guides/source/asset_pipeline.textile +++ b/railties/guides/source/asset_pipeline.textile @@ -128,7 +128,7 @@ For example, these files: <plain> app/assets/javascripts/home.js lib/assets/javascripts/moovinator.js -vendor/assets/javascript/slider.js +vendor/assets/javascripts/slider.js </plain> would be referenced in a manifest like this: diff --git a/railties/guides/source/command_line.textile b/railties/guides/source/command_line.textile index 8ae8c61ae6..fe4a84dae9 100644 --- a/railties/guides/source/command_line.textile +++ b/railties/guides/source/command_line.textile @@ -521,9 +521,7 @@ development: adapter: postgresql encoding: unicode database: gitapp_development - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 username: gitapp password: ... diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index e796f44606..619c0ae16c 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+. @@ -493,7 +501,7 @@ Rails has 5 initialization events which can be hooked into (listed in the order * +after_initialize+: Run directly after the initialization of the application, but before the application initializers are run. -To define an event for these hooks, use the block syntax within a +Rails::Aplication+, +Rails::Railtie+ or +Rails::Engine+ subclass: +To define an event for these hooks, use the block syntax within a +Rails::Application+, +Rails::Railtie+ or +Rails::Engine+ subclass: <ruby> module YourApp @@ -652,19 +660,17 @@ The error occurred while evaluating nil.each h3. Database pooling -Active Record database connections are managed by +ActiveRecord::ConnectionAdapters::ConnectionPool+ which ensures that a connection pool synchronizes the amount of thread access to a limited number of database connections. This limit defaults to 1 and can be configured in +database.yml+. +Active Record database connections are managed by +ActiveRecord::ConnectionAdapters::ConnectionPool+ which ensures that a connection pool synchronizes the amount of thread access to a limited number of database connections. This limit defaults to 5 and can be configured in +database.yml+. <ruby> development: adapter: sqlite3 database: db/development.sqlite3 - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 timeout: 5000 </ruby> -Since the connection pooling is handled inside of Active Record by default, all application servers (Thin, Mongrel, Unicorn etc.) should behave the same. Initially, the database connection pool is empty and it will create additional connections as the demand for them increases, until it reaches the connection pool limit. +Since the connection pooling is handled inside of ActiveRecord by default, all application servers (Thin, mongrel, Unicorn etc.) should behave the same. Initially, the database connection pool is empty and it will create additional connections as the demand for them increases, until it reaches the connection pool limit. Any one request will check out a connection the first time it requires access to the database, after which it will check the connection back in, at the end of the request, meaning that the additional connection slot will be available again for the next request in the queue. diff --git a/railties/guides/source/form_helpers.textile b/railties/guides/source/form_helpers.textile index a696e4f8ae..6d9cd5440b 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/getting_started.textile b/railties/guides/source/getting_started.textile index d6f3c3e217..bed14ef6a8 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -329,9 +329,7 @@ environment: development: adapter: sqlite3 database: db/development.sqlite3 - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 timeout: 5000 </yaml> @@ -352,9 +350,7 @@ development: adapter: mysql2 encoding: utf8 database: blog_development - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 username: root password: socket: /tmp/mysql.sock @@ -374,9 +370,7 @@ development: adapter: postgresql encoding: unicode database: blog_development - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 username: blog password: </yaml> diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 25201888e7..320f1e9d20 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -521,7 +521,7 @@ h5. Bulk and Namespace Lookup To look up multiple translations at once, an array of keys can be passed: <ruby> -I18n.t [:odd, :even], :scope => 'activerecord.errors.messages' +I18n.t [:odd, :even], :scope => 'errors.messages' # => ["must be odd", "must be even"] </ruby> 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/guides/source/routing.textile b/railties/guides/source/routing.textile index c5567b3350..e93b1280e0 100644 --- a/railties/guides/source/routing.textile +++ b/railties/guides/source/routing.textile @@ -234,14 +234,14 @@ end In addition to the routes for magazines, this declaration will also route ads to an +AdsController+. The ad URLs require a magazine: -|_.HTTP Verb |_.Path |_.action |_.used for | -|GET |/magazines/:id/ads |index |display a list of all ads for a specific magazine | -|GET |/magazines/:id/ads/new |new |return an HTML form for creating a new ad belonging to a specific magazine | -|POST |/magazines/:id/ads |create |create a new ad belonging to a specific magazine | -|GET |/magazines/:id/ads/:id |show |display a specific ad belonging to a specific magazine | -|GET |/magazines/:id/ads/:id/edit |edit |return an HTML form for editing an ad belonging to a specific magazine | -|PATCH/PUT |/magazines/:id/ads/:id |update |update a specific ad belonging to a specific magazine | -|DELETE |/magazines/:id/ads/:id |destroy |delete a specific ad belonging to a specific magazine | +|_.HTTP Verb |_.Path |_.action |_.used for | +|GET |/magazines/:magazine_id/ads |index |display a list of all ads for a specific magazine | +|GET |/magazines/:magazine_id/ads/new |new |return an HTML form for creating a new ad belonging to a specific magazine | +|POST |/magazines/:magazine_id/ads |create |create a new ad belonging to a specific magazine | +|GET |/magazines/:magazine_id/ads/:id |show |display a specific ad belonging to a specific magazine | +|GET |/magazines/:magazine_id/ads/:id/edit |edit |return an HTML form for editing an ad belonging to a specific magazine | +|PATCH/PUT |/magazines/:magazine_id/ads/:id |update |update a specific ad belonging to a specific magazine | +|DELETE |/magazines/:magazine_id/ads/:id |destroy |delete a specific ad belonging to a specific magazine | This will also create routing helpers such as +magazine_ads_url+ and +edit_magazine_ad_path+. These helpers take an instance of Magazine as the first parameter (+magazine_ads_url(@magazine)+). @@ -389,7 +389,7 @@ match ':controller/:action/:id/:user_id' An incoming path of +/photos/show/1/2+ will be dispatched to the +show+ action of the +PhotosController+. +params[:id]+ will be +"1"+, and +params[:user_id]+ will be +"2"+. -NOTE: You can't use +namespace+ or +:module+ with a +:controller+ path segment. If you need to do this then use a constraint on :controller that matches the namespace you require. e.g: +NOTE: You can't use +:namespace+ or +:module+ with a +:controller+ path segment. If you need to do this then use a constraint on :controller that matches the namespace you require. e.g: <ruby> match ':controller(/:action(/:id))', :controller => /admin\/[^\/]+/ diff --git a/railties/guides/source/upgrading_ruby_on_rails.textile b/railties/guides/source/upgrading_ruby_on_rails.textile index 6e84b7fe40..731d56c850 100644 --- a/railties/guides/source/upgrading_ruby_on_rails.textile +++ b/railties/guides/source/upgrading_ruby_on_rails.textile @@ -38,14 +38,14 @@ h3. Upgrading from Rails 3.1 to Rails 3.2 If your application is currently on any version of Rails older than 3.1.x, you should upgrade to Rails 3.1 before attempting an update to Rails 3.2. -The following changes are meant for upgrading your application to Rails 3.2.1, the latest 3.2.x version of Rails. +The following changes are meant for upgrading your application to Rails 3.2.2, the latest 3.2.x version of Rails. h4(#gemfile3_2). Gemfile Make the following changes to your +Gemfile+. <ruby> -gem 'rails', '= 3.2.1' +gem 'rails', '= 3.2.2' group :assets do gem 'sass-rails', '~> 3.2.3' 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/configuration.rb b/railties/lib/rails/application/configuration.rb index 1e424d9b4a..0f5fc2b7bc 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -11,7 +11,7 @@ module Rails :force_ssl, :helpers_paths, :logger, :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 + :time_zone, :reload_classes_only_on_change, :use_schema_cache_dump attr_writer :log_level attr_reader :encoding @@ -41,6 +41,7 @@ module Rails @file_watcher = ActiveSupport::FileUpdateChecker @exceptions_app = nil @autoflush_log = true + @use_schema_cache_dump = true @assets = ActiveSupport::OrderedOptions.new @assets.enabled = false 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/engine.rb b/railties/lib/rails/engine.rb index af2bde5a6e..9629ac55c2 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -245,7 +245,7 @@ module Rails # # Additionally, an isolated engine will set its name according to namespace, so # MyEngine::Engine.engine_name will be "my_engine". It will also set MyEngine.table_name_prefix - # to "my_engine_", changing the MyEngine::Article model to use the my_engine_article table. + # to "my_engine_", changing the MyEngine::Article model to use the my_engine_articles table. # # == Using Engine's routes outside Engine # 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/databases/mysql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml index 950016ad92..c3349912aa 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/mysql.yml @@ -12,9 +12,7 @@ development: adapter: mysql2 encoding: utf8 database: <%= app_name %>_development - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 username: root password: <% if mysql_socket -%> @@ -30,9 +28,7 @@ test: adapter: mysql2 encoding: utf8 database: <%= app_name %>_test - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 username: root password: <% if mysql_socket -%> @@ -45,9 +41,7 @@ production: adapter: mysql2 encoding: utf8 database: <%= app_name %>_production - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 username: root password: <% if mysql_socket -%> diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml index a8ed15c2dc..f08f86aac3 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/postgresql.yml @@ -16,9 +16,7 @@ development: adapter: postgresql encoding: unicode database: <%= app_name %>_development - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 username: <%= app_name %> password: @@ -44,9 +42,7 @@ test: adapter: postgresql encoding: unicode database: <%= app_name %>_test - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 username: <%= app_name %> password: @@ -54,8 +50,6 @@ production: adapter: postgresql encoding: unicode database: <%= app_name %>_production - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 username: <%= app_name %> password: diff --git a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml index 32a998ad72..51a4dd459d 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml +++ b/railties/lib/rails/generators/rails/app/templates/config/databases/sqlite3.yml @@ -6,9 +6,7 @@ development: adapter: sqlite3 database: db/development.sqlite3 - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 timeout: 5000 # Warning: The database defined as "test" will be erased and @@ -17,15 +15,11 @@ development: test: adapter: sqlite3 database: db/test.sqlite3 - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 timeout: 5000 production: adapter: sqlite3 database: db/production.sqlite3 - # Maximum number of database connections available per process. Please - # increase this number in multithreaded applications. - pool: 1 + pool: 5 timeout: 5000 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 8812685620..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 @@ -193,5 +194,26 @@ module ApplicationTests require "#{app_path}/config/environment" assert_nil defined?(ActiveRecord::Base) end + + test "use schema cache dump" do + Dir.chdir(app_path) do + `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. + assert ActiveRecord::Base.connection.schema_cache.tables["posts"] + end + + test "expire schema cache dump" do + Dir.chdir(app_path) do + `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 ff12b3e9fc..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,10 +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 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 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/session_migration_generator_test.rb b/railties/test/generators/session_migration_generator_test.rb index 89506222df..b590047ff0 100644 --- a/railties/test/generators/session_migration_generator_test.rb +++ b/railties/test/generators/session_migration_generator_test.rb @@ -15,7 +15,7 @@ class SessionMigrationGeneratorTest < Rails::Generators::TestCase end def test_session_migration_with_custom_table_name - ActiveRecord::SessionStore::Session.stubs(:table_name => "custom_table_name") + ActiveRecord::SessionStore::Session.table_name = "custom_table_name" run_generator assert_migration "db/migrate/add_sessions_table.rb" do |migration| assert_match(/class AddSessionsTable < ActiveRecord::Migration/, migration) |