aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRajinder Yadav <devguy.ca@gmail.com>2010-10-15 22:19:39 -0400
committerRajinder Yadav <devguy.ca@gmail.com>2010-10-15 22:19:39 -0400
commit62eb00a62e3868c3500372def52968b919332b51 (patch)
tree8497f517402c19eae1fea22943565cbf11496ef7
parent018bf0f5756ef3887815ea46d6671dc1d3b526cf (diff)
parentcffae06a4f1b7aac86a7e99cfb244890f837f976 (diff)
downloadrails-62eb00a62e3868c3500372def52968b919332b51.tar.gz
rails-62eb00a62e3868c3500372def52968b919332b51.tar.bz2
rails-62eb00a62e3868c3500372def52968b919332b51.zip
Merge branch 'master' of git://github.com/lifo/docrails
-rw-r--r--Gemfile6
-rw-r--r--actionmailer/lib/action_mailer.rb1
-rw-r--r--actionmailer/lib/action_mailer/base.rb9
-rw-r--r--actionmailer/lib/action_mailer/old_api.rb4
-rw-r--r--actionmailer/lib/action_mailer/railtie.rb6
-rw-r--r--actionmailer/lib/rails/generators/mailer/templates/mailer.rb2
-rw-r--r--actionmailer/test/old_base/mail_service_test.rb5
-rw-r--r--actionpack/CHANGELOG14
-rw-r--r--actionpack/RUNNING_UNIT_TESTS13
-rw-r--r--actionpack/lib/abstract_controller/base.rb9
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb5
-rw-r--r--actionpack/lib/action_controller/metal.rb5
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb2
-rw-r--r--actionpack/lib/action_controller/metal/head.rb6
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb22
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb1
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb4
-rw-r--r--actionpack/lib/action_controller/railtie.rb8
-rw-r--r--actionpack/lib/action_controller/railties/paths.rb6
-rw-r--r--actionpack/lib/action_controller/test_case.rb11
-rw-r--r--actionpack/lib/action_dispatch/http/cache.rb22
-rw-r--r--actionpack/lib/action_dispatch/http/request.rb21
-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.rb74
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb3
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb8
-rw-r--r--actionpack/lib/action_dispatch/testing/performance_test.rb3
-rw-r--r--actionpack/lib/action_view.rb9
-rw-r--r--actionpack/lib/action_view/base.rb38
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb5
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb15
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb1
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/text_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb51
-rw-r--r--actionpack/lib/action_view/lookup_context.rb31
-rw-r--r--actionpack/lib/action_view/paths.rb4
-rw-r--r--actionpack/lib/action_view/render/layouts.rb83
-rw-r--r--actionpack/lib/action_view/render/partials.rb161
-rw-r--r--actionpack/lib/action_view/render/rendering.rb95
-rw-r--r--actionpack/lib/action_view/renderer/abstract_renderer.rb36
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb166
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb97
-rw-r--r--actionpack/lib/action_view/template.rb156
-rw-r--r--actionpack/lib/action_view/template/error.rb5
-rw-r--r--actionpack/lib/action_view/template/handler.rb10
-rw-r--r--actionpack/lib/action_view/template/handlers.rb10
-rw-r--r--actionpack/lib/action_view/template/handlers/builder.rb8
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb25
-rw-r--r--actionpack/lib/action_view/template/handlers/rjs.rb12
-rw-r--r--actionpack/lib/action_view/template/inline.rb20
-rw-r--r--actionpack/lib/action_view/template/resolver.rb115
-rw-r--r--actionpack/lib/action_view/template/text.rb4
-rw-r--r--actionpack/lib/action_view/test_case.rb26
-rw-r--r--actionpack/lib/action_view/testing/resolvers.rb13
-rw-r--r--actionpack/test/abstract_unit.rb35
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb7
-rw-r--r--actionpack/test/controller/capture_test.rb6
-rw-r--r--actionpack/test/controller/content_type_test.rb24
-rw-r--r--actionpack/test/controller/filters_test.rb5
-rw-r--r--actionpack/test/controller/integration_test.rb2
-rw-r--r--actionpack/test/controller/layout_test.rb18
-rw-r--r--actionpack/test/controller/log_subscriber_test.rb6
-rw-r--r--actionpack/test/controller/new_base/bare_metal_test.rb15
-rw-r--r--actionpack/test/controller/new_base/etag_test.rb46
-rw-r--r--actionpack/test/controller/new_base/render_once_test.rb72
-rw-r--r--actionpack/test/controller/new_base/render_partial_test.rb17
-rw-r--r--actionpack/test/controller/new_base/render_template_test.rb26
-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/prefix_generation_test.rb93
-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/fixtures/layout_tests/alt/hello.erb1
-rw-r--r--actionpack/test/fixtures/layout_tests/alt/hello.rhtml1
-rw-r--r--actionpack/test/fixtures/layout_tests/alt/layouts/alt.erb (renamed from actionpack/test/fixtures/layout_tests/alt/layouts/alt.rhtml)0
-rw-r--r--actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.erb1
-rw-r--r--actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml1
-rw-r--r--actionpack/test/fixtures/layout_tests/layouts/item.erb1
-rw-r--r--actionpack/test/fixtures/layout_tests/layouts/item.rhtml1
-rw-r--r--actionpack/test/fixtures/layout_tests/layouts/layout_test.erb1
-rw-r--r--actionpack/test/fixtures/layout_tests/layouts/layout_test.rhtml1
-rw-r--r--actionpack/test/fixtures/layout_tests/views/goodbye.erb1
-rw-r--r--actionpack/test/fixtures/layout_tests/views/goodbye.rhtml1
-rw-r--r--actionpack/test/fixtures/layout_tests/views/hello.erb1
-rw-r--r--actionpack/test/fixtures/layout_tests/views/hello.rhtml1
-rw-r--r--actionpack/test/fixtures/old_content_type/render_default_for_builder.builder (renamed from actionpack/test/fixtures/old_content_type/render_default_for_rxml.rxml)0
-rw-r--r--actionpack/test/fixtures/old_content_type/render_default_for_erb.erb (renamed from actionpack/test/fixtures/old_content_type/render_default_for_rhtml.rhtml)0
-rw-r--r--actionpack/test/fixtures/test/_object_inspector.erb1
-rw-r--r--actionpack/test/fixtures/test/proper_block_detection.erb2
-rw-r--r--actionpack/test/lib/controller/fake_models.rb19
-rw-r--r--actionpack/test/template/form_helper_test.rb45
-rw-r--r--actionpack/test/template/lookup_context_test.rb70
-rw-r--r--actionpack/test/template/render_test.rb17
-rw-r--r--actionpack/test/template/template_test.rb118
-rw-r--r--actionpack/test/template/test_case_test.rb31
-rw-r--r--actionpack/test/template/translation_helper_test.rb4
-rw-r--r--actionpack/test/template/url_helper_test.rb20
-rw-r--r--activemodel/lib/active_model.rb3
-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/RUNNING_UNIT_TESTS7
-rw-r--r--activerecord/examples/performance.rb2
-rw-r--r--activerecord/lib/active_record.rb6
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb12
-rw-r--r--activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb2
-rw-r--r--activerecord/lib/active_record/base.rb36
-rw-r--r--activerecord/lib/active_record/callbacks.rb10
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb16
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb8
-rw-r--r--activerecord/lib/active_record/migration.rb69
-rw-r--r--activerecord/lib/active_record/railtie.rb2
-rw-r--r--activerecord/lib/active_record/railties/databases.rake52
-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/serialization.rb2
-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/connection_pool_test.rb29
-rw-r--r--activerecord/test/cases/inheritance_test.rb14
-rw-r--r--activerecord/test/cases/migration_test.rb64
-rw-r--r--activerecord/test/migrations/to_copy2/1_create_articles.rb7
-rw-r--r--activerecord/test/migrations/to_copy2/2_create_comments.rb7
-rw-r--r--activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb7
-rw-r--r--activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb7
-rw-r--r--activeresource/lib/active_resource.rb1
-rw-r--r--activeresource/lib/active_resource/http_mock.rb56
-rw-r--r--activeresource/lib/active_resource/validations.rb2
-rw-r--r--activeresource/test/cases/format_test.rb2
-rw-r--r--activeresource/test/cases/http_mock_test.rb36
-rw-r--r--activesupport/lib/active_support.rb1
-rw-r--r--activesupport/lib/active_support/cache/mem_cache_store.rb4
-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/core_ext/logger.rb49
-rw-r--r--activesupport/lib/active_support/core_ext/time/conversions.rb4
-rw-r--r--activesupport/lib/active_support/hash_with_indifferent_access.rb2
-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/core_ext/hash_ext_test.rb10
-rw-r--r--activesupport/test/test_case_test.rb57
-rw-r--r--activesupport/test/test_test.rb7
-rw-r--r--railties/CHANGELOG4
-rw-r--r--railties/guides/source/action_view_overview.textile3
-rw-r--r--railties/guides/source/association_basics.textile2
-rw-r--r--railties/guides/source/command_line.textile2
-rw-r--r--railties/guides/source/debugging_rails_applications.textile4
-rw-r--r--railties/guides/source/generators.textile2
-rw-r--r--railties/guides/source/getting_started.textile4
-rw-r--r--railties/guides/source/index.html.erb4
-rw-r--r--railties/guides/source/initialization.textile2
-rw-r--r--railties/guides/source/layout.html.erb1
-rw-r--r--railties/guides/source/layouts_and_rendering.textile16
-rw-r--r--railties/guides/source/routing.textile2
-rw-r--r--railties/guides/source/ruby_on_rails_guides_guidelines.textile77
-rw-r--r--railties/lib/rails.rb2
-rw-r--r--railties/lib/rails/application.rb28
-rw-r--r--railties/lib/rails/application/bootstrap.rb2
-rw-r--r--railties/lib/rails/application/configuration.rb19
-rw-r--r--railties/lib/rails/application/finisher.rb2
-rw-r--r--railties/lib/rails/application/routes_reloader.rb45
-rw-r--r--railties/lib/rails/commands.rb19
-rw-r--r--railties/lib/rails/commands/plugin.rb2
-rw-r--r--railties/lib/rails/engine.rb217
-rw-r--r--railties/lib/rails/engine/configuration.rb71
-rw-r--r--railties/lib/rails/engine/railties.rb2
-rw-r--r--railties/lib/rails/generators/actions.rb4
-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/generators/rails/app/templates/config/databases/oracle.yml2
-rw-r--r--railties/lib/rails/paths.rb154
-rw-r--r--railties/lib/rails/plugin.rb6
-rw-r--r--railties/lib/rails/railtie.rb9
-rw-r--r--railties/lib/rails/railtie/configuration.rb33
-rw-r--r--railties/lib/rails/tasks/documentation.rake2
-rw-r--r--railties/lib/rails/tasks/railties.rake4
-rw-r--r--railties/lib/rails/test_unit/railtie.rb2
-rw-r--r--railties/lib/rails/test_unit/testing.rake7
-rw-r--r--railties/railties.gemspec2
-rw-r--r--railties/test/application/configuration_test.rb4
-rw-r--r--railties/test/application/console_test.rb5
-rw-r--r--railties/test/application/generators_test.rb2
-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/paths_test.rb28
-rw-r--r--railties/test/application/routing_test.rb61
-rw-r--r--railties/test/generators/app_generator_test.rb6
-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/generators/namespaced_generators_test.rb4
-rw-r--r--railties/test/isolation/abstract_unit.rb27
-rw-r--r--railties/test/paths_test.rb268
-rw-r--r--railties/test/railties/engine_test.rb147
-rw-r--r--railties/test/railties/mounted_engine_test.rb2
-rw-r--r--railties/test/railties/plugin_test.rb2
-rw-r--r--railties/test/railties/railtie_test.rb24
-rw-r--r--railties/test/railties/shared_tests.rb27
221 files changed, 3185 insertions, 2204 deletions
diff --git a/Gemfile b/Gemfile
index 9d29c42e1c..13bf277d7a 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"
@@ -54,6 +55,11 @@ platforms :jruby do
gem "activerecord-jdbcsqlite3-adapter"
+ # This is needed by now to let tests work on JRuby
+ # TODO: When the JRuby guys merge jruby-openssl in
+ # jruby this will be removed
+ gem "jruby-openssl"
+
group :db do
gem "activerecord-jdbcmysql-adapter"
gem "activerecord-jdbcpostgresql-adapter"
diff --git a/actionmailer/lib/action_mailer.rb b/actionmailer/lib/action_mailer.rb
index 05ba12197a..02a9916703 100644
--- a/actionmailer/lib/action_mailer.rb
+++ b/actionmailer/lib/action_mailer.rb
@@ -26,6 +26,7 @@ $:.unshift(actionpack_path) if File.directory?(actionpack_path) && !$:.include?(
require 'abstract_controller'
require 'action_view'
+require 'action_mailer/version'
# Common Active Support usage in Action Mailer
require 'active_support/core_ext/class'
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 6ae3940e6d..0bf60a2c24 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -409,7 +409,7 @@ module ActionMailer #:nodoc:
protected
def set_payload_for_mail(payload, mail) #:nodoc:
- payload[:mailer] = self.name
+ payload[:mailer] = name
payload[:message_id] = mail.message_id
payload[:subject] = mail.subject
payload[:to] = mail.to
@@ -421,11 +421,8 @@ module ActionMailer #:nodoc:
end
def method_missing(method, *args) #:nodoc:
- if action_methods.include?(method.to_s)
- new(method, *args).message
- else
- super
- end
+ return super unless respond_to?(method)
+ new(method, *args).message
end
end
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/action_mailer/railtie.rb b/actionmailer/lib/action_mailer/railtie.rb
index 86136bdafd..d67a5b6521 100644
--- a/actionmailer/lib/action_mailer/railtie.rb
+++ b/actionmailer/lib/action_mailer/railtie.rb
@@ -14,9 +14,9 @@ module ActionMailer
paths = app.config.paths
options = app.config.action_mailer
- options.assets_dir ||= paths.public.to_a.first
- options.javascripts_dir ||= paths.public.javascripts.to_a.first
- options.stylesheets_dir ||= paths.public.stylesheets.to_a.first
+ options.assets_dir ||= paths["public"].first
+ options.javascripts_dir ||= paths["public/javascripts"].first
+ options.stylesheets_dir ||= paths["public/stylesheets"].first
# make sure readers methods get compiled
options.asset_path ||= nil
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/actionmailer/test/old_base/mail_service_test.rb b/actionmailer/test/old_base/mail_service_test.rb
index 93921978b6..0b5b0b2da3 100644
--- a/actionmailer/test/old_base/mail_service_test.rb
+++ b/actionmailer/test/old_base/mail_service_test.rb
@@ -328,7 +328,6 @@ class ActionMailerTest < Test::Unit::TestCase
mail
end
- # Replacing logger work around for mocha bug. Should be fixed in mocha 0.3.3
def setup
set_delivery_method :test
ActionMailer::Base.perform_deliveries = true
@@ -336,14 +335,12 @@ class ActionMailerTest < Test::Unit::TestCase
ActionMailer::Base.deliveries.clear
ActiveSupport::Deprecation.silenced = true
- @original_logger = TestMailer.logger
@recipient = 'test@localhost'
TestMailer.delivery_method = :test
end
def teardown
- TestMailer.logger = @original_logger
ActiveSupport::Deprecation.silenced = false
restore_delivery_method
end
@@ -1097,4 +1094,4 @@ EOF
ensure
TestMailer.smtp_settings.merge!(:enable_starttls_auto => true)
end
-end \ No newline at end of file
+end
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 6f8314109b..3f8188b1f7 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,12 +1,20 @@
*Rails 3.1.0 (unreleased)*
+* Added render :once. You can pass either a string or an array of strings and Rails will ensure they each of them are rendered just once. [José Valim]
+
+* Deprecate old template handler API. The new API simply requires a template handler to respond to call. [José Valim]
+
+* :rhtml and :rxml were finally removed as template handlers. [José Valim]
+
+* Moved etag responsibility from ActionDispatch::Response to the middleware stack. [José Valim]
+
+* 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/RUNNING_UNIT_TESTS b/actionpack/RUNNING_UNIT_TESTS
index d6a1ccf871..1e3ba7abe7 100644
--- a/actionpack/RUNNING_UNIT_TESTS
+++ b/actionpack/RUNNING_UNIT_TESTS
@@ -8,15 +8,18 @@ Rake can be found at http://rake.rubyforge.org
== Running by hand
-If you only want to run a single test suite, or don't want to bother with Rake,
-you can do so with something like:
+To run a single test suite
- ruby -Itest test/controller/base_tests.rb
+ rake test TEST=path/to/test.rb
-== Dependency on ActiveRecord and database setup
+which can be further narrowed down to one test:
+
+ rake test TEST=path/to/test.rb TESTOPTS="--name=test_something"
+
+== Dependency on Active Record and database setup
Test cases in the test/controller/active_record/ directory depend on having
-activerecord and sqlite installed. If ActiveRecord is not in
+activerecord and sqlite installed. If Active Record is not in
actionpack/../activerecord directory, or the sqlite rubygem is not installed,
these tests are skipped.
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index 85270d84d8..f83eaded88 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -13,6 +13,7 @@ module AbstractController
class Base
attr_internal :response_body
attr_internal :action_name
+ attr_internal :formats
include ActiveSupport::Configurable
extend ActiveSupport::DescendantsTracker
@@ -61,13 +62,13 @@ module AbstractController
def action_methods
@action_methods ||= begin
# All public instance methods of this class, including ancestors
- methods = public_instance_methods(true).map { |m| m.to_s }.to_set -
+ methods = (public_instance_methods(true) -
# Except for public instance methods of Base and its ancestors
- internal_methods.map { |m| m.to_s } +
+ internal_methods +
# Be sure to include shadowed public instance methods of this class
- public_instance_methods(false).map { |m| m.to_s } -
+ public_instance_methods(false)).uniq.map { |x| x.to_s } -
# And always exclude explicitly hidden actions
- hidden_actions
+ hidden_actions.to_a
# Clear out AS callback method pollution
methods.reject { |method| method =~ /_one_time_conditions/ }
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 5d9b35d297..1c63fb2d14 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -12,7 +12,6 @@ module AbstractController
# This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
# it will trigger the lookup_context and consequently expire the cache.
- # TODO Add some deprecation warnings to remove I18n.locale from controllers
class I18nProxy < ::I18n::Config #:nodoc:
attr_reader :i18n_config, :lookup_context
@@ -50,7 +49,7 @@ module AbstractController
if controller.respond_to?(:_helpers)
include controller._helpers
- if controller.respond_to?(:_routes)
+ if controller.respond_to?(:_routes) && controller._routes
include controller._routes.url_helpers
include controller._routes.mounted_helpers
end
@@ -156,7 +155,7 @@ module AbstractController
options[:partial] = action_name
end
- if (options.keys & [:partial, :file, :template]).empty?
+ if (options.keys & [:partial, :file, :template, :once]).empty?
options[:prefix] ||= _prefix
end
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index ace1aabe03..329798e84f 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -119,6 +119,11 @@ module ActionController
headers["Location"] = url
end
+ # basic url_for that can be overridden for more robust functionality
+ def url_for(string)
+ string
+ end
+
def status
@_status
end
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index 8930c13a68..a5e37172c9 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -66,7 +66,7 @@ module ActionController
# Examples:
# expires_in 20.minutes
# expires_in 3.hours, :public => true
- # expires in 3.hours, 'max-stale' => 5.hours, :public => true
+ # expires_in 3.hours, 'max-stale' => 5.hours, :public => true
#
# This method will overwrite an existing Cache-Control header.
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb
index 2b4a3b9392..8abcad55a2 100644
--- a/actionpack/lib/action_controller/metal/head.rb
+++ b/actionpack/lib/action_controller/metal/head.rb
@@ -2,8 +2,6 @@ module ActionController
module Head
extend ActiveSupport::Concern
- include ActionController::UrlFor
-
# Return a response that has no content (merely headers). The options
# argument is interpreted to be a hash of header names and values.
# This allows you to easily return a response that consists only of
@@ -27,8 +25,8 @@ module ActionController
self.status = status
self.location = url_for(location) if location
- self.content_type = Mime[formats.first]
+ self.content_type = Mime[formats.first] if formats
self.response_body = " "
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 6a7e170306..547cec7081 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -214,7 +214,7 @@ module ActionController
def encode_credentials(http_method, credentials, password, password_is_ha1)
credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
- "Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ')
+ "Digest " + credentials.sort_by {|x| x[0].to_s }.map {|v| "#{v[0]}='#{v[1]}'" }.join(', ')
end
def decode_credentials_header(request)
@@ -423,14 +423,13 @@ module ActionController
# Returns nil if no token is found.
def token_and_options(request)
if header = request.authorization.to_s[/^Token (.*)/]
- values = $1.split(',').
- inject({}) do |memo, value|
- value.strip! # remove any spaces between commas and values
- key, value = value.split(/\=\"?/) # split key=value pairs
- value.chomp!('"') # chomp trailing " in value
- value.gsub!(/\\\"/, '"') # unescape remaining quotes
- memo.update(key => value)
- end
+ values = Hash[$1.split(',').map do |value|
+ value.strip! # remove any spaces between commas and values
+ key, value = value.split(/\=\"?/) # split key=value pairs
+ value.chomp!('"') # chomp trailing " in value
+ value.gsub!(/\\\"/, '"') # unescape remaining quotes
+ [key, value]
+ end]
[values.delete("token"), values.with_indifferent_access]
end
end
@@ -442,9 +441,8 @@ module ActionController
#
# Returns String.
def encode_credentials(token, options = {})
- values = ["token=#{token.to_s.inspect}"]
- options.each do |key, value|
- values << "#{key}=#{value.to_s.inspect}"
+ values = ["token=#{token.to_s.inspect}"] + options.map do |key, value|
+ "#{key}=#{value.to_s.inspect}"
end
"Token #{values * ", "}"
end
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 86bb810947..e524e546ad 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -2,7 +2,6 @@ module ActionController
module Rendering
extend ActiveSupport::Concern
- include ActionController::RackDelegation
include AbstractController::Rendering
# Before processing, set the request formats in current controller formats.
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 2013da3adb..312dc8eb3e 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -101,10 +101,6 @@ module ActionController #:nodoc:
# send_data image.data, :type => image.content_type, :disposition => 'inline'
#
# See +send_file+ for more information on HTTP Content-* headers and caching.
- #
- # <b>Tip:</b> if you want to stream large amounts of on-the-fly generated
- # data to the browser, then use <tt>render :text => proc { ... }</tt>
- # instead. See ActionController::Base#render for more information.
def send_data(data, options = {}) #:doc:
send_file_headers! options.dup
render options.slice(:status, :content_type).merge(:text => data)
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 0ade42ba2d..c5a661f2b0 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -21,10 +21,10 @@ module ActionController
paths = app.config.paths
options = app.config.action_controller
- options.assets_dir ||= paths.public.to_a.first
- options.javascripts_dir ||= paths.public.javascripts.to_a.first
- options.stylesheets_dir ||= paths.public.stylesheets.to_a.first
- options.page_cache_directory ||= paths.public.to_a.first
+ options.assets_dir ||= paths["public"].first
+ options.javascripts_dir ||= paths["public/javascripts"].first
+ options.stylesheets_dir ||= paths["public/stylesheets"].first
+ options.page_cache_directory ||= paths["public"].first
# make sure readers methods get compiled
options.asset_path ||= nil
diff --git a/actionpack/lib/action_controller/railties/paths.rb b/actionpack/lib/action_controller/railties/paths.rb
index fa71d55946..7a59d4f2f3 100644
--- a/actionpack/lib/action_controller/railties/paths.rb
+++ b/actionpack/lib/action_controller/railties/paths.rb
@@ -5,12 +5,14 @@ module ActionController
Module.new do
define_method(:inherited) do |klass|
super(klass)
+
if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) }
- klass.helpers_path = namespace._railtie.config.paths.app.helpers.to_a
+ paths = namespace._railtie.paths["app/helpers"].existent
else
- klass.helpers_path = app.config.helpers_paths
+ paths = app.config.helpers_paths
end
+ klass.helpers_path = paths
klass.helper :all if klass.superclass == ActionController::Base
end
end
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 70a5de7f30..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
@@ -394,7 +397,7 @@ module ActionController
parameters ||= {}
@request.assign_parameters(@routes, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
- @request.session = ActionController::TestSession.new(session) unless session.nil?
+ @request.session = ActionController::TestSession.new(session) if session
@request.session["flash"] = @request.flash.update(flash || {})
@request.session["flash"].sweep
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 7a28228817..bbcdefb190 100644
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -54,11 +54,7 @@ module ActionDispatch
# the application should use), this \method returns the overridden
# value, not the original.
def request_method
- @request_method ||= begin
- method = env["REQUEST_METHOD"]
- HTTP_METHOD_LOOKUP[method] || raise(ActionController::UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
- method
- end
+ @request_method ||= check_method(env["REQUEST_METHOD"])
end
# Returns a symbol form of the #request_method
@@ -70,11 +66,7 @@ module ActionDispatch
# even if it was overridden by middleware. See #request_method for
# more information.
def method
- @method ||= begin
- method = env["rack.methodoverride.original_method"] || env['REQUEST_METHOD']
- HTTP_METHOD_LOOKUP[method] || raise(ActionController::UnknownHttpMethod, "#{method}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
- method
- end
+ @method ||= check_method(env["rack.methodoverride.original_method"] || env['REQUEST_METHOD'])
end
# Returns a symbol form of the #method
@@ -207,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
@@ -246,5 +238,12 @@ module ActionDispatch
def local?
LOCALHOST.any? { |local_ip| local_ip === remote_addr && local_ip === remote_ip }
end
+
+ private
+
+ def check_method(name)
+ HTTP_METHOD_LOOKUP[name] || raise(ActionController::UnknownHttpMethod, "#{name}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
+ name
+ end
end
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 ea49b30630..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 }
- unless options[:expire_after].nil?
- 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 0cb02c5b80..3c7dcea003 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
@@ -345,11 +345,11 @@ module ActionDispatch
# Redirect any path to another path:
#
# match "/stories" => redirect("/posts")
- def redirect(*args, &block)
+ def redirect(*args)
options = args.last.is_a?(Hash) ? args.pop : {}
- path = args.shift || block
- path_proc = path.is_a?(Proc) ? path : proc { |params| params.empty? ? path : (path % params) }
+ path = args.shift || Proc.new
+ path_proc = path.is_a?(Proc) ? path : proc { |params| (params.empty? || !path.match(/%\{\w*\}/)) ? path : (path % params) }
status = options[:status] || 301
lambda do |env|
@@ -735,15 +735,15 @@ module ActionDispatch
resource_scope(SingletonResource.new(resources.pop, options)) do
yield if block_given?
- collection_scope do
+ collection do
post :create
end if parent_resource.actions.include?(:create)
- new_scope do
+ new do
get :new
end if parent_resource.actions.include?(:new)
- member_scope do
+ member do
get :edit if parent_resource.actions.include?(:edit)
get :show if parent_resource.actions.include?(:show)
put :update if parent_resource.actions.include?(:update)
@@ -780,16 +780,16 @@ module ActionDispatch
resource_scope(Resource.new(resources.pop, options)) do
yield if block_given?
- collection_scope do
+ collection do
get :index if parent_resource.actions.include?(:index)
post :create if parent_resource.actions.include?(:create)
end
- new_scope do
+ new do
get :new
end if parent_resource.actions.include?(:new)
- member_scope do
+ member do
get :edit if parent_resource.actions.include?(:edit)
get :show if parent_resource.actions.include?(:show)
put :update if parent_resource.actions.include?(:update)
@@ -813,12 +813,14 @@ module ActionDispatch
# create the <tt>search_photos_url</tt> and <tt>search_photos_path</tt>
# route helpers.
def collection
- unless @scope[:scope_level] == :resources
- raise ArgumentError, "can't use collection outside resources scope"
+ unless resource_scope?
+ raise ArgumentError, "can't use collection outside resource(s) scope"
end
- collection_scope do
- yield
+ with_scope_level(:collection) do
+ scope(parent_resource.collection_scope) do
+ yield
+ end
end
end
@@ -838,8 +840,10 @@ module ActionDispatch
raise ArgumentError, "can't use member outside resource(s) scope"
end
- member_scope do
- yield
+ with_scope_level(:member) do
+ scope(parent_resource.member_scope) do
+ yield
+ end
end
end
@@ -848,8 +852,10 @@ module ActionDispatch
raise ArgumentError, "can't use new outside resource(s) scope"
end
- new_scope do
- yield
+ with_scope_level(:new) do
+ scope(parent_resource.new_scope(action_path(:new))) do
+ yield
+ end
end
end
@@ -1034,30 +1040,6 @@ module ActionDispatch
end
end
- def new_scope
- with_scope_level(:new) do
- scope(parent_resource.new_scope(action_path(:new))) do
- yield
- end
- end
- end
-
- def collection_scope
- with_scope_level(:collection) do
- scope(parent_resource.collection_scope) do
- yield
- end
- end
- end
-
- def member_scope
- with_scope_level(:member) do
- scope(parent_resource.member_scope) do
- yield
- end
- end
- end
-
def nested_options
{}.tap do |options|
options[:as] = parent_resource.member_name
@@ -1130,7 +1112,7 @@ module ActionDispatch
end
candidate = name.select(&:present?).join("_").presence
- candidate unless as.nil? && @set.routes.map(&:name).include?(candidate)
+ candidate unless as.nil? && @set.routes.find { |r| r.name == candidate }
end
end
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 5d18dfe369..32f41934f1 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -1,6 +1,7 @@
require 'rack/mount'
require 'forwardable'
require 'active_support/core_ext/object/to_query'
+require 'active_support/core_ext/hash/slice'
module ActionDispatch
module Routing
@@ -511,7 +512,7 @@ module ActionDispatch
end
script_name = options.delete(:script_name)
- path = (script_name.blank? ? _generate_prefix(options) : script_name).to_s
+ path = (script_name.blank? ? _generate_prefix(options) : script_name.chomp('/')).to_s
path_options = options.except(*RESERVED_OPTIONS)
path_options = yield(path_options) if block_given?
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index 10b122557a..1558c3ae05 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -81,14 +81,10 @@ module ActionDispatch
def normalize_argument_to_redirection(fragment)
case fragment
- when %r{^\w[\w\d+.-]*:.*}
+ when %r{^\w[A-Za-z\d+.-]*:.*}
fragment
when String
- if fragment =~ %r{^\w[\w\d+.-]*:.*}
- fragment
- else
- @request.protocol + @request.host_with_port + fragment
- end
+ @request.protocol + @request.host_with_port + fragment
when :back
raise RedirectBackError unless refer = @request.headers["Referer"]
refer
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.rb b/actionpack/lib/action_view.rb
index 5f9dc70766..0f9d35d062 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -38,11 +38,17 @@ module ActionView
autoload :Helpers
autoload :Base
autoload :LookupContext
+ autoload :Render
autoload :PathSet, "action_view/paths"
autoload :TestCase, "action_view/test_case"
+ autoload_under "renderer" do
+ autoload :AbstractRenderer
+ autoload :PartialRenderer
+ autoload :TemplateRenderer
+ end
+
autoload_under "render" do
- autoload :Layouts
autoload :Partials
autoload :Rendering
end
@@ -51,6 +57,7 @@ module ActionView
autoload :Resolver
autoload :PathResolver
autoload :FileSystemResolver
+ autoload :FallbackFileSystemResolver
end
autoload_at "action_view/template/error" do
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 0bef3e3a08..a7a6bbd3a4 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -155,10 +155,7 @@ module ActionView #:nodoc:
#
# See the ActionView::Helpers::PrototypeHelper::JavaScriptGenerator::GeneratorMethods documentation for more details.
class Base
- module Subclasses
- end
-
- include Helpers, Rendering, Partials, Layouts, ::ERB::Util, Context
+ include Helpers, Rendering, Partials, ::ERB::Util, Context
# Specify whether RJS responses should be wrapped in a try/catch block
# that alert()s the caught exception (and then re-raises it).
@@ -177,13 +174,12 @@ module ActionView #:nodoc:
delegate :logger, :to => 'ActionController::Base', :allow_nil => true
end
- attr_accessor :base_path, :assigns, :template_extension, :lookup_context
- attr_internal :captures, :request, :controller, :template, :config
+ attr_accessor :_template
+ attr_internal :request, :controller, :config, :assigns, :lookup_context
- delegate :find_template, :template_exists?, :formats, :formats=, :locale, :locale=,
- :view_paths, :view_paths=, :with_fallbacks, :update_details, :with_layout_format, :to => :lookup_context
+ delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
- delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
+ delegate :request_forgery_protection_token, :params, :session, :cookies, :response, :headers,
:flash, :action_name, :controller_name, :to => :controller
delegate :logger, :to => :controller, :allow_nil => true
@@ -198,26 +194,30 @@ module ActionView #:nodoc:
end
def assign(new_assigns) # :nodoc:
- self.assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
+ @_assigns = new_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
end
def initialize(lookup_context = nil, assigns_for_first_render = {}, controller = nil, formats = nil) #:nodoc:
assign(assigns_for_first_render)
- self.helpers = self.class.helpers || Module.new
-
- if @_controller = controller
- @_request = controller.request if controller.respond_to?(:request)
- end
-
- @_config = controller && controller.respond_to?(:config) ? controller.config.inheritable_copy : {}
+ self.helpers = Module.new unless self.class.helpers
+ @_config = {}
@_content_for = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
@_virtual_path = nil
@output_buffer = nil
- @lookup_context = lookup_context.is_a?(ActionView::LookupContext) ?
+ if @_controller = controller
+ @_request = controller.request if controller.respond_to?(:request)
+ @_config = controller.config.inheritable_copy if controller.respond_to?(:config)
+ end
+
+ @_lookup_context = lookup_context.is_a?(ActionView::LookupContext) ?
lookup_context : ActionView::LookupContext.new(lookup_context)
- @lookup_context.formats = formats if formats
+ @_lookup_context.formats = formats if formats
+ end
+
+ def store_content_for(key, value)
+ @_content_for[key] = value
end
def controller_path
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index c1dfbe5dc3..d0f91f6b71 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -152,7 +152,7 @@ module ActionView
#
# # Normally you'd calculate RELEASE_NUMBER at startup.
# RELEASE_NUMBER = 12345
- # config.action_controller.asset_path_template = proc { |asset_path|
+ # config.action_controller.asset_path = proc { |asset_path|
# "/release-#{RELEASE_NUMBER}#{asset_path}"
# }
#
@@ -292,9 +292,6 @@ module ActionView
#
# * = The application.js file is only referenced if it exists
#
- # Though it's not really recommended practice, if you need to extend the default JavaScript set for any reason
- # (e.g., you're going to be using a certain .js file in every action), then take a look at the register_javascript_include_default method.
- #
# You can also include all javascripts in the +javascripts+ directory using <tt>:all</tt> as the source:
#
# javascript_include_tag :all # =>
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index c47fac05ef..b34a74788e 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".
@@ -1015,19 +1015,14 @@ module ActionView
module ClassMethods
def value(object, method_name)
- object.send method_name unless object.nil?
+ object.send method_name if object
end
def value_before_type_cast(object, method_name)
unless object.nil?
- if object.respond_to?(method_name)
- object.send(method_name)
- # FIXME: this is AR dependent
- elsif object.respond_to?(method_name + "_before_type_cast")
- object.send(method_name + "_before_type_cast")
- else
- raise NoMethodError, "Model #{object.class} does not respond to #{method_name}"
- end
+ object.respond_to?(method_name + "_before_type_cast") ?
+ object.send(method_name + "_before_type_cast") :
+ object.send(method_name)
end
end
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 83434a9340..7d6aca0470 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -297,7 +297,6 @@ module ActionView
def options_for_select(container, selected = nil)
return container if String === container
- container = container.to_a if Hash === container
selected, disabled = extract_selected_and_disabled(selected).map do | r |
Array.wrap(r).map(&:to_s)
end
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 300207dfac..41cd8d5171 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -546,7 +546,7 @@ module ActionView
end
def with_formats(*args)
- @context ? @context.update_details(:formats => args) { yield } : yield
+ @context ? @context.lookup_context.update_details(:formats => args) { yield } : yield
end
def javascript_object_for(object)
diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb
index 3bc5afc2c4..94348cf9fa 100644
--- a/actionpack/lib/action_view/helpers/text_helper.rb
+++ b/actionpack/lib/action_view/helpers/text_helper.rb
@@ -365,7 +365,7 @@ module ActionView
# <% end %>
def current_cycle(name = "default")
cycle = get_cycle(name)
- cycle.current_value unless cycle.nil?
+ cycle.current_value if cycle
end
# Resets a cycle so that it starts from the first element the next time
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index 13767a09f9..8574ca6595 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -45,8 +45,8 @@ module ActionView
private
def scope_key_by_partial(key)
if key.to_s.first == "."
- if @_virtual_path
- @_virtual_path.gsub(%r{/_?}, ".") + key.to_s
+ if (path = @_template && @_template.virtual_path)
+ path.gsub(%r{/_?}, ".") + key.to_s
else
raise "Cannot use t(#{key.inspect}) shortcut because path is not available"
end
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/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
index 3ea8b86af1..80451798b1 100644
--- a/actionpack/lib/action_view/lookup_context.rb
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -10,7 +10,7 @@ module ActionView
# this key is generated just once during the request, it speeds up all cache accesses.
class LookupContext #:nodoc:
mattr_accessor :fallbacks
- @@fallbacks = [FileSystemResolver.new(""), FileSystemResolver.new("/")]
+ @@fallbacks = FallbackFileSystemResolver.instances
mattr_accessor :registered_details
self.registered_details = []
@@ -61,6 +61,7 @@ module ActionView
def initialize(view_paths, details = {})
@details, @details_key = { :handlers => default_handlers }, nil
@frozen_formats, @skip_default_locale = false, false
+ @cache = details.key?(:cache) ? details.delete(:cache) : true
self.view_paths = view_paths
self.registered_detail_setters.each do |key, setter|
@@ -77,17 +78,17 @@ module ActionView
@view_paths = ActionView::Base.process_view_paths(paths)
end
- def find(name, prefix = nil, partial = false)
- @view_paths.find(*args_for_lookup(name, prefix, partial))
+ def find(name, prefix = nil, partial = false, keys = [])
+ @view_paths.find(*args_for_lookup(name, prefix, partial, keys))
end
alias :find_template :find
- def find_all(name, prefix = nil, partial = false)
- @view_paths.find_all(*args_for_lookup(name, prefix, partial))
+ def find_all(name, prefix = nil, partial = false, keys = [])
+ @view_paths.find_all(*args_for_lookup(name, prefix, partial, keys))
end
- def exists?(name, prefix = nil, partial = false)
- @view_paths.exists?(*args_for_lookup(name, prefix, partial))
+ def exists?(name, prefix = nil, partial = false, keys = [])
+ @view_paths.exists?(*args_for_lookup(name, prefix, partial, keys))
end
alias :template_exists? :exists?
@@ -106,9 +107,9 @@ module ActionView
protected
- def args_for_lookup(name, prefix, partial) #:nodoc:
+ def args_for_lookup(name, prefix, partial, keys) #:nodoc:
name, prefix = normalize_name(name, prefix)
- [name, prefix, partial || false, @details, details_key]
+ [name, prefix, partial || false, @details, keys, details_key]
end
# Support legacy foo.erb names even though we now ignore .erb
@@ -130,10 +131,20 @@ module ActionView
end
module Details
+ attr_accessor :cache
+
# Calculate the details key. Remove the handlers from calculation to improve performance
# since the user cannot modify it explicitly.
def details_key #:nodoc:
- @details_key ||= DetailsKey.get(@details)
+ @details_key ||= DetailsKey.get(@details) if @cache
+ end
+
+ # Temporary skip passing the details_key forward.
+ def disable_cache
+ old_value, @cache = @cache, false
+ yield
+ ensure
+ @cache = old_value
end
# Freeze the current formats in the lookup context. By freezing them, you are guaranteeing
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index 9857d688d2..dc26d75ff3 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -10,8 +10,8 @@ module ActionView #:nodoc:
METHOD
end
- def find(path, prefix = nil, partial = false, details = {}, key = nil)
- template = find_all(path, prefix, partial, details, key).first
+ def find(path, prefix = nil, partial = false, details = {}, keys = [], key = nil)
+ template = find_all(path, prefix, partial, details, keys, key).first
raise MissingTemplate.new(self, "#{prefix}/#{path}", details, partial) unless template
template
end
diff --git a/actionpack/lib/action_view/render/layouts.rb b/actionpack/lib/action_view/render/layouts.rb
deleted file mode 100644
index 8882acca31..0000000000
--- a/actionpack/lib/action_view/render/layouts.rb
+++ /dev/null
@@ -1,83 +0,0 @@
-module ActionView
- # = Action View Layouts
- module Layouts
- # Returns the contents that are yielded to a layout, given a name or a block.
- #
- # You can think of a layout as a method that is called with a block. If the user calls
- # <tt>yield :some_name</tt>, the block, by default, returns <tt>content_for(:some_name)</tt>.
- # If the user calls simply +yield+, the default block returns <tt>content_for(:layout)</tt>.
- #
- # The user can override this default by passing a block to the layout:
- #
- # # The template
- # <%= render :layout => "my_layout" do %>
- # Content
- # <% end %>
- #
- # # The layout
- # <html>
- # <%= yield %>
- # </html>
- #
- # In this case, instead of the default block, which would return <tt>content_for(:layout)</tt>,
- # this method returns the block that was passed in to <tt>render :layout</tt>, and the response
- # would be
- #
- # <html>
- # Content
- # </html>
- #
- # Finally, the block can take block arguments, which can be passed in by +yield+:
- #
- # # The template
- # <%= render :layout => "my_layout" do |customer| %>
- # Hello <%= customer.name %>
- # <% end %>
- #
- # # The layout
- # <html>
- # <%= yield Struct.new(:name).new("David") %>
- # </html>
- #
- # In this case, the layout would receive the block passed into <tt>render :layout</tt>,
- # and the struct specified would be passed into the block as an argument. The result
- # would be
- #
- # <html>
- # Hello David
- # </html>
- #
- def _layout_for(*args, &block) #:nodoc:
- name = args.first
-
- if name.is_a?(Symbol)
- @_content_for[name].html_safe
- elsif block
- capture(*args, &block)
- else
- @_content_for[:layout].html_safe
- end
- end
-
- # This is the method which actually finds the layout using details in the lookup
- # context object. If no layout is found, it checks if at least a layout with
- # the given name exists across all details before raising the error.
- def find_layout(layout)
- begin
- with_layout_format do
- layout =~ /^\// ?
- with_fallbacks { find_template(layout) } : find_template(layout)
- end
- rescue ActionView::MissingTemplate => e
- update_details(:formats => nil) do
- raise unless template_exists?(layout)
- end
- end
- end
-
- # Contains the logic that actually renders the layout.
- def _render_layout(layout, locals, &block) #:nodoc:
- layout.render(self, locals){ |*name| _layout_for(*name, &block) }
- end
- end
-end
diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb
index cc9b444837..844c3e9572 100644
--- a/actionpack/lib/action_view/render/partials.rb
+++ b/actionpack/lib/action_view/render/partials.rb
@@ -210,165 +210,12 @@ module ActionView
# <%- end -%>
# <% end %>
module Partials
- extend ActiveSupport::Concern
-
- class PartialRenderer
- PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
-
- def initialize(view_context, options, block)
- @view = view_context
- @partial_names = PARTIAL_NAMES[@view.controller.class.name]
-
- setup(options, block)
- end
-
- def setup(options, block)
- partial = options[:partial]
-
- @options = options
- @locals = options[:locals] || {}
- @block = block
-
- if String === partial
- @object = options[:object]
- @path = partial
- @collection = collection
- else
- @object = partial
-
- if @collection = collection
- paths = @collection_paths = @collection.map { |o| partial_path(o) }
- @path = paths.uniq.size == 1 ? paths.first : nil
- else
- @path = partial_path
- end
- end
- end
-
- def render
- identifier = ((@template = find_template) ? @template.identifier : @path)
-
- if @collection
- ActiveSupport::Notifications.instrument("render_collection.action_view",
- :identifier => identifier || "collection", :count => @collection.size) do
- render_collection
- end
- else
- content = ActiveSupport::Notifications.instrument("render_partial.action_view",
- :identifier => identifier) do
- render_partial
- end
-
- if !@block && (layout = @options[:layout])
- content = @view._render_layout(find_template(layout), @locals){ content }
- end
-
- content
- end
- end
-
- def render_collection
- return nil if @collection.blank?
-
- if @options.key?(:spacer_template)
- spacer = find_template(@options[:spacer_template]).render(@view, @locals)
- end
-
- result = @template ? collection_with_template : collection_without_template
- result.join(spacer).html_safe
- end
-
- def collection_with_template(template = @template)
- segments, locals, template = [], @locals, @template
-
- if @options[:as]
- as = @options[:as]
- counter = "#{as}_counter".to_sym
- else
- as = template.variable_name
- counter = template.counter_name
- end
-
- locals[counter] = -1
-
- @collection.each do |object|
- locals[counter] += 1
- locals[as] = object
- segments << template.render(@view, locals)
- end
-
- segments
- end
-
- def collection_without_template(collection_paths = @collection_paths)
- segments, locals = [], @locals
- index, template = -1, nil
-
- if @options[:as]
- as = @options[:as]
- counter = "#{as}_counter"
- end
-
- @collection.each_with_index do |object, i|
- template = find_template(collection_paths[i])
- locals[as || template.variable_name] = object
- locals[counter || template.counter_name] = (index += 1)
-
- segments << template.render(@view, locals)
- end
-
- @template = template
- segments
- end
-
- def render_partial(object = @object)
- locals, view, template = @locals, @view, @template
-
- object ||= locals[template.variable_name]
- locals[@options[:as] || template.variable_name] = object
-
- template.render(view, locals) do |*name|
- view._layout_for(*name, &@block)
- end
- end
-
- private
-
- def collection
- if @object.respond_to?(:to_ary)
- @object
- elsif @options.key?(:collection)
- @options[:collection] || []
- end
- end
-
- def find_template(path=@path)
- return path unless path.is_a?(String)
- prefix = @view.controller_path unless path.include?(?/)
- @view.find_template(path, prefix, true)
- end
-
- def partial_path(object = @object)
- @partial_names[object.class.name] ||= begin
- object = object.to_model if object.respond_to?(:to_model)
-
- object.class.model_name.partial_path.dup.tap do |partial|
- path = @view.controller_path
- partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
- end
- end
- end
- end
-
def _render_partial(options, &block) #:nodoc:
- if defined?(@renderer)
- @renderer.setup(options, block)
- else
- @renderer = PartialRenderer.new(self, options, block)
- end
-
- @renderer.render
+ _partial_renderer.setup(options, block).render
end
+ def _partial_renderer #:nodoc:
+ @_partial_renderer ||= PartialRenderer.new(self)
+ end
end
end
diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb
index 5320245173..baa5d2c3fd 100644
--- a/actionpack/lib/action_view/render/rendering.rb
+++ b/actionpack/lib/action_view/render/rendering.rb
@@ -10,6 +10,7 @@ module ActionView
# * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
# * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
# * <tt>:text</tt> - Renders the text passed in out.
+ # * <tt>:once</tt> - Accepts a string or an array of strings and Rails will ensure they each of them are rendered just once.
#
# If no options hash is passed or :update specified, the default is to render a partial and use the second parameter
# as the locals hash.
@@ -20,10 +21,10 @@ module ActionView
_render_partial(options.merge(:partial => options[:layout]), &block)
elsif options.key?(:partial)
_render_partial(options)
+ elsif options.key?(:once)
+ _render_once(options)
else
- template = _determine_template(options)
- lookup_context.freeze_formats(template.formats, true)
- _render_template(template, options[:layout], options)
+ _render_template(options)
end
when :update
update_page(&block)
@@ -32,36 +33,74 @@ module ActionView
end
end
- # Determine the template to be rendered using the given options.
- def _determine_template(options) #:nodoc:
- if options.key?(:inline)
- handler = Template.handler_class_for_extension(options[:type] || "erb")
- Template.new(options[:inline], "inline template", handler, {})
- elsif options.key?(:text)
- Template::Text.new(options[:text], formats.try(:first))
- elsif options.key?(:file)
- with_fallbacks { find_template(options[:file], options[:prefix]) }
- elsif options.key?(:template)
- options[:template].respond_to?(:render) ?
- options[:template] : find_template(options[:template], options[:prefix])
+ # Returns the contents that are yielded to a layout, given a name or a block.
+ #
+ # You can think of a layout as a method that is called with a block. If the user calls
+ # <tt>yield :some_name</tt>, the block, by default, returns <tt>content_for(:some_name)</tt>.
+ # If the user calls simply +yield+, the default block returns <tt>content_for(:layout)</tt>.
+ #
+ # The user can override this default by passing a block to the layout:
+ #
+ # # The template
+ # <%= render :layout => "my_layout" do %>
+ # Content
+ # <% end %>
+ #
+ # # The layout
+ # <html>
+ # <%= yield %>
+ # </html>
+ #
+ # In this case, instead of the default block, which would return <tt>content_for(:layout)</tt>,
+ # this method returns the block that was passed in to <tt>render :layout</tt>, and the response
+ # would be
+ #
+ # <html>
+ # Content
+ # </html>
+ #
+ # Finally, the block can take block arguments, which can be passed in by +yield+:
+ #
+ # # The template
+ # <%= render :layout => "my_layout" do |customer| %>
+ # Hello <%= customer.name %>
+ # <% end %>
+ #
+ # # The layout
+ # <html>
+ # <%= yield Struct.new(:name).new("David") %>
+ # </html>
+ #
+ # In this case, the layout would receive the block passed into <tt>render :layout</tt>,
+ # and the struct specified would be passed into the block as an argument. The result
+ # would be
+ #
+ # <html>
+ # Hello David
+ # </html>
+ #
+ def _layout_for(*args, &block)
+ name = args.first
+
+ if name.is_a?(Symbol)
+ @_content_for[name].html_safe
+ elsif block
+ capture(*args, &block)
+ else
+ @_content_for[:layout].html_safe
end
end
- # Renders the given template. An string representing the layout can be
- # supplied as well.
- def _render_template(template, layout = nil, options = {}) #:nodoc:
- locals = options[:locals] || {}
- layout = find_layout(layout) if layout
-
- ActiveSupport::Notifications.instrument("render_template.action_view",
- :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
+ def _render_once(options) #:nodoc:
+ _template_renderer.render_once(options)
+ end
- content = template.render(self, locals) { |*name| _layout_for(*name) }
- @_content_for[:layout] = content if layout
+ def _render_template(options) #:nodoc:
+ _template_renderer.render(options)
+ end
- content = _render_layout(layout, locals) if layout
- content
- end
+ def _template_renderer #:nodoc:
+ @_template_renderer ||= TemplateRenderer.new(self)
end
end
end
diff --git a/actionpack/lib/action_view/renderer/abstract_renderer.rb b/actionpack/lib/action_view/renderer/abstract_renderer.rb
new file mode 100644
index 0000000000..77cfa51dff
--- /dev/null
+++ b/actionpack/lib/action_view/renderer/abstract_renderer.rb
@@ -0,0 +1,36 @@
+module ActionView
+ class AbstractRenderer #:nodoc:
+ attr_reader :vew, :lookup_context
+
+ delegate :find_template, :template_exists?, :with_fallbacks, :update_details,
+ :with_layout_format, :formats, :to => :lookup_context
+
+ def initialize(view)
+ @view = view
+ @lookup_context = view.lookup_context
+ end
+
+ def render
+ raise NotImplementedError
+ end
+
+ # Checks if the given path contains a format and if so, change
+ # the lookup context to take this new format into account.
+ def wrap_formats(value)
+ return yield unless value.is_a?(String)
+ @@formats_regexp ||= /\.(#{Mime::SET.symbols.join('|')})$/
+
+ if value.sub!(@@formats_regexp, "")
+ update_details(:formats => [$1.to_sym]){ yield }
+ else
+ yield
+ end
+ end
+
+ protected
+
+ def instrument(name, options={})
+ ActiveSupport::Notifications.instrument("render_#{name}.action_view", options){ yield }
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
new file mode 100644
index 0000000000..eff425687b
--- /dev/null
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -0,0 +1,166 @@
+require 'action_view/renderer/abstract_renderer'
+
+module ActionView
+ class PartialRenderer < AbstractRenderer #:nodoc:
+ PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
+
+ def initialize(view)
+ super
+ @partial_names = PARTIAL_NAMES[@view.controller.class.name]
+ end
+
+ def setup(options, block)
+ partial = options[:partial]
+
+ @options = options
+ @locals = options[:locals] || {}
+ @block = block
+
+ if String === partial
+ @object = options[:object]
+ @path = partial
+ @collection = collection
+ else
+ @object = partial
+
+ if @collection = collection_from_object || collection
+ paths = @collection_data = @collection.map { |o| partial_path(o) }
+ @path = paths.uniq.size == 1 ? paths.first : nil
+ else
+ @path = partial_path
+ end
+ end
+
+ if @path
+ @variable, @variable_counter = retrieve_variable(@path)
+ else
+ paths.map! { |path| retrieve_variable(path).unshift(path) }
+ end
+
+ self
+ end
+
+ def render
+ wrap_formats(@path) do
+ identifier = ((@template = find_partial) ? @template.identifier : @path)
+
+ if @collection
+ instrument(:collection, :identifier => identifier || "collection", :count => @collection.size) do
+ render_collection
+ end
+ else
+ instrument(:partial, :identifier => identifier) do
+ render_partial
+ end
+ end
+ end
+ end
+
+ def render_collection
+ return nil if @collection.blank?
+
+ if @options.key?(:spacer_template)
+ spacer = find_template(@options[:spacer_template]).render(@view, @locals)
+ end
+
+ result = @template ? collection_with_template : collection_without_template
+ result.join(spacer).html_safe
+ end
+
+ def render_partial
+ locals, view, block = @locals, @view, @block
+ object, as = @object, @variable
+
+ if !block && (layout = @options[:layout])
+ layout = find_template(layout)
+ end
+
+ object ||= locals[as]
+ locals[as] = object
+
+ content = @template.render(view, locals) do |*name|
+ view._layout_for(*name, &block)
+ end
+
+ content = layout.render(view, locals){ content } if layout
+ content
+ end
+
+ private
+
+ def collection
+ if @options.key?(:collection)
+ @options[:collection] || []
+ end
+ end
+
+ def collection_from_object
+ if @object.respond_to?(:to_ary)
+ @object
+ end
+ end
+
+ def find_partial
+ if path = @path
+ locals = @locals.keys
+ locals << @variable
+ locals << @variable_counter if @collection
+ find_template(path, locals)
+ end
+ end
+
+ def find_template(path=@path, locals=@locals.keys)
+ prefix = @view.controller_path unless path.include?(?/)
+ @lookup_context.find_template(path, prefix, true, locals)
+ end
+
+ def collection_with_template
+ segments, locals, template = [], @locals, @template
+ as, counter = @variable, @variable_counter
+
+ locals[counter] = -1
+
+ @collection.each do |object|
+ locals[counter] += 1
+ locals[as] = object
+ segments << template.render(@view, locals)
+ end
+
+ segments
+ end
+
+ def collection_without_template
+ segments, locals, collection_data = [], @locals, @collection_data
+ index, template, cache = -1, nil, {}
+ keys = @locals.keys
+
+ @collection.each_with_index do |object, i|
+ path, *data = collection_data[i]
+ template = (cache[path] ||= find_template(path, keys + data))
+ locals[data[0]] = object
+ locals[data[1]] = (index += 1)
+ segments << template.render(@view, locals)
+ end
+
+ @template = template
+ segments
+ end
+
+ def partial_path(object = @object)
+ @partial_names[object.class.name] ||= begin
+ object = object.to_model if object.respond_to?(:to_model)
+
+ object.class.model_name.partial_path.dup.tap do |partial|
+ path = @view.controller_path
+ partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/)
+ end
+ end
+ end
+
+ def retrieve_variable(path)
+ variable = @options[:as] || path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
+ variable_counter = :"#{variable}_counter" if @collection
+ [variable, variable_counter]
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
new file mode 100644
index 0000000000..a9076760c5
--- /dev/null
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -0,0 +1,97 @@
+require 'set'
+require 'active_support/core_ext/object/try'
+require 'active_support/core_ext/array/wrap'
+require 'action_view/renderer/abstract_renderer'
+
+module ActionView
+ class TemplateRenderer < AbstractRenderer #:nodoc:
+ attr_reader :rendered
+
+ def initialize(view)
+ super
+ @rendered = Set.new
+ end
+
+ def render(options)
+ wrap_formats(options[:template] || options[:file]) do
+ template = determine_template(options)
+ render_template(template, options[:layout], options[:locals])
+ end
+ end
+
+ def render_once(options)
+ paths, locals = options[:once], options[:locals] || {}
+ layout, keys, prefix = options[:layout], locals.keys, options[:prefix]
+
+ raise "render :once expects a String or an Array to be given" unless paths
+
+ render_with_layout(layout, locals) do
+ contents = []
+ Array.wrap(paths).each do |path|
+ template = find_template(path, prefix, false, keys)
+ contents << render_template(template, nil, locals) if @rendered.add?(template)
+ end
+ contents.join("\n")
+ end
+ end
+
+ # Determine the template to be rendered using the given options.
+ def determine_template(options) #:nodoc:
+ keys = options[:locals].try(:keys) || []
+
+ if options.key?(:text)
+ Template::Text.new(options[:text], formats.try(:first))
+ elsif options.key?(:file)
+ with_fallbacks { find_template(options[:file], options[:prefix], false, keys) }
+ elsif options.key?(:inline)
+ handler = Template.handler_class_for_extension(options[:type] || "erb")
+ Template::Inline.new(options[:inline], handler, :locals => keys)
+ elsif options.key?(:template)
+ options[:template].respond_to?(:render) ?
+ options[:template] : find_template(options[:template], options[:prefix], false, keys)
+ end
+ end
+
+ # Renders the given template. An string representing the layout can be
+ # supplied as well.
+ def render_template(template, layout_name = nil, locals = {}) #:nodoc:
+ lookup_context.freeze_formats(template.formats, true)
+ view, locals = @view, locals || {}
+
+ render_with_layout(layout_name, locals) do |layout|
+ instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
+ template.render(view, locals) { |*name| view._layout_for(*name) }
+ end
+ end
+ end
+
+ def render_with_layout(path, locals) #:nodoc:
+ layout = path && find_layout(path, locals.keys)
+ content = yield(layout)
+
+ if layout
+ view = @view
+ view.store_content_for(:layout, content)
+ layout.render(view, locals){ |*name| view._layout_for(*name) }
+ else
+ content
+ end
+ end
+
+ # This is the method which actually finds the layout using details in the lookup
+ # context object. If no layout is found, it checks if at least a layout with
+ # the given name exists across all details before raising the error.
+ def find_layout(layout, keys)
+ begin
+ with_layout_format do
+ layout =~ /^\// ?
+ with_fallbacks { find_template(layout, nil, false, keys) } : find_template(layout, nil, false, keys)
+ end
+ rescue ActionView::MissingTemplate => e
+ update_details(:formats => nil) do
+ raise unless template_exists?(layout)
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index a999a0b7d7..3ba18cbfae 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -93,14 +93,18 @@ module ActionView
autoload :Error
autoload :Handler
autoload :Handlers
+ autoload :Inline
autoload :Text
end
extend Template::Handlers
- attr_reader :source, :identifier, :handler, :virtual_path, :formats,
- :original_encoding
+ attr_accessor :locals, :formats, :virtual_path
+ attr_reader :source, :identifier, :handler, :original_encoding, :updated_at
+
+ # This finalizer is needed (and exactly with a proc inside another proc)
+ # otherwise templates leak in development.
Finalizer = proc do |method_name, mod|
proc do
mod.module_eval do
@@ -110,49 +114,83 @@ module ActionView
end
def initialize(source, identifier, handler, details)
- @source = source
- @identifier = identifier
- @handler = handler
- @original_encoding = nil
- @method_names = {}
-
- format = details[:format] || :html
- @formats = Array.wrap(format).map(&:to_sym)
- @virtual_path = details[:virtual_path].try(:sub, ".#{format}", "")
+ format = details[:format] || (handler.default_format if handler.respond_to?(:default_format))
+
+ @source = source
+ @identifier = identifier
+ @handler = handler
+ @compiled = false
+ @original_encoding = nil
+ @locals = details[:locals] || []
+ @virtual_path = details[:virtual_path]
+ @updated_at = details[:updated_at] || Time.now
+ @formats = Array.wrap(format).map(&:to_sym)
end
+ # Render a template. If the template was not compiled yet, it is done
+ # exactly before rendering.
+ #
+ # This method is instrumented as "!render_template.action_view". Notice that
+ # we use a bang in this instrumentation because you don't want to
+ # consume this in production. This is only slow if it's being listened to.
def render(view, locals, &block)
- # Notice that we use a bang in this instrumentation because you don't want to
- # consume this in production. This is only slow if it's being listened to.
+ old_template, view._template = view._template, self
ActiveSupport::Notifications.instrument("!render_template.action_view", :virtual_path => @virtual_path) do
- if view.is_a?(ActionView::CompiledTemplates)
- mod = ActionView::CompiledTemplates
- else
- mod = view.singleton_class
- end
-
- method_name = compile(locals, view, mod)
+ compile!(view)
view.send(method_name, locals, &block)
end
rescue Exception => e
- if e.is_a?(Template::Error)
- e.sub_template_of(self)
- raise e
- else
- raise Template::Error.new(self, view.respond_to?(:assigns) ? view.assigns : {}, e)
- end
+ handle_render_error(view, e)
+ ensure
+ view._template = old_template
end
def mime_type
@mime_type ||= Mime::Type.lookup_by_extension(@formats.first.to_s) if @formats.first
end
- def variable_name
- @variable_name ||= @virtual_path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
+ # Receives a view object and return a template similar to self by using @virtual_path.
+ #
+ # This method is useful if you have a template object but it does not contain its source
+ # anymore since it was already compiled. In such cases, all you need to do is to call
+ # refresh passing in the view object.
+ #
+ # Notice this method raises an error if the template to be refreshed does not have a
+ # virtual path set (true just for inline templates).
+ def refresh(view)
+ raise "A template needs to have a virtual path in order to be refreshed" unless @virtual_path
+ lookup = view.lookup_context
+ pieces = @virtual_path.split("/")
+ name = pieces.pop
+ partial = !!name.sub!(/^_/, "")
+ lookup.disable_cache do
+ lookup.find_template(name, pieces.join, partial, @locals)
+ end
+ end
+
+ # Expires this template by setting his updated_at date to Jan 1st, 1970.
+ def expire!
+ @updated_at = Time.utc(1970)
end
- def counter_name
- @counter_name ||= "#{variable_name}_counter".to_sym
+ # Receives a view context and renders a template exactly like self by using
+ # the @virtual_path. It raises an error if no @virtual_path was given.
+ def rerender(view)
+ raise "A template needs to have a virtual path in order to be rerendered" unless @virtual_path
+ name = @virtual_path.dup
+ if name.sub!(/(^|\/)_([^\/]*)$/, '\1\2')
+ view.render :partial => name
+ else
+ view.render :template => @virtual_path
+ end
+ end
+
+ def hash
+ identifier.hash
+ end
+
+ def eql?(other)
+ other.is_a?(Template) && other.identifier == identifier
end
def inspect
@@ -164,7 +202,27 @@ module ActionView
end
end
- private
+ protected
+
+ # Compile a template. This method ensures a template is compiled
+ # just once and removes the source after it is compiled.
+ def compile!(view) #:nodoc:
+ return if @compiled
+
+ if view.is_a?(ActionView::CompiledTemplates)
+ mod = ActionView::CompiledTemplates
+ else
+ mod = view.singleton_class
+ end
+
+ compile(view, mod)
+
+ # Just discard the source if we have a virtual path. This
+ # means we can get the template back.
+ @source = nil if @virtual_path
+ @compiled = true
+ end
+
# Among other things, this method is responsible for properly setting
# the encoding of the source. Until this point, we assume that the
# source is BINARY data. If no additional information is supplied,
@@ -185,11 +243,8 @@ module ActionView
# encode the source into Encoding.default_internal. In general,
# this means that templates will be UTF-8 inside of Rails,
# regardless of the original source encoding.
- def compile(locals, view, mod)
- method_name = build_method_name(locals)
- return method_name if view.respond_to?(method_name)
-
- locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join
+ def compile(view, mod) #:nodoc:
+ method_name = self.method_name
if source.encoding_aware?
# Look for # encoding: *. If we find one, we'll encode the
@@ -229,9 +284,9 @@ module ActionView
# encoding of the code
source = <<-end_src
def #{method_name}(local_assigns)
- _old_virtual_path, @_virtual_path = @_virtual_path, #{@virtual_path.inspect};_old_output_buffer = @output_buffer;#{locals_code};#{code}
+ _old_output_buffer = @output_buffer;#{locals_code};#{code}
ensure
- @_virtual_path, @output_buffer = _old_virtual_path, _old_output_buffer
+ @output_buffer = _old_output_buffer
end
end_src
@@ -254,8 +309,6 @@ module ActionView
begin
mod.module_eval(source, identifier, 0)
ObjectSpace.define_finalizer(self, Finalizer[method_name, mod])
-
- method_name
rescue Exception => e # errors from template code
if logger = (view && view.logger)
logger.debug "ERROR: compiling #{method_name} RAISED #{e}"
@@ -267,12 +320,27 @@ module ActionView
end
end
- def build_method_name(locals)
- @method_names[locals.keys.hash] ||= "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}_#{locals.keys.hash}".gsub('-', "_")
+ def handle_render_error(view, e) #:nodoc:
+ if e.is_a?(Template::Error)
+ e.sub_template_of(self)
+ raise e
+ else
+ assigns = view.respond_to?(:assigns) ? view.assigns : {}
+ template = @virtual_path ? refresh(view) : self
+ raise Template::Error.new(template, assigns, e)
+ end
+ end
+
+ def locals_code #:nodoc:
+ @locals.map { |key| "#{key} = local_assigns[:#{key}];" }.join
+ end
+
+ def method_name #:nodoc:
+ @method_name ||= "_#{identifier_method_name}__#{@identifier.hash}_#{__id__}".gsub('-', "_")
end
- def identifier_method_name
- @identifier_method_name ||= inspect.gsub(/[^a-z_]/, '_')
+ def identifier_method_name #:nodoc:
+ inspect.gsub(/[^a-z_]/, '_')
end
end
end
diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb
index 423e1e0bf5..ff256738a9 100644
--- a/actionpack/lib/action_view/template/error.rb
+++ b/actionpack/lib/action_view/template/error.rb
@@ -43,8 +43,9 @@ module ActionView
end
class Template
- # The Template::Error exception is raised when the compilation of the template fails. This exception then gathers a
- # bunch of intimate details and uses it to report a very precise exception message.
+ # The Template::Error exception is raised when the compilation or rendering of the template
+ # fails. This exception then gathers a bunch of intimate details and uses it to report a
+ # precise exception message.
class Error < ActionViewError #:nodoc:
SOURCE_CODE_RADIUS = 3
diff --git a/actionpack/lib/action_view/template/handler.rb b/actionpack/lib/action_view/template/handler.rb
index c6a1bc6235..0a9d299807 100644
--- a/actionpack/lib/action_view/template/handler.rb
+++ b/actionpack/lib/action_view/template/handler.rb
@@ -1,4 +1,4 @@
-require "action_dispatch/http/mime_type"
+require 'action_dispatch/http/mime_type'
require 'active_support/core_ext/class/attribute'
# Legacy TemplateHandler stub
@@ -7,6 +7,8 @@ module ActionView
module Handlers #:nodoc:
module Compilable
def self.included(base)
+ ActiveSupport::Deprecation.warn "Including Compilable in your template handler is deprecated. " <<
+ "All the API your template handler needs to implement is to respond to #call."
base.extend(ClassMethods)
end
@@ -26,6 +28,12 @@ module ActionView
class_attribute :default_format
self.default_format = Mime::HTML
+ def self.inherited(base)
+ ActiveSupport::Deprecation.warn "Inheriting from ActionView::Template::Handler is deprecated. " <<
+ "All the API your template handler needs to implement is to respond to #call."
+ super
+ end
+
def self.call(template)
raise "Need to implement #{self.class.name}#call(template)"
end
diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb
index ed397699b0..60347e2a95 100644
--- a/actionpack/lib/action_view/template/handlers.rb
+++ b/actionpack/lib/action_view/template/handlers.rb
@@ -7,13 +7,9 @@ module ActionView #:nodoc:
autoload :Builder, 'action_view/template/handlers/builder'
def self.extended(base)
- base.register_default_template_handler :erb, ERB
- base.register_template_handler :rjs, RJS
- base.register_template_handler :builder, Builder
-
- # TODO: Depreciate old template extensions
- base.register_template_handler :rhtml, ERB
- base.register_template_handler :rxml, Builder
+ base.register_default_template_handler :erb, ERB.new
+ base.register_template_handler :rjs, RJS.new
+ base.register_template_handler :builder, Builder.new
end
@@template_handlers = {}
diff --git a/actionpack/lib/action_view/template/handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb
index a93cfca8aa..2c52cfd90e 100644
--- a/actionpack/lib/action_view/template/handlers/builder.rb
+++ b/actionpack/lib/action_view/template/handlers/builder.rb
@@ -1,11 +1,11 @@
module ActionView
module Template::Handlers
- class Builder < Template::Handler
- include Compilable
-
+ class Builder
+ # Default format used by Builder.
+ class_attribute :default_format
self.default_format = Mime::XML
- def compile(template)
+ def call(template)
require 'builder'
"xml = ::Builder::XmlMarkup.new(:indent => 2);" +
"self.output_buffer = xml.target!;" +
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index 24e1e44c1d..b827610456 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -1,6 +1,7 @@
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/string/output_safety'
-require "action_view/template"
+require 'action_view/template'
+require 'action_view/template/handler'
require 'erubis'
module ActionView
@@ -47,28 +48,31 @@ module ActionView
end
end
- class ERB < Handler
- include Compilable
-
- ##
- # :singleton-method:
+ class ERB
# Specify trim mode for the ERB compiler. Defaults to '-'.
# See ERb documentation for suitable values.
- cattr_accessor :erb_trim_mode
+ class_attribute :erb_trim_mode
self.erb_trim_mode = '-'
+ # Default format used by ERB.
+ class_attribute :default_format
self.default_format = Mime::HTML
- cattr_accessor :erb_implementation
+ # Default implemenation used.
+ class_attribute :erb_implementation
self.erb_implementation = Erubis
ENCODING_TAG = Regexp.new("\\A(<%#{ENCODING_FLAG}-?%>)[ \\t]*")
- def self.handles_encoding?
+ def self.call(template)
+ new.call(template)
+ end
+
+ def handles_encoding?
true
end
- def compile(template)
+ def call(template)
if template.source.encoding_aware?
# First, convert to BINARY, so in case the encoding is
# wrong, we can still find an encoding tag
@@ -94,6 +98,7 @@ module ActionView
end
private
+
def valid_encoding(string, encoding)
# If a magic encoding comment was found, tag the
# String with this encoding. This is for a case
diff --git a/actionpack/lib/action_view/template/handlers/rjs.rb b/actionpack/lib/action_view/template/handlers/rjs.rb
index 128be5077c..9d71059134 100644
--- a/actionpack/lib/action_view/template/handlers/rjs.rb
+++ b/actionpack/lib/action_view/template/handlers/rjs.rb
@@ -1,17 +1,13 @@
module ActionView
module Template::Handlers
- class RJS < Template::Handler
- include Compilable
-
+ class RJS
+ # Default format used by RJS.
+ class_attribute :default_format
self.default_format = Mime::JS
- def compile(template)
+ def call(template)
"update_page do |page|;#{template.source}\nend"
end
-
- def default_format
- Mime::JS
- end
end
end
end
diff --git a/actionpack/lib/action_view/template/inline.rb b/actionpack/lib/action_view/template/inline.rb
new file mode 100644
index 0000000000..be08065b6b
--- /dev/null
+++ b/actionpack/lib/action_view/template/inline.rb
@@ -0,0 +1,20 @@
+require 'digest/md5'
+
+module ActionView
+ class Template
+ class Inline < ::ActionView::Template
+ def initialize(source, handler, options={})
+ super(source, "inline template", handler, options)
+ end
+
+ def md5_source
+ @md5_source ||= Digest::MD5.hexdigest(source)
+ end
+
+ def eql?(other)
+ other.is_a?(Inline) && other.md5_source == md5_source
+ end
+ end
+ end
+end
+ \ No newline at end of file
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index a261e08dbc..7707dbcf98 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -6,9 +6,8 @@ module ActionView
# = Action View Resolver
class Resolver
def initialize
- @path = nil
- @cached = Hash.new { |h1,k1| h1[k1] =
- Hash.new { |h2,k2| h2[k2] = Hash.new { |h3, k3| h3[k3] = {} } } }
+ @cached = Hash.new { |h1,k1| h1[k1] = Hash.new { |h2,k2|
+ h2[k2] = Hash.new { |h3,k3| h3[k3] = Hash.new { |h4,k4| h4[k4] = {} } } } }
end
def clear_cache
@@ -16,8 +15,8 @@ module ActionView
end
# Normalizes the arguments and passes it on to find_template.
- def find_all(name, prefix=nil, partial=false, details={}, key=nil)
- cached(key, prefix, name, partial) do
+ def find_all(name, prefix=nil, partial=false, details={}, locals=[], key=nil)
+ cached(key, [name, prefix, partial], details, locals) do
find_templates(name, prefix, partial, details)
end
end
@@ -35,34 +34,73 @@ module ActionView
raise NotImplementedError
end
- def cached(key, prefix, name, partial)
- return yield unless key && caching?
- @cached[key][prefix][name][partial] ||= yield
+ # Helpers that builds a path. Useful for building virtual paths.
+ def build_path(name, prefix, partial)
+ path = ""
+ path << "#{prefix}/" unless prefix.empty?
+ path << (partial ? "_#{name}" : name)
+ path
+ end
+
+ # Hnadles templates caching. If a key is given and caching is on
+ # always check the cache before hitting the resolver. Otherwise,
+ # it always hits the resolver but check if the resolver is fresher
+ # before returning it.
+ def cached(key, path_info, details, locals) #:nodoc:
+ name, prefix, partial = path_info
+ locals = sort_locals(locals)
+
+ if key && caching?
+ @cached[key][name][prefix][partial][locals] ||= decorate(yield, path_info, details, locals)
+ else
+ fresh = decorate(yield, path_info, details, locals)
+ return fresh unless key
+
+ scope = @cached[key][name][prefix][partial]
+ cache = scope[locals]
+ mtime = cache && cache.map(&:updated_at).max
+
+ if !mtime || fresh.empty? || fresh.any? { |t| t.updated_at > mtime }
+ scope[locals] = fresh
+ else
+ cache
+ end
+ end
+ end
+
+ # Ensures all the resolver information is set in the template.
+ def decorate(templates, path_info, details, locals) #:nodoc:
+ cached = nil
+ templates.each do |t|
+ t.locals = locals
+ t.formats = details[:formats] || [:html] if t.formats.empty?
+ t.virtual_path ||= (cached ||= build_path(*path_info))
+ end
+ end
+
+ if :symbol.respond_to?("<=>")
+ def sort_locals(locals) #:nodoc:
+ locals.sort.freeze
+ end
+ else
+ def sort_locals(locals) #:nodoc:
+ locals = locals.map{ |l| l.to_s }
+ locals.sort!
+ locals.freeze
+ end
end
end
class PathResolver < Resolver
EXTENSION_ORDER = [:locale, :formats, :handlers]
- def to_s
- @path.to_s
- end
- alias :to_path :to_s
-
- private
+ private
def find_templates(name, prefix, partial, details)
- path = build_path(name, prefix, partial, details)
+ path = build_path(name, prefix, partial)
query(path, EXTENSION_ORDER.map { |ext| details[ext] }, details[:formats])
end
- def build_path(name, prefix, partial, details)
- path = ""
- path << "#{prefix}/" unless prefix.empty?
- path << (partial ? "_#{name}" : name)
- path
- end
-
def query(path, exts, formats)
query = File.join(@path, path)
@@ -76,26 +114,28 @@ module ActionView
contents = File.open(p, "rb") {|io| io.read }
Template.new(contents, File.expand_path(p), handler,
- :virtual_path => path, :format => format)
+ :virtual_path => path, :format => format, :updated_at => mtime(p))
end
end
+ # Returns the file mtime from the filesystem.
+ def mtime(p)
+ File.stat(p).mtime
+ end
+
# Extract handler and formats from path. If a format cannot be a found neither
# from the path, or the handler, we should return the array of formats given
# to the resolver.
def extract_handler_and_format(path, default_formats)
pieces = File.basename(path).split(".")
pieces.shift
-
- handler = Template.handler_class_for_extension(pieces.pop)
- format = pieces.last && Mime[pieces.last] && pieces.pop.to_sym
- format ||= handler.default_format if handler.respond_to?(:default_format)
- format ||= default_formats
-
+ handler = Template.handler_class_for_extension(pieces.pop)
+ format = pieces.last && Mime[pieces.last]
[handler, format]
end
end
+ # A resolver that loads files from the filesystem.
class FileSystemResolver < PathResolver
def initialize(path)
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
@@ -103,9 +143,26 @@ module ActionView
@path = File.expand_path(path)
end
+ def to_s
+ @path.to_s
+ end
+ alias :to_path :to_s
+
def eql?(resolver)
self.class.equal?(resolver.class) && to_path == resolver.to_path
end
alias :== :eql?
end
+
+ # The same as FileSystemResolver but does not allow templates to store
+ # a virtual path since it is invalid for such resolvers.
+ class FallbackFileSystemResolver < FileSystemResolver
+ def self.instances
+ [new(""), new("/")]
+ end
+
+ def decorate(*)
+ super.each { |t| t.virtual_path = nil }
+ end
+ end
end
diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb
index 51be831dfb..4261c3b5e2 100644
--- a/actionpack/lib/action_view/template/text.rb
+++ b/actionpack/lib/action_view/template/text.rb
@@ -25,10 +25,6 @@ module ActionView #:nodoc:
def formats
[@mime_type.to_sym]
end
-
- def partial?
- false
- end
end
end
end
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/lib/action_view/testing/resolvers.rb b/actionpack/lib/action_view/testing/resolvers.rb
index b2b62528a9..55583096e0 100644
--- a/actionpack/lib/action_view/testing/resolvers.rb
+++ b/actionpack/lib/action_view/testing/resolvers.rb
@@ -13,26 +13,29 @@ module ActionView #:nodoc:
@hash = hash
end
- private
+ private
def query(path, exts, formats)
- query = Regexp.escape(path)
+ query = ""
exts.each do |ext|
query << '(' << ext.map {|e| e && Regexp.escape(".#{e}") }.join('|') << '|)'
end
+ query = /^(#{Regexp.escape(path)})#{query}$/
templates = []
- @hash.select { |k,v| k =~ /^#{query}$/ }.each do |_path, source|
+ @hash.each do |_path, array|
+ source, updated_at = array
+ next unless _path =~ query
handler, format = extract_handler_and_format(_path, formats)
templates << Template.new(source, _path, handler,
- :virtual_path => _path, :format => format)
+ :virtual_path => $1, :format => format, :updated_at => updated_at)
end
templates.sort_by {|t| -t.identifier.match(/^#{query}$/).captures.reject(&:blank?).size }
end
end
- class NullResolver < ActionView::PathResolver
+ class NullResolver < PathResolver
def query(path, exts, formats)
handler, format = extract_handler_and_format(path, formats)
[ActionView::Template.new("Template generated by Null Resolver", path, handler, :virtual_path => path, :format => format)]
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/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index d9d258e593..5a8b763717 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -32,6 +32,8 @@ class ActionPackAssertionsController < ActionController::Base
def redirect_to_path() redirect_to '/some/path' end
+ def redirect_invalid_external_route() redirect_to 'ht_tp://www.rubyonrails.org' end
+
def redirect_to_named_route() redirect_to route_one_url end
def redirect_external() redirect_to "http://www.rubyonrails.org"; end
@@ -368,6 +370,11 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
end
end
+ def test_redirect_invalid_external_route
+ process :redirect_invalid_external_route
+ assert_redirected_to "http://test.hostht_tp://www.rubyonrails.org"
+ end
+
def test_redirected_to_url_full_url
process :redirect_to_path
assert_redirected_to 'http://test.host/some/path'
diff --git a/actionpack/test/controller/capture_test.rb b/actionpack/test/controller/capture_test.rb
index eb426e855b..d78acb8ce8 100644
--- a/actionpack/test/controller/capture_test.rb
+++ b/actionpack/test/controller/capture_test.rb
@@ -25,6 +25,10 @@ class CaptureController < ActionController::Base
render :layout => "talk_from_action"
end
+ def proper_block_detection
+ @todo = "some todo"
+ end
+
def rescue_action(e) raise end
end
@@ -66,8 +70,8 @@ class CaptureTest < ActionController::TestCase
end
def test_proper_block_detection
- @todo = "some todo"
get :proper_block_detection
+ assert_equal "some todo", @response.body
end
private
diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb
index 967107853b..9500c25a32 100644
--- a/actionpack/test/controller/content_type_test.rb
+++ b/actionpack/test/controller/content_type_test.rb
@@ -29,18 +29,18 @@ class OldContentTypeController < ActionController::Base
render :text => "hello world!"
end
- def render_default_for_rhtml
+ def render_default_for_erb
end
- def render_default_for_rxml
+ def render_default_for_builder
end
def render_default_for_rjs
end
- def render_change_for_rxml
+ def render_change_for_builder
response.content_type = Mime::HTML
- render :action => "render_default_for_rxml"
+ render :action => "render_default_for_builder"
end
def render_default_content_types_for_respond_to
@@ -108,23 +108,23 @@ class ContentTypeTest < ActionController::TestCase
assert_equal "utf-8", @response.charset, @response.headers.inspect
end
- def test_nil_default_for_rhtml
+ def test_nil_default_for_erb
OldContentTypeController.default_charset = nil
- get :render_default_for_rhtml
+ get :render_default_for_erb
assert_equal Mime::HTML, @response.content_type
assert_nil @response.charset, @response.headers.inspect
ensure
OldContentTypeController.default_charset = "utf-8"
end
- def test_default_for_rhtml
- get :render_default_for_rhtml
+ def test_default_for_erb
+ get :render_default_for_erb
assert_equal Mime::HTML, @response.content_type
assert_equal "utf-8", @response.charset
end
- def test_default_for_rxml
- get :render_default_for_rxml
+ def test_default_for_builder
+ get :render_default_for_builder
assert_equal Mime::XML, @response.content_type
assert_equal "utf-8", @response.charset
end
@@ -135,8 +135,8 @@ class ContentTypeTest < ActionController::TestCase
assert_equal "utf-8", @response.charset
end
- def test_change_for_rxml
- get :render_change_for_rxml
+ def test_change_for_builder
+ get :render_change_for_builder
assert_equal Mime::HTML, @response.content_type
assert_equal "utf-8", @response.charset
end
diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb
index d13ebc705a..3a8a37d967 100644
--- a/actionpack/test/controller/filters_test.rb
+++ b/actionpack/test/controller/filters_test.rb
@@ -314,6 +314,7 @@ class FilterTest < ActionController::TestCase
def initialize
@@execution_log = ""
+ super()
end
before_filter { |c| c.class.execution_log << " before procfilter " }
@@ -757,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/layout_test.rb b/actionpack/test/controller/layout_test.rb
index 32a0f40614..cafe2b9320 100644
--- a/actionpack/test/controller/layout_test.rb
+++ b/actionpack/test/controller/layout_test.rb
@@ -46,13 +46,13 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase
def test_application_layout_is_default_when_no_controller_match
@controller = ProductController.new
get :hello
- assert_equal 'layout_test.rhtml hello.rhtml', @response.body
+ assert_equal 'layout_test.erb hello.erb', @response.body
end
def test_controller_name_layout_name_match
@controller = ItemController.new
get :hello
- assert_equal 'item.rhtml hello.rhtml', @response.body
+ assert_equal 'item.erb hello.erb', @response.body
end
def test_third_party_template_library_auto_discovers_layout
@@ -65,13 +65,13 @@ class LayoutAutoDiscoveryTest < ActionController::TestCase
def test_namespaced_controllers_auto_detect_layouts1
@controller = ControllerNameSpace::NestedController.new
get :hello
- assert_equal 'controller_name_space/nested.rhtml hello.rhtml', @response.body
+ assert_equal 'controller_name_space/nested.erb hello.erb', @response.body
end
def test_namespaced_controllers_auto_detect_layouts2
@controller = MultipleExtensions.new
get :hello
- assert_equal 'multiple_extensions.html.erb hello.rhtml', @response.body.strip
+ assert_equal 'multiple_extensions.html.erb hello.erb', @response.body.strip
end
end
@@ -79,7 +79,7 @@ class DefaultLayoutController < LayoutTest
end
class AbsolutePathLayoutController < LayoutTest
- layout File.expand_path(File.expand_path(__FILE__) + '/../../fixtures/layout_tests/layouts/layout_test.rhtml')
+ layout File.expand_path(File.expand_path(__FILE__) + '/../../fixtures/layout_tests/layouts/layout_test.erb')
end
class HasOwnLayoutController < LayoutTest
@@ -137,7 +137,7 @@ class LayoutSetInResponseTest < ActionController::TestCase
def test_layout_only_exception_when_excepted
@controller = OnlyLayoutController.new
get :goodbye
- assert !@response.body.include?("item.rhtml"), "#{@response.body.inspect} included 'item.rhtml'"
+ assert !@response.body.include?("item.erb"), "#{@response.body.inspect} included 'item.erb'"
end
def test_layout_except_exception_when_included
@@ -149,7 +149,7 @@ class LayoutSetInResponseTest < ActionController::TestCase
def test_layout_except_exception_when_excepted
@controller = ExceptLayoutController.new
get :goodbye
- assert !@response.body.include?("item.rhtml"), "#{@response.body.inspect} included 'item.rhtml'"
+ assert !@response.body.include?("item.erb"), "#{@response.body.inspect} included 'item.erb'"
end
def test_layout_set_when_using_render
@@ -173,7 +173,7 @@ class LayoutSetInResponseTest < ActionController::TestCase
def test_absolute_pathed_layout
@controller = AbsolutePathLayoutController.new
get :hello
- assert_equal "layout_test.rhtml hello.rhtml", @response.body.strip
+ assert_equal "layout_test.erb hello.erb", @response.body.strip
end
end
@@ -184,7 +184,7 @@ class RenderWithTemplateOptionController < LayoutTest
end
class SetsNonExistentLayoutFile < LayoutTest
- layout "nofile.rhtml"
+ layout "nofile.erb"
end
class LayoutExceptionRaised < ActionController::TestCase
diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb
index b5bc0e9e9a..90c944d890 100644
--- a/actionpack/test/controller/log_subscriber_test.rb
+++ b/actionpack/test/controller/log_subscriber_test.rb
@@ -23,7 +23,7 @@ module Another
def with_fragment_cache
render :inline => "<%= cache('foo'){ 'bar' } %>"
end
-
+
def with_fragment_cache_and_percent_in_key
render :inline => "<%= cache('foo%bar'){ 'Contains % sign in key' } %>"
end
@@ -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/bare_metal_test.rb b/actionpack/test/controller/new_base/bare_metal_test.rb
index 8a06e8d2a0..44922cecff 100644
--- a/actionpack/test/controller/new_base/bare_metal_test.rb
+++ b/actionpack/test/controller/new_base/bare_metal_test.rb
@@ -24,4 +24,19 @@ module BareMetalTest
assert_equal "Hello world", string
end
end
+
+ class HeadController < ActionController::Metal
+ include ActionController::Head
+
+ def index
+ head :not_found
+ end
+ end
+
+ class HeadTest < ActiveSupport::TestCase
+ test "head works on its own" do
+ status, headers, body = HeadController.action(:index).call(Rack::MockRequest.env_for("/"))
+ assert_equal 404, status
+ end
+ end
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/new_base/render_once_test.rb b/actionpack/test/controller/new_base/render_once_test.rb
new file mode 100644
index 0000000000..63de25be52
--- /dev/null
+++ b/actionpack/test/controller/new_base/render_once_test.rb
@@ -0,0 +1,72 @@
+require 'abstract_unit'
+
+module RenderTemplate
+ class RenderOnceController < ActionController::Base
+ layout false
+
+ RESOLVER = ActionView::FixtureResolver.new(
+ "test/a.html.erb" => "a",
+ "test/b.html.erb" => "<>",
+ "test/c.html.erb" => "c",
+ "test/one.html.erb" => "<%= render :once => 'test/result' %>",
+ "test/two.html.erb" => "<%= render :once => 'test/result' %>",
+ "test/three.html.erb" => "<%= render :once => 'test/result' %>",
+ "test/result.html.erb" => "YES!",
+ "layouts/test.html.erb" => "l<%= yield %>l"
+ )
+
+ self.view_paths = [RESOLVER]
+
+ def multiple
+ render :once => %w(test/a test/b test/c)
+ end
+
+ def once
+ render :once => %w(test/one test/two test/three)
+ end
+
+ def duplicate
+ render :once => %w(test/a test/a test/a)
+ end
+
+ def with_layout
+ render :once => %w(test/a test/b test/c), :layout => "test"
+ end
+ end
+
+ module Tests
+ def test_mutliple_arguments_get_all_rendered
+ get :multiple
+ assert_response "a\n<>\nc"
+ end
+
+ def test_referenced_templates_get_rendered_once
+ get :once
+ assert_response "YES!\n\n"
+ end
+
+ def test_duplicated_templates_get_rendered_once
+ get :duplicate
+ assert_response "a"
+ end
+
+ def test_layout_wraps_all_rendered_templates
+ get :with_layout
+ assert_response "la\n<>\ncl"
+ end
+ end
+
+ class TestWithResolverCache < Rack::TestCase
+ testing RenderTemplate::RenderOnceController
+ include Tests
+ end
+
+ class TestWithoutResolverCache < Rack::TestCase
+ testing RenderTemplate::RenderOnceController
+ include Tests
+
+ def setup
+ RenderTemplate::RenderOnceController::RESOLVER.stubs(:caching?).returns(false)
+ end
+ end
+end
diff --git a/actionpack/test/controller/new_base/render_partial_test.rb b/actionpack/test/controller/new_base/render_partial_test.rb
index 5c7e66dba2..d800ea264d 100644
--- a/actionpack/test/controller/new_base/render_partial_test.rb
+++ b/actionpack/test/controller/new_base/render_partial_test.rb
@@ -5,10 +5,17 @@ module RenderPartial
class BasicController < ActionController::Base
self.view_paths = [ActionView::FixtureResolver.new(
- "render_partial/basic/_basic.html.erb" => "BasicPartial!",
- "render_partial/basic/basic.html.erb" => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'basic' %><%= @test_unchanged %>"
+ "render_partial/basic/_basic.html.erb" => "BasicPartial!",
+ "render_partial/basic/basic.html.erb" => "<%= @test_unchanged = 'goodbye' %><%= render :partial => 'basic' %><%= @test_unchanged %>",
+ "render_partial/basic/with_json.html.erb" => "<%= render 'with_json.json' %>",
+ "render_partial/basic/_with_json.json.erb" => "<%= render 'final' %>",
+ "render_partial/basic/_final.json.erb" => "{ final: json }"
)]
+ def html_with_json_inside_json
+ render :action => "with_json"
+ end
+
def changing
@test_unchanged = 'hello'
render :action => "basic"
@@ -22,6 +29,12 @@ module RenderPartial
get :changing
assert_response("goodbyeBasicPartial!goodbye")
end
+
+ test "rendering a template with renders another partial with other format that renders other partial in the same format" do
+ get :html_with_json_inside_json
+ assert_content_type "text/html; charset=utf-8"
+ assert_response "{ final: json }"
+ end
end
end
diff --git a/actionpack/test/controller/new_base/render_template_test.rb b/actionpack/test/controller/new_base/render_template_test.rb
index fea98d6bbd..d31193a063 100644
--- a/actionpack/test/controller/new_base/render_template_test.rb
+++ b/actionpack/test/controller/new_base/render_template_test.rb
@@ -8,13 +8,21 @@ module RenderTemplate
"shared.html.erb" => "Elastica",
"locals.html.erb" => "The secret is <%= secret %>",
"xml_template.xml.builder" => "xml.html do\n xml.p 'Hello'\nend",
- "with_raw.html.erb" => "Hello <%=raw '<strong>this is raw</strong>' %>"
+ "with_raw.html.erb" => "Hello <%=raw '<strong>this is raw</strong>' %>",
+ "test/with_json.html.erb" => "<%= render :template => 'test/with_json.json' %>",
+ "test/with_json.json.erb" => "<%= render :template => 'test/final' %>",
+ "test/final.json.erb" => "{ final: json }",
+ "test/with_error.html.erb" => "<%= idontexist %>"
)]
def index
render :template => "test/basic"
end
+ def html_with_json_inside_json
+ render :template => "test/with_json"
+ end
+
def index_without_key
render "test/basic"
end
@@ -42,6 +50,10 @@ module RenderTemplate
def with_raw
render :template => "with_raw"
end
+
+ def with_error
+ render :template => "test/with_error"
+ end
end
class TestWithoutLayout < Rack::TestCase
@@ -88,6 +100,18 @@ module RenderTemplate
assert_body "Hello <strong>this is raw</strong>"
assert_status 200
end
+
+ test "rendering a template with renders another template with other format that renders other template in the same format" do
+ get :html_with_json_inside_json
+ assert_content_type "text/html; charset=utf-8"
+ assert_response "{ final: json }"
+ end
+
+ test "rendering a template with error properly exceprts the code" do
+ get :with_error
+ assert_status 500
+ assert_match "undefined local variable or method `idontexist'", response.body
+ end
end
class WithLayoutController < ::ApplicationController
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/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb
index 26d76557dd..18f28deee4 100644
--- a/actionpack/test/dispatch/prefix_generation_test.rb
+++ b/actionpack/test/dispatch/prefix_generation_test.rb
@@ -1,8 +1,23 @@
require 'abstract_unit'
+require 'rack/test'
module TestGenerationPrefix
+ class Post
+ extend ActiveModel::Naming
+
+ def to_param
+ "1"
+ end
+
+ def self.model_name
+ klass = "Post"
+ def klass.name; self end
+
+ ActiveModel::Name.new(klass)
+ end
+ end
+
class WithMountedEngine < ActionDispatch::IntegrationTest
- require 'rack/test'
include Rack::Test::Methods
class BlogEngine
@@ -55,21 +70,6 @@ module TestGenerationPrefix
# force draw
RailsApplication.routes
- class Post
- extend ActiveModel::Naming
-
- def to_param
- "1"
- end
-
- def self.model_name
- klass = "Post"
- def klass.name; self end
-
- ActiveModel::Name.new(klass)
- end
- end
-
class ::InsideEngineGeneratingController < ActionController::Base
include BlogEngine.routes.url_helpers
include RailsApplication.routes.mounted_helpers
@@ -253,4 +253,65 @@ module TestGenerationPrefix
assert_equal "http://www.example.com/awesome/blog/posts/1", path
end
end
+
+ class EngineMountedAtRoot < ActionDispatch::IntegrationTest
+ include Rack::Test::Methods
+
+ class BlogEngine
+ def self.routes
+ @routes ||= begin
+ routes = ActionDispatch::Routing::RouteSet.new
+ routes.draw do
+ match "/posts/:id", :to => "posts#show", :as => :post
+ end
+
+ routes
+ end
+ end
+
+ def self.call(env)
+ env['action_dispatch.routes'] = routes
+ routes.call(env)
+ end
+ end
+
+ class RailsApplication
+ def self.routes
+ @routes ||= begin
+ routes = ActionDispatch::Routing::RouteSet.new
+ routes.draw do
+ mount BlogEngine => "/"
+ end
+
+ routes
+ end
+ end
+
+ def self.call(env)
+ env['action_dispatch.routes'] = routes
+ routes.call(env)
+ end
+ end
+
+ # force draw
+ RailsApplication.routes
+
+ class ::PostsController < ActionController::Base
+ include BlogEngine.routes.url_helpers
+ include RailsApplication.routes.mounted_helpers
+
+ def show
+ render :text => post_path(:id => params[:id])
+ end
+ end
+
+ def app
+ RailsApplication
+ end
+
+ test "generating path inside engine" do
+ get "/posts/1"
+ assert_equal "/posts/1", last_response.body
+ end
+ end
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/fixtures/layout_tests/alt/hello.erb b/actionpack/test/fixtures/layout_tests/alt/hello.erb
new file mode 100644
index 0000000000..1055c36659
--- /dev/null
+++ b/actionpack/test/fixtures/layout_tests/alt/hello.erb
@@ -0,0 +1 @@
+alt/hello.erb
diff --git a/actionpack/test/fixtures/layout_tests/alt/hello.rhtml b/actionpack/test/fixtures/layout_tests/alt/hello.rhtml
deleted file mode 100644
index fcda6cf97a..0000000000
--- a/actionpack/test/fixtures/layout_tests/alt/hello.rhtml
+++ /dev/null
@@ -1 +0,0 @@
-alt/hello.rhtml
diff --git a/actionpack/test/fixtures/layout_tests/alt/layouts/alt.rhtml b/actionpack/test/fixtures/layout_tests/alt/layouts/alt.erb
index e69de29bb2..e69de29bb2 100644
--- a/actionpack/test/fixtures/layout_tests/alt/layouts/alt.rhtml
+++ b/actionpack/test/fixtures/layout_tests/alt/layouts/alt.erb
diff --git a/actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.erb b/actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.erb
new file mode 100644
index 0000000000..121bc079a1
--- /dev/null
+++ b/actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.erb
@@ -0,0 +1 @@
+controller_name_space/nested.erb <%= yield %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml b/actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml
deleted file mode 100644
index 5f86a7de4d..0000000000
--- a/actionpack/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml
+++ /dev/null
@@ -1 +0,0 @@
-controller_name_space/nested.rhtml <%= yield %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/layouts/item.erb b/actionpack/test/fixtures/layout_tests/layouts/item.erb
new file mode 100644
index 0000000000..60f04d77d5
--- /dev/null
+++ b/actionpack/test/fixtures/layout_tests/layouts/item.erb
@@ -0,0 +1 @@
+item.erb <%= yield %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/layouts/item.rhtml b/actionpack/test/fixtures/layout_tests/layouts/item.rhtml
deleted file mode 100644
index 1bc7cbda06..0000000000
--- a/actionpack/test/fixtures/layout_tests/layouts/item.rhtml
+++ /dev/null
@@ -1 +0,0 @@
-item.rhtml <%= yield %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/layouts/layout_test.erb b/actionpack/test/fixtures/layout_tests/layouts/layout_test.erb
new file mode 100644
index 0000000000..b74ac0840d
--- /dev/null
+++ b/actionpack/test/fixtures/layout_tests/layouts/layout_test.erb
@@ -0,0 +1 @@
+layout_test.erb <%= yield %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/layouts/layout_test.rhtml b/actionpack/test/fixtures/layout_tests/layouts/layout_test.rhtml
deleted file mode 100644
index c0f2642b40..0000000000
--- a/actionpack/test/fixtures/layout_tests/layouts/layout_test.rhtml
+++ /dev/null
@@ -1 +0,0 @@
-layout_test.rhtml <%= yield %> \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/views/goodbye.erb b/actionpack/test/fixtures/layout_tests/views/goodbye.erb
new file mode 100644
index 0000000000..4ee911188e
--- /dev/null
+++ b/actionpack/test/fixtures/layout_tests/views/goodbye.erb
@@ -0,0 +1 @@
+hello.erb \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/views/goodbye.rhtml b/actionpack/test/fixtures/layout_tests/views/goodbye.rhtml
deleted file mode 100644
index bbccf0913e..0000000000
--- a/actionpack/test/fixtures/layout_tests/views/goodbye.rhtml
+++ /dev/null
@@ -1 +0,0 @@
-hello.rhtml \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/views/hello.erb b/actionpack/test/fixtures/layout_tests/views/hello.erb
new file mode 100644
index 0000000000..4ee911188e
--- /dev/null
+++ b/actionpack/test/fixtures/layout_tests/views/hello.erb
@@ -0,0 +1 @@
+hello.erb \ No newline at end of file
diff --git a/actionpack/test/fixtures/layout_tests/views/hello.rhtml b/actionpack/test/fixtures/layout_tests/views/hello.rhtml
deleted file mode 100644
index bbccf0913e..0000000000
--- a/actionpack/test/fixtures/layout_tests/views/hello.rhtml
+++ /dev/null
@@ -1 +0,0 @@
-hello.rhtml \ No newline at end of file
diff --git a/actionpack/test/fixtures/old_content_type/render_default_for_rxml.rxml b/actionpack/test/fixtures/old_content_type/render_default_for_builder.builder
index 598d62e2fc..598d62e2fc 100644
--- a/actionpack/test/fixtures/old_content_type/render_default_for_rxml.rxml
+++ b/actionpack/test/fixtures/old_content_type/render_default_for_builder.builder
diff --git a/actionpack/test/fixtures/old_content_type/render_default_for_rhtml.rhtml b/actionpack/test/fixtures/old_content_type/render_default_for_erb.erb
index c7926d48bb..c7926d48bb 100644
--- a/actionpack/test/fixtures/old_content_type/render_default_for_rhtml.rhtml
+++ b/actionpack/test/fixtures/old_content_type/render_default_for_erb.erb
diff --git a/actionpack/test/fixtures/test/_object_inspector.erb b/actionpack/test/fixtures/test/_object_inspector.erb
new file mode 100644
index 0000000000..53af593821
--- /dev/null
+++ b/actionpack/test/fixtures/test/_object_inspector.erb
@@ -0,0 +1 @@
+<%= object_inspector.inspect -%> \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/proper_block_detection.erb b/actionpack/test/fixtures/test/proper_block_detection.erb
index 23564dbcee..b55efbb25d 100644
--- a/actionpack/test/fixtures/test/proper_block_detection.erb
+++ b/actionpack/test/fixtures/test/proper_block_detection.erb
@@ -1 +1 @@
-<%= @todo %>
+<%= @todo %> \ No newline at end of file
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..0bfdbeebd1 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -4,18 +4,6 @@ require 'controller/fake_models'
class FormHelperTest < ActionView::TestCase
tests ActionView::Helpers::FormHelper
- class Developer
- def name_before_type_cast
- "David"
- end
-
- def name
- "Santiago"
- end
-
- attr_writer :language
- end
-
def form_for(*)
@output_buffer = super
end
@@ -278,24 +266,6 @@ class FormHelperTest < ActionView::TestCase
text_field("user", "email", :type => "email")
end
- def test_text_field_from_a_user_defined_method
- @developer = Developer.new
- assert_dom_equal(
- '<input id="developer_name" name="developer[name]" size="30" type="text" value="Santiago" />', text_field("developer", "name")
- )
- end
-
- def test_text_field_on_a_model_with_undefined_attr_reader
- @developer = Developer.new
- @developer.language = 'ruby'
- begin
- text_field("developer", "language")
- rescue NoMethodError => error
- message = error.message
- end
- assert_equal "Model #{Developer} does not respond to language", message
- end
-
def test_check_box
assert_dom_equal(
'<input name="post[secret]" type="hidden" value="0" /><input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" />',
@@ -761,6 +731,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 +1721,5 @@ class FormHelperTest < ActionView::TestCase
def protect_against_forgery?
false
end
+
end
diff --git a/actionpack/test/template/lookup_context_test.rb b/actionpack/test/template/lookup_context_test.rb
index cc71cb42d0..850589b13b 100644
--- a/actionpack/test/template/lookup_context_test.rb
+++ b/actionpack/test/template/lookup_context_test.rb
@@ -100,8 +100,8 @@ class LookupContextTest < ActiveSupport::TestCase
@lookup_context.with_fallbacks do
assert_equal 3, @lookup_context.view_paths.size
- assert @lookup_context.view_paths.include?(ActionView::FileSystemResolver.new(""))
- assert @lookup_context.view_paths.include?(ActionView::FileSystemResolver.new("/"))
+ assert @lookup_context.view_paths.include?(ActionView::FallbackFileSystemResolver.new(""))
+ assert @lookup_context.view_paths.include?(ActionView::FallbackFileSystemResolver.new("/"))
end
end
@@ -163,4 +163,70 @@ class LookupContextTest < ActiveSupport::TestCase
template = @lookup_context.find("foo", "test", true)
assert_equal "Bar", template.source
end
+
+ test "can disable the cache on demand" do
+ @lookup_context.view_paths = ActionView::FixtureResolver.new("test/_foo.erb" => "Foo")
+ old_template = @lookup_context.find("foo", "test", true)
+
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal template, old_template
+
+ assert @lookup_context.cache
+ template = @lookup_context.disable_cache do
+ assert !@lookup_context.cache
+ @lookup_context.find("foo", "test", true)
+ end
+ assert @lookup_context.cache
+
+ assert_not_equal template, old_template
+ end
+
+ test "can have cache disabled on initialization" do
+ assert !ActionView::LookupContext.new(FIXTURE_LOAD_PATH, :cache => false).cache
+ end
+end
+
+class LookupContextWithFalseCaching < ActiveSupport::TestCase
+ def setup
+ @resolver = ActionView::FixtureResolver.new("test/_foo.erb" => ["Foo", Time.utc(2000)])
+ @resolver.stubs(:caching?).returns(false)
+ @lookup_context = ActionView::LookupContext.new(@resolver, {})
+ end
+
+ test "templates are always found in the resolver but timestamp is checked before being compiled" do
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal "Foo", template.source
+
+ # Now we are going to change the template, but it won't change the returned template
+ # since the timestamp is the same.
+ @resolver.hash["test/_foo.erb"][0] = "Bar"
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal "Foo", template.source
+
+ # Now update the timestamp.
+ @resolver.hash["test/_foo.erb"][1] = Time.now.utc
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal "Bar", template.source
+ end
+
+ test "if no template was found in the second lookup, give it higher preference" do
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal "Foo", template.source
+
+ @resolver.hash.clear
+ assert_raise ActionView::MissingTemplate do
+ @lookup_context.find("foo", "test", true)
+ end
+ end
+
+ test "if no template was cached in the first lookup, do not use the cache in the second" do
+ @resolver.hash.clear
+ assert_raise ActionView::MissingTemplate do
+ @lookup_context.find("foo", "test", true)
+ end
+
+ @resolver.hash["test/_foo.erb"] = ["Foo", Time.utc(2000)]
+ template = @lookup_context.find("foo", "test", true)
+ assert_equal "Foo", template.source
+ end
end \ No newline at end of file
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index 205fdcf345..17bb610b6a 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -114,7 +114,7 @@ module RenderTestCases
end
def test_render_sub_template_with_errors
- @view.render(:file => "test/sub_template_raise")
+ @view.render(:template => "test/sub_template_raise")
flunk "Render did not raise Template::Error"
rescue ActionView::Template::Error => e
assert_match %r!method.*doesnt_exist!, e.message
@@ -123,10 +123,25 @@ module RenderTestCases
assert_equal File.expand_path("#{FIXTURE_LOAD_PATH}/test/_raise.html.erb"), e.file_name
end
+ def test_render_file_with_errors
+ @view.render(:file => File.expand_path("test/_raise", FIXTURE_LOAD_PATH))
+ flunk "Render did not raise Template::Error"
+ rescue ActionView::Template::Error => e
+ assert_match %r!method.*doesnt_exist!, e.message
+ assert_equal "", e.sub_template_message
+ assert_equal "1", e.line_number
+ assert_equal "1: <%= doesnt_exist %>", e.annoted_source_code.strip
+ assert_equal File.expand_path("#{FIXTURE_LOAD_PATH}/test/_raise.html.erb"), e.file_name
+ end
+
def test_render_object
assert_equal "Hello: david", @view.render(:partial => "test/customer", :object => Customer.new("david"))
end
+ def test_render_object_with_array
+ assert_equal "[1, 2, 3]", @view.render(:partial => "test/object_inspector", :object => [1, 2, 3])
+ end
+
def test_render_partial_collection
assert_equal "Hello: davidHello: mary", @view.render(:partial => "test/customer", :collection => [ Customer.new("david"), Customer.new("mary") ])
end
diff --git a/actionpack/test/template/template_test.rb b/actionpack/test/template/template_test.rb
index c7c33af670..f2156c31de 100644
--- a/actionpack/test/template/template_test.rb
+++ b/actionpack/test/template/template_test.rb
@@ -1,9 +1,18 @@
require "abstract_unit"
+require "logger"
class TestERBTemplate < ActiveSupport::TestCase
- ERBHandler = ActionView::Template::Handlers::ERB
+ ERBHandler = ActionView::Template::Handlers::ERB.new
+
+ class LookupContext
+ def disable_cache
+ yield
+ end
+ end
class Context
+ attr_accessor :_template
+
def initialize
@output_buffer = "original"
@_virtual_path = nil
@@ -15,15 +24,18 @@ class TestERBTemplate < ActiveSupport::TestCase
def partial
ActionView::Template.new(
- "<%= @_virtual_path %>",
+ "<%= @_template.virtual_path %>",
"partial",
ERBHandler,
:virtual_path => "partial"
)
end
+ def lookup_context
+ @lookup_context ||= LookupContext.new
+ end
+
def logger
- require "logger"
Logger.new(STDERR)
end
@@ -32,16 +44,16 @@ class TestERBTemplate < ActiveSupport::TestCase
end
end
- def new_template(body = "<%= hello %>", handler = ERBHandler, details = {})
- ActionView::Template.new(body, "hello template", ERBHandler, {:virtual_path => "hello"})
+ def new_template(body = "<%= hello %>", details = {})
+ ActionView::Template.new(body, "hello template", ERBHandler, {:virtual_path => "hello"}.merge!(details))
end
def render(locals = {})
- @template.render(@obj, locals)
+ @template.render(@context, locals)
end
def setup
- @obj = Context.new
+ @context = Context.new
end
def test_basic_template
@@ -49,24 +61,104 @@ class TestERBTemplate < ActiveSupport::TestCase
assert_equal "Hello", render
end
+ def test_template_loses_its_source_after_rendering
+ @template = new_template
+ render
+ assert_nil @template.source
+ end
+
+ def test_template_does_not_lose_its_source_after_rendering_if_it_does_not_have_a_virtual_path
+ @template = new_template("Hello", :virtual_path => nil)
+ render
+ assert_equal "Hello", @template.source
+ end
+
def test_locals
@template = new_template("<%= my_local %>")
+ @template.locals = [:my_local]
assert_equal "I'm a local", render(:my_local => "I'm a local")
end
def test_restores_buffer
@template = new_template
assert_equal "Hello", render
- assert_equal "original", @obj.my_buffer
+ assert_equal "original", @context.my_buffer
end
def test_virtual_path
- @template = new_template("<%= @_virtual_path %>" \
+ @template = new_template("<%= @_template.virtual_path %>" \
"<%= partial.render(self, {}) %>" \
- "<%= @_virtual_path %>")
+ "<%= @_template.virtual_path %>")
assert_equal "hellopartialhello", render
end
+ def test_refresh_with_templates
+ @template = new_template("Hello", :virtual_path => "test/foo")
+ @template.locals = [:key]
+ @context.lookup_context.expects(:find_template).with("foo", "test", false, [:key]).returns("template")
+ assert_equal "template", @template.refresh(@context)
+ end
+
+ def test_refresh_with_partials
+ @template = new_template("Hello", :virtual_path => "test/_foo")
+ @template.locals = [:key]
+ @context.lookup_context.expects(:find_template).with("foo", "test", true, [:key]).returns("partial")
+ assert_equal "partial", @template.refresh(@context)
+ end
+
+ def test_refresh_raises_an_error_without_virtual_path
+ @template = new_template("Hello", :virtual_path => nil)
+ assert_raise RuntimeError do
+ @template.refresh(@context)
+ end
+ end
+
+ def test_template_expire_sets_the_timestamp_to_1970
+ @template = new_template("Hello", :updated_at => Time.utc(2010))
+ assert_equal Time.utc(2010), @template.updated_at
+ @template.expire!
+ assert_equal Time.utc(1970), @template.updated_at
+ end
+
+ def test_template_rerender_renders_a_template_like_self
+ @template = new_template("Hello", :virtual_path => "test/foo_bar")
+ @context.expects(:render).with(:template => "test/foo_bar").returns("template")
+ assert_equal "template", @template.rerender(@context)
+ end
+
+ def test_template_rerender_renders_a_root_template_like_self
+ @template = new_template("Hello", :virtual_path => "foo_bar")
+ @context.expects(:render).with(:template => "foo_bar").returns("template")
+ assert_equal "template", @template.rerender(@context)
+ end
+
+ def test_template_rerender_renders_a_partial_like_self
+ @template = new_template("Hello", :virtual_path => "test/_foo_bar")
+ @context.expects(:render).with(:partial => "test/foo_bar").returns("partial")
+ assert_equal "partial", @template.rerender(@context)
+ end
+
+ def test_template_rerender_renders_a_root_partial_like_self
+ @template = new_template("Hello", :virtual_path => "_foo_bar")
+ @context.expects(:render).with(:partial => "foo_bar").returns("partial")
+ assert_equal "partial", @template.rerender(@context)
+ end
+
+ def test_rerender_raises_an_error_without_virtual_path
+ @template = new_template("Hello", :virtual_path => nil)
+ assert_raise RuntimeError do
+ @template.rerender(@context)
+ end
+ end
+
+ def test_inline_template_is_only_equal_if_source_match
+ inline1 = ActionView::Template::Inline.new("sample", ERBHandler)
+ inline2 = ActionView::Template::Inline.new("sample", ERBHandler)
+ inline3 = ActionView::Template::Inline.new("other", ERBHandler)
+ assert inline1.eql?(inline2)
+ assert !inline1.eql?(inline3)
+ end
+
if "ruby".encoding_aware?
def test_resulting_string_is_utf8
@template = new_template
@@ -101,14 +193,14 @@ class TestERBTemplate < ActiveSupport::TestCase
# inside Rails.
def test_lying_with_magic_comment
assert_raises(ActionView::Template::Error) do
- @template = new_template("# encoding: UTF-8\nhello \xFCmlat")
+ @template = new_template("# encoding: UTF-8\nhello \xFCmlat", :virtual_path => nil)
render
end
end
def test_encoding_can_be_specified_with_magic_comment_in_erb
with_external_encoding Encoding::UTF_8 do
- @template = new_template("<%# encoding: ISO-8859-1 %>hello \xFCmlat")
+ @template = new_template("<%# encoding: ISO-8859-1 %>hello \xFCmlat", :virtual_path => nil)
result = render
assert_equal Encoding::UTF_8, render.encoding
assert_equal "hello \u{fc}mlat", render
@@ -117,7 +209,7 @@ class TestERBTemplate < ActiveSupport::TestCase
def test_error_when_template_isnt_valid_utf8
assert_raises(ActionView::Template::Error, /\xFC/) do
- @template = new_template("hello \xFCmlat")
+ @template = new_template("hello \xFCmlat", :virtual_path => nil)
render
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/translation_helper_test.rb b/actionpack/test/template/translation_helper_test.rb
index 952719a589..763080550b 100644
--- a/actionpack/test/template/translation_helper_test.rb
+++ b/actionpack/test/template/translation_helper_test.rb
@@ -31,13 +31,13 @@ class TranslationHelperTest < ActiveSupport::TestCase
def test_scoping_by_partial
I18n.expects(:translate).with("test.translation.helper", :raise => true).returns("helper")
- @view = ActionView::Base.new(ActionController::Base.view_paths, {})
+ @view = ::ActionView::Base.new(ActionController::Base.view_paths, {})
assert_equal "helper", @view.render(:file => "test/translation")
end
def test_scoping_by_partial_of_an_array
I18n.expects(:translate).with("test.scoped_translation.foo.bar", :raise => true).returns(["foo", "bar"])
- @view = ActionView::Base.new(ActionController::Base.view_paths, {})
+ @view = ::ActionView::Base.new(ActionController::Base.view_paths, {})
assert_equal "foobar", @view.render(:file => "test/scoped_translation")
end
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index db8fd82aeb..b8a7d4259d 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -9,7 +9,7 @@ class UrlHelperTest < ActiveSupport::TestCase
# or request.
#
# In those cases, we'll set up a simple mock
- attr_accessor :controller, :request
+ attr_accessor :controller, :request, :_template
routes = ActionDispatch::Routing::RouteSet.new
routes.draw do
@@ -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.rb b/activemodel/lib/active_model.rb
index 9b8f843432..be0f24ff92 100644
--- a/activemodel/lib/active_model.rb
+++ b/activemodel/lib/active_model.rb
@@ -24,7 +24,7 @@
activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
require 'active_support'
-
+require 'active_model/version'
module ActiveModel
extend ActiveSupport::Autoload
@@ -45,7 +45,6 @@ module ActiveModel
autoload :Serialization
autoload :TestCase
autoload :Translation
- autoload :VERSION
autoload :Validations
autoload :Validator
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/RUNNING_UNIT_TESTS b/activerecord/RUNNING_UNIT_TESTS
index 324df2c025..18e3936d8a 100644
--- a/activerecord/RUNNING_UNIT_TESTS
+++ b/activerecord/RUNNING_UNIT_TESTS
@@ -31,8 +31,13 @@ That'll run the base suite using the MySQL-Ruby adapter. Some tests rely on the
being initialized - you can initialize the schema with:
rake test_mysql TEST=test/cases/aaa_create_tables_test.rb
+ rake mysql:build_databases
+
+To setup the testing environment for PostgreSQL use this command:
+
+ rake postgresql:build_databases
The incantation for running a particular test looks like this
- ruby -w -I"lib:test:test/connections/native_postgresql" test/cases/datatype_test_postgresql.rb -n test_timestamp_with_zone_values_without_rails_time_zone_support
+ rake test TEST=test/cases/datatype_test_postgresql.rb TESTOPTS="-n test_timestamp_with_zone_values_without_rails_time_zone_support"
diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb
index ccd60c6c69..c4ce361b5d 100644
--- a/activerecord/examples/performance.rb
+++ b/activerecord/examples/performance.rb
@@ -88,7 +88,7 @@ else
)
end
- mysqldump_bin = %w[mysqldump mysqldump5].select { |bin| `which #{bin}`.length > 0 }
+ mysqldump_bin = %w[mysqldump mysqldump5].detect { |bin| `which #{bin}`.length > 0 }
`#{mysqldump_bin} -u #{conn[:username]} #{"-p#{conn[:password]}" unless conn[:password].blank?} #{conn[:database]} exhibits users > #{sqlfile}`
end
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index e2f2508ae8..c80bce2849 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -33,12 +33,12 @@ require 'active_support/i18n'
require 'active_model'
require 'arel'
+require 'active_record/version'
+
module ActiveRecord
extend ActiveSupport::Autoload
eager_autoload do
- autoload :VERSION
-
autoload :ActiveRecordError, 'active_record/errors'
autoload :ConnectionNotEstablished, 'active_record/errors'
@@ -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/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
index 38454ec242..e429806b0c 100644
--- a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
+++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb
@@ -39,7 +39,7 @@ module ActiveRecord
def set_inverse_instance(record, instance)
return if record.nil? || !we_can_set_the_inverse_on_this?(record)
inverse_relationship = @reflection.polymorphic_inverse_of(record.class)
- unless inverse_relationship.nil?
+ if inverse_relationship
record.send(:"set_#{inverse_relationship.name}_target", instance)
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/callbacks.rb b/activerecord/lib/active_record/callbacks.rb
index 49671a1042..47428cfd0f 100644
--- a/activerecord/lib/active_record/callbacks.rb
+++ b/activerecord/lib/active_record/callbacks.rb
@@ -218,16 +218,6 @@ module ActiveRecord
# needs to be aware of it because an ordinary +save+ will raise such exception
# instead of quietly returning +false+.
#
- # == Debugging callbacks
- #
- # To list the methods and procs registered with a particular callback, append <tt>_callback_chain</tt> to
- # the callback name that you wish to list and send that to your class from the Rails console:
- #
- # >> Topic.after_save_callback_chain
- # => [#<ActiveSupport::Callbacks::Callback:0x3f6a448
- # @method=#<Proc:0x03f9a42c@/Users/foo/bar/app/models/topic.rb:43>, kind:after_save, identifiernil,
- # options{}]
- #
module Callbacks
extend ActiveSupport::Concern
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
index 37e584a629..ca9314ec99 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb
@@ -73,12 +73,7 @@ module ActiveRecord
# The mutex used to synchronize pool access
@connection_mutex = Monitor.new
@queue = @connection_mutex.new_cond
-
- # default 5 second timeout unless on ruby 1.9
- @timeout =
- if RUBY_VERSION < '1.9'
- spec.config[:wait_timeout] || 5
- end
+ @timeout = spec.config[:wait_timeout] || 5
# default max pool size to 5
@size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
@@ -161,7 +156,6 @@ module ActiveRecord
keys = @reserved_connections.keys - Thread.list.find_all { |t|
t.alive?
}.map { |thread| thread.object_id }
-
keys.each do |key|
checkin @reserved_connections[key]
@reserved_connections.delete(key)
@@ -194,16 +188,18 @@ module ActiveRecord
checkout_new_connection
end
return conn if conn
- # No connections available; wait for one
- if @queue.wait(@timeout)
+
+ @queue.wait(@timeout)
+
+ if(@checked_out.size < @connections.size)
next
else
- # try looting dead threads
clear_stale_cached_connections!
if @size == @checked_out.size
raise ConnectionTimeoutError, "could not obtain a database connection#{" within #{@timeout} seconds" if @timeout}. The max pool size is currently #{@size}; consider increasing it."
end
end
+
end
end
end
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..a4c09b654a 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -384,23 +384,32 @@ module ActiveRecord
end
end
- def copy(destination, sources)
+ def copy(destination, sources, options = {})
copied = []
- sources.each do |scope, path|
- destination_migrations = ActiveRecord::Migrator.migrations(destination)
+ destination_migrations = ActiveRecord::Migrator.migrations(destination)
+ last = destination_migrations.last
+ sources.each do |name, path|
source_migrations = ActiveRecord::Migrator.migrations(path)
- last = destination_migrations.last
source_migrations.each do |migration|
- next if destination_migrations.any? { |m| m.name == migration.name && m.scope == scope.to_s }
+ source = File.read(migration.filename)
+ source = "# This migration comes from #{name} (originally #{migration.version})\n#{source}"
+
+ if duplicate = destination_migrations.detect { |m| m.name == migration.name }
+ options[:on_skip].call(name, migration) if File.read(duplicate.filename) != source && options[:on_skip]
+ next
+ end
migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
+ new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.rb")
+ old_path, migration.filename = migration.filename, new_path
last = migration
- new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.#{scope}.rb")
- FileUtils.cp(migration.filename, new_path)
- copied << new_path
+ FileUtils.cp(old_path, migration.filename)
+ copied << migration
+ options[:on_copy].call(name, migration, old_path) if options[:on_copy]
+ destination_migrations << migration
end
end
@@ -419,9 +428,16 @@ 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)
+
+ def initialize(name, version, filename)
+ super
+ @migration = nil
+ end
- attr_accessor :name, :version, :filename, :scope
+ def basename
+ File.basename(filename)
+ end
delegate :migrate, :announce, :write, :to=>:migration
@@ -504,26 +520,21 @@ module ActiveRecord
def migrations(path)
files = Dir["#{path}/[0-9]*_*.rb"]
- migrations = files.inject([]) do |klasses, file|
- version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?.rb/).first
+ seen = Hash.new false
+
+ migrations = files.map do |file|
+ version, name = file.scan(/([0-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]
- if klasses.detect { |m| m.name == name.camelize && m.scope == scope }
- raise DuplicateMigrationNameError.new(name.camelize)
- end
+ seen[version] = seen[name] = true
- migration = MigrationProxy.new
- migration.name = name.camelize
- migration.version = version
- migration.filename = file
- migration.scope = scope
- klasses << migration
+ MigrationProxy.new(name, version, file)
end
migrations.sort_by(&:version)
@@ -570,7 +581,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 +590,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/railtie.rb b/activerecord/lib/active_record/railtie.rb
index 94dda4e413..868fd6c3ff 100644
--- a/activerecord/lib/active_record/railtie.rb
+++ b/activerecord/lib/active_record/railtie.rb
@@ -13,7 +13,7 @@ module ActiveRecord
class Railtie < Rails::Railtie
config.active_record = ActiveSupport::OrderedOptions.new
- config.generators.orm :active_record, :migration => true,
+ config.app_generators.orm :active_record, :migration => true,
:timestamps => true
config.app_middleware.insert_after "::ActionDispatch::Callbacks",
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 389a5d5884..5ad440e58d 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -2,28 +2,7 @@ namespace :db do
task :load_config => :rails_env do
require 'active_record'
ActiveRecord::Base.configurations = Rails.application.config.database_configuration
- ActiveRecord::Migrator.migrations_path = Rails.application.config.paths.db.migrate.to_a.first
- end
-
- task :copy_migrations => :load_config do
- to_load = ENV["FROM"].blank? ? :all : ENV["FROM"].split(",").map {|n| n.strip }
- railties = {}
- Rails.application.railties.all do |railtie|
- next unless to_load == :all || to_load.include?(railtie.railtie_name)
-
- if railtie.config.respond_to?(:paths) && railtie.config.paths.db
- railties[railtie.railtie_name] = railtie.config.paths.db.migrate.to_a.first
- end
- end
-
- copied = ActiveRecord::Migration.copy(ActiveRecord::Migrator.migrations_path, railties)
-
- if copied.blank?
- puts "No migrations were copied, project is up to date."
- else
- puts "The following migrations were copied:"
- puts copied.map{ |path| File.basename(path) }.join("\n")
- end
+ ActiveRecord::Migrator.migrations_path = Rails.application.paths["db/migrate"].first
end
namespace :create do
@@ -108,7 +87,7 @@ namespace :db do
end
end
when 'postgresql'
- @encoding = config[:encoding] || ENV['CHARSET'] || 'utf8'
+ @encoding = config['encoding'] || ENV['CHARSET'] || 'utf8'
begin
ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => @encoding))
@@ -501,8 +480,31 @@ namespace :db do
end
namespace :railties do
- desc "Copies missing migrations from Railties (e.g. plugins, engines). You can specify Railties to use with FROM=railtie1,railtie2"
- task :copy_migrations => 'db:copy_migrations'
+ namespace :install do
+ # desc "Copies missing migrations from Railties (e.g. plugins, engines). You can specify Railties to use with FROM=railtie1,railtie2"
+ task :migrations => :"db:load_config" do
+ to_load = ENV["FROM"].blank? ? :all : ENV["FROM"].split(",").map {|n| n.strip }
+ railties = {}
+ Rails.application.railties.all do |railtie|
+ next unless to_load == :all || to_load.include?(railtie.railtie_name)
+
+ if railtie.respond_to?(:paths) && (path = railtie.paths["db/migrate"].first)
+ railties[railtie.railtie_name] = path
+ end
+ end
+
+ on_skip = Proc.new do |name, migration|
+ $stderr.puts "WARNING: Migration #{migration.basename} from #{name} has been skipped. Migration with the same name already exists."
+ end
+
+ on_copy = Proc.new do |name, migration, old_path|
+ puts "Copied migration #{migration.basename} from #{name}"
+ end
+
+ ActiveRecord::Migration.copy( ActiveRecord::Migrator.migrations_path, railties,
+ :on_skip => on_skip, :on_copy => on_copy)
+ end
+ end
end
task 'test:prepare' => 'db:test:prepare'
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 2e0a2effc2..001207514d 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/serialization.rb b/activerecord/lib/active_record/serialization.rb
index dc73075a59..2bde06f562 100644
--- a/activerecord/lib/active_record/serialization.rb
+++ b/activerecord/lib/active_record/serialization.rb
@@ -45,7 +45,7 @@ module ActiveRecord #:nodoc:
send(association)
end
- unless records.nil?
+ if records
association_options = include_has_options ? include_associations[association] : base_only_or_except
opts = options.merge(association_options)
yield(association, records, opts)
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/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 82b3c36ed2..f0ec5c751c 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -26,6 +26,35 @@ module ActiveRecord
"threads should have been removed")
assert_equal pool.checkins.length, threads.length
end
+
+ def test_checkout_behaviour
+ pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
+ connection = pool.connection
+ assert_not_nil connection
+ threads = []
+ 4.times do |i|
+ threads << Thread.new(i) do |pool_count|
+ connection = pool.connection
+ assert_not_nil connection
+ end
+ end
+
+ threads.each {|t| t.join}
+
+ Thread.new do
+ threads.each do |t|
+ thread_ids = pool.instance_variable_get(:@reserved_connections).keys
+ assert thread_ids.include?(t.object_id)
+ end
+
+ pool.connection
+ threads.each do |t|
+ thread_ids = pool.instance_variable_get(:@reserved_connections).keys
+ assert !thread_ids.include?(t.object_id)
+ end
+ end.join()
+
+ end
end
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/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 6e8ee95613..ef949300b0 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -1910,9 +1910,9 @@ if ActiveRecord::Base.connection.supports_migrations?
@existing_migrations = Dir[@migrations_path + "/*.rb"]
copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
- assert File.exists?(@migrations_path + "/4_people_have_hobbies.bukkits.rb")
- assert File.exists?(@migrations_path + "/5_people_have_descriptions.bukkits.rb")
- assert_equal [@migrations_path + "/4_people_have_hobbies.bukkits.rb", @migrations_path + "/5_people_have_descriptions.bukkits.rb"], copied
+ assert File.exists?(@migrations_path + "/4_people_have_hobbies.rb")
+ assert File.exists?(@migrations_path + "/5_people_have_descriptions.rb")
+ assert_equal [@migrations_path + "/4_people_have_hobbies.rb", @migrations_path + "/5_people_have_descriptions.rb"], copied.map(&:filename)
files_count = Dir[@migrations_path + "/*.rb"].length
copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
@@ -1928,12 +1928,13 @@ if ActiveRecord::Base.connection.supports_migrations?
@existing_migrations = Dir[@migrations_path + "/*.rb"]
sources = ActiveSupport::OrderedHash.new
- sources[:bukkits] = sources[:omg] = MIGRATIONS_ROOT + "/to_copy"
+ sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy"
+ sources[:omg] = MIGRATIONS_ROOT + "/to_copy2"
ActiveRecord::Migration.copy(@migrations_path, sources)
- assert File.exists?(@migrations_path + "/4_people_have_hobbies.omg.rb")
- assert File.exists?(@migrations_path + "/5_people_have_descriptions.omg.rb")
- assert File.exists?(@migrations_path + "/6_people_have_hobbies.bukkits.rb")
- assert File.exists?(@migrations_path + "/7_people_have_descriptions.bukkits.rb")
+ assert File.exists?(@migrations_path + "/4_people_have_hobbies.rb")
+ assert File.exists?(@migrations_path + "/5_people_have_descriptions.rb")
+ assert File.exists?(@migrations_path + "/6_create_articles.rb")
+ assert File.exists?(@migrations_path + "/7_create_comments.rb")
files_count = Dir[@migrations_path + "/*.rb"].length
ActiveRecord::Migration.copy(@migrations_path, sources)
@@ -1948,11 +1949,11 @@ if ActiveRecord::Base.connection.supports_migrations?
Time.travel_to(created_at = Time.utc(2010, 7, 26, 10, 10, 10)) do
copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
- assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
- assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
- expected = [@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb",
- @migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb"]
- assert_equal expected, copied
+ assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
+ assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
+ expected = [@migrations_path + "/20100726101010_people_have_hobbies.rb",
+ @migrations_path + "/20100726101011_people_have_descriptions.rb"]
+ assert_equal expected, copied.map(&:filename)
files_count = Dir[@migrations_path + "/*.rb"].length
copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
@@ -1968,14 +1969,15 @@ if ActiveRecord::Base.connection.supports_migrations?
@existing_migrations = Dir[@migrations_path + "/*.rb"]
sources = ActiveSupport::OrderedHash.new
- sources[:bukkits] = sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
+ sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
+ sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps2"
Time.travel_to(created_at = Time.utc(2010, 7, 26, 10, 10, 10)) do
copied = ActiveRecord::Migration.copy(@migrations_path, sources)
- assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.omg.rb")
- assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.omg.rb")
- assert File.exists?(@migrations_path + "/20100726101012_people_have_hobbies.bukkits.rb")
- assert File.exists?(@migrations_path + "/20100726101013_people_have_descriptions.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
+ assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
+ assert File.exists?(@migrations_path + "/20100726101012_create_articles.rb")
+ assert File.exists?(@migrations_path + "/20100726101013_create_comments.rb")
assert_equal 4, copied.length
files_count = Dir[@migrations_path + "/*.rb"].length
@@ -1992,8 +1994,8 @@ if ActiveRecord::Base.connection.supports_migrations?
Time.travel_to(created_at = Time.utc(2010, 2, 20, 10, 10, 10)) do
ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
- assert File.exists?(@migrations_path + "/20100301010102_people_have_hobbies.bukkits.rb")
- assert File.exists?(@migrations_path + "/20100301010103_people_have_descriptions.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100301010102_people_have_hobbies.rb")
+ assert File.exists?(@migrations_path + "/20100301010103_people_have_descriptions.rb")
files_count = Dir[@migrations_path + "/*.rb"].length
copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
@@ -2004,14 +2006,32 @@ if ActiveRecord::Base.connection.supports_migrations?
clear
end
+ def test_skipping_migrations
+ @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
+
+ sources = ActiveSupport::OrderedHash.new
+ sources[:bukkits] = sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
+
+ skipped = []
+ on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" }
+ copied = ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
+ assert_equal 2, copied.length
+
+ assert_equal 2, skipped.length
+ assert_equal ["bukkits PeopleHaveHobbies", "bukkits PeopleHaveDescriptions"], skipped
+ ensure
+ clear
+ end
+
def test_copying_migrations_to_empty_directory
@migrations_path = MIGRATIONS_ROOT + "/empty"
@existing_migrations = []
Time.travel_to(created_at = Time.utc(2010, 7, 26, 10, 10, 10)) do
copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
- assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
- assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
+ assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
assert_equal 2, copied.length
end
ensure
diff --git a/activerecord/test/migrations/to_copy2/1_create_articles.rb b/activerecord/test/migrations/to_copy2/1_create_articles.rb
new file mode 100644
index 0000000000..0f048d90f7
--- /dev/null
+++ b/activerecord/test/migrations/to_copy2/1_create_articles.rb
@@ -0,0 +1,7 @@
+class CreateArticles < ActiveRecord::Migration
+ def self.up
+ end
+
+ def self.down
+ end
+end
diff --git a/activerecord/test/migrations/to_copy2/2_create_comments.rb b/activerecord/test/migrations/to_copy2/2_create_comments.rb
new file mode 100644
index 0000000000..0f048d90f7
--- /dev/null
+++ b/activerecord/test/migrations/to_copy2/2_create_comments.rb
@@ -0,0 +1,7 @@
+class CreateArticles < ActiveRecord::Migration
+ def self.up
+ end
+
+ def self.down
+ end
+end
diff --git a/activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb
new file mode 100644
index 0000000000..0f048d90f7
--- /dev/null
+++ b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010101_create_articles.rb
@@ -0,0 +1,7 @@
+class CreateArticles < ActiveRecord::Migration
+ def self.up
+ end
+
+ def self.down
+ end
+end
diff --git a/activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb
new file mode 100644
index 0000000000..2b048edbb5
--- /dev/null
+++ b/activerecord/test/migrations/to_copy_with_timestamps2/20090101010202_create_comments.rb
@@ -0,0 +1,7 @@
+class CreateComments < ActiveRecord::Migration
+ def self.up
+ end
+
+ def self.down
+ end
+end
diff --git a/activeresource/lib/active_resource.rb b/activeresource/lib/active_resource.rb
index 3e4a1dd4a1..186865f811 100644
--- a/activeresource/lib/active_resource.rb
+++ b/activeresource/lib/active_resource.rb
@@ -29,6 +29,7 @@ $:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include
require 'active_support'
require 'active_model'
+require 'active_resource/version'
module ActiveResource
extend ActiveSupport::Autoload
diff --git a/activeresource/lib/active_resource/http_mock.rb b/activeresource/lib/active_resource/http_mock.rb
index ddd3fb1f5d..9aefde7c30 100644
--- a/activeresource/lib/active_resource/http_mock.rb
+++ b/activeresource/lib/active_resource/http_mock.rb
@@ -100,11 +100,11 @@ module ActiveResource
# Accepts a block which declares a set of requests and responses for the HttpMock to respond to in
# the following format:
- #
+ #
# mock.http_method(path, request_headers = {}, body = nil, status = 200, response_headers = {})
- #
+ #
# === Example
- #
+ #
# @matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person")
# ActiveResource::HttpMock.respond_to do |mock|
# mock.post "/people.xml", {}, @matz, 201, "Location" => "/people/1.xml"
@@ -112,65 +112,76 @@ module ActiveResource
# mock.put "/people/1.xml", {}, nil, 204
# mock.delete "/people/1.xml", {}, nil, 200
# end
- #
+ #
# Alternatively, accepts a hash of <tt>{Request => Response}</tt> pairs allowing you to generate
# these the following format:
- #
+ #
# ActiveResource::Request.new(method, path, body, request_headers)
# ActiveResource::Response.new(body, status, response_headers)
- #
+ #
# === Example
- #
+ #
# Request.new(:#{method}, path, nil, request_headers)
- #
+ #
# @matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person")
#
# create_matz = ActiveResource::Request.new(:post, '/people.xml', @matz, {})
# created_response = ActiveResource::Response.new("", 201, {"Location" => "/people/1.xml"})
# get_matz = ActiveResource::Request.new(:get, '/people/1.xml', nil)
# ok_response = ActiveResource::Response.new("", 200, {})
- #
+ #
# pairs = {create_matz => created_response, get_matz => ok_response}
- #
+ #
# ActiveResource::HttpMock.respond_to(pairs)
#
# Note, by default, every time you call +respond_to+, any previous request and response pairs stored
# in HttpMock will be deleted giving you a clean slate to work on.
- #
+ #
# If you want to override this behaviour, pass in +false+ as the last argument to +respond_to+
- #
+ #
# === Example
- #
+ #
# ActiveResource::HttpMock.respond_to do |mock|
# mock.send(:get, "/people/1", {}, "XML1")
# end
# ActiveResource::HttpMock.responses.length #=> 1
- #
+ #
# ActiveResource::HttpMock.respond_to(false) do |mock|
# mock.send(:get, "/people/2", {}, "XML2")
# end
# ActiveResource::HttpMock.responses.length #=> 2
- #
+ #
# This also works with passing in generated pairs of requests and responses, again, just pass in false
# as the last argument:
- #
+ #
# === Example
- #
+ #
# ActiveResource::HttpMock.respond_to do |mock|
# mock.send(:get, "/people/1", {}, "XML1")
# end
# ActiveResource::HttpMock.responses.length #=> 1
- #
+ #
# get_matz = ActiveResource::Request.new(:get, '/people/1.xml', nil)
# ok_response = ActiveResource::Response.new("", 200, {})
- #
+ #
# pairs = {get_matz => ok_response}
#
# ActiveResource::HttpMock.respond_to(pairs, false)
# ActiveResource::HttpMock.responses.length #=> 2
+ #
+ # # If you add a response with an existing request, it will be replaced
+ #
+ # fail_response = ActiveResource::Response.new("", 404, {})
+ # pairs = {get_matz => fail_response}
+ #
+ # ActiveResource::HttpMock.respond_to(pairs, false)
+ # ActiveResource::HttpMock.responses.length #=> 2
+ #
def respond_to(*args) #:yields: mock
pairs = args.first || {}
reset! if args.last.class != FalseClass
+
+ delete_responses_to_replace pairs.to_a
responses.concat pairs.to_a
if block_given?
yield Responder.new(responses)
@@ -179,6 +190,13 @@ module ActiveResource
end
end
+ def delete_responses_to_replace(new_responses)
+ new_responses.each{|nr|
+ request_to_remove = nr[0]
+ @@responses = responses.delete_if{|r| r[0] == request_to_remove}
+ }
+ end
+
# Deletes all logged requests and responses.
def reset!
requests.clear
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 c3733e13d8..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.select { |p| p.name == 'David' }
+ assert remote_programmers.find { |p| p.name == 'David' }
end
end
end
diff --git a/activeresource/test/cases/http_mock_test.rb b/activeresource/test/cases/http_mock_test.rb
index d90d1e01b8..82b5e60c77 100644
--- a/activeresource/test/cases/http_mock_test.rb
+++ b/activeresource/test/cases/http_mock_test.rb
@@ -69,19 +69,19 @@ class HttpMockTest < ActiveSupport::TestCase
request(method, "/people/1", FORMAT_HEADER[method] => "application/json")
end
end
-
+
end
test "allows you to send in pairs directly to the respond_to method" do
matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person")
-
+
create_matz = ActiveResource::Request.new(:post, '/people.xml', matz, {})
created_response = ActiveResource::Response.new("", 201, {"Location" => "/people/1.xml"})
get_matz = ActiveResource::Request.new(:get, '/people/1.xml', nil)
ok_response = ActiveResource::Response.new(matz, 200, {})
-
+
pairs = {create_matz => created_response, get_matz => ok_response}
-
+
ActiveResource::HttpMock.respond_to(pairs)
assert_equal 2, ActiveResource::HttpMock.responses.length
assert_equal "", ActiveResource::HttpMock.responses.assoc(create_matz)[1].body
@@ -140,6 +140,34 @@ class HttpMockTest < ActiveSupport::TestCase
assert_equal 2, ActiveResource::HttpMock.responses.length
end
+ test "allows you to replace the existing reponse with the same request" do
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.send(:get, "/people/1", {}, "XML1")
+ end
+ assert_equal 1, ActiveResource::HttpMock.responses.length
+
+ matz = { :id => 1, :name => "Matz" }.to_xml(:root => "person")
+ get_matz = ActiveResource::Request.new(:get, '/people/1', nil)
+ ok_response = ActiveResource::Response.new(matz, 200, {})
+
+ ActiveResource::HttpMock.respond_to({get_matz => ok_response}, false)
+
+ assert_equal 1, ActiveResource::HttpMock.responses.length
+ end
+
+ test "do not replace the response with the same path but different method" do
+ ActiveResource::HttpMock.respond_to do |mock|
+ mock.send(:get, "/people/1", {}, "XML1")
+ end
+ assert_equal 1, ActiveResource::HttpMock.responses.length
+
+ put_matz = ActiveResource::Request.new(:put, '/people/1', nil)
+ ok_response = ActiveResource::Response.new("", 200, {})
+
+ ActiveResource::HttpMock.respond_to({put_matz => ok_response}, false)
+ assert_equal 2, ActiveResource::HttpMock.responses.length
+ end
+
def request(method, path, headers = {}, body = nil)
if [:put, :post].include? method
@http.send(method, path, body, headers)
diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb
index ba91e8bba3..6b87774978 100644
--- a/activesupport/lib/active_support.rb
+++ b/activesupport/lib/active_support.rb
@@ -35,6 +35,7 @@ module ActiveSupport
end
require "active_support/dependencies/autoload"
+require "active_support/version"
module ActiveSupport
extend ActiveSupport::Autoload
diff --git a/activesupport/lib/active_support/cache/mem_cache_store.rb b/activesupport/lib/active_support/cache/mem_cache_store.rb
index f32b562368..45263d482f 100644
--- a/activesupport/lib/active_support/cache/mem_cache_store.rb
+++ b/activesupport/lib/active_support/cache/mem_cache_store.rb
@@ -1,7 +1,7 @@
begin
require 'memcache'
rescue LoadError => e
- $stderr.puts "You don't have memcache installed in your application. Please add it to your Gemfile and run bundle install"
+ $stderr.puts "You don't have memcache-client installed in your application. Please add it to your Gemfile and run bundle install"
raise e
end
require 'digest/md5'
@@ -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/core_ext/logger.rb b/activesupport/lib/active_support/core_ext/logger.rb
index a1c351bfd9..e63a0a9ed9 100644
--- a/activesupport/lib/active_support/core_ext/logger.rb
+++ b/activesupport/lib/active_support/core_ext/logger.rb
@@ -4,18 +4,17 @@ require 'active_support/core_ext/class/attribute_accessors'
class Logger #:nodoc:
def self.define_around_helper(level)
module_eval <<-end_eval, __FILE__, __LINE__ + 1
- def around_#{level}(before_message, after_message, &block) # def around_debug(before_message, after_message, &block)
- self.#{level}(before_message) # self.debug(before_message)
- return_value = block.call(self) # return_value = block.call(self)
- self.#{level}(after_message) # self.debug(after_message)
- return return_value # return return_value
- end # end
+ def around_#{level}(before_message, after_message) # def around_debug(before_message, after_message, &block)
+ self.#{level}(before_message) # self.debug(before_message)
+ return_value = yield(self) # return_value = yield(self)
+ self.#{level}(after_message) # self.debug(after_message)
+ return_value # return_value
+ end # end
end_eval
end
[:debug, :info, :error, :fatal].each {|level| define_around_helper(level) }
end
-
require 'logger'
# Extensions to the built-in Ruby logger.
@@ -65,11 +64,11 @@ class Logger
formatter.datetime_format if formatter.respond_to?(:datetime_format)
end
- alias :old_formatter :formatter if method_defined?(:formatter)
- # Get the current formatter. The default formatter is a SimpleFormatter which only
- # displays the log message
- def formatter
- @formatter ||= SimpleFormatter.new
+ alias :old_initialize :initialize
+ # Overwrite initialize to set a default formatter.
+ def initialize(*args)
+ old_initialize(*args)
+ self.formatter = SimpleFormatter.new
end
# Simple formatter which only displays the message.
@@ -79,30 +78,4 @@ class Logger
"#{String === msg ? msg : msg.inspect}\n"
end
end
-
- private
- alias old_format_message format_message
-
- # Ruby 1.8.3 transposed the msg and progname arguments to format_message.
- # We can't test RUBY_VERSION because some distributions don't keep Ruby
- # and its standard library in sync, leading to installations of Ruby 1.8.2
- # with Logger from 1.8.3 and vice versa.
- if method_defined?(:formatter=)
- def format_message(severity, timestamp, progname, msg)
- formatter.call(severity, timestamp, progname, msg)
- end
- else
- def format_message(severity, timestamp, msg, progname)
- formatter.call(severity, timestamp, progname, msg)
- end
-
- attr_writer :formatter
- public :formatter=
-
- alias old_format_datetime format_datetime
- def format_datetime(datetime) datetime end
-
- alias old_msg2str msg2str
- def msg2str(msg) msg end
- end
end
diff --git a/activesupport/lib/active_support/core_ext/time/conversions.rb b/activesupport/lib/active_support/core_ext/time/conversions.rb
index 025c619783..d4ae3131ec 100644
--- a/activesupport/lib/active_support/core_ext/time/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/time/conversions.rb
@@ -19,8 +19,8 @@ class Time
#
# time = Time.now # => Thu Jan 18 06:10:17 CST 2007
#
- # time.to_formatted_s(:time) # => "06:10:17"
- # time.to_s(:time) # => "06:10:17"
+ # time.to_formatted_s(:time) # => "06:10"
+ # time.to_s(:time) # => "06:10"
#
# time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
# time.to_formatted_s(:number) # => "20070118061017"
diff --git a/activesupport/lib/active_support/hash_with_indifferent_access.rb b/activesupport/lib/active_support/hash_with_indifferent_access.rb
index e8215bc566..c406dd3c2e 100644
--- a/activesupport/lib/active_support/hash_with_indifferent_access.rb
+++ b/activesupport/lib/active_support/hash_with_indifferent_access.rb
@@ -45,6 +45,8 @@ module ActiveSupport
regular_writer(convert_key(key), convert_value(value))
end
+ alias_method :store, :[]=
+
# Updates the instantized hash with values from the second:
#
# hash_1 = HashWithIndifferentAccess.new
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/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index 0f35eb9e78..545fed2684 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -316,6 +316,16 @@ class HashExtTest < Test::Unit::TestCase
assert_equal expected, hash_1
end
+ def test_store_on_indifferent_access
+ hash = HashWithIndifferentAccess.new
+ hash.store(:test1, 1)
+ hash.store('test1', 11)
+ hash[:test2] = 2
+ hash['test2'] = 22
+ expected = { "test1" => 11, "test2" => 22 }
+ assert_equal expected, hash
+ end
+
def test_reverse_merge
defaults = { :a => "x", :b => "y", :c => 10 }.freeze
options = { :a => 1, :b => 2 }
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/CHANGELOG b/railties/CHANGELOG
index d2a4add375..b38a9ce750 100644
--- a/railties/CHANGELOG
+++ b/railties/CHANGELOG
@@ -1,6 +1,8 @@
*Rails 3.1.0 (unreleased)*
-* Added Rack::Cache to the default middleware stack
+* Added Rack::Etag and Rack::ConditionalGet to the default middleware stack [José Valim]
+
+* Added Rack::Cache to the default middleware stack [Yehuda Katz and Carl Lerche]
* Engine is now rack application [Piotr Sarnacki]
diff --git a/railties/guides/source/action_view_overview.textile b/railties/guides/source/action_view_overview.textile
index 843dfe530d..0e1a352ebd 100644
--- a/railties/guides/source/action_view_overview.textile
+++ b/railties/guides/source/action_view_overview.textile
@@ -253,9 +253,6 @@ javascript_include_tag :monkey # =>
<script type="text/javascript" src="/javascripts/tail.js"></script>
</ruby>
-h5. register_javascript_include_default
-
-Register one or more additional JavaScript files to be included when +javascript_include_tag :defaults+ is called. This method is typically intended to be called from plugin initialization to register additional +.js+ files that the plugin installed in +public/javascripts+.
h5. register_stylesheet_expansion
diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile
index 6996dab8c5..f6d61373e1 100644
--- a/railties/guides/source/association_basics.textile
+++ b/railties/guides/source/association_basics.textile
@@ -550,7 +550,7 @@ build_customer
create_customer
</ruby>
-h6(#belongs_to-association). <tt>_association_(force_reload = false)</tt>
+h6(#belongs_to-association). <tt><em>association</em>(force_reload = false)</tt>
The <tt><em>association</em></tt> method returns the associated object, if any. If no associated object is found, it returns +nil+.
diff --git a/railties/guides/source/command_line.textile b/railties/guides/source/command_line.textile
index cbdfda9215..752b5926f7 100644
--- a/railties/guides/source/command_line.textile
+++ b/railties/guides/source/command_line.textile
@@ -145,7 +145,7 @@ $ rails generate controller Greetings hello
What all did this generate? It made sure a bunch of directories were in our application, and created a controller file, a functional test file, a helper for the view, and a view file.
-Check out the controller and modify it a little (in +app/controllers/greetings_controller.rb+):ma
+Check out the controller and modify it a little (in +app/controllers/greetings_controller.rb+):
<ruby>
class GreetingsController < ApplicationController
diff --git a/railties/guides/source/debugging_rails_applications.textile b/railties/guides/source/debugging_rails_applications.textile
index 35069f33ad..b9d45fb13a 100644
--- a/railties/guides/source/debugging_rails_applications.textile
+++ b/railties/guides/source/debugging_rails_applications.textile
@@ -250,7 +250,7 @@ Make sure you have started your web server with the option +--debugger+:
<shell>
~/PathTo/rails_project$ rails server --debugger
-=> Booting Mongrel (use 'rails server webrick' to force WEBrick)
+=> Booting WEBrick
=> Rails 3.0.0 application starting on http://0.0.0.0:3000
=> Debugger enabled
...
@@ -258,8 +258,6 @@ Make sure you have started your web server with the option +--debugger+:
TIP: In development mode, you can dynamically +require \'ruby-debug\'+ instead of restarting the server, if it was started without +--debugger+.
-In order to use Rails debugging you'll need to be running either *WEBrick* or *Mongrel*. For the moment, no alternative servers are supported.
-
h4. The Shell
As soon as your application calls the +debugger+ method, the debugger will be started in a debugger shell inside the terminal window where you launched your application server, and you will be placed at ruby-debug's prompt +(rdb:n)+. The _n_ is the thread number. The prompt will also show you the next line of code that is waiting to run.
diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile
index b1f8ea29da..0f2cbb76dc 100644
--- a/railties/guides/source/generators.textile
+++ b/railties/guides/source/generators.textile
@@ -144,7 +144,7 @@ generators/initializer_generator.rb
If none is found you get an error message.
-INFO: The examples above put files under the application's +lib+ because said directoty belongs to +$LOAD_PATH+.
+INFO: The examples above put files under the application's +lib+ because said directory belongs to +$LOAD_PATH+.
h3. Customizing Your Workflow
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index b2810188c2..3f433c9704 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -282,7 +282,7 @@ You actually have a functional Rails application already. To see it, you need to
$ rails server
</shell>
-This will fire up an instance of the Mongrel web server by default (Rails can also use several other web servers). To see your application in action, open a browser window and navigate to "http://localhost:3000":http://localhost:3000. You should see Rails' default information page:
+This will fire up an instance of the WEBrick web server by default (Rails can also use several other web servers). To see your application in action, open a browser window and navigate to "http://localhost:3000":http://localhost:3000. You should see Rails' default information page:
!images/rails_welcome.png(Welcome Aboard screenshot)!
@@ -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/initialization.textile b/railties/guides/source/initialization.textile
index 599ddccdd6..3e02bc0158 100644
--- a/railties/guides/source/initialization.textile
+++ b/railties/guides/source/initialization.textile
@@ -3070,7 +3070,7 @@ The +Rails::Plugin::Configuration+ class may be a bit difficult to find at first
<ruby>
module Rails
- class Plugin < Railtie
+ class Plugin < Engine
...
end
end
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/layouts_and_rendering.textile b/railties/guides/source/layouts_and_rendering.textile
index 50c5986a64..088e1f817c 100644
--- a/railties/guides/source/layouts_and_rendering.textile
+++ b/railties/guides/source/layouts_and_rendering.textile
@@ -90,7 +90,7 @@ If we want to display the properties of all the books in our view, we can do so
<%= link_to 'New book', new_book_path %>
</ruby>
-NOTE: The actual rendering is done by subclasses of +ActionView::TemplateHandlers+. This guide does not dig into that process, but it's important to know that the file extension on your view controls the choice of template handler. In Rails 2, the standard extensions are +.erb+ for ERB (HTML with embedded Ruby), +.rjs+ for RJS (javascript with embedded ruby) and +.builder+ for Builder (XML generator).
+NOTE: The actual rendering is done by subclasses of +ActionView::TemplateHandlers+. This guide does not dig into that process, but it's important to know that the file extension on your view controls the choice of template handler. In Rails 2, the standard extensions are +.erb+ for ERB (HTML with embedded Ruby), +.rjs+ for RJS (JavaScript with embedded ruby) and +.builder+ for Builder (XML generator).
h4. Using +render+
@@ -252,7 +252,7 @@ render :inline =>
h5. Using +render+ with +:update+
-You can also render javascript-based page updates inline using the +:update+ option to +render+:
+You can also render JavaScript-based page updates inline using the +:update+ option to +render+:
<ruby>
render :update do |page|
@@ -260,7 +260,7 @@ render :update do |page|
end
</ruby>
-WARNING: Placing javascript updates in your controller may seem to streamline small updates, but it defeats the MVC orientation of Rails and will make it harder for other developers to follow the logic of your project. We recommend using a separate rjs template instead, no matter how small the update.
+WARNING: Placing JavaScript updates in your controller may seem to streamline small updates, but it defeats the MVC orientation of Rails and will make it harder for other developers to follow the logic of your project. We recommend using a separate RJS template instead, no matter how small the update.
h5. Rendering Text
@@ -276,7 +276,7 @@ NOTE: By default, if you use the +:text+ option, the text is rendered without us
h5. Rendering JSON
-JSON is a javascript data format used by many AJAX libraries. Rails has built-in support for converting objects to JSON and rendering that JSON back to the browser:
+JSON is a JavaScript data format used by many AJAX libraries. Rails has built-in support for converting objects to JSON and rendering that JSON back to the browser:
<ruby>
render :json => @product
@@ -655,7 +655,7 @@ I'll discuss each of these in turn.
h4. Asset Tags
-Asset tags provide methods for generating HTML that links views to assets like images, videos, audio, javascript, stylesheets, and feeds. There are six types of include tag:
+Asset tags provide methods for generating HTML that links views to assets like images, videos, audio, JavaScript, stylesheets, and feeds. There are six types of include tag:
* +auto_discovery_link_tag+
* +javascript_include_tag+
@@ -715,7 +715,7 @@ The +defaults+ option loads the Prototype and Scriptaculous libraries:
<%= javascript_include_tag :defaults %>
</erb>
-The +all+ option loads every javascript file in +public/javascripts+, starting with the Prototype and Scriptaculous libraries:
+The +all+ option loads every JavaScript file in +public/javascripts+, starting with the Prototype and Scriptaculous libraries:
<erb>
<%= javascript_include_tag :all %>
@@ -727,7 +727,7 @@ You can supply the +:recursive+ option to load files in subfolders of +public/ja
<%= javascript_include_tag :all, :recursive => true %>
</erb>
-If you're loading multiple javascript files, you can create a better user experience by combining multiple files into a single download. To make this happen in production, specify +:cache => true+ in your +javascript_include_tag+:
+If you're loading multiple JavaScript files, you can create a better user experience by combining multiple files into a single download. To make this happen in production, specify +:cache => true+ in your +javascript_include_tag+:
<erb>
<%= javascript_include_tag "main", "columns", :cache => true %>
@@ -962,7 +962,7 @@ The result of rendering this page into the supplied layout would be this HTML:
</html>
</erb>
-The +content_for+ method is very helpful when your layout contains distinct regions such as sidebars and footers that should get their own blocks of content inserted. It's also useful for inserting tags that load page-specific javascript or css files into the header of an otherwise generic layout.
+The +content_for+ method is very helpful when your layout contains distinct regions such as sidebars and footers that should get their own blocks of content inserted. It's also useful for inserting tags that load page-specific JavaScript or css files into the header of an otherwise generic layout.
h4. Using Partials
diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index 7d4fb69d3b..a8a8ee58ec 100644
--- a/railties/guides/source/routing.textile
+++ b/railties/guides/source/routing.textile
@@ -91,7 +91,7 @@ Creating a resourceful route will also expose a number of helpers to the control
* +photos_path+ returns +/photos+
* +new_photo_path+ returns +/photos/new+
-* +edit_photo_path+ returns +/photos/:id/edit+
+* +edit_photo_path(id)+ returns +/photos/:id/edit+ (for instance, +edit_photo_path(10)+ returns +/photos/10/edit+)
* +photo_path(id)+ returns +/photos/:id+ (for instance, +photo_path(10)+ returns +/photos/10+)
Each of these helpers has a corresponding +_url+ helper (such as +photos_url+) which returns the same path prefixed with the current host, port and path prefix.
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.rb b/railties/lib/rails.rb
index 3663910281..cca0891835 100644
--- a/railties/lib/rails.rb
+++ b/railties/lib/rails.rb
@@ -92,7 +92,7 @@ module Rails
end
def public_path
- application && application.paths.public.to_a.first
+ application && application.paths["public"].first
end
end
end
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 0e85e6d1d5..d13356ab4d 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -39,6 +39,7 @@ module Rails
autoload :Configuration, 'rails/application/configuration'
autoload :Finisher, 'rails/application/finisher'
autoload :Railties, 'rails/application/railties'
+ autoload :RoutesReloader, 'rails/application/routes_reloader'
class << self
def inherited(base)
@@ -71,7 +72,7 @@ module Rails
end
def require_environment! #:nodoc:
- environment = paths.config.environment.to_a.first
+ environment = paths["config/environment"].existent.first
require environment if environment
end
@@ -80,18 +81,12 @@ module Rails
super
end
- def routes_reloader
- @routes_reloader ||= ActiveSupport::FileUpdateChecker.new([]){ reload_routes! }
+ def reload_routes!
+ routes_reloader.reload!
end
- def reload_routes!
- _routes = self.routes
- _routes.disable_clear_and_finalize = true
- _routes.clear!
- routes_reloader.paths.each { |path| load(path) }
- ActiveSupport.on_load(:action_controller) { _routes.finalize! }
- ensure
- _routes.disable_clear_and_finalize = false
+ def routes_reloader
+ @routes_reloader ||= RoutesReloader.new
end
def initialize!
@@ -133,10 +128,9 @@ module Rails
end
def initializers
- initializers = Bootstrap.initializers_for(self)
- initializers += super
- initializers += Finisher.initializers_for(self)
- initializers
+ Bootstrap.initializers_for(self) +
+ super +
+ Finisher.initializers_for(self)
end
def config
@@ -150,8 +144,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
@@ -170,6 +164,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/bootstrap.rb b/railties/lib/rails/application/bootstrap.rb
index e39b3bc705..213aa0768a 100644
--- a/railties/lib/rails/application/bootstrap.rb
+++ b/railties/lib/rails/application/bootstrap.rb
@@ -23,7 +23,7 @@ module Rails
# Initialize the logger early in the stack in case we need to log some deprecation.
initializer :initialize_logger do
Rails.logger ||= config.logger || begin
- path = config.paths.log.to_a.first
+ path = config.paths["log"].first
logger = ActiveSupport::BufferedLogger.new(path)
logger.level = ActiveSupport::BufferedLogger.const_get(config.log_level.to_s.upcase)
logger.auto_flushing = false if Rails.env.production?
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index a0ecbc0fc8..3505388479 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -9,7 +9,7 @@ module Rails
:filter_parameters, :log_level, :logger,
:preload_frameworks, :reload_plugins,
:secret_token, :serve_static_assets, :session_options,
- :time_zone, :whiny_nils
+ :time_zone, :whiny_nils, :helpers_paths
def initialize(*)
super
@@ -17,6 +17,7 @@ module Rails
@allow_concurrency = false
@consider_all_requests_local = false
@filter_parameters = []
+ @helpers_paths = []
@dependency_loading = true
@serve_static_assets = true
@session_store = :cookie_store
@@ -24,6 +25,7 @@ module Rails
@time_zone = "UTC"
@middleware = app_middleware
@asset_path = '/'
+ @generators = app_generators
end
def asset_path=(value)
@@ -59,13 +61,12 @@ module Rails
def paths
@paths ||= begin
paths = super
- paths.config.database "config/database.yml"
- paths.config.environment "config/environment.rb"
- paths.lib.templates "lib/templates"
- paths.log "log/#{Rails.env}.log"
- paths.tmp "tmp"
- paths.tmp.cache "tmp/cache"
-
+ paths.add "config/database", :with => "config/database.yml"
+ paths.add "config/environment", :with => "config/environment.rb"
+ paths.add "lib/templates"
+ paths.add "log", :with => "log/#{Rails.env}.log"
+ paths.add "tmp"
+ paths.add "tmp/cache"
paths
end
end
@@ -87,7 +88,7 @@ module Rails
# YAML::load.
def database_configuration
require 'erb'
- YAML::load(ERB.new(IO.read(paths.config.database.to_a.first)).result)
+ YAML::load(ERB.new(IO.read(paths["config/database"].first)).result)
end
def cache_store
diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb
index b95df467c7..e3342be7ee 100644
--- a/railties/lib/rails/application/finisher.rb
+++ b/railties/lib/rails/application/finisher.rb
@@ -4,7 +4,7 @@ module Rails
include Initializable
initializer :add_generator_templates do
- config.generators.templates.unshift(*paths.lib.templates.to_a)
+ config.generators.templates.unshift(*paths["lib/templates"].existent)
end
initializer :ensure_autoload_once_paths_as_subset do
diff --git a/railties/lib/rails/application/routes_reloader.rb b/railties/lib/rails/application/routes_reloader.rb
new file mode 100644
index 0000000000..1d1f5e1b06
--- /dev/null
+++ b/railties/lib/rails/application/routes_reloader.rb
@@ -0,0 +1,45 @@
+module Rails
+ class Application
+ class RoutesReloader < ::ActiveSupport::FileUpdateChecker
+ attr_reader :route_sets
+
+ def initialize
+ super([]) { reload! }
+ @route_sets = []
+ end
+
+ def reload!
+ clear!
+ load_paths
+ finalize!
+ ensure
+ revert
+ end
+
+ protected
+
+ def clear!
+ route_sets.each do |routes|
+ routes.disable_clear_and_finalize = true
+ routes.clear!
+ end
+ end
+
+ def load_paths
+ paths.each { |path| load(path) }
+ end
+
+ def finalize!
+ route_sets.each do |routes|
+ ActiveSupport.on_load(:action_controller) { routes.finalize! }
+ end
+ end
+
+ def revert
+ route_sets.each do |routes|
+ routes.disable_clear_and_finalize = false
+ end
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index 60a93c9848..e3aa6d7c3e 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -11,7 +11,17 @@ command = ARGV.shift
command = aliases[command] || command
case command
-when 'generate', 'destroy', 'plugin', 'benchmarker', 'profiler'
+when 'generate', 'destroy', 'plugin'
+ require APP_PATH
+ Rails.application.require_environment!
+
+ if defined?(ENGINE_PATH)
+ engine = Rails.application.railties.engines.find { |r| r.root.to_s == ENGINE_PATH }
+ Rails.application = engine
+ end
+ require "rails/commands/#{command}"
+
+when 'benchmarker', 'profiler'
require APP_PATH
Rails.application.require_environment!
require "rails/commands/#{command}"
@@ -23,8 +33,15 @@ when 'console'
Rails::Console.start(Rails.application)
when 'server'
+ # Change to the application's path if there is no config.ru file in current dir.
+ # This allows us to run script/rails server from other directories, but still get
+ # the main config.ru and properly set the tmp directory.
+ Dir.chdir(File.expand_path('../../', APP_PATH)) unless File.exists?(File.expand_path("config.ru"))
+
require 'rails/commands/server'
Rails::Server.new.tap { |server|
+ # We need to require application after the server sets environment,
+ # otherwise the --environment option given to the server won't propagate.
require APP_PATH
Dir.chdir(Rails.application.root)
server.start
diff --git a/railties/lib/rails/commands/plugin.rb b/railties/lib/rails/commands/plugin.rb
index 7bb4a1c054..8b2cd1bdba 100644
--- a/railties/lib/rails/commands/plugin.rb
+++ b/railties/lib/rails/commands/plugin.rb
@@ -58,7 +58,7 @@ class RailsEnvironment
else
plugin = name_uri_or_plugin
end
- unless plugin.nil?
+ if plugin
plugin.install
else
puts "Plugin not found: #{name_uri_or_plugin}"
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 0620b8608e..e9ce9610b8 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -17,7 +17,7 @@ module Rails
# In Rails versions before to 3.0, your gems automatically behaved as Engine, however
# this coupled Rails to Rubygems. Since Rails 3.0, if you want a gem to automatically
# behave as Engine, you have to specify an Engine for it somewhere inside your plugin
- # lib folder (similar with how we spceify a Railtie):
+ # lib folder (similar to how we specify a Railtie):
#
# # lib/my_engine.rb
# module MyEngine
@@ -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.
@@ -57,30 +77,30 @@ module Rails
# you need to do is:
#
# class MyEngine < Rails::Engine
- # paths.app.controllers = "lib/controllers"
+ # paths["app/controllers"] = "lib/controllers"
# end
#
# You can also have your controllers being loaded from both "app/controllers" and
# "lib/controllers":
#
# class MyEngine < Rails::Engine
- # paths.app.controllers << "lib/controllers"
+ # paths["app/controllers"] << "lib/controllers"
# end
#
# The available paths in an Engine are:
#
# class MyEngine < Rails::Engine
- # paths.app = "app"
- # paths.app.controllers = "app/controllers"
- # paths.app.helpers = "app/helpers"
- # paths.app.models = "app/models"
- # paths.app.views = "app/views"
- # paths.lib = "lib"
- # paths.lib.tasks = "lib/tasks"
- # paths.config = "config"
- # paths.config.initializers = "config/initializers"
- # paths.config.locales = "config/locales"
- # paths.config.routes = "config/routes.rb"
+ # paths["app"] #=> ["app"]
+ # paths["app/controllers"] #=> ["app/controllers"]
+ # paths["app/helpers"] #=> ["app/helpers"]
+ # paths["app/models"] #=> ["app/models"]
+ # paths["app/views"] #=> ["app/views"]
+ # paths["lib"] #=> ["lib"]
+ # paths["lib/tasks"] #=> ["lib/tasks"]
+ # paths["config"] #=> ["config"]
+ # paths["config/initializers"] #=> ["config/initializers"]
+ # paths["config/locales"] #=> ["config/locales"]
+ # paths["config/routes"] #=> ["config/routes.rb"]
# end
#
# Your Application class adds a couple more paths to this set. And as in your Application,
@@ -165,19 +185,21 @@ module Rails
# == Serving static files
#
# By default, rails use ActionDispatch::Static to serve static files in development mode. This is ok
- # while you develop your application, but when you want to deploy it, assets from engine will not be served.
+ # while you develop your application, but when you want to deploy it, assets from engine will not be
+ # served by default. You should choose one of the two following strategies:
#
- # You can fix it in one of two ways:
# * enable serving static files by setting config.serve_static_assets to true
# * symlink engines' public directories in application's public directory by running
- # `rake railties:create_symlinks`
+ # `rake ENGINE_NAME:install:assets`, where ENGINE_NAME is usually my_engine for the
+ # examples above
#
# == Engine name
#
# There are some places where engine's name is used.
+ #
# * routes: when you mount engine with mount(MyEngine::Engine => '/my_engine'), it's used as default :as option
- # * migrations: when you copy engine's migrations, they will be decorated with suffix based on engine_name, for example:
- # 2010010203121314_create_users.my_engine.rb
+ #
+ # * rake tasks: engines have a few rake tasks. They are usually under my_engine namespace.
#
# Engine name is set by default based on class name. For MyEngine::Engine it will be my_engine_engine.
# You can change it manually it manually using engine_name method:
@@ -190,15 +212,17 @@ module Rails
#
# == Namespaced Engine
#
- # Normally, when you create controllers, helpers and models inside engine, they are treated
- # as they would be created inside application. One of the cosequences of that is including
- # application's helpers and url_helpers inside controller. Sometimes, especially when your
- # engine provides its own routes, you don't want that. To isolate engine's stuff from application
- # you can use namespace method:
+ # Normally when you create controllers, helpers and models inside engine, they are treated
+ # as they were created inside the application. This means all applications helpers and named routes
+ # will be available to your engine controllers.
+ #
+ # However, sometimes you want to isolate your engine from the application, specially if your engine
+ # have its own router. To do that, you simply need to call +isolate_namespace+. This method requires
+ # you to pass a module where all your controllers, helpers and models should be nested to:
#
# module MyEngine
# class Engine < Rails::Engine
- # namespace MyEngine
+ # isolate_namespace MyEngine
# end
# end
#
@@ -215,15 +239,21 @@ module Rails
# url_helpers from MyEngine::Engine.routes.
#
# The next thing that changes in isolated engine is routes behaviour. Normally, when you namespace
- # your controllers, you need to use scope or namespace method in routes. With isolated engine,
- # the namespace is applied by default, so you can ignore it in routes. Further more, you don't need
- # to use longer url helpers like "my_engine_articles_path". As the prefix is not set you can just use
- # articles_path as you would normally do.
+ # your controllers, you also need to do namespace all your routes. With isolated engine,
+ # the namespace is applied by default, so you can ignore it in routes:
+ #
+ # MyEngine::Engine.routes.draw do
+ # resources :articles
+ # end
+ #
+ # The routes above will automatically point to MyEngine::ApplicationContoller. Further more, you don't
+ # need to use longer url helpers like "my_engine_articles_path". Instead, you shuold simply use
+ # articles_path as you would do with your application.
#
# To make that behaviour consistent with other parts of framework, isolated engine has influence also on
# ActiveModel::Naming. When you use namespaced model, like MyEngine::Article, it will normally
- # use the prefix "my_engine". In isolated engine, the prefix will be ommited in most of the places,
- # like url helpers or form fields.
+ # use the prefix "my_engine". In isolated engine, the prefix will be ommited in url helpers and
+ # form fields for convenience.
#
# polymorphic_url(MyEngine::Article.new) #=> "articles_path"
#
@@ -231,16 +261,15 @@ module Rails
# text_field :title #=> <input type="text" name="article[title]" id="article_title" />
# end
#
- #
- # Additionaly namespaced engine will set its name according to namespace, so in that case:
- # MyEngine::Engine.engine_name #=> "my_engine" and it will set MyEngine.table_name_prefix
- # to "my_engine_".
+ # Additionaly isolated engine will set its name according to namespace, so
+ # MyEngine::Engine.engine_name #=> "my_engine". It will also set MyEngine.table_name_prefix
+ # to "my_engine_", changing MyEngine::Article model to use my_engine_article table.
#
# == Using Engine's routes outside Engine
#
- # Since you can mount engine inside application's routes now, you do not have direct access to engine's
- # url_helpers inside application. When you mount Engine in application's routes special helper is
- # created to allow doing that. Consider such scenario:
+ # Since now you can mount engine inside application's routes, you do not have direct access to engine's
+ # url_helpers inside application. When you mount Engine in application's routes, a special helper is
+ # created to allow you to do that. Consider such scenario:
#
# # APP/config/routes.rb
# MyApplication::Application.routes.draw do
@@ -248,7 +277,7 @@ module Rails
# match "/foo" => "foo#index"
# end
#
- # Now, you can use my_engine helper:
+ # Now, you can use my_engine helper inside your application:
#
# class FooController < ApplicationController
# def index
@@ -256,24 +285,27 @@ module Rails
# end
# end
#
- # There is also 'app' helper that gives you access to application's routes inside Engine:
+ # There is also 'main_app' helper that gives you access to application's routes inside Engine:
#
# module MyEngine
# class BarController
- # app.foo_path #=> /foo
+ # def index
+ # main_app.foo_path #=> /foo
+ # end
# end
# end
#
- # Note that :as option takes engine_name as default, so most of the time you can ommit it.
+ # Note that the :as option given to mount takes the engine_name as default, so most of the time
+ # you can simply ommit it.
#
- # If you want to generate url to engine's route using polymorphic_url, you can also use that helpers.
- #
- # Let's say that you want to create a form pointing to one of the engine's routes. All you need to do
- # is passing helper as the first element in array with attributes for url:
+ # Finally, if you want to generate url to engine's route using polymorphic_url, you also need
+ # to pass the engine helper. Let's say that you want to create a form pointing to one of the
+ # engine's routes. All you need to do is pass the helper as the first element in array with
+ # attributes for url:
#
# form_for([my_engine, @user])
#
- # This code will use my_engine.user_path(@user) to generate proper route.
+ # This code will use my_engine.user_path(@user) to generate the proper route.
#
# == Migrations & seed data
#
@@ -283,7 +315,7 @@ module Rails
# To use engine's migrations in application you can use rake task, which copies them to
# application's dir:
#
- # rake railties:copy_migrations
+ # rake ENGINE_NAME:install:migrations
#
# If your engine has migrations, you may also want to prepare data for the database in
# seeds.rb file. You can load that data using load_seed method, e.g.
@@ -295,7 +327,7 @@ module Rails
autoload :Configuration, "rails/engine/configuration"
class << self
- attr_accessor :called_from, :namespaced
+ attr_accessor :called_from, :isolated
alias :engine_name :railtie_name
def inherited(base)
@@ -330,37 +362,38 @@ module Rails
@endpoint
end
- def namespace(mod)
+ def isolate_namespace(mod)
engine_name(generate_railtie_name(mod))
- _railtie = self
name = engine_name
- mod.singleton_class.instance_eval do
- define_method(:_railtie) do
- _railtie
- end
-
- define_method(:table_name_prefix) do
- "#{name}_"
- end
- end
-
self.routes.default_scope = {:module => name}
-
- self.namespaced = true
+ self.isolated = true
+
+ unless mod.respond_to?(:_railtie)
+ _railtie = self
+ mod.singleton_class.instance_eval do
+ define_method(:_railtie) do
+ _railtie
+ end
+
+ define_method(:table_name_prefix) do
+ "#{name}_"
+ end
+ end
+ end
end
- def namespaced?
- !!namespaced
+ def isolated?
+ !!isolated
end
end
delegate :middleware, :root, :paths, :to => :config
- delegate :engine_name, :namespaced?, :to => "self.class"
+ delegate :engine_name, :isolated?, :to => "self.class"
def load_tasks
super
- config.paths.lib.tasks.to_a.sort.each { |ext| load(ext) }
+ paths["lib/tasks"].existent.sort.each { |ext| load(ext) }
end
def eager_load!
@@ -400,6 +433,8 @@ module Rails
def routes
@routes ||= ActionDispatch::Routing::RouteSet.new
+ @routes.append(&Proc.new) if block_given?
+ @routes
end
def initializers
@@ -418,7 +453,7 @@ module Rails
#
# Blog::Engine.load_seed
def load_seed
- seed_file = config.paths.db.seeds.to_a.first
+ seed_file = paths["db/seeds"].existent.first
load(seed_file) if File.exist?(seed_file)
end
@@ -446,19 +481,22 @@ module Rails
end
initializer :add_routing_paths do |app|
- paths.config.routes.to_a.each do |route|
- app.routes_reloader.paths.unshift(route) if File.exists?(route)
+ paths = self.paths["config/routes"].existent
+
+ if routes? || paths.any?
+ app.routes_reloader.paths.unshift(*paths)
+ app.routes_reloader.route_sets << routes
end
end
# I18n load paths are a special case since the ones added
# later have higher priority.
initializer :add_locales do
- config.i18n.railties_load_path.concat(paths.config.locales.to_a)
+ config.i18n.railties_load_path.concat(paths["config/locales"].existent)
end
initializer :add_view_paths do
- views = paths.app.views.to_a
+ views = paths["app/views"].existent
unless views.empty?
ActiveSupport.on_load(:action_controller){ prepend_view_path(views) }
ActiveSupport.on_load(:action_mailer){ prepend_view_path(views) }
@@ -466,28 +504,27 @@ module Rails
end
initializer :load_environment_config, :before => :load_environment_hook do
- environment = config.paths.config.environments.to_a.first
+ environment = paths["config/environments"].existent.first
require environment if environment
end
initializer :append_asset_paths do
config.asset_path ||= "/#{engine_name}%s"
- public_path = config.paths.public.to_a.first
+ public_path = paths["public"].first
if config.compiled_asset_path && File.exist?(public_path)
config.static_asset_paths[config.compiled_asset_path] = public_path
end
end
- initializer :prepend_helpers_path do
- unless namespaced?
- config.helpers_paths = [] unless config.respond_to?(:helpers_paths)
- config.helpers_paths = config.paths.app.helpers.to_a + config.helpers_paths
+ initializer :prepend_helpers_path do |app|
+ if !isolated? || (app == self)
+ app.config.helpers_paths.unshift(*paths["app/helpers"].existent)
end
end
initializer :load_config_initializers do
- paths.config.initializers.to_a.sort.each do |initializer|
+ config.paths["config/initializers"].existent.sort.each do |initializer|
load(initializer)
end
end
@@ -497,7 +534,27 @@ module Rails
# consistently executed after all the initializers above across all engines.
end
+ rake_tasks do
+ next if self.is_a?(Rails::Application)
+
+ namespace railtie_name do
+ namespace :install do
+ # TODO Add assets copying to this list
+ # TODO Skip this if there is no paths["db/migrate"] for the engine
+ desc "Copy migrations from #{railtie_name} to application"
+ task :migrations do
+ ENV["FROM"] = railtie_name
+ Rake::Task["railties:install:migrations"].invoke
+ end
+ end
+ end
+ end
+
protected
+ def routes?
+ defined?(@routes)
+ end
+
def find_root_with_flag(flag, default=nil)
root_path = self.class.called_from
@@ -509,7 +566,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..7a07dcad7d 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -4,40 +4,61 @@ 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)
- paths.app "app", :eager_load => true, :glob => "*"
- paths.app.controllers "app/controllers", :eager_load => true
- paths.app.helpers "app/helpers", :eager_load => true
- paths.app.models "app/models", :eager_load => true
- paths.app.mailers "app/mailers", :eager_load => true
- paths.app.views "app/views"
- paths.lib "lib", :load_path => true
- paths.lib.tasks "lib/tasks", :glob => "**/*.rake"
- paths.config "config"
- paths.config.initializers "config/initializers", :glob => "**/*.rb"
- paths.config.locales "config/locales", :glob => "*.{rb,yml}"
- paths.config.routes "config/routes.rb"
- paths.config.environments "config/environments", :glob => "#{Rails.env}.rb"
- paths.public "public"
- paths.public.javascripts "public/javascripts"
- paths.public.stylesheets "public/stylesheets"
- paths.vendor "vendor", :load_path => true
- paths.vendor.plugins "vendor/plugins"
- paths.db "db"
- paths.db.migrate "db/migrate"
- paths.db.seeds "db/seeds.rb"
+ paths.add "app", :eager_load => true, :glob => "*"
+ paths.add "app/controllers", :eager_load => true
+ paths.add "app/helpers", :eager_load => true
+ paths.add "app/models", :eager_load => true
+ paths.add "app/mailers", :eager_load => true
+ paths.add "app/views"
+ paths.add "lib", :load_path => true
+ paths.add "lib/tasks", :glob => "**/*.rake"
+ paths.add "config"
+ paths.add "config/environments", :glob => "#{Rails.env}.rb"
+ paths.add "config/initializers", :glob => "**/*.rb"
+ paths.add "config/locales", :glob => "*.{rb,yml}"
+ paths.add "config/routes", :with => "config/routes.rb"
+ paths.add "db"
+ paths.add "db/migrate"
+ paths.add "db/seeds", :with => "db/seeds.rb"
+ paths.add "public"
+ paths.add "public/javascripts"
+ paths.add "public/stylesheets"
+ paths.add "vendor", :load_path => true
+ paths.add "vendor/plugins"
paths
end
end
diff --git a/railties/lib/rails/engine/railties.rb b/railties/lib/rails/engine/railties.rb
index 389a7602c6..e91bdbf1e5 100644
--- a/railties/lib/rails/engine/railties.rb
+++ b/railties/lib/rails/engine/railties.rb
@@ -15,7 +15,7 @@ module Rails
def plugins
@plugins ||= begin
plugin_names = (@config.plugins || [:all]).map { |p| p.to_sym }
- Plugin.all(plugin_names, @config.paths.vendor.plugins)
+ Plugin.all(plugin_names, @config.paths["vendor/plugins"].existent)
end
end
end
diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb
index 8f0d5ffff4..378c07cb0e 100644
--- a/railties/lib/rails/generators/actions.rb
+++ b/railties/lib/rails/generators/actions.rb
@@ -187,8 +187,8 @@ module Rails
# initializer("globals.rb") do
# data = ""
#
- # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do
- # data << "#{const} = :entp"
+ # ['MY_WORK', 'ADMINS', 'BEST_COMPANY_EVAR'].each do |const|
+ # data << "#{const} = :entp\n"
# end
#
# data
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/generators/rails/app/templates/config/databases/oracle.yml b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml
index fddf8b8144..b661a60389 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml
+++ b/railties/lib/rails/generators/rails/app/templates/config/databases/oracle.yml
@@ -10,7 +10,7 @@
#
# By default prefetch_rows (OCI_ATTR_PREFETCH_ROWS) is set to 100. And
# until true bind variables are supported, cursor_sharing is set by default
-# to 'similar'. Both can be changed in the configation below; the defaults
+# to 'similar'. Both can be changed in the configuration below; the defaults
# are equivalent to specifying:
#
# prefetch_rows: 100
diff --git a/railties/lib/rails/paths.rb b/railties/lib/rails/paths.rb
index d303212f52..8570fc7b3f 100644
--- a/railties/lib/rails/paths.rb
+++ b/railties/lib/rails/paths.rb
@@ -2,15 +2,17 @@ require 'set'
module Rails
module Paths
- module PathParent
- attr_reader :children
-
+ module PathParent #:nodoc:
def method_missing(id, *args)
- name = id.to_s
+ match = id.to_s.match(/^(.*)=$/)
+ full = [@current, $1 || id].compact.join("/")
+
+ ActiveSupport::Deprecation.warn 'config.paths.app.controller API is deprecated in ' <<
+ 'favor of config.paths["app/controller"] API.'
- if name =~ /^(.*)=$/ || args.any?
- @children[$1 || name] = Path.new(@root, *args)
- elsif path = @children[name]
+ if match || args.any?
+ @root[full] = Path.new(@root, full, *args)
+ elsif path = @root[full]
path
else
super
@@ -18,22 +20,72 @@ module Rails
end
end
- class Root
+ # This object is an extended hash that behaves as root of the Rails::Paths system.
+ # It allows you to collect information about how you want to structure your application
+ # paths by a Hash like API. It requires you to give a physical path on initialization.
+ #
+ # root = Root.new
+ # root.add "app/controllers", :eager_load => true
+ #
+ # The command above creates a new root object and add "app/controllers" as a path.
+ # This means we can get a Path object back like below:
+ #
+ # path = root["app/controllers"]
+ # path.eager_load? #=> true
+ # path.is_a?(Rails::Paths::Path) #=> true
+ #
+ # The Path object is simply an array and allows you to easily add extra paths:
+ #
+ # path.is_a?(Array) #=> true
+ # path.inspect #=> ["app/controllers"]
+ #
+ # path << "lib/controllers"
+ # path.inspect #=> ["app/controllers", "lib/controllers"]
+ #
+ # Notice that when you add a path using #add, the path object created already
+ # contains the path with the same path value given to #add. In some situations,
+ # you may not want this behavior, so you can give :with as option.
+ #
+ # root.add "config/routes", :with => "config/routes.rb"
+ # root["config/routes"].inspect #=> ["config/routes.rb"]
+ #
+ # #add also accepts the following options as argument: eager_load, autoload,
+ # autoload_once and glob.
+ #
+ # Finally, the Path object also provides a few helpers:
+ #
+ # root = Root.new
+ # root.path = "/rails"
+ # root.add "app/controllers"
+ #
+ # root["app/controllers"].expanded #=> ["/rails/app/controllers"]
+ # root["app/controllers"].existent #=> ["/rails/app/controllers"]
+ #
+ # Check the Path documentation for more information.
+ class Root < ::Hash
include PathParent
-
attr_accessor :path
def initialize(path)
raise if path.is_a?(Array)
- @children = {}
+ @current = nil
@path = path
@root = self
- @all_paths = []
+ super()
+ end
+
+ def []=(path, value)
+ value = Path.new(self, path, value) unless value.is_a?(Path)
+ super(path, value)
+ end
+
+ def add(path, options={})
+ with = options[:with] || path
+ self[path] = Path.new(self, path, with, options)
end
def all_paths
- @all_paths.uniq!
- @all_paths
+ values.tap { |v| v.uniq! }
end
def autoload_once
@@ -52,68 +104,54 @@ module Rails
filter_by(:load_path?)
end
- def push(*)
- raise "Application root can only have one physical path"
- end
-
- alias unshift push
- alias << push
- alias concat push
-
protected
def filter_by(constraint)
all = []
all_paths.each do |path|
if path.send(constraint)
- paths = path.paths
- paths -= path.children.values.map { |p| p.send(constraint) ? [] : p.paths }.flatten
+ paths = path.existent
+ paths -= path.children.map { |p| p.send(constraint) ? [] : p.existent }.flatten
all.concat(paths)
end
end
all.uniq!
- all.reject! { |p| !File.exists?(p) }
all
end
end
- class Path
- include PathParent, Enumerable
+ class Path < Array
+ include PathParent
attr_reader :path
attr_accessor :glob
- def initialize(root, *paths)
- options = paths.last.is_a?(::Hash) ? paths.pop : {}
- @children = {}
+ def initialize(root, current, *paths)
+ options = paths.last.is_a?(::Hash) ? paths.pop : {}
+ super(paths.flatten)
+
+ @current = current
@root = root
- @paths = paths.flatten
@glob = options[:glob]
autoload_once! if options[:autoload_once]
eager_load! if options[:eager_load]
autoload! if options[:autoload]
load_path! if options[:load_path]
-
- @root.all_paths << self
- end
-
- def each
- to_a.each { |p| yield p }
end
- def push(path)
- @paths.push path
+ def children
+ keys = @root.keys.select { |k| k.include?(@current) }
+ keys.delete(@current)
+ @root.values_at(*keys.sort)
end
- alias << push
-
- def unshift(path)
- @paths.unshift path
+ def first
+ expanded.first
end
- def concat(paths)
- @paths.concat paths
+ def last
+ expanded.last
end
%w(autoload_once eager_load autoload load_path).each do |m|
@@ -132,20 +170,36 @@ module Rails
RUBY
end
- def paths
+ # Expands all paths against the root and return all unique values.
+ def expanded
raise "You need to set a path root" unless @root.path
+ result = []
- result = @paths.map do |p|
+ each do |p|
path = File.expand_path(p, @root.path)
- @glob ? Dir[File.join(path, @glob)] : path
+
+ if @glob
+ result.concat Dir[File.join(path, @glob)]
+ else
+ result << path
+ end
end
- result.flatten!
result.uniq!
result
end
- alias to_a paths
+ # Returns all expanded paths but only if they exist in the filesystem.
+ def existent
+ expanded.select { |f| File.exists?(f) }
+ end
+
+ def paths
+ ActiveSupport::Deprecation.warn "paths is deprecated. Please call expand instead."
+ expanded
+ end
+
+ alias to_a expanded
end
end
-end \ No newline at end of file
+end
diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb
index 5614624673..ceddd25eaa 100644
--- a/railties/lib/rails/plugin.rb
+++ b/railties/lib/rails/plugin.rb
@@ -62,13 +62,13 @@ module Rails
end
initializer :handle_lib_autoload, :before => :set_load_path do |app|
- paths = if app.config.reload_plugins
+ autoload = if app.config.reload_plugins
config.autoload_paths
else
config.autoload_once_paths
end
- paths.concat config.paths.lib.to_a
+ autoload.concat paths["lib"].existent
end
initializer :load_init_rb, :before => :load_config_initializers do |app|
@@ -83,7 +83,7 @@ module Rails
initializer :sanity_check_railties_collision do
if Engine.subclasses.map { |k| k.root.to_s }.include?(root.to_s)
- raise "\"#{name}\" is a Railtie/Engine and cannot be installed as plugin"
+ raise "\"#{name}\" is a Railtie/Engine and cannot be installed as a plugin"
end
end
end
diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb
index 09650789ac..c76bc83377 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
@@ -191,6 +191,13 @@ module Rails
def load_tasks
self.class.rake_tasks.each(&:call)
+
+ # load also tasks from all superclasses
+ klass = self.class.superclass
+ while klass.respond_to?(:rake_tasks)
+ klass.rake_tasks.each { |t| self.instance_exec(&t) }
+ klass = klass.superclass
+ end
end
def load_generators
diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb
index f09e3940cc..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.
@@ -17,25 +16,19 @@ module Rails
@@app_middleware ||= Rails::Configuration::MiddlewareStackProxy.new
end
- # Holds generators configuration:
+ # This allows you to modify application's generators from Railties.
#
- # 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
+ # Values set on app_generators will become defaults for applicaiton, unless
+ # application overwrites them.
+ def app_generators
+ @@app_generators ||= Rails::Configuration::Generators.new
+ yield(@@app_generators) if block_given?
+ @@app_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)
@@ -70,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/lib/rails/tasks/documentation.rake b/railties/lib/rails/tasks/documentation.rake
index 7aefbb49b0..edd716d7d0 100644
--- a/railties/lib/rails/tasks/documentation.rake
+++ b/railties/lib/rails/tasks/documentation.rake
@@ -47,7 +47,7 @@ namespace :doc do
rdoc.rdoc_files.include('app/**/*.rb')
rdoc.rdoc_files.include('lib/**/*.rb')
}
- Rake::Task['doc:app'].comment = "Generate docs for the app -- also availble doc:rails, doc:guides, doc:plugins (options: TEMPLATE=/rdoc-template.rb, TITLE=\"Custom Title\")"
+ Rake::Task['doc:app'].comment = "Generate docs for the app -- also available doc:rails, doc:guides, doc:plugins (options: TEMPLATE=/rdoc-template.rb, TITLE=\"Custom Title\")"
# desc 'Generate documentation for the Rails framework.'
RDocTaskWithoutDescriptions.new("rails") { |rdoc|
diff --git a/railties/lib/rails/tasks/railties.rake b/railties/lib/rails/tasks/railties.rake
index 7cf31f84a0..e08bd9687d 100644
--- a/railties/lib/rails/tasks/railties.rake
+++ b/railties/lib/rails/tasks/railties.rake
@@ -1,8 +1,8 @@
namespace :railties do
- desc "Create symlinks to railties public directories in application's public directory."
+ # desc "Create symlinks to railties public directories in application's public directory."
task :create_symlinks => :environment do
paths = Rails.application.config.static_asset_paths.dup
- app_public_path = Rails.application.config.paths.public.to_a.first
+ app_public_path = Rails.application.paths["public"].first
paths.each do |mount_path, path|
symlink_path = File.join(app_public_path, mount_path)
diff --git a/railties/lib/rails/test_unit/railtie.rb b/railties/lib/rails/test_unit/railtie.rb
index e3fafc4b9d..2b6170ebfb 100644
--- a/railties/lib/rails/test_unit/railtie.rb
+++ b/railties/lib/rails/test_unit/railtie.rb
@@ -1,6 +1,6 @@
module Rails
class TestUnitRailtie < Rails::Railtie
- config.generators do |c|
+ config.app_generators do |c|
c.test_framework :test_unit, :fixture => true,
:fixture_replacement => nil
diff --git a/railties/lib/rails/test_unit/testing.rake b/railties/lib/rails/test_unit/testing.rake
index 713833f884..28dc40379b 100644
--- a/railties/lib/rails/test_unit/testing.rake
+++ b/railties/lib/rails/test_unit/testing.rake
@@ -73,7 +73,8 @@ end
desc 'Runs test:units, test:functionals, test:integration together (also available: test:benchmark, test:profile, test:plugins)'
task :test do
- errors = %w(test:units test:functionals test:integration).collect do |task|
+ tests_to_run = ENV['TEST'] ? ["test:single"] : %w(test:units test:functionals test:integration)
+ errors = tests_to_run.collect do |task|
begin
Rake::Task[task].invoke
nil
@@ -123,6 +124,10 @@ namespace :test do
end
Rake::Task['test:uncommitted'].comment = "Test changes since last checkin (only Subversion and Git)"
+ Rake::TestTask.new(:single => "test:prepare") do |t|
+ t.libs << "test"
+ end
+
TestTaskWithoutDescription.new(:units => "test:prepare") do |t|
t.libs << "test"
t.pattern = 'test/unit/**/*_test.rb'
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/configuration_test.rb b/railties/test/application/configuration_test.rb
index 7403d16cf4..b8d0854286 100644
--- a/railties/test/application/configuration_test.rb
+++ b/railties/test/application/configuration_test.rb
@@ -42,7 +42,7 @@ module ApplicationTests
test "Rails::Application responds to paths" do
require "#{app_path}/config/environment"
assert_respond_to AppTemplate::Application, :paths
- assert_equal AppTemplate::Application.paths.app.views.to_a, ["#{app_path}/app/views"]
+ assert_equal AppTemplate::Application.paths["app/views"].expanded, ["#{app_path}/app/views"]
end
test "the application root is set correctly" do
@@ -180,7 +180,7 @@ module ApplicationTests
test "config.paths.public sets Rails.public_path" do
add_to_config <<-RUBY
- config.paths.public = "somewhere"
+ config.paths["public"] = "somewhere"
RUBY
require "#{app_path}/config/application"
diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb
index 25b4a21902..d4159dd0fd 100644
--- a/railties/test/application/console_test.rb
+++ b/railties/test/application/console_test.rb
@@ -1,7 +1,7 @@
require 'isolation/abstract_unit'
class ConsoleTest < Test::Unit::TestCase
- include ActiveSupport::Testing::Isolation
+ include ActiveSupport::Testing::Isolation
def setup
build_app
@@ -14,16 +14,15 @@ class ConsoleTest < Test::Unit::TestCase
end
def test_app_method_should_return_integration_session
+ TestHelpers::Rack.send :remove_method, :app
load_environment
console_session = app
- assert_not_nil console_session
assert_instance_of ActionDispatch::Integration::Session, console_session
end
def test_new_session_should_return_integration_session
load_environment
session = new_session
- assert_not_nil session
assert_instance_of ActionDispatch::Integration::Session, session
end
diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb
index d258625f42..551e966c85 100644
--- a/railties/test/application/generators_test.rb
+++ b/railties/test/application/generators_test.rb
@@ -69,7 +69,7 @@ module ApplicationTests
assert_equal :rspec, Rails::Generators.options[:rails][:test_framework]
assert_equal "-w", Rails::Generators.aliases[:rails][:test_framework]
assert_equal Hash[:shoulda => :test_unit], Rails::Generators.fallbacks
- assert_equal ["#{app_path}/lib/templates", "some/where"], Rails::Generators.templates_path
+ assert_equal ["some/where"], Rails::Generators.templates_path
end
test "generators no color on initialization" do
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/paths_test.rb b/railties/test/application/paths_test.rb
index c98b11556b..b1ff6e9cb1 100644
--- a/railties/test/application/paths_test.rb
+++ b/railties/test/application/paths_test.rb
@@ -25,7 +25,7 @@ module ApplicationTests
end
def assert_path(paths, *dir)
- assert_equal [root(*dir)], paths.paths
+ assert_equal [root(*dir)], paths.expanded
end
def assert_in_load_path(*path)
@@ -37,20 +37,20 @@ module ApplicationTests
end
test "booting up Rails yields a valid paths object" do
- assert_path @paths.app.models, "app", "models"
- assert_path @paths.app.helpers, "app", "helpers"
- assert_path @paths.app.views, "app", "views"
- assert_path @paths.lib, "lib"
- assert_path @paths.vendor, "vendor"
- assert_path @paths.vendor.plugins, "vendor", "plugins"
- assert_path @paths.tmp, "tmp"
- assert_path @paths.tmp.cache, "tmp", "cache"
- assert_path @paths.config, "config"
- assert_path @paths.config.locales, "config", "locales", "en.yml"
- assert_path @paths.config.environment, "config", "environment.rb"
- assert_path @paths.config.environments, "config", "environments", "development.rb"
+ assert_path @paths["app/models"], "app/models"
+ assert_path @paths["app/helpers"], "app/helpers"
+ assert_path @paths["app/views"], "app/views"
+ assert_path @paths["lib"], "lib"
+ assert_path @paths["vendor"], "vendor"
+ assert_path @paths["vendor/plugins"], "vendor/plugins"
+ assert_path @paths["tmp"], "tmp"
+ assert_path @paths["tmp/cache"], "tmp/cache"
+ assert_path @paths["config"], "config"
+ assert_path @paths["config/locales"], "config/locales/en.yml"
+ assert_path @paths["config/environment"], "config/environment.rb"
+ assert_path @paths["config/environments"], "config/environments/development.rb"
- assert_equal root("app", "controllers"), @paths.app.controllers.to_a.first
+ assert_equal root("app", "controllers"), @paths["app/controllers"].expanded.first
end
test "booting up Rails yields a list of paths that are eager" do
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/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 3653b067c8..51277b805b 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -165,9 +165,13 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "Gemfile", /^gem\s+["']mysql2["']$/
end
- def test_config_database_is_not_added_if_skip_active_record_is_given
+ def test_generator_if_skip_active_record_is_given
run_generator [destination_root, "--skip-active-record"]
assert_no_file "config/database.yml"
+ assert_file "test/test_helper.rb" do |helper_content|
+ assert_no_match /fixtures :all/, helper_content
+ end
+ assert_file "test/performance/browsing_test.rb"
end
def test_active_record_is_removed_from_frameworks_if_skip_active_record_is_given
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/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb
index 38b95a49ac..d1190fd17d 100644
--- a/railties/test/generators/namespaced_generators_test.rb
+++ b/railties/test/generators/namespaced_generators_test.rb
@@ -6,14 +6,14 @@ require 'rails/generators/mailer/mailer_generator'
class NamespacedGeneratorTestCase < Rails::Generators::TestCase
def setup
- TestApp::Application.namespace(TestApp)
+ TestApp::Application.isolate_namespace(TestApp)
end
def teardown
if TestApp.respond_to?(:_railtie)
TestApp.singleton_class.send(:undef_method, :_railtie)
TestApp.singleton_class.send(:undef_method, :table_name_prefix)
- TestApp::Application.namespaced = false
+ TestApp::Application.isolated = false
end
end
end
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/paths_test.rb b/railties/test/paths_test.rb
index 80fae8c543..6e4e3446b3 100644
--- a/railties/test/paths_test.rb
+++ b/railties/test/paths_test.rb
@@ -20,234 +20,264 @@ class PathsTest < ActiveSupport::TestCase
test "a paths object initialized with nil can be updated" do
root = Rails::Paths::Root.new(nil)
- root.app = "app"
+ root.add "app"
root.path = "/root"
- assert_equal ["/root/app"], root.app.to_a
+ assert_equal ["app"], root["app"]
+ assert_equal ["/root/app"], root["app"].to_a
end
test "creating a root level path" do
- @root.app = "/foo/bar"
- assert_equal ["/foo/bar"], @root.app.to_a
+ @root.add "app"
+ assert_equal ["/foo/bar/app"], @root["app"].to_a
+ end
+
+ test "creating a root level path with options" do
+ @root.add "app", :with => "/foo/bar"
+ assert_equal ["/foo/bar"], @root["app"].to_a
end
test "raises exception if root path never set" do
root = Rails::Paths::Root.new(nil)
- root.app = "app"
+ root.add "app"
assert_raises RuntimeError do
- root.app.to_a
+ root["app"].to_a
end
end
- test "creating a root level path without assignment" do
- @root.app "/foo/bar"
- assert_equal ["/foo/bar"], @root.app.to_a
- end
-
- test "trying to access a path that does not exist raises NoMethodError" do
- assert_raises(NoMethodError) { @root.app }
- end
-
- test "relative paths are relative to the paths root" do
- @root.app = "app"
- assert_equal ["/foo/bar/app"], @root.app.to_a
- end
-
- test "relative paths are relative to the paths root without assignment" do
- @root.app "app"
- assert_equal ["/foo/bar/app"], @root.app.to_a
- end
-
test "creating a child level path" do
- @root.app = "/foo/bar"
- @root.app.models = "/foo/bar/baz"
- assert_equal ["/foo/bar/baz"], @root.app.models.to_a
+ @root.add "app"
+ @root.add "app/models"
+ assert_equal ["/foo/bar/app/models"], @root["app/models"].to_a
end
- test "creating a child level path without assignment" do
- @root.app = "/foo/bar"
- @root.app.models "/foo/bar/baz"
- assert_equal ["/foo/bar/baz"], @root.app.models.to_a
+ test "creating a child level path with option" do
+ @root.add "app"
+ @root.add "app/models", :with => "/foo/bar/baz"
+ assert_equal ["/foo/bar/baz"], @root["app/models"].to_a
end
test "child level paths are relative from the root" do
- @root.app = "/app"
- @root.app.models = "baz"
-
- assert_equal ["/foo/bar/baz"], @root.app.models.to_a
+ @root.add "app"
+ @root.add "app/models", :with => "baz"
+ assert_equal ["/foo/bar/baz"], @root["app/models"].to_a
end
test "adding multiple physical paths as an array" do
- @root.app = ["/app", "/app2"]
- assert_equal ["/app", "/app2"], @root.app.to_a
- end
-
- test "adding multiple physical paths as an array without assignment" do
- @root.app "/app", "/app2"
- assert_equal ["/app", "/app2"], @root.app.to_a
+ @root.add "app", :with => ["/app", "/app2"]
+ assert_equal ["/app", "/app2"], @root["app"].to_a
end
test "adding multiple physical paths using #push" do
- @root.app = "/app"
- @root.app.push "/app2"
- assert_equal ["/app", "/app2"], @root.app.to_a
+ @root.add "app"
+ @root["app"].push "app2"
+ assert_equal ["/foo/bar/app", "/foo/bar/app2"], @root["app"].to_a
end
test "adding multiple physical paths using <<" do
- @root.app = "/app"
- @root.app << "/app2"
- assert_equal ["/app", "/app2"], @root.app.to_a
+ @root.add "app"
+ @root["app"] << "app2"
+ assert_equal ["/foo/bar/app", "/foo/bar/app2"], @root["app"].to_a
end
test "adding multiple physical paths using concat" do
- @root.app = "/app"
- @root.app.concat ["/app2", "/app3"]
- assert_equal ["/app", "/app2", "/app3"], @root.app.to_a
+ @root.add "app"
+ @root["app"].concat ["app2", "/app3"]
+ assert_equal ["/foo/bar/app", "/foo/bar/app2", "/app3"], @root["app"].to_a
end
test "adding multiple physical paths using #unshift" do
- @root.app = "/app"
- @root.app.unshift "/app2"
- assert_equal ["/app2", "/app"], @root.app.to_a
+ @root.add "app"
+ @root["app"].unshift "app2"
+ assert_equal ["/foo/bar/app2", "/foo/bar/app"], @root["app"].to_a
end
test "the root can only have one physical path" do
assert_raise(RuntimeError) { Rails::Paths::Root.new(["/fiz", "/biz"]) }
- assert_raise(RuntimeError) { @root.push "/biz" }
- assert_raise(RuntimeError) { @root.unshift "/biz" }
- assert_raise(RuntimeError) { @root.concat ["/biz"]}
- assert_raise(RuntimeError) { @root << "/biz" }
end
test "it is possible to add a path that should be autoloaded only once" do
- @root.app = "/app"
- @root.app.autoload_once!
- assert @root.app.autoload_once?
- assert @root.autoload_once.include?(@root.app.paths.first)
+ @root.add "app", :with => "/app"
+ @root["app"].autoload_once!
+ assert @root["app"].autoload_once?
+ assert @root.autoload_once.include?(@root["app"].expanded.first)
end
test "it is possible to remove a path that should be autoloaded only once" do
- @root.app = "/app"
- @root.app.autoload_once!
- assert @root.app.autoload_once?
+ @root["app"] = "/app"
+ @root["app"].autoload_once!
+ assert @root["app"].autoload_once?
- @root.app.skip_autoload_once!
- assert !@root.app.autoload_once?
- assert !@root.autoload_once.include?(@root.app.paths.first)
+ @root["app"].skip_autoload_once!
+ assert !@root["app"].autoload_once?
+ assert !@root.autoload_once.include?(@root["app"].expanded.first)
end
test "it is possible to add a path without assignment and specify it should be loaded only once" do
- @root.app "/app", :autoload_once => true
- assert @root.app.autoload_once?
+ @root.add "app", :with => "/app", :autoload_once => true
+ assert @root["app"].autoload_once?
assert @root.autoload_once.include?("/app")
end
test "it is possible to add multiple paths without assignment and specify it should be loaded only once" do
- @root.app "/app", "/app2", :autoload_once => true
- assert @root.app.autoload_once?
+ @root.add "app", :with => ["/app", "/app2"], :autoload_once => true
+ assert @root["app"].autoload_once?
assert @root.autoload_once.include?("/app")
assert @root.autoload_once.include?("/app2")
end
test "making a path autoload_once more than once only includes it once in @root.load_once" do
- @root.app = "/app"
- @root.app.autoload_once!
- @root.app.autoload_once!
- assert_equal 1, @root.autoload_once.select {|p| p == @root.app.paths.first }.size
+ @root["app"] = "/app"
+ @root["app"].autoload_once!
+ @root["app"].autoload_once!
+ assert_equal 1, @root.autoload_once.select {|p| p == @root["app"].expanded.first }.size
end
test "paths added to a load_once path should be added to the autoload_once collection" do
- @root.app = "/app"
- @root.app.autoload_once!
- @root.app << "/app2"
+ @root["app"] = "/app"
+ @root["app"].autoload_once!
+ @root["app"] << "/app2"
assert_equal 2, @root.autoload_once.size
end
test "it is possible to mark a path as eager loaded" do
- @root.app = "/app"
- @root.app.eager_load!
- assert @root.app.eager_load?
- assert @root.eager_load.include?(@root.app.paths.first)
+ @root["app"] = "/app"
+ @root["app"].eager_load!
+ assert @root["app"].eager_load?
+ assert @root.eager_load.include?(@root["app"].to_a.first)
end
test "it is possible to skip a path from eager loading" do
- @root.app = "/app"
- @root.app.eager_load!
- assert @root.app.eager_load?
+ @root["app"] = "/app"
+ @root["app"].eager_load!
+ assert @root["app"].eager_load?
- @root.app.skip_eager_load!
- assert !@root.app.eager_load?
- assert !@root.eager_load.include?(@root.app.paths.first)
+ @root["app"].skip_eager_load!
+ assert !@root["app"].eager_load?
+ assert !@root.eager_load.include?(@root["app"].to_a.first)
end
test "it is possible to add a path without assignment and mark it as eager" do
- @root.app "/app", :eager_load => true
- assert @root.app.eager_load?
+ @root.add "app", :with => "/app", :eager_load => true
+ assert @root["app"].eager_load?
assert @root.eager_load.include?("/app")
end
test "it is possible to add multiple paths without assignment and mark them as eager" do
- @root.app "/app", "/app2", :eager_load => true
- assert @root.app.eager_load?
+ @root.add "app", :with => ["/app", "/app2"], :eager_load => true
+ assert @root["app"].eager_load?
assert @root.eager_load.include?("/app")
assert @root.eager_load.include?("/app2")
end
test "it is possible to create a path without assignment and mark it both as eager and load once" do
- @root.app "/app", :eager_load => true, :autoload_once => true
- assert @root.app.eager_load?
- assert @root.app.autoload_once?
+ @root.add "app", :with => "/app", :eager_load => true, :autoload_once => true
+ assert @root["app"].eager_load?
+ assert @root["app"].autoload_once?
assert @root.eager_load.include?("/app")
assert @root.autoload_once.include?("/app")
end
test "making a path eager more than once only includes it once in @root.eager_paths" do
- @root.app = "/app"
- @root.app.eager_load!
- @root.app.eager_load!
- assert_equal 1, @root.eager_load.select {|p| p == @root.app.paths.first }.size
+ @root["app"] = "/app"
+ @root["app"].eager_load!
+ @root["app"].eager_load!
+ assert_equal 1, @root.eager_load.select {|p| p == @root["app"].expanded.first }.size
end
test "paths added to a eager_load path should be added to the eager_load collection" do
- @root.app = "/app"
- @root.app.eager_load!
- @root.app << "/app2"
+ @root["app"] = "/app"
+ @root["app"].eager_load!
+ @root["app"] << "/app2"
assert_equal 2, @root.eager_load.size
end
test "it should be possible to add a path's default glob" do
- @root.app = "/app"
- @root.app.glob = "*.rb"
- assert_equal "*.rb", @root.app.glob
+ @root["app"] = "/app"
+ @root["app"].glob = "*.rb"
+ assert_equal "*.rb", @root["app"].glob
end
test "it should be possible to override a path's default glob without assignment" do
- @root.app "/app", :glob => "*.rb"
- assert_equal "*.rb", @root.app.glob
+ @root.add "app", :with => "/app", :glob => "*.rb"
+ assert_equal "*.rb", @root["app"].glob
end
test "a path can be added to the load path" do
- @root.app = "app"
- @root.app.load_path!
- @root.app.models = "app/models"
+ @root["app"] = "app"
+ @root["app"].load_path!
+ @root["app/models"] = "app/models"
assert_equal ["/foo/bar/app"], @root.load_paths
end
test "a path can be added to the load path on creation" do
- @root.app "/app", :load_path => true
- assert @root.app.load_path?
+ @root.add "app", :with => "/app", :load_path => true
+ assert @root["app"].load_path?
assert_equal ["/app"], @root.load_paths
end
test "a path can be marked as autoload path" do
- @root.app = "app"
- @root.app.autoload!
- @root.app.models = "app/models"
+ @root["app"] = "app"
+ @root["app"].autoload!
+ @root["app/models"] = "app/models"
assert_equal ["/foo/bar/app"], @root.autoload_paths
end
test "a path can be marked as autoload on creation" do
- @root.app "/app", :autoload => true
- assert @root.app.autoload?
+ @root.add "app", :with => "/app", :autoload => true
+ assert @root["app"].autoload?
assert_equal ["/app"], @root.autoload_paths
end
+
+ # Deprecated API tests
+
+ test "reading a root level path with assignment" do
+ @root.add "app"
+ assert_deprecated do
+ assert_equal ["/foo/bar/app"], @root.app.to_a
+ end
+ end
+
+ test "creating a root level path with assignment" do
+ assert_deprecated do
+ @root.app = "/foo/bar"
+ end
+ assert_equal ["/foo/bar"], @root["app"].to_a
+ end
+
+ test "creating a root level path without assignment" do
+ assert_deprecated do
+ @root.app "/foo/bar"
+ end
+ assert_equal ["/foo/bar"], @root["app"].to_a
+ end
+
+ test "reading a nested level path with assignment" do
+ @root.add "app"
+ @root.add "app/model"
+ assert_deprecated do
+ assert_equal ["/foo/bar/app/model"], @root.app.model.to_a
+ end
+ end
+
+ test "creating a nested level path with assignment" do
+ @root.add "app"
+ assert_deprecated do
+ @root.app.model = "/foo/bar"
+ end
+ assert_equal ["/foo/bar"], @root["app/model"].to_a
+ end
+
+ test "creating a nested level path without assignment" do
+ @root.add "app"
+ assert_deprecated do
+ @root.app.model "/foo/bar"
+ end
+ assert_equal ["/foo/bar"], @root["app/model"].to_a
+ end
+
+ test "trying to access a path that does not exist raises NoMethodError" do
+ assert_deprecated do
+ assert_raises(NoMethodError) { @root.app }
+ end
+ end
end
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index a9dd7d4c1b..db74e41472 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -62,17 +62,18 @@ module RailtiesTest
def call(env)
response = @app.call(env)
- response[2].upcase!
+ response[2].each { |b| b.upcase! }
response
end
end
test "engine is a rack app and can have his own middleware stack" do
+ add_to_config("config.action_dispatch.show_exceptions = false")
+
@plugin.write "lib/bukkits.rb", <<-RUBY
class Bukkits
class Engine < ::Rails::Engine
- endpoint lambda { |env| [200, {'Content-Type' => 'text/html'}, 'Hello World'] }
-
+ endpoint lambda { |env| [200, {'Content-Type' => 'text/html'}, ['Hello World']] }
config.middleware.use ::RailtiesTest::EngineTest::Upcaser
end
end
@@ -89,7 +90,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
@@ -102,7 +103,7 @@ module RailtiesTest
@plugin.write "config/routes.rb", <<-RUBY
Bukkits::Engine.routes.draw do
- match "/foo" => lambda { |env| [200, {'Content-Type' => 'text/html'}, 'foo'] }
+ match "/foo" => lambda { |env| [200, {'Content-Type' => 'text/html'}, ['foo']] }
end
RUBY
@@ -116,8 +117,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,22 +379,22 @@ 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
@plugin.write "lib/bukkits.rb", <<-RUBY
module Bukkits
class Engine < ::Rails::Engine
- namespace Bukkits
+ isolate_namespace Bukkits
end
end
RUBY
@@ -488,30 +488,30 @@ 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
@plugin.write "lib/bukkits.rb", <<-RUBY
module Bukkits
class Engine < ::Rails::Engine
- namespace Bukkits
+ isolate_namespace Bukkits
end
end
RUBY
@@ -553,11 +553,11 @@ module RailtiesTest
end
RUBY
- @plugin.write "app/views/bukkits/posts/new.html.erb", <<-RUBY
+ @plugin.write "app/views/bukkits/posts/new.html.erb", <<-ERB
<%= form_for(Bukkits::Post.new) do |f| %>
<%= f.text_field :title %>
<% end %>
- RUBY
+ ERB
add_to_config("config.action_dispatch.show_exceptions = false")
@@ -572,7 +572,7 @@ module RailtiesTest
@plugin.write "lib/bukkits.rb", <<-RUBY
module Bukkits
class Engine < ::Rails::Engine
- namespace(Bukkits)
+ isolate_namespace(Bukkits)
end
end
RUBY
@@ -593,8 +593,8 @@ module RailtiesTest
@plugin.write "lib/bukkits.rb", <<-RUBY
module Bukkits
class Engine < ::Rails::Engine
- namespace(Bukkits)
- config.paths.public = "#{File.join(@plugin.path, "alternate_public")}"
+ isolate_namespace(Bukkits)
+ paths["public"] = "#{File.join(@plugin.path, "alternate_public")}"
end
end
RUBY
@@ -611,8 +611,8 @@ module RailtiesTest
@plugin.write "lib/bukkits.rb", <<-RUBY
module Bukkits
class Engine < ::Rails::Engine
- namespace(Bukkits)
- config.paths.public = "#{File.join(@plugin.path, "not_existing")}"
+ isolate_namespace(Bukkits)
+ paths["public"] = "#{File.join(@plugin.path, "not_existing")}"
end
end
RUBY
@@ -643,5 +643,104 @@ module RailtiesTest
Bukkits::Engine.load_seed
assert Bukkits::Engine.config.bukkits_seeds_loaded
end
+
+ test "using namespace more than once on one module should not overwrite _railtie method" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module AppTemplate
+ class Engine < ::Rails::Engine
+ isolate_namespace(AppTemplate)
+ end
+ end
+ RUBY
+
+ add_to_config "isolate_namespace AppTemplate"
+
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do end
+ RUBY
+
+ boot_rails
+
+ assert_equal AppTemplate._railtie, AppTemplate::Engine
+ end
+
+ test "properly reload routes" do
+ # when routes are inside application class definition
+ # they should not be reloaded when engine's routes
+ # file has changed
+ add_to_config <<-RUBY
+ routes do
+ mount lambda{|env| [200, {}, ["foo"]]} => "/foo"
+ mount Bukkits::Engine => "/bukkits"
+ end
+ RUBY
+
+ FileUtils.rm(File.join(app_path, "config/routes.rb"))
+
+ @plugin.write "config/routes.rb", <<-RUBY
+ Bukkits::Engine.routes.draw do
+ mount lambda{|env| [200, {}, ["bar"]]} => "/bar"
+ end
+ RUBY
+
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ isolate_namespace(Bukkits)
+ end
+ end
+ RUBY
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ boot_rails
+
+ require "#{rails_root}/config/environment"
+ get "/foo"
+ assert_equal "foo", last_response.body
+
+ get "/bukkits/bar"
+ assert_equal "bar", last_response.body
+ end
+
+ test "setting generators for engine and overriding app generator's" do
+ @plugin.write "lib/bukkits.rb", <<-RUBY
+ module Bukkits
+ class Engine < ::Rails::Engine
+ config.generators do |g|
+ g.orm :datamapper
+ g.template_engine :haml
+ g.test_framework :rspec
+ end
+
+ config.app_generators do |g|
+ g.orm :mongoid
+ g.template_engine :liquid
+ g.test_framework :shoulda
+ end
+ end
+ end
+ RUBY
+
+ add_to_config <<-RUBY
+ config.generators do |g|
+ g.test_framework :test_unit
+ end
+ RUBY
+
+ boot_rails
+ require "#{rails_root}/config/environment"
+
+ app_generators = Rails.application.config.generators.options[:rails]
+ assert_equal :mongoid , app_generators[:orm]
+ assert_equal :liquid , app_generators[:template_engine]
+ assert_equal :test_unit, app_generators[:test_framework]
+
+ generators = Bukkits::Engine.config.generators.options[:rails]
+ assert_equal :datamapper, generators[:orm]
+ assert_equal :haml , generators[:template_engine]
+ assert_equal :rspec , generators[:test_framework]
+ end
end
end
diff --git a/railties/test/railties/mounted_engine_test.rb b/railties/test/railties/mounted_engine_test.rb
index b52ced92ec..47a4753e78 100644
--- a/railties/test/railties/mounted_engine_test.rb
+++ b/railties/test/railties/mounted_engine_test.rb
@@ -49,7 +49,7 @@ module ApplicationTests
@plugin.write "lib/blog.rb", <<-RUBY
module Blog
class Engine < ::Rails::Engine
- namespace(Blog)
+ isolate_namespace(Blog)
end
end
RUBY
diff --git a/railties/test/railties/plugin_test.rb b/railties/test/railties/plugin_test.rb
index bae05d6978..c15ac05103 100644
--- a/railties/test/railties/plugin_test.rb
+++ b/railties/test/railties/plugin_test.rb
@@ -110,7 +110,7 @@ module RailtiesTest
boot_rails
rescue Exception => e
rescued = true
- assert_equal '"bukkits" is a Railtie/Engine and cannot be installed as plugin', e.message
+ assert_equal '"bukkits" is a Railtie/Engine and cannot be installed as a plugin', e.message
end
assert rescued, "Expected boot rails to fail"
diff --git a/railties/test/railties/railtie_test.rb b/railties/test/railties/railtie_test.rb
index 406d5d764f..6d194eecba 100644
--- a/railties/test/railties/railtie_test.rb
+++ b/railties/test/railties/railtie_test.rb
@@ -103,6 +103,30 @@ module RailtiesTest
assert $ran_block
end
+ test "rake_tasks block defined in superclass of railtie is also executed" do
+ $ran_block = []
+
+ class Rails::Railtie
+ rake_tasks do
+ $ran_block << railtie_name
+ end
+ end
+
+ class MyTie < Rails::Railtie
+ railtie_name "my_tie"
+ end
+
+ require "#{app_path}/config/environment"
+
+ assert_equal [], $ran_block
+ require 'rake'
+ require 'rake/testtask'
+ require 'rake/rdoctask'
+
+ AppTemplate::Application.load_tasks
+ assert $ran_block.include?("my_tie")
+ end
+
test "generators block is executed when MyApp.load_generators is called" do
$ran_block = false
diff --git a/railties/test/railties/shared_tests.rb b/railties/test/railties/shared_tests.rb
index 9b62f88fd7..b1d7580dff 100644
--- a/railties/test/railties/shared_tests.rb
+++ b/railties/test/railties/shared_tests.rb
@@ -21,6 +21,11 @@ module RailtiesTest
end
RUBY
+ @plugin.write "db/migrate/3_create_sessions.rb", <<-RUBY
+ class CreateSessions < ActiveRecord::Migration
+ end
+ RUBY
+
app_file "db/migrate/1_create_sessions.rb", <<-RUBY
class CreateSessions < ActiveRecord::Migration
end
@@ -38,24 +43,26 @@ module RailtiesTest
add_to_config "ActiveRecord::Base.timestamped_migrations = false"
Dir.chdir(app_path) do
- output = `rake railties:copy_migrations FROM=bukkits`
+ output = `rake bukkits:install:migrations 2>&1`
- assert File.exists?("#{app_path}/db/migrate/2_create_users.bukkits.rb")
- assert File.exists?("#{app_path}/db/migrate/3_add_last_name_to_users.bukkits.rb")
- assert_match /2_create_users/, output
- assert_match /3_add_last_name_to_users/, output
+ assert File.exists?("#{app_path}/db/migrate/2_create_users.rb")
+ assert File.exists?("#{app_path}/db/migrate/3_add_last_name_to_users.rb")
+ assert_match /Copied migration 2_create_users.rb from bukkits/, output
+ assert_match /Copied migration 3_add_last_name_to_users.rb from bukkits/, output
+ assert_match /WARNING: Migration 3_create_sessions.rb from bukkits has been skipped/, output
assert_equal 3, Dir["#{app_path}/db/migrate/*.rb"].length
- output = `rake railties:copy_migrations`
+ output = `rake railties:install:migrations 2>&1`
- assert File.exists?("#{app_path}/db/migrate/4_create_yaffles.acts_as_yaffle.rb")
- assert_match /4_create_yaffles/, output
+ assert File.exists?("#{app_path}/db/migrate/4_create_yaffles.rb")
+ assert_match /WARNING: Migration 3_create_sessions.rb from bukkits has been skipped/, output
+ assert_match /Copied migration 4_create_yaffles.rb from acts_as_yaffle/, output
+ assert_no_match /2_create_users/, output
migrations_count = Dir["#{app_path}/db/migrate/*.rb"].length
- output = `rake railties:copy_migrations`
+ output = `rake railties:install:migrations 2>&1`
assert_equal migrations_count, Dir["#{app_path}/db/migrate/*.rb"].length
- assert_match /No migrations were copied/, output
end
end