aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Leighton <j@jonathanleighton.com>2010-10-06 12:11:38 +0100
committerJon Leighton <j@jonathanleighton.com>2010-10-06 12:11:38 +0100
commitc954d54e2f36bb53ced5e3655adc071dd233797e (patch)
treed84bf4bf51f65a1c817089c6fe37c3e9f20420d0
parentf2b41914d6be935182d37e0c0d491352ac3de043 (diff)
parentd40ca9cce241a8083756c993d6c99a79e62e050e (diff)
downloadrails-c954d54e2f36bb53ced5e3655adc071dd233797e.tar.gz
rails-c954d54e2f36bb53ced5e3655adc071dd233797e.tar.bz2
rails-c954d54e2f36bb53ced5e3655adc071dd233797e.zip
Merge branch 'master' into nested_has_many_through
-rw-r--r--Gemfile1
-rw-r--r--actionmailer/lib/action_mailer/old_api.rb4
-rw-r--r--actionmailer/lib/rails/generators/mailer/templates/mailer.rb2
-rw-r--r--actionpack/CHANGELOG6
-rw-r--r--actionpack/lib/action_controller/test_case.rb9
-rw-r--r--actionpack/lib/action_dispatch/http/cache.rb22
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb2
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb25
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb5
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb283
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb64
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb53
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb6
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb8
-rw-r--r--actionpack/lib/action_dispatch/testing/performance_test.rb3
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb51
-rw-r--r--actionpack/lib/action_view/test_case.rb26
-rw-r--r--actionpack/test/abstract_unit.rb35
-rw-r--r--actionpack/test/controller/filters_test.rb4
-rw-r--r--actionpack/test/controller/integration_test.rb2
-rw-r--r--actionpack/test/controller/log_subscriber_test.rb4
-rw-r--r--actionpack/test/controller/new_base/etag_test.rb46
-rw-r--r--actionpack/test/controller/record_identifier_test.rb27
-rw-r--r--actionpack/test/controller/redirect_test.rb18
-rw-r--r--actionpack/test/controller/render_test.rb118
-rw-r--r--actionpack/test/controller/rescue_test.rb16
-rw-r--r--actionpack/test/dispatch/request/multipart_params_parsing_test.rb7
-rw-r--r--actionpack/test/dispatch/response_test.rb11
-rw-r--r--actionpack/test/dispatch/session/cookie_store_test.rb12
-rw-r--r--actionpack/test/dispatch/show_exceptions_test.rb14
-rw-r--r--actionpack/test/dispatch/static_test.rb47
-rw-r--r--actionpack/test/dispatch/uploaded_file_test.rb58
-rw-r--r--actionpack/test/lib/controller/fake_models.rb19
-rw-r--r--actionpack/test/template/form_helper_test.rb15
-rw-r--r--actionpack/test/template/test_case_test.rb31
-rw-r--r--actionpack/test/template/url_helper_test.rb18
-rw-r--r--activemodel/lib/active_model/naming.rb6
-rw-r--r--activemodel/test/cases/naming_test.rb4
-rw-r--r--activemodel/test/models/track_back.rb7
-rw-r--r--activerecord/lib/active_record.rb2
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb12
-rw-r--r--activerecord/lib/active_record/base.rb36
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb8
-rw-r--r--activerecord/lib/active_record/migration.rb38
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb25
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb16
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb6
-rw-r--r--activerecord/lib/active_record/schema_dumper.rb12
-rw-r--r--activerecord/lib/active_record/session_store.rb7
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb6
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb6
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb14
-rw-r--r--activerecord/test/cases/calculations_test.rb12
-rw-r--r--activerecord/test/cases/inheritance_test.rb14
-rw-r--r--activeresource/lib/active_resource/validations.rb2
-rw-r--r--activeresource/test/cases/format_test.rb2
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb6
-rw-r--r--activesupport/lib/active_support/core_ext/enumerable.rb5
-rw-r--r--activesupport/lib/active_support/test_case.rb3
-rw-r--r--activesupport/lib/active_support/testing/default.rb9
-rw-r--r--activesupport/lib/active_support/xml_mini.rb2
-rw-r--r--activesupport/test/test_case_test.rb57
-rw-r--r--activesupport/test/test_test.rb7
-rw-r--r--railties/guides/source/getting_started.textile2
-rw-r--r--railties/guides/source/index.html.erb4
-rw-r--r--railties/guides/source/layout.html.erb1
-rw-r--r--railties/guides/source/ruby_on_rails_guides_guidelines.textile77
-rw-r--r--railties/lib/rails/application.rb8
-rw-r--r--railties/lib/rails/application/routes_reloader.rb6
-rw-r--r--railties/lib/rails/engine.rb22
-rw-r--r--railties/lib/rails/engine/configuration.rb28
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb25
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt2
-rw-r--r--railties/lib/rails/railtie.rb2
-rw-r--r--railties/lib/rails/railtie/configuration.rb32
-rw-r--r--railties/railties.gemspec2
-rw-r--r--railties/test/application/middleware/best_practices_test.rb26
-rw-r--r--railties/test/application/middleware/cache_test.rb12
-rw-r--r--railties/test/application/middleware/remote_ip_test.rb63
-rw-r--r--railties/test/application/middleware/sendfile_test.rb56
-rw-r--r--railties/test/application/middleware_test.rb137
-rw-r--r--railties/test/application/routing_test.rb61
-rw-r--r--railties/test/generators/generated_attribute_test.rb2
-rw-r--r--railties/test/generators/mailer_generator_test.rb9
-rw-r--r--railties/test/isolation/abstract_unit.rb27
-rw-r--r--railties/test/railties/engine_test.rb21
90 files changed, 990 insertions, 1040 deletions
diff --git a/Gemfile b/Gemfile
index 9d29c42e1c..7fe1c3ad22 100644
--- a/Gemfile
+++ b/Gemfile
@@ -6,6 +6,7 @@ else
gem "arel", :git => "git://github.com/rails/arel.git"
end
+gem "rack", :git => "git://github.com/rack/rack.git"
gem "rails", :path => File.dirname(__FILE__)
gem "rake", ">= 0.8.7"
diff --git a/actionmailer/lib/action_mailer/old_api.rb b/actionmailer/lib/action_mailer/old_api.rb
index b8c15df263..0396f0775e 100644
--- a/actionmailer/lib/action_mailer/old_api.rb
+++ b/actionmailer/lib/action_mailer/old_api.rb
@@ -247,8 +247,8 @@ module ActionMailer
[ nil, {} ]
else
ctype, *attrs = @content_type.split(/;\s*/)
- attrs = attrs.inject({}) { |h,s| k,v = s.split(/\=/, 2); h[k] = v; h }
- [ctype, {"charset" => @charset}.merge(attrs)]
+ attrs = Hash[attrs.map { |attr| attr.split(/\=/, 2) }]
+ [ctype, {"charset" => @charset}.merge!(attrs)]
end
end
end
diff --git a/actionmailer/lib/rails/generators/mailer/templates/mailer.rb b/actionmailer/lib/rails/generators/mailer/templates/mailer.rb
index 4d21c65101..370a508cad 100644
--- a/actionmailer/lib/rails/generators/mailer/templates/mailer.rb
+++ b/actionmailer/lib/rails/generators/mailer/templates/mailer.rb
@@ -6,7 +6,7 @@ class <%= class_name %> < ActionMailer::Base
# Subject can be set in your I18n file at config/locales/en.yml
# with the following lookup:
#
- # en.<%= file_name %>.<%= action %>.subject
+ # en.<%= file_path.gsub("/",".") %>.<%= action %>.subject
#
def <%= action %>
@greeting = "Hi"
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 6f8314109b..6352b97a6b 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,12 +1,12 @@
*Rails 3.1.0 (unreleased)*
+* Rely on Rack::Session stores API for more compatibility across the Ruby world. This is backwards incompatible since Rack::Session expects #get_session to accept 4 arguments and requires #destroy_session instead of simply #destroy. [José Valim]
+
* file_field automatically adds :multipart => true to the enclosing form. [Santiago Pastorino]
* Renames csrf_meta_tag -> csrf_meta_tags, and aliases csrf_meta_tag for backwards compatibility. [fxn]
-* Add Rack::Cache to the default stack. Create a Rails store that delegates to the Rails cache, so by default, whatever caching layer you are using will be used
- for HTTP caching. Note that Rack::Cache will be used if you use #expires_in, #fresh_when or #stale with :public => true. Otherwise, the caching rules will apply
- to the browser only.
+* Add Rack::Cache to the default stack. Create a Rails store that delegates to the Rails cache, so by default, whatever caching layer you are using will be used for HTTP caching. Note that Rack::Cache will be used if you use #expires_in, #fresh_when or #stale with :public => true. Otherwise, the caching rules will apply to the browser only. [Yehuda Katz, Carl Lerche]
*Rails 3.0.0 (August 29, 2010)*
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 1af75fc2d7..6061945622 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -187,15 +187,18 @@ module ActionController
end
end
- class TestSession < ActionDispatch::Session::AbstractStore::SessionHash #:nodoc:
- DEFAULT_OPTIONS = ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS
+ class TestSession < Rack::Session::Abstract::SessionHash #:nodoc:
+ DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
def initialize(session = {})
+ @env, @by = nil, nil
replace(session.stringify_keys)
@loaded = true
end
- def exists?; true; end
+ def exists?
+ true
+ end
end
# Superclass for ActionController functional tests. Functional tests allow you to
diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb
index 047fab006e..4061222d11 100644
--- a/actionpack/lib/action_dispatch/http/cache.rb
+++ b/actionpack/lib/action_dispatch/http/cache.rb
@@ -50,8 +50,7 @@ module ActionDispatch
if cache_control = self["Cache-Control"]
cache_control.split(/,\s*/).each do |segment|
first, last = segment.split("=")
- last ||= true
- @cache_control[first.to_sym] = last
+ @cache_control[first.to_sym] = last || true
end
end
end
@@ -88,28 +87,9 @@ module ActionDispatch
def handle_conditional_get!
if etag? || last_modified? || !@cache_control.empty?
set_conditional_cache_control!
- elsif nonempty_ok_response?
- self.etag = body
-
- if request && request.etag_matches?(etag)
- self.status = 304
- self.body = []
- end
-
- set_conditional_cache_control!
- else
- headers["Cache-Control"] = "no-cache"
end
end
- def nonempty_ok_response?
- @status == 200 && string_body?
- end
-
- def string_body?
- !@blank && @body.respond_to?(:all?) && @body.all? { |part| part.is_a?(String) }
- end
-
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
def set_conditional_cache_control!
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 09d6ba8223..bbcdefb190 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -199,7 +199,7 @@ module ActionDispatch
# TODO This should be broken apart into AD::Request::Session and probably
# be included by the session middleware.
def reset_session
- session.destroy if session
+ session.destroy if session && session.respond_to?(:destroy)
self.session = {}
@env['action_dispatch.request.flash_hash'] = nil
end
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 151c90167b..72871e328a 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -132,7 +132,7 @@ module ActionDispatch # :nodoc:
# information.
attr_accessor :charset, :content_type
- CONTENT_TYPE = "Content-Type"
+ CONTENT_TYPE = "Content-Type"
cattr_accessor(:default_charset) { "utf-8" }
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
index 49465d820e..84e58d7d6a 100644
--- a/actionpack/lib/action_dispatch/http/upload.rb
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -2,27 +2,28 @@ require 'active_support/core_ext/object/blank'
module ActionDispatch
module Http
- class UploadedFile < Tempfile
+ class UploadedFile
attr_accessor :original_filename, :content_type, :tempfile, :headers
def initialize(hash)
@original_filename = hash[:filename]
@content_type = hash[:type]
@headers = hash[:head]
+ @tempfile = hash[:tempfile]
+ raise(ArgumentError, ':tempfile is required') unless @tempfile
+ end
- # To the untrained eye, this may appear as insanity. Given the alternatives,
- # such as busting the method cache on every request or breaking backwards
- # compatibility with is_a?(Tempfile), this solution is the best available
- # option.
- #
- # TODO: Deprecate is_a?(Tempfile) and define a real API for this parameter
- tempfile = hash[:tempfile]
- tempfile.instance_variables.each do |ivar|
- instance_variable_set(ivar, tempfile.instance_variable_get(ivar))
- end
+ def read(*args)
+ @tempfile.read(*args)
end
- alias local_path path
+ def rewind
+ @tempfile.rewind
+ end
+
+ def size
+ @tempfile.size
+ end
end
module Upload
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 3e5cd6a2f9..cfee95eb4b 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -18,11 +18,6 @@ module ActionDispatch
@protocol ||= ssl? ? 'https://' : 'http://'
end
- # Is this an SSL request?
- def ssl?
- @ssl ||= @env['HTTPS'] == 'on' || @env['HTTP_X_FORWARDED_PROTO'] == 'https'
- end
-
# Returns the \host for this request, such as "example.com".
def raw_host_with_port
if forwarded = env["HTTP_X_FORWARDED_HOST"]
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
index db0187c015..64d3a87fd0 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -1,5 +1,6 @@
require 'rack/utils'
require 'rack/request'
+require 'rack/session/abstract/id'
require 'action_dispatch/middleware/cookies'
require 'active_support/core_ext/object/blank'
@@ -8,252 +9,76 @@ module ActionDispatch
class SessionRestoreError < StandardError #:nodoc:
end
- class AbstractStore
- ENV_SESSION_KEY = 'rack.session'.freeze
- ENV_SESSION_OPTIONS_KEY = 'rack.session.options'.freeze
-
- # thin wrapper around Hash that allows us to lazily
- # load session id into session_options
- class OptionsHash < Hash
- def initialize(by, env, default_options)
- @by = by
- @env = env
- @session_id_loaded = false
- merge!(default_options)
- end
-
- def [](key)
- if key == :id
- load_session_id! unless key?(:id) || has_session_id?
- end
- super
- end
-
- private
-
- def has_session_id?
- @session_id_loaded
- end
-
- def load_session_id!
- self[:id] = @by.send(:extract_session_id, @env)
- @session_id_loaded = true
- end
- end
-
- class SessionHash < Hash
- def initialize(by, env)
- super()
- @by = by
- @env = env
- @loaded = false
- end
-
- def [](key)
- load_for_read!
- super(key.to_s)
- end
-
- def has_key?(key)
- load_for_read!
- super(key.to_s)
- end
-
- def []=(key, value)
- load_for_write!
- super(key.to_s, value)
- end
-
- def clear
- load_for_write!
- super
- end
-
- def to_hash
- load_for_read!
- h = {}.replace(self)
- h.delete_if { |k,v| v.nil? }
- h
- end
-
- def update(hash)
- load_for_write!
- super(hash.stringify_keys)
- end
-
- def delete(key)
- load_for_write!
- super(key.to_s)
- end
-
- def inspect
- load_for_read!
- super
- end
-
- def exists?
- return @exists if instance_variable_defined?(:@exists)
- @exists = @by.send(:exists?, @env)
- end
-
- def loaded?
- @loaded
- end
-
- def destroy
- clear
- @by.send(:destroy, @env) if defined?(@by) && @by
- @env[ENV_SESSION_OPTIONS_KEY][:id] = nil if defined?(@env) && @env && @env[ENV_SESSION_OPTIONS_KEY]
- @loaded = false
- end
-
- private
-
- def load_for_read!
- load! if !loaded? && exists?
- end
-
- def load_for_write!
- load! unless loaded?
- end
-
- def load!
- id, session = @by.send(:load_session, @env)
- @env[ENV_SESSION_OPTIONS_KEY][:id] = id
- replace(session.stringify_keys)
- @loaded = true
- end
-
+ module DestroyableSession
+ def destroy
+ clear
+ options = @env[Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY] if @env
+ options ||= {}
+ @by.send(:destroy_session, @env, options[:id], options) if @by
+ options[:id] = nil
+ @loaded = false
end
+ end
- DEFAULT_OPTIONS = {
- :key => '_session_id',
- :path => '/',
- :domain => nil,
- :expire_after => nil,
- :secure => false,
- :httponly => true,
- :cookie_only => true
- }
+ ::Rack::Session::Abstract::SessionHash.send :include, DestroyableSession
+ module Compatibility
def initialize(app, options = {})
- @app = app
- @default_options = DEFAULT_OPTIONS.merge(options)
- @key = @default_options.delete(:key).freeze
- @cookie_only = @default_options.delete(:cookie_only)
- ensure_session_key!
+ options[:key] ||= '_session_id'
+ super
end
- def call(env)
- prepare!(env)
- response = @app.call(env)
-
- session_data = env[ENV_SESSION_KEY]
- options = env[ENV_SESSION_OPTIONS_KEY]
-
- if !session_data.is_a?(AbstractStore::SessionHash) || session_data.loaded? || options[:expire_after]
- request = ActionDispatch::Request.new(env)
-
- return response if (options[:secure] && !request.ssl?)
-
- session_data.send(:load!) if session_data.is_a?(AbstractStore::SessionHash) && !session_data.loaded?
-
- sid = options[:id] || generate_sid
- session_data = session_data.to_hash
-
- value = set_session(env, sid, session_data)
- return response unless value
-
- cookie = { :value => value }
- if options[:expire_after]
- cookie[:expires] = Time.now + options.delete(:expire_after)
- end
-
- set_cookie(request, cookie.merge!(options))
- end
-
- response
+ def generate_sid
+ ActiveSupport::SecureRandom.hex(16)
end
- private
-
- def prepare!(env)
- env[ENV_SESSION_KEY] = SessionHash.new(self, env)
- env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options)
- end
-
- def generate_sid
- ActiveSupport::SecureRandom.hex(16)
- end
+ protected
- def set_cookie(request, options)
- if request.cookie_jar[@key] != options[:value] || !options[:expires].nil?
- request.cookie_jar[@key] = options
- end
- end
-
- def load_session(env)
- stale_session_check! do
- sid = current_session_id(env)
- sid, session = get_session(env, sid)
- [sid, session]
- end
- end
-
- def extract_session_id(env)
- stale_session_check! do
- request = ActionDispatch::Request.new(env)
- sid = request.cookies[@key]
- sid ||= request.params[@key] unless @cookie_only
- sid
- end
- end
+ def initialize_sid
+ @default_options.delete(:sidbits)
+ @default_options.delete(:secure_random)
+ end
+ end
- def current_session_id(env)
- env[ENV_SESSION_OPTIONS_KEY][:id]
- end
+ module StaleSessionCheck
+ def load_session(env)
+ stale_session_check! { super }
+ end
- def ensure_session_key!
- if @key.blank?
- raise ArgumentError, 'A key is required to write a ' +
- 'cookie containing the session data. Use ' +
- 'config.session_store SESSION_STORE, { :key => ' +
- '"_myapp_session" } in config/application.rb'
- end
- end
+ def extract_session_id(env)
+ stale_session_check! { super }
+ end
- def stale_session_check!
- yield
- rescue ArgumentError => argument_error
- if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
- begin
- # Note that the regexp does not allow $1 to end with a ':'
- $1.constantize
- rescue LoadError, NameError => const_error
- raise ActionDispatch::Session::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n"
- end
- retry
- else
- raise
+ def stale_session_check!
+ yield
+ rescue ArgumentError => argument_error
+ if argument_error.message =~ %r{undefined class/module ([\w:]*\w)}
+ begin
+ # Note that the regexp does not allow $1 to end with a ':'
+ $1.constantize
+ rescue LoadError, NameError => const_error
+ raise ActionDispatch::Session::SessionRestoreError, "Session contains objects whose class definition isn't available.\nRemember to require the classes for all objects kept in the session.\n(Original exception: #{const_error.message} [#{const_error.class}])\n"
end
+ retry
+ else
+ raise
end
+ end
+ end
- def exists?(env)
- current_session_id(env).present?
- end
-
- def get_session(env, sid)
- raise '#get_session needs to be implemented.'
- end
+ class AbstractStore < Rack::Session::Abstract::ID
+ include Compatibility
+ include StaleSessionCheck
- def set_session(env, sid, session_data)
- raise '#set_session needs to be implemented and should return ' <<
- 'the value to be stored in the cookie (usually the sid)'
- end
+ def destroy_session(env, sid, options)
+ ActiveSupport::Deprecation.warn "Implementing #destroy in session stores is deprecated. " <<
+ "Please implement destroy_session(env, session_id, options) instead."
+ destroy(env)
+ end
- def destroy(env)
- raise '#destroy needs to be implemented.'
- end
+ def destroy(env)
+ raise '#destroy needs to be implemented.'
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index ca1494425f..9c9ccc62f5 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -1,5 +1,7 @@
require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/object/blank'
+require 'action_dispatch/middleware/session/abstract_store'
+require 'rack/session/cookie'
module ActionDispatch
module Session
@@ -38,58 +40,32 @@ module ActionDispatch
# "rake secret" and set the key in config/initializers/secret_token.rb.
#
# Note that changing digest or secret invalidates all existing sessions!
- class CookieStore < AbstractStore
-
- def initialize(app, options = {})
- super(app, options.merge!(:cookie_only => true))
- freeze
- end
+ class CookieStore < Rack::Session::Cookie
+ include Compatibility
+ include StaleSessionCheck
private
- def load_session(env)
- data = unpacked_cookie_data(env)
- data = persistent_session_id!(data)
- [data["session_id"], data]
- end
-
- def extract_session_id(env)
- if data = unpacked_cookie_data(env)
- data["session_id"]
- else
- nil
- end
- end
-
- def unpacked_cookie_data(env)
- env["action_dispatch.request.unsigned_session_cookie"] ||= begin
- stale_session_check! do
- request = ActionDispatch::Request.new(env)
- if data = request.cookie_jar.signed[@key]
- data.stringify_keys!
- end
- data || {}
+ def unpacked_cookie_data(env)
+ env["action_dispatch.request.unsigned_session_cookie"] ||= begin
+ stale_session_check! do
+ request = ActionDispatch::Request.new(env)
+ if data = request.cookie_jar.signed[@key]
+ data.stringify_keys!
end
+ data || {}
end
end
+ end
- def set_cookie(request, options)
- request.cookie_jar.signed[@key] = options
- end
-
- def set_session(env, sid, session_data)
- persistent_session_id!(session_data, sid)
- end
-
- def destroy(env)
- # session data is stored on client; nothing to do here
- end
+ def set_session(env, sid, session_data, options)
+ persistent_session_id!(session_data, sid)
+ end
- def persistent_session_id!(data, sid=nil)
- data ||= {}
- data["session_id"] ||= sid || generate_sid
- data
- end
+ def set_cookie(env, session_id, cookie)
+ request = ActionDispatch::Request.new(env)
+ request.cookie_jar.signed[@key] = cookie
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb
index 28e3dbd732..4dd9a946c2 100644
--- a/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/mem_cache_store.rb
@@ -1,56 +1,17 @@
+require 'action_dispatch/middleware/session/abstract_store'
+require 'rack/session/memcache'
+
module ActionDispatch
module Session
- class MemCacheStore < AbstractStore
+ class MemCacheStore < Rack::Session::Memcache
+ include Compatibility
+ include StaleSessionCheck
+
def initialize(app, options = {})
require 'memcache'
-
- # Support old :expires option
options[:expire_after] ||= options[:expires]
-
- super
-
- @default_options = {
- :namespace => 'rack:session',
- :memcache_server => 'localhost:11211'
- }.merge(@default_options)
-
- @pool = options[:cache] || MemCache.new(@default_options[:memcache_server], @default_options)
- unless @pool.servers.any? { |s| s.alive? }
- raise "#{self} unable to find server during initialization."
- end
- @mutex = Mutex.new
-
super
end
-
- private
- def get_session(env, sid)
- sid ||= generate_sid
- begin
- session = @pool.get(sid) || {}
- rescue MemCache::MemCacheError, Errno::ECONNREFUSED
- session = {}
- end
- [sid, session]
- end
-
- def set_session(env, sid, session_data)
- options = env['rack.session.options']
- expiry = options[:expire_after] || 0
- @pool.set(sid, session_data, expiry)
- sid
- rescue MemCache::MemCacheError, Errno::ECONNREFUSED
- false
- end
-
- def destroy(env)
- if sid = current_session_id(env)
- @pool.delete(sid)
- end
- rescue MemCache::MemCacheError, Errno::ECONNREFUSED
- false
- end
-
end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index cf13938331..913b899e20 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -6,13 +6,13 @@ module ActionDispatch
@at, @root = at.chomp('/'), root.chomp('/')
@compiled_at = (Regexp.compile(/^#{Regexp.escape(at)}/) unless @at.blank?)
@compiled_root = Regexp.compile(/^#{Regexp.escape(root)}/)
- @file_server = ::Rack::File.new(root)
+ @file_server = ::Rack::File.new(@root)
end
def match?(path)
path = path.dup
- if @compiled_at.blank? || path.sub!(@compiled_at, '')
- full_path = File.join(@root, ::Rack::Utils.unescape(path))
+ if !@compiled_at || path.sub!(@compiled_at, '')
+ full_path = path.empty? ? @root : File.join(@root, ::Rack::Utils.unescape(path))
paths = "#{full_path}#{ext}"
matches = Dir[paths]
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 47aed0273c..bf10f81127 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -171,13 +171,13 @@ module ActionDispatch
end
def blocks
+ block = @scope[:blocks] || []
+
if @options[:constraints].present? && !@options[:constraints].is_a?(Hash)
- block = @options[:constraints]
- else
- block = nil
+ block << @options[:constraints]
end
- ((@scope[:blocks] || []) + [ block ]).compact
+ block
end
def constraints
diff --git a/actionpack/lib/action_dispatch/testing/performance_test.rb b/actionpack/lib/action_dispatch/testing/performance_test.rb
index 33a5c68b9d..d6c98b4db7 100644
--- a/actionpack/lib/action_dispatch/testing/performance_test.rb
+++ b/actionpack/lib/action_dispatch/testing/performance_test.rb
@@ -11,9 +11,8 @@ begin
# formats are written, so you'll have two output files per test method.
class PerformanceTest < ActionDispatch::IntegrationTest
include ActiveSupport::Testing::Performance
- include ActiveSupport::Testing::Default
end
end
rescue NameError
$stderr.puts "Specify ruby-prof as application's dependency in Gemfile to run benchmarks."
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 1836baaf12..3cd8b02bc4 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -791,7 +791,7 @@ module ActionView
options["incremental"] = true unless options.has_key?("incremental")
end
- InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("search", options)
+ InstanceTag.new(object_name, method, self, options.delete("object")).to_input_field_tag("search", options)
end
# Returns a text_field of type "tel".
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index 1c3ca78d28..da42d94318 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -476,39 +476,36 @@ module ActionView
html_options = html_options.stringify_keys
encode = html_options.delete("encode").to_s
- cc, bcc, subject, body = html_options.delete("cc"), html_options.delete("bcc"), html_options.delete("subject"), html_options.delete("body")
- extras = []
- extras << "cc=#{Rack::Utils.escape(cc).gsub("+", "%20")}" unless cc.nil?
- extras << "bcc=#{Rack::Utils.escape(bcc).gsub("+", "%20")}" unless bcc.nil?
- extras << "body=#{Rack::Utils.escape(body).gsub("+", "%20")}" unless body.nil?
- extras << "subject=#{Rack::Utils.escape(subject).gsub("+", "%20")}" unless subject.nil?
+ extras = %w{ cc bcc body subject }.map { |item|
+ option = html_options.delete(item) || next
+ "#{item}=#{Rack::Utils.escape(option).gsub("+", "%20")}"
+ }.compact
extras = extras.empty? ? '' : '?' + html_escape(extras.join('&'))
email_address_obfuscated = email_address.dup
- email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.has_key?("replace_at")
- email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
-
- string = ''
-
- if encode == "javascript"
- "document.write('#{content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe))}');".each_byte do |c|
- string << sprintf("%%%x", c)
- end
+ email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.key?("replace_at")
+ email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.key?("replace_dot")
+
+ case encode
+ when "javascript"
+ string =
+ "document.write('#{content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe))}');".unpack('C*').map { |c|
+ sprintf("%%%x", c)
+ }.join
"<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>".html_safe
- elsif encode == "hex"
- email_address_encoded = ''
- email_address_obfuscated.each_byte do |c|
- email_address_encoded << sprintf("&#%d;", c)
- end
-
- protocol = 'mailto:'
- protocol.each_byte { |c| string << sprintf("&#%d;", c) }
-
- email_address.each_byte do |c|
+ when "hex"
+ email_address_encoded = email_address_obfuscated.unpack('C*').map {|c|
+ sprintf("&#%d;", c)
+ }.join
+
+ string = 'mailto:'.unpack('C*').map { |c|
+ sprintf("&#%d;", c)
+ }.join + email_address.unpack('C*').map { |c|
char = c.chr
- string << (char =~ /\w/ ? sprintf("%%%x", c) : char)
- end
+ char =~ /\w/ ? sprintf("%%%x", c) : char
+ }.join
+
content_tag "a", name || email_address_encoded.html_safe, html_options.merge("href" => "#{string}#{extras}".html_safe)
else
content_tag "a", name || email_address_obfuscated.html_safe, html_options.merge("href" => "mailto:#{email_address}#{extras}".html_safe)
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 915c2f90d7..4026f7a40e 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -103,7 +103,7 @@ module ActionView
end
def render(options = {}, local_assigns = {}, &block)
- view.assign(_assigns)
+ view.assign(view_assigns)
@rendered << output = view.render(options, local_assigns, &block)
output
end
@@ -169,15 +169,19 @@ module ActionView
alias_method :_view, :view
- EXCLUDE_IVARS = %w{
+ INTERNAL_IVARS = %w{
+ @__name__
@_assertion_wrapped
+ @_assertions
@_result
+ @_routes
@controller
@layouts
@locals
@method_name
@output_buffer
@partials
+ @passed
@rendered
@request
@routes
@@ -187,12 +191,24 @@ module ActionView
@view_context_class
}
- def _instance_variables
- instance_variables.map(&:to_s) - EXCLUDE_IVARS
+ def _user_defined_ivars
+ instance_variables.map(&:to_s) - INTERNAL_IVARS
+ end
+
+ # Returns a Hash of instance variables and their values, as defined by
+ # the user in the test case, which are then assigned to the view being
+ # rendered. This is generally intended for internal use and extension
+ # frameworks.
+ def view_assigns
+ Hash[_user_defined_ivars.map do |var|
+ [var[1, var.length].to_sym, instance_variable_get(var)]
+ end]
end
def _assigns
- _instance_variables.map { |var| [var[1..-1].to_sym, instance_variable_get(var)] }
+ ActiveSupport::Deprecation.warn "ActionView::TestCase#_assigns is deprecated and will be removed in future versions. " <<
+ "Please use view_assigns instead."
+ view_assigns
end
def _routes
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 3540af13ac..470b36dbe2 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -308,3 +308,38 @@ module ActionView
end
end
end
+
+class Workshop
+ extend ActiveModel::Naming
+ include ActiveModel::Conversion
+ attr_accessor :id
+
+ def initialize(id)
+ @id = id
+ end
+
+ def persisted?
+ id.present?
+ end
+
+ def to_s
+ id.to_s
+ end
+end
+
+module ActionDispatch
+ class ShowExceptions
+ private
+ remove_method :public_path
+ def public_path
+ "#{FIXTURE_LOAD_PATH}/public"
+ end
+
+ remove_method :logger
+ # Silence logger
+ def logger
+ nil
+ end
+ end
+end
+
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index dfc90943e1..3a8a37d967 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -758,12 +758,12 @@ class ControllerWithSymbolAsFilter < PostsController
def without_exception
# Do stuff...
- 1 + 1
+ wtf = 1 + 1
yield
# Do stuff...
- 1 + 1
+ wtf += 1
end
end
diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb
index 4ff39fb76c..f0d62b0b13 100644
--- a/actionpack/test/controller/integration_test.rb
+++ b/actionpack/test/controller/integration_test.rb
@@ -167,7 +167,7 @@ end
class IntegrationTestTest < Test::Unit::TestCase
def setup
- @test = ::ActionDispatch::IntegrationTest.new(:default_test)
+ @test = ::ActionDispatch::IntegrationTest.new(:app)
@test.class.stubs(:fixture_table_names).returns([])
@session = @test.open_session
end
diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb
index 10873708fe..90c944d890 100644
--- a/actionpack/test/controller/log_subscriber_test.rb
+++ b/actionpack/test/controller/log_subscriber_test.rb
@@ -151,8 +151,8 @@ class ACLogSubscriberTest < ActionController::TestCase
wait
assert_equal 4, logs.size
- assert_match /Exist fragment\? views\/foo%bar/, logs[1]
- assert_match /Write fragment views\/foo%bar/, logs[2]
+ assert_match(/Exist fragment\? views\/foo%bar/, logs[1])
+ assert_match(/Write fragment views\/foo%bar/, logs[2])
ensure
@controller.config.perform_caching = true
end
diff --git a/actionpack/test/controller/new_base/etag_test.rb b/actionpack/test/controller/new_base/etag_test.rb
deleted file mode 100644
index 2bca5aec6a..0000000000
--- a/actionpack/test/controller/new_base/etag_test.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-require 'abstract_unit'
-
-module Etags
- class BasicController < ActionController::Base
- self.view_paths = [ActionView::FixtureResolver.new(
- "etags/basic/base.html.erb" => "Hello from without_layout.html.erb",
- "layouts/etags.html.erb" => "teh <%= yield %> tagz"
- )]
-
- def without_layout
- render :action => "base"
- end
-
- def with_layout
- render :action => "base", :layout => "etags"
- end
- end
-
- class EtagTest < Rack::TestCase
- describe "Rendering without any special etag options returns an etag that is an MD5 hash of its text"
-
- test "an action without a layout" do
- get "/etags/basic/without_layout"
-
- body = "Hello from without_layout.html.erb"
- assert_body body
- assert_header "Etag", etag_for(body)
- assert_status 200
- end
-
- test "an action with a layout" do
- get "/etags/basic/with_layout"
-
- body = "teh Hello from without_layout.html.erb tagz"
- assert_body body
- assert_header "Etag", etag_for(body)
- assert_status 200
- end
-
- private
-
- def etag_for(text)
- %("#{Digest::MD5.hexdigest(text)}")
- end
- end
-end
diff --git a/actionpack/test/controller/record_identifier_test.rb b/actionpack/test/controller/record_identifier_test.rb
index 835a0e970b..f3e5ff8a47 100644
--- a/actionpack/test/controller/record_identifier_test.rb
+++ b/actionpack/test/controller/record_identifier_test.rb
@@ -1,30 +1,5 @@
require 'abstract_unit'
-
-class Comment
- extend ActiveModel::Naming
- include ActiveModel::Conversion
-
- attr_reader :id
- def to_key; id ? [id] : nil end
- def save; @id = 1 end
- def new_record?; @id.nil? end
- def name
- @id.nil? ? 'new comment' : "comment ##{@id}"
- end
-end
-
-class Sheep
- extend ActiveModel::Naming
- include ActiveModel::Conversion
-
- attr_reader :id
- def to_key; id ? [id] : nil end
- def save; @id = 1 end
- def new_record?; @id.nil? end
- def name
- @id.nil? ? 'new sheep' : "sheep ##{@id}"
- end
-end
+require 'controller/fake_models'
class RecordIdentifierTest < Test::Unit::TestCase
include ActionController::RecordIdentifier
diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb
index b00142c92d..92d4a6d98b 100644
--- a/actionpack/test/controller/redirect_test.rb
+++ b/actionpack/test/controller/redirect_test.rb
@@ -3,24 +3,6 @@ require 'abstract_unit'
class WorkshopsController < ActionController::Base
end
-class Workshop
- extend ActiveModel::Naming
- include ActiveModel::Conversion
- attr_accessor :id
-
- def initialize(id)
- @id = id
- end
-
- def persisted?
- id.present?
- end
-
- def to_s
- id.to_s
- end
-end
-
class RedirectController < ActionController::Base
def simple_redirect
redirect_to :action => "hello_world"
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index 7ca784c467..fca8de60bc 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -99,11 +99,6 @@ class TestController < ActionController::Base
render :template => "test/hello_world"
end
- def render_hello_world_with_etag_set
- response.etag = "hello_world"
- render :template => "test/hello_world"
- end
-
# :ported: compatibility
def render_hello_world_with_forward_slash
render :template => "/test/hello_world"
@@ -1386,119 +1381,6 @@ class ExpiresInRenderTest < ActionController::TestCase
end
end
-
-class EtagRenderTest < ActionController::TestCase
- tests TestController
-
- def setup
- super
- @request.host = "www.nextangle.com"
- @expected_bang_etag = etag_for(expand_key([:foo, 123]))
- end
-
- def test_render_blank_body_shouldnt_set_etag
- get :blank_response
- assert !@response.etag?
- end
-
- def test_render_200_should_set_etag
- get :render_hello_world_from_variable
- assert_equal etag_for("hello david"), @response.headers['ETag']
- assert_equal "max-age=0, private, must-revalidate", @response.headers['Cache-Control']
- end
-
- def test_render_against_etag_request_should_304_when_match
- @request.if_none_match = etag_for("hello david")
- get :render_hello_world_from_variable
- assert_equal 304, @response.status.to_i
- assert @response.body.empty?
- end
-
- def test_render_against_etag_request_should_have_no_content_length_when_match
- @request.if_none_match = etag_for("hello david")
- get :render_hello_world_from_variable
- assert !@response.headers.has_key?("Content-Length")
- end
-
- def test_render_against_etag_request_should_200_when_no_match
- @request.if_none_match = etag_for("hello somewhere else")
- get :render_hello_world_from_variable
- assert_equal 200, @response.status.to_i
- assert !@response.body.empty?
- end
-
- def test_render_should_not_set_etag_when_last_modified_has_been_specified
- get :render_hello_world_with_last_modified_set
- assert_equal 200, @response.status.to_i
- assert_not_nil @response.last_modified
- assert_nil @response.etag
- assert_present @response.body
- end
-
- def test_render_with_etag
- get :render_hello_world_from_variable
- expected_etag = etag_for('hello david')
- assert_equal expected_etag, @response.headers['ETag']
- @response = ActionController::TestResponse.new
-
- @request.if_none_match = expected_etag
- get :render_hello_world_from_variable
- assert_equal 304, @response.status.to_i
-
- @response = ActionController::TestResponse.new
- @request.if_none_match = "\"diftag\""
- get :render_hello_world_from_variable
- assert_equal 200, @response.status.to_i
- end
-
- def render_with_404_shouldnt_have_etag
- get :render_custom_code
- assert_nil @response.headers['ETag']
- end
-
- def test_etag_should_not_be_changed_when_already_set
- get :render_hello_world_with_etag_set
- assert_equal etag_for("hello_world"), @response.headers['ETag']
- end
-
- def test_etag_should_govern_renders_with_layouts_too
- get :builder_layout_test
- assert_equal "<wrapper>\n<html>\n <p>Hello </p>\n<p>This is grand!</p>\n</html>\n</wrapper>\n", @response.body
- assert_equal etag_for("<wrapper>\n<html>\n <p>Hello </p>\n<p>This is grand!</p>\n</html>\n</wrapper>\n"), @response.headers['ETag']
- end
-
- def test_etag_with_bang_should_set_etag
- get :conditional_hello_with_bangs
- assert_equal @expected_bang_etag, @response.headers["ETag"]
- assert_response :success
- end
-
- def test_etag_with_bang_should_obey_if_none_match
- @request.if_none_match = @expected_bang_etag
- get :conditional_hello_with_bangs
- assert_response :not_modified
- end
-
- def test_etag_with_public_true_should_set_header
- get :conditional_hello_with_public_header
- assert_equal "public", @response.headers['Cache-Control']
- end
-
- def test_etag_with_public_true_should_set_header_and_retain_other_headers
- get :conditional_hello_with_public_header_and_expires_at
- assert_equal "max-age=60, public", @response.headers['Cache-Control']
- end
-
- protected
- def etag_for(text)
- %("#{Digest::MD5.hexdigest(text)}")
- end
-
- def expand_key(args)
- ActiveSupport::Cache.expand_cache_key(args)
- end
-end
-
class LastModifiedRenderTest < ActionController::TestCase
tests TestController
diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb
index a2418bb7c0..c445285538 100644
--- a/actionpack/test/controller/rescue_test.rb
+++ b/actionpack/test/controller/rescue_test.rb
@@ -1,21 +1,5 @@
require 'abstract_unit'
-module ActionDispatch
- class ShowExceptions
- private
- remove_method :public_path
- def public_path
- "#{FIXTURE_LOAD_PATH}/public"
- end
-
- remove_method :logger
- # Silence logger
- def logger
- nil
- end
- end
-end
-
class RescueController < ActionController::Base
class NotAuthorized < StandardError
end
diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
index 073dd3ddad..3ff558ec5a 100644
--- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
@@ -36,7 +36,6 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
assert_equal 'bar', params['foo']
file = params['file']
- assert_kind_of Tempfile, file
assert_equal 'file.txt', file.original_filename
assert_equal "text/plain", file.content_type
assert_equal 'contents', file.read
@@ -49,8 +48,6 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
file = params['file']
foo = params['foo']
- assert_kind_of Tempfile, file
-
assert_equal 'file.txt', file.original_filename
assert_equal "text/plain", file.content_type
@@ -64,8 +61,6 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
file = params['file']
- assert_kind_of Tempfile, file
-
assert_equal 'file.txt', file.original_filename
assert_equal "text/plain", file.content_type
assert_equal(('a' * 20480), file.read)
@@ -77,13 +72,11 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
assert_equal 'bar', params['foo']
file = params['file']
- assert_kind_of Tempfile, file
assert_equal 'file.csv', file.original_filename
assert_nil file.content_type
assert_equal 'contents', file.read
file = params['flowers']
- assert_kind_of Tempfile, file
assert_equal 'flowers.jpg', file.original_filename
assert_equal "image/jpeg", file.content_type
assert_equal 19512, file.size
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index cd0418c338..be6398fead 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -11,9 +11,7 @@ class ResponseTest < ActiveSupport::TestCase
status, headers, body = @response.to_a
assert_equal 200, status
assert_equal({
- "Content-Type" => "text/html; charset=utf-8",
- "Cache-Control" => "max-age=0, private, must-revalidate",
- "ETag" => '"65a8e27d8879283831b664bd8b7f0ad4"'
+ "Content-Type" => "text/html; charset=utf-8"
}, headers)
parts = []
@@ -27,9 +25,7 @@ class ResponseTest < ActiveSupport::TestCase
status, headers, body = @response.to_a
assert_equal 200, status
assert_equal({
- "Content-Type" => "text/html; charset=utf-8",
- "Cache-Control" => "max-age=0, private, must-revalidate",
- "ETag" => '"ebb5e89e8a94e9dd22abf5d915d112b2"'
+ "Content-Type" => "text/html; charset=utf-8"
}, headers)
end
@@ -41,8 +37,7 @@ class ResponseTest < ActiveSupport::TestCase
status, headers, body = @response.to_a
assert_equal 200, status
assert_equal({
- "Content-Type" => "text/html; charset=utf-8",
- "Cache-Control" => "no-cache"
+ "Content-Type" => "text/html; charset=utf-8"
}, headers)
parts = []
diff --git a/actionpack/test/dispatch/session/cookie_store_test.rb b/actionpack/test/dispatch/session/cookie_store_test.rb
index 3489f628ed..256d0781c7 100644
--- a/actionpack/test/dispatch/session/cookie_store_test.rb
+++ b/actionpack/test/dispatch/session/cookie_store_test.rb
@@ -53,18 +53,6 @@ class CookieStoreTest < ActionDispatch::IntegrationTest
def rescue_action(e) raise end
end
- def test_raises_argument_error_if_missing_session_key
- assert_raise(ArgumentError, nil.inspect) {
- ActionDispatch::Session::CookieStore.new(nil,
- :key => nil, :secret => SessionSecret)
- }
-
- assert_raise(ArgumentError, ''.inspect) {
- ActionDispatch::Session::CookieStore.new(nil,
- :key => '', :secret => SessionSecret)
- }
- end
-
def test_setting_session_value
with_test_route_set do
get '/set_session_value'
diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb
index 4ede1ab47c..ce6c397e32 100644
--- a/actionpack/test/dispatch/show_exceptions_test.rb
+++ b/actionpack/test/dispatch/show_exceptions_test.rb
@@ -1,19 +1,5 @@
require 'abstract_unit'
-module ActionDispatch
- class ShowExceptions
- private
- def public_path
- "#{FIXTURE_LOAD_PATH}/public"
- end
-
- # Silence logger
- def logger
- nil
- end
- end
-end
-
class ShowExceptionsTest < ActionDispatch::IntegrationTest
Boomer = lambda do |env|
req = ActionDispatch::Request.new(env)
diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb
index 2eb82fc5d8..655745a848 100644
--- a/actionpack/test/dispatch/static_test.rb
+++ b/actionpack/test/dispatch/static_test.rb
@@ -2,30 +2,37 @@ require 'abstract_unit'
module StaticTests
def test_serves_dynamic_content
- assert_equal "Hello, World!", get("/nofile")
+ assert_equal "Hello, World!", get("/nofile").body
end
def test_serves_static_index_at_root
- assert_equal "/index.html", get("/index.html")
- assert_equal "/index.html", get("/index")
- assert_equal "/index.html", get("/")
+ assert_html "/index.html", get("/index.html")
+ assert_html "/index.html", get("/index")
+ assert_html "/index.html", get("/")
+ assert_html "/index.html", get("")
end
def test_serves_static_file_in_directory
- assert_equal "/foo/bar.html", get("/foo/bar.html")
- assert_equal "/foo/bar.html", get("/foo/bar/")
- assert_equal "/foo/bar.html", get("/foo/bar")
+ assert_html "/foo/bar.html", get("/foo/bar.html")
+ assert_html "/foo/bar.html", get("/foo/bar/")
+ assert_html "/foo/bar.html", get("/foo/bar")
end
def test_serves_static_index_file_in_directory
- assert_equal "/foo/index.html", get("/foo/index.html")
- assert_equal "/foo/index.html", get("/foo/")
- assert_equal "/foo/index.html", get("/foo")
+ assert_html "/foo/index.html", get("/foo/index.html")
+ assert_html "/foo/index.html", get("/foo/")
+ assert_html "/foo/index.html", get("/foo")
end
private
+
+ def assert_html(body, response)
+ assert_equal body, response.body
+ assert_equal "text/html", response.headers["Content-Type"]
+ end
+
def get(path)
- Rack::MockRequest.new(@app).request("GET", path).body
+ Rack::MockRequest.new(@app).request("GET", path)
end
end
@@ -59,16 +66,16 @@ class MultipleDirectorisStaticTest < ActiveSupport::TestCase
include StaticTests
test "serves files from other mounted directories" do
- assert_equal "/blog/index.html", get("/blog/index.html")
- assert_equal "/blog/index.html", get("/blog/index")
- assert_equal "/blog/index.html", get("/blog/")
+ assert_html "/blog/index.html", get("/blog/index.html")
+ assert_html "/blog/index.html", get("/blog/index")
+ assert_html "/blog/index.html", get("/blog/")
- assert_equal "/blog/blog.html", get("/blog/blog/")
- assert_equal "/blog/blog.html", get("/blog/blog.html")
- assert_equal "/blog/blog.html", get("/blog/blog")
+ assert_html "/blog/blog.html", get("/blog/blog/")
+ assert_html "/blog/blog.html", get("/blog/blog.html")
+ assert_html "/blog/blog.html", get("/blog/blog")
- assert_equal "/blog/subdir/index.html", get("/blog/subdir/index.html")
- assert_equal "/blog/subdir/index.html", get("/blog/subdir/")
- assert_equal "/blog/subdir/index.html", get("/blog/subdir")
+ assert_html "/blog/subdir/index.html", get("/blog/subdir/index.html")
+ assert_html "/blog/subdir/index.html", get("/blog/subdir/")
+ assert_html "/blog/subdir/index.html", get("/blog/subdir")
end
end
diff --git a/actionpack/test/dispatch/uploaded_file_test.rb b/actionpack/test/dispatch/uploaded_file_test.rb
new file mode 100644
index 0000000000..b51697b930
--- /dev/null
+++ b/actionpack/test/dispatch/uploaded_file_test.rb
@@ -0,0 +1,58 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ class UploadedFileTest < ActiveSupport::TestCase
+ def test_constructor_with_argument_error
+ assert_raises(ArgumentError) do
+ Http::UploadedFile.new({})
+ end
+ end
+
+ def test_original_filename
+ uf = Http::UploadedFile.new(:filename => 'foo', :tempfile => Object.new)
+ assert_equal 'foo', uf.original_filename
+ end
+
+ def test_content_type
+ uf = Http::UploadedFile.new(:type => 'foo', :tempfile => Object.new)
+ assert_equal 'foo', uf.content_type
+ end
+
+ def test_headers
+ uf = Http::UploadedFile.new(:head => 'foo', :tempfile => Object.new)
+ assert_equal 'foo', uf.headers
+ end
+
+ def test_tempfile
+ uf = Http::UploadedFile.new(:tempfile => 'foo')
+ assert_equal 'foo', uf.tempfile
+ end
+
+ def test_delegates_to_tempfile
+ tf = Class.new { def read; 'thunderhorse' end }
+ uf = Http::UploadedFile.new(:tempfile => tf.new)
+ assert_equal 'thunderhorse', uf.read
+ end
+
+ def test_delegates_to_tempfile_with_params
+ tf = Class.new { def read *args; args end }
+ uf = Http::UploadedFile.new(:tempfile => tf.new)
+ assert_equal %w{ thunder horse }, uf.read(*%w{ thunder horse })
+ end
+
+ def test_delegate_respects_respond_to?
+ tf = Class.new { def read; yield end; private :read }
+ uf = Http::UploadedFile.new(:tempfile => tf.new)
+ assert_raises(NoMethodError) do
+ uf.read
+ end
+ end
+
+ def test_respond_to?
+ tf = Class.new { def read; yield end }
+ uf = Http::UploadedFile.new(:tempfile => tf.new)
+ assert uf.respond_to?(:headers), 'responds to headers'
+ assert uf.respond_to?(:read), 'responds to read'
+ end
+ end
+end
diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb
index 8cb3b4940a..dba632e6df 100644
--- a/actionpack/test/lib/controller/fake_models.rb
+++ b/actionpack/test/lib/controller/fake_models.rb
@@ -55,6 +55,11 @@ class Post < Struct.new(:title, :author_name, :body, :secret, :written_on, :cost
alias_method :secret?, :secret
+ def initialize(*args)
+ super
+ @persisted = false
+ end
+
def persisted=(boolean)
@persisted = boolean
end
@@ -130,6 +135,20 @@ class CommentRelevance
end
end
+class Sheep
+ extend ActiveModel::Naming
+ include ActiveModel::Conversion
+
+ attr_reader :id
+ def to_key; id ? [id] : nil end
+ def save; @id = 1 end
+ def new_record?; @id.nil? end
+ def name
+ @id.nil? ? 'new sheep' : "sheep ##{@id}"
+ end
+end
+
+
class TagRelevance
extend ActiveModel::Naming
include ActiveModel::Conversion
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index abc98ebe69..8809e510fb 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -761,6 +761,20 @@ class FormHelperTest < ActionView::TestCase
assert_dom_equal expected, output_buffer
end
+
+ def test_form_for_with_search_field
+ # Test case for bug which would emit an "object" attribute
+ # when used with form_for using a search_field form helper
+ form_for(Post.new, :url => "/search", :html => { :id => 'search-post' }) do |f|
+ concat f.search_field(:title)
+ end
+
+ expected = whole_form("/search", "search-post", "new_post") do
+ "<input name='post[title]' size='30' type='search' id='post_title' />"
+ end
+
+ assert_dom_equal expected, output_buffer
+ end
def test_form_for_with_remote
form_for(@post, :url => '/', :remote => true, :html => { :id => 'create-post', :method => :put }) do |f|
@@ -1737,4 +1751,5 @@ class FormHelperTest < ActionView::TestCase
def protect_against_forgery?
false
end
+
end
diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb
index 8526db61cc..a745999622 100644
--- a/actionpack/test/template/test_case_test.rb
+++ b/actionpack/test/template/test_case_test.rb
@@ -116,6 +116,37 @@ module ActionView
end
end
+ class AssignsTest < ActionView::TestCase
+ setup do
+ ActiveSupport::Deprecation.stubs(:warn)
+ end
+
+ test "_assigns delegates to user_defined_ivars" do
+ self.expects(:view_assigns)
+ _assigns
+ end
+
+ test "_assigns is deprecated" do
+ ActiveSupport::Deprecation.expects(:warn)
+ _assigns
+ end
+ end
+
+ class ViewAssignsTest < ActionView::TestCase
+ test "view_assigns returns a Hash of user defined ivars" do
+ @a = 'b'
+ @c = 'd'
+ assert_equal({:a => 'b', :c => 'd'}, view_assigns)
+ end
+
+ test "view_assigns excludes internal ivars" do
+ INTERNAL_IVARS.each do |ivar|
+ assert defined?(ivar), "expected #{ivar} to be defined"
+ assert !view_assigns.keys.include?(ivar.sub('@','').to_sym), "expected #{ivar} to be excluded from view_assigns"
+ end
+ end
+ end
+
class HelperExposureTest < ActionView::TestCase
helper(Module.new do
def render_from_helper
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index db8fd82aeb..98276da559 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -547,24 +547,6 @@ class LinkToUnlessCurrentWithControllerTest < ActionController::TestCase
end
end
-class Workshop
- extend ActiveModel::Naming
- include ActiveModel::Conversion
- attr_accessor :id
-
- def initialize(id)
- @id = id
- end
-
- def persisted?
- id.present?
- end
-
- def to_s
- id.to_s
- end
-end
-
class Session
extend ActiveModel::Naming
include ActiveModel::Conversion
diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb
index 2d580fd325..adb71f788f 100644
--- a/activemodel/lib/active_model/naming.rb
+++ b/activemodel/lib/active_model/naming.rb
@@ -129,7 +129,11 @@ module ActiveModel
private
def self.model_name_from_record_or_class(record_or_class)
- (record_or_class.is_a?(Class) ? record_or_class : record_or_class.class).model_name
+ (record_or_class.is_a?(Class) ? record_or_class : convert_to_model(record_or_class).class).model_name
+ end
+
+ def self.convert_to_model(object)
+ object.respond_to?(:to_model) ? object.to_model : object
end
end
diff --git a/activemodel/test/cases/naming_test.rb b/activemodel/test/cases/naming_test.rb
index 40ce4c0e2d..a7dde2c433 100644
--- a/activemodel/test/cases/naming_test.rb
+++ b/activemodel/test/cases/naming_test.rb
@@ -125,6 +125,10 @@ class NamingHelpersTest < Test::Unit::TestCase
@param_key = 'contact'
end
+ def test_to_model_called_on_record
+ assert_equal 'post_named_track_backs', plural(Post::TrackBack.new)
+ end
+
def test_singular
assert_equal @singular, singular(@record)
end
diff --git a/activemodel/test/models/track_back.rb b/activemodel/test/models/track_back.rb
index d137e4ff8f..768c96ecf0 100644
--- a/activemodel/test/models/track_back.rb
+++ b/activemodel/test/models/track_back.rb
@@ -1,4 +1,11 @@
class Post
class TrackBack
+ def to_model
+ NamedTrackBack.new
+ end
+ end
+
+ class NamedTrackBack
+ extend ActiveModel::Naming
end
end \ No newline at end of file
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index e2f2508ae8..f692e5ac89 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -118,7 +118,7 @@ module ActiveRecord
end
ActiveSupport.on_load(:active_record) do
- Arel::Table.engine = Arel::Sql::Engine.new(self)
+ Arel::Table.engine = self
end
I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index 91e0a9f2f8..cb2d9e0a79 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -363,6 +363,7 @@ module ActiveRecord
def include?(record)
return false unless record.is_a?(@reflection.klass)
+ return include_in_memory?(record) if record.new_record?
load_target if @reflection.options[:finder_sql] && !loaded?
return @target.include?(record) if loaded?
exists?(record)
@@ -554,6 +555,17 @@ module ActiveRecord
args.first.kind_of?(Hash) || !(loaded? || @owner.new_record? || @reflection.options[:finder_sql] ||
@target.any? { |record| record.new_record? } || args.first.kind_of?(Integer))
end
+
+ def include_in_memory?(record)
+ if @reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
+ @owner.send(proxy_reflection.through_reflection.name.to_sym).any? do |source|
+ target = source.send(proxy_reflection.source_reflection.name)
+ target.respond_to?(:include?) ? target.include?(record) : target == record
+ end
+ else
+ @target.include?(record)
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 2157a0aded..ff6be4ff19 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -834,7 +834,7 @@ module ActiveRecord #:nodoc:
if self == ActiveRecord::Base
Arel::Table.engine
else
- connection_handler.connection_pools[name] ? Arel::Sql::Engine.new(self) : superclass.arel_engine
+ connection_handler.connection_pools[name] ? self : superclass.arel_engine
end
end
end
@@ -884,13 +884,9 @@ module ActiveRecord #:nodoc:
# single-table inheritance model that makes it possible to create
# objects of different types from the same table.
def instantiate(record)
- find_sti_class(record[inheritance_column]).allocate.instance_eval do
- @attributes, @attributes_cache, @previously_changed, @changed_attributes = record, {}, {}, {}
- @new_record = @readonly = @destroyed = @marked_for_destruction = false
- _run_find_callbacks
- _run_initialize_callbacks
- self
- end
+ model = find_sti_class(record[inheritance_column]).allocate
+ model.init_with('attributes' => record)
+ model
end
def find_sti_class(type_name)
@@ -1274,8 +1270,10 @@ MSG
attrs = expand_hash_conditions_for_aggregates(attrs)
table = Arel::Table.new(self.table_name, :engine => arel_engine, :as => default_table_name)
- builder = PredicateBuilder.new(arel_engine)
- builder.build_from_hash(attrs, table).map{ |b| b.to_sql }.join(' AND ')
+ viz = Arel::Visitors.for(arel_engine)
+ PredicateBuilder.build_from_hash(arel_engine, attrs, table).map { |b|
+ viz.accept b
+ }.join(' AND ')
end
alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions
@@ -1416,6 +1414,24 @@ MSG
populate_with_current_scope_attributes
end
+ # Initialize an empty model object from +coder+. +coder+ must contain
+ # the attributes necessary for initializing an empty model object. For
+ # example:
+ #
+ # class Post < ActiveRecord::Base
+ # end
+ #
+ # post = Post.allocate
+ # post.init_with('attributes' => { 'title' => 'hello world' })
+ # post.title # => 'hello world'
+ def init_with(coder)
+ @attributes = coder['attributes']
+ @attributes_cache, @previously_changed, @changed_attributes = {}, {}, {}
+ @new_record = @readonly = @destroyed = @marked_for_destruction = false
+ _run_find_callbacks
+ _run_initialize_callbacks
+ end
+
# Returns a String, which Action Pack uses for constructing an URL to this
# object. The default implementation returns this record's id as a String,
# or nil if this record's unsaved.
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 194842a9a0..5f14284615 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -975,7 +975,7 @@ module ActiveRecord
def select(sql, name = nil)
fields, rows = select_raw(sql, name)
rows.map do |row|
- Hash[*fields.zip(row).flatten]
+ Hash[fields.zip(row)]
end
end
@@ -1017,11 +1017,11 @@ module ActiveRecord
end
def extract_pg_identifier_from_name(name)
- match_data = name[0,1] == '"' ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
+ match_data = name.start_with?('"') ? name.match(/\"([^\"]+)\"/) : name.match(/([^\.]+)/)
if match_data
- rest = name[match_data[0].length..-1]
- rest = rest[1..-1] if rest[0,1] == "."
+ rest = name[match_data[0].length, name.length]
+ rest = rest[1, rest.length] if rest.start_with? "."
[match_data[1], (rest.length > 0 ? rest : nil)]
end
end
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index e708b3fbcf..9ac18f9939 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -419,9 +419,12 @@ module ActiveRecord
# MigrationProxy is used to defer loading of the actual migration classes
# until they are needed
- class MigrationProxy
+ class MigrationProxy < Struct.new(:name, :version, :filename, :scope)
- attr_accessor :name, :version, :filename, :scope
+ def initialize(name, version, filename, scope)
+ super
+ @migration = nil
+ end
delegate :migrate, :announce, :write, :to=>:migration
@@ -504,26 +507,21 @@ module ActiveRecord
def migrations(path)
files = Dir["#{path}/[0-9]*_*.rb"]
- migrations = files.inject([]) do |klasses, file|
+ seen = Hash.new false
+
+ migrations = files.map do |file|
version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?.rb/).first
raise IllegalMigrationNameError.new(file) unless version
version = version.to_i
+ name = name.camelize
- if klasses.detect { |m| m.version == version }
- raise DuplicateMigrationVersionError.new(version)
- end
+ raise DuplicateMigrationVersionError.new(version) if seen[version]
+ raise DuplicateMigrationNameError.new(name) if seen[[name, scope]]
- if klasses.detect { |m| m.name == name.camelize && m.scope == scope }
- raise DuplicateMigrationNameError.new(name.camelize)
- end
+ seen[version] = seen[[name, scope]] = true
- migration = MigrationProxy.new
- migration.name = name.camelize
- migration.version = version
- migration.filename = file
- migration.scope = scope
- klasses << migration
+ MigrationProxy.new(name, version, file, scope)
end
migrations.sort_by(&:version)
@@ -570,7 +568,7 @@ module ActiveRecord
current = migrations.detect { |m| m.version == current_version }
target = migrations.detect { |m| m.version == @target_version }
- if target.nil? && !@target_version.nil? && @target_version > 0
+ if target.nil? && @target_version && @target_version > 0
raise UnknownMigrationVersionError.new(@target_version)
end
@@ -579,16 +577,18 @@ module ActiveRecord
runnable = migrations[start..finish]
# skip the last migration if we're headed down, but not ALL the way down
- runnable.pop if down? && !target.nil?
+ runnable.pop if down? && target
runnable.each do |migration|
Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
+ seen = migrated.include?(migration.version.to_i)
+
# On our way up, we skip migrating the ones we've already migrated
- next if up? && migrated.include?(migration.version.to_i)
+ next if up? && seen
# On our way down, we skip reverting the ones we've never migrated
- if down? && !migrated.include?(migration.version.to_i)
+ if down? && !seen
migration.announce 'never migrated, skipping'; migration.write
next
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 03862c78e4..d79ef78b4d 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -183,12 +183,16 @@ module ActiveRecord
end
end
- def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
- column = if @klass.column_names.include?(column_name.to_s)
+ def aggregate_column(column_name)
+ if @klass.column_names.include?(column_name.to_s)
Arel::Attribute.new(@klass.unscoped.table, column_name)
else
- Arel::SqlLiteral.new(column_name == :all ? "*" : column_name.to_s)
+ Arel.sql(column_name == :all ? "*" : column_name.to_s)
end
+ end
+
+ def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
+ column = aggregate_column(column_name)
# Postgresql doesn't like ORDER BY when there are no GROUP BY
relation = except(:order)
@@ -209,18 +213,17 @@ module ActiveRecord
group = @klass.connection.adapter_name == 'FrontBase' ? group_alias : group_field
- aggregate_alias = column_alias_for(operation, column_name)
-
- select_statement = if operation == 'count' && column_name == :all
- ["COUNT(*) AS count_all"]
+ if operation == 'count' && column_name == :all
+ aggregate_alias = 'count_all'
else
- [Arel::Attribute.new(@klass.unscoped.table, column_name).send(operation).as(aggregate_alias)]
+ aggregate_alias = column_alias_for(operation, column_name)
end
- select_statement << "#{group_field} AS #{group_alias}"
-
relation = except(:group).group(group)
- relation.select_values = select_statement
+ relation.select_values = [
+ aggregate_column(column_name).send(operation).as(aggregate_alias),
+ "#{group_field} AS #{group_alias}"
+ ]
calculated_data = @klass.connection.select_all(relation.to_sql)
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 0d1307d87e..c5428dccd6 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -1,23 +1,18 @@
module ActiveRecord
- class PredicateBuilder
-
- def initialize(engine)
- @engine = engine
- end
-
- def build_from_hash(attributes, default_table)
+ class PredicateBuilder # :nodoc:
+ def self.build_from_hash(engine, attributes, default_table)
predicates = attributes.map do |column, value|
table = default_table
if value.is_a?(Hash)
- table = Arel::Table.new(column, :engine => @engine)
- build_from_hash(value, table)
+ table = Arel::Table.new(column, :engine => engine)
+ build_from_hash(engine, value, table)
else
column = column.to_s
if column.include?('.')
table_name, column = column.split('.', 2)
- table = Arel::Table.new(table_name, :engine => @engine)
+ table = Arel::Table.new(table_name, :engine => engine)
end
attribute = table[column] || Arel::Attribute.new(table, column)
@@ -38,6 +33,5 @@ module ActiveRecord
predicates.flatten
end
-
end
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index acc42faf7d..f314ff861f 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -196,20 +196,20 @@ module ActiveRecord
arel
end
+ private
+
def build_where(opts, other = [])
case opts
when String, Array
[@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
when Hash
attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
- PredicateBuilder.new(table.engine).build_from_hash(attributes, table)
+ PredicateBuilder.build_from_hash(table.engine, attributes, table)
else
[opts]
end
end
- private
-
def build_joins(relation, joins)
association_joins = []
diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb
index 6faa88ab78..f5331bb8a9 100644
--- a/activerecord/lib/active_record/schema_dumper.rb
+++ b/activerecord/lib/active_record/schema_dumper.rb
@@ -176,13 +176,15 @@ HEADER
def indexes(table, stream)
if (indexes = @connection.indexes(table)).any?
add_index_statements = indexes.map do |index|
- statement_parts = [ ('add_index ' + index.table.inspect) ]
- statement_parts << index.columns.inspect
- statement_parts << (':name => ' + index.name.inspect)
+ statement_parts = [
+ ('add_index ' + index.table.inspect),
+ index.columns.inspect,
+ (':name => ' + index.name.inspect),
+ ]
statement_parts << ':unique => true' if index.unique
- index_lengths = index.lengths.compact if index.lengths.is_a?(Array)
- statement_parts << (':length => ' + Hash[*index.columns.zip(index.lengths).flatten].inspect) if index_lengths.present?
+ index_lengths = (index.lengths || []).compact
+ statement_parts << (':length => ' + Hash[index.columns.zip(index.lengths)].inspect) unless index_lengths.empty?
' ' + statement_parts.join(', ')
end
diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb
index 01cc14b8d6..3fc596e02a 100644
--- a/activerecord/lib/active_record/session_store.rb
+++ b/activerecord/lib/active_record/session_store.rb
@@ -288,6 +288,7 @@ module ActiveRecord
self.session_class = Session
SESSION_RECORD_KEY = 'rack.session.record'
+ ENV_SESSION_OPTIONS_KEY = Rack::Session::Abstract::ENV_SESSION_OPTIONS_KEY
private
def get_session(env, sid)
@@ -299,7 +300,7 @@ module ActiveRecord
end
end
- def set_session(env, sid, session_data)
+ def set_session(env, sid, session_data, options)
Base.silence do
record = get_session_model(env, sid)
record.data = session_data
@@ -316,12 +317,14 @@ module ActiveRecord
sid
end
- def destroy(env)
+ def destroy_session(env, session_id, options)
if sid = current_session_id(env)
Base.silence do
get_session_model(env, sid).destroy
end
end
+
+ generate_sid unless options[:drop]
end
def get_session_model(env, sid)
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index c0be7dfdcc..7e070e1746 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -858,4 +858,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal new_developer.name, "Marcelo"
assert_equal new_developer.salary, 90_000
end
+
+ def test_include_method_in_has_and_belongs_to_many_association_should_return_true_for_instance_added_with_build
+ project = Project.new
+ developer = project.developers.build
+ assert project.developers.include?(developer)
+ end
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 720b7fc386..c9f00fd737 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1267,4 +1267,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal new_comment.type, "SpecialComment"
assert_equal new_comment.post_id, posts(:welcome).id
end
+
+ def test_include_method_in_has_many_association_should_return_true_for_instance_added_with_build
+ post = Post.new
+ comment = post.comments.build
+ assert post.comments.include?(comment)
+ end
end
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index 0dac633852..4b9f49f1ec 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -435,4 +435,18 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal new_subscriber.nick, "marklazz"
assert_equal new_subscriber.name, "Marcelo Giorgi"
end
+
+ def test_include_method_in_association_through_should_return_true_for_instance_added_with_build
+ person = Person.new
+ reference = person.references.build
+ job = reference.build_job
+ assert person.jobs.include?(job)
+ end
+
+ def test_include_method_in_association_through_should_return_true_for_instance_added_with_nested_builds
+ author = Author.new
+ post = author.posts.build
+ comment = post.comments.build
+ assert author.comments.include?(comment)
+ end
end
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index afef31396e..7ec40906d4 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -275,6 +275,17 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 2, Account.count(:firm_id, :conditions => "credit_limit = 50 AND firm_id IS NOT NULL")
end
+ def test_should_count_field_in_joined_table
+ assert_equal 5, Account.count('companies.id', :joins => :firm)
+ assert_equal 4, Account.count('companies.id', :joins => :firm, :distinct => true)
+ end
+
+ def test_should_count_field_in_joined_table_with_group_by
+ c = Account.count('companies.id', :group => 'accounts.firm_id', :joins => :firm)
+
+ [1,6,2,9].each { |firm_id| assert c.keys.include?(firm_id) }
+ end
+
def test_count_with_no_parameters_isnt_deprecated
assert_not_deprecated { Account.count }
end
@@ -335,5 +346,4 @@ class CalculationsTest < ActiveRecord::TestCase
def test_from_option_with_table_different_than_class
assert_equal Account.count(:all), Company.count(:all, :from => 'accounts')
end
-
end
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index 8c09fc4d59..31679b2efe 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -14,6 +14,20 @@ class InheritanceTest < ActiveRecord::TestCase
ActiveRecord::Base.store_full_sti_class = old
end
+ def test_class_with_blank_sti_name
+ company = Company.find(:first)
+ company = company.clone
+ company.extend(Module.new {
+ def read_attribute(name)
+ return ' ' if name == 'type'
+ super
+ end
+ })
+ company.save!
+ company = Company.find(:all).find { |x| x.id == company.id }
+ assert_equal ' ', company.type
+ end
+
def test_class_without_store_full_sti_class_returns_demodulized_name
old = ActiveRecord::Base.store_full_sti_class
ActiveRecord::Base.store_full_sti_class = false
diff --git a/activeresource/lib/active_resource/validations.rb b/activeresource/lib/active_resource/validations.rb
index d3b19ee560..a373e53f11 100644
--- a/activeresource/lib/active_resource/validations.rb
+++ b/activeresource/lib/active_resource/validations.rb
@@ -13,7 +13,7 @@ module ActiveResource
# or not (by passing true)
def from_array(messages, save_cache = false)
clear unless save_cache
- humanized_attributes = @base.attributes.keys.inject({}) { |h, attr_name| h.update(attr_name.humanize => attr_name) }
+ humanized_attributes = Hash[@base.attributes.keys.map { |attr_name| [attr_name.humanize, attr_name] }]
messages.each do |message|
attr_message = humanized_attributes.keys.detect do |attr_name|
if message[0, attr_name.size + 1] == "#{attr_name} "
diff --git a/activeresource/test/cases/format_test.rb b/activeresource/test/cases/format_test.rb
index bed95ef524..fc1a7b8c6f 100644
--- a/activeresource/test/cases/format_test.rb
+++ b/activeresource/test/cases/format_test.rb
@@ -33,7 +33,7 @@ class FormatTest < Test::Unit::TestCase
ActiveResource::HttpMock.respond_to.get "/people.#{format}", {'Accept' => ActiveResource::Formats[format].mime_type}, ActiveResource::Formats[format].encode(@programmers)
remote_programmers = Person.find(:all)
assert_equal 2, remote_programmers.size
- assert remote_programmers.map { |p| p.name }.include? 'David'
+ assert remote_programmers.find { |p| p.name == 'David' }
end
end
end
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index f32b562368..9eee3fc5e3 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -73,7 +73,7 @@ module ActiveSupport
def read_multi(*names)
options = names.extract_options!
options = merged_options(options)
- keys_to_names = names.inject({}){|map, name| map[escape_key(namespaced_key(name, options))] = name; map}
+ keys_to_names = Hash[names.map{|name| [escape_key(namespaced_key(name, options)), name]}]
raw_values = @data.get_multi(keys_to_names.keys, :raw => true)
values = {}
raw_values.each do |key, value|
diff --git a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
index 7a6a0be69d..af30bfc13a 100644
--- a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
+++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
@@ -158,9 +158,9 @@ class Class # :nodoc:
if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
new_inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
else
- new_inheritable_attributes = inheritable_attributes.inject({}) do |memo, (key, value)|
- memo.update(key => value.duplicable? ? value.dup : value)
- end
+ new_inheritable_attributes = Hash[inheritable_attributes.map do |(key, value)|
+ [key, value.duplicable? ? value.dup : value]
+ end]
end
child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes)
diff --git a/activesupport/lib/active_support/core_ext/enumerable.rb b/activesupport/lib/active_support/core_ext/enumerable.rb
index f1103029c8..6ecedc26ef 100644
--- a/activesupport/lib/active_support/core_ext/enumerable.rb
+++ b/activesupport/lib/active_support/core_ext/enumerable.rb
@@ -90,10 +90,7 @@ module Enumerable
# => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
#
def index_by
- inject({}) do |accum, elem|
- accum[yield(elem)] = elem
- accum
- end
+ Hash[map { |elem| [yield(elem), elem] }]
end
# Returns true if the collection has more than 1 element. Functionally equivalent to collection.size > 1.
diff --git a/activesupport/lib/active_support/test_case.rb b/activesupport/lib/active_support/test_case.rb
index ed8c02ba3e..fb52fc7083 100644
--- a/activesupport/lib/active_support/test_case.rb
+++ b/activesupport/lib/active_support/test_case.rb
@@ -24,8 +24,7 @@ module ActiveSupport
else
Assertion = Test::Unit::AssertionFailedError
- require 'active_support/testing/default'
- include ActiveSupport::Testing::Default
+ undef :default_test
end
$tags = {}
diff --git a/activesupport/lib/active_support/testing/default.rb b/activesupport/lib/active_support/testing/default.rb
deleted file mode 100644
index a0bd6303c7..0000000000
--- a/activesupport/lib/active_support/testing/default.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module ActiveSupport
- module Testing
- module Default #:nodoc:
- # Placeholder so test/unit ignores test cases without any tests.
- def default_test
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/xml_mini.rb b/activesupport/lib/active_support/xml_mini.rb
index 352172027b..b6a8cf3caf 100644
--- a/activesupport/lib/active_support/xml_mini.rb
+++ b/activesupport/lib/active_support/xml_mini.rb
@@ -25,7 +25,7 @@ module ActiveSupport
DEFAULT_ENCODINGS = {
"binary" => "base64"
- } unless defined?(TYPE_NAMES)
+ } unless defined?(DEFAULT_ENCODINGS)
TYPE_NAMES = {
"Symbol" => "symbol",
diff --git a/activesupport/test/test_case_test.rb b/activesupport/test/test_case_test.rb
new file mode 100644
index 0000000000..7e65c63062
--- /dev/null
+++ b/activesupport/test/test_case_test.rb
@@ -0,0 +1,57 @@
+require 'abstract_unit'
+
+module ActiveSupport
+ class TestCaseTest < ActiveSupport::TestCase
+ class FakeRunner
+ attr_reader :puked
+
+ def initialize
+ @puked = []
+ end
+
+ def puke(klass, name, e)
+ @puked << [klass, name, e]
+ end
+ end
+
+ if defined?(MiniTest::Assertions) && TestCase < MiniTest::Assertions
+ def test_callback_with_exception
+ tc = Class.new(TestCase) do
+ setup :bad_callback
+ def bad_callback; raise 'oh noes' end
+ def test_true; assert true end
+ end
+
+ test_name = 'test_true'
+ fr = FakeRunner.new
+
+ test = tc.new test_name
+ test.run fr
+ klass, name, exception = *fr.puked.first
+
+ assert_equal tc, klass
+ assert_equal test_name, name
+ assert_equal 'oh noes', exception.message
+ end
+
+ def test_teardown_callback_with_exception
+ tc = Class.new(TestCase) do
+ teardown :bad_callback
+ def bad_callback; raise 'oh noes' end
+ def test_true; assert true end
+ end
+
+ test_name = 'test_true'
+ fr = FakeRunner.new
+
+ test = tc.new test_name
+ test.run fr
+ klass, name, exception = *fr.puked.first
+
+ assert_equal tc, klass
+ assert_equal test_name, name
+ assert_equal 'oh noes', exception.message
+ end
+ end
+ end
+end
diff --git a/activesupport/test/test_test.rb b/activesupport/test/test_test.rb
index cdaf63961a..ee5a20c789 100644
--- a/activesupport/test/test_test.rb
+++ b/activesupport/test/test_test.rb
@@ -126,13 +126,6 @@ class AssertPresentTest < ActiveSupport::TestCase
end
end
-# These should always pass
-if ActiveSupport::Testing.const_defined?(:Default)
- class NotTestingThingsTest < Test::Unit::TestCase
- include ActiveSupport::Testing::Default
- end
-end
-
class AlsoDoingNothingTest < ActiveSupport::TestCase
end
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index 92b9131b59..9eae712a93 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -559,7 +559,7 @@ The view is only part of the story of how HTML is displayed in your web browser.
<title>Blog</title>
<%= stylesheet_link_tag :all %>
<%= javascript_include_tag :defaults %>
- <%= csrf_meta_tag %>
+ <%= csrf_meta_tags %>
</head>
<body style="background: #EEEEEE;">
diff --git a/railties/guides/source/index.html.erb b/railties/guides/source/index.html.erb
index e6d327168a..6b897e3a6a 100644
--- a/railties/guides/source/index.html.erb
+++ b/railties/guides/source/index.html.erb
@@ -161,6 +161,10 @@ Ruby on Rails Guides
<%= guide('API Documentation Guidelines', 'api_documentation_guidelines.html') do %>
<p>This guide documents the Ruby on Rails API documentation guidelines.</p>
<% end %>
+
+ <%= guide('Ruby on Rails Guides Guidelines', 'ruby_on_rails_guides_guidelines.html') do %>
+ <p>This guide documents the Ruby on Rails guides guidelines.</p>
+ <% end %>
</dl>
<h3>Release Notes</h3>
diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb
index 2039c76213..f0aa227c7e 100644
--- a/railties/guides/source/layout.html.erb
+++ b/railties/guides/source/layout.html.erb
@@ -81,6 +81,7 @@
<dt>Contributing to Rails</dt>
<dd><a href="contributing_to_rails.html">Contributing to Rails</a></dd>
<dd><a href="api_documentation_guidelines.html">API Documentation Guidelines</a></dd>
+ <dd><a href="ruby_on_rails_guides_guidelines.html">Ruby on Rails Guides Guidelines</a></dd>
<dt>Release Notes</dt>
<dd><a href="3_0_release_notes.html">Ruby on Rails 3.0 Release Notes</a></dd>
diff --git a/railties/guides/source/ruby_on_rails_guides_guidelines.textile b/railties/guides/source/ruby_on_rails_guides_guidelines.textile
new file mode 100644
index 0000000000..0bc409cbda
--- /dev/null
+++ b/railties/guides/source/ruby_on_rails_guides_guidelines.textile
@@ -0,0 +1,77 @@
+h2. Ruby on Rails Guides Guidelines
+
+This guide documents guidelines for writing guides. This guide follows itself in a gracile loop.
+
+endprologue.
+
+h3. Textile
+
+Guides are written in "Textile":http://www.textism.com/tools/textile/. There's comprehensive documentation "here":http://redcloth.org/hobix.com/textile/ and a cheatsheet for markup "here":http://redcloth.org/hobix.com/textile/quick.html.
+
+h3. Prologue
+
+Each guide should start with motivational text at the top. That's the little introduction in the blue area. The prologue should tell the readers what's the guide about, and what will they learn. See for example the "Routing Guide":routing.html.
+
+h3. Titles
+
+The title of every guide uses +h2+, guide sections use +h3+, subsections +h4+, etc.
+
+Capitalize all words except for internal articles, prepositions, conjuctions, and forms of the verb to be:
+
+<plain>
+h5. Middleware Stack is an Array
+h5. When are Objects Saved?
+</plain>
+
+Use same typography as in regular text:
+
+<plain>
+h6. The +:content_type+ Option
+</plain>
+
+h3. API Documentation Guidelines
+
+The guides and the API should be coherent where appropriate. Please have a look at these particular sections of the "API Documentation Guidelines":api_documentation_guidelines.html:
+
+* "Wording":api_documentation_guidelines.html#wording
+* "Example Code":api_documentation_guidelines.html#example-code
+* "Filenames":api_documentation_guidelines.html#filenames
+* "Fonts":api_documentation_guidelines.html#fonts
+
+Those guidelines apply also to guides.
+
+h3. HTML Generation
+
+To generate all the guides just cd into the +railties+ directory and execute
+
+<plain>
+rake generate_guides
+</plain>
+
+You'll need the gems erubis, i18n, and RedCloth.
+
+To process +my_guide.textile+ and nothing else use the +ONLY+ environment variable:
+
+<plain>
+rake generate_guides ONLY=my_guide
+</plain>
+
+Although by default guides that have not been modified are not processed, so +ONLY+ is rarely needed in practice.
+
+To force process of al the guides pass +ALL=1+.
+
+It is also recommended that you work with +WARNINGS=1+, this detects duplicate IDs and warns about broken internal links.
+
+h3. HTML validation
+
+Please do validate the generated HTML with
+
+<plain>
+rake validate_guides
+</plain>
+
+Particularly, titles get an ID generated from their content and this often leads to duplicates. Please set +WARNINGS=1+ when generating guides to detect them. The warning messages suggest a way to fix them.
+
+h3. Changelog
+
+* October 5, 2010: ported from the docrails wiki and revised by "Xavier Noria":credits.html#fxn
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index aafbbc29ee..075e3c5692 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -81,6 +81,10 @@ module Rails
super
end
+ def reload_routes!
+ routes_reloader.reload!
+ end
+
def routes_reloader
@routes_reloader ||= RoutesReloader.new
end
@@ -141,8 +145,8 @@ module Rails
rack_cache = config.action_controller.perform_caching && config.action_dispatch.rack_cache
require "action_dispatch/http/rack_cache" if rack_cache
+ middleware.use ::Rack::Cache, rack_cache if rack_cache
- middleware.use ::Rack::Cache, rack_cache if rack_cache
middleware.use ::ActionDispatch::Static, config.static_asset_paths if config.serve_static_assets
middleware.use ::Rack::Lock if !config.allow_concurrency
middleware.use ::Rack::Runtime
@@ -161,6 +165,8 @@ module Rails
middleware.use ::ActionDispatch::ParamsParser
middleware.use ::Rack::MethodOverride
middleware.use ::ActionDispatch::Head
+ middleware.use ::Rack::ConditionalGet
+ middleware.use ::Rack::ETag, "no-cache"
middleware.use ::ActionDispatch::BestStandardsSupport, config.action_dispatch.best_standards_support if config.action_dispatch.best_standards_support
end
end
diff --git a/railties/lib/rails/application/routes_reloader.rb b/railties/lib/rails/application/routes_reloader.rb
index 23b72a0ec6..6da903c1ac 100644
--- a/railties/lib/rails/application/routes_reloader.rb
+++ b/railties/lib/rails/application/routes_reloader.rb
@@ -8,7 +8,7 @@ module Rails
def blocks
@blocks ||= {}
end
- private
+
def reload!
clear!
load_blocks
@@ -18,6 +18,8 @@ module Rails
revert
end
+ protected
+
def clear!
routers.each do |routes|
routes.disable_clear_and_finalize = true
@@ -32,7 +34,7 @@ module Rails
end
def load_paths
- paths.each { |path| load(path) }
+ paths.each { |path| load(path) }
end
def finalize!
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 9ae235b818..3981e8dfd5 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -47,6 +47,26 @@ module Rails
# end
# end
#
+ # == Generators
+ #
+ # You can set up generators for engine with config.generators method:
+ #
+ # class MyEngine < Rails::Engine
+ # config.generators do |g|
+ # g.orm :active_record
+ # g.template_engine :erb
+ # g.test_framework :test_unit
+ # end
+ # end
+ #
+ # You can also set generators for application by using config.app_generators:
+ #
+ # class MyEngine < Rails::Engine
+ # # note that you can also pass block to app_generators in the same way you
+ # # can pass it to generators method
+ # config.app_generators.orm :datamapper
+ # end
+ #
# == Paths
#
# Since Rails 3.0, both your Application and Engines do not have hardcoded paths.
@@ -515,7 +535,7 @@ module Rails
root = File.exist?("#{root_path}/#{flag}") ? root_path : default
raise "Could not find root path for #{self}" unless root
- Config::CONFIG['host_os'] =~ /mswin|mingw/ ?
+ RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ ?
Pathname.new(root).expand_path : Pathname.new(root).realpath
end
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index d4d87be527..b69c0e1c53 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -4,16 +4,38 @@ module Rails
class Engine
class Configuration < ::Rails::Railtie::Configuration
attr_reader :root
- attr_writer :eager_load_paths, :autoload_once_paths, :autoload_paths
- attr_accessor :middleware, :plugins, :asset_path
+ attr_writer :middleware, :eager_load_paths, :autoload_once_paths, :autoload_paths
+ attr_accessor :plugins, :asset_path
def initialize(root=nil)
super()
@root = root
- @middleware = Rails::Configuration::MiddlewareStackProxy.new
@helpers_paths = []
end
+ # Returns the middleware stack for the engine.
+ def middleware
+ @middleware ||= Rails::Configuration::MiddlewareStackProxy.new
+ end
+
+ # Holds generators configuration:
+ #
+ # config.generators do |g|
+ # g.orm :datamapper, :migration => true
+ # g.template_engine :haml
+ # g.test_framework :rspec
+ # end
+ #
+ # If you want to disable color in console, do:
+ #
+ # config.generators.colorize_logging = false
+ #
+ def generators #:nodoc
+ @generators ||= Rails::Configuration::Generators.new
+ yield(@generators) if block_given?
+ @generators
+ end
+
def paths
@paths ||= begin
paths = Rails::Paths::Root.new(@root)
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 2715483914..7907191c74 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -14,23 +14,20 @@ module Rails
@options = generator.options
end
- private
- %w(template copy_file directory empty_directory inside
- empty_directory_with_gitkeep create_file chmod shebang).each do |method|
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def #{method}(*args, &block)
- @generator.send(:#{method}, *args, &block)
- end
- RUBY
- end
+ private
- # TODO: Remove once this is fully in place
- def method_missing(meth, *args, &block)
- STDERR.puts "Calling #{meth} with #{args.inspect} with #{block}"
- @generator.send(meth, *args, &block)
- end
+ def method_missing(meth, *args, &block)
+ @generator.send(meth, *args, &block)
+ end
end
+ # The application builder allows you to override elements of the application
+ # generator without being forced to reverse the operations of the default
+ # generator.
+ #
+ # This allows you to override entire operations, like the creation of the
+ # Gemfile, README, or javascript files, without needing to know exactly
+ # what those operations do so you can create another template action.
class AppBuilder
def rakefile
template "Rakefile"
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index 1dbf27d978..40213b1261 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -3,15 +3,18 @@ source 'http://rubygems.org'
<%- if options.dev? -%>
gem 'rails', :path => '<%= Rails::Generators::RAILS_DEV_PATH %>'
gem 'arel', :git => 'git://github.com/rails/arel.git'
+gem "rack", :git => "git://github.com/rack/rack.git"
<%- elsif options.edge? -%>
gem 'rails', :git => 'git://github.com/rails/rails.git'
gem 'arel', :git => 'git://github.com/rails/arel.git'
+gem "rack", :git => "git://github.com/rack/rack.git"
<%- else -%>
gem 'rails', '<%= Rails::VERSION::STRING %>'
# Bundle edge Rails instead:
# gem 'rails', :git => 'git://github.com/rails/rails.git'
# gem 'arel', :git => 'git://github.com/rails/arel.git'
+# gem "rack", :git => "git://github.com/rack/rack.git"
<%- end -%>
<% unless options[:skip_active_record] -%>
diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
index 1dd112b4a6..1de78eecae 100644
--- a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt
@@ -4,7 +4,7 @@
<title><%= app_const_base %></title>
<%%= stylesheet_link_tag :all %>
<%%= javascript_include_tag :defaults %>
- <%%= csrf_meta_tag %>
+ <%%= csrf_meta_tags %>
</head>
<body>
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index 09650789ac..2b68a3c453 100644
--- a/railties/lib/rails/railtie.rb
+++ b/railties/lib/rails/railtie.rb
@@ -83,7 +83,7 @@ module Rails
#
# class MyRailtie < Rails::Railtie
# # Customize the ORM
- # config.generators.orm :my_railtie_orm
+ # config.app_generators.orm :my_railtie_orm
#
# # Add a to_prepare block which is executed once in production
# # and before each request in development
diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb
index e0e4324a4a..afeceafb67 100644
--- a/railties/lib/rails/railtie/configuration.rb
+++ b/railties/lib/rails/railtie/configuration.rb
@@ -5,7 +5,6 @@ module Rails
class Configuration
def initialize
@@options ||= {}
- @@static_asset_paths = ActiveSupport::OrderedHash.new
end
# This allows you to modify the application's middlewares from Engines.
@@ -23,32 +22,13 @@ module Rails
# application overwrites them.
def app_generators
@@app_generators ||= Rails::Configuration::Generators.new
- if block_given?
- yield @@app_generators
- else
- @@app_generators
- end
+ yield(@@app_generators) if block_given?
+ @@app_generators
end
- # Holds generators configuration:
- #
- # config.generators do |g|
- # g.orm :datamapper, :migration => true
- # g.template_engine :haml
- # g.test_framework :rspec
- # end
- #
- # If you want to disable color in console, do:
- #
- # config.generators.colorize_logging = false
- #
- def generators
- @generators ||= Rails::Configuration::Generators.new
- if block_given?
- yield @generators
- else
- @generators
- end
+ def generators(&block) #:nodoc
+ ActiveSupport::Deprecation.warn "config.generators in Rails::Railtie is deprecated. Please use config.app_generators instead."
+ app_generators(&block)
end
def before_configuration(&block)
@@ -83,7 +63,7 @@ module Rails
# with associated public folders, like:
# { "/" => "/app/public", "/my_engine" => "app/engines/my_engine/public" }
def static_asset_paths
- @@static_asset_paths
+ @@static_asset_paths ||= ActiveSupport::OrderedHash.new
end
private
diff --git a/railties/railties.gemspec b/railties/railties.gemspec
index 73acb73dec..d26c1bcdbc 100644
--- a/railties/railties.gemspec
+++ b/railties/railties.gemspec
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
s.has_rdoc = false
s.add_dependency('rake', '>= 0.8.7')
- s.add_dependency('thor', '~> 0.14.2')
+ s.add_dependency('thor', '~> 0.14.3')
s.add_dependency('activesupport', version)
s.add_dependency('actionpack', version)
end
diff --git a/railties/test/application/middleware/best_practices_test.rb b/railties/test/application/middleware/best_practices_test.rb
new file mode 100644
index 0000000000..5b722e7510
--- /dev/null
+++ b/railties/test/application/middleware/best_practices_test.rb
@@ -0,0 +1,26 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class BestPracticesTest < Test::Unit::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ require 'rack/test'
+ extend Rack::Test::Methods
+ simple_controller
+ end
+
+ test "simple controller in production mode returns best standards" do
+ get '/foo'
+ assert_equal "IE=Edge,chrome=1", last_response.headers["X-UA-Compatible"]
+ end
+
+ test "simple controller in development mode leaves out Chrome" do
+ app("development")
+ get "/foo"
+ assert_equal "IE=Edge", last_response.headers["X-UA-Compatible"]
+ end
+ end
+end
diff --git a/railties/test/application/middleware/cache_test.rb b/railties/test/application/middleware/cache_test.rb
index 5675cebfd9..f582ed0e42 100644
--- a/railties/test/application/middleware/cache_test.rb
+++ b/railties/test/application/middleware/cache_test.rb
@@ -11,18 +11,6 @@ module ApplicationTests
extend Rack::Test::Methods
end
- def app(env = "production")
- old_env = ENV["RAILS_ENV"]
-
- @app ||= begin
- ENV["RAILS_ENV"] = env
- require "#{app_path}/config/environment"
- Rails.application
- end
- ensure
- ENV["RAILS_ENV"] = old_env
- end
-
def simple_controller
controller :expires, <<-RUBY
class ExpiresController < ApplicationController
diff --git a/railties/test/application/middleware/remote_ip_test.rb b/railties/test/application/middleware/remote_ip_test.rb
new file mode 100644
index 0000000000..f28302d70a
--- /dev/null
+++ b/railties/test/application/middleware/remote_ip_test.rb
@@ -0,0 +1,63 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class RemoteIpTest < Test::Unit::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ def remote_ip(env = {})
+ remote_ip = nil
+ env = Rack::MockRequest.env_for("/").merge(env).merge!(
+ 'action_dispatch.show_exceptions' => false,
+ 'action_dispatch.secret_token' => 'b3c631c314c0bbca50c1b2843150fe33'
+ )
+
+ endpoint = Proc.new do |e|
+ remote_ip = ActionDispatch::Request.new(e).remote_ip
+ [200, {}, ["Hello"]]
+ end
+
+ Rails.application.middleware.build(endpoint).call(env)
+ remote_ip
+ end
+
+ test "remote_ip works" do
+ make_basic_app
+ assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "1.1.1.1")
+ end
+
+ test "checks IP spoofing by default" do
+ make_basic_app
+ assert_raises(ActionDispatch::RemoteIp::IpSpoofAttackError) do
+ remote_ip("HTTP_X_FORWARDED_FOR" => "1.1.1.1", "HTTP_CLIENT_IP" => "1.1.1.2")
+ end
+ end
+
+ test "can disable IP spoofing check" do
+ make_basic_app do |app|
+ app.config.action_dispatch.ip_spoofing_check = false
+ end
+
+ assert_nothing_raised(ActionDispatch::RemoteIp::IpSpoofAttackError) do
+ assert_equal "1.1.1.2", remote_ip("HTTP_X_FORWARDED_FOR" => "1.1.1.1", "HTTP_CLIENT_IP" => "1.1.1.2")
+ end
+ end
+
+ test "the user can set trusted proxies" do
+ make_basic_app do |app|
+ app.config.action_dispatch.trusted_proxies = /^4\.2\.42\.42$/
+ end
+
+ assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "4.2.42.42,1.1.1.1")
+ end
+ end
+end
diff --git a/railties/test/application/middleware/sendfile_test.rb b/railties/test/application/middleware/sendfile_test.rb
new file mode 100644
index 0000000000..0128261cd4
--- /dev/null
+++ b/railties/test/application/middleware/sendfile_test.rb
@@ -0,0 +1,56 @@
+require 'isolation/abstract_unit'
+
+module ApplicationTests
+ class SendfileTest < Test::Unit::TestCase
+ include ActiveSupport::Testing::Isolation
+
+ def setup
+ build_app
+ boot_rails
+ FileUtils.rm_rf "#{app_path}/config/environments"
+ end
+
+ def app
+ @app ||= Rails.application
+ end
+
+ define_method :simple_controller do
+ class ::OmgController < ActionController::Base
+ def index
+ send_file __FILE__
+ end
+ end
+ end
+
+ # x_sendfile_header middleware
+ test "config.action_dispatch.x_sendfile_header defaults to ''" do
+ make_basic_app
+ simple_controller
+
+ get "/"
+ assert_equal File.read(__FILE__), last_response.body
+ end
+
+ test "config.action_dispatch.x_sendfile_header can be set" do
+ make_basic_app do |app|
+ app.config.action_dispatch.x_sendfile_header = "X-Sendfile"
+ end
+
+ simple_controller
+
+ get "/"
+ assert_equal File.expand_path(__FILE__), last_response.headers["X-Sendfile"]
+ end
+
+ test "config.action_dispatch.x_sendfile_header is sent to Rack::Sendfile" do
+ make_basic_app do |app|
+ app.config.action_dispatch.x_sendfile_header = 'X-Lighttpd-Send-File'
+ end
+
+ simple_controller
+
+ get "/"
+ assert_equal File.expand_path(__FILE__), last_response.headers["X-Lighttpd-Send-File"]
+ end
+ end
+end
diff --git a/railties/test/application/middleware_test.rb b/railties/test/application/middleware_test.rb
index f9b594eb33..173ac40b12 100644
--- a/railties/test/application/middleware_test.rb
+++ b/railties/test/application/middleware_test.rb
@@ -36,6 +36,8 @@ module ApplicationTests
"ActionDispatch::ParamsParser",
"Rack::MethodOverride",
"ActionDispatch::Head",
+ "Rack::ConditionalGet",
+ "Rack::ETag",
"ActionDispatch::BestStandardsSupport"
], middleware
end
@@ -45,27 +47,7 @@ module ApplicationTests
boot!
- assert_equal [
- "Rack::Cache",
- "ActionDispatch::Static",
- "Rack::Lock",
- "ActiveSupport::Cache::Strategy::LocalCache",
- "Rack::Runtime",
- "Rails::Rack::Logger",
- "ActionDispatch::ShowExceptions",
- "ActionDispatch::RemoteIp",
- "Rack::Sendfile",
- "ActionDispatch::Callbacks",
- "ActiveRecord::ConnectionAdapters::ConnectionManagement",
- "ActiveRecord::QueryCache",
- "ActionDispatch::Cookies",
- "ActionDispatch::Session::CookieStore",
- "ActionDispatch::Flash",
- "ActionDispatch::ParamsParser",
- "Rack::MethodOverride",
- "ActionDispatch::Head",
- "ActionDispatch::BestStandardsSupport"
- ], middleware
+ assert_equal "Rack::Cache", middleware.first
end
test "removing Active Record omits its middleware" do
@@ -129,81 +111,46 @@ module ApplicationTests
assert_equal "Rack::Config", middleware.first
end
- # x_sendfile_header middleware
- test "config.action_dispatch.x_sendfile_header defaults to ''" do
+ # ConditionalGet + Etag
+ test "conditional get + etag middlewares handle http caching based on body" do
make_basic_app
class ::OmgController < ActionController::Base
def index
- send_file __FILE__
- end
- end
-
- get "/"
- assert_equal File.read(__FILE__), last_response.body
- end
-
- test "config.action_dispatch.x_sendfile_header can be set" do
- make_basic_app do |app|
- app.config.action_dispatch.x_sendfile_header = "X-Sendfile"
- end
-
- class ::OmgController < ActionController::Base
- def index
- send_file __FILE__
+ if params[:nothing]
+ render :text => ""
+ else
+ render :text => "OMG"
+ end
end
end
- get "/"
- assert_equal File.expand_path(__FILE__), last_response.headers["X-Sendfile"]
- end
-
- test "config.action_dispatch.x_sendfile_header is sent to Rack::Sendfile" do
- make_basic_app do |app|
- app.config.action_dispatch.x_sendfile_header = 'X-Lighttpd-Send-File'
- end
-
- class ::OmgController < ActionController::Base
- def index
- send_file __FILE__
- end
- end
+ etag = "5af83e3196bf99f440f31f2e1a6c9afe".inspect
get "/"
- assert_equal File.expand_path(__FILE__), last_response.headers["X-Lighttpd-Send-File"]
- end
-
- # remote_ip tests
- test "remote_ip works" do
- make_basic_app
- assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "1.1.1.1")
- end
-
- test "checks IP spoofing by default" do
- make_basic_app
- assert_raises(ActionDispatch::RemoteIp::IpSpoofAttackError) do
- remote_ip("HTTP_X_FORWARDED_FOR" => "1.1.1.1", "HTTP_CLIENT_IP" => "1.1.1.2")
- end
- end
-
- test "can disable IP spoofing check" do
- make_basic_app do |app|
- app.config.action_dispatch.ip_spoofing_check = false
- end
-
- assert_nothing_raised(ActionDispatch::RemoteIp::IpSpoofAttackError) do
- assert_equal "1.1.1.2", remote_ip("HTTP_X_FORWARDED_FOR" => "1.1.1.1", "HTTP_CLIENT_IP" => "1.1.1.2")
- end
- end
-
- test "the user can set trusted proxies" do
- make_basic_app do |app|
- app.config.action_dispatch.trusted_proxies = /^4\.2\.42\.42$/
- end
-
- assert_equal "1.1.1.1", remote_ip("REMOTE_ADDR" => "4.2.42.42,1.1.1.1")
- end
-
+ assert_equal 200, last_response.status
+ assert_equal "OMG", last_response.body
+ assert_equal "text/html; charset=utf-8", last_response.headers["Content-Type"]
+ assert_equal "max-age=0, private, must-revalidate", last_response.headers["Cache-Control"]
+ assert_equal etag, last_response.headers["Etag"]
+
+ get "/", {}, "HTTP_IF_NONE_MATCH" => etag
+ assert_equal 304, last_response.status
+ assert_equal "", last_response.body
+ assert_equal nil, last_response.headers["Content-Type"]
+ assert_equal "max-age=0, private, must-revalidate", last_response.headers["Cache-Control"]
+ assert_equal etag, last_response.headers["Etag"]
+
+ get "/?nothing=true"
+ puts last_response.body
+ assert_equal 200, last_response.status
+ assert_equal "", last_response.body
+ assert_equal "text/html; charset=utf-8", last_response.headers["Content-Type"]
+ assert_equal "no-cache", last_response.headers["Cache-Control"]
+ assert_equal nil, last_response.headers["Etag"]
+ end
+
+ # Show exceptions middleware
test "show exceptions middleware filter backtrace before logging" do
my_middleware = Struct.new(:app) do
def call(env)
@@ -232,21 +179,5 @@ module ApplicationTests
def middleware
AppTemplate::Application.middleware.map(&:klass).map(&:name)
end
-
- def remote_ip(env = {})
- remote_ip = nil
- env = Rack::MockRequest.env_for("/").merge(env).merge!(
- 'action_dispatch.show_exceptions' => false,
- 'action_dispatch.secret_token' => 'b3c631c314c0bbca50c1b2843150fe33'
- )
-
- endpoint = Proc.new do |e|
- remote_ip = ActionDispatch::Request.new(e).remote_ip
- [200, {}, ["Hello"]]
- end
-
- Rails.application.middleware.build(endpoint).call(env)
- remote_ip
- end
end
end
diff --git a/railties/test/application/routing_test.rb b/railties/test/application/routing_test.rb
index 416a5de5b0..62589c998d 100644
--- a/railties/test/application/routing_test.rb
+++ b/railties/test/application/routing_test.rb
@@ -11,34 +11,6 @@ module ApplicationTests
extend Rack::Test::Methods
end
- def app(env = "production")
- old_env = ENV["RAILS_ENV"]
-
- @app ||= begin
- ENV["RAILS_ENV"] = env
- require "#{app_path}/config/environment"
- Rails.application
- end
- ensure
- ENV["RAILS_ENV"] = old_env
- end
-
- def simple_controller
- controller :foo, <<-RUBY
- class FooController < ApplicationController
- def index
- render :text => "foo"
- end
- end
- RUBY
-
- app_file 'config/routes.rb', <<-RUBY
- AppTemplate::Application.routes.draw do
- match ':controller(/:action)'
- end
- RUBY
- end
-
test "rails/info/properties in development" do
app("development")
get "/rails/info/properties"
@@ -58,21 +30,6 @@ module ApplicationTests
assert_equal 'foo', last_response.body
end
- test "simple controller in production mode returns best standards" do
- simple_controller
-
- get '/foo'
- assert_equal "IE=Edge,chrome=1", last_response.headers["X-UA-Compatible"]
- end
-
- test "simple controller in development mode leaves out Chrome" do
- simple_controller
- app("development")
-
- get "/foo"
- assert_equal "IE=Edge", last_response.headers["X-UA-Compatible"]
- end
-
test "simple controller with helper" do
controller :foo, <<-RUBY
class FooController < ApplicationController
@@ -177,7 +134,7 @@ module ApplicationTests
assert_equal 'admin::foo', last_response.body
end
- def test_reloads_appended_route_blocks
+ test "routes appending blocks" do
app_file 'config/routes.rb', <<-RUBY
AppTemplate::Application.routes.draw do
match ':controller#:action'
@@ -246,9 +203,12 @@ module ApplicationTests
test 'routes are loaded just after initialization' do
require "#{app_path}/config/application"
- app_file 'config/routes.rb', <<-RUBY
- InitializeRackApp = lambda { |env| [200, {}, ["InitializeRackApp"]] }
+ # Create the rack app just inside after initialize callback
+ ActiveSupport.on_load(:after_initialize) do
+ ::InitializeRackApp = lambda { |env| [200, {}, ["InitializeRackApp"]] }
+ end
+ app_file 'config/routes.rb', <<-RUBY
AppTemplate::Application.routes.draw do
match 'foo', :to => ::InitializeRackApp
end
@@ -258,7 +218,14 @@ module ApplicationTests
assert_equal "InitializeRackApp", last_response.body
end
- test 'resource routing with irrigular inflection' do
+ test 'reload_routes! is part of Rails.application API' do
+ app("development")
+ assert_nothing_raised do
+ Rails.application.reload_routes!
+ end
+ end
+
+ test 'resource routing with irregular inflection' do
app_file 'config/initializers/inflection.rb', <<-RUBY
ActiveSupport::Inflector.inflections do |inflect|
inflect.irregular 'yazi', 'yazilar'
diff --git a/railties/test/generators/generated_attribute_test.rb b/railties/test/generators/generated_attribute_test.rb
index 272e179fe3..064546a3f3 100644
--- a/railties/test/generators/generated_attribute_test.rb
+++ b/railties/test/generators/generated_attribute_test.rb
@@ -117,7 +117,7 @@ class GeneratedAttributeTest < Rails::Generators::TestCase
def test_missing_type_raises_exception
assert_raise Thor::Error do
- create_generated_attribute(:'', 'title')
+ create_generated_attribute('', 'title')
end
end
end
diff --git a/railties/test/generators/mailer_generator_test.rb b/railties/test/generators/mailer_generator_test.rb
index 450dec7716..f4fdc46328 100644
--- a/railties/test/generators/mailer_generator_test.rb
+++ b/railties/test/generators/mailer_generator_test.rb
@@ -59,6 +59,15 @@ class MailerGeneratorTest < Rails::Generators::TestCase
assert_match /haml \[not found\]/, content
end
+ def test_mailer_with_namedspaced_mailer
+ run_generator ["Farm::Animal", "moos"]
+ assert_file "app/mailers/farm/animal.rb" do |mailer|
+ assert_match /class Farm::Animal < ActionMailer::Base/, mailer
+ assert_match /en\.farm\.animal\.moos\.subject/, mailer
+ end
+ assert_file "app/views/farm/animal/moos.text.erb"
+ end
+
def test_actions_are_turned_into_methods
run_generator
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index 79c7735019..3b03e4eb3d 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -45,6 +45,17 @@ module TestHelpers
end
module Rack
+ def app(env = "production")
+ old_env = ENV["RAILS_ENV"]
+ @app ||= begin
+ ENV["RAILS_ENV"] = env
+ require "#{app_path}/config/environment"
+ Rails.application
+ end
+ ensure
+ ENV["RAILS_ENV"] = old_env
+ end
+
def extract_body(response)
"".tap do |body|
response[2].each {|chunk| body << chunk }
@@ -124,6 +135,22 @@ module TestHelpers
extend ::Rack::Test::Methods
end
+ def simple_controller
+ controller :foo, <<-RUBY
+ class FooController < ApplicationController
+ def index
+ render :text => "foo"
+ end
+ end
+ RUBY
+
+ app_file 'config/routes.rb', <<-RUBY
+ AppTemplate::Application.routes.draw do
+ match ':controller(/:action)'
+ end
+ RUBY
+ end
+
class Bukkit
attr_reader :path
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index c75639d740..89262aeaae 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -89,7 +89,7 @@ module RailtiesTest
env = Rack::MockRequest.env_for("/bukkits")
response = Rails.application.call(env)
- assert_equal "HELLO WORLD", response[2]
+ assert_equal ["HELLO WORLD"], response[2]
end
test "it provides routes as default endpoint" do
@@ -116,8 +116,7 @@ module RailtiesTest
env = Rack::MockRequest.env_for("/bukkits/foo")
response = Rails.application.call(env)
-
- assert_equal "foo", response[2]
+ assert_equal ["foo"], response[2]
end
test "engine can load its own plugins" do
@@ -379,15 +378,15 @@ module RailtiesTest
env = Rack::MockRequest.env_for("/foo")
response = Rails.application.call(env)
- assert_equal "Something... Something... Something...", response[2].body
+ assert_equal ["Something... Something... Something..."], response[2]
env = Rack::MockRequest.env_for("/foo/show")
response = Rails.application.call(env)
- assert_equal "/foo", response[2].body
+ assert_equal ["/foo"], response[2]
env = Rack::MockRequest.env_for("/foo/bar")
response = Rails.application.call(env)
- assert_equal "It's a bar.", response[2].body
+ assert_equal ["It's a bar."], response[2]
end
test "isolated engine should include only its own routes and helpers" do
@@ -488,23 +487,23 @@ module RailtiesTest
env = Rack::MockRequest.env_for("/bukkits/from_app")
response = AppTemplate::Application.call(env)
- assert_equal "false", response[2].body
+ assert_equal ["false"], response[2]
env = Rack::MockRequest.env_for("/bukkits/foo/show")
response = AppTemplate::Application.call(env)
- assert_equal "/bukkits/foo", response[2].body
+ assert_equal ["/bukkits/foo"], response[2]
env = Rack::MockRequest.env_for("/bukkits/foo")
response = AppTemplate::Application.call(env)
- assert_equal "Helped.", response[2].body
+ assert_equal ["Helped."], response[2]
env = Rack::MockRequest.env_for("/bukkits/routes_helpers_in_view")
response = AppTemplate::Application.call(env)
- assert_equal "/bukkits/foo, /bar", response[2].body
+ assert_equal ["/bukkits/foo, /bar"], response[2]
env = Rack::MockRequest.env_for("/bukkits/polymorphic_path_without_namespace")
response = AppTemplate::Application.call(env)
- assert_equal "/bukkits/posts/1", response[2].body
+ assert_equal ["/bukkits/posts/1"], response[2]
end
test "isolated engine should avoid namespace in names if that's possible" do