Merge branch 'master' into laurocaetano-fix_send_file

* master: (536 commits)
  doc, API example on how to use `Model#exists?` with multiple IDs. [ci skip]
  Restore DATABASE_URL even if it's nil in connection_handler test
  [ci skip] - error_messages_for has been deprecated since 2.3.8 - lets reduce any confusion for users
  Ensure Active Record connection consistency
  Revert "ask the fixture set for the sql statements"
  Check `respond_to` before delegation due to: d781caaf31
  Adding Hash#compact and Hash#compact! methods
  MySQL version 4.1 was EOL on December 31, 2009 We should at least recommend modern versions of MySQL to users.
  clear cache on body close so that cache remains during rendering
  add a more restricted codepath for templates fixes #13390
  refactor generator tests to use block form of Tempfile
  Fix typo [ci skip]
  Move finish_template as the last public method in the generator
  Minor typos fix [ci skip]
  make `change_column_null` reversible. Closes #13576.
  create/drop test and development databases only if RAILS_ENV is nil
  Revert "Speedup String#to"
  typo fix in test name. [ci skip].
  `core_ext/string/access.rb` test what we are documenting.
  Fix typo in image_tag documentation
  ...

Conflicts:
	actionpack/CHANGELOG.md
This commit is contained in:
Aaron Patterson 2014-01-10 11:00:30 -08:00
commit caa981d881
494 changed files with 9632 additions and 3411 deletions

@ -5,8 +5,9 @@ before_install:
rvm:
- 1.9.3
- 2.0.0
- rbx-2.2.1
- jruby-19mode
- 2.1.0
- rbx
- jruby
env:
- "GEM=railties"
- "GEM=ap,am,amo,as,av"
@ -16,8 +17,9 @@ env:
- "GEM=ar:postgresql"
matrix:
allow_failures:
- rvm: rbx-2.2.1
- rvm: jruby-19mode
- rvm: rbx
- rvm: jruby
fast_finish: true
notifications:
email: false
irc:
@ -31,3 +33,6 @@ notifications:
rooms:
- secure: "YA1alef1ESHWGFNVwvmVGCkMe4cUy4j+UcNvMUESraceiAfVyRMAovlQBGs6\n9kBRm7DHYBUXYC2ABQoJbQRLDr/1B5JPf/M8+Qd7BKu8tcDC03U01SMHFLpO\naOs/HLXcDxtnnpL07tGVsm0zhMc5N8tq4/L3SHxK7Vi+TacwQzI="
bundler_args: --path vendor/bundle --without test
services:
- memcached

@ -12,7 +12,6 @@ gem 'bcrypt-ruby', '~> 3.1.2'
gem 'jquery-rails', '~> 2.2.0'
gem 'turbolinks'
gem 'coffee-rails', '~> 4.0.0'
gem 'arel', github: 'rails/arel', branch: 'master'
# This needs to be with require false to avoid
# it being automatically loaded by sprockets
@ -37,9 +36,9 @@ group :test do
gem 'ruby-prof', '~> 0.11.2'
end
platforms :mri_19, :mri_20 do
gem 'debugger'
end
# platforms :mri_19, :mri_20 do
# gem 'debugger'
# end
gem 'benchmark-ips'
end

@ -1 +1 @@
4.1.0.beta
4.1.0.beta1

@ -110,7 +110,7 @@ what to do in case anything goes wrong:
$ rake all:build
$ git commit -am'updating RAILS_VERSION'
$ git tag -m'tagging rc release' v3.0.10.rc1
$ git tag -m 'v3.0.10.rc1 release' v3.0.10.rc1
$ git push
$ git push --tags
$ for i in $(ls pkg); do gem push $i; done

@ -1,3 +1,28 @@
* Add mailer previews feature based on 37 Signals mail_view gem
*Andrew White*
* Calling `mail()` without arguments serves as getter for the current mail
message and keeps previously set headers.
Fixes #13090.
Example:
class MailerWithCallback < ActionMailer::Base
after_action :a_callback
def welcome
mail subject: "subject", to: ["joe@example.com"]
end
def a_callback
mail # => returns the current mail message
end
end
*Yves Senn*
* Instrument the generation of Action Mailer messages. The time it takes to
generate a message is written to the log.

@ -1,4 +1,4 @@
Copyright (c) 2004-2013 David Heinemeier Hansson
Copyright (c) 2004-2014 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

@ -1,5 +1,5 @@
#--
# Copyright (c) 2004-2013 David Heinemeier Hansson
# Copyright (c) 2004-2014 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@ -41,6 +41,8 @@ module ActionMailer
autoload :Base
autoload :DeliveryMethods
autoload :MailHelper
autoload :Preview
autoload :Previews, 'action_mailer/preview'
autoload :TestCase
autoload :TestHelper
end

@ -50,7 +50,7 @@ module ActionMailer
#
# * <tt>mail</tt> - Allows you to specify email to be sent.
#
# The hash passed to the mail method allows you to specify any header that a Mail::Message
# The hash passed to the mail method allows you to specify any header that a <tt>Mail::Message</tt>
# will accept (any valid Email header including optional fields).
#
# The mail method, if not passed a block, will inspect your views and send all the views with
@ -229,7 +229,7 @@ module ActionMailer
# An interceptor class must implement the <tt>:delivering_email(message)</tt> method which will be
# called before the email is sent, allowing you to make modifications to the email before it hits
# the delivery agents. Your class should make any needed modifications directly to the passed
# in Mail::Message instance.
# in <tt>Mail::Message</tt> instance.
#
# = Default Hash
#
@ -308,6 +308,28 @@ module ActionMailer
# Note that unless you have a specific reason to do so, you should prefer using before_action
# rather than after_action in your ActionMailer classes so that headers are parsed properly.
#
# = Previewing emails
#
# You can preview your email templates visually by adding a mailer preview file to the
# <tt>ActionMailer::Base.preview_path</tt>. Since most emails do something interesting
# with database data, you'll need to write some scenarios to load messages with fake data:
#
# class NotifierPreview < ActionMailer::Preview
# def welcome
# Notifier.welcome(User.first)
# end
# end
#
# Methods must return a <tt>Mail::Message</tt> object which can be generated by calling the mailer
# method without the additional <tt>deliver</tt>. The location of the mailer previews
# directory can be configured using the <tt>preview_path</tt> option which has a default
# of <tt>test/mailers/previews</tt>:
#
# config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
#
# An overview of all previews is accessible at <tt>http://localhost:3000/rails/mailers</tt>
# on a running development server instance.
#
# = Configuration options
#
# These options are specified on the class level, like
@ -317,7 +339,7 @@ module ActionMailer
# per the above section.
#
# * <tt>logger</tt> - the logger is used for generating information on the mailing run if available.
# Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
# Can be set to +nil+ for no logging. Compatible with both Ruby's own +Logger+ and Log4r loggers.
#
# * <tt>smtp_settings</tt> - Allows detailed configuration for <tt>:smtp</tt> delivery method:
# * <tt>:address</tt> - Allows you to use a remote mail server. Just change it from its default
@ -335,8 +357,9 @@ module ActionMailer
# and starts to use it.
# * <tt>:openssl_verify_mode</tt> - When using TLS, you can set how OpenSSL checks the certificate. This is
# really useful if you need to validate a self-signed and/or a wildcard certificate. You can use the name
# of an OpenSSL verify constant ('none', 'peer', 'client_once', 'fail_if_no_peer_cert') or directly the
# constant (OpenSSL::SSL::VERIFY_NONE, OpenSSL::SSL::VERIFY_PEER, ...).
# of an OpenSSL verify constant (<tt>'none'</tt>, <tt>'peer'</tt>, <tt>'client_once'</tt>,
# <tt>'fail_if_no_peer_cert'</tt>) or directly the constant (<tt>OpenSSL::SSL::VERIFY_NONE</tt>,
# <tt>OpenSSL::SSL::VERIFY_PEER</tt>, ...).
#
# * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
# * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
@ -351,7 +374,7 @@ module ActionMailer
#
# * <tt>delivery_method</tt> - Defines a delivery method. Possible values are <tt>:smtp</tt> (default),
# <tt>:sendmail</tt>, <tt>:test</tt>, and <tt>:file</tt>. Or you may provide a custom delivery method
# object e.g. MyOwnDeliveryMethodClass. See the Mail gem documentation on the interface you need to
# object e.g. +MyOwnDeliveryMethodClass+. See the Mail gem documentation on the interface you need to
# implement for a custom delivery agent.
#
# * <tt>perform_deliveries</tt> - Determines whether emails are actually sent from Action Mailer when you
@ -362,6 +385,7 @@ module ActionMailer
# <tt>delivery_method :test</tt>. Most useful for unit and functional testing.
class Base < AbstractController::Base
include DeliveryMethods
include Previews
abstract!
@ -373,6 +397,8 @@ class Base < AbstractController::Base
include AbstractController::AssetPaths
include AbstractController::Callbacks
include ActionView::Layouts
PROTECTED_IVARS = AbstractController::Rendering::DEFAULT_PROTECTED_INSTANCE_VARIABLES + [:@_action_has_layout]
def _protected_ivars # :nodoc:
@ -404,7 +430,7 @@ def register_interceptors(*interceptors)
# Register an Observer which will be notified when mail is delivered.
# Either a class or a string can be passed in as the Observer. If a string is passed in
# it will be +constantize+d.
# it will be <tt>constantize</tt>d.
def register_observer(observer)
delivery_observer = (observer.is_a?(String) ? observer.constantize : observer)
Mail.register_observer(delivery_observer)
@ -462,11 +488,11 @@ def receive(raw_mail)
end
end
# Wraps an email delivery inside of ActiveSupport::Notifications instrumentation.
# Wraps an email delivery inside of <tt>ActiveSupport::Notifications</tt> instrumentation.
#
# This method is actually called by the Mail::Message object itself
# through a callback when you call +:deliver+ on the Mail::Message,
# calling +deliver_mail+ directly and passing a Mail::Message will do
# This method is actually called by the <tt>Mail::Message</tt> object itself
# through a callback when you call <tt>:deliver</tt> on the <tt>Mail::Message</tt>,
# calling +deliver_mail+ directly and passing a <tt>Mail::Message</tt> will do
# nothing except tell the logger you sent the email.
def deliver_mail(mail) #:nodoc:
ActiveSupport::Notifications.instrument("deliver.action_mailer") do |payload|
@ -542,18 +568,18 @@ def mailer_name
self.class.mailer_name
end
# Allows you to pass random and unusual headers to the new Mail::Message
# Allows you to pass random and unusual headers to the new <tt>Mail::Message</tt>
# object which will add them to itself.
#
# headers['X-Special-Domain-Specific-Header'] = "SecretValue"
#
# You can also pass a hash into headers of header field names and values,
# which will then be set on the Mail::Message object:
# which will then be set on the <tt>Mail::Message</tt> object:
#
# headers 'X-Special-Domain-Specific-Header' => "SecretValue",
# 'In-Reply-To' => incoming.message_id
#
# The resulting Mail::Message will have the following in its header:
# The resulting <tt>Mail::Message</tt> will have the following in its header:
#
# X-Special-Domain-Specific-Header: SecretValue
def headers(args = nil)
@ -642,8 +668,8 @@ def attachments
# templates in the view paths using by default the mailer name and the
# method name that it is being called from, it will then create parts for
# each of these templates intelligently, making educated guesses on correct
# content type and sequence, and return a fully prepared Mail::Message
# ready to call +:deliver+ on to send.
# content type and sequence, and return a fully prepared <tt>Mail::Message</tt>
# ready to call <tt>:deliver</tt> on to send.
#
# For example:
#
@ -689,6 +715,8 @@ def attachments
# end
#
def mail(headers = {}, &block)
return @_message if @_mail_was_called && headers.blank? && !block
@_mail_was_called = true
m = @_message

@ -0,0 +1,73 @@
require 'active_support/descendants_tracker'
module ActionMailer
module Previews #:nodoc:
extend ActiveSupport::Concern
included do
# Set the location of mailer previews through app configuration:
#
# config.action_mailer.preview_path = "#{Rails.root}/lib/mailer_previews"
#
class_attribute :preview_path, instance_writer: false
end
end
class Preview
extend ActiveSupport::DescendantsTracker
class << self
# Returns all mailer preview classes
def all
load_previews if descendants.empty?
descendants
end
# Returns the mail object for the given email name
def call(email)
preview = self.new
preview.public_send(email)
end
# Returns all of the available email previews
def emails
public_instance_methods(false).map(&:to_s).sort
end
# Returns true if the email exists
def email_exists?(email)
emails.include?(email)
end
# Returns true if the preview exists
def exists?(preview)
all.any?{ |p| p.preview_name == preview }
end
# Find a mailer preview by its underscored class name
def find(preview)
all.find{ |p| p.preview_name == preview }
end
# Returns the underscored name of the mailer preview without the suffix
def preview_name
name.sub(/Preview$/, '').underscore
end
protected
def load_previews #:nodoc:
if preview_path?
Dir["#{preview_path}/**/*_preview.rb"].each{ |file| require_dependency file }
end
end
def preview_path #:nodoc:
Base.preview_path
end
def preview_path? #:nodoc:
Base.preview_path?
end
end
end
end

