diff options
Diffstat (limited to 'actionpack')
70 files changed, 1592 insertions, 620 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 2b6425caa5..6940683c8c 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,9 +1,104 @@ ## Rails 4.0.0 (unreleased) ## -* `BestStandardsSupport` no longer duplicates `X-UA-Compatible` values on - each request to prevent header size from blowing up. +* Change `image_alt` method to replace underscores/hyphens to spaces in filenames. - *Edward Anderson* + Previously, underscored filenames became `alt="A_long_file_name_with_underscores"` + in HTML, which is poor for accessibility. For instance, Apple's VoiceOver Utility + pronounces each underscore. `A_long_file_name` thus would be read as `A underscore + long underscore file underscore name.` Now underscored or hyphenated filenames + (both of which are very popular naming conventions) read more naturally in + screen readers by converting both hyphens and underscores to spaces. + + Before: + image_tag('underscored_file_name.png') + # => <img alt="Underscored_file_name" src="/assets/underscored_file_name.png" /> + + After: + image_tag('underscored_file_name.png') + # => <img alt="Underscored file name" src="/assets/underscored_file_name.png" /> + + *Nick Cox* + +* We don't support the `:controller` option for route definitions + with the ruby constant notation. This will now result in an + `ArgumentError`. + + Example: + # This raises an ArgumentError: + resources :posts, :controller => "Admin::Posts" + + # Use directory notation instead: + resources :posts, :controller => "admin/posts" + + *Yves Senn* + +* `assert_template` can be used to verify the locals of partials, + which live inside a directory. + Fixes #8516. + + # Prefixed partials inside directories worked and still work. + assert_template partial: 'directory/_partial', locals: {name: 'John'} + + # This did not work but does now. + assert_template partial: 'directory/partial', locals: {name: 'John'} + + *Yves Senn* + +* Fix `content_tag_for` with array html option. + It would embed array as string instead of joining it like `content_tag` does: + + content_tag(:td, class: ["foo", "bar"]){} + #=> '<td class="foo bar"></td>' + + Before: + + content_tag_for(:td, item, class: ["foo", "bar"]) + #=> '<td class="item ["foo", "bar"]" id="item_1"></td>' + + After: + + content_tag_for(:td, item, class: ["foo", "bar"]) + #=> '<td class="item foo bar" id="item_1"></td>' + + *Semyon Perepelitsa* + +* Remove `BestStandardsSupport` middleware, !DOCTYPE html already triggers + standards mode per http://msdn.microsoft.com/en-us/library/jj676915(v=vs.85).aspx + and ChromeFrame header has been moved to `config.action_dispatch.default_headers` + + *Guillermo Iguaran* + +* Fix CSRF protection and `current_url?` helper to work with HEAD requests + now that `ActionDispatch::Head` has been removed in favor of `Rack::Head`. + + *Michiel Sikkes* + +* Change `asset_path` to not include `SCRIPT_NAME` when it's used + from a mounted engine. Fixes #8119. + + *Piotr Sarnacki* + +* Add javascript based routing path matcher to `/rails/info/routes`. + Routes can now be filtered by whether or not they match a path. + + *Richard Schneeman* + +* Given + + params.permit(:name) + + `:name` passes if it is a key of `params` whose value is a permitted scalar. + + Similarly, given + + params.permit(tags: []) + + `:tags` passes if it is a key of `params` whose value is an array of + permitted scalars. + + Permitted scalars filtering happens at any level of nesting. + + *Xavier Noria* * Change the behavior of route defaults so that explicit defaults are no longer required where the key is not part of the path. For example: @@ -197,7 +292,8 @@ * More descriptive error messages when calling `render :partial` with an invalid `:layout` argument. - #8376 + + Fixes #8376. render partial: 'partial', layout: true @@ -729,7 +825,7 @@ * Removed old text helper apis from `highlight`, `excerpt` and `word_wrap`. *Jeremy Walker* * Templates without a handler extension now raises a deprecation warning but still - defaults to ERb. In future releases, it will simply return the template contents. *Steve Klabnik* + defaults to ERB. In future releases, it will simply return the template contents. *Steve Klabnik* * Deprecate `:disable_with` in favor of `data: { disable_with: "Text" }` option from `submit_tag`, `button_tag` and `button_to` helpers. diff --git a/actionpack/RUNNING_UNIT_TESTS b/actionpack/RUNNING_UNIT_TESTS.rdoc index 1b29abd2d1..1b29abd2d1 100644 --- a/actionpack/RUNNING_UNIT_TESTS +++ b/actionpack/RUNNING_UNIT_TESTS.rdoc diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index f00fc46b2e..008f17bceb 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |s| s.add_dependency 'activesupport', version s.add_dependency 'builder', '~> 3.1.0' - s.add_dependency 'rack', '~> 1.4.3' + s.add_dependency 'rack', '~> 1.5.2' s.add_dependency 'rack-test', '~> 0.6.1' s.add_dependency 'erubis', '~> 2.7.0' diff --git a/actionpack/lib/abstract_controller/translation.rb b/actionpack/lib/abstract_controller/translation.rb index b6c484d188..db48022b9f 100644 --- a/actionpack/lib/abstract_controller/translation.rb +++ b/actionpack/lib/abstract_controller/translation.rb @@ -1,5 +1,13 @@ module AbstractController module Translation + # Delegates to <tt>I18n.translate</tt>. Also aliased as <tt>t</tt>. + # + # When the given key starts with a period, it will be scoped by the current + # controller and action. So if you call <tt>translate(".foo")</tt> from + # <tt>PeopleController#index</tt>, it will convert the call to + # <tt>I18n.translate("people.index.foo")</tt>. This makes it less repetitive + # to translate many keys within the same controller / action and gives you a + # simple framework for scoping them consistently. def translate(*args) key = args.first if key.is_a?(String) && (key[0] == '.') @@ -11,6 +19,7 @@ module AbstractController end alias :t :translate + # Delegates to <tt>I18n.localize</tt>. Also aliased as <tt>l</tt>. def localize(*args) I18n.localize(*args) end diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index cf2cda039d..ea33d975ef 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -82,10 +82,6 @@ module ActionController end end - def caching_allowed? - request.get? && response.status == 200 - end - def view_cache_dependencies self.class._view_cache_dependencies.map { |dep| instance_exec(&dep) }.compact end diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 832dec7b2a..143b3e0cbd 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/array/extract_options' require 'action_dispatch/middleware/stack' module ActionController diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb index eddee08545..6e0cd51d8b 100644 --- a/actionpack/lib/action_controller/metal/conditional_get.rb +++ b/actionpack/lib/action_controller/metal/conditional_get.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/hash/keys' + module ActionController module ConditionalGet extend ActiveSupport::Concern diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb index d04fbae150..93568da9ef 100644 --- a/actionpack/lib/action_controller/metal/mime_responds.rb +++ b/actionpack/lib/action_controller/metal/mime_responds.rb @@ -1,3 +1,4 @@ +require 'active_support/core_ext/array/extract_options' require 'abstract_controller/collector' module ActionController #:nodoc: diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb index 59b91a240e..e9031f3fac 100644 --- a/actionpack/lib/action_controller/metal/redirecting.rb +++ b/actionpack/lib/action_controller/metal/redirecting.rb @@ -88,7 +88,7 @@ module ActionController # letters, digits, and the plus ("+"), period ("."), or hyphen ("-") # characters; and is terminated by a colon (":"). # The protocol relative scheme starts with a double slash "//" - when %r{^(\w[\w+.-]*:|//).*} + when %r{\A(\w[\w+.-]*:|//).*} options when String request.protocol + request.host_with_port + options diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index c5db0cb0d4..17379cf7ac 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -102,15 +102,16 @@ module ActionController #:nodoc: # This is the method that defines the application behavior when a request is found to be unverified. def handle_unverified_request - request.session = NullSessionHash.new + request.session = NullSessionHash.new(request.env) request.env['action_dispatch.request.flash_hash'] = nil request.env['rack.session.options'] = { skip: true } request.env['action_dispatch.cookies'] = NullCookieJar.build(request) end class NullSessionHash < Rack::Session::Abstract::SessionHash #:nodoc: - def initialize - super(nil, nil) + def initialize(env) + super(nil, env) + @data = {} @loaded = true end @@ -125,7 +126,7 @@ module ActionController #:nodoc: host = request.host secure = request.ssl? - new(key_generator, host, secure) + new(key_generator, host, secure, options_for_env({})) end def write(*) @@ -162,11 +163,11 @@ module ActionController #:nodoc: # Returns true or false if a request is verified. Checks: # - # * is it a GET request? Gets should be safe and idempotent + # * is it a GET or HEAD request? Gets should be safe and idempotent # * Does the form_authenticity_token match the given token value from the params? # * Does the X-CSRF-Token header match the form_authenticity_token def verified_request? - !protect_against_forgery? || request.get? || + !protect_against_forgery? || request.get? || request.head? || form_authenticity_token == params[request_forgery_protection_token] || form_authenticity_token == request.headers['X-CSRF-Token'] end diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index 2c96e03f55..7e720ca6f5 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -1,6 +1,7 @@ require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/array/wrap' require 'active_support/rescuable' +require 'action_dispatch/http/upload' module ActionController # Raised when a required parameter is missing. @@ -184,6 +185,21 @@ module ActionController # permitted.has_key?(:age) # => true # permitted.has_key?(:role) # => false # + # Only permitted scalars pass the filter. For example, given + # + # params.permit(:name) + # + # +:name+ passes it is a key of +params+ whose associated value is of type + # +String+, +Symbol+, +NilClass+, +Numeric+, +TrueClass+, +FalseClass+, + # +Date+, +Time+, +DateTime+, +StringIO+, +IO+, or + # +ActionDispatch::Http::UploadedFile+. Otherwise, the key +:name+ is + # filtered out. + # + # You may declare that the parameter should be an array of permitted scalars + # by mapping it to an empty array: + # + # params.permit(tags: []) + # # You can also use +permit+ on nested parameters, like: # # params = ActionController::Parameters.new({ @@ -230,33 +246,14 @@ module ActionController filters.flatten.each do |filter| case filter - when Symbol, String then - if has_key?(filter) - _value = self[filter] - params[filter] = _value unless Hash === _value - end - keys.grep(/\A#{Regexp.escape(filter)}\(\d+[if]?\)\z/) { |key| params[key] = self[key] } + when Symbol, String + permitted_scalar_filter(params, filter) when Hash then - filter = filter.with_indifferent_access - - self.slice(*filter.keys).each do |key, values| - return unless values - - key = key.to_sym - - params[key] = each_element(values) do |value| - # filters are a Hash, so we expect value to be a Hash too - next if filter.is_a?(Hash) && !value.is_a?(Hash) - - value = self.class.new(value) if !value.respond_to?(:permit) - - value.permit(*Array.wrap(filter[key])) - end - end + hash_filter(params, filter) end end - unpermitted_parameters!(params) + unpermitted_parameters!(params) if self.class.action_on_unpermitted_parameters params.permit! end @@ -352,6 +349,83 @@ module ActionController def unpermitted_keys(params) self.keys - params.keys - NEVER_UNPERMITTED_PARAMS end + + # + # --- Filtering ---------------------------------------------------------- + # + + # This is a white list of permitted scalar types that includes the ones + # supported in XML and JSON requests. + # + # This list is in particular used to filter ordinary requests, String goes + # as first element to quickly short-circuit the common case. + # + # If you modify this collection please update the API of +permit+ above. + PERMITTED_SCALAR_TYPES = [ + String, + Symbol, + NilClass, + Numeric, + TrueClass, + FalseClass, + Date, + Time, + # DateTimes are Dates, we document the type but avoid the redundant check. + StringIO, + IO, + ActionDispatch::Http::UploadedFile, + ] + + def permitted_scalar?(value) + PERMITTED_SCALAR_TYPES.any? {|type| value.is_a?(type)} + end + + def permitted_scalar_filter(params, key) + if has_key?(key) && permitted_scalar?(self[key]) + params[key] = self[key] + end + + keys.grep(/\A#{Regexp.escape(key)}\(\d+[if]?\)\z/) do |k| + if permitted_scalar?(self[k]) + params[k] = self[k] + end + end + end + + def array_of_permitted_scalars?(value) + if value.is_a?(Array) + value.all? {|element| permitted_scalar?(element)} + end + end + + def array_of_permitted_scalars_filter(params, key) + if has_key?(key) && array_of_permitted_scalars?(self[key]) + params[key] = self[key] + end + end + + EMPTY_ARRAY = [] + def hash_filter(params, filter) + filter = filter.with_indifferent_access + + # Slicing filters out non-declared keys. + slice(*filter.keys).each do |key, value| + return unless value + + if filter[key] == EMPTY_ARRAY + # Declaration { comment_ids: [] }. + array_of_permitted_scalars_filter(params, key) + else + # Declaration { user: :name } or { user: [:name, :age, { adress: ... }] }. + params[key] = each_element(value) do |element| + if element.is_a?(Hash) + element = self.class.new(element) unless element.respond_to?(:permit) + element.permit(*Array.wrap(filter[key])) + end + end + end + end + end end # == Strong \Parameters diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index d8206b573d..bba1f1e201 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -1,6 +1,7 @@ require 'rack/session/abstract/id' require 'active_support/core_ext/object/to_query' require 'active_support/core_ext/module/anonymous' +require 'active_support/core_ext/hash/keys' module ActionController module TemplateAssertions @@ -125,7 +126,11 @@ module ActionController if expected_partial = options[:partial] if expected_locals = options[:locals] if defined?(@_rendered_views) - view = expected_partial.to_s.sub(/^_/,'') + view = expected_partial.to_s.sub(/^_/, '').sub(/\/_(?=[^\/]+\z)/, '/') + + partial_was_not_rendered_msg = "expected %s to be rendered but it was not." % view + assert_includes @_rendered_views.rendered_views, view, partial_was_not_rendered_msg + msg = 'expecting %s to be rendered with %s but was with %s' % [expected_partial, expected_locals, @_rendered_views.locals_for(view)] @@ -235,18 +240,39 @@ module ActionController end end + # Methods #destroy and #load! are overridden to avoid calling methods on the + # @store object, which does not exist for the TestSession class. class TestSession < Rack::Session::Abstract::SessionHash #:nodoc: DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS def initialize(session = {}) super(nil, nil) - replace(session.stringify_keys) + @id = SecureRandom.hex(16) + @data = stringify_keys(session) @loaded = true end def exists? true end + + def keys + @data.keys + end + + def values + @data.values + end + + def destroy + clear + end + + private + + def load! + @id + end end # Superclass for ActionController functional tests. Functional tests allow you to diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 9ab048b756..618e2f3033 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -47,7 +47,6 @@ module ActionDispatch autoload_under 'middleware' do autoload :RequestId - autoload :BestStandardsSupport autoload :Callbacks autoload :Cookies autoload :DebugExceptions diff --git a/actionpack/lib/action_dispatch/http/parameters.rb b/actionpack/lib/action_dispatch/http/parameters.rb index 25edd196c3..446862aad0 100644 --- a/actionpack/lib/action_dispatch/http/parameters.rb +++ b/actionpack/lib/action_dispatch/http/parameters.rb @@ -55,7 +55,7 @@ module ActionDispatch # should really prevent that from happening def encode_params(params) if params.is_a?(String) - return params.force_encoding("UTF-8").encode! + return params.force_encoding(Encoding::UTF_8).encode! elsif !params.is_a?(Hash) return params end diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index 91cf4784db..e7e8905d7e 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -137,6 +137,7 @@ module ActionDispatch # :nodoc: @committed end + # Sets the HTTP status code. def status=(status) @status = Rack::Utils.status_code(status) end @@ -145,16 +146,24 @@ module ActionDispatch # :nodoc: @content_type = content_type.to_s end - # The response code of the request + # The response code of the request. def response_code @status end - # Returns a String to ensure compatibility with Net::HTTPResponse + # Returns a string to ensure compatibility with <tt>Net::HTTPResponse</tt>. def code @status.to_s end + # Returns the corresponding message for the current HTTP status code: + # + # response.status = 200 + # response.message # => "OK" + # + # response.status = 404 + # response.message # => "Not Found" + # def message Rack::Utils::HTTP_STATUS_CODES[@status] end @@ -172,6 +181,8 @@ module ActionDispatch # :nodoc: stream.to_path end + # Returns the content of the response as a string. This contains the contents + # of any calls to <tt>render</tt>. def body strings = [] each { |part| strings << part.to_s } @@ -180,6 +191,7 @@ module ActionDispatch # :nodoc: EMPTY = " " + # Allows you to manually set or override the response body. def body=(body) @blank = true if body == EMPTY diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb index 8a97248eb3..67cb7fbcb5 100644 --- a/actionpack/lib/action_dispatch/http/upload.rb +++ b/actionpack/lib/action_dispatch/http/upload.rb @@ -70,7 +70,7 @@ module ActionDispatch def encode_filename(filename) # Encode the filename in the utf8 encoding, unless it is nil - filename.force_encoding("UTF-8").encode! if filename + filename.force_encoding(Encoding::UTF_8).encode! if filename end end diff --git a/actionpack/lib/action_dispatch/middleware/best_standards_support.rb b/actionpack/lib/action_dispatch/middleware/best_standards_support.rb deleted file mode 100644 index 94efeb79fa..0000000000 --- a/actionpack/lib/action_dispatch/middleware/best_standards_support.rb +++ /dev/null @@ -1,30 +0,0 @@ -module ActionDispatch - class BestStandardsSupport - def initialize(app, type = true) - @app = app - - @header = case type - when true - "IE=Edge,chrome=1" - when :builtin - "IE=Edge" - when false - nil - end - end - - def call(env) - status, headers, body = @app.call(env) - - if headers["X-UA-Compatible"] && @header - unless headers["X-UA-Compatible"][@header] - headers["X-UA-Compatible"] << "," << @header.to_s - end - else - headers["X-UA-Compatible"] = @header - end - - [status, headers, body] - end - end -end diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index 6ecbb03784..fff26bd1b2 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/hash/keys' require 'active_support/core_ext/module/attribute_accessors' +require 'active_support/key_generator' require 'active_support/message_verifier' module ActionDispatch @@ -110,13 +111,17 @@ module ActionDispatch # $& => example.local DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/ + def self.options_for_env(env) #:nodoc: + { signed_cookie_salt: env[SIGNED_COOKIE_SALT] || '', + encrypted_cookie_salt: env[ENCRYPTED_COOKIE_SALT] || '', + encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT] || '', + token_key: env[TOKEN_KEY] } + end + def self.build(request) env = request.env key_generator = env[GENERATOR_KEY] - options = { signed_cookie_salt: env[SIGNED_COOKIE_SALT], - encrypted_cookie_salt: env[ENCRYPTED_COOKIE_SALT], - encrypted_signed_cookie_salt: env[ENCRYPTED_SIGNED_COOKIE_SALT], - token_key: env[TOKEN_KEY] } + options = options_for_env env host = request.host secure = request.ssl? @@ -145,6 +150,10 @@ module ActionDispatch @cookies[name.to_s] end + def fetch(name, *args, &block) + @cookies.fetch(name.to_s, *args, &block) + end + def key?(name) @cookies.key?(name.to_s) end diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb index 7c12590c49..84df55fd5a 100644 --- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb +++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb @@ -26,7 +26,7 @@ module ActionDispatch def generate_sid sid = SecureRandom.hex(16) - sid.encode!('UTF-8') + sid.encode!(Encoding::UTF_8) sid end diff --git a/actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb b/actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb index 400ae97d22..24e44f31ac 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/routes/_route.html.erb @@ -7,7 +7,7 @@ <td data-route-verb='<%= route[:verb] %>'> <%= route[:verb] %> </td> - <td data-route-path='<%= route[:path] %>'> + <td data-route-path='<%= route[:path] %>' data-regexp='<%= route[:regexp] %>'> <%= route[:path] %> </td> <td data-route-reqs='<%= route[:reqs] %>'> diff --git a/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb b/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb index 9026c4eeb2..95461fa693 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/routes/_table.html.erb @@ -1,22 +1,58 @@ <% content_for :style do %> - #route_table td { padding: 0 30px; } - #route_table { margin: 0 auto 0; } + #route_table { + margin: 0 auto 0; + border-collapse: collapse; + } + + #route_table td { + padding: 0 30px; + } + + #route_table tr.bottom th { + padding-bottom: 10px; + line-height: 15px; + } + + #route_table .matched_paths { + background-color: LightGoldenRodYellow; + } + + #route_table .matched_paths { + border-bottom: solid 3px SlateGrey; + } + + #path_search { + width: 80%; + font-size: inherit; + } <% end %> <table id='route_table' class='route_table'> <thead> <tr> - <th>Helper<br /> + <th>Helper</th> + <th>HTTP Verb</th> + <th>Path</th> + <th>Controller#Action</th> + </tr> + <tr class='bottom'> + <th><%# Helper %> <%= link_to "Path", "#", 'data-route-helper' => '_path', title: "Returns a relative path (without the http or domain)" %> / <%= link_to "Url", "#", 'data-route-helper' => '_url', - title: "Returns an absolute url (with the http and domain)" %> + title: "Returns an absolute url (with the http and domain)" %> + </th> + <th><%# HTTP Verb %> + </th> + <th><%# Path %> + <%= search_field(:path, nil, id: 'path_search', placeholder: "Path Match") %> + </th> + <th><%# Controller#action %> </th> - <th>HTTP Verb</th> - <th>Path</th> - <th>Controller#Action</th> </tr> </thead> + <tbody class='matched_paths' id='matched_paths'> + </tbody> <tbody> <%= yield %> </tbody> @@ -25,7 +61,7 @@ <script type='text/javascript'> function each(elems, func) { if (!elems instanceof Array) { elems = [elems]; } - for (var i = elems.length; i--; ) { + for (var i = 0, len = elems.length; i < len; i++) { func(elems[i]); } } @@ -46,11 +82,63 @@ function setupRouteToggleHelperLinks() { var toggleLinks = document.querySelectorAll('#route_table [data-route-helper]'); onClick(toggleLinks, function(){ - var helperTxt = this.getAttribute("data-route-helper"); - var helperElems = document.querySelectorAll('[data-route-name] span.helper'); + var helperTxt = this.getAttribute("data-route-helper"), + helperElems = document.querySelectorAll('[data-route-name] span.helper'); setValOn(helperElems, helperTxt); }); } + // takes an array of elements with a data-regexp attribute and + // passes their their parent <tr> into the callback function + // if the regexp matchs a given path + function eachElemsForPath(elems, path, func) { + each(elems, function(e){ + var reg = e.getAttribute("data-regexp"); + if (path.match(RegExp(reg))) { + func(e.parentNode.cloneNode(true)); + } + }) + } + + // Ensure path always starts with a slash "/" and remove params or fragments + function sanitizePath(path) { + var path = path.charAt(0) == '/' ? path : "/" + path; + return path.replace(/\#.*|\?.*/, ''); + } + + // Enables path search functionality + function setupMatchPaths() { + var regexpElems = document.querySelectorAll('#route_table [data-regexp]'), + pathElem = document.querySelector('#path_search'), + selectedSection = document.querySelector('#matched_paths'), + noMatchText = '<tr><th colspan="4">None</th></tr>'; + + + // Remove matches if no path is present + pathElem.onblur = function(e) { + if (pathElem.value === "") selectedSection.innerHTML = ""; + } + + // On key press perform a search for matching paths + pathElem.onkeyup = function(e){ + var path = sanitizePath(pathElem.value), + defaultText = '<tr><th colspan="4">Paths Matching (' + path + '):</th></tr>'; + + // Clear out results section + selectedSection.innerHTML= defaultText; + + // Display matches if they exist + eachElemsForPath(regexpElems, path, function(e){ + selectedSection.appendChild(e); + }); + + // If no match present, tell the user + if (selectedSection.innerHTML === defaultText) { + selectedSection.innerHTML = selectedSection.innerHTML + noMatchText; + } + } + } + + setupMatchPaths(); setupRouteToggleHelperLinks(); </script> diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index 5a835ae439..edf37bb9a5 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -6,7 +6,6 @@ module ActionDispatch config.action_dispatch.x_sendfile_header = nil config.action_dispatch.ip_spoofing_check = true config.action_dispatch.show_exceptions = true - config.action_dispatch.best_standards_support = true config.action_dispatch.tld_length = 1 config.action_dispatch.ignore_accept_header = false config.action_dispatch.rescue_templates = { } @@ -21,7 +20,8 @@ module ActionDispatch config.action_dispatch.default_headers = { 'X-Frame-Options' => 'SAMEORIGIN', 'X-XSS-Protection' => '1; mode=block', - 'X-Content-Type-Options' => 'nosniff' + 'X-Content-Type-Options' => 'nosniff', + 'X-UA-Compatible' => 'chrome=1' } config.eager_load_namespaces << ActionDispatch diff --git a/actionpack/lib/action_dispatch/request/session.rb b/actionpack/lib/action_dispatch/request/session.rb index a05a23d953..7bc812fd22 100644 --- a/actionpack/lib/action_dispatch/request/session.rb +++ b/actionpack/lib/action_dispatch/request/session.rb @@ -63,6 +63,10 @@ module ActionDispatch @exists = nil # we haven't checked yet end + def id + options[:id] + end + def options Options.find @env end diff --git a/actionpack/lib/action_dispatch/routing/inspector.rb b/actionpack/lib/action_dispatch/routing/inspector.rb index 6c970f3024..bc6dd7145c 100644 --- a/actionpack/lib/action_dispatch/routing/inspector.rb +++ b/actionpack/lib/action_dispatch/routing/inspector.rb @@ -34,6 +34,23 @@ module ActionDispatch super.to_s end + def regexp + __getobj__.path.to_regexp + end + + def json_regexp + str = regexp.inspect. + sub('\\A' , '^'). + sub('\\Z' , '$'). + sub('\\z' , '$'). + sub(/^\// , ''). + sub(/\/[a-z]*$/ , ''). + gsub(/\(\?#.+\)/ , ''). + gsub(/\(\?-\w+:/ , '('). + gsub(/\s/ , '') + Regexp.new(str).source + end + def reqs @reqs ||= begin reqs = endpoint @@ -101,7 +118,11 @@ module ActionDispatch end.collect do |route| collect_engine_routes(route) - { name: route.name, verb: route.verb, path: route.path, reqs: route.reqs } + { name: route.name, + verb: route.verb, + path: route.path, + reqs: route.reqs, + regexp: route.json_regexp } end end diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 6d93f609a6..0a41ed0fcf 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -2,6 +2,7 @@ require 'active_support/core_ext/hash/except' require 'active_support/core_ext/hash/reverse_merge' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/enumerable' +require 'active_support/core_ext/array/extract_options' require 'active_support/inflector' require 'action_dispatch/routing/redirection' @@ -245,6 +246,12 @@ module ActionDispatch raise ArgumentError, "missing :action" end + if controller.is_a?(String) && controller !~ /\A[a-z_0-9\/]*\z/ + message = "'#{controller}' is not a supported controller name. This can lead to potential routing problems." + message << " See http://guides.rubyonrails.org/routing.html#specifying-a-controller-to-use" + raise ArgumentError, message + end + hash = {} hash[:controller] = controller unless controller.blank? hash[:action] = action unless action.blank? @@ -1403,9 +1410,10 @@ module ActionDispatch def add_route(action, options) # :nodoc: path = path_for_action(action, options.delete(:path)) + action = action.to_s.dup - if action.to_s =~ /^[\w\/]+$/ - options[:action] ||= action unless action.to_s.include?("/") + if action =~ /^[\w\/]+$/ + options[:action] ||= action unless action.include?("/") else action = nil end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index c72310cca3..ff86f87d49 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -4,6 +4,7 @@ require 'thread_safe' require 'active_support/core_ext/object/to_query' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/module/remove_method' +require 'active_support/core_ext/array/extract_options' require 'action_controller/metal/exceptions' module ActionDispatch @@ -100,36 +101,16 @@ module ActionDispatch def initialize @routes = {} @helpers = [] - @module = Module.new do - protected - - def handle_positional_args(args, options, segment_keys) - inner_options = args.extract_options! - result = options.dup - - if args.size > 0 - keys = segment_keys - if args.size < keys.size - 1 # take format into account - keys -= self.url_options.keys if self.respond_to?(:url_options) - keys -= options.keys - end - result.merge!(Hash[keys.zip(args)]) - end - - result.merge!(inner_options) - end - end + @module = Module.new end def helper_names - self.module.instance_methods.map(&:to_s) + @helpers.map(&:to_s) end def clear! @helpers.each do |helper| - @module.module_eval do - remove_possible_method helper - end + @module.remove_possible_method helper end @routes.clear @@ -162,68 +143,126 @@ module ActionDispatch routes.length end - private + class UrlHelper # :nodoc: + def self.create(route, options) + if optimize_helper?(route) + OptimizedUrlHelper.new(route, options) + else + new route, options + end + end - def define_named_route_methods(name, route) - define_url_helper route, :"#{name}_path", - route.defaults.merge(:use_route => name, :only_path => true) - define_url_helper route, :"#{name}_url", - route.defaults.merge(:use_route => name, :only_path => false) + def self.optimize_helper?(route) + route.requirements.except(:controller, :action).empty? end - # Create a url helper allowing ordered parameters to be associated - # with corresponding dynamic segments, so you can do: - # - # foo_url(bar, baz, bang) - # - # Instead of: - # - # foo_url(bar: bar, baz: baz, bang: bang) - # - # Also allow options hash, so you can do: - # - # foo_url(bar, baz, bang, sort_by: 'baz') - # - def define_url_helper(route, name, options) - @module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1 - remove_possible_method :#{name} - def #{name}(*args) - if #{optimize_helper?(route)} && args.size == #{route.required_parts.size} && !args.last.is_a?(Hash) && optimize_routes_generation? - options = #{options.inspect} - options.merge!(url_options) if respond_to?(:url_options) - options[:path] = "#{optimized_helper(route)}" - ActionDispatch::Http::URL.url_for(options) - else - url_for(handle_positional_args(args, #{options.inspect}, #{route.segment_keys.inspect})) - end + class OptimizedUrlHelper < UrlHelper # :nodoc: + attr_reader :arg_size + + def initialize(route, options) + super + @path_parts = @route.required_parts + @arg_size = @path_parts.size + @string_route = string_route(route) + end + + def call(t, args) + if args.size == arg_size && !args.last.is_a?(Hash) && optimize_routes_generation?(t) + @options.merge!(t.url_options) if t.respond_to?(:url_options) + @options[:path] = optimized_helper(args) + ActionDispatch::Http::URL.url_for(@options) + else + super end - END_EVAL + end + + private + + def string_route(route) + string_route = route.ast.to_s.dup + while string_route.gsub!(/\([^\)]*\)/, "") + true + end + string_route + end + + def optimized_helper(args) + path = @string_route.dup + klass = Journey::Router::Utils - helpers << name + @path_parts.zip(args) do |part, arg| + # Replace each route parameter + # e.g. :id for regular parameter or *path for globbing + # with ruby string interpolation code + path.gsub!(/(\*|:)#{part}/, klass.escape_fragment(arg.to_param)) + end + path + end + + def optimize_routes_generation?(t) + t.send(:optimize_routes_generation?) + end end - # Clause check about when we need to generate an optimized helper. - def optimize_helper?(route) #:nodoc: - route.requirements.except(:controller, :action).empty? + def initialize(route, options) + @options = options + @segment_keys = route.segment_keys + @route = route + end + + def call(t, args) + t.url_for(handle_positional_args(t, args, @options, @segment_keys)) end - # Generates the interpolation to be used in the optimized helper. - def optimized_helper(route) - string_route = route.ast.to_s + def handle_positional_args(t, args, options, keys) + inner_options = args.extract_options! + result = options.dup - while string_route.gsub!(/\([^\)]*\)/, "") - true + if args.size > 0 + if args.size < keys.size - 1 # take format into account + keys -= t.url_options.keys if t.respond_to?(:url_options) + keys -= options.keys + end + result.merge!(Hash[keys.zip(args)]) end - route.required_parts.each_with_index do |part, i| - # Replace each route parameter - # e.g. :id for regular parameter or *path for globbing - # with ruby string interpolation code - string_route.gsub!(/(\*|:)#{part}/, "\#{Journey::Router::Utils.escape_fragment(args[#{i}].to_param)}") - end + result.merge!(inner_options) + end + end + + private + # Create a url helper allowing ordered parameters to be associated + # with corresponding dynamic segments, so you can do: + # + # foo_url(bar, baz, bang) + # + # Instead of: + # + # foo_url(bar: bar, baz: baz, bang: bang) + # + # Also allow options hash, so you can do: + # + # foo_url(bar, baz, bang, sort_by: 'baz') + # + def define_url_helper(route, name, options) + helper = UrlHelper.create(route, options.dup) - string_route + @module.remove_possible_method name + @module.module_eval do + define_method(name) do |*args| + helper.call self, args + end end + + helpers << name + end + + def define_named_route_methods(name, route) + define_url_helper route, :"#{name}_path", + route.defaults.merge(:use_route => name, :only_path => true) + define_url_helper route, :"#{name}_url", + route.defaults.merge(:use_route => name, :only_path => false) + end end attr_accessor :formatter, :set, :named_routes, :default_scope, :router diff --git a/actionpack/lib/action_dispatch/routing/routes_proxy.rb b/actionpack/lib/action_dispatch/routing/routes_proxy.rb index 73af5920ed..e2393d3799 100644 --- a/actionpack/lib/action_dispatch/routing/routes_proxy.rb +++ b/actionpack/lib/action_dispatch/routing/routes_proxy.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/array/extract_options' + module ActionDispatch module Routing class RoutesProxy #:nodoc: diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 11743e36f2..31e37893c6 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -182,6 +182,8 @@ module ActionView # width="30" and height="45", and "50" becomes width="50" and height="50". # <tt>:size</tt> will be ignored if the value is not in the correct format. # + # ==== Examples + # # image_tag("icon") # # => <img alt="Icon" src="/assets/icon" /> # image_tag("icon.png") @@ -212,10 +214,24 @@ module ActionView end # Returns a string suitable for an html image tag alt attribute. - # +src+ is meant to be an image file path. - # It removes the basename of the file path and the digest, if any. + # The +src+ argument is meant to be an image file path. + # The method removes the basename of the file path and the digest, + # if any. It also removes hyphens and underscores from file names and + # replaces them with spaces, returning a space-separated, titleized + # string. + # + # ==== Examples + # + # image_tag('rails.png') + # # => <img alt="Rails" src="/assets/rails.png" /> + # + # image_tag('hyphenated-file-name.png') + # # => <img alt="Hyphenated file name" src="/assets/hyphenated-file-name.png" /> + # + # image_tag('underscored_file_name.png') + # # => <img alt="Underscored file name" src="/assets/underscored_file_name.png" /> def image_alt(src) - File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').capitalize + File.basename(src, '.*').sub(/-[[:xdigit:]]{32}\z/, '').tr('-_', ' ').capitalize end # Returns an html video tag for the +sources+. If +sources+ is a string, diff --git a/actionpack/lib/action_view/helpers/asset_url_helper.rb b/actionpack/lib/action_view/helpers/asset_url_helper.rb index 0affac41e8..71b78cf0b5 100644 --- a/actionpack/lib/action_view/helpers/asset_url_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_url_helper.rb @@ -132,8 +132,7 @@ module ActionView source = compute_asset_path(source, options) end - relative_url_root = (defined?(config.relative_url_root) && config.relative_url_root) || - (respond_to?(:request) && request.try(:script_name)) + relative_url_root = defined?(config.relative_url_root) && config.relative_url_root if relative_url_root source = "#{relative_url_root}#{source}" unless source.starts_with?("#{relative_url_root}/") end diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 10748aacf4..61f939bff1 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -1,5 +1,6 @@ require 'date' require 'action_view/helpers/tag_helper' +require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/date/conversions' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/object/with_options' @@ -1040,14 +1041,38 @@ module ActionView end class FormBuilder + # Wraps ActionView::Helpers::DateHelper#date_select for form builders: + # + # <%= form_for @person do |f| %> + # <%= f.date_select :birth_date %> + # <%= f.submit %> + # <% end %> + # + # Please refer to the documentation of the base helper for details. def date_select(method, options = {}, html_options = {}) @template.date_select(@object_name, method, objectify_options(options), html_options) end + # Wraps ActionView::Helpers::DateHelper#time_select for form builders: + # + # <%= form_for @race do |f| %> + # <%= f.time_select :average_lap %> + # <%= f.submit %> + # <% end %> + # + # Please refer to the documentation of the base helper for details. def time_select(method, options = {}, html_options = {}) @template.time_select(@object_name, method, objectify_options(options), html_options) end + # Wraps ActionView::Helpers::DateHelper#datetime_select for form builders: + # + # <%= form_for @person do |f| %> + # <%= f.time_select :last_request_at %> + # <%= f.submit %> + # <% end %> + # + # Please refer to the documentation of the base helper for details. def datetime_select(method, options = {}, html_options = {}) @template.datetime_select(@object_name, method, objectify_options(options), html_options) end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 4a31de715d..3dae1fc87a 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -8,7 +8,6 @@ require 'action_view/model_naming' require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/string/output_safety' -require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/string/inflections' module ActionView @@ -771,8 +770,8 @@ module ActionView # text_field(:post, :title, class: "create_input") # # => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" /> # - # text_field(:session, :user, onchange: "if $('#session_user').value == 'admin' { alert('Your login can not be admin!'); }") - # # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange = "if $('#session_user').value == 'admin' { alert('Your login can not be admin!'); }"/> + # text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login can not be admin!'); }") + # # => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login can not be admin!'); }"/> # # text_field(:snippet, :code, size: 20, class: 'code_input') # # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" /> @@ -792,8 +791,8 @@ module ActionView # password_field(:account, :secret, class: "form_input", value: @account.secret) # # => <input type="password" id="account_secret" name="account[secret]" value="#{@account.secret}" class="form_input" /> # - # password_field(:user, :password, onchange: "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }") - # # => <input type="password" id="user_password" name="user[password]" onchange = "if $('user[password]').length > 30 { alert('Your password needs to be shorter!'); }"/> + # password_field(:user, :password, onchange: "if ($('#user_password').val().length > 30) { alert('Your password needs to be shorter!'); }") + # # => <input type="password" id="user_password" name="user[password]" onchange="if ($('#user_password').val().length > 30) { alert('Your password needs to be shorter!'); }"/> # # password_field(:account, :pin, size: 20, class: 'form_input') # # => <input type="password" id="account_pin" name="account[pin]" size="20" class="form_input" /> diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index ae7bfd1ec6..8b3a37a853 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -2,6 +2,7 @@ require 'cgi' require 'erb' require 'action_view/helpers/form_helper' require 'active_support/core_ext/string/output_safety' +require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/array/wrap' module ActionView @@ -756,26 +757,74 @@ module ActionView end class FormBuilder + # Wraps ActionView::Helpers::FormOptionsHelper#select for form builders: + # + # <%= form_for @post do |f| %> + # <%= f.select :person_id, Person.all.collect {|p| [ p.name, p.id ] }, { include_blank: true }) %> + # <%= f.submit %> + # <% end %> + # + # Please refer to the documentation of the base helper for details. def select(method, choices, options = {}, html_options = {}) @template.select(@object_name, method, choices, objectify_options(options), @default_options.merge(html_options)) end + # Wraps ActionView::Helpers::FormOptionsHelper#collection_select for form builders: + # + # <%= form_for @post do |f| %> + # <%= f.collection_select :person_id, Author.all, :id, :name_with_initial, prompt: true %> + # <%= f.submit %> + # <% end %> + # + # Please refer to the documentation of the base helper for details. def collection_select(method, collection, value_method, text_method, options = {}, html_options = {}) @template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options)) end + # Wraps ActionView::Helpers::FormOptionsHelper#grouped_collection_select for form builders: + # + # <%= form_for @city do |f| %> + # <%= f.grouped_collection_select :country_id, :country_id, @continents, :countries, :name, :id, :name %> + # <%= f.submit %> + # <% end %> + # + # Please refer to the documentation of the base helper for details. def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {}) @template.grouped_collection_select(@object_name, method, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_options.merge(html_options)) end + # Wraps ActionView::Helpers::FormOptionsHelper#time_zone_select for form builders: + # + # <%= form_for @user do |f| %> + # <%= f.time_zone_select :time_zone, nil, include_blank: true %> + # <%= f.submit %> + # <% end %> + # + # Please refer to the documentation of the base helper for details. def time_zone_select(method, priority_zones = nil, options = {}, html_options = {}) @template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options)) end + # Wraps ActionView::Helpers::FormOptionsHelper#collection_check_boxes for form builders: + # + # <%= form_for @post do |f| %> + # <%= f.collection_check_boxes :author_ids, Author.all, :id, :name_with_initial %> + # <%= f.submit %> + # <% end %> + # + # Please refer to the documentation of the base helper for details. def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block) @template.collection_check_boxes(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options), &block) end + # Wraps ActionView::Helpers::FormOptionsHelper#collection_radio_buttons for form builders: + # + # <%= form_for @post do |f| %> + # <%= f.collection_radio_buttons :author_id, Author.all, :id, :name_with_initial %> + # <%= f.submit %> + # <% end %> + # + # Please refer to the documentation of the base helper for details. def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block) @template.collection_radio_buttons(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options), &block) end diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index cfdd7c77d8..878d3e0eda 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -13,8 +13,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!] = '
' + JS_ESCAPE_MAP["\342\200\250".force_encoding(Encoding::UTF_8).encode!] = '
' + JS_ESCAPE_MAP["\342\200\251".force_encoding(Encoding::UTF_8).encode!] = '
' # Escapes carriage returns and single and double quotes for JavaScript segments. # diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb index 271a194913..f767957fa9 100644 --- a/actionpack/lib/action_view/helpers/record_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb @@ -92,7 +92,7 @@ module ActionView # for each record. def content_tag_for_single_record(tag_name, record, prefix, options, &block) options = options ? options.dup : {} - options[:class] = "#{dom_class(record, prefix)} #{options[:class]}".rstrip + options[:class] = [ dom_class(record, prefix), options[:class] ].compact options[:id] = dom_id(record, prefix) if block_given? diff --git a/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb b/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb index d27df45b5a..9655008fe2 100644 --- a/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb +++ b/actionpack/lib/action_view/helpers/tags/collection_check_boxes.rb @@ -13,13 +13,13 @@ module ActionView end end - def render + def render(&block) rendered_collection = render_collection do |item, value, text, default_html_options| default_html_options[:multiple] = true builder = instantiate_builder(CheckBoxBuilder, item, value, text, default_html_options) if block_given? - yield builder + @template_object.capture(builder, &block) else render_component(builder) end diff --git a/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb b/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb index 81f2ecb2b3..893f4411e7 100644 --- a/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb +++ b/actionpack/lib/action_view/helpers/tags/collection_radio_buttons.rb @@ -13,12 +13,12 @@ module ActionView end end - def render + def render(&block) render_collection do |item, value, text, default_html_options| builder = instantiate_builder(RadioButtonBuilder, item, value, text, default_html_options) if block_given? - yield builder + @template_object.capture(builder, &block) else render_component(builder) end diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index bade121d44..5e20b557d8 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -514,7 +514,7 @@ module ActionView "in a #request method" end - return false unless request.get? + return false unless request.get? || request.head? url_string = url_for(options) diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index b927c69260..f73d14c79b 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -80,8 +80,7 @@ module ActionView # problems with converting the user's data to # the <tt>default_internal</tt>. # - # To do so, simply raise the raise +WrongEncodingError+ - # as follows: + # To do so, simply raise +WrongEncodingError+ as follows: # # raise WrongEncodingError.new( # problematic_string, diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb index b479f991bc..a89d51221e 100644 --- a/actionpack/lib/action_view/template/error.rb +++ b/actionpack/lib/action_view/template/error.rb @@ -17,7 +17,7 @@ module ActionView end def message - @string.force_encoding("BINARY") + @string.force_encoding(Encoding::ASCII_8BIT) "Your template was not saved as valid #{@encoding}. Please " \ "either specify #{@encoding} as the encoding for your template " \ "in your text editor, or mark the template with its " \ diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb index afbbece90f..5aaafc15c1 100644 --- a/actionpack/lib/action_view/template/handlers/erb.rb +++ b/actionpack/lib/action_view/template/handlers/erb.rb @@ -81,7 +81,7 @@ module ActionView # wrong, we can still find an encoding tag # (<%# encoding %>) inside the String using a regular # expression - template_source = template.source.dup.force_encoding("BINARY") + template_source = template.source.dup.force_encoding(Encoding::ASCII_8BIT) erb = template_source.gsub(ENCODING_TAG, '') encoding = $2 diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 4479da5bc4..463f192d0c 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -122,7 +122,7 @@ module ActionView class RenderedViewsCollection def initialize - @rendered_views ||= {} + @rendered_views ||= Hash.new { |hash, key| hash[key] = [] } end def add(view, locals) @@ -134,6 +134,10 @@ module ActionView @rendered_views[view] end + def rendered_views + @rendered_views.keys + end + def view_rendered?(view, expected_locals) locals_for(view).any? do |actual_locals| expected_locals.all? {|key, value| value == actual_locals[key] } diff --git a/actionpack/test/abstract/translation_test.rb b/actionpack/test/abstract/translation_test.rb index 99064a8b87..4fdc480b43 100644 --- a/actionpack/test/abstract/translation_test.rb +++ b/actionpack/test/abstract/translation_test.rb @@ -1,39 +1,50 @@ require 'abstract_unit' -# class TranslatingController < ActionController::Base -# end - -class TranslationControllerTest < ActiveSupport::TestCase - def setup - @controller = ActionController::Base.new - end - - def test_action_controller_base_responds_to_translate - assert_respond_to @controller, :translate - end - - def test_action_controller_base_responds_to_t - assert_respond_to @controller, :t - end - - def test_action_controller_base_responds_to_localize - assert_respond_to @controller, :localize - end - - def test_action_controller_base_responds_to_l - assert_respond_to @controller, :l - end - - def test_lazy_lookup - expected = 'bar' - @controller.stubs(:action_name => :index) - I18n.stubs(:translate).with('action_controller.base.index.foo').returns(expected) - assert_equal expected, @controller.t('.foo') - end - - def test_default_translation - key, expected = 'one.two' 'bar' - I18n.stubs(:translate).with(key).returns(expected) - assert_equal expected, @controller.t(key) +module AbstractController + module Testing + class TranslationController < AbstractController::Base + include AbstractController::Translation + end + + class TranslationControllerTest < ActiveSupport::TestCase + def setup + @controller = TranslationController.new + end + + def test_action_controller_base_responds_to_translate + assert_respond_to @controller, :translate + end + + def test_action_controller_base_responds_to_t + assert_respond_to @controller, :t + end + + def test_action_controller_base_responds_to_localize + assert_respond_to @controller, :localize + end + + def test_action_controller_base_responds_to_l + assert_respond_to @controller, :l + end + + def test_lazy_lookup + expected = 'bar' + @controller.stubs(action_name: :index) + I18n.stubs(:translate).with('abstract_controller.testing.translation.index.foo').returns(expected) + assert_equal expected, @controller.t('.foo') + end + + def test_default_translation + key, expected = 'one.two', 'bar' + I18n.stubs(:translate).with(key).returns(expected) + assert_equal expected, @controller.t(key) + end + + def test_localize + time, expected = Time.gm(2000), 'Sat, 01 Jan 2000 00:00:00 +0000' + I18n.stubs(:localize).with(time).returns(expected) + assert_equal expected, @controller.l(time) + end + end end end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index bbcd289886..7157bccfb3 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -38,6 +38,8 @@ end ActiveSupport::Dependencies.hook! +Thread.abort_on_exception = true + # Show backtraces for deprecated behavior for quicker cleanup. ActiveSupport::Deprecation.debug = true diff --git a/actionpack/test/activerecord/form_helper_activerecord_test.rb b/actionpack/test/activerecord/form_helper_activerecord_test.rb new file mode 100644 index 0000000000..2e302c65a7 --- /dev/null +++ b/actionpack/test/activerecord/form_helper_activerecord_test.rb @@ -0,0 +1,91 @@ +require 'active_record_unit' +require 'fixtures/project' +require 'fixtures/developer' + +class FormHelperActiveRecordTest < ActionView::TestCase + tests ActionView::Helpers::FormHelper + + def form_for(*) + @output_buffer = super + end + + def setup + @developer = Developer.new + @developer.id = 123 + @developer.name = "developer #123" + + @project = Project.new + @project.id = 321 + @project.name = "project #321" + @project.save + + @developer.projects << @project + @developer.save + end + + def teardown + Project.delete(321) + Developer.delete(123) + end + + Routes = ActionDispatch::Routing::RouteSet.new + Routes.draw do + resources :developers do + resources :projects + end + end + + def _routes + Routes + end + + include Routes.url_helpers + + def test_nested_fields_for_with_child_index_option_override_on_a_nested_attributes_collection_association + form_for(@developer) do |f| + concat f.fields_for(:projects, @developer.projects.first, :child_index => 'abc') { |cf| + concat cf.text_field(:name) + } + end + + expected = whole_form('/developers/123', 'edit_developer_123', 'edit_developer', :method => 'patch') do + '<input id="developer_projects_attributes_abc_name" name="developer[projects_attributes][abc][name]" type="text" value="project #321" />' + + '<input id="developer_projects_attributes_abc_id" name="developer[projects_attributes][abc][id]" type="hidden" value="321" />' + end + + assert_dom_equal expected, output_buffer + end + + protected + + def hidden_fields(method = nil) + txt = %{<div style="margin:0;padding:0;display:inline">} + txt << %{<input name="utf8" type="hidden" value="✓" />} + if method && !%w(get post).include?(method.to_s) + txt << %{<input name="_method" type="hidden" value="#{method}" />} + end + txt << %{</div>} + end + + def form_text(action = "/", id = nil, html_class = nil, remote = nil, multipart = nil, method = nil) + txt = %{<form accept-charset="UTF-8" action="#{action}"} + txt << %{ enctype="multipart/form-data"} if multipart + txt << %{ data-remote="true"} if remote + txt << %{ class="#{html_class}"} if html_class + txt << %{ id="#{id}"} if id + method = method.to_s == "get" ? "get" : "post" + txt << %{ method="#{method}">} + end + + def whole_form(action = "/", id = nil, html_class = nil, options = nil) + contents = block_given? ? yield : "" + + if options.is_a?(Hash) + method, remote, multipart = options.values_at(:method, :remote, :multipart) + else + method = options + end + + form_text(action, id, html_class, remote, multipart, method) + hidden_fields(method) + contents + "</form>" + end +end
\ No newline at end of file diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index e2239c05c7..72b882539c 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -751,13 +751,17 @@ class UrlOptionsIntegrationTest < ActionDispatch::IntegrationTest assert_equal "http://bar.com/foo", foos_url end - test "test can override default url options" do + def test_can_override_default_url_options + original_host = default_url_options.dup + default_url_options[:host] = "foobar.com" assert_equal "http://foobar.com/foo", foos_url get "/bar" assert_response :success assert_equal "http://foobar.com/foo", foos_url + ensure + ActionDispatch::Integration::Session.default_url_options = self.default_url_options = original_host end test "current request path parameters are recalled" do diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb index 365fa04570..71bcfd664e 100644 --- a/actionpack/test/controller/layout_test.rb +++ b/actionpack/test/controller/layout_test.rb @@ -1,5 +1,6 @@ require 'abstract_unit' require 'rbconfig' +require 'active_support/core_ext/array/extract_options' # The view_paths array must be set on Base and not LayoutTest so that LayoutTest's inherited # method has access to the view_paths array when looking for a layout to automatically assign. diff --git a/actionpack/test/controller/localized_templates_test.rb b/actionpack/test/controller/localized_templates_test.rb index 41ff2f3809..bac1d02977 100644 --- a/actionpack/test/controller/localized_templates_test.rb +++ b/actionpack/test/controller/localized_templates_test.rb @@ -9,14 +9,20 @@ class LocalizedTemplatesTest < ActionController::TestCase tests LocalizedController def test_localized_template_is_used + old_locale = I18n.locale I18n.locale = :de get :hello_world assert_equal "Gutten Tag", @response.body + ensure + I18n.locale = old_locale end def test_default_locale_template_is_used_when_locale_is_missing + old_locale = I18n.locale I18n.locale = :dk get :hello_world assert_equal "Hello World", @response.body + ensure + I18n.locale = old_locale end -end
\ No newline at end of file +end diff --git a/actionpack/test/controller/parameters/nested_parameters_test.rb b/actionpack/test/controller/parameters/nested_parameters_test.rb index 6df849c4e2..91df527dec 100644 --- a/actionpack/test/controller/parameters/nested_parameters_test.rb +++ b/actionpack/test/controller/parameters/nested_parameters_test.rb @@ -2,6 +2,10 @@ require 'abstract_unit' require 'action_controller/metal/strong_parameters' class NestedParametersTest < ActiveSupport::TestCase + def assert_filtered_out(params, key) + assert !params.has_key?(key), "key #{key.inspect} has not been filtered out" + end + test "permitted nested parameters" do params = ActionController::Parameters.new({ book: { @@ -11,6 +15,8 @@ class NestedParametersTest < ActiveSupport::TestCase born: "1564-04-26" }, { name: "Christopher Marlowe" + }, { + name: %w(malicious injected names) }], details: { pages: 200, @@ -30,10 +36,12 @@ class NestedParametersTest < ActiveSupport::TestCase assert_equal "William Shakespeare", permitted[:book][:authors][0][:name] assert_equal "Christopher Marlowe", permitted[:book][:authors][1][:name] assert_equal 200, permitted[:book][:details][:pages] - assert_nil permitted[:book][:id] - assert_nil permitted[:book][:details][:genre] - assert_nil permitted[:book][:authors][0][:born] - assert_nil permitted[:magazine] + + assert_filtered_out permitted, :magazine + assert_filtered_out permitted[:book], :id + assert_filtered_out permitted[:book][:details], :genre + assert_filtered_out permitted[:book][:authors][0], :born + assert_filtered_out permitted[:book][:authors][2], :name end test "permitted nested parameters with a string or a symbol as a key" do @@ -63,25 +71,25 @@ class NestedParametersTest < ActiveSupport::TestCase test "nested arrays with strings" do params = ActionController::Parameters.new({ - :book => { - :genres => ["Tragedy"] + book: { + genres: ["Tragedy"] } }) - permitted = params.permit :book => :genres + permitted = params.permit book: {genres: []} assert_equal ["Tragedy"], permitted[:book][:genres] end test "permit may specify symbols or strings" do params = ActionController::Parameters.new({ - :book => { - :title => "Romeo and Juliet", - :author => "William Shakespeare" + book: { + title: "Romeo and Juliet", + author: "William Shakespeare" }, - :magazine => "Shakespeare Today" + magazine: "Shakespeare Today" }) - permitted = params.permit({:book => ["title", :author]}, "magazine") + permitted = params.permit({book: ["title", :author]}, "magazine") assert_equal "Romeo and Juliet", permitted[:book][:title] assert_equal "William Shakespeare", permitted[:book][:author] assert_equal "Shakespeare Today", permitted[:magazine] @@ -127,16 +135,38 @@ class NestedParametersTest < ActiveSupport::TestCase book: { authors_attributes: { :'0' => { name: 'William Shakespeare', age_of_death: '52' }, - :'-1' => { name: 'Unattributed Assistant' } + :'1' => { name: 'Unattributed Assistant' }, + :'2' => { name: %w(injected names)} } } }) permitted = params.permit book: { authors_attributes: [ :name ] } assert_not_nil permitted[:book][:authors_attributes]['0'] - assert_not_nil permitted[:book][:authors_attributes]['-1'] - assert_nil permitted[:book][:authors_attributes]['0'][:age_of_death] + assert_not_nil permitted[:book][:authors_attributes]['1'] + assert_empty permitted[:book][:authors_attributes]['2'] assert_equal 'William Shakespeare', permitted[:book][:authors_attributes]['0'][:name] - assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['-1'][:name] + assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['1'][:name] + + assert_filtered_out permitted[:book][:authors_attributes]['0'], :age_of_death + end + + test "fields_for-style nested params with negative numbers" do + params = ActionController::Parameters.new({ + book: { + authors_attributes: { + :'-1' => { name: 'William Shakespeare', age_of_death: '52' }, + :'-2' => { name: 'Unattributed Assistant' } + } + } + }) + permitted = params.permit book: { authors_attributes: [:name] } + + assert_not_nil permitted[:book][:authors_attributes]['-1'] + assert_not_nil permitted[:book][:authors_attributes]['-2'] + assert_equal 'William Shakespeare', permitted[:book][:authors_attributes]['-1'][:name] + assert_equal 'Unattributed Assistant', permitted[:book][:authors_attributes]['-2'][:name] + + assert_filtered_out permitted[:book][:authors_attributes]['-1'], :age_of_death end end diff --git a/actionpack/test/controller/parameters/parameters_permit_test.rb b/actionpack/test/controller/parameters/parameters_permit_test.rb index 7cc71fe6dc..aadb142660 100644 --- a/actionpack/test/controller/parameters/parameters_permit_test.rb +++ b/actionpack/test/controller/parameters/parameters_permit_test.rb @@ -1,11 +1,133 @@ require 'abstract_unit' +require 'action_dispatch/http/upload' require 'action_controller/metal/strong_parameters' class ParametersPermitTest < ActiveSupport::TestCase + def assert_filtered_out(params, key) + assert !params.has_key?(key), "key #{key.inspect} has not been filtered out" + end + setup do @params = ActionController::Parameters.new({ person: { age: "32", name: { first: "David", last: "Heinemeier Hansson" } }}) + + @struct_fields = [] + %w(0 1 12).each do |number| + ['', 'i', 'f'].each do |suffix| + @struct_fields << "sf(#{number}#{suffix})" + end + end + end + + test 'if nothing is permitted, the hash becomes empty' do + params = ActionController::Parameters.new(id: '1234') + permitted = params.permit + assert permitted.permitted? + assert permitted.empty? + end + + test 'key: permitted scalar values' do + values = ['a', :a, nil] + values += [0, 1.0, 2**128, BigDecimal.new(1)] + values += [true, false] + values += [Date.today, Time.now, DateTime.now] + values += [STDOUT, StringIO.new, ActionDispatch::Http::UploadedFile.new(tempfile: __FILE__)] + + values.each do |value| + params = ActionController::Parameters.new(id: value) + permitted = params.permit(:id) + assert_equal value, permitted[:id] + + @struct_fields.each do |sf| + params = ActionController::Parameters.new(sf => value) + permitted = params.permit(:sf) + assert_equal value, permitted[sf] + end + end + end + + test 'key: unknown keys are filtered out' do + params = ActionController::Parameters.new(id: '1234', injected: 'injected') + permitted = params.permit(:id) + assert_equal '1234', permitted[:id] + assert_filtered_out permitted, :injected + end + + test 'key: arrays are filtered out' do + [[], [1], ['1']].each do |array| + params = ActionController::Parameters.new(id: array) + permitted = params.permit(:id) + assert_filtered_out permitted, :id + + @struct_fields.each do |sf| + params = ActionController::Parameters.new(sf => array) + permitted = params.permit(:sf) + assert_filtered_out permitted, sf + end + end + end + + test 'key: hashes are filtered out' do + [{}, {foo: 1}, {foo: 'bar'}].each do |hash| + params = ActionController::Parameters.new(id: hash) + permitted = params.permit(:id) + assert_filtered_out permitted, :id + + @struct_fields.each do |sf| + params = ActionController::Parameters.new(sf => hash) + permitted = params.permit(:sf) + assert_filtered_out permitted, sf + end + end + end + + test 'key: non-permitted scalar values are filtered out' do + params = ActionController::Parameters.new(id: Object.new) + permitted = params.permit(:id) + assert_filtered_out permitted, :id + + @struct_fields.each do |sf| + params = ActionController::Parameters.new(sf => Object.new) + permitted = params.permit(:sf) + assert_filtered_out permitted, sf + end + end + + test 'key: it is not assigned if not present in params' do + params = ActionController::Parameters.new(name: 'Joe') + permitted = params.permit(:id) + assert !permitted.has_key?(:id) + end + + test 'key to empty array: empty arrays pass' do + params = ActionController::Parameters.new(id: []) + permitted = params.permit(id: []) + assert_equal [], permitted[:id] + end + + test 'key to empty array: arrays of permitted scalars pass' do + [['foo'], [1], ['foo', 'bar'], [1, 2, 3]].each do |array| + params = ActionController::Parameters.new(id: array) + permitted = params.permit(id: []) + assert_equal array, permitted[:id] + end + end + + test 'key to empty array: permitted scalar values do not pass' do + ['foo', 1].each do |permitted_scalar| + params = ActionController::Parameters.new(id: permitted_scalar) + permitted = params.permit(id: []) + assert_filtered_out permitted, :id + end + end + + test 'key to empty array: arrays of non-permitted scalar do not pass' do + [[Object.new], [[]], [[1]], [{}], [{id: '1'}]].each do |non_permitted_scalar| + params = ActionController::Parameters.new(id: non_permitted_scalar) + permitted = params.permit(id: []) + assert_filtered_out permitted, :id + end end test "fetch raises ParameterMissing exception" do @@ -73,10 +195,6 @@ class ParametersPermitTest < ActiveSupport::TestCase assert_equal "Jonas", @params[:person][:family][:brother] end - test "permitting parameters that are not there should not include the keys" do - assert !@params.permit(:person, :funky).has_key?(:funky) - end - test "permit state is kept on a dup" do @params.permit! assert_equal @params.permitted?, @params.dup.permitted? diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index 523a8d0572..c272e785c2 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -66,6 +66,19 @@ class RequestForgeryProtectionControllerUsingException < ActionController::Base protect_from_forgery :only => %w(index meta), :with => :exception end +class RequestForgeryProtectionControllerUsingNullSession < ActionController::Base + protect_from_forgery :with => :null_session + + def signed + cookies.signed[:foo] = 'bar' + render :nothing => true + end + + def encrypted + cookies.encrypted[:foo] = 'bar' + render :nothing => true + end +end class FreeCookieController < RequestForgeryProtectionControllerUsingResetSession self.allow_forgery_protection = false @@ -170,6 +183,10 @@ module RequestForgeryProtectionTests assert_not_blocked { get :index } end + def test_should_allow_head + assert_not_blocked { head :index } + end + def test_should_allow_post_without_token_on_unsafe_action assert_not_blocked { post :unsafe } end @@ -283,6 +300,28 @@ class RequestForgeryProtectionControllerUsingResetSessionTest < ActionController end end +class NullSessionDummyKeyGenerator + def generate_key(secret) + '03312270731a2ed0d11ed091c2338a06' + end +end + +class RequestForgeryProtectionControllerUsingNullSessionTest < ActionController::TestCase + def setup + @request.env[ActionDispatch::Cookies::GENERATOR_KEY] = NullSessionDummyKeyGenerator.new + end + + test 'should allow to set signed cookies' do + post :signed + assert_response :ok + end + + test 'should allow to set encrypted cookies' do + post :encrypted + assert_response :ok + end +end + class RequestForgeryProtectionControllerUsingExceptionTest < ActionController::TestCase include RequestForgeryProtectionTests def assert_blocked diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 305659b219..9aea7e860a 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -1,6 +1,7 @@ require 'abstract_unit' require 'active_support/core_ext/object/try' require 'active_support/core_ext/object/with_options' +require 'active_support/core_ext/array/extract_options' class ResourcesTest < ActionController::TestCase def test_default_restful_routes diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb index 0480295056..19d5652d81 100644 --- a/actionpack/test/controller/webservice_test.rb +++ b/actionpack/test/controller/webservice_test.rb @@ -129,19 +129,6 @@ class WebServiceTest < ActionDispatch::IntegrationTest $stderr = STDERR end - def test_register_and_use_yaml - with_test_route_set do - with_params_parsers Mime::YAML => Proc.new { |d| YAML.load(d) } do - post "/", {"entry" => "loaded from yaml"}.to_yaml, - {'CONTENT_TYPE' => 'application/x-yaml'} - - assert_equal 'entry', @controller.response.body - assert @controller.params.has_key?(:entry) - assert_equal 'loaded from yaml', @controller.params["entry"] - end - end - end - def test_register_and_use_xml_simple with_test_route_set do with_params_parsers Mime::XML => Proc.new { |data| Hash.from_xml(data)['request'].with_indifferent_access } do diff --git a/actionpack/test/dispatch/best_standards_support_test.rb b/actionpack/test/dispatch/best_standards_support_test.rb deleted file mode 100644 index 551bb9621a..0000000000 --- a/actionpack/test/dispatch/best_standards_support_test.rb +++ /dev/null @@ -1,35 +0,0 @@ -require 'abstract_unit' - -class BestStandardsSupportTest < ActiveSupport::TestCase - def test_with_best_standards_support - _, headers, _ = app(true, {}).call({}) - assert_equal "IE=Edge,chrome=1", headers["X-UA-Compatible"] - end - - def test_with_builtin_best_standards_support - _, headers, _ = app(:builtin, {}).call({}) - assert_equal "IE=Edge", headers["X-UA-Compatible"] - end - - def test_without_best_standards_support - _, headers, _ = app(false, {}).call({}) - assert_equal nil, headers["X-UA-Compatible"] - end - - def test_appends_to_app_headers_without_duplication_after_multiple_requests - app_headers = { "X-UA-Compatible" => "requiresActiveX=true" } - _, headers, _ = app(true, app_headers).call({}) - _, headers, _ = app(true, app_headers).call({}) - - expects = "requiresActiveX=true,IE=Edge,chrome=1" - assert_equal expects, headers["X-UA-Compatible"] - end - - private - - def app(type, headers) - app = proc { [200, headers, "response"] } - ActionDispatch::BestStandardsSupport.new(app, type) - end - -end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index ffa91d63c4..66d2cade22 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -160,6 +160,36 @@ class CookiesTest < ActionController::TestCase @request.host = "www.nextangle.com" end + def test_fetch + x = Object.new + assert_not request.cookie_jar.key?('zzzzzz') + assert_equal x, request.cookie_jar.fetch('zzzzzz', x) + assert_not request.cookie_jar.key?('zzzzzz') + end + + def test_fetch_exists + x = Object.new + request.cookie_jar['foo'] = 'bar' + assert_equal 'bar', request.cookie_jar.fetch('foo', x) + end + + def test_fetch_block + x = Object.new + assert_not request.cookie_jar.key?('zzzzzz') + assert_equal x, request.cookie_jar.fetch('zzzzzz') { x } + end + + def test_key_is_to_s + request.cookie_jar['foo'] = 'bar' + assert_equal 'bar', request.cookie_jar.fetch(:foo) + end + + def test_fetch_type_error + assert_raises(KeyError) do + request.cookie_jar.fetch(:omglolwut) + end + end + def test_each request.cookie_jar['foo'] = :bar list = [] @@ -211,13 +241,13 @@ class CookiesTest < ActionController::TestCase def test_setting_cookie_for_fourteen_days get :authenticate_for_fourteen_days - assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT" + assert_cookie_header "user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 -0000" assert_equal({"user_name" => "david"}, @response.cookies) end def test_setting_cookie_for_fourteen_days_with_symbols get :authenticate_for_fourteen_days_with_symbols - assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT" + assert_cookie_header "user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 -0000" assert_equal({"user_name" => "david"}, @response.cookies) end @@ -250,7 +280,7 @@ class CookiesTest < ActionController::TestCase def test_multiple_cookies get :set_multiple_cookies assert_equal 2, @response.cookies.size - assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT\nlogin=XJ-122; path=/" + assert_cookie_header "user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 -0000\nlogin=XJ-122; path=/" assert_equal({"login" => "XJ-122", "user_name" => "david"}, @response.cookies) end @@ -261,14 +291,14 @@ class CookiesTest < ActionController::TestCase def test_expiring_cookie request.cookies[:user_name] = 'Joe' get :logout - assert_cookie_header "user_name=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT" + assert_cookie_header "user_name=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000" assert_equal({"user_name" => nil}, @response.cookies) end def test_delete_cookie_with_path request.cookies[:user_name] = 'Joe' get :delete_cookie_with_path - assert_cookie_header "user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT" + assert_cookie_header "user_name=; path=/beaten; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000" end def test_delete_unexisting_cookie @@ -330,7 +360,7 @@ class CookiesTest < ActionController::TestCase def test_delete_and_set_cookie request.cookies[:user_name] = 'Joe' get :delete_and_set_cookie - assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT" + assert_cookie_header "user_name=david; path=/; expires=Mon, 10 Oct 2005 05:00:00 -0000" assert_equal({"user_name" => "david"}, @response.cookies) end @@ -435,7 +465,7 @@ class CookiesTest < ActionController::TestCase request.cookies[:user_name] = 'Joe' get :delete_cookie_with_domain assert_response :success - assert_cookie_header "user_name=; domain=.nextangle.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT" + assert_cookie_header "user_name=; domain=.nextangle.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000" end def test_cookie_with_all_domain_option_and_tld_length @@ -462,7 +492,7 @@ class CookiesTest < ActionController::TestCase request.cookies[:user_name] = 'Joe' get :delete_cookie_with_domain_and_tld assert_response :success - assert_cookie_header "user_name=; domain=.nextangle.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT" + assert_cookie_header "user_name=; domain=.nextangle.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000" end def test_cookie_with_several_preset_domains_using_one_of_these_domains @@ -491,7 +521,7 @@ class CookiesTest < ActionController::TestCase request.cookies[:user_name] = 'Joe' get :delete_cookie_with_domains assert_response :success - assert_cookie_header "user_name=; domain=example2.com; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT" + assert_cookie_header "user_name=; domain=example2.com; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000" end def test_deletings_cookie_with_several_preset_domains_using_other_domain @@ -499,7 +529,7 @@ class CookiesTest < ActionController::TestCase request.cookies[:user_name] = 'Joe' get :delete_cookie_with_domains assert_response :success - assert_cookie_header "user_name=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT" + assert_cookie_header "user_name=; path=/; max-age=0; expires=Thu, 01 Jan 1970 00:00:00 -0000" end def test_cookies_hash_is_indifferent_access diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 185a9e9b18..74f5253c11 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -35,7 +35,7 @@ class ResponseTest < ActiveSupport::TestCase end def test_response_body_encoding - body = ["hello".encode('utf-8')] + body = ["hello".encode(Encoding::UTF_8)] response = ActionDispatch::Response.new 200, {}, body assert_equal Encoding::UTF_8, response.body.encoding end @@ -129,7 +129,7 @@ class ResponseTest < ActiveSupport::TestCase @response.set_cookie("login", :value => "foo&bar", :path => "/", :expires => Time.utc(2005, 10, 10,5)) status, headers, body = @response.to_a - assert_equal "user_name=david; path=/\nlogin=foo%26bar; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT", headers["Set-Cookie"] + assert_equal "user_name=david; path=/\nlogin=foo%26bar; path=/; expires=Mon, 10 Oct 2005 05:00:00 -0000", headers["Set-Cookie"] assert_equal({"login" => "foo&bar", "user_name" => "david"}, @response.cookies) @response.delete_cookie("login") @@ -182,7 +182,8 @@ class ResponseTest < ActiveSupport::TestCase ActionDispatch::Response.default_headers = { 'X-Frame-Options' => 'DENY', 'X-Content-Type-Options' => 'nosniff', - 'X-XSS-Protection' => '1;' + 'X-XSS-Protection' => '1;', + 'X-UA-Compatible' => 'chrome=1' } resp = ActionDispatch::Response.new.tap { |response| response.body = 'Hello' @@ -192,6 +193,7 @@ class ResponseTest < ActiveSupport::TestCase assert_equal('DENY', resp.headers['X-Frame-Options']) assert_equal('nosniff', resp.headers['X-Content-Type-Options']) assert_equal('1;', resp.headers['X-XSS-Protection']) + assert_equal('chrome=1', resp.headers['X-UA-Compatible']) ensure ActionDispatch::Response.default_headers = nil end diff --git a/actionpack/test/dispatch/routing/inspector_test.rb b/actionpack/test/dispatch/routing/inspector_test.rb index c7dcb5a683..55221f87c4 100644 --- a/actionpack/test/dispatch/routing/inspector_test.rb +++ b/actionpack/test/dispatch/routing/inspector_test.rb @@ -21,6 +21,14 @@ module ActionDispatch inspector.format(ActionDispatch::Routing::ConsoleFormatter.new, options[:filter]).split("\n") end + def test_json_regexp_converter + @set.draw do + get '/cart', :to => 'cart#show' + end + route = ActionDispatch::Routing::RouteWrapper.new(@set.routes.first) + assert_equal "^\\/cart(?:\\.([^\\/.?]+))?$", route.json_regexp + end + def test_displaying_routes_for_engines engine = Class.new(Rails::Engine) do def self.inspect diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index da7474e73c..143733254b 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -1031,6 +1031,18 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal 'users/home#index', @response.body end + def test_namespace_containing_numbers + draw do + namespace :v2 do + resources :subscriptions + end + end + + get '/v2/subscriptions' + assert_equal 'v2/subscriptions#index', @response.body + assert_equal '/v2/subscriptions', v2_subscriptions_path + end + def test_articles_with_id draw do controller :articles do @@ -2678,6 +2690,15 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal '0c0c0b68-d24b-11e1-a861-001ff3fffe6f', @request.params[:download] end + def test_action_from_path_is_not_frozen + draw do + get 'search' => 'search' + end + + get '/search' + assert !@request.params[:action].frozen? + end + private def draw(&block) @@ -2824,21 +2845,52 @@ class TestNamespaceWithControllerOption < ActionDispatch::IntegrationTest end end - DefaultScopeRoutes = ActionDispatch::Routing::RouteSet.new - DefaultScopeRoutes.draw do - namespace :admin do - resources :storage_files, :controller => "StorageFiles" - end + def draw(&block) + @app = ActionDispatch::Routing::RouteSet.new + @app.draw(&block) end - def app - DefaultScopeRoutes - end + def test_valid_controller_options_inside_namespace + draw do + namespace :admin do + resources :storage_files, :controller => "storage_files" + end + end - def test_controller_options get '/admin/storage_files' assert_equal "admin/storage_files#index", @response.body end + + def test_resources_with_valid_namespaced_controller_option + draw do + resources :storage_files, :controller => 'admin/storage_files' + end + + get 'storage_files' + assert_equal "admin/storage_files#index", @response.body + end + + def test_warn_with_ruby_constant_syntax_controller_option + e = assert_raise(ArgumentError) do + draw do + namespace :admin do + resources :storage_files, :controller => "StorageFiles" + end + end + end + + assert_match "'admin/StorageFiles' is not a supported controller name", e.message + end + + def test_warn_with_ruby_constant_syntax_namespaced_controller_option + e = assert_raise(ArgumentError) do + draw do + resources :storage_files, :controller => 'Admin::StorageFiles' + end + end + + assert_match "'Admin::StorageFiles' is not a supported controller name", e.message + end end class TestDefaultScope < ActionDispatch::IntegrationTest diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb index 1677dee524..d8bf22dec8 100644 --- a/actionpack/test/dispatch/session/cookie_store_test.rb +++ b/actionpack/test/dispatch/session/cookie_store_test.rb @@ -276,7 +276,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest # First request accesses the session time = Time.local(2008, 4, 24) Time.stubs(:now).returns(time) - expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d-%b-%Y %H:%M:%S GMT") + expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000") cookies[SessionKey] = SignedBar @@ -290,7 +290,7 @@ class CookieStoreTest < ActionDispatch::IntegrationTest # Second request does not access the session time = Time.local(2008, 4, 25) Time.stubs(:now).returns(time) - expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d-%b-%Y %H:%M:%S GMT") + expected_expiry = (time + 5.hours).gmtime.strftime("%a, %d %b %Y %H:%M:%S -0000") get '/no_session_access' assert_response :success diff --git a/actionpack/test/dispatch/session/test_session_test.rb b/actionpack/test/dispatch/session/test_session_test.rb index 904398f563..d30461a623 100644 --- a/actionpack/test/dispatch/session/test_session_test.rb +++ b/actionpack/test/dispatch/session/test_session_test.rb @@ -2,8 +2,8 @@ require 'abstract_unit' require 'stringio' class ActionController::TestSessionTest < ActiveSupport::TestCase - def test_ctor_allows_setting - session = ActionController::TestSession.new({:one => 'one', :two => 'two'}) + def test_initialize_with_values + session = ActionController::TestSession.new(one: 'one', two: 'two') assert_equal('one', session[:one]) assert_equal('two', session[:two]) end @@ -23,15 +23,21 @@ class ActionController::TestSessionTest < ActiveSupport::TestCase end def test_calling_update_with_params_passes_to_attributes - session = ActionController::TestSession.new() + session = ActionController::TestSession.new session.update('key' => 'value') assert_equal('value', session[:key]) end - def test_clear_emptys_session - session = ActionController::TestSession.new({:one => 'one', :two => 'two'}) + def test_clear_empties_session + session = ActionController::TestSession.new(one: 'one', two: 'two') session.clear assert_nil(session[:one]) assert_nil(session[:two]) end + + def test_keys_and_values + session = ActionController::TestSession.new(one: '1', two: '2') + assert_equal %w(one two), session.keys + assert_equal %w(1 2), session.values + end end diff --git a/actionpack/test/dispatch/test_request_test.rb b/actionpack/test/dispatch/test_request_test.rb index 6047631ba3..3db862c810 100644 --- a/actionpack/test/dispatch/test_request_test.rb +++ b/actionpack/test/dispatch/test_request_test.rb @@ -18,7 +18,7 @@ class TestRequestTest < ActiveSupport::TestCase assert_equal "0.0.0.0", env.delete("REMOTE_ADDR") assert_equal "Rails Testing", env.delete("HTTP_USER_AGENT") - assert_equal [1, 1], env.delete("rack.version") + assert_equal [1, 2], env.delete("rack.version") assert_equal "", env.delete("rack.input").string assert_kind_of StringIO, env.delete("rack.errors") assert_equal true, env.delete("rack.multithread") @@ -40,10 +40,10 @@ class TestRequestTest < ActiveSupport::TestCase req.cookie_jar["login"] = "XJ-122" assert_cookies({"user_name" => "david", "login" => "XJ-122"}, req.cookie_jar) - assert_nothing_raised do + assert_nothing_raised do req.cookie_jar["login"] = nil assert_cookies({"user_name" => "david", "login" => nil}, req.cookie_jar) - end + end req.cookie_jar.delete(:login) assert_cookies({"user_name" => "david"}, req.cookie_jar) diff --git a/actionpack/test/fixtures/developer.rb b/actionpack/test/fixtures/developer.rb index dd14548fac..4941463015 100644 --- a/actionpack/test/fixtures/developer.rb +++ b/actionpack/test/fixtures/developer.rb @@ -2,6 +2,7 @@ class Developer < ActiveRecord::Base has_and_belongs_to_many :projects has_many :replies has_many :topics, :through => :replies + accepts_nested_attributes_for :projects end class DeVeLoPeR < ActiveRecord::Base diff --git a/actionpack/test/fixtures/test/_directory/_partial_with_locales.html.erb b/actionpack/test/fixtures/test/_directory/_partial_with_locales.html.erb new file mode 100644 index 0000000000..1cc8d41475 --- /dev/null +++ b/actionpack/test/fixtures/test/_directory/_partial_with_locales.html.erb @@ -0,0 +1 @@ +Hello <%= name %> diff --git a/actionpack/test/fixtures/test/render_partial_inside_directory.html.erb b/actionpack/test/fixtures/test/render_partial_inside_directory.html.erb new file mode 100644 index 0000000000..1461b95186 --- /dev/null +++ b/actionpack/test/fixtures/test/render_partial_inside_directory.html.erb @@ -0,0 +1 @@ +<%= render partial: 'test/_directory/partial_with_locales', locals: {'name' => 'Jane'} %> diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 82c9d383ac..11614a45dc 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -443,7 +443,8 @@ class AssetTagHelperTest < ActionView::TestCase [nil, '/', '/foo/bar/', 'foo/bar/'].each do |prefix| assert_equal 'Rails', image_alt("#{prefix}rails.png") assert_equal 'Rails', image_alt("#{prefix}rails-9c0a079bdd7701d7e729bd956823d153.png") - assert_equal 'Avatar-0000', image_alt("#{prefix}avatar-0000.png") + assert_equal 'Long file name with hyphens', image_alt("#{prefix}long-file-name-with-hyphens.png") + assert_equal 'Long file name with underscores', image_alt("#{prefix}long_file_name_with_underscores.png") end end diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index f9890a2eef..268bab6ad2 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -15,26 +15,26 @@ class FormHelperTest < ActionView::TestCase # Create "label" locale for testing I18n label helpers I18n.backend.store_translations 'label', { - :activemodel => { - :attributes => { - :post => { - :cost => "Total cost" + activemodel: { + attributes: { + post: { + cost: "Total cost" } } }, - :helpers => { - :label => { - :post => { - :body => "Write entire text here", - :color => { - :red => "Rojo" + helpers: { + label: { + post: { + body: "Write entire text here", + color: { + red: "Rojo" }, - :comments => { - :body => "Write body here" + comments: { + body: "Write body here" } }, - :tag => { - :value => "Tag" + tag: { + value: "Tag" } } } @@ -42,13 +42,13 @@ class FormHelperTest < ActionView::TestCase # Create "submit" locale for testing I18n submit helpers I18n.backend.store_translations 'submit', { - :helpers => { - :submit => { - :create => 'Create %{model}', - :update => 'Confirm %{model} changes', - :submit => 'Save changes', - :another_post => { - :update => 'Update your %{model}' + helpers: { + submit: { + create: 'Create %{model}', + update: 'Confirm %{model} changes', + submit: 'Save changes', + another_post: { + update: 'Update your %{model}' } } } @@ -57,11 +57,11 @@ class FormHelperTest < ActionView::TestCase @post = Post.new @comment = Comment.new def @post.errors() - Class.new{ + Class.new { def [](field); field == "author_name" ? ["can't be empty"] : [] end def empty?() false end def count() 1 end - def full_messages() [ "Author name can't be empty" ] end + def full_messages() ["Author name can't be empty"] end }.new end def @post.to_key; [123]; end @@ -96,8 +96,8 @@ class FormHelperTest < ActionView::TestCase end end - get "/foo", :to => "controller#action" - root :to => "main#index" + get "/foo", to: "controller#action" + root to: "main#index" end def _routes @@ -108,9 +108,11 @@ class FormHelperTest < ActionView::TestCase def url_for(object) @url_for_options = object + if object.is_a?(Hash) && object[:use_route].blank? && object[:controller].blank? - object.merge!(:controller => "main", :action => "index") + object.merge!(controller: "main", action: "index") end + super end @@ -124,10 +126,13 @@ class FormHelperTest < ActionView::TestCase def test_label assert_dom_equal('<label for="post_title">Title</label>', label("post", "title")) - assert_dom_equal('<label for="post_title">The title goes here</label>', label("post", "title", "The title goes here")) + assert_dom_equal( + '<label for="post_title">The title goes here</label>', + label("post", "title", "The title goes here") + ) assert_dom_equal( '<label class="title_label" for="post_title">Title</label>', - label("post", "title", nil, :class => 'title_label') + label("post", "title", nil, class: 'title_label') ) assert_dom_equal('<label for="post_secret">Secret?</label>', label("post", "secret?")) end @@ -160,28 +165,31 @@ class FormHelperTest < ActionView::TestCase def test_label_with_locales_and_options old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal('<label for="post_body" class="post_body">Write entire text here</label>', label(:post, :body, :class => 'post_body')) + assert_dom_equal( + '<label for="post_body" class="post_body">Write entire text here</label>', + label(:post, :body, class: "post_body") + ) ensure I18n.locale = old_locale end def test_label_with_locales_and_value old_locale, I18n.locale = I18n.locale, :label - assert_dom_equal('<label for="post_color_red">Rojo</label>', label(:post, :color, :value => "red")) + assert_dom_equal('<label for="post_color_red">Rojo</label>', label(:post, :color, value: "red")) ensure I18n.locale = old_locale end def test_label_with_locales_and_nested_attributes old_locale, I18n.locale = I18n.locale, :label - form_for(@post, :html => { :id => 'create-post' }) do |f| + form_for(@post, html: { id: 'create-post' }) do |f| f.fields_for(:comments) do |cf| concat cf.label(:body) end end - expected = whole_form("/posts/123", "create-post" , "edit_post", :method => 'patch') do - "<label for=\"post_comments_attributes_0_body\">Write body here</label>" + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + '<label for="post_comments_attributes_0_body">Write body here</label>' end assert_dom_equal expected, output_buffer @@ -191,14 +199,14 @@ class FormHelperTest < ActionView::TestCase def test_label_with_locales_fallback_and_nested_attributes old_locale, I18n.locale = I18n.locale, :label - form_for(@post, :html => { :id => 'create-post' }) do |f| + form_for(@post, html: { id: 'create-post' }) do |f| f.fields_for(:tags) do |cf| concat cf.label(:value) end end - expected = whole_form("/posts/123", "create-post" , "edit_post", :method => 'patch') do - "<label for=\"post_tags_attributes_0_value\">Tag</label>" + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do + '<label for="post_tags_attributes_0_value">Tag</label>' end assert_dom_equal expected, output_buffer @@ -207,7 +215,7 @@ class FormHelperTest < ActionView::TestCase end def test_label_with_for_attribute_as_symbol - assert_dom_equal('<label for="my_for">Title</label>', label(:post, :title, nil, :for => "my_for")) + assert_dom_equal('<label for="my_for">Title</label>', label(:post, :title, nil, for: "my_for")) end def test_label_with_for_attribute_as_string @@ -215,61 +223,93 @@ class FormHelperTest < ActionView::TestCase end def test_label_does_not_generate_for_attribute_when_given_nil - assert_dom_equal('<label>Title</label>', label(:post, :title, :for => nil)) + assert_dom_equal('<label>Title</label>', label(:post, :title, for: nil)) end def test_label_with_id_attribute_as_symbol - assert_dom_equal('<label for="post_title" id="my_id">Title</label>', label(:post, :title, nil, :id => "my_id")) + assert_dom_equal( + '<label for="post_title" id="my_id">Title</label>', + label(:post, :title, nil, id: "my_id") + ) end def test_label_with_id_attribute_as_string - assert_dom_equal('<label for="post_title" id="my_id">Title</label>', label(:post, :title, nil, "id" => "my_id")) + assert_dom_equal( + '<label for="post_title" id="my_id">Title</label>', + label(:post, :title, nil, "id" => "my_id") + ) end def test_label_with_for_and_id_attributes_as_symbol - assert_dom_equal('<label for="my_for" id="my_id">Title</label>', label(:post, :title, nil, :for => "my_for", :id => "my_id")) + assert_dom_equal( + '<label for="my_for" id="my_id">Title</label>', + label(:post, :title, nil, for: "my_for", id: "my_id") + ) end def test_label_with_for_and_id_attributes_as_string - assert_dom_equal('<label for="my_for" id="my_id">Title</label>', label(:post, :title, nil, "for" => "my_for", "id" => "my_id")) + assert_dom_equal( + '<label for="my_for" id="my_id">Title</label>', + label(:post, :title, nil, "for" => "my_for", "id" => "my_id") + ) end def test_label_for_radio_buttons_with_value - assert_dom_equal('<label for="post_title_great_title">The title goes here</label>', label("post", "title", "The title goes here", :value => "great_title")) - assert_dom_equal('<label for="post_title_great_title">The title goes here</label>', label("post", "title", "The title goes here", :value => "great title")) + assert_dom_equal( + '<label for="post_title_great_title">The title goes here</label>', + label("post", "title", "The title goes here", value: "great_title") + ) + assert_dom_equal( + '<label for="post_title_great_title">The title goes here</label>', + label("post", "title", "The title goes here", value: "great title") + ) end def test_label_with_block - assert_dom_equal('<label for="post_title">The title, please:</label>', label(:post, :title) { "The title, please:" }) + assert_dom_equal( + '<label for="post_title">The title, please:</label>', + label(:post, :title) { "The title, please:" } + ) end def test_label_with_block_and_options - assert_dom_equal('<label for="my_for">The title, please:</label>', label(:post, :title, "for" => "my_for") { "The title, please:" }) + assert_dom_equal( + '<label for="my_for">The title, please:</label>', + label(:post, :title, "for" => "my_for") { "The title, please:" } + ) end def test_label_with_block_in_erb - 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") + 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]" 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]" 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]" 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]" 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]" 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 @@ -284,29 +324,29 @@ class FormHelperTest < ActionView::TestCase def test_text_field_with_options expected = '<input id="post_title" name="post[title]" size="35" type="text" value="Hello World" />' assert_dom_equal expected, text_field("post", "title", "size" => 35) - assert_dom_equal expected, text_field("post", "title", :size => 35) + assert_dom_equal expected, text_field("post", "title", size: 35) end def test_text_field_assuming_size expected = '<input id="post_title" maxlength="35" name="post[title]" size="35" type="text" value="Hello World" />' assert_dom_equal expected, text_field("post", "title", "maxlength" => 35) - assert_dom_equal expected, text_field("post", "title", :maxlength => 35) + assert_dom_equal expected, text_field("post", "title", maxlength: 35) end def test_text_field_removing_size expected = '<input id="post_title" maxlength="35" name="post[title]" type="text" value="Hello World" />' assert_dom_equal expected, text_field("post", "title", "maxlength" => 35, "size" => nil) - assert_dom_equal expected, text_field("post", "title", :maxlength => 35, :size => nil) + assert_dom_equal expected, text_field("post", "title", maxlength: 35, size: nil) end def test_text_field_with_nil_value expected = '<input id="post_title" name="post[title]" type="text" />' - assert_dom_equal expected, text_field("post", "title", :value => nil) + assert_dom_equal expected, text_field("post", "title", value: nil) end def test_text_field_with_nil_name expected = '<input id="post_title" type="text" value="Hello World" />' - assert_dom_equal expected, text_field("post", "title", :name => nil) + assert_dom_equal expected, text_field("post", "title", name: nil) end def test_text_field_doesnt_change_param_values @@ -322,31 +362,41 @@ class FormHelperTest < ActionView::TestCase end def test_hidden_field - assert_dom_equal '<input id="post_title" name="post[title]" type="hidden" value="Hello World" />', + assert_dom_equal( + '<input id="post_title" name="post[title]" type="hidden" value="Hello World" />', hidden_field("post", "title") - assert_dom_equal '<input id="post_secret" name="post[secret]" type="hidden" value="1" />', - hidden_field("post", "secret?") + ) + assert_dom_equal( + '<input id="post_secret" name="post[secret]" type="hidden" value="1" />', + hidden_field("post", "secret?") + ) end def test_hidden_field_with_escapes @post.title = "<b>Hello World</b>" - assert_dom_equal '<input id="post_title" name="post[title]" type="hidden" value="<b>Hello World</b>" />', + assert_dom_equal( + '<input id="post_title" name="post[title]" type="hidden" value="<b>Hello World</b>" />', hidden_field("post", "title") + ) end def test_hidden_field_with_nil_value expected = '<input id="post_title" name="post[title]" type="hidden" />' - assert_dom_equal expected, hidden_field("post", "title", :value => nil) + assert_dom_equal expected, hidden_field("post", "title", value: nil) end def test_hidden_field_with_options - assert_dom_equal '<input id="post_title" name="post[title]" type="hidden" value="Something Else" />', - hidden_field("post", "title", :value => "Something Else") + assert_dom_equal( + '<input id="post_title" name="post[title]" type="hidden" value="Something Else" />', + hidden_field("post", "title", value: "Something Else") + ) end def test_text_field_with_custom_type - assert_dom_equal '<input id="user_email" name="user[email]" type="email" />', - text_field("user", "email", :type => "email") + assert_dom_equal( + '<input id="user_email" name="user[email]" type="email" />', + text_field("user", "email", type: "email") + ) end def test_check_box_is_html_safe @@ -371,7 +421,7 @@ class FormHelperTest < ActionView::TestCase def test_check_box_checked_if_option_checked_is_present assert_dom_equal( '<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" />', - check_box("post", "secret" ,{"checked"=>"checked"}) + check_box("post", "secret", "checked"=>"checked") ) end @@ -410,7 +460,10 @@ class FormHelperTest < ActionView::TestCase def test_check_box_with_include_hidden_false @post.secret = false - assert_dom_equal('<input id="post_secret" name="post[secret]" type="checkbox" value="1" />', check_box("post", "secret", :include_hidden => false)) + assert_dom_equal( + '<input id="post_secret" name="post[secret]" type="checkbox" value="1" />', + check_box("post", "secret", include_hidden: false) + ) end def test_check_box_with_explicit_checked_and_unchecked_values_when_object_value_is_string @@ -517,11 +570,11 @@ class FormHelperTest < ActionView::TestCase @post.comment_ids = [2,3] assert_dom_equal( '<input name="post[comment_ids][]" type="hidden" value="0" /><input id="post_comment_ids_1" name="post[comment_ids][]" type="checkbox" value="1" />', - check_box("post", "comment_ids", { :multiple => true }, 1) + check_box("post", "comment_ids", { multiple: true }, 1) ) assert_dom_equal( '<input name="post[comment_ids][]" type="hidden" value="0" /><input checked="checked" id="post_comment_ids_3" name="post[comment_ids][]" type="checkbox" value="3" />', - check_box("post", "comment_ids", { :multiple => true }, 3) + check_box("post", "comment_ids", { multiple: true }, 3) ) end @@ -529,11 +582,11 @@ class FormHelperTest < ActionView::TestCase @post.comment_ids = [2,3] assert_dom_equal( '<input name="post[foo][comment_ids][]" type="hidden" value="0" /><input id="post_foo_comment_ids_1" name="post[foo][comment_ids][]" type="checkbox" value="1" />', - check_box("post", "comment_ids", { :multiple => true, :index => "foo" }, 1) + check_box("post", "comment_ids", { multiple: true, index: "foo" }, 1) ) assert_dom_equal( '<input name="post[bar][comment_ids][]" type="hidden" value="0" /><input checked="checked" id="post_bar_comment_ids_3" name="post[bar][comment_ids][]" type="checkbox" value="3" />', - check_box("post", "comment_ids", { :multiple => true, :index => "bar" }, 3) + check_box("post", "comment_ids", { multiple: true, index: "bar" }, 3) ) end @@ -541,14 +594,14 @@ class FormHelperTest < ActionView::TestCase def test_checkbox_disabled_disables_hidden_field assert_dom_equal( '<input name="post[secret]" type="hidden" value="0" disabled="disabled"/><input checked="checked" disabled="disabled" id="post_secret" name="post[secret]" type="checkbox" value="1" />', - check_box("post", "secret", { :disabled => true }) + check_box("post", "secret", { disabled: true }) ) end def test_checkbox_form_html5_attribute assert_dom_equal( '<input form="new_form" name="post[secret]" type="hidden" value="0" /><input checked="checked" form="new_form" id="post_secret" name="post[secret]" type="checkbox" value="1" />', - check_box("post", "secret", :form => "new_form") + check_box("post", "secret", form: "new_form") ) end @@ -577,7 +630,7 @@ class FormHelperTest < ActionView::TestCase def test_radio_button_respects_passed_in_id assert_dom_equal('<input checked="checked" id="foo" name="post[secret]" type="radio" value="1" />', - radio_button("post", "secret", "1", :id=>"foo") + radio_button("post", "secret", "1", id: "foo") ) end @@ -599,7 +652,7 @@ class FormHelperTest < ActionView::TestCase end def test_text_area_with_escapes - @post.body = "Back to <i>the</i> hill and over it again!" + @post.body = "Back to <i>the</i> hill and over it again!" assert_dom_equal( %{<textarea id="post_body" name="post[body]">\nBack to <i>the</i> hill and over it again!</textarea>}, text_area("post", "body") @@ -609,12 +662,12 @@ class FormHelperTest < ActionView::TestCase def test_text_area_with_alternate_value assert_dom_equal( %{<textarea id="post_body" name="post[body]">\nTesting alternate values.</textarea>}, - text_area("post", "body", :value => 'Testing alternate values.') + text_area("post", "body", value: "Testing alternate values.") ) end def test_text_area_with_html_entities - @post.body = "The HTML Entity for & is &" + @post.body = "The HTML Entity for & is &" assert_dom_equal( %{<textarea id="post_body" name="post[body]">\nThe HTML Entity for & is &amp;</textarea>}, text_area("post", "body") @@ -624,7 +677,7 @@ class FormHelperTest < ActionView::TestCase def test_text_area_with_size_option assert_dom_equal( %{<textarea cols="183" id="post_body" name="post[body]" rows="820">\nBack to the hill and over it again!</textarea>}, - text_area("post", "body", :size => "183x820") + text_area("post", "body", size: "183x820") ) end @@ -666,7 +719,7 @@ class FormHelperTest < ActionView::TestCase min_value = DateTime.new(2000, 6, 15) max_value = DateTime.new(2010, 8, 15) step = 2 - assert_dom_equal(expected, date_field("post", "written_on", :min => min_value, :max => max_value, :step => step)) + assert_dom_equal(expected, date_field("post", "written_on", min: min_value, max: max_value, step: step)) end def test_date_field_with_timewithzone_value @@ -701,7 +754,7 @@ class FormHelperTest < ActionView::TestCase min_value = DateTime.new(2000, 6, 15, 20, 45, 30) max_value = DateTime.new(2010, 8, 15, 10, 25, 00) step = 60 - assert_dom_equal(expected, time_field("post", "written_on", :min => min_value, :max => max_value, :step => step)) + assert_dom_equal(expected, time_field("post", "written_on", min: min_value, max: max_value, step: step)) end def test_time_field_with_timewithzone_value @@ -736,7 +789,7 @@ class FormHelperTest < ActionView::TestCase min_value = DateTime.new(2000, 6, 15, 20, 45, 30) max_value = DateTime.new(2010, 8, 15, 10, 25, 00) step = 60 - assert_dom_equal(expected, datetime_field("post", "written_on", :min => min_value, :max => max_value, :step => step)) + assert_dom_equal(expected, datetime_field("post", "written_on", min: min_value, max: max_value, step: step)) end def test_datetime_field_with_timewithzone_value @@ -771,7 +824,7 @@ class FormHelperTest < ActionView::TestCase min_value = DateTime.new(2000, 6, 15, 20, 45, 30) max_value = DateTime.new(2010, 8, 15, 10, 25, 00) step = 60 - assert_dom_equal(expected, datetime_local_field("post", "written_on", :min => min_value, :max => max_value, :step => step)) + assert_dom_equal(expected, datetime_local_field("post", "written_on", min: min_value, max: max_value, step: step)) end def test_datetime_local_field_with_timewithzone_value @@ -812,7 +865,7 @@ class FormHelperTest < ActionView::TestCase min_value = DateTime.new(2000, 2, 13) max_value = DateTime.new(2010, 12, 23) step = 2 - assert_dom_equal(expected, month_field("post", "written_on", :min => min_value, :max => max_value, :step => step)) + assert_dom_equal(expected, month_field("post", "written_on", min: min_value, max: max_value, step: step)) end def test_month_field_with_timewithzone_value @@ -847,7 +900,7 @@ class FormHelperTest < ActionView::TestCase min_value = DateTime.new(2000, 2, 13) max_value = DateTime.new(2010, 12, 23) step = 2 - assert_dom_equal(expected, week_field("post", "written_on", :min => min_value, :max => max_value, :step => step)) + assert_dom_equal(expected, week_field("post", "written_on", min: min_value, max: max_value, step: step)) end def test_week_field_with_timewithzone_value @@ -871,21 +924,22 @@ class FormHelperTest < ActionView::TestCase def test_number_field expected = %{<input name="order[quantity]" max="9" id="order_quantity" type="number" min="1" />} - assert_dom_equal(expected, number_field("order", "quantity", :in => 1...10)) + assert_dom_equal(expected, number_field("order", "quantity", in: 1...10)) expected = %{<input name="order[quantity]" size="30" max="9" id="order_quantity" type="number" min="1" />} - assert_dom_equal(expected, number_field("order", "quantity", :size => 30, :in => 1...10)) + assert_dom_equal(expected, number_field("order", "quantity", size: 30, in: 1...10)) end def test_range_input expected = %{<input name="hifi[volume]" step="0.1" max="11" id="hifi_volume" type="range" min="0" />} - assert_dom_equal(expected, range_field("hifi", "volume", :in => 0..11, :step => 0.1)) + assert_dom_equal(expected, range_field("hifi", "volume", in: 0..11, step: 0.1)) expected = %{<input name="hifi[volume]" step="0.1" size="30" max="11" id="hifi_volume" type="range" min="0" />} - assert_dom_equal(expected, range_field("hifi", "volume", :size => 30, :in => 0..11, :step => 0.1)) + assert_dom_equal(expected, range_field("hifi", "volume", size: 30, in: 0..11, step: 0.1)) end def test_explicit_name assert_dom_equal( - '<input id="post_title" name="dont guess" 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 id="post_body" name="really!">\nBack to the hill and over it again!</textarea>}, @@ -895,17 +949,24 @@ class FormHelperTest < ActionView::TestCase '<input name="i mean it" type="hidden" value="0" /><input checked="checked" id="post_secret" name="i mean it" type="checkbox" value="1" />', check_box("post", "secret", "name" => "i mean it") ) - assert_dom_equal text_field("post", "title", "name" => "dont guess"), - text_field("post", "title", :name => "dont guess") - assert_dom_equal text_area("post", "body", "name" => "really!"), - text_area("post", "body", :name => "really!") - assert_dom_equal check_box("post", "secret", "name" => "i mean it"), - check_box("post", "secret", :name => "i mean it") + assert_dom_equal( + text_field("post", "title", "name" => "dont guess"), + text_field("post", "title", name: "dont guess") + ) + assert_dom_equal( + text_area("post", "body", "name" => "really!"), + text_area("post", "body", name: "really!") + ) + assert_dom_equal( + check_box("post", "secret", "name" => "i mean it"), + check_box("post", "secret", name: "i mean it") + ) end def test_explicit_id assert_dom_equal( - '<input id="dont guess" name="post[title]" 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 id="really!" name="post[body]">\nBack to the hill and over it again!</textarea>}, @@ -915,17 +976,24 @@ class FormHelperTest < ActionView::TestCase '<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="i mean it" name="post[secret]" type="checkbox" value="1" />', check_box("post", "secret", "id" => "i mean it") ) - assert_dom_equal text_field("post", "title", "id" => "dont guess"), - text_field("post", "title", :id => "dont guess") - assert_dom_equal text_area("post", "body", "id" => "really!"), - text_area("post", "body", :id => "really!") - assert_dom_equal check_box("post", "secret", "id" => "i mean it"), - check_box("post", "secret", :id => "i mean it") + assert_dom_equal( + text_field("post", "title", "id" => "dont guess"), + text_field("post", "title", id: "dont guess") + ) + assert_dom_equal( + text_area("post", "body", "id" => "really!"), + text_area("post", "body", id: "really!") + ) + assert_dom_equal( + check_box("post", "secret", "id" => "i mean it"), + check_box("post", "secret", id: "i mean it") + ) end def test_nil_id assert_dom_equal( - '<input name="post[title]" 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 name="post[body]">\nBack to the hill and over it again!</textarea>}, @@ -943,14 +1011,22 @@ class FormHelperTest < ActionView::TestCase '<select name="post[secret]"></select>', select("post", "secret", [], {}, "id" => nil) ) - assert_dom_equal text_field("post", "title", "id" => nil), - text_field("post", "title", :id => nil) - assert_dom_equal text_area("post", "body", "id" => nil), - text_area("post", "body", :id => nil) - assert_dom_equal check_box("post", "secret", "id" => nil), - check_box("post", "secret", :id => nil) - assert_dom_equal radio_button("post", "secret", "0", "id" => nil), - radio_button("post", "secret", "0", :id => nil) + assert_dom_equal( + text_field("post", "title", "id" => nil), + text_field("post", "title", id: nil) + ) + assert_dom_equal( + text_area("post", "body", "id" => nil), + text_area("post", "body", id: nil) + ) + assert_dom_equal( + check_box("post", "secret", "id" => nil), + check_box("post", "secret", id: nil) + ) + assert_dom_equal( + radio_button("post", "secret", "0", "id" => nil), + radio_button("post", "secret", "0", id: nil) + ) end def test_index @@ -995,40 +1071,42 @@ class FormHelperTest < ActionView::TestCase ) assert_dom_equal( text_field("post", "title", "index" => 5, 'id' => nil), - text_field("post", "title", :index => 5, :id => nil) + text_field("post", "title", index: 5, id: nil) ) assert_dom_equal( text_area("post", "body", "index" => 5, 'id' => nil), - text_area("post", "body", :index => 5, :id => nil) + text_area("post", "body", index: 5, id: nil) ) assert_dom_equal( check_box("post", "secret", "index" => 5, 'id' => nil), - check_box("post", "secret", :index => 5, :id => nil) + check_box("post", "secret", index: 5, id: nil) ) end def test_auto_index pid = 123 assert_dom_equal( - "<label for=\"post_#{pid}_title\">Title</label>", + %{<label for="post_#{pid}_title">Title</label>}, label("post[]", "title") ) assert_dom_equal( - "<input id=\"post_#{pid}_title\" name=\"post[#{pid}][title]\" 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 id=\"post_#{pid}_body\" name=\"post[#{pid}][body]\">\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( - "<input name=\"post[#{pid}][secret]\" type=\"hidden\" value=\"0\" /><input checked=\"checked\" id=\"post_#{pid}_secret\" name=\"post[#{pid}][secret]\" type=\"checkbox\" value=\"1\" />", + %{<input name="post[#{pid}][secret]" type="hidden" value="0" /><input checked="checked" id="post_#{pid}_secret" name="post[#{pid}][secret]" type="checkbox" value="1" />}, check_box("post[]", "secret") ) assert_dom_equal( - "<input checked=\"checked\" id=\"post_#{pid}_title_hello_world\" name=\"post[#{pid}][title]\" type=\"radio\" value=\"Hello World\" />", + %{<input checked="checked" id="post_#{pid}_title_hello_world" name="post[#{pid}][title]" type="radio" value="Hello World" />}, radio_button("post[]", "title", "Hello World") ) - assert_dom_equal("<input id=\"post_#{pid}_title_goodbye_world\" name=\"post[#{pid}][title]\" type=\"radio\" value=\"Goodbye World\" />", + assert_dom_equal( + %{<input id="post_#{pid}_title_goodbye_world" name="post[#{pid}][title]" type="radio" value="Goodbye World" />}, radio_button("post[]", "title", "Goodbye World") ) end @@ -1036,48 +1114,49 @@ class FormHelperTest < ActionView::TestCase def test_auto_index_with_nil_id pid = 123 assert_dom_equal( - "<input name=\"post[#{pid}][title]\" type=\"text\" value=\"Hello World\" />", - text_field("post[]","title", :id => nil) + %{<input name="post[#{pid}][title]" type="text" value="Hello World" />}, + text_field("post[]", "title", id: nil) ) assert_dom_equal( - "<textarea name=\"post[#{pid}][body]\">\nBack to the hill and over it again!</textarea>", - text_area("post[]", "body", :id => nil) + %{<textarea name="post[#{pid}][body]">\nBack to the hill and over it again!</textarea>}, + text_area("post[]", "body", id: nil) ) assert_dom_equal( - "<input name=\"post[#{pid}][secret]\" type=\"hidden\" value=\"0\" /><input checked=\"checked\" name=\"post[#{pid}][secret]\" type=\"checkbox\" value=\"1\" />", - check_box("post[]", "secret", :id => nil) + %{<input name="post[#{pid}][secret]" type="hidden" value="0" /><input checked="checked" name="post[#{pid}][secret]" type="checkbox" value="1" />}, + check_box("post[]", "secret", id: nil) ) assert_dom_equal( -"<input checked=\"checked\" name=\"post[#{pid}][title]\" type=\"radio\" value=\"Hello World\" />", - radio_button("post[]", "title", "Hello World", :id => nil) + %{<input checked="checked" name="post[#{pid}][title]" type="radio" value="Hello World" />}, + radio_button("post[]", "title", "Hello World", id: nil) ) - assert_dom_equal("<input name=\"post[#{pid}][title]\" type=\"radio\" value=\"Goodbye World\" />", - radio_button("post[]", "title", "Goodbye World", :id => nil) + assert_dom_equal( + %{<input name="post[#{pid}][title]" type="radio" value="Goodbye World" />}, + radio_button("post[]", "title", "Goodbye World", id: nil) ) end def test_form_for_requires_block assert_raises(ArgumentError) do - form_for(:post, @post, :html => { :id => 'create-post' }) + form_for(:post, @post, html: { id: 'create-post' }) end end def test_form_for_requires_arguments error = assert_raises(ArgumentError) do - form_for(nil, :html => { :id => 'create-post' }) do + form_for(nil, html: { id: 'create-post' }) do end end assert_equal "First argument in form cannot contain nil or be empty", error.message error = assert_raises(ArgumentError) do - form_for([nil, nil], :html => { :id => 'create-post' }) do + form_for([nil, nil], html: { id: 'create-post' }) do end end assert_equal "First argument in form cannot contain nil or be empty", error.message end def test_form_for - form_for(@post, :html => { :id => 'create-post' }) do |f| + form_for(@post, html: { id: 'create-post' }) do |f| concat f.label(:title) { "The Title" } concat f.text_field(:title) concat f.text_area(:body) @@ -1089,7 +1168,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form("/posts/123", "create-post" , "edit_post", :method => 'patch') do + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch") do "<label for='post_title'>The Title</label>" + "<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>" + @@ -1110,7 +1189,7 @@ class FormHelperTest < ActionView::TestCase concat f.collection_radio_buttons(:active, [true, false], :to_s, :to_s) end - expected = whole_form("/posts", "new_post" , "new_post") do + expected = whole_form("/posts", "new_post", "new_post") do "<input id='post_active_true' name='post[active]' type='radio' value='true' />" + "<label for='post_active_true'>true</label>" + "<input checked='checked' id='post_active_false' name='post[active]' type='radio' value='false' />" + @@ -1123,6 +1202,7 @@ class FormHelperTest < ActionView::TestCase def test_form_for_with_collection_radio_buttons_with_custom_builder_block post = Post.new def post.active; false; end + form_for(post) do |f| rendered_radio_buttons = f.collection_radio_buttons(:active, [true, false], :to_s, :to_s) do |b| b.label { b.radio_button + b.text } @@ -1130,7 +1210,7 @@ class FormHelperTest < ActionView::TestCase concat rendered_radio_buttons end - expected = whole_form("/posts", "new_post" , "new_post") do + expected = whole_form("/posts", "new_post", "new_post") do "<label for='post_active_true'>"+ "<input id='post_active_true' name='post[active]' type='radio' value='true' />" + "true</label>" + @@ -1142,15 +1222,41 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_form_for_with_collection_radio_buttons_with_custom_builder_block_does_not_leak_the_template + post = Post.new + def post.active; false; end + def post.id; 1; end + + form_for(post) do |f| + rendered_radio_buttons = f.collection_radio_buttons(:active, [true, false], :to_s, :to_s) do |b| + b.label { b.radio_button + b.text } + end + concat rendered_radio_buttons + concat f.hidden_field :id + end + + expected = whole_form("/posts", "new_post_1", "new_post") do + "<label for='post_active_true'>"+ + "<input id='post_active_true' name='post[active]' type='radio' value='true' />" + + "true</label>" + + "<label for='post_active_false'>"+ + "<input checked='checked' id='post_active_false' name='post[active]' type='radio' value='false' />" + + "false</label>"+ + "<input id='post_id' name='post[id]' type='hidden' value='1' />" + end + + assert_dom_equal expected, output_buffer + end + def test_form_for_with_collection_check_boxes post = Post.new def post.tag_ids; [1, 3]; end - collection = (1..3).map{|i| [i, "Tag #{i}"] } + collection = (1..3).map { |i| [i, "Tag #{i}"] } form_for(post) do |f| concat f.collection_check_boxes(:tag_ids, collection, :first, :last) end - expected = whole_form("/posts", "new_post" , "new_post") do + expected = whole_form("/posts", "new_post", "new_post") do "<input checked='checked' id='post_tag_ids_1' name='post[tag_ids][]' type='checkbox' value='1' />" + "<label for='post_tag_ids_1'>Tag 1</label>" + "<input id='post_tag_ids_2' name='post[tag_ids][]' type='checkbox' value='2' />" + @@ -1166,7 +1272,7 @@ class FormHelperTest < ActionView::TestCase def test_form_for_with_collection_check_boxes_with_custom_builder_block post = Post.new def post.tag_ids; [1, 3]; end - collection = (1..3).map{|i| [i, "Tag #{i}"] } + collection = (1..3).map { |i| [i, "Tag #{i}"] } form_for(post) do |f| rendered_check_boxes = f.collection_check_boxes(:tag_ids, collection, :first, :last) do |b| b.label { b.check_box + b.text } @@ -1174,7 +1280,7 @@ class FormHelperTest < ActionView::TestCase concat rendered_check_boxes end - expected = whole_form("/posts", "new_post" , "new_post") do + expected = whole_form("/posts", "new_post", "new_post") do "<label for='post_tag_ids_1'>" + "<input checked='checked' id='post_tag_ids_1' name='post[tag_ids][]' type='checkbox' value='1' />" + "Tag 1</label>" + @@ -1190,14 +1296,45 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_form_for_with_collection_check_boxes_with_custom_builder_block_does_not_leak_the_template + post = Post.new + def post.tag_ids; [1, 3]; end + def post.id; 1; end + collection = (1..3).map { |i| [i, "Tag #{i}"] } + + form_for(post) do |f| + rendered_check_boxes = f.collection_check_boxes(:tag_ids, collection, :first, :last) do |b| + b.label { b.check_box + b.text } + end + concat rendered_check_boxes + concat f.hidden_field :id + end + + expected = whole_form("/posts", "new_post_1", "new_post") do + "<label for='post_tag_ids_1'>" + + "<input checked='checked' id='post_tag_ids_1' name='post[tag_ids][]' type='checkbox' value='1' />" + + "Tag 1</label>" + + "<label for='post_tag_ids_2'>" + + "<input id='post_tag_ids_2' name='post[tag_ids][]' type='checkbox' value='2' />" + + "Tag 2</label>" + + "<label for='post_tag_ids_3'>" + + "<input checked='checked' id='post_tag_ids_3' name='post[tag_ids][]' type='checkbox' value='3' />" + + "Tag 3</label>" + + "<input name='post[tag_ids][]' type='hidden' value='' />"+ + "<input id='post_id' name='post[id]' type='hidden' value='1' />" + end + + assert_dom_equal expected, output_buffer + end + def test_form_for_with_file_field_generate_multipart Post.send :attr_accessor, :file - form_for(@post, :html => { :id => 'create-post' }) do |f| + form_for(@post, html: { id: 'create-post' }) do |f| concat f.file_field(:file) end - expected = whole_form("/posts/123", "create-post" , "edit_post", :method => 'patch', :multipart => true) do + expected = whole_form("/posts/123", "create-post", "edit_post", method: "patch", multipart: true) do "<input name='post[file]' type='file' id='post_file' />" end @@ -1213,7 +1350,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form("/posts/123", "edit_post_123" , "edit_post", :method => 'patch', :multipart => true) do + expected = whole_form("/posts/123", "edit_post_123", "edit_post", method: "patch", multipart: true) do "<input name='post[comment][file]' type='file' id='post_comment_file' />" end @@ -1221,11 +1358,11 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_format - form_for(@post, :format => :json, :html => { :id => "edit_post_123", :class => "edit_post" }) do |f| + form_for(@post, format: :json, html: { id: "edit_post_123", class: "edit_post" }) do |f| concat f.label(:title) end - expected = whole_form("/posts/123.json", "edit_post_123" , "edit_post", :method => 'patch') do + expected = whole_form("/posts/123.json", "edit_post_123", "edit_post", method: 'patch') do "<label for='post_title'>Title</label>" end @@ -1240,7 +1377,7 @@ class FormHelperTest < ActionView::TestCase concat f.submit('Edit post') end - expected = whole_form("/posts/44", "edit_post_44" , "edit_post", :method => 'patch') do + expected = whole_form("/posts/44", "edit_post_44", "edit_post", method: "patch") do "<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 @@ -1249,15 +1386,15 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_symbol_object_name - form_for(@post, :as => "other_name", :html => { :id => 'create-post' }) do |f| - concat f.label(:title, :class => 'post_title') + form_for(@post, as: "other_name", html: { id: "create-post" }) do |f| + concat f.label(:title, class: 'post_title') concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) concat f.submit('Create post') end - expected = whole_form("/posts/123", "create-post", "edit_other_name", :method => 'patch') do + 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]' 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>" + @@ -1270,13 +1407,13 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_method_as_part_of_html_options - form_for(@post, :url => '/', :html => { :id => 'create-post', :method => :delete }) do |f| + form_for(@post, url: '/', html: { id: 'create-post', method: :delete }) do |f| concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) end - expected = whole_form("/", "create-post", "edit_post", "delete") do + expected = whole_form("/", "create-post", "edit_post", method: "delete") do "<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' />" + @@ -1287,13 +1424,13 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_method - form_for(@post, :url => '/', :method => :delete, :html => { :id => 'create-post' }) do |f| + form_for(@post, url: '/', method: :delete, html: { id: 'create-post' }) do |f| concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) end - expected = whole_form("/", "create-post", "edit_post", "delete") do + expected = whole_form("/", "create-post", "edit_post", method: "delete") do "<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' />" + @@ -1306,11 +1443,11 @@ class FormHelperTest < ActionView::TestCase def test_form_for_with_search_field # Test case for bug which would emit an "object" attribute # when used with form_for using a search_field form helper - form_for(Post.new, :url => "/search", :html => { :id => 'search-post', :method => :get}) do |f| + form_for(Post.new, url: "/search", html: { id: "search-post", method: :get }) do |f| concat f.search_field(:title) end - expected = whole_form("/search", "search-post", "new_post", "get") do + expected = whole_form("/search", "search-post", "new_post", method: "get") do "<input name='post[title]' type='search' id='post_title' />" end @@ -1318,13 +1455,13 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_remote - form_for(@post, :url => '/', :remote => true, :html => { :id => 'create-post', :method => :patch}) do |f| + form_for(@post, url: '/', remote: true, html: { id: 'create-post', method: :patch }) do |f| concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) end - expected = whole_form("/", "create-post", "edit_post", :method => 'patch', :remote => true) do + expected = whole_form("/", "create-post", "edit_post", method: "patch", remote: true) do "<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' />" + @@ -1335,13 +1472,13 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_remote_in_html - form_for(@post, :url => '/', :html => { :remote => true, :id => 'create-post', :method => :patch }) do |f| + form_for(@post, url: '/', html: { remote: true, id: 'create-post', method: :patch }) do |f| concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) end - expected = whole_form("/", "create-post", "edit_post", :method => 'patch', :remote => true) do + expected = whole_form("/", "create-post", "edit_post", method: "patch", remote: true) do "<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' />" + @@ -1354,13 +1491,13 @@ class FormHelperTest < ActionView::TestCase def test_form_for_with_remote_without_html @post.persisted = false @post.stubs(:to_key).returns(nil) - form_for(@post, :remote => true) do |f| + form_for(@post, remote: true) do |f| concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) end - expected = whole_form("/posts", 'new_post', 'new_post', :remote => true) do + expected = whole_form("/posts", "new_post", "new_post", remote: true) do "<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' />" + @@ -1371,7 +1508,7 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_without_object - form_for(:post, :html => { :id => 'create-post' }) do |f| + form_for(:post, html: { id: 'create-post' }) do |f| concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) @@ -1388,14 +1525,14 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_index - form_for(@post, :as => "post[]") do |f| + form_for(@post, as: "post[]") do |f| concat f.label(:title) concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) end - expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do + expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', method: 'patch') do "<label for='post_123_title'>Title</label>" + "<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>" + @@ -1407,13 +1544,13 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_nil_index_option_override - form_for(@post, :as => "post[]", :index => nil) do |f| + form_for(@post, as: "post[]", index: nil) do |f| concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) end - expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do + expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', method: 'patch') do "<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' />" + @@ -1425,12 +1562,12 @@ class FormHelperTest < ActionView::TestCase def test_form_for_label_error_wrapping form_for(@post) do |f| - concat f.label(:author_name, :class => 'label') + concat f.label(:author_name, class: 'label') concat f.text_field(:author_name) concat f.submit('Create post') end - expected = whole_form('/posts/123', 'edit_post_123' , 'edit_post', 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do "<div class='field_with_errors'><label for='post_author_name' class='label'>Author name</label></div>" + "<div class='field_with_errors'><input name='post[author_name]' type='text' id='post_author_name' value='' /></div>" + "<input name='commit' type='submit' value='Create post' />" @@ -1443,12 +1580,12 @@ class FormHelperTest < ActionView::TestCase post = remove_instance_variable :@post form_for(post) do |f| - concat f.label(:author_name, :class => 'label') + concat f.label(:author_name, class: 'label') concat f.text_field(:author_name) concat f.submit('Create post') end - expected = whole_form('/posts/123', 'edit_post_123' , 'edit_post', 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do "<div class='field_with_errors'><label for='post_author_name' class='label'>Author name</label></div>" + "<div class='field_with_errors'><input name='post[author_name]' type='text' id='post_author_name' value='' /></div>" + "<input name='commit' type='submit' value='Create post' />" @@ -1459,11 +1596,11 @@ class FormHelperTest < ActionView::TestCase def test_form_for_label_error_wrapping_block_and_non_block_versions form_for(@post) do |f| - concat f.label(:author_name, 'Name', :class => 'label') - concat f.label(:author_name, :class => 'label') { 'Name' } + concat f.label(:author_name, 'Name', class: 'label') + concat f.label(:author_name, class: 'label') { 'Name' } end - expected = whole_form('/posts/123', 'edit_post_123' , 'edit_post', 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do "<div class='field_with_errors'><label for='post_author_name' class='label'>Name</label></div>" + "<div class='field_with_errors'><label for='post_author_name' class='label'>Name</label></div>" end @@ -1472,13 +1609,13 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_namespace - form_for(@post, :namespace => 'namespace') do |f| + form_for(@post, namespace: 'namespace') do |f| concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) end - expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', 'patch') do + expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', method: 'patch') do "<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' />" + @@ -1489,7 +1626,7 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_namespace_with_date_select - form_for(@post, :namespace => 'namespace') do |f| + form_for(@post, namespace: 'namespace') do |f| concat f.date_select(:written_on) end @@ -1497,12 +1634,12 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_namespace_with_label - form_for(@post, :namespace => 'namespace') do |f| + form_for(@post, namespace: 'namespace') do |f| concat f.label(:title) concat f.text_field(:title) end - expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', 'patch') do + expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', method: 'patch') do "<label for='namespace_post_title'>Title</label>" + "<input name='post[title]' type='text' id='namespace_post_title' value='Hello World' />" end @@ -1511,24 +1648,24 @@ class FormHelperTest < ActionView::TestCase end def test_two_form_for_with_namespace - form_for(@post, :namespace => 'namespace_1') do |f| + form_for(@post, namespace: 'namespace_1') do |f| concat f.label(:title) concat f.text_field(:title) end - expected_1 = whole_form('/posts/123', 'namespace_1_edit_post_123', 'edit_post', 'patch') do + expected_1 = whole_form('/posts/123', 'namespace_1_edit_post_123', 'edit_post', method: 'patch') do "<label for='namespace_1_post_title'>Title</label>" + "<input name='post[title]' type='text' id='namespace_1_post_title' value='Hello World' />" end assert_dom_equal expected_1, output_buffer - form_for(@post, :namespace => 'namespace_2') do |f| + form_for(@post, namespace: 'namespace_2') do |f| concat f.label(:title) concat f.text_field(:title) end - expected_2 = whole_form('/posts/123', 'namespace_2_edit_post_123', 'edit_post', 'patch') do + expected_2 = whole_form('/posts/123', 'namespace_2_edit_post_123', 'edit_post', method: 'patch') do "<label for='namespace_2_post_title'>Title</label>" + "<input name='post[title]' type='text' id='namespace_2_post_title' value='Hello World' />" end @@ -1538,7 +1675,7 @@ class FormHelperTest < ActionView::TestCase def test_fields_for_with_namespace @comment.body = 'Hello World' - form_for(@post, :namespace => 'namespace') do |f| + form_for(@post, namespace: 'namespace') do |f| concat f.text_field(:title) concat f.text_area(:body) concat f.fields_for(@comment) { |c| @@ -1546,7 +1683,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', 'patch') do + expected = whole_form('/posts/123', 'namespace_edit_post_123', 'edit_post', method: 'patch') do "<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' />" @@ -1580,7 +1717,7 @@ class FormHelperTest < ActionView::TestCase concat f.submit end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do "<input name='commit' type='submit' value='Confirm Post changes' />" end @@ -1593,7 +1730,7 @@ class FormHelperTest < ActionView::TestCase old_locale, I18n.locale = I18n.locale, :submit form_for(:post) do |f| - concat f.submit :class => "extra" + concat f.submit class: "extra" end expected = whole_form do @@ -1608,11 +1745,11 @@ class FormHelperTest < ActionView::TestCase def test_submit_with_object_and_nested_lookup old_locale, I18n.locale = I18n.locale, :submit - form_for(@post, :as => :another_post) do |f| + form_for(@post, as: :another_post) do |f| concat f.submit end - expected = whole_form('/posts/123', 'edit_another_post', 'edit_another_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_another_post', 'edit_another_post', method: 'patch') do "<input name='commit' type='submit' value='Update your Post' />" end @@ -1629,7 +1766,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do "<input name='post[comment][body]' type='text' id='post_comment_body' value='Hello World' />" end @@ -1637,14 +1774,14 @@ class FormHelperTest < ActionView::TestCase end def test_nested_fields_for_with_nested_collections - form_for(@post, :as => 'post[]') do |f| + form_for(@post, as: 'post[]') do |f| concat f.text_field(:title) concat f.fields_for('comment[]', @comment) { |c| concat c.text_field(:name) } end - expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do + expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', method: 'patch') do "<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 @@ -1653,14 +1790,14 @@ class FormHelperTest < ActionView::TestCase end def test_nested_fields_for_with_index_and_parent_fields - form_for(@post, :index => 1) do |c| + form_for(@post, index: 1) do |c| concat c.text_field(:title) - concat c.fields_for('comment', @comment, :index => 1) { |r| + concat c.fields_for('comment', @comment, index: 1) { |r| concat r.text_field(:name) } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do "<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 @@ -1669,13 +1806,13 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_index_and_nested_fields_for - output_buffer = form_for(@post, :index => 1) do |f| + output_buffer = form_for(@post, index: 1) do |f| concat f.fields_for(:comment, @post) { |c| concat c.text_field(:title) } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do "<input name='post[1][comment][title]' type='text' id='post_1_comment_title' value='Hello World' />" end @@ -1683,13 +1820,13 @@ class FormHelperTest < ActionView::TestCase end def test_nested_fields_for_with_index_on_both - form_for(@post, :index => 1) do |f| - concat f.fields_for(:comment, @post, :index => 5) { |c| + form_for(@post, index: 1) do |f| + concat f.fields_for(:comment, @post, index: 5) { |c| concat c.text_field(:title) } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do "<input name='post[1][comment][5][title]' type='text' id='post_1_comment_5_title' value='Hello World' />" end @@ -1697,13 +1834,13 @@ class FormHelperTest < ActionView::TestCase end def test_nested_fields_for_with_auto_index - form_for(@post, :as => "post[]") do |f| + form_for(@post, as: "post[]") do |f| concat f.fields_for(:comment, @post) { |c| concat c.text_field(:title) } end - expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do + expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', method: 'patch') do "<input name='post[123][comment][title]' type='text' id='post_123_comment_title' value='Hello World' />" end @@ -1712,12 +1849,12 @@ class FormHelperTest < ActionView::TestCase def test_nested_fields_for_with_index_radio_button form_for(@post) do |f| - concat f.fields_for(:comment, @post, :index => 5) { |c| + concat f.fields_for(:comment, @post, index: 5) { |c| concat c.radio_button(:title, "hello") } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do "<input name='post[comment][5][title]' type='radio' id='post_comment_5_title_hello' value='hello' />" end @@ -1725,13 +1862,13 @@ class FormHelperTest < ActionView::TestCase end def test_nested_fields_for_with_auto_index_on_both - form_for(@post, :as => "post[]") do |f| + form_for(@post, as: "post[]") do |f| concat f.fields_for("comment[]", @post) { |c| concat c.text_field(:title) } end - expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do + expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', method: 'patch') do "<input name='post[123][comment][123][title]' type='text' id='post_123_comment_123_title' value='Hello World' />" end @@ -1739,21 +1876,21 @@ class FormHelperTest < ActionView::TestCase end def test_nested_fields_for_with_index_and_auto_index - output_buffer = form_for(@post, :as => "post[]") do |f| - concat f.fields_for(:comment, @post, :index => 5) { |c| + output_buffer = form_for(@post, as: "post[]") do |f| + concat f.fields_for(:comment, @post, index: 5) { |c| concat c.text_field(:title) } end - output_buffer << form_for(@post, :as => :post, :index => 1) do |f| + output_buffer << form_for(@post, as: :post, index: 1) do |f| concat f.fields_for("comment[]", @post) { |c| concat c.text_field(:title) } end - expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', 'patch') do + expected = whole_form('/posts/123', 'edit_post[]', 'edit_post[]', method: 'patch') do "<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 + end + whole_form('/posts/123', 'edit_post', 'edit_post', method: 'patch') do "<input name='post[1][comment][123][title]' type='text' id='post_1_comment_123_title' value='Hello World' />" end @@ -1770,7 +1907,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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 @@ -1797,7 +1934,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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" />' @@ -1816,7 +1953,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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" />' @@ -1830,12 +1967,12 @@ class FormHelperTest < ActionView::TestCase form_for(@post) do |f| concat f.text_field(:title) - concat f.fields_for(:author, :include_id => false) { |af| + concat f.fields_for(:author, include_id: false) { |af| af.text_field(:name) } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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 @@ -1846,14 +1983,14 @@ class FormHelperTest < ActionView::TestCase def test_nested_fields_for_with_an_existing_record_on_a_nested_attributes_one_to_one_association_with_disabled_hidden_id_inherited @post.author = Author.new(321) - form_for(@post, :include_id => false) do |f| + form_for(@post, include_id: false) do |f| concat f.text_field(:title) concat f.fields_for(:author) { |af| af.text_field(:name) } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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 @@ -1864,14 +2001,14 @@ class FormHelperTest < ActionView::TestCase def test_nested_fields_for_with_an_existing_record_on_a_nested_attributes_one_to_one_association_with_disabled_hidden_id_override @post.author = Author.new(321) - form_for(@post, :include_id => false) do |f| + form_for(@post, include_id: false) do |f| concat f.text_field(:title) - concat f.fields_for(:author, :include_id => true) { |af| + concat f.fields_for(:author, include_id: true) { |af| af.text_field(:name) } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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" />' @@ -1891,7 +2028,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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]" type="text" value="author #321" />' @@ -1912,7 +2049,7 @@ class FormHelperTest < ActionView::TestCase end end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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" />' + @@ -1933,13 +2070,13 @@ class FormHelperTest < ActionView::TestCase concat af.text_field(:name) } @post.comments.each do |comment| - concat f.fields_for(:comments, comment, :include_id => false) { |cf| + concat f.fields_for(:comments, comment, include_id: false) { |cf| concat cf.text_field(:name) } end end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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" />' + @@ -1954,7 +2091,7 @@ class FormHelperTest < ActionView::TestCase @post.comments = Array.new(2) { |id| Comment.new(id + 1) } @post.author = Author.new(321) - form_for(@post, :include_id => false) do |f| + form_for(@post, include_id: false) do |f| concat f.text_field(:title) concat f.fields_for(:author) { |af| concat af.text_field(:name) @@ -1966,7 +2103,7 @@ class FormHelperTest < ActionView::TestCase end end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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" />' + @@ -1980,9 +2117,9 @@ class FormHelperTest < ActionView::TestCase @post.comments = Array.new(2) { |id| Comment.new(id + 1) } @post.author = Author.new(321) - form_for(@post, :include_id => false) do |f| + form_for(@post, include_id: false) do |f| concat f.text_field(:title) - concat f.fields_for(:author, :include_id => true) { |af| + concat f.fields_for(:author, include_id: true) { |af| concat af.text_field(:name) } @post.comments.each do |comment| @@ -1992,7 +2129,7 @@ class FormHelperTest < ActionView::TestCase end end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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" />' + @@ -2015,7 +2152,7 @@ class FormHelperTest < ActionView::TestCase end end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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" />' + @@ -2039,7 +2176,7 @@ class FormHelperTest < ActionView::TestCase end end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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]" type="text" value="comment #1" />' + @@ -2062,7 +2199,7 @@ class FormHelperTest < ActionView::TestCase end end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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" />' @@ -2083,7 +2220,7 @@ class FormHelperTest < ActionView::TestCase end end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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" />' + @@ -2101,7 +2238,7 @@ class FormHelperTest < ActionView::TestCase end end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<input name="post[title]" type="text" id="post_title" value="Hello World" />' end @@ -2118,7 +2255,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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" />' + @@ -2139,7 +2276,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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" />' + @@ -2161,7 +2298,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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" />' + @@ -2184,7 +2321,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<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" />' + @@ -2199,12 +2336,12 @@ class FormHelperTest < ActionView::TestCase @post.comments = [] form_for(@post) do |f| - concat f.fields_for(:comments, Comment.new(321), :child_index => 'abc') { |cf| + concat f.fields_for(:comments, Comment.new(321), child_index: 'abc') { |cf| concat cf.text_field(:name) } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + 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]" type="text" value="comment #321" />' + '<input id="post_comments_attributes_abc_id" name="post[comments_attributes][abc][id]" type="hidden" value="321" />' end @@ -2222,12 +2359,12 @@ class FormHelperTest < ActionView::TestCase @post.comments = FakeAssociationProxy.new form_for(@post) do |f| - concat f.fields_for(:comments, Comment.new(321), :child_index => 'abc') { |cf| + concat f.fields_for(:comments, Comment.new(321), child_index: 'abc') { |cf| concat cf.text_field(:name) } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + 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]" type="text" value="comment #321" />' + '<input id="post_comments_attributes_abc_id" name="post[comments_attributes][abc][id]" type="hidden" value="321" />' end @@ -2279,7 +2416,7 @@ class FormHelperTest < ActionView::TestCase @post.comments = [] form_for(@post) do |f| - f.fields_for(:comments, Comment.new(321), :child_index => 'abc') { |cf| + f.fields_for(:comments, Comment.new(321), child_index: 'abc') { |cf| assert_equal cf.index, 'abc' } end @@ -2313,7 +2450,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + 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]" 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" />' + @@ -2340,7 +2477,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do '<input id="post_author_attributes_name" name="post[author_attributes][name]" type="text" value="hash backed author" />' end @@ -2380,7 +2517,7 @@ class FormHelperTest < ActionView::TestCase end def test_fields_for_with_nil_index_option_override - output_buffer = fields_for("post[]", @post, :index => nil) do |f| + output_buffer = fields_for("post[]", @post, index: nil) do |f| concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) @@ -2396,7 +2533,7 @@ class FormHelperTest < ActionView::TestCase end def test_fields_for_with_index_option_override - output_buffer = fields_for("post[]", @post, :index => "abc") do |f| + output_buffer = fields_for("post[]", @post, index: "abc") do |f| concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) @@ -2455,7 +2592,7 @@ class FormHelperTest < ActionView::TestCase end def test_fields_for_object_with_bracketed_name_and_index - output_buffer = fields_for("author[post]", @post, :index => 1) do |f| + output_buffer = fields_for("author[post]", @post, index: 1) do |f| concat f.label(:title) concat f.text_field(:title) end @@ -2470,7 +2607,7 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_and_fields_for - form_for(@post, :as => :post, :html => { :id => 'create-post' }) do |post_form| + form_for(@post, as: :post, html: { id: 'create-post' }) do |post_form| concat post_form.text_field(:title) concat post_form.text_area(:body) @@ -2479,7 +2616,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form('/posts/123', 'create-post', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'create-post', 'edit_post', method: 'patch') do "<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' />" + @@ -2490,7 +2627,7 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_and_fields_for_with_object - form_for(@post, :as => :post, :html => { :id => 'create-post' }) do |post_form| + form_for(@post, as: :post, html: { id: 'create-post' }) do |post_form| concat post_form.text_field(:title) concat post_form.text_area(:body) @@ -2499,7 +2636,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form('/posts/123', 'create-post', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'create-post', 'edit_post', method: 'patch') do "<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' />" @@ -2515,7 +2652,7 @@ class FormHelperTest < ActionView::TestCase } end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do "<input name='post[category][name]' type='text' id='post_category_name' />" end @@ -2533,13 +2670,13 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_labelled_builder - form_for(@post, :builder => LabelledFormBuilder) do |f| + form_for(@post, builder: LabelledFormBuilder) do |f| concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do "<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/>" @@ -2558,7 +2695,7 @@ class FormHelperTest < ActionView::TestCase concat f.check_box(:secret) end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do "<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/>" @@ -2577,7 +2714,7 @@ class FormHelperTest < ActionView::TestCase concat f.text_field(:title) end - expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', :method => 'patch') do + expected = whole_form('/posts/123', 'edit_post_123', 'edit_post', method: 'patch') do "<label for='title'>Title:</label> <input name='post[title]' type='text' id='post_title' value='Hello World' /><br/>" end @@ -2587,7 +2724,7 @@ class FormHelperTest < ActionView::TestCase end def test_fields_for_with_labelled_builder - output_buffer = fields_for(:post, @post, :builder => LabelledFormBuilder) do |f| + output_buffer = fields_for(:post, @post, builder: LabelledFormBuilder) do |f| concat f.text_field(:title) concat f.text_area(:body) concat f.check_box(:secret) @@ -2604,7 +2741,7 @@ class FormHelperTest < ActionView::TestCase def test_form_for_with_labelled_builder_with_nested_fields_for_without_options_hash klass = nil - form_for(@post, :builder => LabelledFormBuilder) do |f| + form_for(@post, builder: LabelledFormBuilder) do |f| f.fields_for(:comments, Comment.new) do |nested_fields| klass = nested_fields.class '' @@ -2617,8 +2754,8 @@ class FormHelperTest < ActionView::TestCase def test_form_for_with_labelled_builder_with_nested_fields_for_with_options_hash klass = nil - form_for(@post, :builder => LabelledFormBuilder) do |f| - f.fields_for(:comments, Comment.new, :index => 'foo') do |nested_fields| + form_for(@post, builder: LabelledFormBuilder) do |f| + f.fields_for(:comments, Comment.new, index: 'foo') do |nested_fields| klass = nested_fields.class '' end @@ -2630,7 +2767,7 @@ class FormHelperTest < ActionView::TestCase def test_form_for_with_labelled_builder_path path = nil - form_for(@post, :builder => LabelledFormBuilder) do |f| + form_for(@post, builder: LabelledFormBuilder) do |f| path = f.to_partial_path '' end @@ -2643,8 +2780,8 @@ class FormHelperTest < ActionView::TestCase def test_form_for_with_labelled_builder_with_nested_fields_for_with_custom_builder klass = nil - form_for(@post, :builder => LabelledFormBuilder) do |f| - f.fields_for(:comments, Comment.new, :builder => LabelledFormBuilderSubclass) do |nested_fields| + form_for(@post, builder: LabelledFormBuilder) do |f| + f.fields_for(:comments, Comment.new, builder: LabelledFormBuilderSubclass) do |nested_fields| klass = nested_fields.class '' end @@ -2654,36 +2791,36 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_html_options_adds_options_to_form_tag - form_for(@post, :html => {:id => 'some_form', :class => 'some_class'}) do |f| end - expected = whole_form("/posts/123", "some_form", "some_class", 'patch') + form_for(@post, html: { id: 'some_form', class: 'some_class' }) do |f| end + expected = whole_form("/posts/123", "some_form", "some_class", method: "patch") assert_dom_equal expected, output_buffer end def test_form_for_with_string_url_option - form_for(@post, :url => 'http://www.otherdomain.com') do |f| end + form_for(@post, url: 'http://www.otherdomain.com') do |f| end - assert_equal whole_form("http://www.otherdomain.com", 'edit_post_123', 'edit_post', 'patch'), output_buffer + assert_equal whole_form("http://www.otherdomain.com", "edit_post_123", "edit_post", method: "patch"), output_buffer end def test_form_for_with_hash_url_option - form_for(@post, :url => {:controller => 'controller', :action => 'action'}) do |f| end + form_for(@post, url: { controller: 'controller', action: 'action' }) do |f| end assert_equal 'controller', @url_for_options[:controller] assert_equal 'action', @url_for_options[:action] end def test_form_for_with_record_url_option - form_for(@post, :url => @post) do |f| end + form_for(@post, url: @post) do |f| end - expected = whole_form("/posts/123", 'edit_post_123', 'edit_post', 'patch') + expected = whole_form("/posts/123", "edit_post_123", "edit_post", method: "patch") assert_equal expected, output_buffer end def test_form_for_with_existing_object form_for(@post) do |f| end - expected = whole_form("/posts/123", "edit_post_123", "edit_post", 'patch') + expected = whole_form("/posts/123", "edit_post_123", "edit_post", method: "patch") assert_equal expected, output_buffer end @@ -2702,7 +2839,7 @@ class FormHelperTest < ActionView::TestCase @comment.save form_for([@post, @comment]) {} - expected = whole_form(post_comment_path(@post, @comment), "edit_comment_1", "edit_comment", 'patch') + expected = whole_form(post_comment_path(@post, @comment), "edit_comment_1", "edit_comment", method: "patch") assert_dom_equal expected, output_buffer end @@ -2717,7 +2854,7 @@ class FormHelperTest < ActionView::TestCase @comment.save form_for([:admin, @post, @comment]) {} - expected = whole_form(admin_post_comment_path(@post, @comment), "edit_comment_1", "edit_comment", 'patch') + expected = whole_form(admin_post_comment_path(@post, @comment), "edit_comment_1", "edit_comment", method: "patch") assert_dom_equal expected, output_buffer end @@ -2729,15 +2866,15 @@ class FormHelperTest < ActionView::TestCase end def test_form_for_with_existing_object_and_custom_url - form_for(@post, :url => "/super_posts") do |f| end + form_for(@post, url: "/super_posts") do |f| end - expected = whole_form("/super_posts", "edit_post_123", "edit_post", 'patch') + expected = whole_form("/super_posts", "edit_post_123", "edit_post", method: "patch") assert_equal expected, output_buffer end def test_form_for_with_default_method_as_patch form_for(@post) {} - expected = whole_form("/posts/123", "edit_post_123", "edit_post", "patch") + expected = whole_form("/posts/123", "edit_post_123", "edit_post", method: "patch") assert_dom_equal expected, output_buffer end @@ -2798,14 +2935,10 @@ class FormHelperTest < ActionView::TestCase txt << %{ method="#{method}">} end - def whole_form(action = "/", id = nil, html_class = nil, options = nil) + def whole_form(action = "/", id = nil, html_class = nil, options = {}) contents = block_given? ? yield : "" - if options.is_a?(Hash) - method, remote, multipart = options.values_at(:method, :remote, :multipart) - else - method = options - end + method, remote, multipart = options.values_at(:method, :remote, :multipart) form_text(action, id, html_class, remote, multipart, method) + hidden_fields(method) + contents + "</form>" end diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb index 56919dc592..1eed8adb62 100644 --- a/actionpack/test/template/javascript_helper_test.rb +++ b/actionpack/test/template/javascript_helper_test.rb @@ -27,8 +27,8 @@ class JavaScriptHelperTest < ActionView::TestCase assert_equal %(This \\"thing\\" is really\\n netos\\'), escape_javascript(%(This "thing" is really\n netos')) 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 %(unicode 
 newline), escape_javascript(%(unicode \342\200\250 newline).force_encoding(Encoding::UTF_8).encode!) + assert_equal %(unicode 
 newline), escape_javascript(%(unicode \342\200\251 newline).force_encoding(Encoding::UTF_8).encode!) assert_equal %(dont <\\/close> tags), j(%(dont </close> tags)) end diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb index 9c49438f6a..ab84bccb56 100644 --- a/actionpack/test/template/record_tag_helper_test.rb +++ b/actionpack/test/template/record_tag_helper_test.rb @@ -41,6 +41,12 @@ class RecordTagHelperTest < ActionView::TestCase assert_dom_equal expected, actual end + def test_content_tag_for_with_array_css_class + expected = %(<tr class="record_tag_post special odd" id="record_tag_post_45"></tr>) + actual = content_tag_for(:tr, @post, class: ["special", "odd"]) + assert_dom_equal expected, actual + end + def test_content_tag_for_with_prefix_and_extra_html_options expected = %(<tr class="archived_record_tag_post special" id="archived_record_tag_post_45" style='background-color: #f0f0f0'></tr>) actual = content_tag_for(:tr, @post, :archived, class: "special", style: "background-color: #f0f0f0") diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb index c7231d9cd5..acd002ce73 100644 --- a/actionpack/test/template/test_case_test.rb +++ b/actionpack/test/template/test_case_test.rb @@ -329,6 +329,27 @@ module ActionView assert_template partial: '_partial', locals: { 'second' => '2' } end + test 'raises descriptive error message when template was not rendered' do + controller.controller_path = "test" + render(template: "test/hello_world_with_partial") + e = assert_raise ActiveSupport::TestCase::Assertion do + assert_template partial: 'i_was_never_rendered', locals: { 'did_not' => 'happen' } + end + assert_match "i_was_never_rendered to be rendered but it was not.", e.message + assert_match 'Expected ["/test/partial"] to include "i_was_never_rendered"', e.message + end + + test 'specifying locals works when the partial is inside a directory with underline prefix' do + controller.controller_path = "test" + render(template: 'test/render_partial_inside_directory') + assert_template partial: 'test/_directory/_partial_with_locales', locals: { 'name' => 'Jane' } + end + + test 'specifying locals works when the partial is inside a directory without underline prefix' do + controller.controller_path = "test" + render(template: 'test/render_partial_inside_directory') + assert_template partial: 'test/_directory/partial_with_locales', locals: { 'name' => 'Jane' } + end end module AHelperWithInitialize diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 412d13bb2b..1b2234f4e2 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -95,8 +95,8 @@ class TextHelperTest < ActionView::TestCase end def test_truncate_multibyte - assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding('UTF-8'), - truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding('UTF-8'), :length => 10) + assert_equal "\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 ...".force_encoding(Encoding::UTF_8), + truncate("\354\225\204\353\246\254\353\236\221 \354\225\204\353\246\254 \354\225\204\353\235\274\353\246\254\354\230\244".force_encoding(Encoding::UTF_8), :length => 10) end def test_truncate_does_not_modify_the_options_hash @@ -293,7 +293,7 @@ class TextHelperTest < ActionView::TestCase end def test_excerpt_with_utf8 - assert_equal("...\357\254\203ciency could not be...".force_encoding('UTF-8'), excerpt("That's why e\357\254\203ciency could not be helped".force_encoding('UTF-8'), 'could', :radius => 8)) + assert_equal("...\357\254\203ciency could not be...".force_encoding(Encoding::UTF_8), excerpt("That's why e\357\254\203ciency could not be helped".force_encoding(Encoding::UTF_8), 'could', :radius => 8)) end def test_excerpt_does_not_modify_the_options_hash diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index ba65349b6a..5d87c96605 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -437,6 +437,12 @@ class UrlHelperTest < ActiveSupport::TestCase ActionDispatch::Request.new(env) end + def test_current_page_with_http_head_method + @request = request_for_url("/", :method => :head) + assert current_page?(url_hash) + assert current_page?("http://www.example.com/") + end + def test_current_page_with_simple_url @request = request_for_url("/") assert current_page?(url_hash) |