@ -19,6 +19,10 @@ class Railtie < Rails::Railtie # :nodoc:
options.javascripts_dir ||= paths["public/javascripts"].first
options.stylesheets_dir ||= paths["public/stylesheets"].first
if Rails.env.development?
options.preview_path ||= defined?(Rails.root) ? "#{Rails.root}/test/mailers/previews" : nil
end
# make sure readers methods get compiled
options.asset_host ||= app.config.asset_host
options.relative_url_root ||= app.config.relative_url_root
@ -40,5 +44,11 @@ class Railtie < Rails::Railtie # :nodoc:
config.compile_methods! if config.respond_to?(:compile_methods!)
end
end
config.after_initialize do
if ActionMailer::Base.preview_path?
ActiveSupport::Dependencies.autoload_paths << ActionMailer::Base.preview_path
end
end
end
end

@ -1,7 +1,7 @@
module ActionMailer
# Returns the version of the currently loaded ActionMailer as a Gem::Version
def self.version
Gem::Version.new "4.1.0.beta"
Gem::Version.new "4.1.0.beta1"
end
module VERSION #:nodoc:

@ -20,6 +20,9 @@
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
# Disable available locale checks to avoid warnings running the test suite.
I18n.enforce_available_locales = false
# Bogus template processors
ActionView::Template.register_template_handler :haml, lambda { |template| "Look its HAML!".inspect }
ActionView::Template.register_template_handler :bak, lambda { |template| "Lame backup".inspect }

@ -671,6 +671,27 @@ def welcome
assert_equal ["robert.pankowecki@gmail.com"], DefaultFromMailer.welcome.from
end
test "mail() without arguments serves as getter for the current mail message" do
class MailerWithCallback < ActionMailer::Base
after_action :a_callback
def welcome
headers('X-Special-Header' => 'special indeed!')
mail subject: "subject", body: "hello world", to: ["joe@example.com"]
end
def a_callback
mail.to << "jane@example.com"
end
end
mail = MailerWithCallback.welcome
assert_equal "subject", mail.subject
assert_equal ["joe@example.com", "jane@example.com"], mail.to
assert_equal "hello world", mail.body.encoded.strip
assert_equal "special indeed!", mail["X-Special-Header"].to_s
end
protected
# Execute the block setting the given values and restoring old values after

@ -15,9 +15,6 @@ def mail_with_i18n_subject(recipient)
end
end
# Emulate AV railtie
ActionController::Base.superclass.send(:include, ActionView::Layouts)
class TestController < ActionController::Base
def send_mail
I18nTestMailer.mail_with_i18n_subject("test@localhost").deliver

@ -4,6 +4,199 @@
*Alessandro Diaferia*
* Allow an absolute controller path inside a module scope. Fixes #12777.
Example:
namespace :foo do
# will route to BarController without the namespace.
get '/special', to: '/bar#index'
end
* Unique the segment keys array for non-optimized url helpers
In Rails 3.2 you only needed pass an argument for dynamic segment once so
unique the segment keys array to match the number of args. Since the number
of args is less than required parts the non-optimized code path is selected.
This means to benefit from optimized url generation the arg needs to be
specified as many times as it appears in the path.
Fixes #12808.
*Andrew White*
* Show full route constraints in error message
When an optimized helper fails to generate, show the full route constraints
in the error message. Previously it would only show the contraints that were
required as part of the path.
Fixes #13592.
*Andrew White*
* Use a custom route visitor for optimized url generation. Fixes #13349.
*Andrew White*
* Allow engine root relative redirects using an empty string.
Example:
# application routes.rb
mount BlogEngine => '/blog'
# engine routes.rb
get '/welcome' => redirect('')
This now redirects to the path `/blog`, whereas before it would redirect
to the application root path. In the case of a path redirect or a custom
redirect if the path returned contains a host then the path is treated as
absolute. Similarly for option redirects, if the options hash returned
contains a `:host` or `:domain` key then the path is treated as absolute.
Fixes #7977.
*Andrew White*
* Fix `Encoding::CompatibilityError` when public path is UTF-8
In #5337 we forced the path encoding to ASCII-8BIT to prevent static file handling
from blowing up before an application has had chance to deal with possibly invalid
urls. However this has a negative side effect of making it an incompatible encoding
if the application's public path has UTF-8 characters in it.
To work around the problem we check to see if the path has a valid encoding once
it has been unescaped. If it is not valid then we can return early since it will
not match any file anyway.
Fixes #13518.
*Andrew White*
* `ActionController::Parameters#permit!` permits hashes in array values.
*Xavier Noria*
* Converts hashes in arrays of unfiltered params to unpermitted params.
Fixes #13382.
*Xavier Noria*
* New config option to opt out of params "deep munging" that was used to
address security vulnerability CVE-2013-0155. In your app config:
config.action_dispatch.perform_deep_munge = false
Take care to understand the security risk involved before disabling this.
[Read more.](https://groups.google.com/forum/#!topic/rubyonrails-security/t1WFuuQyavI)
*Bernard Potocki*
* `rake routes` shows routes defined under assets prefix.
*Ryunosuke SATO*
* Extend cross-site request forgery (CSRF) protection to GET requests with
JavaScript responses, protecting apps from cross-origin `<script>` tags.
*Jeremy Kemper*
* Fix generating a path for engine inside a resources block.
Fixes #8533.
*Piotr Sarnacki*
* Add `Mime::Type.register "text/vcard", :vcf` to the default list of mime types.
*DHH*
* Remove deprecated `ActionController::RecordIdentifier`, use
`ActionView::RecordIdentifier` instead.
*kennyj*
* Fix regression when using `ActionView::Helpers::TranslationHelper#translate` with
`options[:raise]`.
This regression was introduced at ec16ba75a5493b9da972eea08bae630eba35b62f.
*Shota Fukumori (sora_h)*
* Introducing Variants
We often want to render different html/json/xml templates for phones,
tablets, and desktop browsers. Variants make it easy.
The request variant is a specialization of the request format, like `:tablet`,
`:phone`, or `:desktop`.
You can set the variant in a `before_action`:
request.variant = :tablet if request.user_agent =~ /iPad/
Respond to variants in the action just like you respond to formats:
respond_to do |format|
format.html do |html|
html.tablet # renders app/views/projects/show.html+tablet.erb
html.phone { extra_setup; render ... }
end
end
Provide separate templates for each format and variant:
app/views/projects/show.html.erb
app/views/projects/show.html+tablet.erb
app/views/projects/show.html+phone.erb
You can also simplify the variants definition using the inline syntax:
respond_to do |format|
format.js { render "trash" }
format.html.phone { redirect_to progress_path }
format.html.none { render "trash" }
end
Variants also support common `any`/`all` block that formats have.
It works for both inline:
respond_to do |format|
format.html.any { render text: "any" }
format.html.phone { render text: "phone" }
end
and block syntax:
respond_to do |format|
format.html do |variant|
variant.any(:tablet, :phablet){ render text: "any" }
variant.phone { render text: "phone" }
end
end
*Łukasz Strzałkowski*
* Fix render of localized templates without an explicit format using wrong
content header and not passing correct formats to template due to the
introduction of the `NullType` for mimes.
Templates like `hello.it.erb` were subject to this issue.
Fixes #13064.
*Angelo Capilleri*, *Carlos Antonio da Silva*
* Try to escape each part of a url correctly when using a redirect route.
Fixes #13110.
*Andrew White*
* Better error message for typos in assert_response argument.
When the response type argument to `assert_response` is not a known
@ -27,9 +220,7 @@
* Add `session#fetch` method
fetch behaves similarly to [Hash#fetch](http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-fetch),
with the exception that the returned value is always saved into the session.
fetch behaves like [Hash#fetch](http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-fetch).
It returns a value from the hash for the given key.
If the key cant be found, there are several options:

@ -1,4 +1,4 @@
Copyright (c) 2004-2013 David Heinemeier Hansson
Copyright (c) 2004-2014 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

@ -23,7 +23,7 @@
s.add_dependency 'rack', '~> 1.5.2'
s.add_dependency 'rack-test', '~> 0.6.2'
s.add_dependency 'actionview', version
s.add_development_dependency 'actionview', version
s.add_development_dependency 'activemodel', version
end

@ -23,7 +23,17 @@ def #{sym}(*args, &block) # def html(*args, &block)
protected
def method_missing(symbol, &block)
mime_constant = Mime.const_get(symbol.upcase)
const_name = symbol.upcase
unless Mime.const_defined?(const_name)
raise NoMethodError, "To respond to a custom format, register it as a MIME type first: " \
"http://guides.rubyonrails.org/action_controller_overview.html#restful-downloads. " \
"If you meant to respond to a variant like :tablet or :phone, not a custom format, " \
"be sure to nest your variant response within a format response: " \
"format.html { |html| html.tablet { ... } }"
end
mime_constant = Mime.const_get(const_name)
if Mime::SET.include?(mime_constant)
AbstractController::Collector.generate_method_for_mime(mime_constant)

@ -1,5 +1,6 @@
require 'active_support/concern'
require 'active_support/core_ext/class/attribute'
require 'action_view/view_paths'
require 'set'
module AbstractController
@ -13,6 +14,7 @@ def initialize(message = nil)
module Rendering
extend ActiveSupport::Concern
include ActionView::ViewPaths
# Normalize arguments, options and then delegates render_to_body and
# sticks the result in self.response_body.
@ -20,7 +22,7 @@ module Rendering
def render(*args, &block)
options = _normalize_render(*args, &block)
self.response_body = render_to_body(options)
_process_format(rendered_format)
_process_format(rendered_format) if rendered_format
self.response_body
end
@ -45,7 +47,7 @@ def render_to_string(*args, &block)
def render_to_body(options = {})
end
# Return Content-Type of rendered content
# Returns Content-Type of rendered content
# :api: public
def rendered_format
Mime::TEXT
@ -102,6 +104,8 @@ def _process_format(format)
# :api: private
def _normalize_render(*args, &block)
options = _normalize_args(*args, &block)
#TODO: remove defined? when we restore AP <=> AV dependency
options[:variant] = request.variant if defined?(request) && request.variant.present?
_normalize_options(options)
options
end

@ -50,7 +50,7 @@ def self.eager_load!
end
# Common Active Support usage in Action Controller
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/load_error'
require 'active_support/core_ext/module/attr_internal'
require 'active_support/core_ext/name_error'

@ -1,21 +1,8 @@
require 'action_view'
require "action_controller/log_subscriber"
require "action_controller/metal/params_wrapper"
module ActionController
# The <tt>metal</tt> anonymous class was introduced to solve issue with including modules in <tt>ActionController::Base</tt>.
# Modules needs to be included in particluar order. First we need to have <tt>AbstractController::Rendering</tt> included,
# next we should include actuall implementation which would be for example <tt>ActionView::Rendering</tt> and after that
# <tt>ActionController::Rendering</tt>. This order must be preserved and as we want to have middle module included dynamicaly
# <tt>metal</tt> class was introduced. It has <tt>AbstractController::Rendering</tt> included and is parent class of
# <tt>ActionController::Base</tt> which includes <tt>ActionController::Rendering</tt>. If we include <tt>ActionView::Rendering</tt>
# beetween them to perserve the required order, we can simply do this by:
#
# ActionController::Base.superclass.send(:include, ActionView::Rendering)
#
metal = Class.new(Metal) do
include AbstractController::Rendering
end
# Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed
# on request and then either it renders a template or redirects to another action. An action is defined as a public method
# on the controller, which will automatically be made accessible to the web-server through \Rails Routes.
@ -99,7 +86,7 @@ module ActionController
# or you can remove the entire session with +reset_session+.
#
# Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted.
# This prevents the user from tampering with the session but also allows him to see its contents.
# This prevents the user from tampering with the session but also allows them to see its contents.
#
# Do not put secret information in cookie-based sessions!
#
@ -174,7 +161,7 @@ module ActionController
# render action: "overthere" # won't be called if monkeys is nil
# end
#
class Base < metal
class Base < Metal
abstract!
# We document the request and response methods here because albeit they are
@ -214,6 +201,7 @@ def self.without_modules(*modules)
end
MODULES = [
AbstractController::Rendering,
AbstractController::Translation,
AbstractController::AssetPaths,
@ -221,6 +209,7 @@ def self.without_modules(*modules)
HideActions,
UrlFor,
Redirecting,
ActionView::Layouts,
Rendering,
Renderers::All,
ConditionalGet,

@ -1,6 +1,6 @@
module ActionController
module Head
# Return a response that has no content (merely headers). The options
# Returns a response that has no content (merely headers). The options
# argument is interpreted to be a hash of header names and values.
# This allows you to easily return a response that consists only of
# significant headers:

@ -67,7 +67,7 @@ def redirect_to(*args)
private
# A hook invoked everytime a before callback is halted.
# A hook invoked every time a before callback is halted.
def halted_callback_hook(filter)
ActiveSupport::Notifications.instrument("halted_callback.action_controller", :filter => filter)
end

@ -48,7 +48,7 @@ module Live
# the server will receive a +Last-Event-ID+ header with value equal to +id+.
#
# After setting an option in the constructor of the SSE object, all future
# SSEs sent accross the stream will use those options unless overridden.
# SSEs sent across the stream will use those options unless overridden.
#
# Example Usage:
#

@ -181,6 +181,61 @@ def clear_respond_to
# end
# end
#
# Formats can have different variants.
#
# The request variant is a specialization of the request format, like <tt>:tablet</tt>,
# <tt>:phone</tt>, or <tt>:desktop</tt>.
#
# We often want to render different html/json/xml templates for phones,
# tablets, and desktop browsers. Variants make it easy.
#
# You can set the variant in a +before_action+:
#
# request.variant = :tablet if request.user_agent =~ /iPad/
#
# Respond to variants in the action just like you respond to formats:
#
# respond_to do |format|
# format.html do |variant|
# variant.tablet # renders app/views/projects/show.html+tablet.erb
# variant.phone { extra_setup; render ... }
# variant.none { special_setup } # executed only if there is no variant set
# end
# end
#
# Provide separate templates for each format and variant:
#
# app/views/projects/show.html.erb
# app/views/projects/show.html+tablet.erb
# app/views/projects/show.html+phone.erb
#
# When you're not sharing any code within the format, you can simplify defining variants
# using the inline syntax:
#
# respond_to do |format|
# format.js { render "trash" }
# format.html.phone { redirect_to progress_path }
# format.html.none { render "trash" }
# end
#
# Variants also support common `any`/`all` block that formats have.
#
# It works for both inline:
#
# respond_to do |format|
# format.html.any { render text: "any" }
# format.html.phone { render text: "phone" }
# end
#
# and block syntax:
#
# respond_to do |format|
# format.html do |variant|
# variant.any(:tablet, :phablet){ render text: "any" }
# variant.phone { render text: "phone" }
# end
# end
#
# Be sure to check the documentation of +respond_with+ and
# <tt>ActionController::MimeResponds.respond_to</tt> for more examples.
def respond_to(*mimes, &block)
@ -321,8 +376,10 @@ def respond_to(*mimes, &block)
# 2. <tt>:action</tt> - overwrites the default render action used after an
# unsuccessful html +post+ request.
def respond_with(*resources, &block)
raise "In order to use respond_with, first you need to declare the formats your " \
"controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
if self.class.mimes_for_respond_to.empty?
raise "In order to use respond_with, first you need to declare the " \
"formats your controller responds to in the class level."
end
if collector = retrieve_collector_from_mimes(&block)
options = resources.size == 1 ? {} : resources.extract_options!
@ -360,7 +417,7 @@ def collect_mimes_from_class_level #:nodoc:
# is available.
def retrieve_collector_from_mimes(mimes=nil, &block) #:nodoc:
mimes ||= collect_mimes_from_class_level
collector = Collector.new(mimes)
collector = Collector.new(mimes, request.variant)
block.call(collector) if block_given?
format = collector.negotiate_format(request)
@ -398,9 +455,11 @@ class Collector
include AbstractController::Collector
attr_accessor :format
def initialize(mimes)
def initialize(mimes, variant = nil)
@responses = {}
mimes.each { |mime| send(mime) }
@variant = variant
mimes.each { |mime| @responses["Mime::#{mime.upcase}".constantize] = nil }
end
def any(*args, &block)
@ -414,16 +473,63 @@ def any(*args, &block)
def custom(mime_type, &block)
mime_type = Mime::Type.lookup(mime_type.to_s) unless mime_type.is_a?(Mime::Type)
@responses[mime_type] ||= block
@responses[mime_type] ||= if block_given?
block
else
VariantCollector.new(@variant)
end
end
def response
@responses.fetch(format, @responses[Mime::ALL])
response = @responses.fetch(format, @responses[Mime::ALL])
if response.is_a?(VariantCollector) # `format.html.phone` - variant inline syntax
response.variant
elsif response.nil? || response.arity == 0 # `format.html` - just a format, call its block
response
else # `format.html{ |variant| variant.phone }` - variant block syntax
variant_collector = VariantCollector.new(@variant)
response.call(variant_collector) #call format block with variants collector
variant_collector.variant
end
end
def negotiate_format(request)
@format = request.negotiate_mime(@responses.keys)
end
class VariantCollector #:nodoc:
def initialize(variant = nil)
@variant = variant
@variants = {}
end
def any(*args, &block)
if block_given?
if args.any? && args.none?{ |a| a == @variant }
args.each{ |v| @variants[v] = block }
else
@variants[:any] = block
end
end
end
alias :all :any
def method_missing(name, *args, &block)
@variants[name] = block if block_given?
end
def variant
key = if @variant.nil?
:none
elsif @variants.has_key?(@variant)
@variant
else
:any
end
@variants[key]
end
end
end
end
end

@ -58,7 +58,7 @@ module Redirecting
# redirect_to post_url(@post), alert: "Watch it, mister!"
# redirect_to post_url(@post), status: :found, notice: "Pay attention to the road"
# redirect_to post_url(@post), status: 301, flash: { updated_post_id: @post.id }
# redirect_to { action: 'atom' }, alert: "Something serious happened"
# redirect_to({ action: 'atom' }, alert: "Something serious happened")
#
# When using <tt>redirect_to :back</tt>, if there is no referrer, ActionController::RedirectBackError will be raised. You may specify some fallback
# behavior for this case by rescuing ActionController::RedirectBackError.

@ -43,7 +43,7 @@ def _handle_render_options(options)
end
# Hash of available renderers, mapping a renderer name to its proc.
# Default keys are :json, :js, :xml.
# Default keys are <tt>:json</tt>, <tt>:js</tt>, <tt>:xml</tt>.
RENDERERS = Set.new
# Adds a new renderer to call within controller actions.

@ -5,14 +5,24 @@ module ActionController #:nodoc:
class InvalidAuthenticityToken < ActionControllerError #:nodoc:
end
class InvalidCrossOriginRequest < ActionControllerError #:nodoc:
end
# Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
# by including a token in the rendered html for your application. This token is
# stored as a random string in the session, to which an attacker does not have
# access. When a request reaches your application, \Rails verifies the received
# token with the token in the session. Only HTML and JavaScript requests are checked,
# so this will not protect your XML API (presumably you'll have a different
# authentication scheme there anyway). Also, GET requests are not protected as these
# should be idempotent.
# authentication scheme there anyway).
#
# GET requests are not protected since they don't have side effects like writing
# to the database and don't leak sensitive information. JavaScript requests are
# an exception: a third-party site can use a <script> tag to reference a JavaScript
# URL on your site. When your JavaScript response loads on their site, it executes.
# With carefully crafted JavaScript on their end, sensitive data in your JavaScript
# response may be extracted. To prevent this, only XmlHttpRequest (known as XHR or
# Ajax) requests are allowed to make GET requests for JavaScript responses.
#
# It's important to remember that XML or JSON requests are also affected and if
# you're building an API you'll need something like:
@ -65,17 +75,16 @@ module RequestForgeryProtection
module ClassMethods
# Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
#
# class ApplicationController < ActionController::Base
# protect_from_forgery
# end
#
# class FooController < ApplicationController
# protect_from_forgery except: :index
#
# You can disable csrf protection on controller-by-controller basis:
#
# You can disable CSRF protection on controller by skipping the verification before_action:
# skip_before_action :verify_authenticity_token
#
# It can also be disabled for specific controller actions:
#
# skip_before_action :verify_authenticity_token, except: [:create]
#
# Valid Options:
#
# * <tt>:only/:except</tt> - Passed to the <tt>before_action</tt> call. Set which actions are verified.
@ -89,6 +98,7 @@ def protect_from_forgery(options = {})
self.forgery_protection_strategy = protection_method_class(options[:with] || :null_session)
self.request_forgery_protection_token ||= :authenticity_token
prepend_before_action :verify_authenticity_token, options
append_after_action :verify_same_origin_request
end
private
@ -169,18 +179,61 @@ def handle_unverified_request
end
protected
# The actual before_action that is used to verify the CSRF token.
# Don't override this directly. Provide your own forgery protection
# strategy instead. If you override, you'll disable same-origin
# `<script>` verification.
#
# Lean on the protect_from_forgery declaration to mark which actions are
# due for same-origin request verification. If protect_from_forgery is
# enabled on an action, this before_action flags its after_action to
# verify that JavaScript responses are for XHR requests, ensuring they
# follow the browser's same-origin policy.
def verify_authenticity_token
mark_for_same_origin_verification!
if !verified_request?
logger.warn "Can't verify CSRF token authenticity" if logger
handle_unverified_request
end
end
def handle_unverified_request
forgery_protection_strategy.new(self).handle_unverified_request
end
# The actual before_action that is used. Modify this to change how you handle unverified requests.
def verify_authenticity_token
unless verified_request?
logger.warn "Can't verify CSRF token authenticity" if logger
handle_unverified_request
CROSS_ORIGIN_JAVASCRIPT_WARNING = "Security warning: an embedded " \
"<script> tag on another site requested protected JavaScript. " \
"If you know what you're doing, go ahead and disable forgery " \
"protection on this action to permit cross-origin JavaScript embedding."
private_constant :CROSS_ORIGIN_JAVASCRIPT_WARNING
# If `verify_authenticity_token` was run (indicating that we have
# forgery protection enabled for this request) then also verify that
# we aren't serving an unauthorized cross-origin response.
def verify_same_origin_request
if marked_for_same_origin_verification? && non_xhr_javascript_response?
logger.warn CROSS_ORIGIN_JAVASCRIPT_WARNING if logger
raise ActionController::InvalidCrossOriginRequest, CROSS_ORIGIN_JAVASCRIPT_WARNING
end
end
# GET requests are checked for cross-origin JavaScript after rendering.
def mark_for_same_origin_verification!
@marked_for_same_origin_verification = request.get?
end
# If the `verify_authenticity_token` before_action ran, verify that
# JavaScript responses are only served to same-origin GET requests.
def marked_for_same_origin_verification?
@marked_for_same_origin_verification ||= false
end
# Check for cross-origin JavaScript responses.
def non_xhr_javascript_response?
content_type =~ %r(\Atext/javascript) && !request.xhr?
end
# Returns true or false if a request is verified. Checks:
#
# * is it a GET or HEAD request? Gets should be safe and idempotent

@ -270,7 +270,7 @@ def has_errors?
resource.respond_to?(:errors) && !resource.errors.empty?
end
# Check whether the neceessary Renderer is available
# Check whether the necessary Renderer is available
def has_renderer?
Renderers::RENDERERS.include?(format)
end

@ -3,6 +3,7 @@
require 'active_support/rescuable'
require 'action_dispatch/http/upload'
require 'stringio'
require 'set'
module ActionController
# Raised when a required parameter is missing.
@ -125,6 +126,13 @@ def initialize(attributes = nil)
@permitted = self.class.permit_all_parameters
end
# Attribute that keeps track of converted arrays, if any, to avoid double
# looping in the common use case permit + mass-assignment. Defined in a
# method to instantiate it only if needed.
def converted_arrays
@converted_arrays ||= Set.new
end
# Returns +true+ if the parameter is permitted, +false+ otherwise.
#
# params = ActionController::Parameters.new
@ -149,8 +157,10 @@ def permitted?
# Person.new(params) # => #<Person id: nil, name: "Francesco">
def permit!
each_pair do |key, value|
convert_hashes_to_parameters(key, value)
self[key].permit! if self[key].respond_to? :permit!
value = convert_hashes_to_parameters(key, value)
Array.wrap(value).each do |_|
_.permit! if _.respond_to? :permit!
end
end
@permitted = true
@ -284,14 +294,7 @@ def [](key)
# params.fetch(:none, 'Francesco') # => "Francesco"
# params.fetch(:none) { 'Francesco' } # => "Francesco"
def fetch(key, *args)
value = super
# Don't rely on +convert_hashes_to_parameters+
# so as to not mutate via a +fetch+
if value.is_a?(Hash)
value = self.class.new(value)
value.permit! if permitted?
end
value
convert_hashes_to_parameters(key, super, false)
rescue KeyError
raise ActionController::ParameterMissing.new(key)
end
@ -329,12 +332,21 @@ def permitted=(new_permitted)
end
private
def convert_hashes_to_parameters(key, value)
if value.is_a?(Parameters) || !value.is_a?(Hash)
def convert_hashes_to_parameters(key, value, assign_if_converted=true)
converted = convert_value_to_parameters(value)
self[key] = converted if assign_if_converted && !converted.equal?(value)
converted
end
def convert_value_to_parameters(value)
if value.is_a?(Array) && !converted_arrays.member?(value)
converted = value.map { |_| convert_value_to_parameters(_) }
converted_arrays << converted
converted
elsif value.is_a?(Parameters) || !value.is_a?(Hash)
value
else
# Convert to Parameters on first access
self[key] = self.class.new(value)
self.class.new(value)
end
end

@ -3,6 +3,7 @@
require "action_dispatch/railtie"
require "abstract_controller/railties/routes_helpers"
require "action_controller/railties/helpers"
require "action_view/railtie"
module ActionController
class Railtie < Rails::Railtie #:nodoc:

@ -1,5 +1,5 @@
#--
# Copyright (c) 2004-2013 David Heinemeier Hansson
# Copyright (c) 2004-2014 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the

@ -10,6 +10,8 @@ module MimeNegotiation
self.ignore_accept_header = false
end
attr_reader :variant
# The MIME type of the HTTP request, such as Mime::XML.
#
# For backward compatibility, the post \format is extracted from the
@ -48,7 +50,7 @@ def accepts
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first
#
def format(view_path = [])
formats.first
formats.first || Mime::NullType.instance
end
def formats
@ -64,6 +66,18 @@ def formats
end
end
# Sets the \variant for template.
def variant=(variant)
if variant.is_a? Symbol
@variant = variant
else
raise ArgumentError, "request.variant must be set to a Symbol, not a #{variant.class}. " \
"For security reasons, never directly set the variant to a user-provided value, " \
"like params[:variant].to_sym. Check user-provided value against a whitelist first, " \
"then set the variant: request.variant = :tablet if params[:variant] == 'tablet'"
end
end
# Sets the \format by string extension, which can be used to force custom formats
# that are not controlled by the extension.
#

@ -1,5 +1,6 @@
require 'set'
require 'active_support/core_ext/class/attribute_accessors'
require 'singleton'
require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/string/starts_ends_with'
module Mime
@ -27,7 +28,7 @@ def #{method}(*)
class << self
def [](type)
return type if type.is_a?(Type)
Type.lookup_by_extension(type) || NullType.new
Type.lookup_by_extension(type)
end
def fetch(type)
@ -292,13 +293,13 @@ def respond_to_missing?(method, include_private = false) #:nodoc:
end
class NullType
include Singleton
def nil?
true
end
def ref
nil
end
def ref; end
def respond_to_missing?(method, include_private = false)
method.to_s.ends_with? '?'

@ -7,6 +7,7 @@
Mime::Type.register "text/css", :css
Mime::Type.register "text/calendar", :ics
Mime::Type.register "text/csv", :csv
Mime::Type.register "text/vcard", :vcf
Mime::Type.register "image/png", :png, [], %w(png)
Mime::Type.register "image/jpeg", :jpeg, [], %w(jpg jpeg jpe pjpeg)

@ -271,7 +271,7 @@ def session_options=(options)
# Override Rack's GET method to support indifferent access
def GET
@env["action_dispatch.request.query_parameters"] ||= (normalize_encode_params(super) || {})
@env["action_dispatch.request.query_parameters"] ||= Utils.deep_munge((normalize_encode_params(super) || {}))
rescue TypeError => e
raise ActionController::BadRequest.new(:query, e)
end
@ -279,7 +279,7 @@ def GET
# Override Rack's POST method to support indifferent access
def POST
@env["action_dispatch.request.request_parameters"] ||= (normalize_encode_params(super) || {})
@env["action_dispatch.request.request_parameters"] ||= Utils.deep_munge((normalize_encode_params(super) || {}))
rescue TypeError => e
raise ActionController::BadRequest.new(:request, e)
end

@ -1,4 +1,4 @@
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/module/attribute_accessors'
require 'monitor'
module ActionDispatch # :nodoc:

@ -33,8 +33,8 @@ def generate(type, name, options, recall = {}, parameterize = nil)
return [route.format(parameterized_parts), params]
end
message = "No route matches #{constraints.inspect}"
message << " missing required keys: #{missing_keys.inspect}" if name
message = "No route matches #{Hash[constraints.sort].inspect}"
message << " missing required keys: #{missing_keys.sort.inspect}" if name
raise ActionController::UrlGenerationError, message
end

@ -1,7 +1,7 @@
#
# DO NOT MODIFY!!!!
# This file is automatically generated by Racc 1.4.9
# from Racc grammer file "".
# from Racc grammar file "".
#
require 'racc/parser.rb'

@ -77,12 +77,32 @@ def visit_GROUP(node)
end
end
class OptimizedPath < String # :nodoc:
class OptimizedPath < Visitor # :nodoc:
def accept(node)
Array(visit(node))
end
private
def visit_GROUP(node)
""
end
def visit_CAT(node)
[visit(node.left), visit(node.right)].flatten
end
def visit_SYMBOL(node)
node.left[1..-1].to_sym
end
def visit_STAR(node)
visit(node.left)
end
def visit_GROUP(node)
[]
end
%w{ LITERAL SLASH DOT }.each do |t|
class_eval %{ def visit_#{t}(n); n.left; end }, __FILE__, __LINE__
end
end
# Used for formatting urls (url_for)

@ -30,7 +30,7 @@ def cookie_jar
# cookies[:login] = { value: "XJ-122", expires: 1.hour.from_now }
#
# # Sets a signed cookie, which prevents users from tampering with its value.
# # The cookie is signed by your app's <tt>config.secret_key_base</tt> value.
# # The cookie is signed by your app's <tt>secrets.secret_key_base</tt> value.
# # It can be read using the signed method <tt>cookies.signed[:name]</tt>
# cookies.signed[:user_id] = current_user.id
#
@ -117,10 +117,10 @@ def permanent
# the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
# cookie was tampered with by the user (or a 3rd party), nil will be returned.
#
# If +config.secret_key_base+ and +config.secret_token+ (deprecated) are both set,
# If +secrets.secret_key_base+ and +config.secret_token+ (deprecated) are both set,
# legacy cookies signed with the old key generator will be transparently upgraded.
#
# This jar requires that you set a suitable secret for the verification on your app's +config.secret_key_base+.
# This jar requires that you set a suitable secret for the verification on your app's +secrets.secret_key_base+.
#
# Example:
#
@ -140,10 +140,10 @@ def signed
# Returns a jar that'll automatically encrypt cookie values before sending them to the client and will decrypt them for read.
# If the cookie was tampered with by the user (or a 3rd party), nil will be returned.
#
# If +config.secret_key_base+ and +config.secret_token+ (deprecated) are both set,
# If +secrets.secret_key_base+ and +config.secret_token+ (deprecated) are both set,
# legacy cookies signed with the old key generator will be transparently upgraded.
#
# This jar requires that you set a suitable secret for the verification on your app's +config.secret_key_base+.
# This jar requires that you set a suitable secret for the verification on your app's +secrets.secret_key_base+.
#
# Example:
#
@ -409,7 +409,7 @@ def verify(signed_message)
end
# UpgradeLegacySignedCookieJar is used instead of SignedCookieJar if
# config.secret_token and config.secret_key_base are both set. It reads
# config.secret_token and secrets.secret_key_base are both set. It reads
# legacy cookies signed with the old dummy key generator and re-saves
# them using the new key generator to provide a smooth upgrade path.
class UpgradeLegacySignedCookieJar < SignedCookieJar #:nodoc:
@ -427,7 +427,7 @@ class EncryptedCookieJar #:nodoc:
def initialize(parent_jar, key_generator, options = {})
if ActiveSupport::LegacyKeyGenerator === key_generator
raise "You didn't set config.secret_key_base, which is required for this cookie jar. " +
raise "You didn't set secrets.secret_key_base, which is required for this cookie jar. " +
"Read the upgrade documentation to learn more about this new config option."
end
@ -465,7 +465,7 @@ def decrypt_and_verify(encrypted_message)
end
# UpgradeLegacyEncryptedCookieJar is used by ActionDispatch::Session::CookieStore
# instead of EncryptedCookieJar if config.secret_token and config.secret_key_base
# instead of EncryptedCookieJar if config.secret_token and secrets.secret_key_base
# are both set. It reads legacy cookies signed with the old dummy key generator and
# encrypts and re-saves them using the new key generator to provide a smooth upgrade path.
class UpgradeLegacyEncryptedCookieJar < EncryptedCookieJar #:nodoc:

@ -1,5 +1,5 @@
require 'action_controller/metal/exceptions'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/module/attribute_accessors'
module ActionDispatch
class ExceptionWrapper

@ -15,8 +15,8 @@ module Session
# best possible option given your application's configuration.
#
# If you only have secret_token set, your cookies will be signed, but
# not encrypted. This means a user cannot alter his +user_id+ without
# knowing your app's secret key, but can easily read his +user_id+. This
# not encrypted. This means a user cannot alter their +user_id+ without
# knowing your app's secret key, but can easily read their +user_id+. This
# was the default for Rails 3 apps.
#
# If you have secret_key_base set, your cookies will be encrypted. This
@ -31,9 +31,10 @@ module Session
#
# Myapp::Application.config.session_store :cookie_store, key: '_your_app_session'
#
# Configure your secret key in config/initializers/secret_token.rb:
# Configure your secret key in config/secrets.yml:
#
# Myapp::Application.config.secret_key_base 'secret key'
# development:
# secret_key_base: 'secret key'
#
# To generate a secret key for an existing application, run `rake secret`.
#

@ -11,9 +11,10 @@ def initialize(root, cache_control)
end
def match?(path)
path = path.dup
path = unescape_path(path)
return false unless path.valid_encoding?
full_path = path.empty? ? @root : File.join(@root, escape_glob_chars(unescape_path(path)))
full_path = path.empty? ? @root : File.join(@root, escape_glob_chars(path))
paths = "#{full_path}#{ext}"
matches = Dir[paths]
@ -40,7 +41,6 @@ def unescape_path(path)
end
def escape_glob_chars(path)
path.force_encoding('binary') if path.respond_to? :force_encoding
path.gsub(/[*?{}\[\]]/, "\\\\\\&")
end
end

@ -89,8 +89,8 @@
}
// 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
// passes their parent <tr> into the callback function
// if the regexp matches a given path
function eachElemsForPath(elems, path, func) {
each(elems, function(e){
var reg = e.getAttribute("data-regexp");

@ -16,6 +16,7 @@ class Railtie < Rails::Railtie # :nodoc:
config.action_dispatch.signed_cookie_salt = 'signed cookie'
config.action_dispatch.encrypted_cookie_salt = 'encrypted cookie'
config.action_dispatch.encrypted_signed_cookie_salt = 'signed encrypted cookie'
config.action_dispatch.perform_deep_munge = true
config.action_dispatch.default_headers = {
'X-Frame-Options' => 'SAMEORIGIN',
@ -28,6 +29,7 @@ class Railtie < Rails::Railtie # :nodoc:
initializer "action_dispatch.configure" do |app|
ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length
ActionDispatch::Request.ignore_accept_header = app.config.action_dispatch.ignore_accept_header
ActionDispatch::Request::Utils.perform_deep_munge = app.config.action_dispatch.perform_deep_munge
ActionDispatch::Response.default_charset = app.config.action_dispatch.default_charset || app.config.encoding
ActionDispatch::Response.default_headers = app.config.action_dispatch.default_headers

@ -7,6 +7,9 @@ class Session # :nodoc:
ENV_SESSION_KEY = Rack::Session::Abstract::ENV_SESSION_KEY # :nodoc:
ENV_SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY # :nodoc:
# Singleton object used to determine if an optional param wasn't specified
Unspecified = Object.new
def self.create(store, env, default_options)
session_was = find env
session = Request::Session.new(store, env)
@ -127,15 +130,12 @@ def delete(key)
@delegate.delete key.to_s
end
def fetch(key, default=nil)
if self.key?(key)
self[key]
elsif default
self[key] = default
elsif block_given?
self[key] = yield(key)
def fetch(key, default=Unspecified, &block)
load_for_read!
if default == Unspecified
@delegate.fetch(key.to_s, &block)
else
raise KeyError
@delegate.fetch(key.to_s, default, &block)
end
end

@ -1,9 +1,15 @@
module ActionDispatch
class Request < Rack::Request
class Utils # :nodoc:
mattr_accessor :perform_deep_munge
self.perform_deep_munge = true
class << self
# Remove nils from the params hash
def deep_munge(hash)
return hash unless perform_deep_munge
hash.each do |k, v|
case v
when Array

@ -69,7 +69,7 @@ def action
end
def internal?
controller.to_s =~ %r{\Arails/(info|welcome)} || path =~ %r{\A#{Rails.application.config.assets.prefix}}
controller.to_s =~ %r{\Arails/(info|mailers|welcome)} || path =~ %r{\A#{Rails.application.config.assets.prefix}\z}
end
def engine?

@ -3,6 +3,7 @@
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/enumerable'
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/module/remove_method'
require 'active_support/inflector'
require 'action_dispatch/routing/redirection'
@ -217,8 +218,12 @@ def default_controller_and_action
controller ||= default_controller
action ||= default_action
unless controller.is_a?(Regexp)
controller = [@scope[:module], controller].compact.join("/").presence
if @scope[:module] && !controller.is_a?(Regexp)
if controller =~ %r{\A/}
controller = controller[1..-1]
else
controller = [@scope[:module], controller].compact.join("/").presence
end
end
if controller.is_a?(String) && controller =~ %r{\A/}
@ -502,11 +507,12 @@ def mount(app, options = nil)
raise "A rack application must be specified" unless path
options[:as] ||= app_name(app)
target_as = name_for_action(options[:as], path)
options[:via] ||= :all
match(path, options.merge(:to => app, :anchor => false, :format => false))
define_generate_prefix(app, options[:as])
define_generate_prefix(app, target_as)
self
end
@ -545,11 +551,11 @@ def define_generate_prefix(app, name)
_routes = @set
app.routes.define_mounted_helper(name)
app.routes.singleton_class.class_eval do
define_method :mounted? do
redefine_method :mounted? do
true
end
define_method :_generate_prefix do |options|
redefine_method :_generate_prefix do |options|
prefix_options = options.slice(*_route.segment_keys)
# we must actually delete prefix segment keys to avoid passing them to next url_for
_route.segment_keys.each { |k| options.delete(k) }

@ -26,14 +26,19 @@ def call(env)
end
uri = URI.parse(path(req.symbolized_path_parameters, req))
unless uri.host
if relative_path?(uri.path)
uri.path = "#{req.script_name}/#{uri.path}"
elsif uri.path.empty?
uri.path = req.script_name.empty? ? "/" : req.script_name
end
end
uri.scheme ||= req.scheme
uri.host ||= req.host
uri.port ||= req.port unless req.standard_port?
if relative_path?(uri.path)
uri.path = "#{req.script_name}/#{uri.path}"
end
body = %(<html><body>You are being <a href="#{ERB::Util.h(uri.to_s)}">redirected</a>.</body></html>)
headers = {
@ -57,11 +62,33 @@ def inspect
def relative_path?(path)
path && !path.empty? && path[0] != '/'
end
def escape(params)
Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }]
end
def escape_fragment(params)
Hash[params.map{ |k,v| [k, Journey::Router::Utils.escape_fragment(v)] }]
end
def escape_path(params)
Hash[params.map{ |k,v| [k, Journey::Router::Utils.escape_path(v)] }]
end
end
class PathRedirect < Redirect
URL_PARTS = /\A([^?]+)?(\?[^#]+)?(#.+)?\z/
def path(params, request)
(params.empty? || !block.match(/%\{\w*\}/)) ? block : (block % escape(params))
if block.match(URL_PARTS)
path = interpolation_required?($1, params) ? $1 % escape_path(params) : $1
query = interpolation_required?($2, params) ? $2 % escape(params) : $2
fragment = interpolation_required?($3, params) ? $3 % escape_fragment(params) : $3
"#{path}#{query}#{fragment}"
else
interpolation_required?(block, params) ? block % escape(params) : block
end
end
def inspect
@ -69,8 +96,8 @@ def inspect
end
private
def escape(params)
Hash[params.map{ |k,v| [k, Rack::Utils.escape(v)] }]
def interpolation_required?(string, params)
!params.empty? && string && string.match(/%\{\w*\}/)
end
end
@ -90,9 +117,14 @@ def path(params, request)
url_options[:path] = (url_options[:path] % escape_path(params))
end
if relative_path?(url_options[:path])
url_options[:path] = "/#{url_options[:path]}"
url_options[:script_name] = request.script_name
unless options[:host] || options[:domain]
if relative_path?(url_options[:path])
url_options[:path] = "/#{url_options[:path]}"
url_options[:script_name] = request.script_name
elsif url_options[:path].empty?
url_options[:path] = request.script_name.empty? ? "/" : ""
url_options[:script_name] = request.script_name
end
end
ActionDispatch::Http::URL.url_for url_options
@ -101,11 +133,6 @@ def path(params, request)
def inspect
"redirect(#{status}, #{options.map{ |k,v| "#{k}: #{v}" }.join(', ')})"
end
private
def escape_path(params)
Hash[params.map{ |k,v| [k, URI.parser.escape(v)] }]
end
end
module Redirection

@ -163,9 +163,10 @@ class OptimizedUrlHelper < UrlHelper # :nodoc:
def initialize(route, options)
super
@path_parts = @route.required_parts
@arg_size = @path_parts.size
@string_route = @route.optimized_path
@klass = Journey::Router::Utils
@required_parts = @route.required_parts
@arg_size = @required_parts.size
@optimized_path = @route.optimized_path
end
def call(t, args)
@ -182,43 +183,36 @@ def call(t, args)
private
def optimized_helper(args)
path = @string_route.dup
klass = Journey::Router::Utils
params = Hash[parameterize_args(args)]
missing_keys = missing_keys(params)
@path_parts.zip(args) do |part, arg|
parameterized_arg = arg.to_param
if parameterized_arg.nil? || parameterized_arg.empty?
raise_generation_error(args)
end
# 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(parameterized_arg))
unless missing_keys.empty?
raise_generation_error(params, missing_keys)
end
path
@optimized_path.map{ |segment| replace_segment(params, segment) }.join
end
def replace_segment(params, segment)
Symbol === segment ? @klass.escape_fragment(params[segment]) : segment
end
def optimize_routes_generation?(t)
t.send(:optimize_routes_generation?)
end
def raise_generation_error(args)
parts, missing_keys = [], []
def parameterize_args(args)
@required_parts.zip(args.map(&:to_param))
end
@path_parts.zip(args) do |part, arg|
parameterized_arg = arg.to_param
def missing_keys(args)
args.select{ |part, arg| arg.nil? || arg.empty? }.keys
end
if parameterized_arg.nil? || parameterized_arg.empty?
missing_keys << part
end
parts << [part, arg]
end
message = "No route matches #{Hash[parts].inspect}"
message << " missing required keys: #{missing_keys.inspect}"
def raise_generation_error(args, missing_keys)
constraints = Hash[@route.requirements.merge(args).sort]
message = "No route matches #{constraints.inspect}"
message << " missing required keys: #{missing_keys.sort.inspect}"
raise ActionController::UrlGenerationError, message
end
@ -226,7 +220,7 @@ def raise_generation_error(args)
def initialize(route, options)
@options = options
@segment_keys = route.segment_keys
@segment_keys = route.segment_keys.uniq
@route = route
end
@ -361,7 +355,7 @@ module MountedHelpers #:nodoc:
include UrlFor
end
# Contains all the mounted helpers accross different
# Contains all the mounted helpers across different
# engines and the `main_app` helper for the application.
# You can include this in your classes if you want to
# access routes for other engines.

@ -211,7 +211,7 @@ def recognized_request_for(path, extras = {})
def fail_on(exception_class)
yield
rescue exception_class => e
raise MiniTest::Assertion, e.message
raise Minitest::Assertion, e.message
end
end
end

@ -137,7 +137,7 @@ def delete_via_redirect(path, parameters = nil, headers_or_env = nil)
class Session
DEFAULT_HOST = "www.example.com"
include MiniTest::Assertions
include Minitest::Assertions
include TestProcess, RequestHelpers, Assertions
%w( status status_message headers body redirect? ).each do |method|
@ -242,7 +242,7 @@ def https!(flag = true)
@https = flag
end
# Return +true+ if the session is mimicking a secure HTTPS request.
# Returns +true+ if the session is mimicking a secure HTTPS request.
#
# if session.https?
# ...

@ -1,5 +1,5 @@
#--
# Copyright (c) 2004-2013 David Heinemeier Hansson
# Copyright (c) 2004-2014 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the

@ -1,7 +1,7 @@
module ActionPack
# Returns the version of the currently loaded ActionPack as a Gem::Version
def self.version
Gem::Version.new "4.1.0.beta"
Gem::Version.new "4.1.0.beta1"
end
module VERSION #:nodoc:

@ -37,7 +37,7 @@ class TestCollector < ActiveSupport::TestCase
test "does not register unknown mime types" do
collector = MyCollector.new
assert_raise NameError do
assert_raise NoMethodError do
collector.unknown
end
end

@ -43,6 +43,9 @@ def env
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
# Disable available locale checks to avoid warnings running the test suite.
I18n.enforce_available_locales = false
# Register danish language for testing
I18n.backend.store_translations 'da', {}
I18n.backend.store_translations 'pt-BR', {}
@ -246,8 +249,6 @@ def assert_header(name, value)
end
end
ActionController::Base.superclass.send(:include, ActionView::Layouts)
module ActionController
class Base
include ActionController::Testing
@ -333,7 +334,6 @@ class ThreadsController < ResourcesController; end
class MessagesController < ResourcesController; end
class CommentsController < ResourcesController; end
class ReviewsController < ResourcesController; end
class AuthorsController < ResourcesController; end
class LogosController < ResourcesController; end
class AccountsController < ResourcesController; end
@ -344,8 +344,6 @@ class PreferencesController < ResourcesController; end
module Backoffice
class ProductsController < ResourcesController; end
class TagsController < ResourcesController; end
class ManufacturersController < ResourcesController; end
class ImagesController < ResourcesController; end
module Admin

@ -19,7 +19,7 @@ def test_assert_response_predicate_methods
@response = FakeResponse.new sym
assert_response sym
assert_raises(MiniTest::Assertion) {
assert_raises(Minitest::Assertion) {
assert_response :unauthorized
}
end
@ -29,11 +29,11 @@ def test_assert_response_fixnum
@response = FakeResponse.new 400
assert_response 400
assert_raises(MiniTest::Assertion) {
assert_raises(Minitest::Assertion) {
assert_response :unauthorized
}
assert_raises(MiniTest::Assertion) {
assert_raises(Minitest::Assertion) {
assert_response 500
}
end
@ -42,11 +42,11 @@ def test_assert_response_sym_status
@response = FakeResponse.new 401
assert_response :unauthorized
assert_raises(MiniTest::Assertion) {
assert_raises(Minitest::Assertion) {
assert_response :ok
}
assert_raises(MiniTest::Assertion) {
assert_raises(Minitest::Assertion) {
assert_response :success
}
end

@ -444,22 +444,18 @@ def test_redirected_to_with_nested_controller
def test_assert_response_uses_exception_message
@controller = AssertResponseWithUnexpectedErrorController.new
get :index
e = assert_raise RuntimeError, 'Expected non-success response' do
get :index
end
assert_response :success
flunk 'Expected non-success response'
rescue RuntimeError => e
assert e.message.include?('FAIL')
assert_includes 'FAIL', e.message
end
def test_assert_response_failure_response_with_no_exception
@controller = AssertResponseWithUnexpectedErrorController.new
get :show
assert_response :success
flunk 'Expected non-success response'
rescue ActiveSupport::TestCase::Assertion
# success
rescue
flunk "assert_response failed to handle failure response with missing, but optional, exception."
assert_response 500
assert_equal 'Boom', response.body
end
end

@ -893,17 +893,6 @@ def around(controller)
around_filter YieldingFilter.new, :only => :raises_after
end
class ControllerWithFilterMethod < PostsController
class YieldingFilter < DefaultFilter
def around(controller)
yield
raise After
end
end
around_filter YieldingFilter.new.method(:around), :only => :raises_after
end
class ControllerWithProcFilter < PostsController
around_filter(:only => :no_raise) do |c,b|
c.instance_variable_set(:"@before", true)

@ -21,7 +21,7 @@ def display
def authenticate
authenticate_or_request_with_http_digest("SuperSecret") do |username|
# Return the password
# Returns the password
USERS[username]
end
end

@ -34,4 +34,15 @@ def test_use_fallback_locales
get :hello_world
assert_equal "Gutten Tag", @response.body
end
def test_localized_template_has_correct_header_with_no_format_in_template_name
old_locale = I18n.locale
I18n.locale = :it
get :hello_world
assert_equal "Ciao Mondo", @response.body
assert_equal "text/html", @response.content_type
ensure
I18n.locale = old_locale
end
end

@ -146,6 +146,106 @@ def iphone_with_html_response_type_without_layout
end
end
def variant_with_implicit_rendering
end
def variant_with_format_and_custom_render
request.variant = :mobile
respond_to do |type|
type.html { render text: "mobile" }
end
end
def multiple_variants_for_format
respond_to do |type|
type.html do |html|
html.tablet { render text: "tablet" }
html.phone { render text: "phone" }
end
end
end
def variant_plus_none_for_format
respond_to do |format|
format.html do |variant|
variant.phone { render text: "phone" }
variant.none
end
end
end
def variant_inline_syntax
respond_to do |format|
format.js { render text: "js" }
format.html.none { render text: "none" }
format.html.phone { render text: "phone" }
end
end
def variant_inline_syntax_without_block
respond_to do |format|
format.js
format.html.none
format.html.phone
end
end
def variant_any
respond_to do |format|
format.html do |variant|
variant.any(:tablet, :phablet){ render text: "any" }
variant.phone { render text: "phone" }
end
end
end
def variant_any_any
respond_to do |format|
format.html do |variant|
variant.any { render text: "any" }
variant.phone { render text: "phone" }
end
end
end
def variant_inline_any
respond_to do |format|
format.html.any(:tablet, :phablet){ render text: "any" }
format.html.phone { render text: "phone" }
end
end
def variant_inline_any_any
respond_to do |format|
format.html.phone { render text: "phone" }
format.html.any { render text: "any" }
end
end
def variant_any_implicit_render
respond_to do |format|
format.html.phone
format.html.any(:tablet, :phablet)
end
end
def variant_any_with_none
respond_to do |format|
format.html.any(:none, :phone){ render text: "none or phone" }
end
end
def format_any_variant_any
respond_to do |format|
format.html { render text: "HTML" }
format.any(:js, :xml) do |variant|
variant.phone{ render text: "phone" }
variant.any(:tablet, :phablet){ render text: "tablet" }
end
end
end
protected
def set_layout
case action_name
@ -490,4 +590,154 @@ def test_invalid_format
get :using_defaults, :format => "invalidformat"
end
end
def test_invalid_variant
@request.variant = :invalid
assert_raises(ActionView::MissingTemplate) do
get :variant_with_implicit_rendering
end
end
def test_variant_not_set_regular_template_missing
assert_raises(ActionView::MissingTemplate) do
get :variant_with_implicit_rendering
end
end
def test_variant_with_implicit_rendering
@request.variant = :mobile
get :variant_with_implicit_rendering
assert_equal "text/html", @response.content_type
assert_equal "mobile", @response.body
end
def test_variant_with_format_and_custom_render
@request.variant = :phone
get :variant_with_format_and_custom_render
assert_equal "text/html", @response.content_type
assert_equal "mobile", @response.body
end
def test_multiple_variants_for_format
@request.variant = :tablet
get :multiple_variants_for_format
assert_equal "text/html", @response.content_type
assert_equal "tablet", @response.body
end
def test_no_variant_in_variant_setup
get :variant_plus_none_for_format
assert_equal "text/html", @response.content_type
assert_equal "none", @response.body
end
def test_variant_inline_syntax
get :variant_inline_syntax, format: :js
assert_equal "text/javascript", @response.content_type
assert_equal "js", @response.body
get :variant_inline_syntax
assert_equal "text/html", @response.content_type
assert_equal "none", @response.body
@request.variant = :phone
get :variant_inline_syntax
assert_equal "text/html", @response.content_type
assert_equal "phone", @response.body
end
def test_variant_inline_syntax_without_block
@request.variant = :phone
get :variant_inline_syntax_without_block
assert_equal "text/html", @response.content_type
assert_equal "phone", @response.body
end
def test_variant_any
@request.variant = :phone
get :variant_any
assert_equal "text/html", @response.content_type
assert_equal "phone", @response.body
@request.variant = :tablet
get :variant_any
assert_equal "text/html", @response.content_type
assert_equal "any", @response.body
@request.variant = :phablet
get :variant_any
assert_equal "text/html", @response.content_type
assert_equal "any", @response.body
end
def test_variant_any_any
@request.variant = :phone
get :variant_any_any
assert_equal "text/html", @response.content_type
assert_equal "phone", @response.body
@request.variant = :yolo
get :variant_any_any
assert_equal "text/html", @response.content_type
assert_equal "any", @response.body
end
def test_variant_inline_any
@request.variant = :phone
get :variant_any
assert_equal "text/html", @response.content_type
assert_equal "phone", @response.body
@request.variant = :tablet
get :variant_inline_any
assert_equal "text/html", @response.content_type
assert_equal "any", @response.body
@request.variant = :phablet
get :variant_inline_any
assert_equal "text/html", @response.content_type
assert_equal "any", @response.body
end
def test_variant_inline_any_any
@request.variant = :phone
get :variant_inline_any_any
assert_equal "text/html", @response.content_type
assert_equal "phone", @response.body
@request.variant = :yolo
get :variant_inline_any_any
assert_equal "text/html", @response.content_type
assert_equal "any", @response.body
end
def test_variant_any_implicit_render
@request.variant = :tablet
get :variant_any_implicit_render
assert_equal "text/html", @response.content_type
assert_equal "tablet", @response.body
@request.variant = :phablet
get :variant_any_implicit_render
assert_equal "text/html", @response.content_type
assert_equal "phablet", @response.body
end
def test_variant_any_with_none
get :variant_any_with_none
assert_equal "text/html", @response.content_type
assert_equal "none or phone", @response.body
@request.variant = :phone
get :variant_any_with_none
assert_equal "text/html", @response.content_type
assert_equal "none or phone", @response.body
end
def test_format_any_variant_any
@request.variant = :tablet
get :format_any_variant_any, format: :js
assert_equal "text/javascript", @response.content_type
assert_equal "tablet", @response.body
end
end

@ -8,9 +8,16 @@ def assert_filtered_out(params, key)
end
setup do
@params = ActionController::Parameters.new({ person: {
age: "32", name: { first: "David", last: "Heinemeier Hansson" }
}})
@params = ActionController::Parameters.new(
person: {
age: '32',
name: {
first: 'David',
last: 'Heinemeier Hansson'
},
addresses: [{city: 'Chicago', state: 'Illinois'}]
}
)
@struct_fields = []
%w(0 1 12).each do |number|
@ -153,6 +160,18 @@ def assert_filtered_out(params, key)
assert_equal nil, params[:foo]
end
test 'hashes in array values get wrapped' do
params = ActionController::Parameters.new(foo: [{}, {}])
params[:foo].each do |hash|
assert !hash.permitted?
end
end
test 'arrays are converted at most once' do
params = ActionController::Parameters.new(foo: [{}])
assert params[:foo].equal?(params[:foo])
end
test "fetch doesnt raise ParameterMissing exception if there is a default" do
assert_equal "monkey", @params.fetch(:foo, "monkey")
assert_equal "monkey", @params.fetch(:foo) { "monkey" }
@ -221,6 +240,7 @@ def assert_filtered_out(params, key)
assert @params.permitted?
assert @params[:person].permitted?
assert @params[:person][:name].permitted?
assert @params[:person][:addresses][0].permitted?
end
test "permitted takes a default value when Parameters.permit_all_parameters is set" do

@ -22,7 +22,7 @@ def show_partial
tests TestController
def test_render_vanilla_js
get :render_vanilla_js_hello
xhr :get, :render_vanilla_js_hello
assert_equal "alert('hello')", @response.body
assert_equal "text/javascript", @response.content_type
end

@ -100,13 +100,13 @@ def test_render_json_with_status
end
def test_render_json_with_callback
get :render_json_hello_world_with_callback
xhr :get, :render_json_hello_world_with_callback
assert_equal 'alert({"hello":"world"})', @response.body
assert_equal 'text/javascript', @response.content_type
end
def test_render_json_with_custom_content_type
get :render_json_with_custom_content_type
xhr :get, :render_json_with_custom_content_type
assert_equal '{"hello":"world"}', @response.body
assert_equal 'text/javascript', @response.content_type
end

@ -52,18 +52,36 @@ def form_for_remote_with_external_token
render :inline => "<%= form_for(:some_resource, :remote => true, :authenticity_token => 'external_token') {} %>"
end
def same_origin_js
render js: 'foo();'
end
def negotiate_same_origin
respond_to do |format|
format.js { same_origin_js }
end
end
def cross_origin_js
same_origin_js
end
def negotiate_cross_origin
negotiate_same_origin
end
def rescue_action(e) raise e end
end
# sample controllers
class RequestForgeryProtectionControllerUsingResetSession < ActionController::Base
include RequestForgeryProtectionActions
protect_from_forgery :only => %w(index meta), :with => :reset_session
protect_from_forgery :only => %w(index meta same_origin_js negotiate_same_origin), :with => :reset_session
end
class RequestForgeryProtectionControllerUsingException < ActionController::Base
include RequestForgeryProtectionActions
protect_from_forgery :only => %w(index meta), :with => :exception
protect_from_forgery :only => %w(index meta same_origin_js negotiate_same_origin), :with => :exception
end
class RequestForgeryProtectionControllerUsingNullSession < ActionController::Base
@ -201,7 +219,7 @@ def test_should_not_allow_post_without_token
end
def test_should_not_allow_post_without_token_irrespective_of_format
assert_blocked { post :index, :format=>'xml' }
assert_blocked { post :index, format: 'xml' }
end
def test_should_not_allow_patch_without_token
@ -271,6 +289,48 @@ def test_should_warn_on_missing_csrf_token
end
end
def test_should_only_allow_same_origin_js_get_with_xhr_header
assert_cross_origin_blocked { get :same_origin_js }
assert_cross_origin_blocked { get :same_origin_js, format: 'js' }
assert_cross_origin_blocked do
@request.accept = 'text/javascript'
get :negotiate_same_origin
end
assert_cross_origin_not_blocked { xhr :get, :same_origin_js }
assert_cross_origin_not_blocked { xhr :get, :same_origin_js, format: 'js' }
assert_cross_origin_not_blocked do
@request.accept = 'text/javascript'
xhr :get, :negotiate_same_origin
end
end
# Allow non-GET requests since GET is all a remote <script> tag can muster.
def test_should_allow_non_get_js_without_xhr_header
assert_cross_origin_not_blocked { post :same_origin_js, custom_authenticity_token: @token }
assert_cross_origin_not_blocked { post :same_origin_js, format: 'js', custom_authenticity_token: @token }
assert_cross_origin_not_blocked do
@request.accept = 'text/javascript'
post :negotiate_same_origin, custom_authenticity_token: @token
end
end
def test_should_only_allow_cross_origin_js_get_without_xhr_header_if_protection_disabled
assert_cross_origin_not_blocked { get :cross_origin_js }
assert_cross_origin_not_blocked { get :cross_origin_js, format: 'js' }
assert_cross_origin_not_blocked do
@request.accept = 'text/javascript'
get :negotiate_cross_origin
end
assert_cross_origin_not_blocked { xhr :get, :cross_origin_js }
assert_cross_origin_not_blocked { xhr :get, :cross_origin_js, format: 'js' }
assert_cross_origin_not_blocked do
@request.accept = 'text/javascript'
xhr :get, :negotiate_cross_origin
end
end
def assert_blocked
session[:something_like_user_id] = 1
yield
@ -282,6 +342,16 @@ def assert_not_blocked
assert_nothing_raised { yield }
assert_response :success
end
def assert_cross_origin_blocked
assert_raises(ActionController::InvalidCrossOriginRequest) do
yield
end
end
def assert_cross_origin_not_blocked
assert_not_blocked { yield }
end
end
# OK let's get our test on
@ -305,13 +375,13 @@ class RequestForgeryProtectionControllerUsingResetSessionTest < ActionController
end
end
class NullSessionDummyKeyGenerator
def generate_key(secret)
'03312270731a2ed0d11ed091c2338a06'
end
end
class RequestForgeryProtectionControllerUsingNullSessionTest < ActionController::TestCase
class NullSessionDummyKeyGenerator
def generate_key(secret)
'03312270731a2ed0d11ed091c2338a06'
end
end
def setup
@request.env[ActionDispatch::Cookies::GENERATOR_KEY] = NullSessionDummyKeyGenerator.new
end
@ -375,8 +445,8 @@ def test_should_allow_all_methods_without_token
class CustomAuthenticityParamControllerTest < ActionController::TestCase
def setup
ActionController::Base.request_forgery_protection_token = :custom_token_name
super
ActionController::Base.request_forgery_protection_token = :custom_token_name
end
def teardown

@ -1833,11 +1833,11 @@ def test_recognize_path
assert_equal({:controller => 'foo', :action => 'id_default', :id => 1 }, @routes.recognize_path('/id_default'))
assert_equal({:controller => 'foo', :action => 'get_or_post'}, @routes.recognize_path('/get_or_post', :method => :get))
assert_equal({:controller => 'foo', :action => 'get_or_post'}, @routes.recognize_path('/get_or_post', :method => :post))
assert_raise(ActionController::ActionControllerError) { @routes.recognize_path('/get_or_post', :method => :put) }
assert_raise(ActionController::ActionControllerError) { @routes.recognize_path('/get_or_post', :method => :delete) }
assert_raise(ActionController::RoutingError) { @routes.recognize_path('/get_or_post', :method => :put) }
assert_raise(ActionController::RoutingError) { @routes.recognize_path('/get_or_post', :method => :delete) }
assert_equal({:controller => 'posts', :action => 'index', :optional => 'bar'}, @routes.recognize_path('/optional/bar'))
assert_raise(ActionController::ActionControllerError) { @routes.recognize_path('/optional') }
assert_raise(ActionController::RoutingError) { @routes.recognize_path('/optional') }
assert_equal({:controller => 'posts', :action => 'show', :id => '1', :ws => true}, @routes.recognize_path('/ws/posts/show/1', :method => :get))
assert_equal({:controller => 'posts', :action => 'list', :ws => true}, @routes.recognize_path('/ws/posts/list', :method => :get))
@ -1916,11 +1916,4 @@ def sort_extras!(extras)
end
extras
end
def assert_raise(e)
result = yield
flunk "Did not raise #{e}, but returned #{result.inspect}"
rescue e
assert true
end
end

@ -148,7 +148,7 @@ def test_send_file_headers_with_bad_symbol
}
@controller.headers = {}
assert !@controller.send(:send_file_headers!, options)
assert_raise(ArgumentError) { @controller.send(:send_file_headers!, options) }
end
def test_send_file_headers_guess_type_from_extension

@ -31,21 +31,21 @@ class MimeTypeTest < ActiveSupport::TestCase
test "parse text with trailing star at the beginning" do
accept = "text/*, text/html, application/json, multipart/form-data"
expect = [Mime::HTML, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::XML, Mime::YAML, Mime::JSON, Mime::MULTIPART_FORM]
expect = [Mime::HTML, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::VCF, Mime::XML, Mime::YAML, Mime::JSON, Mime::MULTIPART_FORM]
parsed = Mime::Type.parse(accept)
assert_equal expect, parsed
end
test "parse text with trailing star in the end" do
accept = "text/html, application/json, multipart/form-data, text/*"
expect = [Mime::HTML, Mime::JSON, Mime::MULTIPART_FORM, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::XML, Mime::YAML]
expect = [Mime::HTML, Mime::JSON, Mime::MULTIPART_FORM, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::VCF, Mime::XML, Mime::YAML]
parsed = Mime::Type.parse(accept)
assert_equal expect, parsed
end
test "parse text with trailing star" do
accept = "text/*"
expect = [Mime::HTML, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::XML, Mime::YAML, Mime::JSON]
expect = [Mime::HTML, Mime::TEXT, Mime::JS, Mime::CSS, Mime::ICS, Mime::CSV, Mime::VCF, Mime::XML, Mime::YAML, Mime::JSON]
parsed = Mime::Type.parse(accept)
assert_equal expect, parsed
end

@ -5,7 +5,7 @@ class TestRoutingMount < ActionDispatch::IntegrationTest
class FakeEngine
def self.routes
Object.new
@routes ||= ActionDispatch::Routing::RouteSet.new
end
def self.call(env)
@ -27,12 +27,23 @@ def self.call(env)
scope "/its_a" do
mount SprocketsApp, :at => "/sprocket"
end
resources :users do
mount FakeEngine, :at => "/fakeengine", :as => :fake_mounted_at_resource
end
end
def app
Router
end
def test_app_name_is_properly_generated_when_engine_is_mounted_in_resources
assert Router.mounted_helpers.method_defined?(:user_fake_mounted_at_resource),
"A mounted helper should be defined with a parent's prefix"
assert Router.named_routes.routes[:user_fake_mounted_at_resource],
"A named route should be defined with a parent's prefix"
end
def test_trailing_slash_is_not_removed_from_path_info
get "/sprockets/omg/"
assert_equal "/sprockets -- /omg/", response.body

@ -32,12 +32,18 @@ def self.routes
get "/conflicting_url", :to => "inside_engine_generating#conflicting"
get "/foo", :to => "never#invoked", :as => :named_helper_that_should_be_invoked_only_in_respond_to_test
get "/relative_path_redirect", :to => redirect("foo")
get "/relative_path_root", :to => redirect("")
get "/relative_path_redirect", :to => redirect("foo")
get "/relative_option_root", :to => redirect(:path => "")
get "/relative_option_redirect", :to => redirect(:path => "foo")
get "/relative_custom_root", :to => redirect { |params, request| "" }
get "/relative_custom_redirect", :to => redirect { |params, request| "foo" }
get "/absolute_path_redirect", :to => redirect("/foo")
get "/absolute_path_root", :to => redirect("/")
get "/absolute_path_redirect", :to => redirect("/foo")
get "/absolute_option_root", :to => redirect(:path => "/")
get "/absolute_option_redirect", :to => redirect(:path => "/foo")
get "/absolute_custom_root", :to => redirect { |params, request| "/" }
get "/absolute_custom_redirect", :to => redirect { |params, request| "/foo" }
end
@ -190,46 +196,64 @@ def setup
assert_equal "engine", last_response.body
end
test "[ENGINE] relative path root uses SCRIPT_NAME from request" do
get "/awesome/blog/relative_path_root"
verify_redirect "http://example.org/awesome/blog"
end
test "[ENGINE] relative path redirect uses SCRIPT_NAME from request" do
get "/awesome/blog/relative_path_redirect"
assert_equal 301, last_response.status
assert_equal "http://example.org/awesome/blog/foo", last_response.headers["Location"]
assert_equal %(<html><body>You are being <a href="http://example.org/awesome/blog/foo">redirected</a>.</body></html>), last_response.body
verify_redirect "http://example.org/awesome/blog/foo"
end
test "[ENGINE] relative option root uses SCRIPT_NAME from request" do
get "/awesome/blog/relative_option_root"
verify_redirect "http://example.org/awesome/blog"
end
test "[ENGINE] relative option redirect uses SCRIPT_NAME from request" do
get "/awesome/blog/relative_option_redirect"
assert_equal 301, last_response.status
assert_equal "http://example.org/awesome/blog/foo", last_response.headers["Location"]
assert_equal %(<html><body>You are being <a href="http://example.org/awesome/blog/foo">redirected</a>.</body></html>), last_response.body
verify_redirect "http://example.org/awesome/blog/foo"
end
test "[ENGINE] relative custom root uses SCRIPT_NAME from request" do
get "/awesome/blog/relative_custom_root"
verify_redirect "http://example.org/awesome/blog"
end
test "[ENGINE] relative custom redirect uses SCRIPT_NAME from request" do
get "/awesome/blog/relative_custom_redirect"
assert_equal 301, last_response.status
assert_equal "http://example.org/awesome/blog/foo", last_response.headers["Location"]
assert_equal %(<html><body>You are being <a href="http://example.org/awesome/blog/foo">redirected</a>.</body></html>), last_response.body
verify_redirect "http://example.org/awesome/blog/foo"
end
test "[ENGINE] absolute path root doesn't use SCRIPT_NAME from request" do
get "/awesome/blog/absolute_path_root"
verify_redirect "http://example.org/"
end
test "[ENGINE] absolute path redirect doesn't use SCRIPT_NAME from request" do
get "/awesome/blog/absolute_path_redirect"
assert_equal 301, last_response.status
assert_equal "http://example.org/foo", last_response.headers["Location"]
assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
verify_redirect "http://example.org/foo"
end
test "[ENGINE] absolute option root doesn't use SCRIPT_NAME from request" do
get "/awesome/blog/absolute_option_root"
verify_redirect "http://example.org/"
end
test "[ENGINE] absolute option redirect doesn't use SCRIPT_NAME from request" do
get "/awesome/blog/absolute_option_redirect"
assert_equal 301, last_response.status
assert_equal "http://example.org/foo", last_response.headers["Location"]
assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
verify_redirect "http://example.org/foo"
end
test "[ENGINE] absolute custom root doesn't use SCRIPT_NAME from request" do
get "/awesome/blog/absolute_custom_root"
verify_redirect "http://example.org/"
end
test "[ENGINE] absolute custom redirect doesn't use SCRIPT_NAME from request" do
get "/awesome/blog/absolute_custom_redirect"
assert_equal 301, last_response.status
assert_equal "http://example.org/foo", last_response.headers["Location"]
assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
verify_redirect "http://example.org/foo"
end
# Inside Application
@ -320,6 +344,17 @@ def setup
path = engine_object.polymorphic_url(Post.new, :host => "www.example.com")
assert_equal "http://www.example.com/awesome/blog/posts/1", path
end
private
def verify_redirect(url, status = 301)
assert_equal status, last_response.status
assert_equal url, last_response.headers["Location"]
assert_equal expected_redirect_body(url), last_response.body
end
def expected_redirect_body(url)
%(<html><body>You are being <a href="#{url}">redirected</a>.</body></html>)
end
end
class EngineMountedAtRoot < ActionDispatch::IntegrationTest
@ -332,12 +367,18 @@ def self.routes
routes.draw do
get "/posts/:id", :to => "posts#show", :as => :post
get "/relative_path_redirect", :to => redirect("foo")
get "/relative_path_root", :to => redirect("")
get "/relative_path_redirect", :to => redirect("foo")
get "/relative_option_root", :to => redirect(:path => "")
get "/relative_option_redirect", :to => redirect(:path => "foo")
get "/relative_custom_root", :to => redirect { |params, request| "" }
get "/relative_custom_redirect", :to => redirect { |params, request| "foo" }
get "/absolute_path_redirect", :to => redirect("/foo")
get "/absolute_path_root", :to => redirect("/")
get "/absolute_path_redirect", :to => redirect("/foo")
get "/absolute_option_root", :to => redirect(:path => "/")
get "/absolute_option_redirect", :to => redirect(:path => "/foo")
get "/absolute_custom_root", :to => redirect { |params, request| "/" }
get "/absolute_custom_redirect", :to => redirect { |params, request| "/foo" }
end
@ -390,46 +431,75 @@ def app
assert_equal "/posts/1", last_response.body
end
test "[ENGINE] relative path root uses SCRIPT_NAME from request" do
get "/relative_path_root"
verify_redirect "http://example.org/"
end
test "[ENGINE] relative path redirect uses SCRIPT_NAME from request" do
get "/relative_path_redirect"
assert_equal 301, last_response.status
assert_equal "http://example.org/foo", last_response.headers["Location"]
assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
verify_redirect "http://example.org/foo"
end
test "[ENGINE] relative option root uses SCRIPT_NAME from request" do
get "/relative_option_root"
verify_redirect "http://example.org/"
end
test "[ENGINE] relative option redirect uses SCRIPT_NAME from request" do
get "/relative_option_redirect"
assert_equal 301, last_response.status
assert_equal "http://example.org/foo", last_response.headers["Location"]
assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
verify_redirect "http://example.org/foo"
end
test "[ENGINE] relative custom root uses SCRIPT_NAME from request" do
get "/relative_custom_root"
verify_redirect "http://example.org/"
end
test "[ENGINE] relative custom redirect uses SCRIPT_NAME from request" do
get "/relative_custom_redirect"
assert_equal 301, last_response.status
assert_equal "http://example.org/foo", last_response.headers["Location"]
assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
verify_redirect "http://example.org/foo"
end
test "[ENGINE] absolute path root doesn't use SCRIPT_NAME from request" do
get "/absolute_path_root"
verify_redirect "http://example.org/"
end
test "[ENGINE] absolute path redirect doesn't use SCRIPT_NAME from request" do
get "/absolute_path_redirect"
assert_equal 301, last_response.status
assert_equal "http://example.org/foo", last_response.headers["Location"]
assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
verify_redirect "http://example.org/foo"
end
test "[ENGINE] absolute option root doesn't use SCRIPT_NAME from request" do
get "/absolute_option_root"
verify_redirect "http://example.org/"
end
test "[ENGINE] absolute option redirect doesn't use SCRIPT_NAME from request" do
get "/absolute_option_redirect"
assert_equal 301, last_response.status
assert_equal "http://example.org/foo", last_response.headers["Location"]
assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
verify_redirect "http://example.org/foo"
end
test "[ENGINE] absolute custom root doesn't use SCRIPT_NAME from request" do
get "/absolute_custom_root"
verify_redirect "http://example.org/"
end
test "[ENGINE] absolute custom redirect doesn't use SCRIPT_NAME from request" do
get "/absolute_custom_redirect"
assert_equal 301, last_response.status
assert_equal "http://example.org/foo", last_response.headers["Location"]
assert_equal %(<html><body>You are being <a href="http://example.org/foo">redirected</a>.</body></html>), last_response.body
verify_redirect "http://example.org/foo"
end
private
def verify_redirect(url, status = 301)
assert_equal status, last_response.status
assert_equal url, last_response.headers["Location"]
assert_equal expected_redirect_body(url), last_response.body
end
def expected_redirect_body(url)
%(<html><body>You are being <a href="#{url}">redirected</a>.</body></html>)
end
end
end

@ -11,6 +11,17 @@ def parse
head :ok
end
end
class EarlyParse
def initialize(app)
@app = app
end
def call(env)
# Trigger a Rack parse so that env caches the query params
Rack::Request.new(env).params
@app.call(env)
end
end
def teardown
TestController.last_query_parameters = nil
@ -93,6 +104,21 @@ def test_array_parses_without_nil
assert_parses({"action" => ['1']}, "action[]=1&action[]")
end
test "perform_deep_munge" do
ActionDispatch::Request::Utils.perform_deep_munge = false
begin
assert_parses({"action" => nil}, "action")
assert_parses({"action" => {"foo" => nil}}, "action[foo]")
assert_parses({"action" => {"foo" => {"bar" => nil}}}, "action[foo][bar]")
assert_parses({"action" => {"foo" => {"bar" => [nil]}}}, "action[foo][bar][]")
assert_parses({"action" => {"foo" => [nil]}}, "action[foo][]")
assert_parses({"action" => {"foo" => [{"bar" => nil}]}}, "action[foo][][bar]")
assert_parses({"action" => ['1',nil]}, "action[]=1&action[]")
ensure
ActionDispatch::Request::Utils.perform_deep_munge = true
end
end
test "query string with empty key" do
assert_parses(
{ "action" => "create_customer", "full_name" => "David Heinemeier Hansson" },
@ -131,6 +157,10 @@ def assert_parses(expected, actual)
set.draw do
get ':action', :to => ::QueryStringParsingTest::TestController
end
@app = self.class.build_app(set) do |middleware|
middleware.use(EarlyParse)
end
get "/parse", actual
assert_response :ok

@ -68,13 +68,12 @@ def test_fetch
assert_equal '1', session.fetch(:one)
assert_equal '2', session.fetch(:two, '2')
assert_equal '2', session.fetch(:two)
assert_nil session.fetch(:two, nil)
assert_equal 'three', session.fetch(:three) {|el| el.to_s }
assert_equal 'three', session.fetch(:three)
assert_raise KeyError do
session.fetch(:four)
session.fetch(:three)
end
end

@ -93,6 +93,14 @@ def url_for(options = {})
assert_equal '1.1.1.1', request.remote_ip
end
test "remote ip spoof protection ignores private addresses" do
request = stub_request 'HTTP_X_FORWARDED_FOR' => '172.17.19.51',
'HTTP_CLIENT_IP' => '172.17.19.51',
'REMOTE_ADDR' => '1.1.1.1',
'HTTP_X_BLUECOAT_VIA' => 'de462e07a2db325e'
assert_equal '1.1.1.1', request.remote_ip
end
test "remote ip v6" do
request = stub_request 'REMOTE_ADDR' => '2001:0db8:85a3:0000:0000:8a2e:0370:7334'
assert_equal '2001:0db8:85a3:0000:0000:8a2e:0370:7334', request.remote_ip
@ -616,10 +624,10 @@ def url_for(options = {})
test "format is not nil with unknown format" do
request = stub_request
request.expects(:parameters).at_least_once.returns({ format: :hello })
assert_equal request.format.nil?, true
assert_equal request.format.html?, false
assert_equal request.format.xml?, false
assert_equal request.format.json?, false
assert request.format.nil?
assert_not request.format.html?
assert_not request.format.xml?
assert_not request.format.json?
end
test "formats with xhr request" do
@ -836,6 +844,19 @@ def url_for(options = {})
end
end
test "setting variant" do
request = stub_request
request.variant = :mobile
assert_equal :mobile, request.variant
end
test "setting variant with non symbol value" do
request = stub_request
assert_raise ArgumentError do
request.variant = "mobile"
end
end
protected
def stub_request(env = {})

@ -203,6 +203,18 @@ def test_rake_routes_dont_show_app_mounted_in_assets_prefix
assert_no_match(/\/sprockets/, output.first)
end
def test_rake_routes_shows_route_defined_in_under_assets_prefix
output = draw do
scope '/sprockets' do
get '/foo' => 'foo#bar'
end
end
assert_equal [
"Prefix Verb URI Pattern Controller#Action",
" foo GET /sprockets/foo(.:format) foo#bar"
], output
end
def test_redirect
output = draw do
get "/foo" => redirect("/foo/bar"), :constraints => { :subdomain => "admin" }

@ -2864,6 +2864,36 @@ def test_action_from_path_is_not_frozen
assert !@request.params[:action].frozen?
end
def test_multiple_positional_args_with_the_same_name
draw do
get '/downloads/:id/:id.tar' => 'downloads#show', as: :download, format: false
end
expected_params = {
controller: 'downloads',
action: 'show',
id: '1'
}
get '/downloads/1/1.tar'
assert_equal 'downloads#show', @response.body
assert_equal expected_params, @request.symbolized_path_parameters
assert_equal '/downloads/1/1.tar', download_path('1')
assert_equal '/downloads/1/1.tar', download_path('1', '1')
end
def test_absolute_controller_namespace
draw do
namespace :foo do
get '/', to: '/bar#index', as: 'root'
end
end
get '/foo'
assert_equal 'bar#index', @response.body
assert_equal '/foo', foo_root_path
end
private
def draw(&block)
@ -3235,7 +3265,9 @@ class TestRedirectInterpolation < ActionDispatch::IntegrationTest
get "/foo/:id" => redirect("/foo/bar/%{id}")
get "/bar/:id" => redirect(:path => "/foo/bar/%{id}")
get "/baz/:id" => redirect("/baz?id=%{id}&foo=?&bar=1#id-%{id}")
get "/foo/bar/:id" => ok
get "/baz" => ok
end
end
@ -3251,6 +3283,14 @@ def app; Routes end
verify_redirect "http://www.example.com/foo/bar/1%3E"
end
test "path redirect escapes interpolated parameters correctly" do
get "/foo/1%201"
verify_redirect "http://www.example.com/foo/bar/1%201"
get "/baz/1%201"
verify_redirect "http://www.example.com/baz?id=1+1&foo=?&bar=1#id-1%201"
end
private
def verify_redirect(url, status=301)
assert_equal status, @response.status
@ -3317,6 +3357,8 @@ class TestOptimizedNamedRoutes < ActionDispatch::IntegrationTest
ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
get '/foo' => ok, as: :foo
get '/post(/:action(/:id))' => ok, as: :posts
get '/:foo/:foo_type/bars/:id' => ok, as: :bar
get '/projects/:id.:format' => ok, as: :project
end
end
@ -3339,6 +3381,16 @@ def app; Routes end
assert_equal '/post', Routes.url_helpers.posts_path
assert_equal '/post', posts_path
end
test 'segments with same prefix are replaced correctly' do
assert_equal '/foo/baz/bars/1', Routes.url_helpers.bar_path('foo', 'baz', '1')
assert_equal '/foo/baz/bars/1', bar_path('foo', 'baz', '1')
end
test 'segments separated with a period are replaced correctly' do
assert_equal '/projects/1.json', Routes.url_helpers.project_path(1, :json)
assert_equal '/projects/1.json', project_path(1, :json)
end
end
class TestNamedRouteUrlHelpers < ActionDispatch::IntegrationTest
@ -3686,3 +3738,28 @@ def test_redirect_doesnt_match_unnamed_route
end
end
end
class TestUrlGenerationErrors < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
get "/products/:id" => 'products#show', :as => :product
end
end
def app; Routes end
include Routes.url_helpers
test "url helpers raise a helpful error message whem generation fails" do
url, missing = { action: 'show', controller: 'products', id: nil }, [:id]
message = "No route matches #{url.inspect} missing required keys: #{missing.inspect}"
# Optimized url helper
error = assert_raises(ActionController::UrlGenerationError){ product_path(nil) }
assert_equal message, error.message
# Non-optimized url helper
error = assert_raises(ActionController::UrlGenerationError, message){ product_path(id: nil) }
assert_equal message, error.message
end
end

@ -137,7 +137,7 @@ def get(path)
end
def with_static_file(file)
path = "#{FIXTURE_LOAD_PATH}/public" + file
path = "#{FIXTURE_LOAD_PATH}/#{public_path}" + file
File.open(path, "wb+") { |f| f.write(file) }
yield file
ensure
@ -149,11 +149,24 @@ class StaticTest < ActiveSupport::TestCase
DummyApp = lambda { |env|
[200, {"Content-Type" => "text/plain"}, ["Hello, World!"]]
}
App = ActionDispatch::Static.new(DummyApp, "#{FIXTURE_LOAD_PATH}/public", "public, max-age=60")
def setup
@app = App
@app = ActionDispatch::Static.new(DummyApp, "#{FIXTURE_LOAD_PATH}/public", "public, max-age=60")
end
def public_path
"public"
end
include StaticTests
end
class StaticEncodingTest < StaticTest
def setup
@app = ActionDispatch::Static.new(DummyApp, "#{FIXTURE_LOAD_PATH}/公共", "public, max-age=60")
end
def public_path
"公共"
end
end

@ -66,7 +66,7 @@ def app
assert_equal "http://www.example.com:8080/foo", foo_url(host: "www.example.com:8080", protocol: "http://")
end
test "port option overides the host" do
test "port option overrides the host" do
assert_equal "http://www.example.com:8080/foo", foo_url(host: "www.example.com:8443", protocol: "http://", port: 8080)
end

@ -0,0 +1 @@
Ciao Mondo

@ -0,0 +1 @@
phablet

@ -0,0 +1 @@
tablet

@ -0,0 +1 @@
none

@ -0,0 +1 @@
mobile

@ -0,0 +1 @@
/foo/bar.html

@ -0,0 +1,3 @@
body {
background: #000;
}

@ -0,0 +1 @@
/foo/index.html

@ -0,0 +1 @@
means hello in Japanese

@ -0,0 +1 @@
/index.html

@ -1,3 +1,73 @@
* Use `display:none` instead of `display:inline` for hidden fields
Fixes #6403
*Gaelian Ditchburn*
* The `video_tag` helper accepts a number as `:size`
The `:size` option of the `video_tag` helper now can be specified
with a stringified number. The `width` and `height` attributes of
the generated tag will be the same.
*Kuldeep Aggarwal*
* A Cycle object should accept an array and cycle through it as it would with a set of
comma-separated objects.
arr = [1,2,3]
cycle(arr) # => '1'
cycle(arr) # => '2'
cycle(arr) # => '3'
Previously, it would return the array as a string, because it took the array as a
single object:
arr = [1,2,3]
cycle(arr) # => '[1,2,3]'
cycle(arr) # => '[1,2,3]'
cycle(arr) # => '[1,2,3]'
*Kristian Freeman*
* Label tags generated by collection helpers only inherit the `:index` and
`:namespace` from the input, because only these attributes modifies the
`for` attribute of the label. Also, the input attributes don't have
precedence over the label attributes anymore.
Before:
collection = [[1, true, { class: 'foo' }]]
f.collection_check_boxes :options, collection, :second, :first do |b|
b.label(class: 'my_custom_class')
end
# => <label class="foo" for="user_active_true">1</label>
After:
collection = [[1, true, { class: 'foo' }]]
f.collection_check_boxes :options, collection, :second, :first do |b|
b.label(class: 'my_custom_class')
end
# => <label class="my_custom_class" for="user_active_true">1</label>
*Andriel Nuernberg*
* Fixed a long-standing bug in `json_escape` that causes quotation marks to be stripped.
This method also escapes the \u2028 and \u2029 unicode newline characters which are
treated as \n in JavaScript. This matches the behaviour of the AS::JSON encoder. (The
original change in the encoder was introduced in #10534.)
*Godfrey Chan*
* `ActionView::MissingTemplate` includes underscore when raised for a partial.
Fixes #13002.
*Yves Senn*
* Use `set_backtrace` instead of instance variable `@backtrace` in ActionView exceptions
*Shimpei Makimoto*

@ -1,4 +1,4 @@
Copyright (c) 2004-2013 David Heinemeier Hansson
Copyright (c) 2004-2014 David Heinemeier Hansson
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

@ -20,10 +20,10 @@
s.requirements << 'none'
s.add_dependency 'activesupport', version
s.add_dependency 'activemodel', version
s.add_dependency 'builder', '~> 3.1.0'
s.add_dependency 'builder', '~> 3.1'
s.add_dependency 'erubis', '~> 2.7.0'
s.add_development_dependency 'actionpack', version
s.add_development_dependency 'actionpack', version
s.add_development_dependency 'activemodel', version
end

@ -1,5 +1,5 @@
#--
# Copyright (c) 2004-2013 David Heinemeier Hansson
# Copyright (c) 2004-2014 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@ -23,6 +23,7 @@
require 'active_support'
require 'active_support/rails'
require 'action_view/version'
module ActionView
extend ActiveSupport::Autoload
@ -84,6 +85,7 @@ module ActionView
def self.eager_load!
super
ActionView::Helpers.eager_load!
ActionView::Template.eager_load!
HTML.eager_load!
end

@ -1,5 +1,5 @@
require 'active_support/core_ext/module/attr_internal'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/ordered_options'
require 'action_view/log_subscriber'
require 'action_view/helpers'

@ -27,6 +27,12 @@ module Helpers #:nodoc:
autoload :TextHelper
autoload :TranslationHelper
autoload :UrlHelper
autoload :Tags
def self.eager_load!
super
Tags.eager_load!
end
extend ActiveSupport::Concern

@ -1,4 +1,4 @@
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/enumerable'
module ActionView

@ -103,7 +103,7 @@ def stylesheet_link_tag(*sources)
}.join("\n").html_safe
end
# Returns a link tag that browsers and news readers can use to auto-detect
# Returns a link tag that browsers and feed readers can use to auto-detect
# an RSS or Atom feed. The +type+ can either be <tt>:rss</tt> (default) or
# <tt>:atom</tt>. Control the link options in url_for format using the
# +url_options+. You can modify the LINK tag itself in +tag_options+.
@ -176,7 +176,7 @@ def favicon_link_tag(source='favicon.ico', options={})
# ==== Options
#
# You can add HTML attributes using the +options+. The +options+ supports
# three additional keys for convenience and conformance:
# two additional keys for convenience and conformance:
#
# * <tt>:alt</tt> - If no alt text is given, the file name part of the
# +source+ is used (capitalized and without the extension)
@ -207,14 +207,7 @@ def image_tag(source, options={})
options[:alt] = options.fetch(:alt){ image_alt(src) }
end
if size = options.delete(:size)
if size =~ %r{\A\d+x\d+\z}
options[:width], options[:height] = size.split('x')
elsif size =~ %r{\A\d+\z}
options[:width] = options[:height] = size
end
end
options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size]
tag("img", options)
end
@ -251,9 +244,9 @@ def image_alt(src)
#
# * <tt>:poster</tt> - Set an image (like a screenshot) to be shown
# before the video loads. The path is calculated like the +src+ of +image_tag+.
# * <tt>:size</tt> - Supplied as "{Width}x{Height}", so "30x45" becomes
# width="30" and height="45". <tt>:size</tt> will be ignored if the
# value is not in the correct format.
# * <tt>:size</tt> - Supplied as "{Width}x{Height}" or "{Number}", so "30x45" becomes
# 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
#
@ -267,6 +260,8 @@ def image_alt(src)
# # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png" />
# video_tag("/trailers/hd.avi", size: "16x16")
# # => <video src="/trailers/hd.avi" width="16" height="16" />
# video_tag("/trailers/hd.avi", size: "16")
# # => <video height="16" src="/trailers/hd.avi" width="16" />
# video_tag("/trailers/hd.avi", height: '32', width: '32')
# # => <video height="32" src="/trailers/hd.avi" width="32" />
# video_tag("trailer.ogg", "trailer.flv")
@ -278,10 +273,7 @@ def image_alt(src)
def video_tag(*sources)
multiple_sources_tag('video', sources) do |options|
options[:poster] = path_to_image(options[:poster]) if options[:poster]
if size = options.delete(:size)
options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
end
options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size]
end
end
@ -317,6 +309,14 @@ def multiple_sources_tag(type, sources)
content_tag(type, nil, options)
end
end
def extract_dimensions(size)
if size =~ %r{\A\d+x\d+\z}
size.split('x')
elsif size =~ %r{\A\d+\z}
[size, size]
end
end
end
end
end

@ -170,8 +170,12 @@ def time_ago_in_words(from_time, include_seconds_or_options = {})
# * <tt>:use_month_names</tt> - Set to an array with 12 month names if you want to customize month names.
# Note: You can also use Rails' i18n functionality for this.
# * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
# * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Time.now.year - 5</tt>.
# * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Time.now.year + 5</tt>.
# * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Date.today.year - 5</tt>if
# you are creating new record. While editing existing record, <tt>:start_year</tt> defaults to
# the current selected year minus 5.
# * <tt>:end_year</tt> - Set the end year for the year select. Default is <tt>Date.today.year + 5</tt> if
# you are creating new record. While editing existing record, <tt>:end_year</tt> defaults to
# the current selected year plus 5.
# * <tt>:discard_day</tt> - Set to true if you don't want to show a day select. This includes the day
# as a hidden field instead of showing a select field. Also note that this implicitly sets the day to be the
# first of the given month in order to not create invalid dates like 31 February.
@ -1062,7 +1066,7 @@ def time_select(method, options = {}, html_options = {})
# Wraps ActionView::Helpers::DateHelper#datetime_select for form builders:
#
# <%= form_for @person do |f| %>
# <%= f.time_select :last_request_at %>
# <%= f.datetime_select :last_request_at %>
# <%= f.submit %>
# <% end %>
#

Some files were not shown because too many files have changed in this diff Show More