aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Gemfile7
-rw-r--r--actionmailer/actionmailer.gemspec2
-rw-r--r--actionmailer/lib/action_mailer/base.rb1
-rw-r--r--actionpack/CHANGELOG2
-rw-r--r--actionpack/README.rdoc3
-rw-r--r--actionpack/lib/abstract_controller/asset_paths.rb4
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb2
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb28
-rw-r--r--actionpack/lib/action_controller.rb1
-rw-r--r--actionpack/lib/action_controller/base.rb1
-rw-r--r--actionpack/lib/action_controller/metal.rb2
-rw-r--r--actionpack/lib/action_controller/metal/data_streaming.rb145
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb10
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb15
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb11
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb176
-rw-r--r--actionpack/lib/action_controller/railtie.rb1
-rw-r--r--actionpack/lib/action_controller/railties/paths.rb8
-rw-r--r--actionpack/lib/action_controller/record_identifier.rb14
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb101
-rw-r--r--actionpack/lib/action_dispatch/middleware/flash.rb42
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb4
-rw-r--r--actionpack/lib/action_dispatch/middleware/static.rb43
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb4
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/selector.rb203
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb2
-rw-r--r--actionpack/lib/action_view.rb11
-rw-r--r--actionpack/lib/action_view/base.rb45
-rw-r--r--actionpack/lib/action_view/buffers.rb43
-rw-r--r--actionpack/lib/action_view/context.rb1
-rw-r--r--actionpack/lib/action_view/flows.rb79
-rw-r--r--actionpack/lib/action_view/helpers.rb6
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb18
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb5
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb16
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb145
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb17
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb21
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb10
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb7
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb119
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb852
-rw-r--r--actionpack/lib/action_view/helpers/scriptaculous_helper.rb263
-rw-r--r--actionpack/lib/action_view/helpers/sprockets_helper.rb69
-rw-r--r--actionpack/lib/action_view/path_set.rb1
-rw-r--r--actionpack/lib/action_view/railtie.rb2
-rw-r--r--actionpack/lib/action_view/renderer/partial_renderer.rb4
-rw-r--r--actionpack/lib/action_view/renderer/streaming_template_renderer.rb130
-rw-r--r--actionpack/lib/action_view/renderer/template_renderer.rb30
-rw-r--r--actionpack/lib/action_view/rendering.rb38
-rw-r--r--actionpack/lib/action_view/template.rb37
-rw-r--r--actionpack/lib/action_view/template/handlers.rb2
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb20
-rw-r--r--actionpack/lib/action_view/template/handlers/rjs.rb13
-rw-r--r--actionpack/lib/action_view/template/resolver.rb2
-rw-r--r--actionpack/lib/action_view/test_case.rb2
-rw-r--r--actionpack/lib/sprockets/railtie.rb62
-rw-r--r--actionpack/test/controller/assert_select_test.rb414
-rw-r--r--actionpack/test/controller/caching_test.rb14
-rw-r--r--actionpack/test/controller/content_type_test.rb9
-rw-r--r--actionpack/test/controller/flash_test.rb9
-rw-r--r--actionpack/test/controller/mime_responds_test.rb31
-rw-r--r--actionpack/test/controller/new_base/content_type_test.rb9
-rw-r--r--actionpack/test/controller/new_base/render_layout_test.rb16
-rw-r--r--actionpack/test/controller/new_base/render_once_test.rb86
-rw-r--r--actionpack/test/controller/new_base/render_rjs_test.rb71
-rw-r--r--actionpack/test/controller/new_base/render_streaming_test.rb101
-rw-r--r--actionpack/test/controller/new_base/render_test.rb3
-rw-r--r--actionpack/test/controller/render_js_test.rb9
-rw-r--r--actionpack/test/controller/render_other_test.rb237
-rw-r--r--actionpack/test/controller/render_test.rb9
-rw-r--r--actionpack/test/controller/view_paths_test.rb29
-rw-r--r--actionpack/test/dispatch/response_test.rb16
-rw-r--r--actionpack/test/dispatch/routing_test.rb2
-rw-r--r--actionpack/test/dispatch/static_test.rb33
-rw-r--r--actionpack/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs6
-rw-r--r--actionpack/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs1
-rw-r--r--actionpack/test/fixtures/layouts/streaming.erb4
-rw-r--r--actionpack/test/fixtures/old_content_type/render_default_for_rjs.rjs1
-rw-r--r--actionpack/test/fixtures/respond_to/all_types_with_layout.js.rjs1
-rw-r--r--actionpack/test/fixtures/respond_to/using_defaults.js.rjs1
-rw-r--r--actionpack/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs1
-rw-r--r--actionpack/test/fixtures/respond_with/using_resource.js.rjs1
-rw-r--r--actionpack/test/fixtures/sprockets/app/images/logo.png (renamed from railties/lib/rails/generators/rails/app/templates/public/images/rails.png)bin6646 -> 6646 bytes
-rw-r--r--actionpack/test/fixtures/sprockets/app/javascripts/application.js0
-rw-r--r--actionpack/test/fixtures/sprockets/app/javascripts/dir/xmlhr.js0
-rw-r--r--actionpack/test/fixtures/sprockets/app/javascripts/xmlhr.js0
-rw-r--r--actionpack/test/fixtures/sprockets/app/stylesheets/application.css0
-rw-r--r--actionpack/test/fixtures/sprockets/app/stylesheets/dir/style.css0
-rw-r--r--actionpack/test/fixtures/sprockets/app/stylesheets/style.css0
-rw-r--r--actionpack/test/fixtures/test/delete_with_js.rjs2
-rw-r--r--actionpack/test/fixtures/test/enum_rjs_test.rjs6
-rw-r--r--actionpack/test/fixtures/test/greeting.js.rjs1
-rw-r--r--actionpack/test/fixtures/test/nested_streaming.erb3
-rw-r--r--actionpack/test/fixtures/test/render_explicit_html_template.js.rjs1
-rw-r--r--actionpack/test/fixtures/test/render_implicit_html_template.js.rjs1
-rw-r--r--actionpack/test/fixtures/test/streaming.erb3
-rw-r--r--actionpack/test/fixtures/test/streaming_buster.erb3
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb23
-rw-r--r--actionpack/test/template/capture_helper_test.rb16
-rw-r--r--actionpack/test/template/date_helper_test.rb6
-rw-r--r--actionpack/test/template/form_helper_test.rb2
-rw-r--r--actionpack/test/template/form_options_helper_test.rb2
-rw-r--r--actionpack/test/template/form_tag_helper_test.rb2
-rw-r--r--actionpack/test/template/javascript_helper_test.rb39
-rw-r--r--actionpack/test/template/lookup_context_test.rb25
-rw-r--r--actionpack/test/template/prototype_helper_test.rb476
-rw-r--r--actionpack/test/template/render_test.rb13
-rw-r--r--actionpack/test/template/scriptaculous_helper_test.rb86
-rw-r--r--actionpack/test/template/sprockets_helper_test.rb119
-rw-r--r--actionpack/test/template/streaming_render_test.rb105
-rw-r--r--actionpack/test/template/template_test.rb38
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb49
-rw-r--r--activemodel/lib/active_model/secure_password.rb7
-rw-r--r--activemodel/lib/active_model/validator.rb2
-rw-r--r--activemodel/test/cases/secure_password_test.rb12
-rw-r--r--activerecord/CHANGELOG52
-rw-r--r--activerecord/lib/active_record/associations/association.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/belongs_to.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/has_many.rb2
-rw-r--r--activerecord/lib/active_record/associations/builder/has_one.rb2
-rw-r--r--activerecord/lib/active_record/associations/collection_association.rb12
-rw-r--r--activerecord/lib/active_record/associations/collection_proxy.rb2
-rw-r--r--activerecord/lib/active_record/associations/has_one_association.rb2
-rw-r--r--activerecord/lib/active_record/associations/join_dependency/join_association.rb22
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb35
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb2
-rw-r--r--activerecord/lib/active_record/attribute_methods/write.rb2
-rw-r--r--activerecord/lib/active_record/base.rb154
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb22
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract/quoting.rb38
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb2
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb12
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb31
-rw-r--r--activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb6
-rw-r--r--activerecord/lib/active_record/named_scope.rb58
-rw-r--r--activerecord/lib/active_record/railties/databases.rake2
-rw-r--r--activerecord/lib/active_record/reflection.rb2
-rw-r--r--activerecord/lib/active_record/relation.rb45
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb14
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb4
-rw-r--r--activerecord/lib/rails/generators/active_record/session_migration/session_migration_generator.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/quoting_test.rb26
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/quoting_test.rb25
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb93
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb14
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb2
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb14
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb8
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb14
-rw-r--r--activerecord/test/cases/base_test.rb16
-rw-r--r--activerecord/test/cases/binary_test.rb19
-rw-r--r--activerecord/test/cases/bind_parameter_test.rb2
-rw-r--r--activerecord/test/cases/defaults_test.rb2
-rw-r--r--activerecord/test/cases/finder_test.rb3
-rw-r--r--activerecord/test/cases/method_scoping_test.rb12
-rw-r--r--activerecord/test/cases/named_scope_test.rb36
-rw-r--r--activerecord/test/cases/quoting_test.rb13
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb115
-rw-r--r--activerecord/test/cases/relation_test.rb2
-rw-r--r--activerecord/test/models/bulb.rb12
-rw-r--r--activerecord/test/models/car.rb3
-rw-r--r--activerecord/test/models/categorization.rb1
-rw-r--r--activerecord/test/models/developer.rb50
-rw-r--r--activerecord/test/models/pirate.rb2
-rw-r--r--activerecord/test/models/post.rb11
-rw-r--r--activerecord/test/models/reference.rb4
-rw-r--r--activerecord/test/models/without_table.rb2
-rw-r--r--activerecord/test/schema/schema.rb1
-rw-r--r--activeresource/lib/active_resource/connection.rb2
-rw-r--r--activeresource/test/cases/http_mock_test.rb2
-rw-r--r--activesupport/CHANGELOG4
-rw-r--r--activesupport/activesupport.gemspec2
-rw-r--r--activesupport/lib/active_support/cache/file_store.rb6
-rw-r--r--activesupport/lib/active_support/callbacks.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/hash/conversions.rb2
-rw-r--r--activesupport/lib/active_support/core_ext/object/inclusion.rb13
-rw-r--r--activesupport/lib/active_support/core_ext/string.rb1
-rw-r--r--activesupport/lib/active_support/core_ext/string/inquiry.rb13
-rw-r--r--activesupport/lib/active_support/json/backends/jsongem.rb47
-rw-r--r--activesupport/lib/active_support/json/backends/yajl.rb44
-rw-r--r--activesupport/lib/active_support/json/backends/yaml.rb113
-rw-r--r--activesupport/lib/active_support/json/decoding.rb60
-rw-r--r--activesupport/lib/active_support/time_with_zone.rb2
-rw-r--r--activesupport/test/core_ext/hash_ext_test.rb9
-rw-r--r--activesupport/test/core_ext/object/inclusion_test.rb10
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb5
-rw-r--r--activesupport/test/json/decoding_test.rb9
-rw-r--r--activesupport/test/ordered_options_test.rb26
-rw-r--r--activesupport/test/transliterate_test.rb2
-rw-r--r--load_paths.rb39
-rw-r--r--railties/CHANGELOG6
-rw-r--r--railties/guides/rails_guides/textile_extensions.rb4
-rw-r--r--railties/guides/source/active_record_validations_callbacks.textile2
-rw-r--r--railties/guides/source/ajax_on_rails.textile143
-rw-r--r--railties/guides/source/command_line.textile4
-rw-r--r--railties/guides/source/configuring.textile2
-rw-r--r--railties/guides/source/debugging_rails_applications.textile20
-rw-r--r--railties/guides/source/getting_started.textile2
-rw-r--r--railties/guides/source/layouts_and_rendering.textile30
-rw-r--r--railties/guides/source/security.textile6
-rw-r--r--railties/guides/source/testing.textile1
-rw-r--r--railties/lib/rails/application.rb13
-rw-r--r--railties/lib/rails/application/configuration.rb21
-rw-r--r--railties/lib/rails/application/finisher.rb2
-rw-r--r--railties/lib/rails/commands.rb2
-rw-r--r--railties/lib/rails/commands/benchmarker.rb2
-rw-r--r--railties/lib/rails/commands/destroy.rb2
-rw-r--r--railties/lib/rails/commands/generate.rb2
-rw-r--r--railties/lib/rails/commands/profiler.rb2
-rw-r--r--railties/lib/rails/engine.rb62
-rw-r--r--railties/lib/rails/engine/configuration.rb11
-rw-r--r--railties/lib/rails/generators.rb9
-rw-r--r--railties/lib/rails/generators/app_base.rb56
-rw-r--r--railties/lib/rails/generators/base.rb2
-rw-r--r--railties/lib/rails/generators/generated_attribute.rb2
-rw-r--r--railties/lib/rails/generators/rails/app/app_generator.rb94
-rw-r--r--railties/lib/rails/generators/rails/app/templates/Gemfile30
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/images/rails.pngbin0 -> 6646 bytes
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt11
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt4
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/application.rb9
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt1
-rw-r--r--railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt3
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/index.html2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/public/javascripts/application.js2
-rw-r--r--railties/lib/rails/generators/rails/app/templates/test/test_helper.rb (renamed from railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt)0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/controls.js (renamed from railties/lib/rails/generators/rails/app/templates/public/javascripts/controls.js)0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/dragdrop.js (renamed from railties/lib/rails/generators/rails/app/templates/public/javascripts/dragdrop.js)0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/effects.js (renamed from railties/lib/rails/generators/rails/app/templates/public/javascripts/effects.js)0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/jquery.js (renamed from railties/lib/rails/generators/rails/app/templates/public/javascripts/jquery.js)0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/jquery_ujs.js (renamed from railties/lib/rails/generators/rails/app/templates/public/javascripts/jquery_ujs.js)0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/prototype.js (renamed from railties/lib/rails/generators/rails/app/templates/public/javascripts/prototype.js)0
-rw-r--r--railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/prototype_ujs.js (renamed from railties/lib/rails/generators/rails/app/templates/public/javascripts/prototype_ujs.js)0
-rw-r--r--railties/lib/rails/generators/rails/assets/USAGE20
-rw-r--r--railties/lib/rails/generators/rails/assets/assets_generator.rb39
-rw-r--r--railties/lib/rails/generators/rails/assets/templates/javascript.js2
-rw-r--r--railties/lib/rails/generators/rails/assets/templates/javascript.js.coffee3
-rw-r--r--railties/lib/rails/generators/rails/assets/templates/stylesheet.css4
-rw-r--r--railties/lib/rails/generators/rails/assets/templates/stylesheet.css.scss5
-rw-r--r--railties/lib/rails/generators/rails/controller/controller_generator.rb2
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb64
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/app/models/.empty_directory0
-rw-r--r--railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb21
-rw-r--r--railties/lib/rails/generators/rails/scaffold/templates/scaffold.css (renamed from railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css)0
-rw-r--r--railties/lib/rails/generators/rails/scaffold/templates/scaffold.css.scss58
-rw-r--r--railties/lib/rails/generators/rails/stylesheets/USAGE5
-rw-r--r--railties/lib/rails/generators/rails/stylesheets/stylesheets_generator.rb9
-rw-r--r--railties/lib/rails/railtie/configuration.rb7
-rw-r--r--railties/lib/rails/source_annotation_extractor.rb6
-rw-r--r--railties/lib/rails/tasks.rb2
-rw-r--r--railties/lib/rails/tasks/assets.rake10
-rw-r--r--railties/lib/rails/tasks/railties.rake29
-rw-r--r--railties/test/generators/app_generator_test.rb54
-rw-r--r--railties/test/generators/assets_generator_test.rb26
-rw-r--r--railties/test/generators/controller_generator_test.rb6
-rw-r--r--railties/test/generators/namespaced_generators_test.rb8
-rw-r--r--railties/test/generators/plugin_new_generator_test.rb49
-rw-r--r--railties/test/generators/scaffold_generator_test.rb52
-rw-r--r--railties/test/generators/stylesheets_generator_test.rb17
-rw-r--r--railties/test/isolation/abstract_unit.rb3
-rw-r--r--railties/test/railties/engine_test.rb258
-rw-r--r--railties/test/railties/shared_tests.rb42
-rw-r--r--tasks/release.rb26
271 files changed, 2880 insertions, 5042 deletions
diff --git a/Gemfile b/Gemfile
index 879598c6db..c720b09a2c 100644
--- a/Gemfile
+++ b/Gemfile
@@ -11,6 +11,10 @@ end
gem "rack", :git => "git://github.com/rack/rack.git"
gem "rack-test", :git => "git://github.com/brynary/rack-test.git"
+gem "sprockets", :git => "git://github.com/sstephenson/sprockets.git"
+gem "coffee-script"
+gem "sass", ">= 3.0"
+
gem "rake", ">= 0.8.7"
gem "mocha", ">= 0.9.8"
@@ -28,6 +32,7 @@ platforms :mri_18 do
gem "system_timer"
gem "ruby-debug", ">= 0.10.3"
gem 'ruby-prof'
+ gem "json"
end
platforms :mri_19 do
@@ -55,7 +60,7 @@ end
platforms :jruby do
gem "ruby-debug", ">= 0.10.3"
-
+ gem "json"
gem "activerecord-jdbcsqlite3-adapter"
# This is needed by now to let tests work on JRuby
diff --git a/actionmailer/actionmailer.gemspec b/actionmailer/actionmailer.gemspec
index ee02cf6945..a59069cc37 100644
--- a/actionmailer/actionmailer.gemspec
+++ b/actionmailer/actionmailer.gemspec
@@ -18,5 +18,5 @@ Gem::Specification.new do |s|
s.requirements << 'none'
s.add_dependency('actionpack', version)
- s.add_dependency('mail', '~> 2.2.15')
+ s.add_dependency('mail', '~> 2.2.16')
end
diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb
index 06de43b76f..1e91d62e24 100644
--- a/actionmailer/lib/action_mailer/base.rb
+++ b/actionmailer/lib/action_mailer/base.rb
@@ -5,6 +5,7 @@ require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/proc'
require 'active_support/core_ext/string/inflections'
+require 'active_support/core_ext/hash/except'
require 'action_mailer/log_subscriber'
module ActionMailer #:nodoc:
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index 25e2d27a01..76dbfe7895 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,5 +1,7 @@
*Rails 3.1.0 (unreleased)*
+* RJS has been extracted out to a gem. [fxn]
+
* Implicit actions named not_implemented can be rendered [Santiago Pastorino]
* Wildcard route will always matching the optional format segment by default. For example if you have this route:
diff --git a/actionpack/README.rdoc b/actionpack/README.rdoc
index b900962d32..1a56c44db5 100644
--- a/actionpack/README.rdoc
+++ b/actionpack/README.rdoc
@@ -20,8 +20,7 @@ It consists of several modules:
* Action View, which handles view template lookup and rendering, and provides
view helpers that assist when building HTML forms, Atom feeds and more.
Template formats that Action View handles are ERB (embedded Ruby, typically
- used to inline short Ruby snippets inside HTML), XML Builder and RJS
- (dynamically generated JavaScript from Ruby code).
+ used to inline short Ruby snippets inside HTML), and XML Builder.
With the Ruby on Rails framework, users only directly interface with the
Action Controller module. Necessary Action Dispatch functionality is activated
diff --git a/actionpack/lib/abstract_controller/asset_paths.rb b/actionpack/lib/abstract_controller/asset_paths.rb
index 9ca2fb742f..ad14cd6d87 100644
--- a/actionpack/lib/abstract_controller/asset_paths.rb
+++ b/actionpack/lib/abstract_controller/asset_paths.rb
@@ -3,7 +3,7 @@ module AbstractController
extend ActiveSupport::Concern
included do
- config_accessor :asset_host, :asset_path, :assets_dir, :javascripts_dir, :stylesheets_dir
+ config_accessor :asset_host, :asset_path, :assets_dir, :javascripts_dir, :stylesheets_dir, :use_sprockets
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index 4ee54474cc..d1b87b67ee 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -334,7 +334,7 @@ module AbstractController
# ==== Parameters
# * <tt>details</tt> - A list of details to restrict the search by. This
# might include details like the format or locale of the template.
- # * <tt>require_logout</tt> - If this is true, raise an ArgumentError
+ # * <tt>require_layout</tt> - If this is true, raise an ArgumentError
# with details about the fact that the exception could not be
# found (defaults to false)
#
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 691310d5d2..306bd41e2d 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -1,5 +1,6 @@
require "abstract_controller/base"
require "action_view"
+require "active_support/core_ext/object/instance_variables"
module AbstractController
class DoubleRenderError < Error
@@ -104,16 +105,16 @@ module AbstractController
# Normalize arguments, options and then delegates render_to_body and
# sticks the result in self.response_body.
def render(*args, &block)
- self.response_body = render_to_string(*args, &block)
+ options = _normalize_render(*args, &block)
+ self.response_body = render_to_body(options)
end
# Raw rendering of a template to a string. Just convert the results of
- # render_to_body into a String.
+ # render_response into a String.
# :api: plugin
def render_to_string(*args, &block)
- options = _normalize_args(*args, &block)
- _normalize_options(options)
- render_to_body(options).tap { self.response_body = nil }
+ options = _normalize_render(*args, &block)
+ render_to_body(options)
end
# Raw rendering of a template to a Rack-compatible body.
@@ -150,8 +151,17 @@ module AbstractController
hash
end
- # Normalize options by converting render "foo" to render :action => "foo" and
+ # Normalize args and options.
+ # :api: private
+ def _normalize_render(*args, &block)
+ options = _normalize_args(*args, &block)
+ _normalize_options(options)
+ options
+ end
+
+ # Normalize args by converting render "foo" to render :action => "foo" and
# render "foo/bar" to render :file => "foo/bar".
+ # :api: plugin
def _normalize_args(action=nil, options={})
case action
when NilClass
@@ -168,12 +178,14 @@ module AbstractController
options
end
+ # Normalize options.
+ # :api: plugin
def _normalize_options(options)
if options[:partial] == true
options[:partial] = action_name
end
- if (options.keys & [:partial, :file, :template, :once]).empty?
+ if (options.keys & [:partial, :file, :template]).empty?
options[:prefixes] ||= _prefixes
end
@@ -181,6 +193,8 @@ module AbstractController
options
end
+ # Process extra options.
+ # :api: plugin
def _process_options(options)
end
end
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 62cc18b253..aab2b9dc25 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -13,6 +13,7 @@ module ActionController
autoload :Compatibility
autoload :ConditionalGet
autoload :Cookies
+ autoload :DataStreaming
autoload :Flash
autoload :ForceSSL
autoload :Head
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 5f9e082cd3..ca0dccf575 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -200,6 +200,7 @@ module ActionController
RequestForgeryProtection,
ForceSSL,
Streaming,
+ DataStreaming,
RecordIdentifier,
HttpAuthentication::Basic::ControllerMethods,
HttpAuthentication::Digest::ControllerMethods,
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 585bd5e5ab..0133b2ecbc 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -131,7 +131,7 @@ module ActionController
attr_internal :headers, :response, :request
delegate :session, :to => "@_request"
- def initialize(*)
+ def initialize
@_headers = {"Content-Type" => "text/html"}
@_status = 200
@_request = nil
diff --git a/actionpack/lib/action_controller/metal/data_streaming.rb b/actionpack/lib/action_controller/metal/data_streaming.rb
new file mode 100644
index 0000000000..997bc6e958
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/data_streaming.rb
@@ -0,0 +1,145 @@
+require 'active_support/core_ext/file/path'
+
+module ActionController #:nodoc:
+ # Methods for sending arbitrary data and for streaming files to the browser,
+ # instead of rendering.
+ module DataStreaming
+ extend ActiveSupport::Concern
+
+ include ActionController::Rendering
+
+ DEFAULT_SEND_FILE_OPTIONS = {
+ :type => 'application/octet-stream'.freeze,
+ :disposition => 'attachment'.freeze,
+ }.freeze
+
+ protected
+ # Sends the file. This uses a server-appropriate method (such as X-Sendfile)
+ # via the Rack::Sendfile middleware. The header to use is set via
+ # config.action_dispatch.x_sendfile_header, and defaults to "X-Sendfile".
+ # Your server can also configure this for you by setting the X-Sendfile-Type header.
+ #
+ # Be careful to sanitize the path parameter if it is coming from a web
+ # page. <tt>send_file(params[:path])</tt> allows a malicious user to
+ # download any file on your server.
+ #
+ # Options:
+ # * <tt>:filename</tt> - suggests a filename for the browser to use.
+ # Defaults to <tt>File.basename(path)</tt>.
+ # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
+ # either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
+ # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
+ # Valid values are 'inline' and 'attachment' (default).
+ # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
+ # * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
+ # the URL, which is necessary for i18n filenames on certain browsers
+ # (setting <tt>:filename</tt> overrides this option).
+ #
+ # The default Content-Type and Content-Disposition headers are
+ # set to download arbitrary binary files in as many browsers as
+ # possible. IE versions 4, 5, 5.5, and 6 are all known to have
+ # a variety of quirks (especially when downloading over SSL).
+ #
+ # Simple download:
+ #
+ # send_file '/path/to.zip'
+ #
+ # Show a JPEG in the browser:
+ #
+ # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
+ #
+ # Show a 404 page in the browser:
+ #
+ # send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404
+ #
+ # Read about the other Content-* HTTP headers if you'd like to
+ # provide the user with more information (such as Content-Description) in
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.
+ #
+ # Also be aware that the document may be cached by proxies and browsers.
+ # The Pragma and Cache-Control headers declare how the file may be cached
+ # by intermediaries. They default to require clients to validate with
+ # the server before releasing cached responses. See
+ # http://www.mnot.net/cache_docs/ for an overview of web caching and
+ # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
+ # for the Cache-Control header spec.
+ def send_file(path, options = {}) #:doc:
+ raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
+
+ options[:filename] ||= File.basename(path) unless options[:url_based_filename]
+ send_file_headers! options
+
+ self.status = options[:status] || 200
+ self.content_type = options[:content_type] if options.key?(:content_type)
+ self.response_body = File.open(path, "rb")
+ end
+
+ # Sends the given binary data to the browser. This method is similar to
+ # <tt>render :text => data</tt>, but also allows you to specify whether
+ # the browser should display the response as a file attachment (i.e. in a
+ # download dialog) or as inline data. You may also set the content type,
+ # the apparent file name, and other things.
+ #
+ # Options:
+ # * <tt>:filename</tt> - suggests a filename for the browser to use.
+ # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
+ # either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
+ # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
+ # Valid values are 'inline' and 'attachment' (default).
+ # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
+ #
+ # Generic data download:
+ #
+ # send_data buffer
+ #
+ # Download a dynamically-generated tarball:
+ #
+ # send_data generate_tgz('dir'), :filename => 'dir.tgz'
+ #
+ # Display an image Active Record in the browser:
+ #
+ # send_data image.data, :type => image.content_type, :disposition => 'inline'
+ #
+ # See +send_file+ for more information on HTTP Content-* headers and caching.
+ def send_data(data, options = {}) #:doc:
+ send_file_headers! options.dup
+ render options.slice(:status, :content_type).merge(:text => data)
+ end
+
+ private
+ def send_file_headers!(options)
+ options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
+ [:type, :disposition].each do |arg|
+ raise ArgumentError, ":#{arg} option required" if options[arg].nil?
+ end
+
+ disposition = options[:disposition]
+ disposition += %(; filename="#{options[:filename]}") if options[:filename]
+
+ content_type = options[:type]
+
+ if content_type.is_a?(Symbol)
+ extension = Mime[content_type]
+ raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension
+ self.content_type = extension
+ else
+ self.content_type = content_type
+ end
+
+ headers.merge!(
+ 'Content-Disposition' => disposition,
+ 'Content-Transfer-Encoding' => 'binary'
+ )
+
+ response.sending_file = true
+
+ # Fix a problem with IE 6.0 on opening downloaded files:
+ # If Cache-Control: no-cache is set (which Rails does by default),
+ # IE removes the file it just downloaded from its cache immediately
+ # after it displays the "open/save" dialog, which means that if you
+ # hit "open" the file isn't there anymore when the application that
+ # is called for handling the download is run, so let's workaround that
+ response.cache_control[:public] ||= false
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 618a6cdb7d..f10287afb4 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -6,6 +6,8 @@ module ActionController #:nodoc:
module MimeResponds
extend ActiveSupport::Concern
+ include ActionController::ImplicitRender
+
included do
class_attribute :responder, :mimes_for_respond_to
self.responder = ActionController::Responder
@@ -33,10 +35,10 @@ module ActionController #:nodoc:
# and all actions except <tt>:edit</tt> respond to <tt>:xml</tt> and
# <tt>:json</tt>.
#
- # respond_to :rjs, :only => :create
+ # respond_to :json, :only => :create
#
# This specifies that the <tt>:create</tt> action and no other responds
- # to <tt>:rjs</tt>.
+ # to <tt>:json</tt>.
def respond_to(*mimes)
options = mimes.extract_options!
@@ -106,8 +108,8 @@ module ActionController #:nodoc:
# end
# end
#
- # If the client wants HTML, we just redirect them back to the person list. If they want Javascript
- # (format.js), then it is an RJS request and we render the RJS template associated with this action.
+ # If the client wants HTML, we just redirect them back to the person list. If they want JavaScript,
+ # then it is an Ajax request and we render the JavaScript template associated with this action.
# Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
# include the person's company in the rendered XML, so you get something like this:
#
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index 38711c8462..0ad9dbeda9 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -41,7 +41,7 @@ module ActionController
end
# Hash of available renderers, mapping a renderer name to its proc.
- # Default keys are :json, :js, :xml and :update.
+ # Default keys are :json, :js, :xml.
RENDERERS = {}
# Adds a new renderer to call within controller actions.
@@ -95,24 +95,17 @@ module ActionController
json = json.to_json(options) unless json.kind_of?(String)
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
self.content_type ||= Mime::JSON
- self.response_body = json
+ json
end
add :js do |js, options|
self.content_type ||= Mime::JS
- self.response_body = js.respond_to?(:to_js) ? js.to_js(options) : js
+ js.respond_to?(:to_js) ? js.to_js(options) : js
end
add :xml do |xml, options|
self.content_type ||= Mime::XML
- self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
- end
-
- add :update do |proc, options|
- view_context = self.view_context
- generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc)
- self.content_type = Mime::JS
- self.response_body = generator.to_s
+ xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
end
end
end
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 32d52c84c4..70fd79bb8b 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -18,6 +18,17 @@ module ActionController
response_body
end
+ # Overwrite render_to_string because body can now be set to a rack body.
+ def render_to_string(*)
+ if self.response_body = super
+ string = ""
+ response_body.each { |r| string << r }
+ string
+ end
+ ensure
+ self.response_body = nil
+ end
+
private
# Normalize arguments by catching blocks and setting them on :update.
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 312dc8eb3e..b9bd49f670 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -1,145 +1,61 @@
require 'active_support/core_ext/file/path'
+require 'rack/chunked'
module ActionController #:nodoc:
- # Methods for sending arbitrary data and for streaming files to the browser,
- # instead of rendering.
+ # Methods for sending streaming templates back to the client.
module Streaming
extend ActiveSupport::Concern
- include ActionController::Rendering
+ include AbstractController::Rendering
+ attr_internal :stream
- DEFAULT_SEND_FILE_OPTIONS = {
- :type => 'application/octet-stream'.freeze,
- :disposition => 'attachment'.freeze,
- }.freeze
-
- protected
- # Sends the file. This uses a server-appropriate method (such as X-Sendfile)
- # via the Rack::Sendfile middleware. The header to use is set via
- # config.action_dispatch.x_sendfile_header, and defaults to "X-Sendfile".
- # Your server can also configure this for you by setting the X-Sendfile-Type header.
- #
- # Be careful to sanitize the path parameter if it is coming from a web
- # page. <tt>send_file(params[:path])</tt> allows a malicious user to
- # download any file on your server.
- #
- # Options:
- # * <tt>:filename</tt> - suggests a filename for the browser to use.
- # Defaults to <tt>File.basename(path)</tt>.
- # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
- # either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
- # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
- # Valid values are 'inline' and 'attachment' (default).
- # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
- # * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
- # the URL, which is necessary for i18n filenames on certain browsers
- # (setting <tt>:filename</tt> overrides this option).
- #
- # The default Content-Type and Content-Disposition headers are
- # set to download arbitrary binary files in as many browsers as
- # possible. IE versions 4, 5, 5.5, and 6 are all known to have
- # a variety of quirks (especially when downloading over SSL).
- #
- # Simple download:
- #
- # send_file '/path/to.zip'
- #
- # Show a JPEG in the browser:
- #
- # send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
- #
- # Show a 404 page in the browser:
- #
- # send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404
- #
- # Read about the other Content-* HTTP headers if you'd like to
- # provide the user with more information (such as Content-Description) in
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.
- #
- # Also be aware that the document may be cached by proxies and browsers.
- # The Pragma and Cache-Control headers declare how the file may be cached
- # by intermediaries. They default to require clients to validate with
- # the server before releasing cached responses. See
- # http://www.mnot.net/cache_docs/ for an overview of web caching and
- # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
- # for the Cache-Control header spec.
- def send_file(path, options = {}) #:doc:
- raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
-
- options[:filename] ||= File.basename(path) unless options[:url_based_filename]
- send_file_headers! options
-
- self.status = options[:status] || 200
- self.content_type = options[:content_type] if options.key?(:content_type)
- self.response_body = File.open(path, "rb")
- end
-
- # Sends the given binary data to the browser. This method is similar to
- # <tt>render :text => data</tt>, but also allows you to specify whether
- # the browser should display the response as a file attachment (i.e. in a
- # download dialog) or as inline data. You may also set the content type,
- # the apparent file name, and other things.
- #
- # Options:
- # * <tt>:filename</tt> - suggests a filename for the browser to use.
- # * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
- # either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
- # * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
- # Valid values are 'inline' and 'attachment' (default).
- # * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
- #
- # Generic data download:
- #
- # send_data buffer
- #
- # Download a dynamically-generated tarball:
- #
- # send_data generate_tgz('dir'), :filename => 'dir.tgz'
- #
- # Display an image Active Record in the browser:
- #
- # send_data image.data, :type => image.content_type, :disposition => 'inline'
- #
- # See +send_file+ for more information on HTTP Content-* headers and caching.
- def send_data(data, options = {}) #:doc:
- send_file_headers! options.dup
- render options.slice(:status, :content_type).merge(:text => data)
- end
-
- private
- def send_file_headers!(options)
- options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
- [:type, :disposition].each do |arg|
- raise ArgumentError, ":#{arg} option required" if options[arg].nil?
+ module ClassMethods
+ # Render streaming templates. It accepts :only, :except, :if and :unless as options
+ # to specify when to stream, as in ActionController filters.
+ def stream(options={})
+ if defined?(Fiber)
+ before_filter :_stream_filter, options
+ else
+ raise "You cannot use streaming if Fiber is not available."
end
+ end
+ end
- disposition = options[:disposition]
- disposition += %(; filename="#{options[:filename]}") if options[:filename]
-
- content_type = options[:type]
+ protected
- if content_type.is_a?(Symbol)
- extension = Mime[content_type]
- raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension
- self.content_type = extension
+ # Mark following render calls as streaming.
+ def _stream_filter #:nodoc:
+ self.stream = true
+ end
+
+ # Consider the stream option when normalazing options.
+ def _normalize_options(options) #:nodoc:
+ super
+ options[:stream] = self.stream unless options.key?(:stream)
+ end
+
+ # Set proper cache control and transfer encoding when streaming
+ def _process_options(options) #:nodoc:
+ super
+ if options[:stream]
+ if env["HTTP_VERSION"] == "HTTP/1.0"
+ options.delete(:stream)
else
- self.content_type = content_type
+ headers["Cache-Control"] ||= "no-cache"
+ headers["Transfer-Encoding"] = "chunked"
+ headers.delete("Content-Length")
end
-
- headers.merge!(
- 'Content-Disposition' => disposition,
- 'Content-Transfer-Encoding' => 'binary'
- )
-
- response.sending_file = true
-
- # Fix a problem with IE 6.0 on opening downloaded files:
- # If Cache-Control: no-cache is set (which Rails does by default),
- # IE removes the file it just downloaded from its cache immediately
- # after it displays the "open/save" dialog, which means that if you
- # hit "open" the file isn't there anymore when the application that
- # is called for handling the download is run, so let's workaround that
- response.cache_control[:public] ||= false
end
+ end
+
+ # Call render_to_body if we are streaming instead of usual +render+.
+ def _render_template(options) #:nodoc:
+ if options.delete(:stream)
+ Rack::Chunked::Body.new view_context.render_body(options)
+ else
+ super
+ end
+ end
end
end
+ \ No newline at end of file
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index f0c29825ba..d2ba052c8d 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -4,6 +4,7 @@ require "action_dispatch/railtie"
require "action_view/railtie"
require "abstract_controller/railties/routes_helpers"
require "action_controller/railties/paths"
+require "sprockets/railtie"
module ActionController
class Railtie < Rails::Railtie
diff --git a/actionpack/lib/action_controller/railties/paths.rb b/actionpack/lib/action_controller/railties/paths.rb
index dce3c2fe88..699c44c62c 100644
--- a/actionpack/lib/action_controller/railties/paths.rb
+++ b/actionpack/lib/action_controller/railties/paths.rb
@@ -16,14 +16,6 @@ module ActionController
if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers
klass.helper :all
end
-
- if app.config.serve_static_assets && namespace
- paths = namespace._railtie.config.paths
-
- klass.config.assets_dir = paths["public"].first
- klass.config.javascripts_dir = paths["public/javascripts"].first
- klass.config.stylesheets_dir = paths["public/stylesheets"].first
- end
end
end
end
diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb
index 3de40b0de3..2def78b51a 100644
--- a/actionpack/lib/action_controller/record_identifier.rb
+++ b/actionpack/lib/action_controller/record_identifier.rb
@@ -18,18 +18,12 @@ module ActionController
# post = Post.find(params[:id])
# post.destroy
#
- # respond_to do |format|
- # format.html { redirect_to(post) } # Calls polymorphic_url(post) which in turn calls post_url(post)
- # format.js do
- # # Calls: new Effect.fade('post_45');
- # render(:update) { |page| page[post].visual_effect(:fade) }
- # end
- # end
+ # redirect_to(post) # Calls polymorphic_url(post) which in turn calls post_url(post)
# end
#
- # As the example above shows, you can stop caring to a large extent what the actual id of the post is. You just know
- # that one is being assigned and that the subsequent calls in redirect_to and the RJS expect that same naming
- # convention and allows you to write less code if you follow it.
+ # As the example above shows, you can stop caring to a large extent what the actual id of the post is.
+ # You just know that one is being assigned and that the subsequent calls in redirect_to expect that
+ # same naming convention and allows you to write less code if you follow it.
module RecordIdentifier
extend self
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index 8e03a7879f..78ecf177be 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -32,24 +32,35 @@ module ActionDispatch # :nodoc:
# puts @response.body
# end
# end
- class Response < Rack::Response
- attr_accessor :request, :blank
+ class Response
+ attr_accessor :request, :header, :status
+ attr_writer :sending_file
- attr_writer :header, :sending_file
alias_method :headers=, :header=
+ alias_method :headers, :header
+
+ delegate :[], :[]=, :to => :@header
+ delegate :each, :to => :@body
+
+ # Sets the HTTP response's content MIME type. For example, in the controller
+ # you could write this:
+ #
+ # response.content_type = "text/plain"
+ #
+ # If a character set has been defined for this response (see charset=) then
+ # the character set information will also be included in the content type
+ # information.
+ attr_accessor :charset, :content_type
+
+ CONTENT_TYPE = "Content-Type"
+
+ cattr_accessor(:default_charset) { "utf-8" }
module Setup
def initialize(status = 200, header = {}, body = [])
- @writer = lambda { |x| @body << x }
- @block = nil
- @length = 0
-
- @header = header
- self.body, self.status = body, status
+ self.body, self.header, self.status = body, header, status
- @cookie = []
@sending_file = false
-
@blank = false
if content_type = self["Content-Type"]
@@ -62,6 +73,7 @@ module ActionDispatch # :nodoc:
end
end
+ include Rack::Response::Helpers
include Setup
include ActionDispatch::Http::Cache::Response
@@ -106,13 +118,21 @@ module ActionDispatch # :nodoc:
def body=(body)
@blank = true if body == EMPTY
- @body = body.respond_to?(:to_str) ? [body] : body
+ @body = body.respond_to?(:each) ? body : [body]
end
def body_parts
@body
end
+ def set_cookie(key, value)
+ ::Rack::Utils.set_cookie_header!(header, key, value)
+ end
+
+ def delete_cookie(key, value={})
+ ::Rack::Utils.delete_cookie_header!(header, key, value)
+ end
+
def location
headers['Location']
end
@@ -122,46 +142,21 @@ module ActionDispatch # :nodoc:
headers['Location'] = url
end
- # Sets the HTTP response's content MIME type. For example, in the controller
- # you could write this:
- #
- # response.content_type = "text/plain"
- #
- # If a character set has been defined for this response (see charset=) then
- # the character set information will also be included in the content type
- # information.
- attr_accessor :charset, :content_type
-
- CONTENT_TYPE = "Content-Type"
-
- cattr_accessor(:default_charset) { "utf-8" }
-
def to_a
assign_default_content_type_and_charset!
handle_conditional_get!
- self["Set-Cookie"] = self["Set-Cookie"].join("\n") if self["Set-Cookie"].respond_to?(:join)
- super
- end
- alias prepare! to_a
+ @header["Set-Cookie"] = @header["Set-Cookie"].join("\n") if @header["Set-Cookie"].respond_to?(:join)
- def each(&callback)
- if @body.respond_to?(:call)
- @writer = lambda { |x| callback.call(x) }
- @body.call(self, self)
+ if [204, 304].include?(@status)
+ @header.delete "Content-Type"
+ [@status, @header, []]
else
- @body.each { |part| callback.call(part.to_s) }
+ [@status, @header, self]
end
-
- @writer = callback
- @block.call(self) if @block
- end
-
- def write(str)
- str = str.to_s
- @writer.call str
- str
end
+ alias prepare! to_a
+ alias to_ary to_a # For implicit splat on 1.9.2
# Returns the response cookies, converted to a Hash of (name => value) pairs
#
@@ -180,18 +175,18 @@ module ActionDispatch # :nodoc:
cookies
end
- private
- def assign_default_content_type_and_charset!
- return if headers[CONTENT_TYPE].present?
+ private
- @content_type ||= Mime::HTML
- @charset ||= self.class.default_charset
+ def assign_default_content_type_and_charset!
+ return if headers[CONTENT_TYPE].present?
- type = @content_type.to_s.dup
- type << "; charset=#{@charset}" unless @sending_file
+ @content_type ||= Mime::HTML
+ @charset ||= self.class.default_charset
- headers[CONTENT_TYPE] = type
- end
+ type = @content_type.to_s.dup
+ type << "; charset=#{@charset}" unless @sending_file
+ headers[CONTENT_TYPE] = type
+ end
end
end
diff --git a/actionpack/lib/action_dispatch/middleware/flash.rb b/actionpack/lib/action_dispatch/middleware/flash.rb
index 027ff7f8ac..735c72d34a 100644
--- a/actionpack/lib/action_dispatch/middleware/flash.rb
+++ b/actionpack/lib/action_dispatch/middleware/flash.rb
@@ -4,7 +4,7 @@ module ActionDispatch
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
# to put a new one.
def flash
- @env['action_dispatch.request.flash_hash'] ||= (session["flash"] || Flash::FlashHash.new)
+ @env[Flash::KEY] ||= (session["flash"] || Flash::FlashHash.new)
end
end
@@ -40,18 +40,14 @@ module ActionDispatch
#
# See docs on the FlashHash class for more details about the flash.
class Flash
+ KEY = 'action_dispatch.request.flash_hash'.freeze
+
class FlashNow #:nodoc:
def initialize(flash)
@flash = flash
- @closed = false
end
- attr_reader :closed
- alias :closed? :closed
- def close!; @closed = true end
-
def []=(k, v)
- raise ClosedError, :flash if closed?
@flash[k] = v
@flash.discard(k)
v
@@ -70,6 +66,10 @@ module ActionDispatch
def notice=(message)
self[:notice] = message
end
+
+ def close!(new_flash)
+ @flash = new_flash
+ end
end
class FlashHash
@@ -79,12 +79,9 @@ module ActionDispatch
@used = Set.new
@closed = false
@flashes = {}
+ @now = nil
end
- attr_reader :closed
- alias :closed? :closed
- def close!; @closed = true end
-
def []=(k, v) #:nodoc:
raise ClosedError, :flash if closed?
keep(k)
@@ -152,6 +149,14 @@ module ActionDispatch
@now ||= FlashNow.new(self)
end
+ attr_reader :closed
+ alias :closed? :closed
+
+ def close!
+ @closed = true
+ @now.close!(self) if @now
+ end
+
# Keeps either the entire current flash or a specific flash entry available for the next action:
#
# flash.keep # keeps the entire flash
@@ -231,13 +236,18 @@ module ActionDispatch
@app.call(env)
ensure
session = env['rack.session'] || {}
- flash_hash = env['action_dispatch.request.flash_hash']
+ flash_hash = env[KEY]
if flash_hash
- if !flash_hash.empty? || session.key?('flash')
- session["flash"] = flash_hash
- end
- flash_hash.close!
+ if !flash_hash.empty? || session.key?('flash')
+ session["flash"] = flash_hash
+ new_hash = flash_hash.dup
+ else
+ new_hash = flash_hash
+ end
+
+ env[KEY] = new_hash
+ new_hash.close!
end
if session.key?('flash') && session['flash'].empty?
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
index 64d3a87fd0..1a811ce1b1 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -29,7 +29,9 @@ module ActionDispatch
end
def generate_sid
- ActiveSupport::SecureRandom.hex(16)
+ sid = ActiveSupport::SecureRandom.hex(16)
+ sid.encode!('UTF-8') if sid.respond_to?(:encode!)
+ sid
end
protected
diff --git a/actionpack/lib/action_dispatch/middleware/static.rb b/actionpack/lib/action_dispatch/middleware/static.rb
index c57f694c4d..348f7b86b8 100644
--- a/actionpack/lib/action_dispatch/middleware/static.rb
+++ b/actionpack/lib/action_dispatch/middleware/static.rb
@@ -2,25 +2,23 @@ require 'rack/utils'
module ActionDispatch
class FileHandler
- def initialize(at, root)
- @at, @root = at.chomp('/'), root.chomp('/')
- @compiled_at = @at.blank? ? nil : /^#{Regexp.escape(at)}/
+ def initialize(root)
+ @root = root.chomp('/')
@compiled_root = /^#{Regexp.escape(root)}/
@file_server = ::Rack::File.new(@root)
end
def match?(path)
path = path.dup
- 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]
- match = matches.detect { |m| File.file?(m) }
- if match
- match.sub!(@compiled_root, '')
- match
- end
+ full_path = path.empty? ? @root : File.join(@root, ::Rack::Utils.unescape(path))
+ paths = "#{full_path}#{ext}"
+
+ matches = Dir[paths]
+ match = matches.detect { |m| File.file?(m) }
+ if match
+ match.sub!(@compiled_root, '')
+ match
end
end
@@ -39,9 +37,9 @@ module ActionDispatch
class Static
FILE_METHODS = %w(GET HEAD).freeze
- def initialize(app, roots)
+ def initialize(app, path)
@app = app
- @file_handlers = create_file_handlers(roots)
+ @file_handler = FileHandler.new(path)
end
def call(env)
@@ -49,24 +47,13 @@ module ActionDispatch
method = env['REQUEST_METHOD']
if FILE_METHODS.include?(method)
- @file_handlers.each do |file_handler|
- if match = file_handler.match?(path)
- env["PATH_INFO"] = match
- return file_handler.call(env)
- end
+ if match = @file_handler.match?(path)
+ env["PATH_INFO"] = match
+ return @file_handler.call(env)
end
end
@app.call(env)
end
-
- private
- def create_file_handlers(roots)
- roots = { '' => roots } unless roots.is_a?(Hash)
-
- roots.map do |at, root|
- FileHandler.new(at, root) if File.exist?(root)
- end.compact
- end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 13aef18ee1..a65f6e1fce 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1346,11 +1346,11 @@ module ActionDispatch
end
def resource_scope? #:nodoc:
- @scope[:scope_level].among?(:resource, :resources)
+ @scope[:scope_level].in?([:resource, :resources])
end
def resource_method_scope? #:nodoc:
- @scope[:scope_level].among?(:collection, :member, :new)
+ @scope[:scope_level].in?([:collection, :member, :new])
end
def with_exclusive_scope
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index 16a6b93ce8..8a04cfa886 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -35,7 +35,7 @@ module ActionDispatch
def assert_response(type, message = nil)
validate_request!
- if type.among?(:success, :missing, :redirect, :error) && @response.send("#{type}?")
+ if type.in?([:success, :missing, :redirect, :error]) && @response.send("#{type}?")
assert_block("") { true } # to count the assertion
elsif type.is_a?(Fixnum) && @response.response_code == type
assert_block("") { true } # to count the assertion
diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
index f41d3e5ddb..c67a0664dc 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
@@ -19,7 +19,7 @@ module ActionDispatch
# from the response HTML or elements selected by the enclosing assertion.
#
# In addition to HTML responses, you can make the following assertions:
- # * +assert_select_rjs+ - Assertions on HTML content of RJS update and insertion operations.
+ #
# * +assert_select_encoded+ - Assertions on HTML encoded inside XML, for example for dealing with feed item descriptions.
# * +assert_select_email+ - Assertions on the HTML body of an e-mail.
#
@@ -80,7 +80,7 @@ module ActionDispatch
return matches
else
- root = response_from_page_or_rjs
+ root = response_from_page
end
case arg
@@ -204,7 +204,7 @@ module ActionDispatch
root.children.concat @selected
else
# Otherwise just operate on the response document.
- root = response_from_page_or_rjs
+ root = response_from_page
end
# First or second argument is the selector: string and we pass
@@ -326,144 +326,6 @@ module ActionDispatch
end
end
- # Selects content from the RJS response.
- #
- # === Narrowing down
- #
- # With no arguments, asserts that one or more elements are updated or
- # inserted by RJS statements.
- #
- # Use the +id+ argument to narrow down the assertion to only statements
- # that update or insert an element with that identifier.
- #
- # Use the first argument to narrow down assertions to only statements
- # of that type. Possible values are <tt>:replace</tt>, <tt>:replace_html</tt>,
- # <tt>:show</tt>, <tt>:hide</tt>, <tt>:toggle</tt>, <tt>:remove</tta>,
- # <tt>:insert_html</tt> and <tt>:redirect</tt>.
- #
- # Use the argument <tt>:insert</tt> followed by an insertion position to narrow
- # down the assertion to only statements that insert elements in that
- # position. Possible values are <tt>:top</tt>, <tt>:bottom</tt>, <tt>:before</tt>
- # and <tt>:after</tt>.
- #
- # Use the argument <tt>:redirect</tt> followed by a path to check that an statement
- # which redirects to the specified path is generated.
- #
- # Using the <tt>:remove</tt> statement, you will be able to pass a block, but it will
- # be ignored as there is no HTML passed for this statement.
- #
- # === Using blocks
- #
- # Without a block, +assert_select_rjs+ merely asserts that the response
- # contains one or more RJS statements that replace or update content.
- #
- # With a block, +assert_select_rjs+ also selects all elements used in
- # these statements and passes them to the block. Nested assertions are
- # supported.
- #
- # Calling +assert_select_rjs+ with no arguments and using nested asserts
- # asserts that the HTML content is returned by one or more RJS statements.
- # Using +assert_select+ directly makes the same assertion on the content,
- # but without distinguishing whether the content is returned in an HTML
- # or JavaScript.
- #
- # ==== Examples
- #
- # # Replacing the element foo.
- # # page.replace 'foo', ...
- # assert_select_rjs :replace, "foo"
- #
- # # Replacing with the chained RJS proxy.
- # # page[:foo].replace ...
- # assert_select_rjs :chained_replace, 'foo'
- #
- # # Inserting into the element bar, top position.
- # assert_select_rjs :insert, :top, "bar"
- #
- # # Remove the element bar
- # assert_select_rjs :remove, "bar"
- #
- # # Changing the element foo, with an image.
- # assert_select_rjs "foo" do
- # assert_select "img[src=/images/logo.gif""
- # end
- #
- # # RJS inserts or updates a list with four items.
- # assert_select_rjs do
- # assert_select "ol>li", 4
- # end
- #
- # # The same, but shorter.
- # assert_select "ol>li", 4
- #
- # # Checking for a redirect.
- # assert_select_rjs :redirect, root_path
- def assert_select_rjs(*args, &block)
- rjs_type = args.first.is_a?(Symbol) ? args.shift : nil
- id = args.first.is_a?(String) ? args.shift : nil
-
- # If the first argument is a symbol, it's the type of RJS statement we're looking
- # for (update, replace, insertion, etc). Otherwise, we're looking for just about
- # any RJS statement.
- if rjs_type
- if rjs_type == :insert
- position = args.shift
- id = args.shift
- insertion = "insert_#{position}".to_sym
- raise ArgumentError, "Unknown RJS insertion type #{position}" unless RJS_STATEMENTS[insertion]
- statement = "(#{RJS_STATEMENTS[insertion]})"
- else
- raise ArgumentError, "Unknown RJS statement type #{rjs_type}" unless RJS_STATEMENTS[rjs_type]
- statement = "(#{RJS_STATEMENTS[rjs_type]})"
- end
- else
- statement = "#{RJS_STATEMENTS[:any]}"
- end
-
- # Next argument we're looking for is the element identifier. If missing, we pick
- # any element, otherwise we replace it in the statement.
- pattern = Regexp.new(
- id ? statement.gsub(RJS_ANY_ID, "\"#{id}\"") : statement
- )
-
- # Duplicate the body since the next step involves destroying it.
- matches = nil
- case rjs_type
- when :remove, :show, :hide, :toggle
- matches = @response.body.match(pattern)
- else
- @response.body.gsub(pattern) do |match|
- html = unescape_rjs(match)
- matches ||= []
- matches.concat HTML::Document.new(html).root.children.select { |n| n.tag? }
- ""
- end
- end
-
- if matches
- assert_block("") { true } # to count the assertion
- if block_given? && !rjs_type.among?(:remove, :show, :hide, :toggle)
- begin
- @selected ||= nil
- in_scope, @selected = @selected, matches
- yield matches
- ensure
- @selected = in_scope
- end
- end
- matches
- else
- # RJS statement not found.
- case rjs_type
- when :remove, :show, :hide, :toggle
- flunk_message = "No RJS statement that #{rjs_type.to_s}s '#{id}' was rendered."
- else
- flunk_message = "No RJS statement that replaces or inserts HTML content."
- end
- flunk args.shift || flunk_message
- end
- end
-
# Extracts the content of an element, treats it as encoded HTML and runs
# nested assertion on it.
#
@@ -563,62 +425,9 @@ module ActionDispatch
end
protected
- RJS_PATTERN_HTML = "\"((\\\\\"|[^\"])*)\""
- RJS_ANY_ID = "\"([^\"])*\""
- RJS_STATEMENTS = {
- :chained_replace => "\\$\\(#{RJS_ANY_ID}\\)\\.replace\\(#{RJS_PATTERN_HTML}\\)",
- :chained_replace_html => "\\$\\(#{RJS_ANY_ID}\\)\\.update\\(#{RJS_PATTERN_HTML}\\)",
- :replace_html => "Element\\.update\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)",
- :replace => "Element\\.replace\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)",
- :redirect => "window.location.href = #{RJS_ANY_ID}"
- }
- [:remove, :show, :hide, :toggle].each do |action|
- RJS_STATEMENTS[action] = "Element\\.#{action}\\(#{RJS_ANY_ID}\\)"
- end
- RJS_INSERTIONS = ["top", "bottom", "before", "after"]
- RJS_INSERTIONS.each do |insertion|
- RJS_STATEMENTS["insert_#{insertion}".to_sym] = "Element.insert\\(#{RJS_ANY_ID}, \\{ #{insertion}: #{RJS_PATTERN_HTML} \\}\\)"
- end
- RJS_STATEMENTS[:insert_html] = "Element.insert\\(#{RJS_ANY_ID}, \\{ (#{RJS_INSERTIONS.join('|')}): #{RJS_PATTERN_HTML} \\}\\)"
- RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})")
- RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/
-
- # +assert_select+ and +css_select+ call this to obtain the content in the HTML
- # page, or from all the RJS statements, depending on the type of response.
- def response_from_page_or_rjs()
- content_type = @response.content_type
-
- if content_type && Mime::JS =~ content_type
- body = @response.body.dup
- root = HTML::Node.new(nil)
-
- while true
- next if body.sub!(RJS_STATEMENTS[:any]) do |match|
- html = unescape_rjs(match)
- matches = HTML::Document.new(html).root.children.select { |n| n.tag? }
- root.children.concat matches
- ""
- end
- break
- end
-
- root
- else
- html_document.root
- end
- end
-
- # Unescapes a RJS string.
- def unescape_rjs(rjs_string)
- # RJS encodes double quotes and line breaks.
- unescaped= rjs_string.gsub('\"', '"')
- unescaped.gsub!(/\\\//, '/')
- unescaped.gsub!('\n', "\n")
- unescaped.gsub!('\076', '>')
- unescaped.gsub!('\074', '<')
- # RJS encodes non-ascii characters.
- unescaped.gsub!(RJS_PATTERN_UNICODE_ESCAPED_CHAR) {|u| [$1.hex].pack('U*')}
- unescaped
+ # +assert_select+ and +css_select+ call this to obtain the content in the HTML page.
+ def response_from_page
+ html_document.root
end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 174babb9aa..7d707d03a9 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -321,7 +321,7 @@ module ActionDispatch
define_method(method) do |*args|
reset! unless integration_session
# reset the html_document variable, but only for new get/post calls
- @html_document = nil unless method.among?("cookies", "assigns")
+ @html_document = nil unless method.in?(["cookies", "assigns"])
integration_session.__send__(method, *args).tap do
copy_session_variables!
end
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 60665387b6..4547aceb28 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -44,6 +44,7 @@ module ActionView
autoload :AbstractRenderer
autoload :PartialRenderer
autoload :TemplateRenderer
+ autoload :StreamingTemplateRenderer
end
autoload_at "action_view/template/resolver" do
@@ -53,6 +54,16 @@ module ActionView
autoload :FallbackFileSystemResolver
end
+ autoload_at "action_view/buffers" do
+ autoload :OutputBuffer
+ autoload :StreamingBuffer
+ end
+
+ autoload_at "action_view/flows" do
+ autoload :OutputFlow
+ autoload :StreamingFlow
+ end
+
autoload_at "action_view/template/error" do
autoload :MissingTemplate
autoload :ActionViewError
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 5519103627..87501d5b88 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -8,9 +8,8 @@ require 'action_view/log_subscriber'
module ActionView #:nodoc:
# = Action View Base
#
- # Action View templates can be written in three ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERB
+ # Action View templates can be written in several ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERb
# (included in Ruby) and HTML. If the template file has a <tt>.builder</tt> (or <tt>.rxml</tt>) extension then Jim Weirich's Builder::XmlMarkup library is used.
- # If the template file has a <tt>.rjs</tt> extension then it will use ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.
#
# == ERB
#
@@ -131,41 +130,19 @@ module ActionView #:nodoc:
# end
#
# More builder documentation can be found at http://builder.rubyforge.org.
- #
- # == JavaScriptGenerator
- #
- # JavaScriptGenerator templates end in <tt>.rjs</tt>. Unlike conventional templates which are used to
- # render the results of an action, these templates generate instructions on how to modify an already rendered page. This makes it easy to
- # modify multiple elements on your page in one declarative Ajax response. Actions with these templates are called in the background with Ajax
- # and make updates to the page where the request originated from.
- #
- # An instance of the JavaScriptGenerator object named +page+ is automatically made available to your template, which is implicitly wrapped in an ActionView::Helpers::PrototypeHelper#update_page block.
- #
- # When an <tt>.rjs</tt> action is called with +link_to_remote+, the generated JavaScript is automatically evaluated. Example:
- #
- # link_to_remote :url => {:action => 'delete'}
- #
- # The subsequently rendered <tt>delete.rjs</tt> might look like:
- #
- # page.replace_html 'sidebar', :partial => 'sidebar'
- # page.remove "person-#{@person.id}"
- # page.visual_effect :highlight, 'user-list'
- #
- # This refreshes the sidebar, removes a person element and highlights the user list.
- #
- # See the ActionView::Helpers::PrototypeHelper::JavaScriptGenerator::GeneratorMethods documentation for more details.
class Base
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).
- cattr_accessor :debug_rjs
- @@debug_rjs = false
-
# Specify the proc used to decorate input tags that refer to attributes with errors.
cattr_accessor :field_error_proc
@@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
+ # How to complete the streaming when an exception occurs.
+ # This is our best guess: first try to close the attribute, then the tag.
+ # Currently this is private API and may be changed at *any* time.
+ cattr_accessor :streaming_completion_on_exception
+ @@streaming_completion_on_exception = %("><script type="text/javascript">window.location = "/500.html"</script></html>)
+
class_attribute :helpers
class_attribute :_routes
@@ -182,7 +159,7 @@ module ActionView #:nodoc:
end
end
- attr_accessor :_template
+ attr_accessor :_template, :_view_flow
attr_internal :request, :controller, :config, :assigns, :lookup_context
delegate :formats, :formats=, :locale, :locale=, :view_paths, :view_paths=, :to => :lookup_context
@@ -210,8 +187,8 @@ module ActionView #:nodoc:
self.helpers = Module.new unless self.class.helpers
@_config = {}
- @_content_for = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
@_virtual_path = nil
+ @_view_flow = OutputFlow.new
@output_buffer = nil
if @_controller = controller
@@ -224,10 +201,6 @@ module ActionView #:nodoc:
@_lookup_context.formats = formats if formats
end
- def store_content_for(key, value)
- @_content_for[key] = value
- end
-
def controller_path
@controller_path ||= controller && controller.controller_path
end
diff --git a/actionpack/lib/action_view/buffers.rb b/actionpack/lib/action_view/buffers.rb
new file mode 100644
index 0000000000..089fc68706
--- /dev/null
+++ b/actionpack/lib/action_view/buffers.rb
@@ -0,0 +1,43 @@
+require 'active_support/core_ext/string/output_safety'
+
+module ActionView
+ class OutputBuffer < ActiveSupport::SafeBuffer #:nodoc:
+ def initialize(*)
+ super
+ encode! if encoding_aware?
+ end
+
+ def <<(value)
+ super(value.to_s)
+ end
+ alias :append= :<<
+ alias :safe_append= :safe_concat
+ end
+
+ class StreamingBuffer #:nodoc:
+ def initialize(block)
+ @block = block
+ end
+
+ def <<(value)
+ value = value.to_s
+ value = ERB::Util.h(value) unless value.html_safe?
+ @block.call(value)
+ end
+ alias :concat :<<
+ alias :append= :<<
+
+ def safe_concat(value)
+ @block.call(value.to_s)
+ end
+ alias :safe_append= :safe_concat
+
+ def html_safe?
+ true
+ end
+
+ def html_safe
+ self
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb
index 39d88333e8..a2a64de206 100644
--- a/actionpack/lib/action_view/context.rb
+++ b/actionpack/lib/action_view/context.rb
@@ -2,6 +2,7 @@ module ActionView
module CompiledTemplates #:nodoc:
# holds compiled template code
end
+
# = Action View Context
#
# Action View contexts are supplied to Action Controller to render template.
diff --git a/actionpack/lib/action_view/flows.rb b/actionpack/lib/action_view/flows.rb
new file mode 100644
index 0000000000..386a06511f
--- /dev/null
+++ b/actionpack/lib/action_view/flows.rb
@@ -0,0 +1,79 @@
+require 'active_support/core_ext/string/output_safety'
+
+module ActionView
+ class OutputFlow #:nodoc:
+ attr_reader :content
+
+ def initialize
+ @content = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
+ end
+
+ # Called by _layout_for to read stored values.
+ def get(key)
+ @content[key]
+ end
+
+ # Called by each renderer object to set the layout contents.
+ def set(key, value)
+ @content[key] = value
+ end
+
+ # Called by content_for
+ def append(key, value)
+ @content[key] << value
+ end
+
+ # Called by provide
+ def append!(key, value)
+ @content[key] << value
+ end
+ end
+
+ class StreamingFlow < OutputFlow #:nodoc:
+ def initialize(view, fiber)
+ @view = view
+ @parent = nil
+ @child = view.output_buffer
+ @content = view._view_flow.content
+ @fiber = fiber
+ @root = Fiber.current.object_id
+ end
+
+ # Try to get an stored content. If the content
+ # is not available and we are inside the layout
+ # fiber, we set that we are waiting for the given
+ # key and yield.
+ def get(key)
+ return super if @content.key?(key)
+
+ if inside_fiber?
+ view = @view
+
+ begin
+ @waiting_for = key
+ view.output_buffer, @parent = @child, view.output_buffer
+ Fiber.yield
+ ensure
+ @waiting_for = nil
+ view.output_buffer, @child = @parent, view.output_buffer
+ end
+ end
+
+ super
+ end
+
+ # Appends the contents for the given key. This is called
+ # by provides and resumes back to the fiber if it is
+ # the key it is waiting for.
+ def append!(key, value)
+ super
+ @fiber.resume if @waiting_for == key
+ end
+
+ private
+
+ def inside_fiber?
+ Fiber.current.object_id != @root
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb
index d338ce616a..205116f610 100644
--- a/actionpack/lib/action_view/helpers.rb
+++ b/actionpack/lib/action_view/helpers.rb
@@ -17,11 +17,10 @@ module ActionView #:nodoc:
autoload :FormTagHelper
autoload :JavaScriptHelper, "action_view/helpers/javascript_helper"
autoload :NumberHelper
- autoload :PrototypeHelper
autoload :OutputSafetyHelper
autoload :RecordTagHelper
autoload :SanitizeHelper
- autoload :ScriptaculousHelper
+ autoload :SprocketsHelper
autoload :TagHelper
autoload :TextHelper
autoload :TranslationHelper
@@ -47,11 +46,10 @@ module ActionView #:nodoc:
include FormTagHelper
include JavaScriptHelper
include NumberHelper
- include PrototypeHelper
include OutputSafetyHelper
include RecordTagHelper
include SanitizeHelper
- include ScriptaculousHelper
+ include SprocketsHelper
include TagHelper
include TextHelper
include TranslationHelper
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index f6b2d4f3f4..e859b3ae49 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -274,7 +274,11 @@ module ActionView
# The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
# plugin authors are encouraged to do so.
def image_path(source)
- asset_paths.compute_public_path(source, 'images')
+ if config.use_sprockets
+ asset_path(source)
+ else
+ asset_paths.compute_public_path(source, 'images')
+ end
end
alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
@@ -289,7 +293,11 @@ module ActionView
# video_path("/trailers/hd.avi") # => /trailers/hd.avi
# video_path("http://www.railsapplication.com/vid/hd.avi") # => http://www.railsapplication.com/vid/hd.avi
def video_path(source)
- asset_paths.compute_public_path(source, 'videos')
+ if config.use_sprockets
+ asset_path(source)
+ else
+ asset_paths.compute_public_path(source, 'videos')
+ end
end
alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
@@ -304,7 +312,11 @@ module ActionView
# audio_path("/sounds/horse.wav") # => /sounds/horse.wav
# audio_path("http://www.railsapplication.com/sounds/horse.wav") # => http://www.railsapplication.com/sounds/horse.wav
def audio_path(source)
- asset_paths.compute_public_path(source, 'audios')
+ if config.use_sprockets
+ asset_path(source)
+ else
+ asset_paths.compute_public_path(source, 'audios')
+ end
end
alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb
index 52eb43a1cd..e4662a2919 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb
@@ -8,9 +8,11 @@ module ActionView
module AssetTagHelper
class AssetIncludeTag
- attr_reader :config, :asset_paths
+ include TagHelper
+ attr_reader :config, :asset_paths
class_attribute :expansions
+
def self.inherited(base)
base.expansions = { }
end
@@ -56,7 +58,6 @@ module ActionView
end
end
-
private
def path_to_asset(source, include_host = true)
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
index 014a03c54d..955634bb19 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
@@ -26,18 +26,18 @@ module ActionView
# roots. Rewrite the asset path for cache-busting asset ids. Include
# asset host, if configured, with the correct request protocol.
def compute_public_path(source, dir, ext = nil, include_host = true)
+ source = source.to_s
return source if is_uri?(source)
source = rewrite_extension(source, dir, ext) if ext
source = "/#{dir}/#{source}" unless source[0] == ?/
- if controller.respond_to?(:env) && controller.env["action_dispatch.asset_path"]
- source = rewrite_asset_path(source, controller.env["action_dispatch.asset_path"])
- end
- source = rewrite_asset_path(source, config.asset_path)
+ source = rewrite_asset_path(source, dir)
- has_request = controller.respond_to?(:request)
- source = rewrite_relative_url_root(source, controller.config.relative_url_root) if has_request && include_host
- source = rewrite_host_and_protocol(source, has_request) if include_host
+ if controller && include_host
+ has_request = controller.respond_to?(:request)
+ source = rewrite_relative_url_root(source, controller.config.relative_url_root) if has_request
+ source = rewrite_host_and_protocol(source, has_request)
+ end
source
end
@@ -73,6 +73,8 @@ module ActionView
# Break out the asset path rewrite in case plugins wish to put the asset id
# someplace other than the query string.
def rewrite_asset_path(source, path = nil)
+ path = config.asset_path
+
if path && path.respond_to?(:call)
return path.call(source)
elsif path && path.is_a?(String)
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
index 82bbfcc7d2..07ff49659a 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
@@ -1,6 +1,5 @@
require 'active_support/concern'
require 'active_support/core_ext/file'
-require 'action_view/helpers/tag_helper'
require 'action_view/helpers/asset_tag_helpers/asset_include_tag'
module ActionView
@@ -8,8 +7,6 @@ module ActionView
module AssetTagHelper
class JavascriptIncludeTag < AssetIncludeTag
- include TagHelper
-
def asset_name
'javascript'
end
@@ -86,99 +83,119 @@ module ActionView
# javascript_path "http://www.railsapplication.com/js/xmlhr" # => http://www.railsapplication.com/js/xmlhr
# javascript_path "http://www.railsapplication.com/js/xmlhr.js" # => http://www.railsapplication.com/js/xmlhr.js
def javascript_path(source)
- asset_paths.compute_public_path(source, 'javascripts', 'js')
+ if config.use_sprockets
+ asset_path(source, 'js')
+ else
+ asset_paths.compute_public_path(source, 'javascripts', 'js')
+ end
end
alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
- # Returns an HTML script tag for each of the +sources+ provided. You
- # can pass in the filename (.js extension is optional) of JavaScript files
- # that exist in your <tt>public/javascripts</tt> directory for inclusion into the
- # current page or you can pass the full path relative to your document
- # root. To include the Prototype and Scriptaculous JavaScript libraries in
- # your application, pass <tt>:defaults</tt> as the source. When using
- # <tt>:defaults</tt>, if an <tt>application.js</tt> file exists in
- # <tt>public/javascripts</tt> it will be included as well. You can modify the
- # HTML attributes of the script tag by passing a hash as the last argument.
+ # Returns an HTML script tag for each of the +sources+ provided.
+ #
+ # Sources may be paths to JavaScript files. Relative paths are assumed to be relative
+ # to <tt>public/javascripts</tt>, full paths are assumed to be relative to the document
+ # root. Relative paths are idiomatic, use absolute paths only when needed.
+ #
+ # When passing paths, the ".js" extension is optional.
+ #
+ # To include the default JavaScript expansion pass <tt>:defaults</tt> as source.
+ # By default, <tt>:defaults</tt> loads jQuery. If the application was generated
+ # with "-j prototype" the libraries Prototype and Scriptaculous are loaded instead.
+ # In any case, the defaults can be overridden in <tt>config/application.rb</tt>:
+ #
+ # config.action_view.javascript_expansions[:defaults] = %w(foo.js bar.js)
+ #
+ # When using <tt>:defaults</tt>, if an <tt>application.js</tt> file exists in
+ # <tt>public/javascripts</tt> it will be included as well at the end.
+ #
+ # You can modify the HTML attributes of the script tag by passing a hash as the
+ # last argument.
#
# ==== Examples
- # javascript_include_tag "xmlhr" # =>
- # <script type="text/javascript" src="/javascripts/xmlhr.js?1284139606"></script>
+ # javascript_include_tag "xmlhr"
+ # # => <script type="text/javascript" src="/javascripts/xmlhr.js?1284139606"></script>
#
- # javascript_include_tag "xmlhr.js" # =>
- # <script type="text/javascript" src="/javascripts/xmlhr.js?1284139606"></script>
+ # javascript_include_tag "xmlhr.js"
+ # # => <script type="text/javascript" src="/javascripts/xmlhr.js?1284139606"></script>
#
- # javascript_include_tag "common.javascript", "/elsewhere/cools" # =>
- # <script type="text/javascript" src="/javascripts/common.javascript?1284139606"></script>
- # <script type="text/javascript" src="/elsewhere/cools.js?1423139606"></script>
+ # javascript_include_tag "common.javascript", "/elsewhere/cools"
+ # # => <script type="text/javascript" src="/javascripts/common.javascript?1284139606"></script>
+ # # <script type="text/javascript" src="/elsewhere/cools.js?1423139606"></script>
#
- # javascript_include_tag "http://www.railsapplication.com/xmlhr" # =>
- # <script type="text/javascript" src="http://www.railsapplication.com/xmlhr.js?1284139606"></script>
+ # javascript_include_tag "http://www.railsapplication.com/xmlhr"
+ # # => <script type="text/javascript" src="http://www.railsapplication.com/xmlhr.js?1284139606"></script>
#
- # javascript_include_tag "http://www.railsapplication.com/xmlhr.js" # =>
- # <script type="text/javascript" src="http://www.railsapplication.com/xmlhr.js?1284139606"></script>
+ # javascript_include_tag "http://www.railsapplication.com/xmlhr.js"
+ # # => <script type="text/javascript" src="http://www.railsapplication.com/xmlhr.js?1284139606"></script>
#
- # javascript_include_tag :defaults # =>
- # <script type="text/javascript" src="/javascripts/prototype.js?1284139606"></script>
- # <script type="text/javascript" src="/javascripts/effects.js?1284139606"></script>
- # ...
- # <script type="text/javascript" src="/javascripts/application.js?1284139606"></script>
+ # javascript_include_tag :defaults
+ # # => <script type="text/javascript" src="/javascripts/jquery.js?1284139606"></script>
+ # # <script type="text/javascript" src="/javascripts/rails.js?1284139606"></script>
+ # # <script type="text/javascript" src="/javascripts/application.js?1284139606"></script>
#
# * = The application.js file is only referenced if it exists
#
- # You can also include all javascripts in the +javascripts+ directory using <tt>:all</tt> as the source:
+ # You can also include all JavaScripts in the +javascripts+ directory using <tt>:all</tt> as the source:
#
- # javascript_include_tag :all # =>
- # <script type="text/javascript" src="/javascripts/prototype.js?1284139606"></script>
- # <script type="text/javascript" src="/javascripts/effects.js?1284139606"></script>
- # ...
- # <script type="text/javascript" src="/javascripts/application.js?1284139606"></script>
- # <script type="text/javascript" src="/javascripts/shop.js?1284139606"></script>
- # <script type="text/javascript" src="/javascripts/checkout.js?1284139606"></script>
+ # javascript_include_tag :all
+ # # => <script type="text/javascript" src="/javascripts/jquery.js?1284139606"></script>
+ # # <script type="text/javascript" src="/javascripts/rails.js?1284139606"></script>
+ # # <script type="text/javascript" src="/javascripts/application.js?1284139606"></script>
+ # # <script type="text/javascript" src="/javascripts/shop.js?1284139606"></script>
+ # # <script type="text/javascript" src="/javascripts/checkout.js?1284139606"></script>
#
- # Note that the default javascript files will be included first. So Prototype and Scriptaculous are available to
- # all subsequently included files.
+ # Note that your defaults of choice will be included first, so they will be available to all subsequently
+ # included files.
#
- # If you want Rails to search in all the subdirectories under javascripts, you should explicitly set <tt>:recursive</tt>:
+ # If you want Rails to search in all the subdirectories under <tt>public/javascripts</tt>, you should
+ # explicitly set <tt>:recursive</tt>:
#
# javascript_include_tag :all, :recursive => true
#
- # == Caching multiple javascripts into one
+ # == Caching multiple JavaScripts into one
#
- # You can also cache multiple javascripts into one file, which requires less HTTP connections to download and can better be
- # compressed by gzip (leading to faster transfers). Caching will only happen if config.perform_caching
- # is set to <tt>true</tt> (which is the case by default for the Rails production environment, but not for the development
- # environment).
+ # You can also cache multiple JavaScripts into one file, which requires less HTTP connections to download
+ # and can better be compressed by gzip (leading to faster transfers). Caching will only happen if
+ # <tt>config.perform_caching</tt> is set to true (which is the case by default for the Rails
+ # production environment, but not for the development environment).
#
# ==== Examples
- # javascript_include_tag :all, :cache => true # when config.perform_caching is false =>
- # <script type="text/javascript" src="/javascripts/prototype.js?1284139606"></script>
- # <script type="text/javascript" src="/javascripts/effects.js?1284139606"></script>
- # ...
- # <script type="text/javascript" src="/javascripts/application.js?1284139606"></script>
- # <script type="text/javascript" src="/javascripts/shop.js?1284139606"></script>
- # <script type="text/javascript" src="/javascripts/checkout.js?1284139606"></script>
#
- # javascript_include_tag :all, :cache => true # when config.perform_caching is true =>
- # <script type="text/javascript" src="/javascripts/all.js?1344139789"></script>
+ # # assuming config.perform_caching is false
+ # javascript_include_tag :all, :cache => true
+ # # => <script type="text/javascript" src="/javascripts/jquery.js?1284139606"></script>
+ # # <script type="text/javascript" src="/javascripts/rails.js?1284139606"></script>
+ # # <script type="text/javascript" src="/javascripts/application.js?1284139606"></script>
+ # # <script type="text/javascript" src="/javascripts/shop.js?1284139606"></script>
+ # # <script type="text/javascript" src="/javascripts/checkout.js?1284139606"></script>
+ #
+ # # assuming config.perform_caching is true
+ # javascript_include_tag :all, :cache => true
+ # # => <script type="text/javascript" src="/javascripts/all.js?1344139789"></script>
#
- # javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when config.perform_caching is false =>
- # <script type="text/javascript" src="/javascripts/prototype.js?1284139606"></script>
- # <script type="text/javascript" src="/javascripts/cart.js?1289139157"></script>
- # <script type="text/javascript" src="/javascripts/checkout.js?1299139816"></script>
+ # # assuming config.perform_caching is false
+ # javascript_include_tag "jquery", "cart", "checkout", :cache => "shop"
+ # # => <script type="text/javascript" src="/javascripts/jquery.js?1284139606"></script>
+ # # <script type="text/javascript" src="/javascripts/cart.js?1289139157"></script>
+ # # <script type="text/javascript" src="/javascripts/checkout.js?1299139816"></script>
#
- # javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when config.perform_caching is true =>
- # <script type="text/javascript" src="/javascripts/shop.js?1299139816"></script>
+ # # assuming config.perform_caching is true
+ # javascript_include_tag "jquery", "cart", "checkout", :cache => "shop"
+ # # => <script type="text/javascript" src="/javascripts/shop.js?1299139816"></script>
#
# The <tt>:recursive</tt> option is also available for caching:
#
# javascript_include_tag :all, :cache => true, :recursive => true
def javascript_include_tag(*sources)
- @javascript_include ||= JavascriptIncludeTag.new(config, asset_paths)
- @javascript_include.include_tag(*sources)
+ if config.use_sprockets
+ sprockets_javascript_include_tag(*sources)
+ else
+ @javascript_include ||= JavascriptIncludeTag.new(config, asset_paths)
+ @javascript_include.include_tag(*sources)
+ end
end
-
end
-
end
end
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
index a48c87b49a..c3dcd410bb 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
@@ -1,6 +1,5 @@
require 'active_support/concern'
require 'active_support/core_ext/file'
-require 'action_view/helpers/tag_helper'
require 'action_view/helpers/asset_tag_helpers/asset_include_tag'
module ActionView
@@ -8,8 +7,6 @@ module ActionView
module AssetTagHelper
class StylesheetIncludeTag < AssetIncludeTag
- include TagHelper
-
def asset_name
'stylesheet'
end
@@ -63,7 +60,11 @@ module ActionView
# stylesheet_path "http://www.railsapplication.com/css/style" # => http://www.railsapplication.com/css/style
# stylesheet_path "http://www.railsapplication.com/css/style.css" # => http://www.railsapplication.com/css/style.css
def stylesheet_path(source)
- asset_paths.compute_public_path(source, 'stylesheets', 'css')
+ if config.use_sprockets
+ asset_path(source, 'css')
+ else
+ asset_paths.compute_public_path(source, 'stylesheets', 'css')
+ end
end
alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
@@ -136,8 +137,12 @@ module ActionView
# stylesheet_link_tag :all, :concat => true
#
def stylesheet_link_tag(*sources)
- @stylesheet_include ||= StylesheetIncludeTag.new(config, asset_paths)
- @stylesheet_include.include_tag(*sources)
+ if config.use_sprockets
+ sprockets_stylesheet_link_tag(*sources)
+ else
+ @stylesheet_include ||= StylesheetIncludeTag.new(config, asset_paths)
+ @stylesheet_include.include_tag(*sources)
+ end
end
end
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 9ac7dff1ec..0139714240 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -107,8 +107,8 @@ module ActionView
# <%= javascript_include_tag :defaults %>
# <% end %>
#
- # That will place <tt>script</tt> tags for Prototype, Scriptaculous, and application.js (if it exists)
- # on the page; this technique is useful if you'll only be using these scripts in a few views.
+ # That will place +script+ tags for your default set of JavaScript files on the page;
+ # this technique is useful if you'll only be using these scripts in a few views.
#
# Note that content_for concatenates the blocks it is given for a particular
# identifier in order. For example:
@@ -135,8 +135,19 @@ module ActionView
# for elements that will be fragment cached.
def content_for(name, content = nil, &block)
content = capture(&block) if block_given?
- @_content_for[name] << content if content
- @_content_for[name] unless content
+ result = @_view_flow.append(name, content) if content
+ result unless content
+ end
+
+ # The same as +content_for+ but when used with streaming flushes
+ # straight back to the layout. In other words, if you want to
+ # concatenate several times to the same buffer when rendering a given
+ # template, you should use +content_for+, if not, use +provide+ to tell
+ # the layout to stop looking for more contents.
+ def provide(name, content = nil, &block)
+ content = capture(&block) if block_given?
+ result = @_view_flow.append!(name, content) if content
+ result unless content
end
# content_for? simply checks whether any content has been captured yet using content_for
@@ -158,7 +169,7 @@ module ActionView
# </body>
# </html>
def content_for?(name)
- @_content_for[name].present?
+ @_view_flow.get(name).present?
end
# Use an alternate output buffer for the duration of the block.
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 6dbd2e3e43..9277359d5c 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -26,9 +26,9 @@ module ActionView
# 30 secs <-> 1 min, 29 secs # => 1 minute
# 1 min, 30 secs <-> 44 mins, 29 secs # => [2..44] minutes
# 44 mins, 30 secs <-> 89 mins, 29 secs # => about 1 hour
- # 89 mins, 29 secs <-> 23 hrs, 59 mins, 29 secs # => about [2..24] hours
- # 23 hrs, 59 mins, 29 secs <-> 47 hrs, 59 mins, 29 secs # => 1 day
- # 47 hrs, 59 mins, 29 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days
+ # 89 mins, 30 secs <-> 23 hrs, 59 mins, 29 secs # => about [2..24] hours
+ # 23 hrs, 59 mins, 30 secs <-> 41 hrs, 59 mins, 29 secs # => 1 day
+ # 41 hrs, 59 mins, 30 secs <-> 29 days, 23 hrs, 59 mins, 29 secs # => [2..29] days
# 29 days, 23 hrs, 59 mins, 30 secs <-> 59 days, 23 hrs, 59 mins, 29 secs # => about 1 month
# 59 days, 23 hrs, 59 mins, 30 secs <-> 1 yr minus 1 sec # => [2..12] months
# 1 yr <-> 1 yr, 3 months # => about 1 year
@@ -89,8 +89,8 @@ module ActionView
when 2..44 then locale.t :x_minutes, :count => distance_in_minutes
when 45..89 then locale.t :about_x_hours, :count => 1
when 90..1439 then locale.t :about_x_hours, :count => (distance_in_minutes.to_f / 60.0).round
- when 1440..2529 then locale.t :x_days, :count => 1
- when 2530..43199 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round
+ when 1440..2519 then locale.t :x_days, :count => 1
+ when 2520..43199 then locale.t :x_days, :count => (distance_in_minutes.to_f / 1440.0).round
when 43200..86399 then locale.t :about_x_months, :count => 1
when 86400..525599 then locale.t :x_months, :count => (distance_in_minutes.to_f / 43200.0).round
else
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 9025d9e24c..440acafa88 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -610,10 +610,11 @@ module ActionView
# label(:post, :body)
# # => <label for="post_body">Write your entire text here</label>
#
- # Localization can also be based purely on the translation of the attribute-name like this:
+ # Localization can also be based purely on the translation of the attribute-name
+ # (if you are using ActiveRecord):
#
- # activemodel:
- # attribute:
+ # activerecord:
+ # attributes:
# post:
# cost: "Total cost"
#
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index a19ba7a968..d7228bab67 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -1,42 +1,8 @@
require 'action_view/helpers/tag_helper'
module ActionView
- # = Action View JavaScript Helpers
module Helpers
- # Provides functionality for working with JavaScript in your views.
- #
- # == Ajax, controls and visual effects
- #
- # * For information on using Ajax, see
- # ActionView::Helpers::PrototypeHelper.
- # * For information on using controls and visual effects, see
- # ActionView::Helpers::ScriptaculousHelper.
- #
- # == Including the JavaScript libraries into your pages
- #
- # Rails includes the Prototype JavaScript framework and the Scriptaculous
- # JavaScript controls and visual effects library. If you wish to use
- # these libraries and their helpers (ActionView::Helpers::PrototypeHelper
- # and ActionView::Helpers::ScriptaculousHelper), you must do one of the
- # following:
- #
- # * Use <tt><%= javascript_include_tag :defaults %></tt> in the HEAD
- # section of your page (recommended): This function will return
- # references to the JavaScript files created by the +rails+ command in
- # your <tt>public/javascripts</tt> directory. Using it is recommended as
- # the browser can then cache the libraries instead of fetching all the
- # functions anew on every request.
- # * Use <tt><%= javascript_include_tag 'prototype' %></tt>: As above, but
- # will only include the Prototype core library, which means you are able
- # to use all basic AJAX functionality. For the Scriptaculous-based
- # JavaScript helpers, like visual effects, autocompletion, drag and drop
- # and so on, you should use the method described above.
- #
- # For documentation on +javascript_include_tag+ see
- # ActionView::Helpers::AssetTagHelper.
module JavaScriptHelper
- include PrototypeHelper
-
JS_ESCAPE_MAP = {
'\\' => '\\\\',
'</' => '<\/',
@@ -96,87 +62,34 @@ module ActionView
"\n//#{cdata_section("\n#{content}\n//")}\n".html_safe
end
- # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the
- # onclick handler.
- #
- # The first argument +name+ is used as the button's value or display text.
- #
- # The next arguments are optional and may include the javascript function definition and a hash of html_options.
+ # Returns a button whose +onclick+ handler triggers the passed JavaScript.
#
- # The +function+ argument can be omitted in favor of an +update_page+
- # block, which evaluates to a string when the template is rendered
- # (instead of making an Ajax request first).
+ # The helper receives a name, JavaScript code, and an optional hash of HTML options. The
+ # name is used as button label and the JavaScript code goes into its +onclick+ attribute.
+ # If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+.
#
- # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
+ # button_to_function "Greeting", "alert('Hello world!')", :class => "ok"
+ # # => <input class="ok" onclick="alert('Hello world!');" type="button" value="Greeting" />
#
- # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
- #
- # Examples:
- # button_to_function "Greeting", "alert('Hello world!')"
- # button_to_function "Delete", "if (confirm('Really?')) do_delete()"
- # button_to_function "Details" do |page|
- # page[:details].visual_effect :toggle_slide
- # end
- # button_to_function "Details", :class => "details_button" do |page|
- # page[:details].visual_effect :toggle_slide
- # end
- def button_to_function(name, *args, &block)
- html_options = args.extract_options!.symbolize_keys
-
- function = block_given? ? update_page(&block) : args[0] || ''
+ def button_to_function(name, function=nil, html_options={})
onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function};"
tag(:input, html_options.merge(:type => 'button', :value => name, :onclick => onclick))
end
- # Returns a link of the given +name+ that will trigger a JavaScript +function+ using the
- # onclick handler and return false after the fact.
- #
- # The first argument +name+ is used as the link text.
+ # Returns a link whose +onclick+ handler triggers the passed JavaScript.
#
- # The next arguments are optional and may include the javascript function definition and a hash of html_options.
+ # The helper receives a name, JavaScript code, and an optional hash of HTML options. The
+ # name is used as the link text and the JavaScript code goes into the +onclick+ attribute.
+ # If +html_options+ has an <tt>:onclick</tt>, that one is put before +function+. Once all
+ # the JavaScript is set, the helper appends "; return false;".
#
- # The +function+ argument can be omitted in favor of an +update_page+
- # block, which evaluates to a string when the template is rendered
- # (instead of making an Ajax request first).
+ # The +href+ attribute of the tag is set to "#" unles +html_options+ has one.
#
- # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button"
+ # link_to_function "Greeting", "alert('Hello world!')", :class => "nav_link"
+ # # => <a class="nav_link" href="#" onclick="alert('Hello world!'); return false;">Greeting</a>
#
- # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil
- #
- #
- # Examples:
- # link_to_function "Greeting", "alert('Hello world!')"
- # Produces:
- # <a onclick="alert('Hello world!'); return false;" href="#">Greeting</a>
- #
- # link_to_function(image_tag("delete"), "if (confirm('Really?')) do_delete()")
- # Produces:
- # <a onclick="if (confirm('Really?')) do_delete(); return false;" href="#">
- # <img src="/images/delete.png?" alt="Delete"/>
- # </a>
- #
- # link_to_function("Show me more", nil, :id => "more_link") do |page|
- # page[:details].visual_effect :toggle_blind
- # page[:more_link].replace_html "Show me less"
- # end
- # Produces:
- # <a href="#" id="more_link" onclick="try {
- # $(&quot;details&quot;).visualEffect(&quot;toggle_blind&quot;);
- # $(&quot;more_link&quot;).update(&quot;Show me less&quot;);
- # }
- # catch (e) {
- # alert('RJS error:\n\n' + e.toString());
- # alert('$(\&quot;details\&quot;).visualEffect(\&quot;toggle_blind\&quot;);
- # \n$(\&quot;more_link\&quot;).update(\&quot;Show me less\&quot;);');
- # throw e
- # };
- # return false;">Show me more</a>
- #
- def link_to_function(name, *args, &block)
- html_options = args.extract_options!.symbolize_keys
-
- function = block_given? ? update_page(&block) : args[0] || ''
+ def link_to_function(name, function, html_options={})
onclick = "#{"#{html_options[:onclick]}; " if html_options[:onclick]}#{function}; return false;"
href = html_options[:href] || '#'
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
deleted file mode 100644
index 506db24dc2..0000000000
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ /dev/null
@@ -1,852 +0,0 @@
-require 'set'
-require 'active_support/json'
-require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/string/output_safety'
-
-module ActionView
- # = Action View Prototype Helpers
- module Helpers
- # Prototype[http://www.prototypejs.org/] is a JavaScript library that provides
- # DOM[http://en.wikipedia.org/wiki/Document_Object_Model] manipulation,
- # Ajax[http://www.adaptivepath.com/publications/essays/archives/000385.php]
- # functionality, and more traditional object-oriented facilities for JavaScript.
- # This module provides a set of helpers to make it more convenient to call
- # functions from Prototype using Rails, including functionality to call remote
- # Rails methods (that is, making a background request to a Rails action) using Ajax.
- # This means that you can call actions in your controllers without
- # reloading the page, but still update certain parts of it using
- # injections into the DOM. A common use case is having a form that adds
- # a new element to a list without reloading the page or updating a shopping
- # cart total when a new item is added.
- #
- # == Usage
- # To be able to use these helpers, you must first include the Prototype
- # JavaScript framework in your pages.
- #
- # javascript_include_tag 'prototype'
- #
- # (See the documentation for
- # ActionView::Helpers::JavaScriptHelper for more information on including
- # this and other JavaScript files in your Rails templates.)
- #
- # Now you're ready to call a remote action either through a link...
- #
- # link_to_remote "Add to cart",
- # :url => { :action => "add", :id => product.id },
- # :update => { :success => "cart", :failure => "error" }
- #
- # ...through a form...
- #
- # <%= form_remote_tag :url => '/shipping' do -%>
- # <div><%= submit_tag 'Recalculate Shipping' %></div>
- # <% end -%>
- #
- # As you can see, there are numerous ways to use Prototype's Ajax functions (and actually more than
- # are listed here); check out the documentation for each method to find out more about its usage and options.
- #
- # === Common Options
- # See link_to_remote for documentation of options common to all Ajax
- # helpers; any of the options specified by link_to_remote can be used
- # by the other helpers.
- #
- # == Designing your Rails actions for Ajax
- # When building your action handlers (that is, the Rails actions that receive your background requests), it's
- # important to remember a few things. First, whatever your action would normally return to the browser, it will
- # return to the Ajax call. As such, you typically don't want to render with a layout. This call will cause
- # the layout to be transmitted back to your page, and, if you have a full HTML/CSS, will likely mess a lot of things up.
- # You can turn the layout off on particular actions by doing the following:
- #
- # class SiteController < ActionController::Base
- # layout "standard", :except => [:ajax_method, :more_ajax, :another_ajax]
- # end
- #
- # Optionally, you could do this in the method you wish to lack a layout:
- #
- # render :layout => false
- #
- # You can tell the type of request from within your action using the <tt>request.xhr?</tt> (XmlHttpRequest, the
- # method that Ajax uses to make background requests) method.
- # def name
- # # Is this an XmlHttpRequest request?
- # if (request.xhr?)
- # render :text => @name.to_s
- # else
- # # No? Then render an action.
- # render :action => 'view_attribute', :attr => @name
- # end
- # end
- #
- # The else clause can be left off and the current action will render with full layout and template. An extension
- # to this solution was posted to Ryan Heneise's blog at ArtOfMission["http://www.artofmission.com/"].
- #
- # layout proc{ |c| c.request.xhr? ? false : "application" }
- #
- # Dropping this in your ApplicationController turns the layout off for every request that is an "xhr" request.
- #
- # If you are just returning a little data or don't want to build a template for your output, you may opt to simply
- # render text output, like this:
- #
- # render :text => 'Return this from my method!'
- #
- # Since whatever the method returns is injected into the DOM, this will simply inject some text (or HTML, if you
- # tell it to). This is usually how small updates, such updating a cart total or a file count, are handled.
- #
- # == Updating multiple elements
- # See JavaScriptGenerator for information on updating multiple elements
- # on the page in an Ajax response.
- module PrototypeHelper
- CALLBACKS = Set.new([ :create, :uninitialized, :loading, :loaded,
- :interactive, :complete, :failure, :success ] +
- (100..599).to_a)
- AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url,
- :asynchronous, :method, :insertion, :position,
- :form, :with, :update, :script, :type ]).merge(CALLBACKS)
-
- # Returns the JavaScript needed for a remote function.
- # See the link_to_remote documentation at https://github.com/rails/prototype_legacy_helper as it takes the same arguments.
- #
- # Example:
- # # Generates: <select id="options" onchange="new Ajax.Updater('options',
- # # '/testing/update_options', {asynchronous:true, evalScripts:true})">
- # <select id="options" onchange="<%= remote_function(:update => "options",
- # :url => { :action => :update_options }) %>">
- # <option value="0">Hello</option>
- # <option value="1">World</option>
- # </select>
- def remote_function(options)
- javascript_options = options_for_ajax(options)
-
- update = ''
- if options[:update] && options[:update].is_a?(Hash)
- update = []
- update << "success:'#{options[:update][:success]}'" if options[:update][:success]
- update << "failure:'#{options[:update][:failure]}'" if options[:update][:failure]
- update = '{' + update.join(',') + '}'
- elsif options[:update]
- update << "'#{options[:update]}'"
- end
-
- function = update.empty? ?
- "new Ajax.Request(" :
- "new Ajax.Updater(#{update}, "
-
- url_options = options[:url]
- function << "'#{ERB::Util.html_escape(escape_javascript(url_for(url_options)))}'"
- function << ", #{javascript_options})"
-
- function = "#{options[:before]}; #{function}" if options[:before]
- function = "#{function}; #{options[:after]}" if options[:after]
- function = "if (#{options[:condition]}) { #{function}; }" if options[:condition]
- function = "if (confirm('#{escape_javascript(options[:confirm])}')) { #{function}; }" if options[:confirm]
-
- return function.html_safe
- end
-
- # All the methods were moved to GeneratorMethods so that
- # #include_helpers_from_context has nothing to overwrite.
- class JavaScriptGenerator #:nodoc:
- def initialize(context, &block) #:nodoc:
- @context, @lines = context, []
- include_helpers_from_context
- @context.with_output_buffer(@lines) do
- @context.instance_exec(self, &block)
- end
- end
-
- private
- def include_helpers_from_context
- extend @context.helpers if @context.respond_to?(:helpers)
- extend GeneratorMethods
- end
-
- # JavaScriptGenerator generates blocks of JavaScript code that allow you
- # to change the content and presentation of multiple DOM elements. Use
- # this in your Ajax response bodies, either in a <tt>\<script></tt> tag
- # or as plain JavaScript sent with a Content-type of "text/javascript".
- #
- # Create new instances with PrototypeHelper#update_page or with
- # ActionController::Base#render, then call +insert_html+, +replace_html+,
- # +remove+, +show+, +hide+, +visual_effect+, or any other of the built-in
- # methods on the yielded generator in any order you like to modify the
- # content and appearance of the current page.
- #
- # Example:
- #
- # # Generates:
- # # new Element.insert("list", { bottom: "<li>Some item</li>" });
- # # new Effect.Highlight("list");
- # # ["status-indicator", "cancel-link"].each(Element.hide);
- # update_page do |page|
- # page.insert_html :bottom, 'list', "<li>#{@item.name}</li>"
- # page.visual_effect :highlight, 'list'
- # page.hide 'status-indicator', 'cancel-link'
- # end
- #
- #
- # Helper methods can be used in conjunction with JavaScriptGenerator.
- # When a helper method is called inside an update block on the +page+
- # object, that method will also have access to a +page+ object.
- #
- # Example:
- #
- # module ApplicationHelper
- # def update_time
- # page.replace_html 'time', Time.now.to_s(:db)
- # page.visual_effect :highlight, 'time'
- # end
- # end
- #
- # # Controller action
- # def poll
- # render(:update) { |page| page.update_time }
- # end
- #
- # Calls to JavaScriptGenerator not matching a helper method below
- # generate a proxy to the JavaScript Class named by the method called.
- #
- # Examples:
- #
- # # Generates:
- # # Foo.init();
- # update_page do |page|
- # page.foo.init
- # end
- #
- # # Generates:
- # # Event.observe('one', 'click', function () {
- # # $('two').show();
- # # });
- # update_page do |page|
- # page.event.observe('one', 'click') do |p|
- # p[:two].show
- # end
- # end
- #
- # You can also use PrototypeHelper#update_page_tag instead of
- # PrototypeHelper#update_page to wrap the generated JavaScript in a
- # <tt>\<script></tt> tag.
- module GeneratorMethods
- def to_s #:nodoc:
- (@lines * $/).tap do |javascript|
- if ActionView::Base.debug_rjs
- source = javascript.dup
- javascript.replace "try {\n#{source}\n} catch (e) "
- javascript << "{ alert('RJS error:\\n\\n' + e.toString()); alert('#{source.gsub('\\','\0\0').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }}'); throw e }"
- end
- end
- end
-
- # Returns a element reference by finding it through +id+ in the DOM. This element can then be
- # used for further method calls. Examples:
- #
- # page['blank_slate'] # => $('blank_slate');
- # page['blank_slate'].show # => $('blank_slate').show();
- # page['blank_slate'].show('first').up # => $('blank_slate').show('first').up();
- #
- # You can also pass in a record, which will use ActionController::RecordIdentifier.dom_id to lookup
- # the correct id:
- #
- # page[@post] # => $('post_45')
- # page[Post.new] # => $('new_post')
- def [](id)
- case id
- when String, Symbol, NilClass
- JavaScriptElementProxy.new(self, id)
- else
- JavaScriptElementProxy.new(self, ActionController::RecordIdentifier.dom_id(id))
- end
- end
-
- # Returns an object whose <tt>to_json</tt> evaluates to +code+. Use this to pass a literal JavaScript
- # expression as an argument to another JavaScriptGenerator method.
- def literal(code)
- ::ActiveSupport::JSON::Variable.new(code.to_s)
- end
-
- # Returns a collection reference by finding it through a CSS +pattern+ in the DOM. This collection can then be
- # used for further method calls. Examples:
- #
- # page.select('p') # => $$('p');
- # page.select('p.welcome b').first # => $$('p.welcome b').first();
- # page.select('p.welcome b').first.hide # => $$('p.welcome b').first().hide();
- #
- # You can also use prototype enumerations with the collection. Observe:
- #
- # # Generates: $$('#items li').each(function(value) { value.hide(); });
- # page.select('#items li').each do |value|
- # value.hide
- # end
- #
- # Though you can call the block param anything you want, they are always rendered in the
- # javascript as 'value, index.' Other enumerations, like collect() return the last statement:
- #
- # # Generates: var hidden = $$('#items li').collect(function(value, index) { return value.hide(); });
- # page.select('#items li').collect('hidden') do |item|
- # item.hide
- # end
- #
- def select(pattern)
- JavaScriptElementCollectionProxy.new(self, pattern)
- end
-
- # Inserts HTML at the specified +position+ relative to the DOM element
- # identified by the given +id+.
- #
- # +position+ may be one of:
- #
- # <tt>:top</tt>:: HTML is inserted inside the element, before the
- # element's existing content.
- # <tt>:bottom</tt>:: HTML is inserted inside the element, after the
- # element's existing content.
- # <tt>:before</tt>:: HTML is inserted immediately preceding the element.
- # <tt>:after</tt>:: HTML is inserted immediately following the element.
- #
- # +options_for_render+ may be either a string of HTML to insert, or a hash
- # of options to be passed to ActionView::Base#render. For example:
- #
- # # Insert the rendered 'navigation' partial just before the DOM
- # # element with ID 'content'.
- # # Generates: Element.insert("content", { before: "-- Contents of 'navigation' partial --" });
- # page.insert_html :before, 'content', :partial => 'navigation'
- #
- # # Add a list item to the bottom of the <ul> with ID 'list'.
- # # Generates: Element.insert("list", { bottom: "<li>Last item</li>" });
- # page.insert_html :bottom, 'list', '<li>Last item</li>'
- #
- def insert_html(position, id, *options_for_render)
- content = javascript_object_for(render(*options_for_render))
- record "Element.insert(\"#{id}\", { #{position.to_s.downcase}: #{content} });"
- end
-
- # Replaces the inner HTML of the DOM element with the given +id+.
- #
- # +options_for_render+ may be either a string of HTML to insert, or a hash
- # of options to be passed to ActionView::Base#render. For example:
- #
- # # Replace the HTML of the DOM element having ID 'person-45' with the
- # # 'person' partial for the appropriate object.
- # # Generates: Element.update("person-45", "-- Contents of 'person' partial --");
- # page.replace_html 'person-45', :partial => 'person', :object => @person
- #
- def replace_html(id, *options_for_render)
- call 'Element.update', id, render(*options_for_render)
- end
-
- # Replaces the "outer HTML" (i.e., the entire element, not just its
- # contents) of the DOM element with the given +id+.
- #
- # +options_for_render+ may be either a string of HTML to insert, or a hash
- # of options to be passed to ActionView::Base#render. For example:
- #
- # # Replace the DOM element having ID 'person-45' with the
- # # 'person' partial for the appropriate object.
- # page.replace 'person-45', :partial => 'person', :object => @person
- #
- # This allows the same partial that is used for the +insert_html+ to
- # be also used for the input to +replace+ without resorting to
- # the use of wrapper elements.
- #
- # Examples:
- #
- # <div id="people">
- # <%= render :partial => 'person', :collection => @people %>
- # </div>
- #
- # # Insert a new person
- # #
- # # Generates: new Insertion.Bottom({object: "Matz", partial: "person"}, "");
- # page.insert_html :bottom, :partial => 'person', :object => @person
- #
- # # Replace an existing person
- #
- # # Generates: Element.replace("person_45", "-- Contents of partial --");
- # page.replace 'person_45', :partial => 'person', :object => @person
- #
- def replace(id, *options_for_render)
- call 'Element.replace', id, render(*options_for_render)
- end
-
- # Removes the DOM elements with the given +ids+ from the page.
- #
- # Example:
- #
- # # Remove a few people
- # # Generates: ["person_23", "person_9", "person_2"].each(Element.remove);
- # page.remove 'person_23', 'person_9', 'person_2'
- #
- def remove(*ids)
- loop_on_multiple_args 'Element.remove', ids
- end
-
- # Shows hidden DOM elements with the given +ids+.
- #
- # Example:
- #
- # # Show a few people
- # # Generates: ["person_6", "person_13", "person_223"].each(Element.show);
- # page.show 'person_6', 'person_13', 'person_223'
- #
- def show(*ids)
- loop_on_multiple_args 'Element.show', ids
- end
-
- # Hides the visible DOM elements with the given +ids+.
- #
- # Example:
- #
- # # Hide a few people
- # # Generates: ["person_29", "person_9", "person_0"].each(Element.hide);
- # page.hide 'person_29', 'person_9', 'person_0'
- #
- def hide(*ids)
- loop_on_multiple_args 'Element.hide', ids
- end
-
- # Toggles the visibility of the DOM elements with the given +ids+.
- # Example:
- #
- # # Show a few people
- # # Generates: ["person_14", "person_12", "person_23"].each(Element.toggle);
- # page.toggle 'person_14', 'person_12', 'person_23' # Hides the elements
- # page.toggle 'person_14', 'person_12', 'person_23' # Shows the previously hidden elements
- #
- def toggle(*ids)
- loop_on_multiple_args 'Element.toggle', ids
- end
-
- # Displays an alert dialog with the given +message+.
- #
- # Example:
- #
- # # Generates: alert('This message is from Rails!')
- # page.alert('This message is from Rails!')
- def alert(message)
- call 'alert', message
- end
-
- # Redirects the browser to the given +location+ using JavaScript, in the same form as +url_for+.
- #
- # Examples:
- #
- # # Generates: window.location.href = "/mycontroller";
- # page.redirect_to(:action => 'index')
- #
- # # Generates: window.location.href = "/account/signup";
- # page.redirect_to(:controller => 'account', :action => 'signup')
- def redirect_to(location)
- url = location.is_a?(String) ? location : @context.url_for(location)
- record "window.location.href = #{url.inspect}"
- end
-
- # Reloads the browser's current +location+ using JavaScript
- #
- # Examples:
- #
- # # Generates: window.location.reload();
- # page.reload
- def reload
- record 'window.location.reload()'
- end
-
- # Calls the JavaScript +function+, optionally with the given +arguments+.
- #
- # If a block is given, the block will be passed to a new JavaScriptGenerator;
- # the resulting JavaScript code will then be wrapped inside <tt>function() { ... }</tt>
- # and passed as the called function's final argument.
- #
- # Examples:
- #
- # # Generates: Element.replace(my_element, "My content to replace with.")
- # page.call 'Element.replace', 'my_element', "My content to replace with."
- #
- # # Generates: alert('My message!')
- # page.call 'alert', 'My message!'
- #
- # # Generates:
- # # my_method(function() {
- # # $("one").show();
- # # $("two").hide();
- # # });
- # page.call(:my_method) do |p|
- # p[:one].show
- # p[:two].hide
- # end
- def call(function, *arguments, &block)
- record "#{function}(#{arguments_for_call(arguments, block)})"
- end
-
- # Assigns the JavaScript +variable+ the given +value+.
- #
- # Examples:
- #
- # # Generates: my_string = "This is mine!";
- # page.assign 'my_string', 'This is mine!'
- #
- # # Generates: record_count = 33;
- # page.assign 'record_count', 33
- #
- # # Generates: tabulated_total = 47
- # page.assign 'tabulated_total', @total_from_cart
- #
- def assign(variable, value)
- record "#{variable} = #{javascript_object_for(value)}"
- end
-
- # Writes raw JavaScript to the page.
- #
- # Example:
- #
- # page << "alert('JavaScript with Prototype.');"
- def <<(javascript)
- @lines << javascript
- end
-
- # Executes the content of the block after a delay of +seconds+. Example:
- #
- # # Generates:
- # # setTimeout(function() {
- # # ;
- # # new Effect.Fade("notice",{});
- # # }, 20000);
- # page.delay(20) do
- # page.visual_effect :fade, 'notice'
- # end
- def delay(seconds = 1)
- record "setTimeout(function() {\n\n"
- yield
- record "}, #{(seconds * 1000).to_i})"
- end
-
- private
- def loop_on_multiple_args(method, ids)
- record(ids.size>1 ?
- "#{javascript_object_for(ids)}.each(#{method})" :
- "#{method}(#{javascript_object_for(ids.first)})")
- end
-
- def page
- self
- end
-
- def record(line)
- line = "#{line.to_s.chomp.gsub(/\;\z/, '')};"
- self << line
- line
- end
-
- def render(*options)
- with_formats(:html) do
- case option = options.first
- when Hash
- @context.render(*options)
- else
- option.to_s
- end
- end
- end
-
- def with_formats(*args)
- @context ? @context.lookup_context.update_details(:formats => args) { yield } : yield
- end
-
- def javascript_object_for(object)
- ::ActiveSupport::JSON.encode(object)
- end
-
- def arguments_for_call(arguments, block = nil)
- arguments << block_to_function(block) if block
- arguments.map { |argument| javascript_object_for(argument) }.join ', '
- end
-
- def block_to_function(block)
- generator = self.class.new(@context, &block)
- literal("function() { #{generator.to_s} }")
- end
-
- def method_missing(method, *arguments)
- JavaScriptProxy.new(self, method.to_s.camelize)
- end
- end
- end
-
- # Yields a JavaScriptGenerator and returns the generated JavaScript code.
- # Use this to update multiple elements on a page in an Ajax response.
- # See JavaScriptGenerator for more information.
- #
- # Example:
- #
- # update_page do |page|
- # page.hide 'spinner'
- # end
- def update_page(&block)
- JavaScriptGenerator.new(self, &block).to_s.html_safe
- end
-
- # Works like update_page but wraps the generated JavaScript in a
- # <tt>\<script></tt> tag. Use this to include generated JavaScript in an
- # ERB template. See JavaScriptGenerator for more information.
- #
- # +html_options+ may be a hash of <tt>\<script></tt> attributes to be
- # passed to ActionView::Helpers::JavaScriptHelper#javascript_tag.
- def update_page_tag(html_options = {}, &block)
- javascript_tag update_page(&block), html_options
- end
-
- protected
- def options_for_javascript(options)
- if options.empty?
- '{}'
- else
- "{#{options.keys.map { |k| "#{k}:#{options[k]}" }.sort.join(', ')}}"
- end
- end
-
- def options_for_ajax(options)
- js_options = build_callbacks(options)
-
- js_options['asynchronous'] = options[:type] != :synchronous
- js_options['method'] = method_option_to_s(options[:method]) if options[:method]
- js_options['insertion'] = "'#{options[:position].to_s.downcase}'" if options[:position]
- js_options['evalScripts'] = options[:script].nil? || options[:script]
-
- if options[:form]
- js_options['parameters'] = 'Form.serialize(this)'
- elsif options[:submit]
- js_options['parameters'] = "Form.serialize('#{options[:submit]}')"
- elsif options[:with]
- js_options['parameters'] = options[:with]
- end
-
- if protect_against_forgery? && !options[:form]
- if js_options['parameters']
- js_options['parameters'] << " + '&"
- else
- js_options['parameters'] = "'"
- end
- js_options['parameters'] << "#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}')"
- end
-
- options_for_javascript(js_options)
- end
-
- def method_option_to_s(method)
- (method.is_a?(String) and !method.index("'").nil?) ? method : "'#{method}'"
- end
-
- def build_callbacks(options)
- callbacks = {}
- options.each do |callback, code|
- if CALLBACKS.include?(callback)
- name = 'on' + callback.to_s.capitalize
- callbacks[name] = "function(request){#{code}}"
- end
- end
- callbacks
- end
- end
-
- # Converts chained method calls on DOM proxy elements into JavaScript chains
- class JavaScriptProxy < ActiveSupport::BasicObject #:nodoc:
-
- def initialize(generator, root = nil)
- @generator = generator
- @generator << root if root
- end
-
- def is_a?(klass)
- klass == JavaScriptProxy
- end
-
- private
- def method_missing(method, *arguments, &block)
- if method.to_s =~ /(.*)=$/
- assign($1, arguments.first)
- else
- call("#{method.to_s.camelize(:lower)}", *arguments, &block)
- end
- end
-
- def call(function, *arguments, &block)
- append_to_function_chain!("#{function}(#{@generator.send(:arguments_for_call, arguments, block)})")
- self
- end
-
- def assign(variable, value)
- append_to_function_chain!("#{variable} = #{@generator.send(:javascript_object_for, value)}")
- end
-
- def function_chain
- @function_chain ||= @generator.instance_variable_get(:@lines)
- end
-
- def append_to_function_chain!(call)
- function_chain[-1].chomp!(';')
- function_chain[-1] += ".#{call};"
- end
- end
-
- class JavaScriptElementProxy < JavaScriptProxy #:nodoc:
- def initialize(generator, id)
- @id = id
- super(generator, "$(#{::ActiveSupport::JSON.encode(id)})")
- end
-
- # Allows access of element attributes through +attribute+. Examples:
- #
- # page['foo']['style'] # => $('foo').style;
- # page['foo']['style']['color'] # => $('blank_slate').style.color;
- # page['foo']['style']['color'] = 'red' # => $('blank_slate').style.color = 'red';
- # page['foo']['style'].color = 'red' # => $('blank_slate').style.color = 'red';
- def [](attribute)
- append_to_function_chain!(attribute)
- self
- end
-
- def []=(variable, value)
- assign(variable, value)
- end
-
- def replace_html(*options_for_render)
- call 'update', @generator.send(:render, *options_for_render)
- end
-
- def replace(*options_for_render)
- call 'replace', @generator.send(:render, *options_for_render)
- end
-
- def reload(options_for_replace = {})
- replace(options_for_replace.merge({ :partial => @id.to_s }))
- end
-
- end
-
- class JavaScriptVariableProxy < JavaScriptProxy #:nodoc:
- def initialize(generator, variable)
- @variable = ::ActiveSupport::JSON::Variable.new(variable)
- @empty = true # only record lines if we have to. gets rid of unnecessary linebreaks
- super(generator)
- end
-
- # The JSON Encoder calls this to check for the +to_json+ method
- # Since it's a blank slate object, I suppose it responds to anything.
- def respond_to?(*)
- true
- end
-
- def as_json(options = nil)
- @variable
- end
-
- private
- def append_to_function_chain!(call)
- @generator << @variable if @empty
- @empty = false
- super
- end
- end
-
- class JavaScriptCollectionProxy < JavaScriptProxy #:nodoc:
- ENUMERABLE_METHODS_WITH_RETURN = [:all, :any, :collect, :map, :detect, :find, :find_all, :select, :max, :min, :partition, :reject, :sort_by, :in_groups_of, :each_slice] unless defined? ENUMERABLE_METHODS_WITH_RETURN
- ENUMERABLE_METHODS = ENUMERABLE_METHODS_WITH_RETURN + [:each] unless defined? ENUMERABLE_METHODS
- attr_reader :generator
- delegate :arguments_for_call, :to => :generator
-
- def initialize(generator, pattern)
- super(generator, @pattern = pattern)
- end
-
- def each_slice(variable, number, &block)
- if block
- enumerate :eachSlice, :variable => variable, :method_args => [number], :yield_args => %w(value index), :return => true, &block
- else
- add_variable_assignment!(variable)
- append_enumerable_function!("eachSlice(#{::ActiveSupport::JSON.encode(number)});")
- end
- end
-
- def grep(variable, pattern, &block)
- enumerate :grep, :variable => variable, :return => true, :method_args => [::ActiveSupport::JSON::Variable.new(pattern.inspect)], :yield_args => %w(value index), &block
- end
-
- def in_groups_of(variable, number, fill_with = nil)
- arguments = [number]
- arguments << fill_with unless fill_with.nil?
- add_variable_assignment!(variable)
- append_enumerable_function!("inGroupsOf(#{arguments_for_call arguments});")
- end
-
- def inject(variable, memo, &block)
- enumerate :inject, :variable => variable, :method_args => [memo], :yield_args => %w(memo value index), :return => true, &block
- end
-
- def pluck(variable, property)
- add_variable_assignment!(variable)
- append_enumerable_function!("pluck(#{::ActiveSupport::JSON.encode(property)});")
- end
-
- def zip(variable, *arguments, &block)
- add_variable_assignment!(variable)
- append_enumerable_function!("zip(#{arguments_for_call arguments}")
- if block
- function_chain[-1] += ", function(array) {"
- yield ::ActiveSupport::JSON::Variable.new('array')
- add_return_statement!
- @generator << '});'
- else
- function_chain[-1] += ');'
- end
- end
-
- private
- def method_missing(method, *arguments, &block)
- if ENUMERABLE_METHODS.include?(method)
- returnable = ENUMERABLE_METHODS_WITH_RETURN.include?(method)
- variable = arguments.first if returnable
- enumerate(method, {:variable => (arguments.first if returnable), :return => returnable, :yield_args => %w(value index)}, &block)
- else
- super
- end
- end
-
- # Options
- # * variable - name of the variable to set the result of the enumeration to
- # * method_args - array of the javascript enumeration method args that occur before the function
- # * yield_args - array of the javascript yield args
- # * return - true if the enumeration should return the last statement
- def enumerate(enumerable, options = {}, &block)
- options[:method_args] ||= []
- options[:yield_args] ||= []
- yield_args = options[:yield_args] * ', '
- method_args = arguments_for_call options[:method_args] # foo, bar, function
- method_args << ', ' unless method_args.blank?
- add_variable_assignment!(options[:variable]) if options[:variable]
- append_enumerable_function!("#{enumerable.to_s.camelize(:lower)}(#{method_args}function(#{yield_args}) {")
- # only yield as many params as were passed in the block
- yield(*options[:yield_args].collect { |p| JavaScriptVariableProxy.new(@generator, p) }[0..block.arity-1])
- add_return_statement! if options[:return]
- @generator << '});'
- end
-
- def add_variable_assignment!(variable)
- function_chain.push("var #{variable} = #{function_chain.pop}")
- end
-
- def add_return_statement!
- unless function_chain.last =~ /return/
- function_chain.push("return #{function_chain.pop.chomp(';')};")
- end
- end
-
- def append_enumerable_function!(call)
- function_chain[-1].chomp!(';')
- function_chain[-1] += ".#{call}"
- end
- end
-
- class JavaScriptElementCollectionProxy < JavaScriptCollectionProxy #:nodoc:\
- def initialize(generator, pattern)
- super(generator, "$$(#{::ActiveSupport::JSON.encode(pattern)})")
- end
- end
- end
-end
diff --git a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb
deleted file mode 100644
index 8610c2469e..0000000000
--- a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb
+++ /dev/null
@@ -1,263 +0,0 @@
-require 'action_view/helpers/javascript_helper'
-require 'active_support/json'
-
-module ActionView
- # = Action View Scriptaculous Helpers
- module Helpers
- # Provides a set of helpers for calling Scriptaculous[http://script.aculo.us/]
- # JavaScript functions, including those which create Ajax controls and visual
- # effects.
- #
- # To be able to use these helpers, you must include the Prototype
- # JavaScript framework and the Scriptaculous JavaScript library in your
- # pages. See the documentation for ActionView::Helpers::JavaScriptHelper
- # for more information on including the necessary JavaScript.
- #
- # The Scriptaculous helpers' behavior can be tweaked with various options.
- #
- # See the documentation at http://script.aculo.us for more information on
- # using these helpers in your application.
- module ScriptaculousHelper
- TOGGLE_EFFECTS = [:toggle_appear, :toggle_slide, :toggle_blind]
-
- # Returns a JavaScript snippet to be used on the Ajax callbacks for
- # starting visual effects.
- #
- # If no +element_id+ is given, it assumes "element" which should be a local
- # variable in the generated JavaScript execution context. This can be
- # used for example with +drop_receiving_element+:
- #
- # <%= drop_receiving_element (...), :loading => visual_effect(:fade) %>
- #
- # This would fade the element that was dropped on the drop receiving
- # element.
- #
- # For toggling visual effects, you can use <tt>:toggle_appear</tt>, <tt>:toggle_slide</tt>, and
- # <tt>:toggle_blind</tt> which will alternate between appear/fade, slidedown/slideup, and
- # blinddown/blindup respectively.
- #
- # You can change the behaviour with various options, see
- # http://script.aculo.us for more documentation.
- def visual_effect(name, element_id = false, js_options = {})
- element = element_id ? ActiveSupport::JSON.encode(element_id) : "element"
-
- js_options[:queue] = if js_options[:queue].is_a?(Hash)
- '{' + js_options[:queue].map {|k, v| k == :limit ? "#{k}:#{v}" : "#{k}:'#{v}'" }.join(',') + '}'
- elsif js_options[:queue]
- "'#{js_options[:queue]}'"
- end if js_options[:queue]
-
- [:endcolor, :direction, :startcolor, :scaleMode, :restorecolor].each do |option|
- js_options[option] = "'#{js_options[option]}'" if js_options[option]
- end
-
- if TOGGLE_EFFECTS.include? name.to_sym
- "Effect.toggle(#{element},'#{name.to_s.gsub(/^toggle_/,'')}',#{options_for_javascript(js_options)});"
- else
- "new Effect.#{name.to_s.camelize}(#{element},#{options_for_javascript(js_options)});"
- end
- end
-
- # Makes the element with the DOM ID specified by +element_id+ sortable
- # by drag-and-drop and make an Ajax call whenever the sort order has
- # changed. By default, the action called gets the serialized sortable
- # element as parameters.
- #
- # Example:
- #
- # <%= sortable_element("my_list", :url => { :action => "order" }) %>
- #
- # In the example, the action gets a "my_list" array parameter
- # containing the values of the ids of elements the sortable consists
- # of, in the current order.
- #
- # Important: For this to work, the sortable elements must have id
- # attributes in the form "string_identifier". For example, "item_1". Only
- # the identifier part of the id attribute will be serialized.
- #
- # Additional +options+ are:
- #
- # * <tt>:format</tt> - A regular expression to determine what to send as the
- # serialized id to the server (the default is <tt>/^[^_]*_(.*)$/</tt>).
- #
- # * <tt>:constraint</tt> - Whether to constrain the dragging to either
- # <tt>:horizontal</tt> or <tt>:vertical</tt> (or false to make it unconstrained).
- #
- # * <tt>:overlap</tt> - Calculate the item overlap in the <tt>:horizontal</tt>
- # or <tt>:vertical</tt> direction.
- #
- # * <tt>:tag</tt> - Which children of the container element to treat as
- # sortable (default is <tt>li</tt>).
- #
- # * <tt>:containment</tt> - Takes an element or array of elements to treat as
- # potential drop targets (defaults to the original target element).
- #
- # * <tt>:only</tt> - A CSS class name or array of class names used to filter
- # out child elements as candidates.
- #
- # * <tt>:scroll</tt> - Determines whether to scroll the list during drag
- # operations if the list runs past the visual border.
- #
- # * <tt>:tree</tt> - Determines whether to treat nested lists as part of the
- # main sortable list. This means that you can create multi-layer lists,
- # and not only sort items at the same level, but drag and sort items
- # between levels.
- #
- # * <tt>:hoverclass</tt> - If set, the Droppable will have this additional CSS class
- # when an accepted Draggable is hovered over it.
- #
- # * <tt>:handle</tt> - Sets whether the element should only be draggable by an
- # embedded handle. The value may be a string referencing a CSS class value
- # (as of script.aculo.us V1.5). The first child/grandchild/etc. element
- # found within the element that has this CSS class value will be used as
- # the handle.
- #
- # * <tt>:ghosting</tt> - Clones the element and drags the clone, leaving
- # the original in place until the clone is dropped (default is <tt>false</tt>).
- #
- # * <tt>:dropOnEmpty</tt> - If true the Sortable container will be made into
- # a Droppable, that can receive a Draggable (as according to the containment
- # rules) as a child element when there are no more elements inside (default
- # is <tt>false</tt>).
- #
- # * <tt>:onChange</tt> - Called whenever the sort order changes while dragging. When
- # dragging from one Sortable to another, the callback is called once on each
- # Sortable. Gets the affected element as its parameter.
- #
- # * <tt>:onUpdate</tt> - Called when the drag ends and the Sortable's order is
- # changed in any way. When dragging from one Sortable to another, the callback
- # is called once on each Sortable. Gets the container as its parameter.
- #
- # See http://script.aculo.us for more documentation.
- def sortable_element(element_id, options = {})
- javascript_tag(sortable_element_js(element_id, options).chop!)
- end
-
- def sortable_element_js(element_id, options = {}) #:nodoc:
- options[:with] ||= "Sortable.serialize(#{ActiveSupport::JSON.encode(element_id)})"
- options[:onUpdate] ||= "function(){" + remote_function(options) + "}"
- options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) }
-
- [:tag, :overlap, :constraint, :handle].each do |option|
- options[option] = "'#{options[option]}'" if options[option]
- end
-
- options[:containment] = array_or_string_for_javascript(options[:containment]) if options[:containment]
- options[:only] = array_or_string_for_javascript(options[:only]) if options[:only]
-
- %(Sortable.create(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});)
- end
-
- # Makes the element with the DOM ID specified by +element_id+ draggable.
- #
- # Example:
- # <%= draggable_element("my_image", :revert => true)
- #
- # You can change the behaviour with various options, see
- # http://script.aculo.us for more documentation.
- def draggable_element(element_id, options = {})
- javascript_tag(draggable_element_js(element_id, options).chop!)
- end
-
- def draggable_element_js(element_id, options = {}) #:nodoc:
- %(new Draggable(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});)
- end
-
- # Makes the element with the DOM ID specified by +element_id+ receive
- # dropped draggable elements (created by +draggable_element+).
- # and make an AJAX call. By default, the action called gets the DOM ID
- # of the element as parameter.
- #
- # Example:
- # <%= drop_receiving_element("my_cart", :url =>
- # { :controller => "cart", :action => "add" }) %>
- #
- # You can change the behaviour with various options, see
- # http://script.aculo.us for more documentation.
- #
- # Some of these +options+ include:
- # * <tt>:accept</tt> - Set this to a string or an array of strings describing the
- # allowable CSS classes that the +draggable_element+ must have in order
- # to be accepted by this +drop_receiving_element+.
- #
- # * <tt>:confirm</tt> - Adds a confirmation dialog. Example:
- #
- # :confirm => "Are you sure you want to do this?"
- #
- # * <tt>:hoverclass</tt> - If set, the +drop_receiving_element+ will have
- # this additional CSS class when an accepted +draggable_element+ is
- # hovered over it.
- #
- # * <tt>:onDrop</tt> - Called when a +draggable_element+ is dropped onto
- # this element. Override this callback with a JavaScript expression to
- # change the default drop behaviour. Example:
- #
- # :onDrop => "function(draggable_element, droppable_element, event) { alert('I like bananas') }"
- #
- # This callback gets three parameters: The Draggable element, the Droppable
- # element and the Event object. You can extract additional information about
- # the drop - like if the Ctrl or Shift keys were pressed - from the Event object.
- #
- # * <tt>:with</tt> - A JavaScript expression specifying the parameters for
- # the XMLHttpRequest. Any expressions should return a valid URL query string.
- def drop_receiving_element(element_id, options = {})
- javascript_tag(drop_receiving_element_js(element_id, options).chop!)
- end
-
- def drop_receiving_element_js(element_id, options = {}) #:nodoc:
- options[:with] ||= "'id=' + encodeURIComponent(element.id)"
- options[:onDrop] ||= "function(element){" + remote_function(options) + "}"
- options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) }
-
- options[:accept] = array_or_string_for_javascript(options[:accept]) if options[:accept]
- options[:hoverclass] = "'#{options[:hoverclass]}'" if options[:hoverclass]
-
- # Confirmation happens during the onDrop callback, so it can be removed from the options
- options.delete(:confirm) if options[:confirm]
-
- %(Droppables.add(#{ActiveSupport::JSON.encode(element_id)}, #{options_for_javascript(options)});)
- end
-
- protected
- def array_or_string_for_javascript(option)
- if option.kind_of?(Array)
- "['#{option.join('\',\'')}']"
- elsif !option.nil?
- "'#{option}'"
- end
- end
- end
-
- module PrototypeHelper
- class JavaScriptGenerator
- module GeneratorMethods
- # Starts a script.aculo.us visual effect. See
- # ActionView::Helpers::ScriptaculousHelper for more information.
- def visual_effect(name, id = nil, options = {})
- record @context.send(:visual_effect, name, id, options)
- end
-
- # Creates a script.aculo.us sortable element. Useful
- # to recreate sortable elements after items get added
- # or deleted.
- # See ActionView::Helpers::ScriptaculousHelper for more information.
- def sortable(id, options = {})
- record @context.send(:sortable_element_js, id, options)
- end
-
- # Creates a script.aculo.us draggable element.
- # See ActionView::Helpers::ScriptaculousHelper for more information.
- def draggable(id, options = {})
- record @context.send(:draggable_element_js, id, options)
- end
-
- # Creates a script.aculo.us drop receiving element.
- # See ActionView::Helpers::ScriptaculousHelper for more information.
- def drop_receiving(id, options = {})
- record @context.send(:drop_receiving_element_js, id, options)
- end
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_view/helpers/sprockets_helper.rb b/actionpack/lib/action_view/helpers/sprockets_helper.rb
new file mode 100644
index 0000000000..947c827f3c
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/sprockets_helper.rb
@@ -0,0 +1,69 @@
+require 'uri'
+require 'action_view/helpers/asset_tag_helpers/asset_paths'
+
+module ActionView
+ module Helpers
+ module SprocketsHelper
+ def asset_path(source, default_ext = nil)
+ sprockets_asset_paths.compute_public_path(source, 'assets', default_ext, true)
+ end
+
+ def sprockets_javascript_include_tag(source, options = {})
+ options = {
+ 'type' => "text/javascript",
+ 'src' => asset_path(source, 'js')
+ }.merge(options.stringify_keys)
+
+ content_tag 'script', "", options
+ end
+
+ def sprockets_stylesheet_link_tag(source, options = {})
+ options = {
+ 'rel' => "stylesheet",
+ 'type' => "text/css",
+ 'media' => "screen",
+ 'href' => asset_path(source, 'css')
+ }.merge(options.stringify_keys)
+
+ tag 'link', options
+ end
+
+ private
+
+ def sprockets_asset_paths
+ @sprockets_asset_paths ||= begin
+ config = self.config if respond_to?(:config)
+ controller = self.controller if respond_to?(:controller)
+ SprocketsHelper::AssetPaths.new(config, controller)
+ end
+ end
+
+ class AssetPaths < ActionView::Helpers::AssetTagHelper::AssetPaths
+ def rewrite_asset_path(source, dir)
+ if source =~ /^\/#{dir}\/(.+)/
+ assets.path($1, performing_caching?, dir)
+ else
+ source
+ end
+ end
+
+ def rewrite_extension(source, dir, ext)
+ if ext && File.extname(source).empty?
+ "#{source}.#{ext}"
+ else
+ source
+ end
+ end
+
+ def assets
+ Rails.application.assets
+ end
+
+ # When included in Sprockets::Context, we need to ask the top-level config as the controller is not available
+ def performing_caching?
+ @config ? @config.perform_caching : Rails.application.config.action_controller.perform_caching
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/path_set.rb b/actionpack/lib/action_view/path_set.rb
index e3de3e1eac..8b840a6463 100644
--- a/actionpack/lib/action_view/path_set.rb
+++ b/actionpack/lib/action_view/path_set.rb
@@ -15,6 +15,7 @@ module ActionView #:nodoc:
end
def find_all(path, prefixes = [], *args)
+ prefixes = [prefixes] if String === prefixes
prefixes.each do |prefix|
each do |resolver|
templates = resolver.find_all(path, prefix, *args)
diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb
index 501ec07b09..f20ba7e6d3 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -6,7 +6,7 @@ module ActionView
class Railtie < Rails::Railtie
config.action_view = ActiveSupport::OrderedOptions.new
config.action_view.stylesheet_expansions = {}
- config.action_view.javascript_expansions = { :defaults => ['prototype', 'effects', 'dragdrop', 'controls', 'rails'] }
+ config.action_view.javascript_expansions = { :defaults => %w(jquery rails) }
initializer "action_view.cache_asset_ids" do |app|
unless app.config.cache_classes
diff --git a/actionpack/lib/action_view/renderer/partial_renderer.rb b/actionpack/lib/action_view/renderer/partial_renderer.rb
index 94c0a8a8fb..10cd37d56f 100644
--- a/actionpack/lib/action_view/renderer/partial_renderer.rb
+++ b/actionpack/lib/action_view/renderer/partial_renderer.rb
@@ -1,5 +1,3 @@
-require 'action_view/renderer/abstract_renderer'
-
module ActionView
class PartialRenderer < AbstractRenderer #:nodoc:
PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
@@ -79,7 +77,7 @@ module ActionView
locals[as] = object
content = @template.render(view, locals) do |*name|
- view._layout_for(*name, &block)
+ view._block_layout_for(*name, &block)
end
content = layout.render(view, locals){ content } if layout
diff --git a/actionpack/lib/action_view/renderer/streaming_template_renderer.rb b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb
new file mode 100644
index 0000000000..03aab444f8
--- /dev/null
+++ b/actionpack/lib/action_view/renderer/streaming_template_renderer.rb
@@ -0,0 +1,130 @@
+# 1.9 ships with Fibers but we need to require the extra
+# methods explicitly. We only load those extra methods if
+# Fiber is available in the first place.
+require 'fiber' if defined?(Fiber)
+
+module ActionView
+ # Consider the following layout:
+ #
+ # <%= yield :header %>
+ # 2
+ # <%= yield %>
+ # 5
+ # <%= yield :footer %>
+ #
+ # And template:
+ #
+ # <%= provide :header, "1" %>
+ # 3
+ # 4
+ # <%= provide :footer, "6" %>
+ #
+ # It will stream:
+ #
+ # "1\n", "2\n", "3\n4\n", "5\n", "6\n"
+ #
+ # Notice that once you <%= yield %>, it will render the whole template
+ # before streaming again. In the future, we can also support streaming
+ # from the template and not only the layout.
+ #
+ # Also, notice we use +provide+ instead of +content_for+, as +provide+
+ # gives the control back to the layout as soon as it is called.
+ # With +content_for+, it would render all the template to find all
+ # +content_for+ calls. For instance, consider this layout:
+ #
+ # <%= yield :header %>
+ #
+ # With this template:
+ #
+ # <%= content_for :header, "1" %>
+ # <%= provide :header, "2" %>
+ # <%= provide :header, "3" %>
+ #
+ # It will return "12\n" because +content_for+ continues rendering the
+ # template but it is returns back to the layout as soon as it sees the
+ # first +provide+.
+ #
+ # == TODO
+ #
+ # * Support streaming from child templates, partials and so on.
+ # * Integrate exceptions with exceptron
+ class StreamingTemplateRenderer < TemplateRenderer #:nodoc:
+ # A valid Rack::Body (i.e. it responds to each).
+ # It is initialized with a block that, when called, starts
+ # rendering the template.
+ class Body #:nodoc:
+ def initialize(&start)
+ @start = start
+ end
+
+ def each(&block)
+ begin
+ @start.call(block)
+ rescue
+ block.call ActionView::Base.streaming_completion_on_exception
+ end
+ self
+ end
+ end
+
+ # For streaming, instead of rendering a given a template, we return a Body
+ # object that responds to each. This object is initialized with a block
+ # that knows how to render the template.
+ def render_template(template, layout_name = nil, locals = {}) #:nodoc:
+ return [super] unless layout_name && template.supports_streaming?
+
+ locals ||= {}
+ layout = layout_name && find_layout(layout_name, locals.keys)
+
+ Body.new do |buffer|
+ delayed_render(buffer, template, layout, @view, locals)
+ end
+ end
+
+ private
+
+ def delayed_render(buffer, template, layout, view, locals)
+ # Wrap the given buffer in the StreamingBuffer and pass it to the
+ # underlying template handler. Now, everytime something is concatenated
+ # to the buffer, it is not appended to an array, but streamed straight
+ # to the client.
+ output = ActionView::StreamingBuffer.new(buffer)
+ yielder = lambda { |*name| view._layout_for(*name) }
+
+ instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
+ fiber = Fiber.new do
+ if layout
+ layout.render(view, locals, output, &yielder)
+ else
+ # If you don't have a layout, just render the thing
+ # and concatenate the final result. This is the same
+ # as a layout with just <%= yield %>
+ output.safe_concat view._layout_for
+ end
+ end
+
+ # Set the view flow to support streaming. It will be aware
+ # when to stop rendering the layout because it needs to search
+ # something in the template and vice-versa.
+ view._view_flow = StreamingFlow.new(view, fiber)
+
+ # Yo! Start the fiber!
+ fiber.resume
+
+ # If the fiber is still alive, it means we need something
+ # from the template, so start rendering it. If not, it means
+ # the layout exited without requiring anything from the template.
+ if fiber.alive?
+ content = template.render(view, locals, &yielder)
+
+ # Once rendering the template is done, sets its content in the :layout key.
+ view._view_flow.set(:layout, content)
+
+ # In case the layout continues yielding, we need to resume
+ # the fiber until all yields are handled.
+ fiber.resume while fiber.alive?
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/renderer/template_renderer.rb b/actionpack/lib/action_view/renderer/template_renderer.rb
index 9ae1636131..6b5ead463f 100644
--- a/actionpack/lib/action_view/renderer/template_renderer.rb
+++ b/actionpack/lib/action_view/renderer/template_renderer.rb
@@ -1,41 +1,16 @@
-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)
+ freeze_formats(template.formats, true)
render_template(template, options[:layout], options[:locals])
end
end
- def render_once(options)
- paths, locals = options[:once], options[:locals] || {}
- layout, keys = options[:layout], locals.keys
- prefixes = options.fetch(:prefixes, @view.controller_prefixes)
-
- 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, prefixes, 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) || []
@@ -56,7 +31,6 @@ module ActionView
# Renders the given template. An string representing the layout can be
# supplied as well.
def render_template(template, layout_name = nil, locals = {}) #:nodoc:
- freeze_formats(template.formats, true)
view, locals = @view, locals || {}
render_with_layout(layout_name, locals) do |layout|
@@ -72,7 +46,7 @@ module ActionView
if layout
view = @view
- view.store_content_for(:layout, content)
+ view._view_flow.set(:layout, content)
layout.render(view, locals){ |*name| view._layout_for(*name) }
else
content
diff --git a/actionpack/lib/action_view/rendering.rb b/actionpack/lib/action_view/rendering.rb
index baa5d2c3fd..2bce2fb045 100644
--- a/actionpack/lib/action_view/rendering.rb
+++ b/actionpack/lib/action_view/rendering.rb
@@ -6,11 +6,9 @@ module ActionView
# Returns the result of a render that's dictated by the options hash. The primary options are:
#
# * <tt>:partial</tt> - See ActionView::Partials.
- # * <tt>:update</tt> - Calls update_page with the block given.
# * <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.
@@ -21,18 +19,27 @@ 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
_render_template(options)
end
- when :update
- update_page(&block)
else
_render_partial(:partial => options, :locals => locals)
end
end
+ # Render but returns a valid Rack body. If fibers are defined, we return
+ # a streaming body that renders the template piece by piece.
+ #
+ # Note that partials are not supported to be rendered with streaming,
+ # so in such cases, we just wrap them in an array.
+ def render_body(options)
+ if options.key?(:partial)
+ [_render_partial(options)]
+ else
+ StreamingTemplateRenderer.new(self).render(options)
+ end
+ end
+
# 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
@@ -79,22 +86,23 @@ module ActionView
# Hello David
# </html>
#
- def _layout_for(*args, &block)
+ def _layout_for(*args)
+ name = args.first
+ name = :layout unless name.is_a?(Symbol)
+ @_view_flow.get(name).html_safe
+ end
+
+ # Handle layout for calls from partials that supports blocks.
+ def _block_layout_for(*args, &block)
name = args.first
- if name.is_a?(Symbol)
- @_content_for[name].html_safe
- elsif block
+ if !name.is_a?(Symbol) && block
capture(*args, &block)
else
- @_content_for[:layout].html_safe
+ _layout_for(*args)
end
end
- def _render_once(options) #:nodoc:
- _template_renderer.render_once(options)
- end
-
def _render_template(options) #:nodoc:
_template_renderer.render(options)
end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index 96d506fac5..6dfc4f68ae 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -126,17 +126,23 @@ module ActionView
@formats = Array.wrap(format).map { |f| f.is_a?(Mime::Type) ? f.ref : f }
end
+ # Returns if the underlying handler supports streaming. If so,
+ # a streaming buffer *may* be passed when it start rendering.
+ def supports_streaming?
+ handler.respond_to?(:supports_streaming?) && handler.supports_streaming?
+ 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)
+ def render(view, locals, buffer=nil, &block)
old_template, view._template = view._template, self
ActiveSupport::Notifications.instrument("!render_template.action_view", :virtual_path => @virtual_path) do
compile!(view)
- view.send(method_name, locals, &block)
+ view.send(method_name, locals, buffer, &block)
end
rescue Exception => e
handle_render_error(view, e)
@@ -167,28 +173,6 @@ module ActionView
end
end
- # Expires this template by setting his updated_at date to Jan 1st, 1970.
- def expire!
- @updated_at = Time.utc(1970)
- end
-
- # 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
-
- # Used to store template data by template handlers.
- def data
- @data ||= {}
- end
-
def inspect
@inspect ||=
if defined?(Rails.root)
@@ -274,13 +258,12 @@ module ActionView
end
end
- arity = @handler.respond_to?(:arity) ? @handler.arity : @handler.method(:call).arity
- code = arity.abs == 1 ? @handler.call(self) : @handler.call(self, view)
+ code = @handler.call(self)
# Make sure that the resulting String to be evalled is in the
# encoding of the code
source = <<-end_src
- def #{method_name}(local_assigns)
+ def #{method_name}(local_assigns, output_buffer)
_old_output_buffer = @output_buffer;#{locals_code};#{code}
ensure
@output_buffer = _old_output_buffer
diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb
index 4438199497..959afa734e 100644
--- a/actionpack/lib/action_view/template/handlers.rb
+++ b/actionpack/lib/action_view/template/handlers.rb
@@ -3,12 +3,10 @@ module ActionView #:nodoc:
class Template
module Handlers #:nodoc:
autoload :ERB, 'action_view/template/handlers/erb'
- autoload :RJS, 'action_view/template/handlers/rjs'
autoload :Builder, 'action_view/template/handlers/builder'
def self.extended(base)
base.register_default_template_handler :erb, ERB.new
- base.register_template_handler :rjs, RJS.new
base.register_template_handler :builder, Builder.new
end
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index 0443b1b0f5..7e9e4e518a 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -1,28 +1,14 @@
require 'active_support/core_ext/class/attribute_accessors'
-require 'active_support/core_ext/string/output_safety'
require 'action_view/template'
require 'action_view/template/handler'
require 'erubis'
module ActionView
- class OutputBuffer < ActiveSupport::SafeBuffer
- def initialize(*)
- super
- encode! if encoding_aware?
- end
-
- def <<(value)
- super(value.to_s)
- end
- alias :append= :<<
- alias :safe_append= :safe_concat
- end
-
class Template
module Handlers
class Erubis < ::Erubis::Eruby
def add_preamble(src)
- src << "@output_buffer = ActionView::OutputBuffer.new;"
+ src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
end
def add_text(src, text)
@@ -73,6 +59,10 @@ module ActionView
new.call(template)
end
+ def supports_streaming?
+ true
+ end
+
def handles_encoding?
true
end
diff --git a/actionpack/lib/action_view/template/handlers/rjs.rb b/actionpack/lib/action_view/template/handlers/rjs.rb
deleted file mode 100644
index 9d71059134..0000000000
--- a/actionpack/lib/action_view/template/handlers/rjs.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-module ActionView
- module Template::Handlers
- class RJS
- # Default format used by RJS.
- class_attribute :default_format
- self.default_format = Mime::JS
-
- def call(template)
- "update_page do |page|;#{template.source}\nend"
- end
- end
- end
-end
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index 41c6310ae2..870897958a 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -186,7 +186,7 @@ module ActionView
# ==== Examples
#
# Default pattern, loads views the same way as previous versions of rails, eg. when you're
- # looking for `users/new` it will produce query glob: `users/new{.{en},}{.{html,js},}{.{erb,haml,rjs},}`
+ # looking for `users/new` it will produce query glob: `users/new{.{en},}{.{html,js},}{.{erb,haml},}`
#
# FileSystemResolver.new("/path/to/views", ":prefix/:action{.:locale,}{.:formats,}{.:handlers,}")
#
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 3e2ddffa16..5c74bf843a 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -121,7 +121,7 @@ module ActionView
# Support the selector assertions
#
# Need to experiment if this priority is the best one: rendered => output_buffer
- def response_from_page_or_rjs
+ def response_from_page
HTML::Document.new(@rendered.blank? ? @output_buffer : @rendered).root
end
diff --git a/actionpack/lib/sprockets/railtie.rb b/actionpack/lib/sprockets/railtie.rb
new file mode 100644
index 0000000000..2f7f95c44d
--- /dev/null
+++ b/actionpack/lib/sprockets/railtie.rb
@@ -0,0 +1,62 @@
+module Sprockets
+ class Railtie < Rails::Railtie
+ def self.using_coffee?
+ require 'coffee-script'
+ defined?(CoffeeScript)
+ rescue LoadError
+ false
+ end
+
+ def self.using_scss?
+ require 'sass'
+ defined?(Sass)
+ rescue LoadError
+ false
+ end
+
+ config.app_generators.javascript_engine :coffee if using_coffee?
+ config.app_generators.stylesheet_engine :scss if using_scss?
+
+ # Configure ActionController to use sprockets.
+ initializer "sprockets.set_configs", :after => "action_controller.set_configs" do |app|
+ ActiveSupport.on_load(:action_controller) do
+ self.use_sprockets = app.config.assets.enabled
+ end
+ end
+
+ # We need to configure this after initialization to ensure we collect
+ # paths from all engines. This hook is invoked exactly before routes
+ # are compiled.
+ config.after_initialize do |app|
+ assets = app.config.assets
+ next unless assets.enabled
+
+ app.assets = asset_environment(app)
+
+ # FIXME: Temp hack for extending Sprockets::Context so
+ ActiveSupport.on_load(:action_view) do
+ ::Sprockets::Context.send :include, ::ActionView::Helpers::SprocketsHelper
+ end
+
+ app.routes.append do
+ mount app.assets => assets.prefix
+ end
+
+ if config.action_controller.perform_caching
+ app.assets = app.assets.index
+ end
+ end
+
+ protected
+
+ def asset_environment(app)
+ require "sprockets"
+ assets = app.config.assets
+ env = Sprockets::Environment.new(app.root.to_s)
+ env.static_root = File.join(app.root.join("public"), assets.prefix)
+ env.paths.concat assets.paths
+ env.logger = Rails.logger
+ env
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/test/controller/assert_select_test.rb b/actionpack/test/controller/assert_select_test.rb
index f63321c78b..878484eb57 100644
--- a/actionpack/test/controller/assert_select_test.rb
+++ b/actionpack/test/controller/assert_select_test.rb
@@ -34,13 +34,6 @@ class AssertSelectTest < ActionController::TestCase
@content = nil
end
- def rjs()
- render :update do |page|
- @update.call page
- end
- @update = nil
- end
-
def xml()
render :text=>@content, :layout=>false, :content_type=>Mime::XML
@content = nil
@@ -219,50 +212,6 @@ class AssertSelectTest < ActionController::TestCase
end
end
- # With single result.
- def test_assert_select_from_rjs_with_single_result
- render_rjs do |page|
- page.replace_html "test", "<div id=\"1\">foo</div>\n<div id=\"2\">foo</div>"
- end
- assert_select "div" do |elements|
- assert elements.size == 2
- assert_select "#1"
- assert_select "#2"
- end
- assert_select "div#?", /\d+/ do |elements|
- assert_select "#1"
- assert_select "#2"
- end
- end
-
- # With multiple results.
- def test_assert_select_from_rjs_with_multiple_results
- render_rjs do |page|
- page.replace_html "test", "<div id=\"1\">foo</div>"
- page.replace_html "test2", "<div id=\"2\">foo</div>"
- end
- assert_select "div" do |elements|
- assert elements.size == 2
- assert_select "#1"
- assert_select "#2"
- end
- end
-
- def test_assert_select_rjs_for_positioned_insert_should_fail_when_mixing_arguments
- render_rjs do |page|
- page.insert_html :top, "test1", "<div id=\"1\">foo</div>"
- page.insert_html :bottom, "test2", "<div id=\"2\">foo</div>"
- end
- assert_raise(Assertion) {assert_select_rjs :insert, :top, "test2"}
- end
-
- def test_assert_select_rjs_for_redirect_to
- render_rjs do |page|
- page.redirect_to '/'
- end
- assert_select_rjs :redirect, '/'
- end
-
def test_elect_with_xml_namespace_attributes
render_html %Q{<link xlink:href="http://nowhere.com"></link>}
assert_nothing_raised { assert_select "link[xlink:href=http://nowhere.com]" }
@@ -296,364 +245,6 @@ class AssertSelectTest < ActionController::TestCase
end
end
- # With one result.
- def test_css_select_from_rjs_with_single_result
- render_rjs do |page|
- page.replace_html "test", "<div id=\"1\">foo</div>\n<div id=\"2\">foo</div>"
- end
- assert_equal 2, css_select("div").size
- assert_equal 1, css_select("#1").size
- assert_equal 1, css_select("#2").size
- end
-
- # With multiple results.
- def test_css_select_from_rjs_with_multiple_results
- render_rjs do |page|
- page.replace_html "test", "<div id=\"1\">foo</div>"
- page.replace_html "test2", "<div id=\"2\">foo</div>"
- end
-
- assert_equal 2, css_select("div").size
- assert_equal 1, css_select("#1").size
- assert_equal 1, css_select("#2").size
- end
-
- #
- # Test assert_select_rjs.
- #
-
- # Test that we can pick up all statements in the result.
- def test_assert_select_rjs_picks_up_all_statements
- render_rjs do |page|
- page.replace "test", "<div id=\"1\">foo</div>"
- page.replace_html "test2", "<div id=\"2\">foo</div>"
- page.insert_html :top, "test3", "<div id=\"3\">foo</div>"
- end
-
- found = false
- assert_select_rjs do
- assert_select "#1"
- assert_select "#2"
- assert_select "#3"
- found = true
- end
- assert found
- end
-
- # Test that we fail if there is nothing to pick.
- def test_assert_select_rjs_fails_if_nothing_to_pick
- render_rjs { }
- assert_raise(Assertion) { assert_select_rjs }
- end
-
- def test_assert_select_rjs_with_unicode
- # Test that non-ascii characters (which are converted into \uXXXX in RJS) are decoded correctly.
- render_rjs do |page|
- page.replace "test", "<div id=\"1\">\343\203\201\343\202\261\343\203\203\343\203\210</div>"
- end
- assert_select_rjs do
- str = "#1"
- assert_select str, :text => "\343\203\201\343\202\261\343\203\203\343\203\210"
- assert_select str, "\343\203\201\343\202\261\343\203\203\343\203\210"
- if str.respond_to?(:force_encoding)
- assert_select str, /\343\203\201..\343\203\210/u
- assert_raise(Assertion) { assert_select str, /\343\203\201.\343\203\210/u }
- else
- assert_select str, Regexp.new("\343\203\201..\343\203\210",0,'U')
- assert_raise(Assertion) { assert_select str, Regexp.new("\343\203\201.\343\203\210",0,'U') }
- end
- end
- end
-
- def test_assert_select_rjs_with_id
- # Test that we can pick up all statements in the result.
- render_rjs do |page|
- page.replace "test1", "<div id=\"1\">foo</div>"
- page.replace_html "test2", "<div id=\"2\">foo</div>"
- page.insert_html :top, "test3", "<div id=\"3\">foo</div>"
- end
- assert_select_rjs "test1" do
- assert_select "div", 1
- assert_select "#1"
- end
- assert_select_rjs "test2" do
- assert_select "div", 1
- assert_select "#2"
- end
- assert_select_rjs "test3" do
- assert_select "div", 1
- assert_select "#3"
- end
- assert_raise(Assertion) { assert_select_rjs "test4" }
- end
-
- def test_assert_select_rjs_for_replace
- render_rjs do |page|
- page.replace "test1", "<div id=\"1\">foo</div>"
- page.replace_html "test2", "<div id=\"2\">foo</div>"
- page.insert_html :top, "test3", "<div id=\"3\">foo</div>"
- end
- # Replace.
- assert_select_rjs :replace do
- assert_select "div", 1
- assert_select "#1"
- end
- assert_select_rjs :replace, "test1" do
- assert_select "div", 1
- assert_select "#1"
- end
- assert_raise(Assertion) { assert_select_rjs :replace, "test2" }
- # Replace HTML.
- assert_select_rjs :replace_html do
- assert_select "div", 1
- assert_select "#2"
- end
- assert_select_rjs :replace_html, "test2" do
- assert_select "div", 1
- assert_select "#2"
- end
- assert_raise(Assertion) { assert_select_rjs :replace_html, "test1" }
- end
-
- def test_assert_select_rjs_for_chained_replace
- render_rjs do |page|
- page['test1'].replace "<div id=\"1\">foo</div>"
- page['test2'].replace_html "<div id=\"2\">foo</div>"
- page.insert_html :top, "test3", "<div id=\"3\">foo</div>"
- end
- # Replace.
- assert_select_rjs :chained_replace do
- assert_select "div", 1
- assert_select "#1"
- end
- assert_select_rjs :chained_replace, "test1" do
- assert_select "div", 1
- assert_select "#1"
- end
- assert_raise(Assertion) { assert_select_rjs :chained_replace, "test2" }
- # Replace HTML.
- assert_select_rjs :chained_replace_html do
- assert_select "div", 1
- assert_select "#2"
- end
- assert_select_rjs :chained_replace_html, "test2" do
- assert_select "div", 1
- assert_select "#2"
- end
- assert_raise(Assertion) { assert_select_rjs :replace_html, "test1" }
- end
-
- # Simple remove
- def test_assert_select_rjs_for_remove
- render_rjs do |page|
- page.remove "test1"
- end
-
- assert_select_rjs :remove, "test1"
- end
-
- def test_assert_select_rjs_for_remove_offers_useful_error_when_assertion_fails
- render_rjs do |page|
- page.remove "test_with_typo"
- end
-
- assert_select_rjs :remove, "test1"
-
- rescue Assertion => e
- assert_equal "No RJS statement that removes 'test1' was rendered.", e.message
- end
-
- def test_assert_select_rjs_for_remove_ignores_block
- render_rjs do |page|
- page.remove "test1"
- end
-
- assert_nothing_raised do
- assert_select_rjs :remove, "test1" do
- assert_select "p"
- end
- end
- end
-
- # Simple show
- def test_assert_select_rjs_for_show
- render_rjs do |page|
- page.show "test1"
- end
-
- assert_select_rjs :show, "test1"
- end
-
- def test_assert_select_rjs_for_show_offers_useful_error_when_assertion_fails
- render_rjs do |page|
- page.show "test_with_typo"
- end
-
- assert_select_rjs :show, "test1"
-
- rescue Assertion => e
- assert_equal "No RJS statement that shows 'test1' was rendered.", e.message
- end
-
- def test_assert_select_rjs_for_show_ignores_block
- render_rjs do |page|
- page.show "test1"
- end
-
- assert_nothing_raised do
- assert_select_rjs :show, "test1" do
- assert_select "p"
- end
- end
- end
-
- # Simple hide
- def test_assert_select_rjs_for_hide
- render_rjs do |page|
- page.hide "test1"
- end
-
- assert_select_rjs :hide, "test1"
- end
-
- def test_assert_select_rjs_for_hide_offers_useful_error_when_assertion_fails
- render_rjs do |page|
- page.hide "test_with_typo"
- end
-
- assert_select_rjs :hide, "test1"
-
- rescue Assertion => e
- assert_equal "No RJS statement that hides 'test1' was rendered.", e.message
- end
-
- def test_assert_select_rjs_for_hide_ignores_block
- render_rjs do |page|
- page.hide "test1"
- end
-
- assert_nothing_raised do
- assert_select_rjs :hide, "test1" do
- assert_select "p"
- end
- end
- end
-
- # Simple toggle
- def test_assert_select_rjs_for_toggle
- render_rjs do |page|
- page.toggle "test1"
- end
-
- assert_select_rjs :toggle, "test1"
- end
-
- def test_assert_select_rjs_for_toggle_offers_useful_error_when_assertion_fails
- render_rjs do |page|
- page.toggle "test_with_typo"
- end
-
- assert_select_rjs :toggle, "test1"
-
- rescue Assertion => e
- assert_equal "No RJS statement that toggles 'test1' was rendered.", e.message
- end
-
- def test_assert_select_rjs_for_toggle_ignores_block
- render_rjs do |page|
- page.toggle "test1"
- end
-
- assert_nothing_raised do
- assert_select_rjs :toggle, "test1" do
- assert_select "p"
- end
- end
- end
-
- # Non-positioned insert.
- def test_assert_select_rjs_for_nonpositioned_insert
- render_rjs do |page|
- page.replace "test1", "<div id=\"1\">foo</div>"
- page.replace_html "test2", "<div id=\"2\">foo</div>"
- page.insert_html :top, "test3", "<div id=\"3\">foo</div>"
- end
- assert_select_rjs :insert_html do
- assert_select "div", 1
- assert_select "#3"
- end
- assert_select_rjs :insert_html, "test3" do
- assert_select "div", 1
- assert_select "#3"
- end
- assert_raise(Assertion) { assert_select_rjs :insert_html, "test1" }
- end
-
- # Positioned insert.
- def test_assert_select_rjs_for_positioned_insert
- render_rjs do |page|
- page.insert_html :top, "test1", "<div id=\"1\">foo</div>"
- page.insert_html :bottom, "test2", "<div id=\"2\">foo</div>"
- page.insert_html :before, "test3", "<div id=\"3\">foo</div>"
- page.insert_html :after, "test4", "<div id=\"4\">foo</div>"
- end
- assert_select_rjs :insert, :top do
- assert_select "div", 1
- assert_select "#1"
- end
- assert_select_rjs :insert, :bottom do
- assert_select "div", 1
- assert_select "#2"
- end
- assert_select_rjs :insert, :before do
- assert_select "div", 1
- assert_select "#3"
- end
- assert_select_rjs :insert, :after do
- assert_select "div", 1
- assert_select "#4"
- end
- assert_select_rjs :insert_html do
- assert_select "div", 4
- end
- end
-
- def test_assert_select_rjs_raise_errors
- assert_raise(ArgumentError) { assert_select_rjs(:destroy) }
- assert_raise(ArgumentError) { assert_select_rjs(:insert, :left) }
- end
-
- # Simple selection from a single result.
- def test_nested_assert_select_rjs_with_single_result
- render_rjs do |page|
- page.replace_html "test", "<div id=\"1\">foo</div>\n<div id=\"2\">foo</div>"
- end
-
- assert_select_rjs "test" do |elements|
- assert_equal 2, elements.size
- assert_select "#1"
- assert_select "#2"
- end
- end
-
- # Deal with two results.
- def test_nested_assert_select_rjs_with_two_results
- render_rjs do |page|
- page.replace_html "test", "<div id=\"1\">foo</div>"
- page.replace_html "test2", "<div id=\"2\">foo</div>"
- end
-
- assert_select_rjs "test" do |elements|
- assert_equal 1, elements.size
- assert_select "#1"
- end
-
- assert_select_rjs "test2" do |elements|
- assert_equal 1, elements.size
- assert_select "#2"
- end
- end
-
def test_feed_item_encoded
render_xml <<-EOF
<rss version="2.0">
@@ -728,11 +319,6 @@ EOF
get :html
end
- def render_rjs(&block)
- @controller.response_with(&block)
- get :rjs
- end
-
def render_xml(xml)
@controller.response_with = xml
get :xml
diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb
index 01f3e8f2b6..fada0c7748 100644
--- a/actionpack/test/controller/caching_test.rb
+++ b/actionpack/test/controller/caching_test.rb
@@ -713,17 +713,10 @@ class FunctionalCachingController < CachingController
end
end
- def js_fragment_cached_with_partial
- respond_to do |format|
- format.js
- end
- end
-
def formatted_fragment_cached
respond_to do |format|
format.html
format.xml
- format.js
end
end
@@ -770,13 +763,6 @@ CACHED
assert_match("Some cached content", @store.read('views/test.host/functional_caching/inline_fragment_cached'))
end
- def test_fragment_caching_in_rjs_partials
- xhr :get, :js_fragment_cached_with_partial
- assert_response :success
- assert_match(/Old fragment caching in a partial/, @response.body)
- assert_match("Old fragment caching in a partial", @store.read('views/test.host/functional_caching/js_fragment_cached_with_partial'))
- end
-
def test_html_formatted_fragment_caching
get :formatted_fragment_cached, :format => "html"
assert_response :success
diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb
index 9500c25a32..b12c798302 100644
--- a/actionpack/test/controller/content_type_test.rb
+++ b/actionpack/test/controller/content_type_test.rb
@@ -35,9 +35,6 @@ class OldContentTypeController < ActionController::Base
def render_default_for_builder
end
- def render_default_for_rjs
- end
-
def render_change_for_builder
response.content_type = Mime::HTML
render :action => "render_default_for_builder"
@@ -129,12 +126,6 @@ class ContentTypeTest < ActionController::TestCase
assert_equal "utf-8", @response.charset
end
- def test_default_for_rjs
- xhr :post, :render_default_for_rjs
- assert_equal Mime::JS, @response.content_type
- assert_equal "utf-8", @response.charset
- end
-
def test_change_for_builder
get :render_change_for_builder
assert_equal Mime::HTML, @response.content_type
diff --git a/actionpack/test/controller/flash_test.rb b/actionpack/test/controller/flash_test.rb
index 9c89f1334d..7b5bf8b21a 100644
--- a/actionpack/test/controller/flash_test.rb
+++ b/actionpack/test/controller/flash_test.rb
@@ -264,6 +264,14 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest
end
end
+ def test_setting_flash_does_not_raise_in_following_requests
+ with_test_route_set do
+ env = { 'action_dispatch.request.flash_hash' => ActionDispatch::Flash::FlashHash.new }
+ get '/set_flash', nil, env
+ get '/set_flash', nil, env
+ end
+ end
+
def test_setting_flash_raises_after_stream_back_to_client_even_with_an_empty_flash
with_test_route_set do
env = { 'action_dispatch.request.flash_hash' => ActionDispatch::Flash::FlashHash.new }
@@ -294,7 +302,6 @@ class FlashIntegrationTest < ActionDispatch::IntegrationTest
end
end
-
private
# Overwrite get to send SessionSecret in env hash
diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb
index be5866e5aa..4a5e597500 100644
--- a/actionpack/test/controller/mime_responds_test.rb
+++ b/actionpack/test/controller/mime_responds_test.rb
@@ -73,13 +73,12 @@ class RespondToController < ActionController::Base
def using_defaults
respond_to do |type|
type.html
- type.js
type.xml
end
end
def using_defaults_with_type_list
- respond_to(:html, :js, :xml)
+ respond_to(:html, :xml)
end
def made_for_content_type
@@ -130,7 +129,6 @@ class RespondToController < ActionController::Base
def all_types_with_layout
respond_to do |type|
type.html
- type.js
end
end
@@ -159,7 +157,7 @@ class RespondToController < ActionController::Base
protected
def set_layout
- if action_name.among?("all_types_with_layout", "iphone_with_html_response_type")
+ if action_name.in?(["all_types_with_layout", "iphone_with_html_response_type"])
"respond_to/layouts/standard"
elsif action_name == "iphone_with_html_response_type_without_layout"
"respond_to/layouts/missing"
@@ -299,11 +297,6 @@ class RespondToControllerTest < ActionController::TestCase
assert_equal "text/html", @response.content_type
assert_equal 'Hello world!', @response.body
- @request.accept = "text/javascript"
- get :using_defaults
- assert_equal "text/javascript", @response.content_type
- assert_equal '$("body").visualEffect("highlight");', @response.body
-
@request.accept = "application/xml"
get :using_defaults
assert_equal "application/xml", @response.content_type
@@ -316,11 +309,6 @@ class RespondToControllerTest < ActionController::TestCase
assert_equal "text/html", @response.content_type
assert_equal 'Hello world!', @response.body
- @request.accept = "text/javascript"
- get :using_defaults_with_type_list
- assert_equal "text/javascript", @response.content_type
- assert_equal '$("body").visualEffect("highlight");', @response.body
-
@request.accept = "application/xml"
get :using_defaults_with_type_list
assert_equal "application/xml", @response.content_type
@@ -428,13 +416,6 @@ class RespondToControllerTest < ActionController::TestCase
assert_equal 'HTML', @response.body
end
-
- def test_rjs_type_skips_layout
- @request.accept = "text/javascript"
- get :all_types_with_layout
- assert_equal 'RJS for all_types_with_layout', @response.body
- end
-
def test_html_type_with_layout
@request.accept = "text/html"
get :all_types_with_layout
@@ -444,9 +425,6 @@ class RespondToControllerTest < ActionController::TestCase
def test_xhr
xhr :get, :js_or_html
assert_equal 'JS', @response.body
-
- xhr :get, :using_defaults
- assert_equal '$("body").visualEffect("highlight");', @response.body
end
def test_custom_constant
@@ -643,11 +621,6 @@ class RespondWithControllerTest < ActionController::TestCase
end
def test_using_resource
- @request.accept = "text/javascript"
- get :using_resource
- assert_equal "text/javascript", @response.content_type
- assert_equal '$("body").visualEffect("highlight");', @response.body
-
@request.accept = "application/xml"
get :using_resource
assert_equal "application/xml", @response.content_type
diff --git a/actionpack/test/controller/new_base/content_type_test.rb b/actionpack/test/controller/new_base/content_type_test.rb
index 8ba30944f5..4b70031c90 100644
--- a/actionpack/test/controller/new_base/content_type_test.rb
+++ b/actionpack/test/controller/new_base/content_type_test.rb
@@ -23,8 +23,7 @@ module ContentType
"content_type/implied/i_am_html_erb.html.erb" => "Hello world!",
"content_type/implied/i_am_xml_erb.xml.erb" => "<xml>Hello world!</xml>",
"content_type/implied/i_am_html_builder.html.builder" => "xml.p 'Hello'",
- "content_type/implied/i_am_xml_builder.xml.builder" => "xml.awesome 'Hello'",
- "content_type/implied/i_am_js_rjs.js.rjs" => "page.alert 'hello'"
+ "content_type/implied/i_am_xml_builder.xml.builder" => "xml.awesome 'Hello'"
)]
end
@@ -93,12 +92,6 @@ module ContentType
assert_header "Content-Type", "application/xml; charset=utf-8"
end
-
- test "sets Content-Type as text/javascript when rendering *.js" do
- get "/content_type/implied/i_am_js_rjs", "format" => "js"
-
- assert_header "Content-Type", "text/javascript; charset=utf-8"
- end
end
class ExplicitCharsetTest < Rack::TestCase
diff --git a/actionpack/test/controller/new_base/render_layout_test.rb b/actionpack/test/controller/new_base/render_layout_test.rb
index bb2a953536..d3dcb5cad6 100644
--- a/actionpack/test/controller/new_base/render_layout_test.rb
+++ b/actionpack/test/controller/new_base/render_layout_test.rb
@@ -70,8 +70,8 @@ module ControllerLayouts
class MismatchFormatController < ::ApplicationController
self.view_paths = [ActionView::FixtureResolver.new(
"layouts/application.html.erb" => "<html><%= yield %></html>",
- "controller_layouts/mismatch_format/index.js.rjs" => "page[:test].ext",
- "controller_layouts/mismatch_format/implicit.rjs" => "page[:test].ext"
+ "controller_layouts/mismatch_format/index.xml.builder" => "xml.instruct!",
+ "controller_layouts/mismatch_format/implicit.builder" => "xml.instruct!"
)]
def explicit
@@ -81,15 +81,17 @@ module ControllerLayouts
class MismatchFormatTest < Rack::TestCase
testing ControllerLayouts::MismatchFormatController
+
+ XML_INSTRUCT = %Q(<?xml version="1.0" encoding="UTF-8"?>\n)
- test "if JS is selected, an HTML template is not also selected" do
- get :index, "format" => "js"
- assert_response "$(\"test\").ext();"
+ test "if XML is selected, an HTML template is not also selected" do
+ get :index, :format => "xml"
+ assert_response XML_INSTRUCT
end
- test "if JS is implicitly selected, an HTML template is not also selected" do
+ test "if XML is implicitly selected, an HTML template is not also selected" do
get :implicit
- assert_response "$(\"test\").ext();"
+ assert_response XML_INSTRUCT
end
test "if an HTML template is explicitly provides for a JS template, an error is raised" do
diff --git a/actionpack/test/controller/new_base/render_once_test.rb b/actionpack/test/controller/new_base/render_once_test.rb
deleted file mode 100644
index 175abf8a7e..0000000000
--- a/actionpack/test/controller/new_base/render_once_test.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-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 => 'result' %>",
- "test/two.html.erb" => "<%= render :once => 'result' %>",
- "test/three.html.erb" => "<%= render :once => 'result' %>",
- "test/result.html.erb" => "YES!",
- "other/result.html.erb" => "NO!",
- "layouts/test.html.erb" => "l<%= yield %>l"
- )
-
- self.view_paths = [RESOLVER]
-
- def _prefixes
- %w(test)
- end
-
- def multiple
- render :once => %w(a b c)
- end
-
- def once
- render :once => %w(one two three)
- end
-
- def duplicate
- render :once => %w(a a a)
- end
-
- def with_layout
- render :once => %w(a b c), :layout => "test"
- end
-
- def with_prefix
- render :once => "result", :prefixes => %w(other)
- end
-
- def with_nil_prefix
- render :once => "test/result", :prefixes => []
- 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
-
- def test_with_prefix_option
- get :with_prefix
- assert_response "NO!"
- end
-
- def test_with_nil_prefix_option
- get :with_nil_prefix
- assert_response "YES!"
- end
- end
-
- class TestRenderOnce < Rack::TestCase
- testing RenderTemplate::RenderOnceController
- include Tests
- end
-end
diff --git a/actionpack/test/controller/new_base/render_rjs_test.rb b/actionpack/test/controller/new_base/render_rjs_test.rb
deleted file mode 100644
index 74bf865b54..0000000000
--- a/actionpack/test/controller/new_base/render_rjs_test.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-require 'abstract_unit'
-
-module RenderRjs
- class BasicController < ActionController::Base
- layout "application", :only => :index_respond_to
-
- self.view_paths = [ActionView::FixtureResolver.new(
- "layouts/application.html.erb" => "",
- "render_rjs/basic/index.js.rjs" => "page[:customer].replace_html render(:partial => 'customer')",
- "render_rjs/basic/index_html.js.rjs" => "page[:customer].replace_html :partial => 'customer'",
- "render_rjs/basic/index_no_js.js.erb" => "<%= render(:partial => 'developer') %>",
- "render_rjs/basic/_customer.js.erb" => "JS Partial",
- "render_rjs/basic/_customer.html.erb" => "HTML Partial",
- "render_rjs/basic/_developer.html.erb" => "HTML Partial",
- "render_rjs/basic/index_locale.js.rjs" => "page[:customer].replace_html :partial => 'customer'",
- "render_rjs/basic/_customer.da.html.erb" => "Danish HTML Partial",
- "render_rjs/basic/_customer.da.js.erb" => "Danish JS Partial"
- )]
-
- def index
- render
- end
-
- def index_respond_to
- respond_to do |format|
- format.js { render :action => "index_no_js" }
- end
- end
-
- def index_locale
- self.locale = :da
- end
- end
-
- class TestBasic < Rack::TestCase
- testing BasicController
-
- def setup
- @old_locale = I18n.locale
- end
-
- def teardown
- I18n.locale = @old_locale
- end
-
- test "rendering a partial in an RJS template should pick the JS template over the HTML one" do
- get :index, "format" => "js"
- assert_response("$(\"customer\").update(\"JS Partial\");")
- end
-
- test "rendering a partial in an RJS template should pick the HTML one if no JS is available" do
- get :index_no_js, "format" => "js"
- assert_response("HTML Partial")
- end
-
- test "rendering a partial in an RJS template should pick the HTML one if no JS is available on respond_to" do
- get :index_respond_to, "format" => "js"
- assert_response("HTML Partial")
- end
-
- test "replacing an element with a partial in an RJS template should pick the HTML template over the JS one" do
- get :index_html, "format" => "js"
- assert_response("$(\"customer\").update(\"HTML Partial\");")
- end
-
- test "replacing an element with a partial in an RJS template with a locale should pick the localed HTML template" do
- get :index_locale, "format" => "js"
- assert_response("$(\"customer\").update(\"Danish HTML Partial\");")
- end
- end
-end
diff --git a/actionpack/test/controller/new_base/render_streaming_test.rb b/actionpack/test/controller/new_base/render_streaming_test.rb
new file mode 100644
index 0000000000..fed8d40b47
--- /dev/null
+++ b/actionpack/test/controller/new_base/render_streaming_test.rb
@@ -0,0 +1,101 @@
+require 'abstract_unit'
+
+module RenderStreaming
+ class BasicController < ActionController::Base
+ self.view_paths = [ActionView::FixtureResolver.new(
+ "render_streaming/basic/hello_world.html.erb" => "Hello world",
+ "render_streaming/basic/boom.html.erb" => "<%= nil.invalid! %>",
+ "layouts/application.html.erb" => "<%= yield %>, I'm here!",
+ "layouts/boom.html.erb" => "<body class=\"<%= nil.invalid! %>\"<%= yield %></body>"
+ )]
+
+ layout "application"
+ stream :only => [:hello_world, :skip]
+
+ def hello_world
+ end
+
+ def layout_exception
+ render :action => "hello_world", :stream => true, :layout => "boom"
+ end
+
+ def template_exception
+ render :action => "boom", :stream => true
+ end
+
+ def skip
+ render :action => "hello_world", :stream => false
+ end
+
+ def explicit
+ render :action => "hello_world", :stream => true
+ end
+
+ def no_layout
+ render :action => "hello_world", :stream => true, :layout => false
+ end
+
+ def explicit_cache
+ headers["Cache-Control"] = "private"
+ render :action => "hello_world", :stream => true
+ end
+ end
+
+ class StreamingTest < Rack::TestCase
+ test "rendering with streaming enabled at the class level" do
+ get "/render_streaming/basic/hello_world"
+ assert_body "b\r\nHello world\r\nb\r\n, I'm here!\r\n0\r\n\r\n"
+ assert_streaming!
+ end
+
+ test "rendering with streaming given to render" do
+ get "/render_streaming/basic/explicit"
+ assert_body "b\r\nHello world\r\nb\r\n, I'm here!\r\n0\r\n\r\n"
+ assert_streaming!
+ end
+
+ test "rendering with streaming do not override explicit cache control given to render" do
+ get "/render_streaming/basic/explicit_cache"
+ assert_body "b\r\nHello world\r\nb\r\n, I'm here!\r\n0\r\n\r\n"
+ assert_streaming! "private"
+ end
+
+ test "rendering with streaming no layout" do
+ get "/render_streaming/basic/no_layout"
+ assert_body "b\r\nHello world\r\n0\r\n\r\n"
+ assert_streaming!
+ end
+
+ test "skip rendering with streaming at render level" do
+ get "/render_streaming/basic/skip"
+ assert_body "Hello world, I'm here!"
+ end
+
+ test "rendering with layout exception" do
+ get "/render_streaming/basic/layout_exception"
+ assert_body "d\r\n<body class=\"\r\n4e\r\n\"><script type=\"text/javascript\">window.location = \"/500.html\"</script></html>\r\n0\r\n\r\n"
+ assert_streaming!
+ end
+
+ test "rendering with template exception" do
+ get "/render_streaming/basic/template_exception"
+ assert_body "4e\r\n\"><script type=\"text/javascript\">window.location = \"/500.html\"</script></html>\r\n0\r\n\r\n"
+ assert_streaming!
+ end
+
+ test "do not stream on HTTP/1.0" do
+ get "/render_streaming/basic/hello_world", nil, "HTTP_VERSION" => "HTTP/1.0"
+ assert_body "Hello world, I'm here!"
+ assert_status 200
+ assert_equal "22", headers["Content-Length"]
+ assert_equal nil, headers["Transfer-Encoding"]
+ end
+
+ def assert_streaming!(cache="no-cache")
+ assert_status 200
+ assert_equal nil, headers["Content-Length"]
+ assert_equal "chunked", headers["Transfer-Encoding"]
+ assert_equal cache, headers["Cache-Control"]
+ end
+ end
+end if defined?(Fiber)
diff --git a/actionpack/test/controller/new_base/render_test.rb b/actionpack/test/controller/new_base/render_test.rb
index d6062bfa8c..60468bf5c7 100644
--- a/actionpack/test/controller/new_base/render_test.rb
+++ b/actionpack/test/controller/new_base/render_test.rb
@@ -81,8 +81,7 @@ module Render
end
class TestOnlyRenderPublicActions < Rack::TestCase
- describe "Only public methods on actual controllers are callable actions"
-
+ # Only public methods on actual controllers are callable actions
test "raises an exception when a method of Object is called" do
assert_raises(AbstractController::ActionNotFound) do
get "/render/blank_render/clone", {}, "action_dispatch.show_exceptions" => false
diff --git a/actionpack/test/controller/render_js_test.rb b/actionpack/test/controller/render_js_test.rb
index 491c98a0fd..f070109b27 100644
--- a/actionpack/test/controller/render_js_test.rb
+++ b/actionpack/test/controller/render_js_test.rb
@@ -14,10 +14,6 @@ class RenderJSTest < ActionController::TestCase
render :js => "alert('hello')"
end
- def greeting
- # let's just rely on the template
- end
-
def show_partial
render :partial => 'partial'
end
@@ -31,11 +27,6 @@ class RenderJSTest < ActionController::TestCase
assert_equal "text/javascript", @response.content_type
end
- def test_render_with_default_from_accept_header
- xhr :get, :greeting
- assert_equal "$(\"body\").visualEffect(\"highlight\");", @response.body
- end
-
def test_should_render_js_partial
xhr :get, :show_partial, :format => 'js'
assert_equal 'partial js', @response.body
diff --git a/actionpack/test/controller/render_other_test.rb b/actionpack/test/controller/render_other_test.rb
index eda777e7a7..b5e74e373d 100644
--- a/actionpack/test/controller/render_other_test.rb
+++ b/actionpack/test/controller/render_other_test.rb
@@ -1,6 +1,4 @@
require 'abstract_unit'
-require 'controller/fake_models'
-require 'pathname'
ActionController.add_renderer :simon do |says, options|
self.content_type = Mime::TEXT
@@ -9,248 +7,13 @@ end
class RenderOtherTest < ActionController::TestCase
class TestController < ActionController::Base
- protect_from_forgery
-
- def self.controller_path
- 'test'
- end
-
- layout :determine_layout
-
- module RenderTestHelper
- def rjs_helper_method_from_module
- page.visual_effect :highlight
- end
- end
-
- helper RenderTestHelper
- helper do
- def rjs_helper_method(value)
- page.visual_effect :highlight, value
- end
- end
-
- def enum_rjs_test
- render :update do |page|
- page.select('.product').each do |value|
- page.rjs_helper_method_from_module
- page.rjs_helper_method(value)
- page.sortable(value, :url => { :action => "order" })
- page.draggable(value)
- end
- end
- end
-
- def render_explicit_html_template
- end
-
- def render_custom_code_rjs
- render :update, :status => 404 do |page|
- page.replace :foo, :partial => 'partial'
- end
- end
-
- def render_implicit_html_template
- end
-
- def render_js_with_explicit_template
- @project_id = 4
- render :template => 'test/delete_with_js'
- end
-
- def render_js_with_explicit_action_template
- @project_id = 4
- render :action => 'delete_with_js'
- end
-
- def delete_with_js
- @project_id = 4
- end
-
- def update_page
- render :update do |page|
- page.replace_html 'balance', '$37,000,000.00'
- page.visual_effect :highlight, 'balance'
- end
- end
-
- def update_page_with_instance_variables
- @money = '$37,000,000.00'
- @div_id = 'balance'
- render :update do |page|
- page.replace_html @div_id, @money
- page.visual_effect :highlight, @div_id
- end
- end
-
- def update_page_with_view_method
- render :update do |page|
- page.replace_html 'person', pluralize(2, 'person')
- end
- end
-
- def partial_as_rjs
- render :update do |page|
- page.replace :foo, :partial => 'partial'
- end
- end
-
- def respond_to_partial_as_rjs
- respond_to do |format|
- format.js do
- render :update do |page|
- page.replace :foo, :partial => 'partial'
- end
- end
- end
- end
-
- def render_alternate_default
- # For this test, the method "default_render" is overridden:
- @alternate_default_render = lambda do
- render :update do |page|
- page.replace :foo, :partial => 'partial'
- end
- end
- end
-
def render_simon_says
render :simon => "foo"
end
-
- private
- def default_render
- @alternate_default_render ||= nil
- if @alternate_default_render
- @alternate_default_render.call
- else
- super
- end
- end
-
- def determine_layout
- case action_name
- when "hello_world", "layout_test", "rendering_without_layout",
- "rendering_nothing_on_layout", "render_text_hello_world",
- "render_text_hello_world_with_layout",
- "hello_world_with_layout_false",
- "partial_only", "partial_only_with_layout",
- "accessing_params_in_template",
- "accessing_params_in_template_with_layout",
- "render_with_explicit_template",
- "render_with_explicit_string_template",
- "update_page", "update_page_with_instance_variables"
-
- "layouts/standard"
- when "action_talk_to_layout", "layout_overriding_layout"
- "layouts/talk_from_action"
- when "render_implicit_html_template_from_xhr_request"
- (request.xhr? ? 'layouts/xhr' : 'layouts/standard')
- end
- end
end
tests TestController
- def setup
- # enable a logger so that (e.g.) the benchmarking stuff runs, so we can get
- # a more accurate simulation of what happens in "real life".
- super
- @controller.logger = Logger.new(nil)
-
- @request.host = "www.nextangle.com"
- end
-
- def test_enum_rjs_test
- ActiveSupport::SecureRandom.stubs(:base64).returns("asdf")
- get :enum_rjs_test
- body = %{
- $$(".product").each(function(value, index) {
- new Effect.Highlight(element,{});
- new Effect.Highlight(value,{});
- Sortable.create(value, {onUpdate:function(){new Ajax.Request('/render_other_test/test/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize(value) + '&authenticity_token=' + encodeURIComponent('asdf')})}});
- new Draggable(value, {});
- });
- }.gsub(/^ /, '').strip
- assert_equal body, @response.body
- end
-
- def test_explicitly_rendering_an_html_template_with_implicit_html_template_renders_should_be_possible_from_an_rjs_template
- [:js, "js"].each do |format|
- assert_nothing_raised do
- get :render_explicit_html_template, :format => format
- assert_equal %(document.write("Hello world\\n");), @response.body
- end
- end
- end
-
- def test_render_custom_code_rjs
- get :render_custom_code_rjs
- assert_response 404
- assert_equal %(Element.replace("foo", "partial html");), @response.body
- end
-
- def test_render_in_an_rjs_template_should_pick_html_templates_when_available
- [:js, "js"].each do |format|
- assert_nothing_raised do
- get :render_implicit_html_template, :format => format
- assert_equal %(document.write("Hello world\\n");), @response.body
- end
- end
- end
-
- def test_render_rjs_template_explicitly
- get :render_js_with_explicit_template
- assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body
- end
-
- def test_rendering_rjs_action_explicitly
- get :render_js_with_explicit_action_template
- assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body
- end
-
- def test_render_rjs_with_default
- get :delete_with_js
- assert_equal %!Element.remove("person");\nnew Effect.Highlight(\"project-4\",{});!, @response.body
- end
-
- def test_update_page
- get :update_page
- assert_template nil
- assert_equal 'text/javascript; charset=utf-8', @response.headers['Content-Type']
- assert_equal 2, @response.body.split($/).length
- end
-
- def test_update_page_with_instance_variables
- get :update_page_with_instance_variables
- assert_template nil
- assert_equal 'text/javascript; charset=utf-8', @response.headers["Content-Type"]
- assert_match(/balance/, @response.body)
- assert_match(/\$37/, @response.body)
- end
-
- def test_update_page_with_view_method
- get :update_page_with_view_method
- assert_template nil
- assert_equal 'text/javascript; charset=utf-8', @response.headers["Content-Type"]
- assert_match(/2 people/, @response.body)
- end
-
- def test_should_render_html_formatted_partial_with_rjs
- xhr :get, :partial_as_rjs
- assert_equal %(Element.replace("foo", "partial html");), @response.body
- end
-
- def test_should_render_html_formatted_partial_with_rjs_and_js_format
- xhr :get, :respond_to_partial_as_rjs
- assert_equal %(Element.replace("foo", "partial html");), @response.body
- end
-
- def test_should_render_with_alternate_default_render
- xhr :get, :render_alternate_default
- assert_equal %(Element.replace("foo", "partial html");), @response.body
- end
-
def test_using_custom_render_option
get :render_simon_says
assert_equal "Simon says: foo", @response.body
diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb
index be492152f2..e62f3155c5 100644
--- a/actionpack/test/controller/render_test.rb
+++ b/actionpack/test/controller/render_test.rb
@@ -517,15 +517,6 @@ class TestController < ActionController::Base
render :partial => 'partial'
end
- def render_alternate_default
- # For this test, the method "default_render" is overridden:
- @alternate_default_render = lambda do
- render :update do |page|
- page.replace :foo, :partial => 'partial'
- end
- end
- end
-
def render_to_string_with_partial
@partial_only = render_to_string :partial => "partial_only"
@partial_with_locals = render_to_string :partial => "customer", :locals => { :customer => Customer.new("david") }
diff --git a/actionpack/test/controller/view_paths_test.rb b/actionpack/test/controller/view_paths_test.rb
index edfcb5cc4d..9280a1c2d3 100644
--- a/actionpack/test/controller/view_paths_test.rb
+++ b/actionpack/test/controller/view_paths_test.rb
@@ -131,6 +131,35 @@ class ViewLoadPathsTest < ActionController::TestCase
assert_equal "Hello overridden world!", @response.body
end
+ def test_override_view_paths_with_custom_resolver
+ resolver_class = Class.new(ActionView::PathResolver) do
+ def initialize(path_set)
+ @path_set = path_set
+ end
+
+ def find_all(*args)
+ @path_set.find_all(*args).collect do |template|
+ ::ActionView::Template.new(
+ "Customized body",
+ template.identifier,
+ template.handler,
+ {
+ :virtual_path => template.virtual_path,
+ :format => template.formats
+ }
+ )
+ end
+ end
+ end
+
+ resolver = resolver_class.new(TestController.view_paths)
+ TestController.view_paths = ActionView::PathSet.new.push(resolver)
+
+ get :hello_world
+ assert_response :success
+ assert_equal "Customized body", @response.body
+ end
+
def test_inheritance
original_load_paths = ActionController::Base.view_paths
diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb
index 6f38714b2e..5abbaf74fe 100644
--- a/actionpack/test/dispatch/response_test.rb
+++ b/actionpack/test/dispatch/response_test.rb
@@ -33,22 +33,6 @@ class ResponseTest < ActiveSupport::TestCase
}, headers)
end
- test "streaming block" do
- @response.body = Proc.new do |response, output|
- 5.times { |n| output.write(n) }
- end
-
- status, headers, body = @response.to_a
- assert_equal 200, status
- assert_equal({
- "Content-Type" => "text/html; charset=utf-8"
- }, headers)
-
- parts = []
- body.each { |part| parts << part.to_s }
- assert_equal ["0", "1", "2", "3", "4"], parts
- end
-
test "content type" do
[204, 304].each do |c|
@response.status = c.to_s
diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb
index cf7a5aaa3b..ba7506721f 100644
--- a/actionpack/test/dispatch/routing_test.rb
+++ b/actionpack/test/dispatch/routing_test.rb
@@ -496,7 +496,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest
resources :todos, :id => /\d+/
end
- scope '/countries/:country', :constraints => lambda { |params, req| params[:country].among?("all", "France") } do
+ scope '/countries/:country', :constraints => lambda { |params, req| params[:country].in?(["all", "France"]) } do
match '/', :to => 'countries#index'
match '/cities', :to => 'countries#cities'
end
diff --git a/actionpack/test/dispatch/static_test.rb b/actionpack/test/dispatch/static_test.rb
index 655745a848..2ebbed4414 100644
--- a/actionpack/test/dispatch/static_test.rb
+++ b/actionpack/test/dispatch/static_test.rb
@@ -47,35 +47,4 @@ class StaticTest < ActiveSupport::TestCase
end
include StaticTests
-end
-
-class MultipleDirectorisStaticTest < ActiveSupport::TestCase
- DummyApp = lambda { |env|
- [200, {"Content-Type" => "text/plain"}, ["Hello, World!"]]
- }
- App = ActionDispatch::Static.new(DummyApp,
- { "/" => "#{FIXTURE_LOAD_PATH}/public",
- "/blog" => "#{FIXTURE_LOAD_PATH}/blog_public",
- "/foo" => "#{FIXTURE_LOAD_PATH}/non_existing_dir"
- })
-
- def setup
- @app = App
- end
-
- include StaticTests
-
- test "serves files from other mounted directories" do
- 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_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_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
+end \ No newline at end of file
diff --git a/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs b/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs
deleted file mode 100644
index 057f15e62f..0000000000
--- a/actionpack/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs
+++ /dev/null
@@ -1,6 +0,0 @@
-page.assign 'title', 'Hey'
-cache do
- page['element_1'].visual_effect :highlight
- page['element_2'].visual_effect :highlight
-end
-page.assign 'footer', 'Bye'
diff --git a/actionpack/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs b/actionpack/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs
deleted file mode 100644
index 248842c9da..0000000000
--- a/actionpack/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs
+++ /dev/null
@@ -1 +0,0 @@
-page.replace_html 'notices', :partial => 'partial' \ No newline at end of file
diff --git a/actionpack/test/fixtures/layouts/streaming.erb b/actionpack/test/fixtures/layouts/streaming.erb
new file mode 100644
index 0000000000..d3f896a6ca
--- /dev/null
+++ b/actionpack/test/fixtures/layouts/streaming.erb
@@ -0,0 +1,4 @@
+<%= yield :header -%>
+<%= yield -%>
+<%= yield :footer -%>
+<%= yield(:unknown).presence || "." -%> \ No newline at end of file
diff --git a/actionpack/test/fixtures/old_content_type/render_default_for_rjs.rjs b/actionpack/test/fixtures/old_content_type/render_default_for_rjs.rjs
deleted file mode 100644
index 8d614d04ad..0000000000
--- a/actionpack/test/fixtures/old_content_type/render_default_for_rjs.rjs
+++ /dev/null
@@ -1 +0,0 @@
-page.alert 'hello world!' \ No newline at end of file
diff --git a/actionpack/test/fixtures/respond_to/all_types_with_layout.js.rjs b/actionpack/test/fixtures/respond_to/all_types_with_layout.js.rjs
deleted file mode 100644
index b7aec7c505..0000000000
--- a/actionpack/test/fixtures/respond_to/all_types_with_layout.js.rjs
+++ /dev/null
@@ -1 +0,0 @@
-page << "RJS for all_types_with_layout" \ No newline at end of file
diff --git a/actionpack/test/fixtures/respond_to/using_defaults.js.rjs b/actionpack/test/fixtures/respond_to/using_defaults.js.rjs
deleted file mode 100644
index 469fcd8e15..0000000000
--- a/actionpack/test/fixtures/respond_to/using_defaults.js.rjs
+++ /dev/null
@@ -1 +0,0 @@
-page[:body].visual_effect :highlight \ No newline at end of file
diff --git a/actionpack/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs b/actionpack/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs
deleted file mode 100644
index 469fcd8e15..0000000000
--- a/actionpack/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs
+++ /dev/null
@@ -1 +0,0 @@
-page[:body].visual_effect :highlight \ No newline at end of file
diff --git a/actionpack/test/fixtures/respond_with/using_resource.js.rjs b/actionpack/test/fixtures/respond_with/using_resource.js.rjs
deleted file mode 100644
index 737c175a4e..0000000000
--- a/actionpack/test/fixtures/respond_with/using_resource.js.rjs
+++ /dev/null
@@ -1 +0,0 @@
-page[:body].visual_effect :highlight
diff --git a/railties/lib/rails/generators/rails/app/templates/public/images/rails.png b/actionpack/test/fixtures/sprockets/app/images/logo.png
index d5edc04e65..d5edc04e65 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/images/rails.png
+++ b/actionpack/test/fixtures/sprockets/app/images/logo.png
Binary files differ
diff --git a/actionpack/test/fixtures/sprockets/app/javascripts/application.js b/actionpack/test/fixtures/sprockets/app/javascripts/application.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionpack/test/fixtures/sprockets/app/javascripts/application.js
diff --git a/actionpack/test/fixtures/sprockets/app/javascripts/dir/xmlhr.js b/actionpack/test/fixtures/sprockets/app/javascripts/dir/xmlhr.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionpack/test/fixtures/sprockets/app/javascripts/dir/xmlhr.js
diff --git a/actionpack/test/fixtures/sprockets/app/javascripts/xmlhr.js b/actionpack/test/fixtures/sprockets/app/javascripts/xmlhr.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionpack/test/fixtures/sprockets/app/javascripts/xmlhr.js
diff --git a/actionpack/test/fixtures/sprockets/app/stylesheets/application.css b/actionpack/test/fixtures/sprockets/app/stylesheets/application.css
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionpack/test/fixtures/sprockets/app/stylesheets/application.css
diff --git a/actionpack/test/fixtures/sprockets/app/stylesheets/dir/style.css b/actionpack/test/fixtures/sprockets/app/stylesheets/dir/style.css
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionpack/test/fixtures/sprockets/app/stylesheets/dir/style.css
diff --git a/actionpack/test/fixtures/sprockets/app/stylesheets/style.css b/actionpack/test/fixtures/sprockets/app/stylesheets/style.css
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/actionpack/test/fixtures/sprockets/app/stylesheets/style.css
diff --git a/actionpack/test/fixtures/test/delete_with_js.rjs b/actionpack/test/fixtures/test/delete_with_js.rjs
deleted file mode 100644
index 4b75a955ad..0000000000
--- a/actionpack/test/fixtures/test/delete_with_js.rjs
+++ /dev/null
@@ -1,2 +0,0 @@
-page.remove 'person'
-page.visual_effect :highlight, "project-#{@project_id}"
diff --git a/actionpack/test/fixtures/test/enum_rjs_test.rjs b/actionpack/test/fixtures/test/enum_rjs_test.rjs
deleted file mode 100644
index e3004076a8..0000000000
--- a/actionpack/test/fixtures/test/enum_rjs_test.rjs
+++ /dev/null
@@ -1,6 +0,0 @@
-page.select('.product').each do |value|
- page.visual_effect :highlight
- page.visual_effect :highlight, value
- page.sortable(value, :url => { :action => "order" })
- page.draggable(value)
-end \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/greeting.js.rjs b/actionpack/test/fixtures/test/greeting.js.rjs
deleted file mode 100644
index 469fcd8e15..0000000000
--- a/actionpack/test/fixtures/test/greeting.js.rjs
+++ /dev/null
@@ -1 +0,0 @@
-page[:body].visual_effect :highlight \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/nested_streaming.erb b/actionpack/test/fixtures/test/nested_streaming.erb
new file mode 100644
index 0000000000..55525e0c92
--- /dev/null
+++ b/actionpack/test/fixtures/test/nested_streaming.erb
@@ -0,0 +1,3 @@
+<%- content_for :header do -%>?<%- end -%>
+<%= render :template => "test/streaming" %>
+? \ No newline at end of file
diff --git a/actionpack/test/fixtures/test/render_explicit_html_template.js.rjs b/actionpack/test/fixtures/test/render_explicit_html_template.js.rjs
deleted file mode 100644
index 4eb12fd6af..0000000000
--- a/actionpack/test/fixtures/test/render_explicit_html_template.js.rjs
+++ /dev/null
@@ -1 +0,0 @@
-page.call "document.write", render(:partial => "one.html.erb")
diff --git a/actionpack/test/fixtures/test/render_implicit_html_template.js.rjs b/actionpack/test/fixtures/test/render_implicit_html_template.js.rjs
deleted file mode 100644
index 3d68041756..0000000000
--- a/actionpack/test/fixtures/test/render_implicit_html_template.js.rjs
+++ /dev/null
@@ -1 +0,0 @@
-page.call "document.write", render(:partial => "one")
diff --git a/actionpack/test/fixtures/test/streaming.erb b/actionpack/test/fixtures/test/streaming.erb
new file mode 100644
index 0000000000..fb9b8b1ade
--- /dev/null
+++ b/actionpack/test/fixtures/test/streaming.erb
@@ -0,0 +1,3 @@
+<%- provide :header do -%>Yes, <%- end -%>
+this works
+<%- content_for :footer, " like a charm" -%>
diff --git a/actionpack/test/fixtures/test/streaming_buster.erb b/actionpack/test/fixtures/test/streaming_buster.erb
new file mode 100644
index 0000000000..4221d56dcc
--- /dev/null
+++ b/actionpack/test/fixtures/test/streaming_buster.erb
@@ -0,0 +1,3 @@
+<%= yield :foo -%>
+This won't look
+<% provide :unknown, " good." -%>
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index 1bf748af14..4a93def5a8 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -477,15 +477,6 @@ class AssetTagHelperTest < ActionView::TestCase
assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
end
- def test_env_asset_path
- @controller.config.asset_path = "/assets%s"
- def @controller.env; @_env ||= {} end
- @controller.env["action_dispatch.asset_path"] = "/omg%s"
-
- expected_path = "/assets/omg/images/rails.png"
- assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
- end
-
def test_proc_asset_id
@controller.config.asset_path = Proc.new do |asset_path|
"/assets.v12345#{asset_path}"
@@ -495,20 +486,6 @@ class AssetTagHelperTest < ActionView::TestCase
assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
end
- def test_env_proc_asset_path
- @controller.config.asset_path = Proc.new do |asset_path|
- "/assets.v12345#{asset_path}"
- end
-
- def @controller.env; @_env ||= {} end
- @controller.env["action_dispatch.asset_path"] = Proc.new do |asset_path|
- "/omg#{asset_path}"
- end
-
- expected_path = "/assets.v12345/omg/images/rails.png"
- assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
- end
-
def test_image_tag_interpreting_email_cid_correctly
# An inline image has no need for an alt tag to be automatically generated from the cid:
assert_equal '<img src="cid:thi%25%25sis@acontentid" />', image_tag("cid:thi%25%25sis@acontentid")
diff --git a/actionpack/test/template/capture_helper_test.rb b/actionpack/test/template/capture_helper_test.rb
index 03050485fa..a9a36e6e6b 100644
--- a/actionpack/test/template/capture_helper_test.rb
+++ b/actionpack/test/template/capture_helper_test.rb
@@ -4,7 +4,7 @@ class CaptureHelperTest < ActionView::TestCase
def setup
super
@av = ActionView::Base.new
- @_content_for = Hash.new {|h,k| h[k] = "" }
+ @_view_flow = ActionView::OutputFlow.new
end
def test_capture_captures_the_temporary_output_buffer_in_its_block
@@ -45,6 +45,20 @@ class CaptureHelperTest < ActionView::TestCase
assert ! content_for?(:something_else)
end
+ def test_provide
+ assert !content_for?(:title)
+ provide :title, "hi"
+ assert content_for?(:title)
+ assert_equal "hi", @_view_flow.get(:title)
+ provide :title, "<p>title</p>"
+ assert_equal "hi&lt;p&gt;title&lt;/p&gt;", @_view_flow.get(:title)
+
+ @_view_flow = ActionView::OutputFlow.new
+ provide :title, "hi"
+ provide :title, "<p>title</p>".html_safe
+ assert_equal "hi<p>title</p>", @_view_flow.get(:title)
+ end
+
def test_with_output_buffer_swaps_the_output_buffer_given_no_argument
assert_nil @av.output_buffer
buffer = @av.with_output_buffer do
diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb
index fd1f824a39..12d2410f49 100644
--- a/actionpack/test/template/date_helper_test.rb
+++ b/actionpack/test/template/date_helper_test.rb
@@ -53,12 +53,12 @@ class DateHelperTest < ActionView::TestCase
assert_equal "about 2 hours", distance_of_time_in_words(from, to + 89.minutes + 30.seconds)
assert_equal "about 24 hours", distance_of_time_in_words(from, to + 23.hours + 59.minutes + 29.seconds)
- # 1440..2529
+ # 1440..2519
assert_equal "1 day", distance_of_time_in_words(from, to + 23.hours + 59.minutes + 30.seconds)
assert_equal "1 day", distance_of_time_in_words(from, to + 41.hours + 59.minutes + 29.seconds)
- # 2530..43199
- assert_equal "2 days", distance_of_time_in_words(from, to + 42.hours + 59.minutes + 30.seconds)
+ # 2520..43199
+ assert_equal "2 days", distance_of_time_in_words(from, to + 41.hours + 59.minutes + 30.seconds)
assert_equal "3 days", distance_of_time_in_words(from, to + 2.days + 12.hours)
assert_equal "30 days", distance_of_time_in_words(from, to + 29.days + 23.hours + 59.minutes + 29.seconds)
diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb
index 435ce81238..7afab3179c 100644
--- a/actionpack/test/template/form_helper_test.rb
+++ b/actionpack/test/template/form_helper_test.rb
@@ -1744,7 +1744,7 @@ class FormHelperTest < ActionView::TestCase
def snowman(method = nil)
txt = %{<div style="margin:0;padding:0;display:inline">}
txt << %{<input name="utf8" type="hidden" value="&#x2713;" />}
- if method && !method.to_s.among?('get', 'post')
+ if method && !method.to_s.in?(['get', 'post'])
txt << %{<input name="_method" type="hidden" value="#{method}" />}
end
txt << %{</div>}
diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb
index 76236aee41..b92e1d9890 100644
--- a/actionpack/test/template/form_options_helper_test.rb
+++ b/actionpack/test/template/form_options_helper_test.rb
@@ -83,7 +83,7 @@ class FormOptionsHelperTest < ActionView::TestCase
def test_collection_options_with_proc_for_disabled
assert_dom_equal(
"<option value=\"&lt;Abe&gt;\">&lt;Abe&gt; went home</option>\n<option value=\"Babe\" disabled=\"disabled\">Babe went home</option>\n<option value=\"Cabe\" disabled=\"disabled\">Cabe went home</option>",
- options_from_collection_for_select(dummy_posts, "author_name", "title", :disabled => lambda{|p| p.author_name.among?("Babe", "Cabe") })
+ options_from_collection_for_select(dummy_posts, "author_name", "title", :disabled => lambda{|p| p.author_name.in?(["Babe", "Cabe"]) })
)
end
diff --git a/actionpack/test/template/form_tag_helper_test.rb b/actionpack/test/template/form_tag_helper_test.rb
index aa2114ff79..656fa0356b 100644
--- a/actionpack/test/template/form_tag_helper_test.rb
+++ b/actionpack/test/template/form_tag_helper_test.rb
@@ -14,7 +14,7 @@ class FormTagHelperTest < ActionView::TestCase
txt = %{<div style="margin:0;padding:0;display:inline">}
txt << %{<input name="utf8" type="hidden" value="&#x2713;" />}
- if method && !method.to_s.among?('get','post')
+ if method && !method.to_s.in?(['get','post'])
txt << %{<input name="_method" type="hidden" value="#{method}" />}
end
txt << %{</div>}
diff --git a/actionpack/test/template/javascript_helper_test.rb b/actionpack/test/template/javascript_helper_test.rb
index 8aa2730da1..538e0e9874 100644
--- a/actionpack/test/template/javascript_helper_test.rb
+++ b/actionpack/test/template/javascript_helper_test.rb
@@ -35,20 +35,6 @@ class JavaScriptHelperTest < ActionView::TestCase
button_to_function("Greeting", "alert('Hello world!')")
end
- def test_button_to_function_with_rjs_block
- html = button_to_function( "Greet me!" ) do |page|
- page.replace_html 'header', "<h1>Greetings</h1>"
- end
- assert_dom_equal %(<input type="button" onclick="Element.update(&quot;header&quot;, &quot;\\u003Ch1\\u003EGreetings\\u003C/h1\\u003E&quot;);;" value="Greet me!" />), html
- end
-
- def test_button_to_function_with_rjs_block_and_options
- html = button_to_function( "Greet me!", :class => "greeter" ) do |page|
- page.replace_html 'header', "<h1>Greetings</h1>"
- end
- assert_dom_equal %(<input type="button" class="greeter" onclick="Element.update(&quot;header&quot;, &quot;\\u003Ch1\\u003EGreetings\\u003C\/h1\\u003E&quot;);;" value="Greet me!" />), html
- end
-
def test_button_to_function_with_onclick
assert_dom_equal "<input onclick=\"alert('Goodbye World :('); alert('Hello world!');\" type=\"button\" value=\"Greeting\" />",
button_to_function("Greeting", "alert('Hello world!')", :onclick => "alert('Goodbye World :(')")
@@ -69,34 +55,11 @@ class JavaScriptHelperTest < ActionView::TestCase
link_to_function("Greeting", "alert('Hello world!')", :onclick => "confirm('Sanity!')")
end
- def test_link_to_function_with_rjs_block
- html = link_to_function( "Greet me!" ) do |page|
- page.replace_html 'header', "<h1>Greetings</h1>"
- end
- assert_dom_equal %(<a href="#" onclick="Element.update(&quot;header&quot;, &quot;\\u003Ch1\\u003EGreetings\\u003C/h1\\u003E&quot;);; return false;">Greet me!</a>), html
- end
-
- def test_link_to_function_with_rjs_block_and_options
- html = link_to_function( "Greet me!", :class => "updater" ) do |page|
- page.replace_html 'header', "<h1>Greetings</h1>"
- end
- assert_dom_equal %(<a href="#" class="updater" onclick="Element.update(&quot;header&quot;, &quot;\\u003Ch1\\u003EGreetings\\u003C/h1\\u003E&quot;);; return false;">Greet me!</a>), html
- end
-
- def test_link_to_function_with_href
+ def test_function_with_href
assert_dom_equal %(<a href="http://example.com/" onclick="alert('Hello world!'); return false;">Greeting</a>),
link_to_function("Greeting", "alert('Hello world!')", :href => 'http://example.com/')
end
- def test_link_to_function_with_inner_block_does_not_raise_exception
- html = link_to_function( "Greet me!" ) do |page|
- page.replace_html 'header', (content_tag :h1 do
- 'Greetings'
- end)
- end
- assert_dom_equal %(<a href="#" onclick="Element.update(&quot;header&quot;, &quot;\\u003Ch1\\u003EGreetings\\u003C/h1\\u003E&quot;);; return false;">Greet me!</a>), html
- end
-
def test_javascript_tag
self.output_buffer = 'foo'
diff --git a/actionpack/test/template/lookup_context_test.rb b/actionpack/test/template/lookup_context_test.rb
index 8d063e66b0..ff94cba59f 100644
--- a/actionpack/test/template/lookup_context_test.rb
+++ b/actionpack/test/template/lookup_context_test.rb
@@ -180,16 +180,6 @@ class LookupContextTest < ActiveSupport::TestCase
assert_not_equal template, old_template
end
-
- test "data can be stored in cached templates" do
- template = @lookup_context.find("hello_world", %w(test))
- template.data["cached"] = "data"
- assert_equal "Hello world!", template.source
-
- template = @lookup_context.find("hello_world", %w(test))
- assert_equal "data", template.data["cached"]
- assert_equal "Hello world!", template.source
- end
end
class LookupContextWithFalseCaching < ActiveSupport::TestCase
@@ -235,21 +225,6 @@ class LookupContextWithFalseCaching < ActiveSupport::TestCase
template = @lookup_context.find("foo", %w(test), true)
assert_equal "Foo", template.source
end
-
- test "data can be stored as long as template was not updated" do
- template = @lookup_context.find("foo", %w(test), true)
- template.data["cached"] = "data"
- assert_equal "Foo", template.source
-
- template = @lookup_context.find("foo", %w(test), true)
- assert_equal "data", template.data["cached"]
- assert_equal "Foo", template.source
-
- @resolver.hash["test/_foo.erb"][1] = Time.now.utc
- template = @lookup_context.find("foo", %w(test), true)
- assert_nil template.data["cached"]
- assert_equal "Foo", template.source
- end
end
class TestMissingTemplate < ActiveSupport::TestCase
diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb
deleted file mode 100644
index a6aa848a00..0000000000
--- a/actionpack/test/template/prototype_helper_test.rb
+++ /dev/null
@@ -1,476 +0,0 @@
-require 'abstract_unit'
-require 'active_model'
-
-class Bunny < Struct.new(:Bunny, :id)
- extend ActiveModel::Naming
- include ActiveModel::Conversion
- def to_key() id ? [id] : nil end
-end
-
-class Author
- 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 author' : "author ##{@id}"
- end
-end
-
-class Article
- extend ActiveModel::Naming
- include ActiveModel::Conversion
- attr_reader :id
- attr_reader :author_id
- def to_key() id ? [id] : nil end
- def save; @id = 1; @author_id = 1 end
- def new_record?; @id.nil? end
- def name
- @id.nil? ? 'new article' : "article ##{@id}"
- end
-end
-
-class Author::Nested < Author; end
-
-
-class PrototypeHelperBaseTest < ActionView::TestCase
- attr_accessor :formats, :output_buffer
-
- def update_details(details)
- @details = details
- yield if block_given?
- end
-
- def setup
- super
- @template = self
- end
-
- def url_for(options)
- if options.is_a?(String)
- options
- else
- url = "http://www.example.com/"
- url << options[:action].to_s if options and options[:action]
- url << "?a=#{options[:a]}" if options && options[:a]
- url << "&b=#{options[:b]}" if options && options[:a] && options[:b]
- url
- end
- end
-
- protected
- def request_forgery_protection_token
- nil
- end
-
- def protect_against_forgery?
- false
- end
-
- def create_generator
- block = Proc.new { |*args| yield(*args) if block_given? }
- JavaScriptGenerator.new self, &block
- end
-end
-
-class PrototypeHelperTest < PrototypeHelperBaseTest
- def _evaluate_assigns_and_ivars() end
-
- def setup
- @record = @author = Author.new
- @article = Article.new
- super
- end
-
- def test_update_page
- old_output_buffer = output_buffer
-
- block = Proc.new { |page| page.replace_html('foo', 'bar') }
- assert_equal create_generator(&block).to_s, update_page(&block)
-
- assert_equal old_output_buffer, output_buffer
- end
-
- def test_update_page_tag
- block = Proc.new { |page| page.replace_html('foo', 'bar') }
- assert_equal javascript_tag(create_generator(&block).to_s), update_page_tag(&block)
- end
-
- def test_update_page_tag_with_html_options
- block = Proc.new { |page| page.replace_html('foo', 'bar') }
- assert_equal javascript_tag(create_generator(&block).to_s, {:defer => 'true'}), update_page_tag({:defer => 'true'}, &block)
- end
-
- def test_remote_function
- res = remote_function(:url => authors_path, :with => "'author[name]='+$F('author_name')+'&author[dob]='+$F('author_dob')")
- assert_equal "new Ajax.Request('/authors', {asynchronous:true, evalScripts:true, parameters:'author[name]='+$F('author_name')+'&author[dob]='+$F('author_dob')})", res
- assert res.html_safe?
- end
-
- protected
- def author_path(record)
- "/authors/#{record.id}"
- end
-
- def authors_path
- "/authors"
- end
-
- def author_articles_path(author)
- "/authors/#{author.id}/articles"
- end
-
- def author_article_path(author, article)
- "/authors/#{author.id}/articles/#{article.id}"
- end
-end
-
-class JavaScriptGeneratorTest < PrototypeHelperBaseTest
- def setup
- super
- @generator = create_generator
- ActiveSupport.escape_html_entities_in_json = true
- end
-
- def teardown
- ActiveSupport.escape_html_entities_in_json = false
- end
-
- def _evaluate_assigns_and_ivars() end
-
- def test_insert_html_with_string
- assert_equal 'Element.insert("element", { top: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });',
- @generator.insert_html(:top, 'element', '<p>This is a test</p>')
- assert_equal 'Element.insert("element", { bottom: "\\u003Cp\u003EThis is a test\\u003C/p\u003E" });',
- @generator.insert_html(:bottom, 'element', '<p>This is a test</p>')
- assert_equal 'Element.insert("element", { before: "\\u003Cp\u003EThis is a test\\u003C/p\u003E" });',
- @generator.insert_html(:before, 'element', '<p>This is a test</p>')
- assert_equal 'Element.insert("element", { after: "\\u003Cp\u003EThis is a test\\u003C/p\u003E" });',
- @generator.insert_html(:after, 'element', '<p>This is a test</p>')
- end
-
- def test_replace_html_with_string
- assert_equal 'Element.update("element", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");',
- @generator.replace_html('element', '<p>This is a test</p>')
- end
-
- def test_replace_element_with_string
- assert_equal 'Element.replace("element", "\\u003Cdiv id=\"element\"\\u003E\\u003Cp\\u003EThis is a test\\u003C/p\\u003E\\u003C/div\\u003E");',
- @generator.replace('element', '<div id="element"><p>This is a test</p></div>')
- end
-
- def test_remove
- assert_equal 'Element.remove("foo");',
- @generator.remove('foo')
- assert_equal '["foo","bar","baz"].each(Element.remove);',
- @generator.remove('foo', 'bar', 'baz')
- end
-
- def test_show
- assert_equal 'Element.show("foo");',
- @generator.show('foo')
- assert_equal '["foo","bar","baz"].each(Element.show);',
- @generator.show('foo', 'bar', 'baz')
- end
-
- def test_hide
- assert_equal 'Element.hide("foo");',
- @generator.hide('foo')
- assert_equal '["foo","bar","baz"].each(Element.hide);',
- @generator.hide('foo', 'bar', 'baz')
- end
-
- def test_toggle
- assert_equal 'Element.toggle("foo");',
- @generator.toggle('foo')
- assert_equal '["foo","bar","baz"].each(Element.toggle);',
- @generator.toggle('foo', 'bar', 'baz')
- end
-
- def test_alert
- assert_equal 'alert("hello");', @generator.alert('hello')
- end
-
- def test_redirect_to
- assert_equal 'window.location.href = "http://www.example.com/welcome";',
- @generator.redirect_to(:action => 'welcome')
- assert_equal 'window.location.href = "http://www.example.com/welcome?a=b&c=d";',
- @generator.redirect_to("http://www.example.com/welcome?a=b&c=d")
- end
-
- def test_reload
- assert_equal 'window.location.reload();',
- @generator.reload
- end
-
- def test_delay
- @generator.delay(20) do
- @generator.hide('foo')
- end
-
- assert_equal "setTimeout(function() {\n;\nElement.hide(\"foo\");\n}, 20000);", @generator.to_s
- end
-
- def test_to_s
- @generator.insert_html(:top, 'element', '<p>This is a test</p>')
- @generator.insert_html(:bottom, 'element', '<p>This is a test</p>')
- @generator.remove('foo', 'bar')
- @generator.replace_html('baz', '<p>This is a test</p>')
-
- assert_equal <<-EOS.chomp, @generator.to_s
-Element.insert("element", { top: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });
-Element.insert("element", { bottom: "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E" });
-["foo","bar"].each(Element.remove);
-Element.update("baz", "\\u003Cp\\u003EThis is a test\\u003C/p\\u003E");
- EOS
- end
-
- def test_element_access
- assert_equal %($("hello");), @generator['hello']
- end
-
- def test_element_access_on_records
- assert_equal %($("bunny_5");), @generator[Bunny.new(:id => 5)]
- assert_equal %($("new_bunny");), @generator[Bunny.new]
- end
-
- def test_element_proxy_one_deep
- @generator['hello'].hide
- assert_equal %($("hello").hide();), @generator.to_s
- end
-
- def test_element_proxy_variable_access
- @generator['hello']['style']
- assert_equal %($("hello").style;), @generator.to_s
- end
-
- def test_element_proxy_variable_access_with_assignment
- @generator['hello']['style']['color'] = 'red'
- assert_equal %($("hello").style.color = "red";), @generator.to_s
- end
-
- def test_element_proxy_assignment
- @generator['hello'].width = 400
- assert_equal %($("hello").width = 400;), @generator.to_s
- end
-
- def test_element_proxy_two_deep
- @generator['hello'].hide("first").clean_whitespace
- assert_equal %($("hello").hide("first").cleanWhitespace();), @generator.to_s
- end
-
- def test_select_access
- assert_equal %($$("div.hello");), @generator.select('div.hello')
- end
-
- def test_select_proxy_one_deep
- @generator.select('p.welcome b').first.hide
- assert_equal %($$("p.welcome b").first().hide();), @generator.to_s
- end
-
- def test_visual_effect
- assert_equal %(new Effect.Puff("blah",{});),
- @generator.visual_effect(:puff,'blah')
- end
-
- def test_visual_effect_toggle
- assert_equal %(Effect.toggle("blah",'appear',{});),
- @generator.visual_effect(:toggle_appear,'blah')
- end
-
- def test_sortable
- assert_equal %(Sortable.create("blah", {onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize("blah")})}});),
- @generator.sortable('blah', :url => { :action => "order" })
- assert_equal %(Sortable.create("blah", {onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:false, evalScripts:true, parameters:Sortable.serialize("blah")})}});),
- @generator.sortable('blah', :url => { :action => "order" }, :type => :synchronous)
- end
-
- def test_draggable
- assert_equal %(new Draggable("blah", {});),
- @generator.draggable('blah')
- end
-
- def test_drop_receiving
- assert_equal %(Droppables.add("blah", {onDrop:function(element){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}});),
- @generator.drop_receiving('blah', :url => { :action => "order" })
- assert_equal %(Droppables.add("blah", {onDrop:function(element){new Ajax.Request('http://www.example.com/order', {asynchronous:false, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}});),
- @generator.drop_receiving('blah', :url => { :action => "order" }, :type => :synchronous)
- end
-
- def test_collection_first_and_last
- @generator.select('p.welcome b').first.hide()
- @generator.select('p.welcome b').last.show()
- assert_equal <<-EOS.strip, @generator.to_s
-$$("p.welcome b").first().hide();
-$$("p.welcome b").last().show();
- EOS
- end
-
- def test_collection_proxy_with_each
- @generator.select('p.welcome b').each do |value|
- value.remove_class_name 'selected'
- end
- @generator.select('p.welcome b').each do |value, index|
- @generator.visual_effect :highlight, value
- end
- assert_equal <<-EOS.strip, @generator.to_s
-$$("p.welcome b").each(function(value, index) {
-value.removeClassName("selected");
-});
-$$("p.welcome b").each(function(value, index) {
-new Effect.Highlight(value,{});
-});
- EOS
- end
-
- def test_collection_proxy_on_collect
- @generator.select('p').collect('a') { |para| para.show }
- @generator.select('p').collect { |para| para.hide }
- assert_equal <<-EOS.strip, @generator.to_s
-var a = $$("p").collect(function(value, index) {
-return value.show();
-});
-$$("p").collect(function(value, index) {
-return value.hide();
-});
- EOS
- @generator = create_generator
- end
-
- def test_collection_proxy_with_grep
- @generator.select('p').grep 'a', /^a/ do |value|
- @generator << '(value.className == "welcome")'
- end
- @generator.select('p').grep 'b', /b$/ do |value, index|
- @generator.call 'alert', value
- @generator << '(value.className == "welcome")'
- end
-
- assert_equal <<-EOS.strip, @generator.to_s
-var a = $$("p").grep(/^a/, function(value, index) {
-return (value.className == "welcome");
-});
-var b = $$("p").grep(/b$/, function(value, index) {
-alert(value);
-return (value.className == "welcome");
-});
- EOS
- end
-
- def test_collection_proxy_with_inject
- @generator.select('p').inject 'a', [] do |memo, value|
- @generator << '(value.className == "welcome")'
- end
- @generator.select('p').inject 'b', nil do |memo, value, index|
- @generator.call 'alert', memo
- @generator << '(value.className == "welcome")'
- end
-
- assert_equal <<-EOS.strip, @generator.to_s
-var a = $$("p").inject([], function(memo, value, index) {
-return (value.className == "welcome");
-});
-var b = $$("p").inject(null, function(memo, value, index) {
-alert(memo);
-return (value.className == "welcome");
-});
- EOS
- end
-
- def test_collection_proxy_with_pluck
- @generator.select('p').pluck('a', 'className')
- assert_equal %(var a = $$("p").pluck("className");), @generator.to_s
- end
-
- def test_collection_proxy_with_zip
- ActionView::Helpers::JavaScriptCollectionProxy.new(@generator, '[1, 2, 3]').zip('a', [4, 5, 6], [7, 8, 9])
- ActionView::Helpers::JavaScriptCollectionProxy.new(@generator, '[1, 2, 3]').zip('b', [4, 5, 6], [7, 8, 9]) do |array|
- @generator.call 'array.reverse'
- end
-
- assert_equal <<-EOS.strip, @generator.to_s
-var a = [1, 2, 3].zip([4,5,6], [7,8,9]);
-var b = [1, 2, 3].zip([4,5,6], [7,8,9], function(array) {
-return array.reverse();
-});
- EOS
- end
-
- def test_collection_proxy_with_find_all
- @generator.select('p').find_all 'a' do |value, index|
- @generator << '(value.className == "welcome")'
- end
-
- assert_equal <<-EOS.strip, @generator.to_s
-var a = $$("p").findAll(function(value, index) {
-return (value.className == "welcome");
-});
- EOS
- end
-
- def test_collection_proxy_with_in_groups_of
- @generator.select('p').in_groups_of('a', 3)
- @generator.select('p').in_groups_of('a', 3, 'x')
- assert_equal <<-EOS.strip, @generator.to_s
-var a = $$("p").inGroupsOf(3);
-var a = $$("p").inGroupsOf(3, "x");
- EOS
- end
-
- def test_collection_proxy_with_each_slice
- @generator.select('p').each_slice('a', 3)
- @generator.select('p').each_slice('a', 3) do |group, index|
- group.reverse
- end
-
- assert_equal <<-EOS.strip, @generator.to_s
-var a = $$("p").eachSlice(3);
-var a = $$("p").eachSlice(3, function(value, index) {
-return value.reverse();
-});
- EOS
- end
-
- def test_debug_rjs
- ActionView::Base.debug_rjs = true
- @generator['welcome'].replace_html 'Welcome'
- assert_equal "try {\n$(\"welcome\").update(\"Welcome\");\n} catch (e) { alert('RJS error:\\n\\n' + e.toString()); alert('$(\\\"welcome\\\").update(\\\"Welcome\\\");'); throw e }", @generator.to_s
- ensure
- ActionView::Base.debug_rjs = false
- end
-
- def test_literal
- literal = @generator.literal("function() {}")
- assert_equal "function() {}", ActiveSupport::JSON.encode(literal)
- assert_equal "", @generator.to_s
- end
-
- def test_class_proxy
- @generator.form.focus('my_field')
- assert_equal "Form.focus(\"my_field\");", @generator.to_s
- end
-
- def test_call_with_block
- @generator.call(:before)
- @generator.call(:my_method) do |p|
- p[:one].show
- p[:two].hide
- end
- @generator.call(:in_between)
- @generator.call(:my_method_with_arguments, true, "hello") do |p|
- p[:three].visual_effect(:highlight)
- end
- assert_equal "before();\nmy_method(function() { $(\"one\").show();\n$(\"two\").hide(); });\nin_between();\nmy_method_with_arguments(true, \"hello\", function() { $(\"three\").visualEffect(\"highlight\"); });", @generator.to_s
- end
-
- def test_class_proxy_call_with_block
- @generator.my_object.my_method do |p|
- p[:one].show
- p[:two].hide
- end
- assert_equal "MyObject.myMethod(function() { $(\"one\").show();\n$(\"two\").hide(); });", @generator.to_s
- end
-end
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index dd86bfed04..d4e912c410 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -68,10 +68,6 @@ module RenderTestCases
assert_equal "The secret is in the sauce\n", @view.render(:file => "test/dot.directory/render_file_with_ivar")
end
- def test_render_update
- assert_equal 'alert("Hello, World!");', @view.render(:update) { |page| page.alert('Hello, World!') }
- end
-
def test_render_partial_from_default
assert_equal "only partial", @view.render("test/partial_only")
end
@@ -231,20 +227,11 @@ module RenderTestCases
"@output_buffer << 'source: #{template.source.inspect}'\n"
end
- WithViewHandler = lambda do |template, view|
- %'"#{template.class} #{view.class}"'
- end
-
def test_render_inline_with_render_from_to_proc
ActionView::Template.register_template_handler :ruby_handler, :source.to_proc
assert_equal '3', @view.render(:inline => "(1 + 2).to_s", :type => :ruby_handler)
end
- def test_render_inline_with_template_handler_with_view
- ActionView::Template.register_template_handler :with_view, WithViewHandler
- assert_equal 'ActionView::Template ActionView::Base', @view.render(:inline => "Hello, World!", :type => :with_view)
- end
-
def test_render_inline_with_compilable_custom_type
ActionView::Template.register_template_handler :foo, CustomHandler
assert_equal 'source: "Hello, World!"', @view.render(:inline => "Hello, World!", :type => :foo)
diff --git a/actionpack/test/template/scriptaculous_helper_test.rb b/actionpack/test/template/scriptaculous_helper_test.rb
deleted file mode 100644
index 233012bfdd..0000000000
--- a/actionpack/test/template/scriptaculous_helper_test.rb
+++ /dev/null
@@ -1,86 +0,0 @@
-require 'abstract_unit'
-
-class ScriptaculousHelperTest < ActionView::TestCase
- tests ActionView::Helpers::ScriptaculousHelper
-
- def url_for(options)
- url = "http://www.example.com/"
- url << options[:action].to_s if options and options[:action]
- url
- end
-
- def test_effect
- assert_equal "new Effect.Highlight(\"posts\",{});", visual_effect(:highlight, "posts")
- assert_equal "new Effect.Highlight(\"posts\",{});", visual_effect("highlight", :posts)
- assert_equal "new Effect.Highlight(\"posts\",{});", visual_effect(:highlight, :posts)
- assert_equal "new Effect.Fade(\"fademe\",{duration:4.0});", visual_effect(:fade, "fademe", :duration => 4.0)
- assert_equal "new Effect.Shake(element,{});", visual_effect(:shake)
- assert_equal "new Effect.DropOut(\"dropme\",{queue:'end'});", visual_effect(:drop_out, 'dropme', :queue => :end)
- assert_equal "new Effect.Highlight(\"status\",{endcolor:'#EEEEEE'});", visual_effect(:highlight, 'status', :endcolor => '#EEEEEE')
- assert_equal "new Effect.Highlight(\"status\",{restorecolor:'#500000', startcolor:'#FEFEFE'});", visual_effect(:highlight, 'status', :restorecolor => '#500000', :startcolor => '#FEFEFE')
-
- # chop the queue params into a comma separated list
- beginning, ending = 'new Effect.DropOut("dropme",{queue:{', '}});'
- ve = [
- visual_effect(:drop_out, 'dropme', :queue => {:position => "end", :scope => "test", :limit => 2}),
- visual_effect(:drop_out, 'dropme', :queue => {:scope => :list, :limit => 2}),
- visual_effect(:drop_out, 'dropme', :queue => {:position => :end, :scope => :test, :limit => 2})
- ].collect { |v| v[beginning.length..-ending.length-1].split(',') }
-
- assert ve[0].include?("limit:2")
- assert ve[0].include?("scope:'test'")
- assert ve[0].include?("position:'end'")
-
- assert ve[1].include?("limit:2")
- assert ve[1].include?("scope:'list'")
-
- assert ve[2].include?("limit:2")
- assert ve[2].include?("scope:'test'")
- assert ve[2].include?("position:'end'")
- end
-
- def test_toggle_effects
- assert_equal "Effect.toggle(\"posts\",'appear',{});", visual_effect(:toggle_appear, "posts")
- assert_equal "Effect.toggle(\"posts\",'slide',{});", visual_effect(:toggle_slide, "posts")
- assert_equal "Effect.toggle(\"posts\",'blind',{});", visual_effect(:toggle_blind, "posts")
- assert_equal "Effect.toggle(\"posts\",'appear',{});", visual_effect("toggle_appear", "posts")
- assert_equal "Effect.toggle(\"posts\",'slide',{});", visual_effect("toggle_slide", "posts")
- assert_equal "Effect.toggle(\"posts\",'blind',{});", visual_effect("toggle_blind", "posts")
- end
-
-
- def test_sortable_element
- assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nSortable.create(\"mylist\", {onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize(\"mylist\")})}})\n//]]>\n</script>),
- sortable_element("mylist", :url => { :action => "order" })
- assert_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nSortable.create(\"mylist\", {constraint:'horizontal', onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize(\"mylist\")})}, tag:'div'})\n//]]>\n</script>),
- sortable_element("mylist", :tag => "div", :constraint => "horizontal", :url => { :action => "order" })
- assert_dom_equal %|<script type=\"text/javascript\">\n//<![CDATA[\nSortable.create(\"mylist\", {constraint:'horizontal', containment:['list1','list2'], onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize(\"mylist\")})}})\n//]]>\n</script>|,
- sortable_element("mylist", :containment => ['list1','list2'], :constraint => "horizontal", :url => { :action => "order" })
- assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nSortable.create(\"mylist\", {constraint:'horizontal', containment:'list1', onUpdate:function(){new Ajax.Request('http://www.example.com/order', {asynchronous:true, evalScripts:true, parameters:Sortable.serialize(\"mylist\")})}})\n//]]>\n</script>),
- sortable_element("mylist", :containment => 'list1', :constraint => "horizontal", :url => { :action => "order" })
- end
-
- def test_draggable_element
- assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Draggable(\"product_13\", {})\n//]]>\n</script>),
- draggable_element("product_13")
- assert_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Draggable(\"product_13\", {revert:true})\n//]]>\n</script>),
- draggable_element("product_13", :revert => true)
- end
-
- def test_drop_receiving_element
- assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nDroppables.add(\"droptarget1\", {onDrop:function(element){new Ajax.Request('http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}})\n//]]>\n</script>),
- drop_receiving_element("droptarget1")
- assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nDroppables.add(\"droptarget1\", {accept:'products', onDrop:function(element){new Ajax.Request('http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}})\n//]]>\n</script>),
- drop_receiving_element("droptarget1", :accept => 'products')
- assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nDroppables.add(\"droptarget1\", {accept:'products', onDrop:function(element){new Ajax.Updater('infobox', 'http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}})\n//]]>\n</script>),
- drop_receiving_element("droptarget1", :accept => 'products', :update => 'infobox')
- assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nDroppables.add(\"droptarget1\", {accept:['tshirts','mugs'], onDrop:function(element){new Ajax.Updater('infobox', 'http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)})}})\n//]]>\n</script>),
- drop_receiving_element("droptarget1", :accept => ['tshirts','mugs'], :update => 'infobox')
- assert_dom_equal %(<script type=\"text/javascript\">\n//<![CDATA[\nDroppables.add("droptarget1", {hoverclass:'dropready', onDrop:function(element){if (confirm('Are you sure?')) { new Ajax.Request('http://www.example.com/update_drop', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(element.id)}); }}})\n//]]>\n</script>),
- drop_receiving_element('droptarget1', :hoverclass=>'dropready', :url=>{:action=>'update_drop'}, :confirm => 'Are you sure?')
-
- end
- def protect_against_forgery?
- false
- end
-end
diff --git a/actionpack/test/template/sprockets_helper_test.rb b/actionpack/test/template/sprockets_helper_test.rb
new file mode 100644
index 0000000000..d8aef34bfb
--- /dev/null
+++ b/actionpack/test/template/sprockets_helper_test.rb
@@ -0,0 +1,119 @@
+require 'abstract_unit'
+require 'sprockets'
+require 'mocha'
+
+module Rails; end
+
+class SprocketsHelperTest < ActionView::TestCase
+ tests ActionView::Helpers::SprocketsHelper
+
+ attr_accessor :assets
+
+ def setup
+ super
+
+ @controller = BasicController.new
+
+ @request = Class.new do
+ def protocol() 'http://' end
+ def ssl?() false end
+ def host_with_port() 'localhost' end
+ end.new
+
+ @controller.request = @request
+
+ @assets = Sprockets::Environment.new
+ @assets.paths << FIXTURES.join("sprockets/app/javascripts")
+ @assets.paths << FIXTURES.join("sprockets/app/stylesheets")
+ @assets.paths << FIXTURES.join("sprockets/app/images")
+
+ application = Object.new
+ Rails.stubs(:application).returns(application)
+ application.stubs(:config).returns(config)
+ application.stubs(:assets).returns(@assets)
+
+ config.perform_caching = true
+ end
+
+ def url_for(*args)
+ "http://www.example.com"
+ end
+
+ test "asset path" do
+ assert_equal "/assets/logo-9c0a079bdd7701d7e729bd956823d153.png",
+ asset_path("logo.png")
+
+ assert_equal "/images/logo",
+ asset_path("/images/logo")
+ assert_equal "/images/logo.gif",
+ asset_path("/images/logo.gif")
+
+ assert_equal "/dir/audio",
+ asset_path("/dir/audio")
+
+ assert_equal "http://www.example.com/video/play",
+ asset_path("http://www.example.com/video/play")
+ assert_equal "http://www.example.com/video/play.mp4",
+ asset_path("http://www.example.com/video/play.mp4")
+ end
+
+ test "javascript path" do
+ assert_equal "/assets/application-d41d8cd98f00b204e9800998ecf8427e.js",
+ asset_path(:application, "js")
+
+ assert_equal "/assets/xmlhr-d41d8cd98f00b204e9800998ecf8427e.js",
+ asset_path("xmlhr", "js")
+ assert_equal "/assets/dir/xmlhr-d41d8cd98f00b204e9800998ecf8427e.js",
+ asset_path("dir/xmlhr.js", "js")
+
+ assert_equal "/dir/xmlhr.js",
+ asset_path("/dir/xmlhr", "js")
+
+ assert_equal "http://www.railsapplication.com/js/xmlhr",
+ asset_path("http://www.railsapplication.com/js/xmlhr", "js")
+ assert_equal "http://www.railsapplication.com/js/xmlhr.js",
+ asset_path("http://www.railsapplication.com/js/xmlhr.js", "js")
+ end
+
+ test "javascript include tag" do
+ assert_equal '<script src="/assets/application-d41d8cd98f00b204e9800998ecf8427e.js" type="text/javascript"></script>',
+ sprockets_javascript_include_tag(:application)
+
+ assert_equal '<script src="/assets/xmlhr-d41d8cd98f00b204e9800998ecf8427e.js" type="text/javascript"></script>',
+ sprockets_javascript_include_tag("xmlhr")
+ assert_equal '<script src="/assets/xmlhr-d41d8cd98f00b204e9800998ecf8427e.js" type="text/javascript"></script>',
+ sprockets_javascript_include_tag("xmlhr.js")
+ assert_equal '<script src="http://www.railsapplication.com/xmlhr" type="text/javascript"></script>',
+ sprockets_javascript_include_tag("http://www.railsapplication.com/xmlhr")
+ end
+
+ test "stylesheet path" do
+ assert_equal "/assets/application-d41d8cd98f00b204e9800998ecf8427e.css", asset_path(:application, "css")
+
+ assert_equal "/assets/style-d41d8cd98f00b204e9800998ecf8427e.css", asset_path("style", "css")
+ assert_equal "/assets/dir/style-d41d8cd98f00b204e9800998ecf8427e.css", asset_path("dir/style.css", "css")
+ assert_equal "/dir/style.css", asset_path("/dir/style.css", "css")
+
+ assert_equal "http://www.railsapplication.com/css/style",
+ asset_path("http://www.railsapplication.com/css/style", "css")
+ assert_equal "http://www.railsapplication.com/css/style.css",
+ asset_path("http://www.railsapplication.com/css/style.css", "css")
+ end
+
+ test "stylesheet link tag" do
+ assert_equal '<link href="/assets/application-d41d8cd98f00b204e9800998ecf8427e.css" media="screen" rel="stylesheet" type="text/css" />',
+ sprockets_stylesheet_link_tag(:application)
+
+ assert_equal '<link href="/assets/style-d41d8cd98f00b204e9800998ecf8427e.css" media="screen" rel="stylesheet" type="text/css" />',
+ sprockets_stylesheet_link_tag("style")
+ assert_equal '<link href="/assets/style-d41d8cd98f00b204e9800998ecf8427e.css" media="screen" rel="stylesheet" type="text/css" />',
+ sprockets_stylesheet_link_tag("style.css")
+
+ assert_equal '<link href="http://www.railsapplication.com/style.css" media="screen" rel="stylesheet" type="text/css" />',
+ sprockets_stylesheet_link_tag("http://www.railsapplication.com/style.css")
+ assert_equal '<link href="/assets/style-d41d8cd98f00b204e9800998ecf8427e.css" media="all" rel="stylesheet" type="text/css" />',
+ sprockets_stylesheet_link_tag("style", :media => "all")
+ assert_equal '<link href="/assets/style-d41d8cd98f00b204e9800998ecf8427e.css" media="print" rel="stylesheet" type="text/css" />',
+ sprockets_stylesheet_link_tag("style", :media => "print")
+ end
+end
diff --git a/actionpack/test/template/streaming_render_test.rb b/actionpack/test/template/streaming_render_test.rb
new file mode 100644
index 0000000000..4d69081570
--- /dev/null
+++ b/actionpack/test/template/streaming_render_test.rb
@@ -0,0 +1,105 @@
+# encoding: utf-8
+require 'abstract_unit'
+require 'controller/fake_models'
+
+class TestController < ActionController::Base
+end
+
+class FiberedTest < ActiveSupport::TestCase
+ def setup
+ view_paths = ActionController::Base.view_paths
+ @assigns = { :secret => 'in the sauce' }
+ @view = ActionView::Base.new(view_paths, @assigns)
+ @controller_view = TestController.new.view_context
+ end
+
+ def buffered_render(options)
+ body = @view.render_body(options)
+ string = ""
+ body.each do |piece|
+ string << piece
+ end
+ string
+ end
+
+ def test_streaming_works
+ content = []
+ body = @view.render_body(:template => "test/hello_world.erb", :layout => "layouts/yield")
+
+ body.each do |piece|
+ content << piece
+ end
+
+ assert_equal "<title>", content[0]
+ assert_equal "", content[1]
+ assert_equal "</title>\n", content[2]
+ assert_equal "Hello world!", content[3]
+ assert_equal "\n", content[4]
+ end
+
+ def test_render_file
+ assert_equal "Hello world!", buffered_render(:file => "test/hello_world.erb")
+ end
+
+ def test_render_file_with_locals
+ locals = { :secret => 'in the sauce' }
+ assert_equal "The secret is in the sauce\n", buffered_render(:file => "test/render_file_with_locals.erb", :locals => locals)
+ end
+
+ def test_render_partial
+ assert_equal "only partial", buffered_render(:partial => "test/partial_only")
+ end
+
+ def test_render_inline
+ assert_equal "Hello, World!", buffered_render(:inline => "Hello, World!")
+ end
+
+ def test_render_without_layout
+ assert_equal "Hello world!", buffered_render(:template => "test/hello_world")
+ end
+
+ def test_render_with_layout
+ assert_equal %(<title></title>\nHello world!\n),
+ buffered_render(:template => "test/hello_world.erb", :layout => "layouts/yield")
+ end
+
+ def test_render_with_layout_which_has_render_inline
+ assert_equal %(welcome\nHello world!\n),
+ buffered_render(:template => "test/hello_world.erb", :layout => "layouts/yield_with_render_inline_inside")
+ end
+
+ def test_render_with_layout_which_renders_another_partial
+ assert_equal %(partial html\nHello world!\n),
+ buffered_render(:template => "test/hello_world.erb", :layout => "layouts/yield_with_render_partial_inside")
+ end
+
+ def test_render_with_nested_layout
+ assert_equal %(<title>title</title>\n\n<div id="column">column</div>\n<div id="content">content</div>\n),
+ buffered_render(:template => "test/nested_layout.erb", :layout => "layouts/yield")
+ end
+
+ def test_render_with_file_in_layout
+ assert_equal %(\n<title>title</title>\n\n),
+ buffered_render(:template => "test/layout_render_file.erb")
+ end
+
+ def test_render_with_handler_without_streaming_support
+ assert_match "<p>This is grand!</p>", buffered_render(:template => "test/hello")
+ end
+
+ def test_render_with_streaming_multiple_yields_provide_and_content_for
+ assert_equal "Yes, \nthis works\n like a charm.",
+ buffered_render(:template => "test/streaming", :layout => "layouts/streaming")
+ end
+
+ def test_render_with_streaming_with_fake_yields_and_streaming_buster
+ assert_equal "This won't look\n good.",
+ buffered_render(:template => "test/streaming_buster", :layout => "layouts/streaming")
+ end
+
+ def test_render_with_nested_streaming_multiple_yields_provide_and_content_for
+ assert_equal "?Yes, \n\nthis works\n\n? like a charm.",
+ buffered_render(:template => "test/nested_streaming", :layout => "layouts/streaming")
+ end
+
+end if defined?(Fiber) \ No newline at end of file
diff --git a/actionpack/test/template/template_test.rb b/actionpack/test/template/template_test.rb
index 3432a02c3c..5c655d5b69 100644
--- a/actionpack/test/template/template_test.rb
+++ b/actionpack/test/template/template_test.rb
@@ -113,44 +113,6 @@ class TestERBTemplate < ActiveSupport::TestCase
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
-
if "ruby".encoding_aware?
def test_resulting_string_is_utf8
@template = new_template
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index be55581c66..6ee5e04267 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -56,6 +56,8 @@ module ActiveModel
module AttributeMethods
extend ActiveSupport::Concern
+ COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/
+
included do
class_attribute :attribute_method_matchers, :instance_writer => false
self.attribute_method_matchers = []
@@ -106,10 +108,13 @@ module ActiveModel
if block_given?
sing.send :define_method, name, &block
else
- if name =~ /^[a-zA-Z_]\w*[!?=]?$/
- sing.class_eval <<-eorb, __FILE__, __LINE__ + 1
- def #{name}; #{value.nil? ? 'nil' : value.to_s.inspect}; end
- eorb
+ # If we can compile the method name, do it. Otherwise use define_method.
+ # This is an important *optimization*, please don't change it. define_method
+ # has slower dispatch and consumes more memory.
+ if name =~ COMPILABLE_REGEXP
+ sing.class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{name}; #{value.nil? ? 'nil' : value.to_s.inspect}; end
+ RUBY
else
value = value.to_s if value
sing.send(:define_method, name) { value }
@@ -232,8 +237,19 @@ module ActiveModel
def alias_attribute(new_name, old_name)
attribute_method_matchers.each do |matcher|
- define_method(matcher.method_name(new_name)) do |*args|
- send(matcher.method_name(old_name), *args)
+ matcher_new = matcher.method_name(new_name).to_s
+ matcher_old = matcher.method_name(old_name).to_s
+
+ if matcher_new =~ COMPILABLE_REGEXP && matcher_old =~ COMPILABLE_REGEXP
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{matcher_new}(*args)
+ send(:#{matcher_old}, *args)
+ end
+ RUBY
+ else
+ define_method(matcher_new) do |*args|
+ send(matcher_old, *args)
+ end
end
end
end
@@ -276,14 +292,25 @@ module ActiveModel
else
method_name = matcher.method_name(attr_name)
- generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
+ generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
if method_defined?('#{method_name}')
undef :'#{method_name}'
end
- define_method('#{method_name}') do |*args|
- send('#{matcher.method_missing_target}', '#{attr_name}', *args)
- end
- STR
+ RUBY
+
+ if method_name.to_s =~ COMPILABLE_REGEXP
+ generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{method_name}(*args)
+ send(:#{matcher.method_missing_target}, '#{attr_name}', *args)
+ end
+ RUBY
+ else
+ generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1
+ define_method('#{method_name}') do |*args|
+ send('#{matcher.method_missing_target}', '#{attr_name}', *args)
+ end
+ RUBY
+ end
end
end
end
diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb
index 957d0ddaaa..ee94ad66cf 100644
--- a/activemodel/lib/active_model/secure_password.rb
+++ b/activemodel/lib/active_model/secure_password.rb
@@ -31,11 +31,10 @@ module ActiveModel
# User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user
def has_secure_password
attr_reader :password
- attr_accessor :password_confirmation
validates_confirmation_of :password
validates_presence_of :password_digest
-
+
include InstanceMethodsOnActivation
if respond_to?(:attributes_protected_by_default)
@@ -59,7 +58,9 @@ module ActiveModel
# Encrypts the password into the password_digest attribute.
def password=(unencrypted_password)
@password = unencrypted_password
- self.password_digest = BCrypt::Password.create(unencrypted_password)
+ unless unencrypted_password.blank?
+ self.password_digest = BCrypt::Password.create(unencrypted_password)
+ end
end
end
end
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index 5f7f7a6ad6..5304743389 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -68,7 +68,7 @@ module ActiveModel #:nodoc:
#
# class TitleValidator < ActiveModel::EachValidator
# def validate_each(record, attribute, value)
- # record.errors[attribute] << 'must be Mr. Mrs. or Dr.' unless value.among?('Mr.', 'Mrs.', 'Dr.')
+ # record.errors[attribute] << 'must be Mr. Mrs. or Dr.' unless value.in?(['Mr.', 'Mrs.', 'Dr.'])
# end
# end
#
diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb
index 4a47a7a226..c455cf57b3 100644
--- a/activemodel/test/cases/secure_password_test.rb
+++ b/activemodel/test/cases/secure_password_test.rb
@@ -9,6 +9,18 @@ class SecurePasswordTest < ActiveModel::TestCase
@user = User.new
end
+ test "blank password" do
+ user = User.new
+ user.password = ''
+ assert !user.valid?, 'user should be invalid'
+ end
+
+ test "nil password" do
+ user = User.new
+ user.password = nil
+ assert !user.valid?, 'user should be invalid'
+ end
+
test "password must be present" do
assert !@user.valid?
assert_equal 1, @user.errors.size
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 88c2562619..9ff29f1155 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,57 @@
*Rails 3.1.0 (unreleased)*
+* default_scope can take a block, lambda, or any other object which responds to `call` for lazy
+ evaluation:
+
+ default_scope { ... }
+ default_scope lambda { ... }
+ default_scope method(:foo)
+
+ This feature was originally implemented by Tim Morgan, but was then removed in favour of
+ defining a 'default_scope' class method, but has now been added back in by Jon Leighton.
+ The relevant lighthouse ticket is #1812.
+
+* Default scopes are now evaluated at the latest possible moment, to avoid problems where
+ scopes would be created which would implicitly contain the default scope, which would then
+ be impossible to get rid of via Model.unscoped.
+
+ Note that this means that if you are inspecting the internal structure of an
+ ActiveRecord::Relation, it will *not* contain the default scope, though the resulting
+ query will do. You can get a relation containing the default scope by calling
+ ActiveRecord#with_default_scope, though this is not part of the public API.
+
+ [Jon Leighton]
+
+* Calling 'default_scope' multiple times in a class (including when a superclass calls
+ 'default_scope') is deprecated. The current behavior is that this will merge the default
+ scopes together:
+
+ class Post < ActiveRecord::Base # Rails 3.1
+ default_scope where(:published => true)
+ default_scope where(:hidden => false)
+ # The default scope is now: where(:published => true, :hidden => false)
+ end
+
+ In Rails 3.2, the behavior will be changed to overwrite previous scopes:
+
+ class Post < ActiveRecord::Base # Rails 3.2
+ default_scope where(:published => true)
+ default_scope where(:hidden => false)
+ # The default scope is now: where(:hidden => false)
+ end
+
+ If you wish to merge default scopes in special ways, it is recommended to define your default
+ scope as a class method and use the standard techniques for sharing code (inheritance, mixins,
+ etc.):
+
+ class Post < ActiveRecord::Base
+ def self.default_scope
+ where(:published => true).where(:hidden => false)
+ end
+ end
+
+ [Jon Leighton]
+
* PostgreSQL adapter only supports PostgreSQL version 8.2 and higher.
* ConnectionManagement middleware is changed to clean up the connection pool
diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb
index 66f6477ec5..687b668634 100644
--- a/activerecord/lib/active_record/associations/association.rb
+++ b/activerecord/lib/active_record/associations/association.rb
@@ -164,7 +164,7 @@ module ActiveRecord
def creation_attributes
attributes = {}
- if reflection.macro.among?(:has_one, :has_many) && !options[:through]
+ if reflection.macro.in?([:has_one, :has_many]) && !options[:through]
attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
if reflection.options[:as]
diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb
index 763b12f708..f6d26840c2 100644
--- a/activerecord/lib/active_record/associations/builder/belongs_to.rb
+++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb
@@ -67,7 +67,7 @@ module ActiveRecord::Associations::Builder
def configure_dependency
if options[:dependent]
- unless options[:dependent].among?(:destroy, :delete)
+ unless options[:dependent].in?([:destroy, :delete])
raise ArgumentError, "The :dependent option expects either :destroy or :delete (#{options[:dependent].inspect})"
end
diff --git a/activerecord/lib/active_record/associations/builder/has_many.rb b/activerecord/lib/active_record/associations/builder/has_many.rb
index 67ce339380..ecbc70888f 100644
--- a/activerecord/lib/active_record/associations/builder/has_many.rb
+++ b/activerecord/lib/active_record/associations/builder/has_many.rb
@@ -16,7 +16,7 @@ module ActiveRecord::Associations::Builder
def configure_dependency
if options[:dependent]
- unless options[:dependent].among?(:destroy, :delete_all, :nullify, :restrict)
+ unless options[:dependent].in?([:destroy, :delete_all, :nullify, :restrict])
raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, " \
":nullify or :restrict (#{options[:dependent].inspect})"
end
diff --git a/activerecord/lib/active_record/associations/builder/has_one.rb b/activerecord/lib/active_record/associations/builder/has_one.rb
index 18d7117bb7..88c0d3e90f 100644
--- a/activerecord/lib/active_record/associations/builder/has_one.rb
+++ b/activerecord/lib/active_record/associations/builder/has_one.rb
@@ -29,7 +29,7 @@ module ActiveRecord::Associations::Builder
def configure_dependency
if options[:dependent]
- unless options[:dependent].among?(:destroy, :delete, :nullify, :restrict)
+ unless options[:dependent].in?([:destroy, :delete, :nullify, :restrict])
raise ArgumentError, "The :dependent option expects either :destroy, :delete, " \
":nullify or :restrict (#{options[:dependent].inspect})"
end
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb
index 9f4fc44cc6..33a184d48d 100644
--- a/activerecord/lib/active_record/associations/collection_association.rb
+++ b/activerecord/lib/active_record/associations/collection_association.rb
@@ -21,14 +21,7 @@ module ActiveRecord
attr_reader :proxy
def initialize(owner, reflection)
- # When scopes are created via method_missing on the proxy, they are stored so that
- # any records fetched from the database are kept around for future use.
- @scopes_cache = Hash.new do |hash, method|
- hash[method] = { }
- end
-
super
-
@proxy = CollectionProxy.new(self)
end
@@ -74,7 +67,6 @@ module ActiveRecord
def reset
@loaded = false
@target = []
- @scopes_cache.clear
end
def select(select = nil)
@@ -327,10 +319,6 @@ module ActiveRecord
end
end
- def cached_scope(method, args)
- @scopes_cache[method][args] ||= scoped.readonly(nil).send(method, *args)
- end
-
def load_target
if find_target?
targets = []
diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb
index cf77d770c9..388173c1fb 100644
--- a/activerecord/lib/active_record/associations/collection_proxy.rb
+++ b/activerecord/lib/active_record/associations/collection_proxy.rb
@@ -82,8 +82,6 @@ module ActiveRecord
end
end
- elsif @association.klass.scopes[method]
- @association.cached_scope(method, args)
else
scoped.readonly(nil).send(method, *args, &block)
end
diff --git a/activerecord/lib/active_record/associations/has_one_association.rb b/activerecord/lib/active_record/associations/has_one_association.rb
index aaa8b97389..7134dc85c8 100644
--- a/activerecord/lib/active_record/associations/has_one_association.rb
+++ b/activerecord/lib/active_record/associations/has_one_association.rb
@@ -52,7 +52,7 @@ module ActiveRecord
end
def remove_target!(method)
- if method.among?(:delete, :destroy)
+ if method.in?([:delete, :destroy])
target.send(method)
else
nullify_owner_attributes(target)
diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
index 4121a5b378..0a666598ed 100644
--- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb
+++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb
@@ -89,14 +89,7 @@ module ActiveRecord
foreign_key = reflection.active_record_primary_key
end
- constraint = table[key].eq(foreign_table[foreign_key])
-
- if reflection.klass.finder_needs_type_condition?
- constraint = table.create_and([
- constraint,
- reflection.klass.send(:type_condition, table)
- ])
- end
+ constraint = build_constraint(reflection, table, key, foreign_table, foreign_key)
relation.from(join(table, constraint))
@@ -111,6 +104,19 @@ module ActiveRecord
relation
end
+ def build_constraint(reflection, table, key, foreign_table, foreign_key)
+ constraint = table[key].eq(foreign_table[foreign_key])
+
+ if reflection.klass.finder_needs_type_condition?
+ constraint = table.create_and([
+ constraint,
+ reflection.klass.send(:type_condition, table)
+ ])
+ end
+
+ constraint
+ end
+
def join_relation(joining_relation)
self.join_type = Arel::OuterJoin
joining_relation.joins(self)
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 69d5cd83f1..aef99e3129 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -43,7 +43,7 @@ module ActiveRecord
end
if attr_name == primary_key && attr_name != "id"
- define_read_method(:id, attr_name, columns_hash[attr_name])
+ define_read_method('id', attr_name, columns_hash[attr_name])
end
end
@@ -59,7 +59,9 @@ module ActiveRecord
end
# Define an attribute reader method. Cope with nil column.
- def define_read_method(symbol, attr_name, column)
+ # method_name is the same as attr_name except when a non-standard primary key is used,
+ # we still define #id as an accessor for the key
+ def define_read_method(method_name, attr_name, column)
cast_code = column.type_cast_code('v')
access_code = "(v=@attributes['#{attr_name}']) && #{cast_code}"
@@ -70,12 +72,25 @@ module ActiveRecord
if cache_attribute?(attr_name)
access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
end
- if symbol =~ /^[a-zA-Z_]\w*[!?=]?$/
- generated_attribute_methods.module_eval("def _#{symbol}; #{access_code}; end; alias #{symbol} _#{symbol}", __FILE__, __LINE__)
+
+ # Where possible, generate the method by evalling a string, as this will result in
+ # faster accesses because it avoids the block eval and then string eval incurred
+ # by the second branch.
+ #
+ # The second, slower, branch is necessary to support instances where the database
+ # returns columns with extra stuff in (like 'my_column(omg)').
+ if method_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP
+ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__
+ def _#{method_name}
+ #{access_code}
+ end
+
+ alias #{method_name} _#{method_name}
+ STR
else
generated_attribute_methods.module_eval do
- define_method("_#{symbol}") { eval(access_code) }
- alias_method(symbol, "_#{symbol}")
+ define_method("_#{method_name}") { eval(access_code) }
+ alias_method(method_name, "_#{method_name}")
end
end
end
@@ -84,9 +99,11 @@ module ActiveRecord
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name)
- send "_#{attr_name}"
- rescue NoMethodError
- _read_attribute attr_name
+ if respond_to? "_#{attr_name}"
+ send "_#{attr_name}" if @attributes.has_key?(attr_name.to_s)
+ else
+ _read_attribute attr_name
+ end
end
def _read_attribute(attr_name)
diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
index 67b0e77a25..62a3cfa9a5 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -59,7 +59,7 @@ module ActiveRecord
private
def create_time_zone_conversion_attribute?(name, column)
- time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) && column.type.among?(:datetime, :timestamp)
+ time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) && column.type.in?([:datetime, :timestamp])
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb
index 7661676f8c..c77a3ac145 100644
--- a/activerecord/lib/active_record/attribute_methods/write.rb
+++ b/activerecord/lib/active_record/attribute_methods/write.rb
@@ -10,7 +10,7 @@ module ActiveRecord
module ClassMethods
protected
def define_method_attribute=(attr_name)
- if attr_name =~ /^[a-zA-Z_]\w*[!?=]?$/
+ if attr_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP
generated_attribute_methods.module_eval("def #{attr_name}=(new_value); write_attribute('#{attr_name}', new_value); end", __FILE__, __LINE__)
else
generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value|
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index fe81c7dc2f..9a01d793f9 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -425,8 +425,8 @@ module ActiveRecord #:nodoc:
self.store_full_sti_class = true
# Stores the default scope for the class
- class_attribute :default_scoping, :instance_writer => false
- self.default_scoping = []
+ class_attribute :default_scopes, :instance_writer => false
+ self.default_scopes = []
# Returns a hash of all the attributes that have been specified for serialization as
# keys and their class restriction as values.
@@ -870,7 +870,9 @@ module ActiveRecord #:nodoc:
# Returns a scope for this class without taking into account the default_scope.
#
# class Post < ActiveRecord::Base
- # default_scope :published => true
+ # def self.default_scope
+ # where :published => true
+ # end
# end
#
# Post.all # Fires "SELECT * FROM posts WHERE published = true"
@@ -892,13 +894,8 @@ module ActiveRecord #:nodoc:
block_given? ? relation.scoping { yield } : relation
end
- def scoped_methods #:nodoc:
- key = :"#{self}_scoped_methods"
- Thread.current[key] = Thread.current[key].presence || self.default_scoping.dup
- end
-
def before_remove_const #:nodoc:
- reset_scoped_methods
+ self.current_scope = nil
end
# Specifies how the record is loaded by +Marshal+.
@@ -1020,7 +1017,7 @@ module ActiveRecord #:nodoc:
super unless all_attributes_exists?(attribute_names)
if match.finder?
options = arguments.extract_options!
- relation = options.any? ? construct_finder_arel(options, current_scoped_methods) : scoped
+ relation = options.any? ? scoped(options) : scoped
relation.send :find_by_attributes, match, attribute_names, *arguments
elsif match.instantiator?
scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block
@@ -1109,43 +1106,47 @@ module ActiveRecord #:nodoc:
# end
#
# *Note*: the +:find+ scope also has effect on update and deletion methods, like +update_all+ and +delete_all+.
- def with_scope(method_scoping = {}, action = :merge, &block)
- method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping)
+ def with_scope(scope = {}, action = :merge, &block)
+ # If another Active Record class has been passed in, get its current scope
+ scope = scope.current_scope if !scope.is_a?(Relation) && scope.respond_to?(:current_scope)
- if method_scoping.is_a?(Hash)
+ previous_scope = self.current_scope
+
+ if scope.is_a?(Hash)
# Dup first and second level of hash (method and params).
- method_scoping = method_scoping.dup
- method_scoping.each do |method, params|
- method_scoping[method] = params.dup unless params == true
+ scope = scope.dup
+ scope.each do |method, params|
+ scope[method] = params.dup unless params == true
end
- method_scoping.assert_valid_keys([ :find, :create ])
- relation = construct_finder_arel(method_scoping[:find] || {})
+ scope.assert_valid_keys([ :find, :create ])
+ relation = construct_finder_arel(scope[:find] || {})
+ relation.default_scoped = true unless action == :overwrite
- if current_scoped_methods && current_scoped_methods.create_with_value && method_scoping[:create]
+ if previous_scope && previous_scope.create_with_value && scope[:create]
scope_for_create = if action == :merge
- current_scoped_methods.create_with_value.merge(method_scoping[:create])
+ previous_scope.create_with_value.merge(scope[:create])
else
- method_scoping[:create]
+ scope[:create]
end
relation = relation.create_with(scope_for_create)
else
- scope_for_create = method_scoping[:create]
- scope_for_create ||= current_scoped_methods.create_with_value if current_scoped_methods
+ scope_for_create = scope[:create]
+ scope_for_create ||= previous_scope.create_with_value if previous_scope
relation = relation.create_with(scope_for_create) if scope_for_create
end
- method_scoping = relation
+ scope = relation
end
- method_scoping = current_scoped_methods.merge(method_scoping) if current_scoped_methods && action == :merge
+ scope = previous_scope.merge(scope) if previous_scope && action == :merge
- self.scoped_methods << method_scoping
+ self.current_scope = scope
begin
yield
ensure
- self.scoped_methods.pop
+ self.current_scope = previous_scope
end
end
@@ -1168,14 +1169,24 @@ MSG
with_scope(method_scoping, :overwrite, &block)
end
- # Sets the default options for the model. The format of the
- # <tt>options</tt> argument is the same as in find.
+ def current_scope #:nodoc:
+ Thread.current[:"#{self}_current_scope"]
+ end
+
+ def current_scope=(scope) #:nodoc:
+ Thread.current[:"#{self}_current_scope"] = scope
+ end
+
+ # Use this macro in your model to set a default scope for all operations on
+ # the model.
#
# class Person < ActiveRecord::Base
# default_scope order('last_name, first_name')
# end
#
- # <tt>default_scope</tt> is also applied while creating/building a record. It is not
+ # Person.all # => SELECT * FROM people ORDER BY last_name, first_name
+ #
+ # The <tt>default_scope</tt> is also applied while creating/building a record. It is not
# applied while updating a record.
#
# class Article < ActiveRecord::Base
@@ -1184,23 +1195,73 @@ MSG
#
# Article.new.published # => true
# Article.create.published # => true
- def default_scope(options = {})
- reset_scoped_methods
- default_scoping = self.default_scoping.dup
- self.default_scoping = default_scoping << construct_finder_arel(options, default_scoping.pop)
- end
+ #
+ # You can also use <tt>default_scope</tt> with a block, in order to have it lazily evaluated:
+ #
+ # class Article < ActiveRecord::Base
+ # default_scope { where(:published_at => Time.now - 1.week) }
+ # end
+ #
+ # (You can also pass any object which responds to <tt>call</tt> to the <tt>default_scope</tt>
+ # macro, and it will be called when building the default scope.)
+ #
+ # If you need to do more complex things with a default scope, you can alternatively
+ # define it as a class method:
+ #
+ # class Article < ActiveRecord::Base
+ # def self.default_scope
+ # # Should return a scope, you can call 'super' here etc.
+ # end
+ # end
+ def default_scope(scope = {})
+ if default_scopes.length != 0
+ ActiveSupport::Deprecation.warn <<-WARN
+Calling 'default_scope' multiple times in a class (including when a superclass calls 'default_scope') is deprecated. The current behavior is that this will merge the default scopes together:
+
+class Post < ActiveRecord::Base # Rails 3.1
+ default_scope where(:published => true)
+ default_scope where(:hidden => false)
+ # The default scope is now: where(:published => true, :hidden => false)
+end
- def current_scoped_methods #:nodoc:
- method = scoped_methods.last
- if method.respond_to?(:call)
- relation.scoping { method.call }
- else
- method
+In Rails 3.2, the behavior will be changed to overwrite previous scopes:
+
+class Post < ActiveRecord::Base # Rails 3.2
+ default_scope where(:published => true)
+ default_scope where(:hidden => false)
+ # The default scope is now: where(:hidden => false)
+end
+
+If you wish to merge default scopes in special ways, it is recommended to define your default scope as a class method and use the standard techniques for sharing code (inheritance, mixins, etc.):
+
+class Post < ActiveRecord::Base
+ def self.default_scope
+ where(:published => true).where(:hidden => false)
+ end
+end
+ WARN
end
+
+ scope = Proc.new if block_given?
+ self.default_scopes = default_scopes.dup << scope
end
- def reset_scoped_methods #:nodoc:
- Thread.current[:"#{self}_scoped_methods"] = nil
+ def build_default_scope #:nodoc:
+ if method(:default_scope).owner != Base.singleton_class
+ # Use relation.scoping to ensure we ignore whatever the current value of
+ # self.current_scope may be.
+ relation.scoping { default_scope }
+ elsif default_scopes.any?
+ default_scopes.inject(relation) do |default_scope, scope|
+ if scope.is_a?(Hash)
+ default_scope.apply_finder_options(scope)
+ elsif !scope.is_a?(Relation) && scope.respond_to?(:call)
+ default_scope.merge(scope.call)
+ else
+ default_scope.merge(scope)
+ end
+ end
+ end
end
# Returns the class type of the record using the current module as a prefix. So descendants of
@@ -1916,11 +1977,8 @@ MSG
end
def populate_with_current_scope_attributes
- if scope = self.class.send(:current_scoped_methods)
- create_with = scope.scope_for_create
- create_with.each { |att,value|
- respond_to?("#{att}=") && send("#{att}=", value)
- }
+ self.class.scoped.scope_for_create.each do |att,value|
+ respond_to?("#{att}=") && send("#{att}=", value)
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
index a3082b8f01..6d9b5c7b32 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb
@@ -56,8 +56,17 @@ module ActiveRecord
end
# Returns the last auto-generated ID from the affected table.
- def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
- insert_sql(sql, name, pk, id_value, sequence_name)
+ #
+ # +id_value+ will be returned unless the value is nil, in
+ # which case the database will attempt to calculate the last inserted
+ # id and return that value.
+ #
+ # If the next id was calculated in advance (as in Oracle), it should be
+ # passed in as +id_value+.
+ def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil, binds = [])
+ sql, binds = sql_for_insert(sql, pk, id_value, sequence_name, binds)
+ value = exec_insert(sql, name, binds)
+ id_value || last_inserted_id(value)
end
# Executes the update statement and returns the number of rows affected.
@@ -364,6 +373,15 @@ module ActiveRecord
end
end
end
+
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
+ [sql, binds]
+ end
+
+ def last_inserted_id(result)
+ row = result.rows.first
+ row && row.first
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
index 7489e88eef..3de850ec9e 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb
@@ -35,7 +35,43 @@ module ActiveRecord
when Date, Time then "'#{quoted_date(value)}'"
when Symbol then "'#{quote_string(value.to_s)}'"
else
- "'#{quote_string(value.to_yaml)}'"
+ "'#{quote_string(YAML.dump(value))}'"
+ end
+ end
+
+ # Cast a +value+ to a type that the database understands. For example,
+ # SQLite does not understand dates, so this method will convert a Date
+ # to a String.
+ def type_cast(value, column)
+ return value.id if value.respond_to?(:quoted_id)
+
+ case value
+ when String, ActiveSupport::Multibyte::Chars
+ value = value.to_s
+ return value unless column
+
+ case column.type
+ when :binary then value
+ when :integer then value.to_i
+ when :float then value.to_f
+ else
+ value
+ end
+
+ when true, false
+ if column && column.type == :integer
+ value ? 1 : 0
+ else
+ value ? 't' : 'f'
+ end
+ # BigDecimals need to be put in a non-normalized form and quoted.
+ when nil then nil
+ when BigDecimal then value.to_s('F')
+ when Numeric then value
+ when Date, Time then quoted_date(value)
+ when Symbol then value.to_s
+ else
+ YAML.dump(value)
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index b0e6136e12..d24cce0a3c 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -105,7 +105,7 @@ module ActiveRecord
# Returns a bind substitution value given a +column+ and list of current
# +binds+
- def substitute_for(column, binds)
+ def substitute_at(column, index)
Arel.sql '?'
end
diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
index 1f7e527eeb..7ac72acd58 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb
@@ -282,6 +282,17 @@ module ActiveRecord
end
alias :create :insert_sql
+ def exec_insert(sql, name, binds)
+ binds = binds.dup
+
+ # Pretend to support bind parameters
+ execute sql.gsub('?') { quote(*binds.shift.reverse) }, name
+ end
+
+ def last_inserted_id(result)
+ @connection.last_id
+ end
+
def update_sql(sql, name = nil)
super
@connection.affected_rows
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index d88075fdea..c2e75acb9a 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -244,6 +244,12 @@ module ActiveRecord
end
end
+ def type_cast(value, column)
+ return super unless value == true || value == false
+
+ value ? 1 : 0
+ end
+
def quote_column_name(name) #:nodoc:
@quoted_column_names[name] ||= "`#{name}`"
end
@@ -403,7 +409,7 @@ module ActiveRecord
end
stmt.execute(*binds.map { |col, val|
- col ? col.type_cast(val) : val
+ type_cast(val, col)
})
if metadata = stmt.result_metadata
cols = cache[:cols] ||= metadata.fetch_fields.map { |field|
@@ -425,6 +431,10 @@ module ActiveRecord
exec_query(sql, name, binds)
end
+ def last_inserted_id(result)
+ @connection.insert_id
+ end
+
def exec_without_stmt(sql, name = 'SQL') # :nodoc:
# Some queries, like SHOW CREATE TABLE don't work through the prepared
# statement API. For those queries, we need to use this method. :'(
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 1c2cc7d5fa..acc3e9c5e3 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -360,6 +360,18 @@ module ActiveRecord
end
end
+ def type_cast(value, column)
+ return super unless column
+
+ case value
+ when String
+ return super unless 'bytea' == column.sql_type
+ { :value => value, :format => 1 }
+ else
+ super
+ end
+ end
+
# Quotes strings for use in SQL input.
def quote_string(s) #:nodoc:
@connection.escape(s)
@@ -511,8 +523,8 @@ module ActiveRecord
end
end
- def substitute_for(column, current_values)
- Arel.sql("$#{current_values.length + 1}")
+ def substitute_at(column, index)
+ Arel.sql("$#{index + 1}")
end
def exec_query(sql, name = 'SQL', binds = [])
@@ -530,7 +542,7 @@ module ActiveRecord
# Clear the queue
@connection.get_last_result
@connection.send_query_prepared(key, binds.map { |col, val|
- col ? col.type_cast(val) : val
+ type_cast(val, col)
})
@connection.block
result = @connection.get_last_result
@@ -544,6 +556,18 @@ module ActiveRecord
exec_query(sql, name, binds)
end
+ def sql_for_insert(sql, pk, id_value, sequence_name, binds)
+ unless pk
+ _, table = extract_schema_and_table(sql.split(" ", 4)[2])
+
+ pk = primary_key(table)
+ end
+
+ sql = "#{sql} RETURNING #{quote_column_name(pk)}" if pk
+
+ [sql, binds]
+ end
+
# Executes an UPDATE query and returns the number of affected tuples.
def update_sql(sql, name = nil)
super.cmd_tuples
@@ -1069,4 +1093,3 @@ module ActiveRecord
end
end
end
-
diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
index 8ef286b473..9e7f874f4b 100644
--- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
@@ -165,7 +165,7 @@ module ActiveRecord
cols = cache[:cols] ||= stmt.columns
stmt.reset!
stmt.bind_params binds.map { |col, val|
- col ? col.type_cast(val) : val
+ type_cast(val, col)
}
end
@@ -177,6 +177,10 @@ module ActiveRecord
exec_query(sql, name, binds)
end
+ def last_inserted_id(result)
+ @connection.last_insert_row_id
+ end
+
def execute(sql, name = nil) #:nodoc:
log(sql, name) { @connection.execute(sql) }
end
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index d5fff65303..588f52be44 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -9,11 +9,6 @@ module ActiveRecord
module NamedScope
extend ActiveSupport::Concern
- included do
- class_attribute :scopes
- self.scopes = {}
- end
-
module ClassMethods
# Returns an anonymous \scope.
#
@@ -35,7 +30,13 @@ module ActiveRecord
if options
scoped.apply_finder_options(options)
else
- current_scoped_methods ? relation.merge(current_scoped_methods) : relation.clone
+ if current_scope
+ current_scope.clone
+ else
+ scope = relation.clone
+ scope.default_scoped = true
+ scope
+ end
end
end
@@ -50,6 +51,14 @@ module ActiveRecord
# The above calls to <tt>scope</tt> define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red,
# in effect, represents the query <tt>Shirt.where(:color => 'red')</tt>.
#
+ # Note that this is simply 'syntactic sugar' for defining an actual class method:
+ #
+ # class Shirt < ActiveRecord::Base
+ # def self.red
+ # where(:color => 'red')
+ # end
+ # end
+ #
# Unlike <tt>Shirt.find(...)</tt>, however, the object returned by Shirt.red is not an Array; it
# resembles the association object constructed by a <tt>has_many</tt> declaration. For instance,
# you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>, <tt>Shirt.red.where(:size => 'small')</tt>.
@@ -76,11 +85,31 @@ module ActiveRecord
# Named \scopes can also be procedural:
#
# class Shirt < ActiveRecord::Base
- # scope :colored, lambda {|color| where(:color => color) }
+ # scope :colored, lambda { |color| where(:color => color) }
# end
#
# In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
#
+ # On Ruby 1.9 you can use the 'stabby lambda' syntax:
+ #
+ # scope :colored, ->(color) { where(:color => color) }
+ #
+ # Note that scopes defined with \scope will be evaluated when they are defined, rather than
+ # when they are used. For example, the following would be incorrect:
+ #
+ # class Post < ActiveRecord::Base
+ # scope :recent, where('published_at >= ?', Time.now - 1.week)
+ # end
+ #
+ # The example above would be 'frozen' to the <tt>Time.now</tt> value when the <tt>Post</tt>
+ # class was defined, and so the resultant SQL query would always be the same. The correct
+ # way to do this would be via a lambda, which will re-evaluate the scope each time
+ # it is called:
+ #
+ # class Post < ActiveRecord::Base
+ # scope :recent, lambda { where('published_at >= ?', Time.now - 1.week) }
+ # end
+ #
# Named \scopes can also have extensions, just as with <tt>has_many</tt> declarations:
#
# class Shirt < ActiveRecord::Base
@@ -129,27 +158,20 @@ module ActiveRecord
scope_proc = lambda do |*args|
options = scope_options.respond_to?(:call) ? scope_options.call(*args) : scope_options
+ options = scoped.apply_finder_options(options) if options.is_a?(Hash)
- relation = if options.is_a?(Hash)
- scoped.apply_finder_options(options)
- elsif options
- scoped.merge(options)
- else
- scoped
- end
+ relation = scoped.merge(options)
extension ? relation.extending(extension) : relation
end
- self.scopes = self.scopes.merge name => scope_proc
-
- singleton_class.send(:redefine_method, name, &scopes[name])
+ singleton_class.send(:redefine_method, name, &scope_proc)
end
protected
def valid_scope_name?(name)
- if !scopes[name] && respond_to?(name, true)
+ if respond_to?(name, true)
logger.warn "Creating scope :#{name}. " \
"Overwriting existing method #{self.name}.#{name}."
end
diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake
index 944a29a3e6..6b3c38cb58 100644
--- a/activerecord/lib/active_record/railties/databases.rake
+++ b/activerecord/lib/active_record/railties/databases.rake
@@ -137,7 +137,7 @@ db_namespace = namespace :db do
end
def local_database?(config, &block)
- if config['host'].among?("127.0.0.1", "localhost") || config['host'].blank?
+ if config['host'].in?(["127.0.0.1", "localhost"]) || config['host'].blank?
yield
else
$stderr.puts "This task only modifies local databases. #{config['database']} is on a remote host."
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index eac6a5dcad..bcba85d7a4 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -164,7 +164,7 @@ module ActiveRecord
def initialize(macro, name, options, active_record)
super
- @collection = macro.among?(:has_many, :has_and_belongs_to_many)
+ @collection = macro.in?([:has_many, :has_and_belongs_to_many])
end
# Returns a new, unsaved instance of the associated class. +options+ will
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index 75269c85d9..359f9d8a66 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -6,7 +6,7 @@ module ActiveRecord
JoinOperation = Struct.new(:relation, :join_class, :on)
ASSOCIATION_METHODS = [:includes, :eager_load, :preload]
MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having, :bind]
- SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :create_with, :from]
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :create_with, :from, :reorder]
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches
@@ -15,14 +15,16 @@ module ActiveRecord
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :to => :klass
attr_reader :table, :klass, :loaded
- attr_accessor :extensions
+ attr_accessor :extensions, :default_scoped
alias :loaded? :loaded
+ alias :default_scoped? :default_scoped
def initialize(klass, table)
@klass, @table = klass, table
@implicit_readonly = nil
@loaded = false
+ @default_scoped = false
SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)}
(ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])}
@@ -46,17 +48,30 @@ module ActiveRecord
im = arel.create_insert
im.into @table
+ conn = @klass.connection
+
+ substitutes = values.sort_by { |arel_attr,_| arel_attr.name }
+ binds = substitutes.map do |arel_attr, value|
+ [@klass.columns_hash[arel_attr.name], value]
+ end
+
+ substitutes.each_with_index do |tuple, i|
+ tuple[1] = conn.substitute_at(tuple.first, i)
+ end
+
if values.empty? # empty insert
im.values = im.create_values [connection.null_insert_value], []
else
- im.insert values
+ im.insert substitutes
end
- @klass.connection.insert(
+ conn.insert(
im.to_sql,
'SQL',
primary_key,
- primary_key_value)
+ primary_key_value,
+ nil,
+ binds)
end
def new(*args, &block)
@@ -154,12 +169,7 @@ module ActiveRecord
# Please check unscoped if you want to remove all previous scopes (including
# the default_scope) during the execution of a block.
def scoping
- @klass.scoped_methods << self
- begin
- yield
- ensure
- @klass.scoped_methods.pop
- end
+ @klass.send(:with_scope, self, :overwrite) { yield }
end
# Updates all records with details given if they match a set of conditions supplied, limits and order can
@@ -377,7 +387,7 @@ module ActiveRecord
end
def where_values_hash
- equalities = @where_values.grep(Arel::Nodes::Equality).find_all { |node|
+ equalities = with_default_scope.where_values.grep(Arel::Nodes::Equality).find_all { |node|
node.left.relation.name == table_name
}
@@ -405,13 +415,20 @@ module ActiveRecord
to_a.inspect
end
+ def with_default_scope #:nodoc:
+ if default_scoped?
+ default_scope = @klass.send(:build_default_scope)
+ default_scope ? default_scope.merge(self) : self
+ else
+ self
+ end
+ end
+
protected
def method_missing(method, *args, &block)
if Array.method_defined?(method)
to_a.send(method, *args, &block)
- elsif @klass.scopes[method]
- merge(@klass.send(method, *args, &block))
elsif @klass.respond_to?(method)
scoping { @klass.send(method, *args, &block) }
elsif arel.respond_to?(method)
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 673e47942b..aae257a0e7 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -320,7 +320,7 @@ module ActiveRecord
column = columns_hash[primary_key]
- substitute = connection.substitute_for(column, @bind_values)
+ substitute = connection.substitute_at(column, @bind_values.length)
relation = where(table[primary_key].eq(substitute))
relation.bind_values = [[column, id]]
record = relation.first
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 02b7056989..94aa999715 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -8,7 +8,8 @@ module ActiveRecord
attr_accessor :includes_values, :eager_load_values, :preload_values,
:select_values, :group_values, :order_values, :joins_values,
:where_values, :having_values, :bind_values,
- :limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value, :from_value
+ :limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value,
+ :from_value, :reorder_value
def includes(*args)
args.reject! {|a| a.blank? }
@@ -63,7 +64,11 @@ module ActiveRecord
end
def reorder(*args)
- except(:order).order(args)
+ return self if args.blank?
+
+ relation = clone
+ relation.reorder_value = args.flatten
+ relation
end
def joins(*args)
@@ -163,7 +168,7 @@ module ActiveRecord
end
def arel
- @arel ||= build_arel
+ @arel ||= with_default_scope.build_arel
end
def build_arel
@@ -180,7 +185,8 @@ module ActiveRecord
arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?
- arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty?
+ order = @reorder_value ? @reorder_value : @order_values
+ arel.order(*order.uniq.reject{|o| o.blank?}) unless order.empty?
build_select(arel, @select_values.uniq)
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index 128e0fbd86..69706b5ead 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -8,6 +8,8 @@ module ActiveRecord
merged_relation = clone
+ r = r.with_default_scope if r.default_scoped? && r.klass != klass
+
Relation::ASSOCIATION_METHODS.each do |method|
value = r.send(:"#{method}_values")
@@ -70,6 +72,7 @@ module ActiveRecord
#
def except(*skips)
result = self.class.new(@klass, table)
+ result.default_scoped = default_scoped
((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) - skips).each do |method|
result.send(:"#{method}_values=", send(:"#{method}_values"))
@@ -94,6 +97,7 @@ module ActiveRecord
#
def only(*onlies)
result = self.class.new(@klass, table)
+ result.default_scoped = default_scoped
((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) & onlies).each do |method|
result.send(:"#{method}_values=", send(:"#{method}_values"))
diff --git a/activerecord/lib/rails/generators/active_record/session_migration/session_migration_generator.rb b/activerecord/lib/rails/generators/active_record/session_migration/session_migration_generator.rb
index d80c8ba996..90923f6e74 100644
--- a/activerecord/lib/rails/generators/active_record/session_migration/session_migration_generator.rb
+++ b/activerecord/lib/rails/generators/active_record/session_migration/session_migration_generator.rb
@@ -14,7 +14,7 @@ module ActiveRecord
def session_table_name
current_table_name = ActiveRecord::SessionStore::Session.table_name
- if current_table_name.among?("sessions", "session")
+ if current_table_name.in?(["sessions", "session"])
current_table_name = (ActiveRecord::Base.pluralize_table_names ? 'session'.pluralize : 'session')
end
current_table_name
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index eb3f8143e7..eee771ecff 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -44,7 +44,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase
end
def test_bind_value_substitute
- bind_param = @connection.substitute_for('foo', [])
+ bind_param = @connection.substitute_at('foo', 0)
assert_equal Arel.sql('?'), bind_param
end
diff --git a/activerecord/test/cases/adapters/mysql/quoting_test.rb b/activerecord/test/cases/adapters/mysql/quoting_test.rb
new file mode 100644
index 0000000000..9673e2bb46
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql/quoting_test.rb
@@ -0,0 +1,26 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class MysqlAdapter
+ class QuotingTest < ActiveRecord::TestCase
+ def setup
+ @conn = ActiveRecord::Base.connection
+ end
+
+ def test_type_cast_true
+ c = Column.new(nil, 1, 'boolean')
+ assert_equal 1, @conn.type_cast(true, nil)
+ assert_equal 1, @conn.type_cast(true, c)
+ end
+
+ def test_type_cast_false
+ c = Column.new(nil, 1, 'boolean')
+ assert_equal 0, @conn.type_cast(false, nil)
+ assert_equal 0, @conn.type_cast(false, c)
+ end
+ end
+ end
+ end
+end
+
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index 2d412a6e2a..7c49236854 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -102,11 +102,11 @@ module ActiveRecord
assert_equal [['1', 'foo']], result.rows
end
- def test_substitute_for
- bind = @connection.substitute_for(nil, [])
+ def test_substitute_at
+ bind = @connection.substitute_at(nil, 0)
assert_equal Arel.sql('$1'), bind
- bind = @connection.substitute_for(nil, [nil])
+ bind = @connection.substitute_at(nil, 1)
assert_equal Arel.sql('$2'), bind
end
diff --git a/activerecord/test/cases/adapters/postgresql/quoting_test.rb b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
new file mode 100644
index 0000000000..172055f15c
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
@@ -0,0 +1,25 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class PostgreSQLAdapter
+ class QuotingTest < ActiveRecord::TestCase
+ def setup
+ @conn = ActiveRecord::Base.connection
+ end
+
+ def test_type_cast_true
+ c = Column.new(nil, 1, 'boolean')
+ assert_equal 't', @conn.type_cast(true, nil)
+ assert_equal 't', @conn.type_cast(true, c)
+ end
+
+ def test_type_cast_false
+ c = Column.new(nil, 1, 'boolean')
+ assert_equal 'f', @conn.type_cast(false, nil)
+ assert_equal 'f', @conn.type_cast(false, c)
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
new file mode 100644
index 0000000000..0d9db92447
--- /dev/null
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -0,0 +1,93 @@
+require "cases/helper"
+require 'bigdecimal'
+require 'yaml'
+
+module ActiveRecord
+ module ConnectionAdapters
+ class SQLiteAdapter
+ class QuotingTest < ActiveRecord::TestCase
+ def setup
+ @conn = Base.sqlite3_connection :database => ':memory:',
+ :adapter => 'sqlite3',
+ :timeout => 100
+ end
+
+ def test_type_cast_symbol
+ assert_equal 'foo', @conn.type_cast(:foo, nil)
+ end
+
+ def test_type_cast_date
+ date = Date.today
+ expected = @conn.quoted_date(date)
+ assert_equal expected, @conn.type_cast(date, nil)
+ end
+
+ def test_type_cast_time
+ time = Time.now
+ expected = @conn.quoted_date(time)
+ assert_equal expected, @conn.type_cast(time, nil)
+ end
+
+ def test_type_cast_numeric
+ assert_equal 10, @conn.type_cast(10, nil)
+ assert_equal 2.2, @conn.type_cast(2.2, nil)
+ end
+
+ def test_type_cast_nil
+ assert_equal nil, @conn.type_cast(nil, nil)
+ end
+
+ def test_type_cast_true
+ c = Column.new(nil, 1, 'int')
+ assert_equal 't', @conn.type_cast(true, nil)
+ assert_equal 1, @conn.type_cast(true, c)
+ end
+
+ def test_type_cast_false
+ c = Column.new(nil, 1, 'int')
+ assert_equal 'f', @conn.type_cast(false, nil)
+ assert_equal 0, @conn.type_cast(false, c)
+ end
+
+ def test_type_cast_string
+ assert_equal '10', @conn.type_cast('10', nil)
+
+ c = Column.new(nil, 1, 'int')
+ assert_equal 10, @conn.type_cast('10', c)
+
+ c = Column.new(nil, 1, 'float')
+ assert_equal 10.1, @conn.type_cast('10.1', c)
+
+ c = Column.new(nil, 1, 'binary')
+ assert_equal '10.1', @conn.type_cast('10.1', c)
+
+ c = Column.new(nil, 1, 'date')
+ assert_equal '10.1', @conn.type_cast('10.1', c)
+ end
+
+ def test_type_cast_bigdecimal
+ bd = BigDecimal.new '10.0'
+ assert_equal bd.to_s('F'), @conn.type_cast(bd, nil)
+ end
+
+ def test_type_cast_unknown
+ obj = Class.new.new
+ assert_equal YAML.dump(obj), @conn.type_cast(obj, nil)
+ end
+
+ def test_quoted_id
+ quoted_id_obj = Class.new {
+ def quoted_id
+ "'zomg'"
+ end
+
+ def id
+ 10
+ end
+ }.new
+ assert_equal 10, @conn.type_cast(quoted_id_obj, nil)
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 0e2f468908..6ff04e3eb3 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -80,7 +80,7 @@ module ActiveRecord
end
def test_bind_value_substitute
- bind_param = @conn.substitute_for('foo', [])
+ bind_param = @conn.substitute_at('foo', 0)
assert_equal Arel.sql('?'), bind_param
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 16d4877fe8..007f11b535 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -70,16 +70,16 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
# would be convenient), because this would cause that scope to be applied to any callbacks etc.
def test_build_and_create_should_not_happen_within_scope
car = cars(:honda)
- original_scoped_methods = Bulb.scoped_methods
+ scoped_count = car.foo_bulbs.scoped.where_values.count
- bulb = car.bulbs.build
- assert_equal original_scoped_methods, bulb.scoped_methods_after_initialize
+ bulb = car.foo_bulbs.build
+ assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count
- bulb = car.bulbs.create
- assert_equal original_scoped_methods, bulb.scoped_methods_after_initialize
+ bulb = car.foo_bulbs.create
+ assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count
- bulb = car.bulbs.create!
- assert_equal original_scoped_methods, bulb.scoped_methods_after_initialize
+ bulb = car.foo_bulbs.create!
+ assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count
end
def test_no_sql_should_be_fired_if_association_already_loaded
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 1efe3420a0..70a4e06dbe 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -44,7 +44,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_associate_existing
- assert_queries(2) { posts(:thinking); people(:david) }
+ posts(:thinking); people(:david) # Warm cache
assert_queries(1) do
posts(:thinking).people << people(:david)
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index c1dad5e246..f3c96ccbe6 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -165,16 +165,16 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
def test_build_and_create_should_not_happen_within_scope
pirate = pirates(:blackbeard)
- original_scoped_methods = Bulb.scoped_methods.dup
+ scoped_count = pirate.association(:foo_bulb).scoped.where_values.count
- bulb = pirate.build_bulb
- assert_equal original_scoped_methods, bulb.scoped_methods_after_initialize
+ bulb = pirate.build_foo_bulb
+ assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count
- bulb = pirate.create_bulb
- assert_equal original_scoped_methods, bulb.scoped_methods_after_initialize
+ bulb = pirate.create_foo_bulb
+ assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count
- bulb = pirate.create_bulb!
- assert_equal original_scoped_methods, bulb.scoped_methods_after_initialize
+ bulb = pirate.create_foo_bulb!
+ assert_not_equal scoped_count, bulb.scope_after_initialize.where_values.count
end
def test_create_association
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index afc830cae9..49a1c117bc 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -516,10 +516,10 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
def test_has_many_through_collection_size_uses_counter_cache_if_it_exists
- author = authors(:david)
- author.stubs(:_read_attribute).with('comments_count').returns(100)
- assert_equal 100, author.comments.size
- assert !author.comments.loaded?
+ c = categories(:general)
+ c.categorizations_count = 100
+ assert_equal 100, c.categorizations.size
+ assert !c.categorizations.loaded?
end
def test_adding_junk_to_has_many_through_should_raise_type_mismatch
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index a3c5c14758..5074ae50ab 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -10,6 +10,7 @@ require 'models/company'
require 'models/category'
require 'models/reply'
require 'models/contact'
+require 'models/keyboard'
class AttributeMethodsTest < ActiveRecord::TestCase
fixtures :topics, :developers, :companies, :computers
@@ -77,6 +78,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
def test_respond_to?
topic = Topic.find(1)
assert_respond_to topic, "title"
+ assert_respond_to topic, "_title"
assert_respond_to topic, "title?"
assert_respond_to topic, "title="
assert_respond_to topic, :title
@@ -88,6 +90,16 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert !topic.respond_to?(:nothingness)
end
+ def test_respond_to_with_custom_primary_key
+ keyboard = Keyboard.create
+ assert_not_nil keyboard.key_number
+ assert_equal keyboard.key_number, keyboard.id
+ assert keyboard.respond_to?('key_number')
+ assert keyboard.respond_to?('_key_number')
+ assert keyboard.respond_to?('id')
+ assert keyboard.respond_to?('_id')
+ end
+
# Syck calls respond_to? before actually calling initialize
def test_respond_to_with_allocated_object
topic = Topic.allocate
@@ -639,7 +651,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
def time_related_columns_on_topic
- Topic.columns.select { |c| c.type.among?(:time, :date, :datetime, :timestamp) }
+ Topic.columns.select { |c| c.type.in?([:time, :date, :datetime, :timestamp]) }
end
def serialized_columns_on_topic
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index b3facf50b8..815ff7b825 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -895,7 +895,7 @@ class BasicsTest < ActiveRecord::TestCase
assert g.save
# Reload and check that we have all the geometric attributes.
- h = Geometric.find(g.id)
+ h = ActiveRecord::IdentityMap.without { Geometric.find(g.id) }
assert_equal '(5,6.1)', h.a_point
assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
@@ -923,7 +923,7 @@ class BasicsTest < ActiveRecord::TestCase
assert g.save
# Reload and check that we have all the geometric attributes.
- h = Geometric.find(g.id)
+ h = ActiveRecord::IdentityMap.without { Geometric.find(g.id) }
assert_equal '(5,6.1)', h.a_point
assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
@@ -1626,18 +1626,14 @@ class BasicsTest < ActiveRecord::TestCase
assert_not_equal c1, c2
end
- def test_default_scope_is_reset
+ def test_current_scope_is_reset
Object.const_set :UnloadablePost, Class.new(ActiveRecord::Base)
- UnloadablePost.table_name = 'posts'
- UnloadablePost.class_eval do
- default_scope order('posts.comments_count ASC')
- end
- UnloadablePost.scoped_methods # make Thread.current[:UnloadablePost_scoped_methods] not nil
+ UnloadablePost.send(:current_scope=, UnloadablePost.scoped)
UnloadablePost.unloadable
- assert_not_nil Thread.current[:UnloadablePost_scoped_methods]
+ assert_not_nil Thread.current[:UnloadablePost_current_scope]
ActiveSupport::Dependencies.remove_unloadable_constants!
- assert_nil Thread.current[:UnloadablePost_scoped_methods]
+ assert_nil Thread.current[:UnloadablePost_current_scope]
ensure
Object.class_eval{ remove_const :UnloadablePost } if defined?(UnloadablePost)
end
diff --git a/activerecord/test/cases/binary_test.rb b/activerecord/test/cases/binary_test.rb
index 8545ba97cc..06c14cb108 100644
--- a/activerecord/test/cases/binary_test.rb
+++ b/activerecord/test/cases/binary_test.rb
@@ -1,3 +1,4 @@
+# encoding: utf-8
require "cases/helper"
# Without using prepared statements, it makes no sense to test
@@ -9,6 +10,24 @@ unless current_adapter?(:SybaseAdapter, :DB2Adapter, :FirebirdAdapter)
class BinaryTest < ActiveRecord::TestCase
FIXTURES = %w(flowers.jpg example.log)
+ def test_mixed_encoding
+ str = "\x80"
+ str.force_encoding('ASCII-8BIT') if str.respond_to?(:force_encoding)
+
+ binary = Binary.new :name => 'いただきます!', :data => str
+ binary.save!
+ binary.reload
+ assert_equal str, binary.data
+
+ name = binary.name
+
+ # Mysql adapter doesn't properly encode things, so we have to do it
+ if current_adapter?(:MysqlAdapter)
+ name.force_encoding('UTF-8') if name.respond_to?(:force_encoding)
+ end
+ assert_equal 'いただきます!', name
+ end
+
def test_load_save
Binary.delete_all
diff --git a/activerecord/test/cases/bind_parameter_test.rb b/activerecord/test/cases/bind_parameter_test.rb
index 19383bb06b..3652255c38 100644
--- a/activerecord/test/cases/bind_parameter_test.rb
+++ b/activerecord/test/cases/bind_parameter_test.rb
@@ -33,7 +33,7 @@ module ActiveRecord
# FIXME: use skip with minitest
return unless @connection.supports_statement_cache?
- sub = @connection.substitute_for(@pk, [])
+ sub = @connection.substitute_at(@pk, 0)
binds = [[@pk, 1]]
sql = "select * from topics where id = #{sub}"
diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb
index 42b14bd25c..b3a281d960 100644
--- a/activerecord/test/cases/defaults_test.rb
+++ b/activerecord/test/cases/defaults_test.rb
@@ -95,7 +95,7 @@ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
assert_equal 0, klass.columns_hash['zero'].default
assert !klass.columns_hash['zero'].null
# 0 in MySQL 4, nil in 5.
- assert klass.columns_hash['omit'].default.among?(0, nil)
+ assert klass.columns_hash['omit'].default.in?([0, nil])
assert !klass.columns_hash['omit'].null
assert_raise(ActiveRecord::StatementInvalid) { klass.create! }
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 3c242667eb..655437318f 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -247,9 +247,10 @@ class FinderTest < ActiveRecord::TestCase
def test_find_only_some_columns
topic = Topic.find(1, :select => "author_name")
assert_raise(ActiveModel::MissingAttributeError) {topic.title}
+ assert_nil topic.read_attribute("title")
assert_equal "David", topic.author_name
assert !topic.attribute_present?("title")
- #assert !topic.respond_to?("title")
+ assert !topic.attribute_present?(:title)
assert topic.attribute_present?("author_name")
assert_respond_to topic, "author_name"
end
diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb
index 7e8383da9e..7f0f007a70 100644
--- a/activerecord/test/cases/method_scoping_test.rb
+++ b/activerecord/test/cases/method_scoping_test.rb
@@ -249,22 +249,21 @@ class MethodScopingTest < ActiveRecord::TestCase
end
def test_scoped_with_duck_typing
- scoping = Struct.new(:method_scoping).new(:find => { :conditions => ["name = ?", 'David'] })
+ scoping = Struct.new(:current_scope).new(:find => { :conditions => ["name = ?", 'David'] })
Developer.send(:with_scope, scoping) do
assert_equal %w(David), Developer.find(:all).map { |d| d.name }
end
end
def test_ensure_that_method_scoping_is_correctly_restored
- scoped_methods = Developer.instance_eval('current_scoped_methods')
-
begin
Developer.send(:with_scope, :find => { :conditions => "name = 'Jamis'" }) do
raise "an exception"
end
rescue
end
- assert_equal scoped_methods, Developer.instance_eval('current_scoped_methods')
+
+ assert !Developer.scoped.where_values.include?("name = 'Jamis'")
end
end
@@ -509,14 +508,15 @@ class NestedScopingTest < ActiveRecord::TestCase
def test_ensure_that_method_scoping_is_correctly_restored
Developer.send(:with_scope, :find => { :conditions => "name = 'David'" }) do
- scoped_methods = Developer.instance_eval('current_scoped_methods')
begin
Developer.send(:with_scope, :find => { :conditions => "name = 'Maiha'" }) do
raise "an exception"
end
rescue
end
- assert_equal scoped_methods, Developer.instance_eval('current_scoped_methods')
+
+ assert Developer.scoped.where_values.include?("name = 'David'")
+ assert !Developer.scoped.where_values.include?("name = 'Maiha'")
end
end
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index 9b20ea08de..8fd1fc2577 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -58,17 +58,6 @@ class NamedScopeTest < ActiveRecord::TestCase
assert Topic.approved.respond_to?(:tables_in_string, true)
end
- def test_subclasses_inherit_scopes
- assert Topic.scopes.include?(:base)
-
- assert Reply.scopes.include?(:base)
- assert_equal Reply.find(:all), Reply.base
- end
-
- def test_classes_dont_inherit_subclasses_scopes
- assert !ActiveRecord::Base.scopes.include?(:base)
- end
-
def test_scopes_with_options_limit_finds_to_those_matching_the_criteria_specified
assert !Topic.find(:all, :conditions => {:approved => true}).empty?
@@ -440,26 +429,31 @@ class NamedScopeTest < ActiveRecord::TestCase
end
end
+ # Note: these next two are kinda odd because they are essentially just testing that the
+ # query cache works as it should, but they are here for legacy reasons as they was previously
+ # a separate cache on association proxies, and these show that that is not necessary.
def test_scopes_are_cached_on_associations
post = posts(:welcome)
- assert_equal post.comments.containing_the_letter_e.object_id, post.comments.containing_the_letter_e.object_id
-
- post.comments.containing_the_letter_e.all # force load
- assert_no_queries { post.comments.containing_the_letter_e.all }
+ Post.cache do
+ assert_queries(1) { post.comments.containing_the_letter_e.all }
+ assert_no_queries { post.comments.containing_the_letter_e.all }
+ end
end
def test_scopes_with_arguments_are_cached_on_associations
post = posts(:welcome)
- one = post.comments.limit_by(1).all
- assert_equal 1, one.size
+ Post.cache do
+ one = assert_queries(1) { post.comments.limit_by(1).all }
+ assert_equal 1, one.size
- two = post.comments.limit_by(2).all
- assert_equal 2, two.size
+ two = assert_queries(1) { post.comments.limit_by(2).all }
+ assert_equal 2, two.size
- assert_no_queries { post.comments.limit_by(1).all }
- assert_no_queries { post.comments.limit_by(2).all }
+ assert_no_queries { post.comments.limit_by(1).all }
+ assert_no_queries { post.comments.limit_by(2).all }
+ end
end
def test_scopes_are_reset_on_association_reload
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index b87fb51d97..80ee74e41e 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -154,15 +154,16 @@ module ActiveRecord
end
def test_crazy_object
- crazy = Class.new { def to_yaml; 'lol' end }.new
- assert_equal "'lol'", @quoter.quote(crazy, nil)
- assert_equal "'lol'", @quoter.quote(crazy, Object.new)
+ crazy = Class.new.new
+ expected = "'#{YAML.dump(crazy)}'"
+ assert_equal expected, @quoter.quote(crazy, nil)
+ assert_equal expected, @quoter.quote(crazy, Object.new)
end
def test_crazy_object_calls_quote_string
- crazy = Class.new { def to_yaml; 'lo\l' end }.new
- assert_equal "'lo\\\\l'", @quoter.quote(crazy, nil)
- assert_equal "'lo\\\\l'", @quoter.quote(crazy, Object.new)
+ crazy = Class.new { def initialize; @lol = 'lo\l' end }.new
+ assert_match "lo\\\\l", @quoter.quote(crazy, nil)
+ assert_match "lo\\\\l", @quoter.quote(crazy, Object.new)
end
def test_quote_string_no_column
diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb
index 30a783d5a2..2ed676fe69 100644
--- a/activerecord/test/cases/relation_scoping_test.rb
+++ b/activerecord/test/cases/relation_scoping_test.rb
@@ -132,8 +132,6 @@ class RelationScopingTest < ActiveRecord::TestCase
end
def test_ensure_that_method_scoping_is_correctly_restored
- scoped_methods = Developer.send(:current_scoped_methods)
-
begin
Developer.where("name = 'Jamis'").scoping do
raise "an exception"
@@ -141,7 +139,7 @@ class RelationScopingTest < ActiveRecord::TestCase
rescue
end
- assert_equal scoped_methods, Developer.send(:current_scoped_methods)
+ assert !Developer.scoped.where_values.include?("name = 'Jamis'")
end
end
@@ -310,33 +308,20 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal expected, received
end
- def test_default_scope_with_lambda
- expected = Post.find_all_by_author_id(2)
- PostForAuthor.selected_author = 2
- received = PostForAuthor.all
- assert_equal expected, received
- expected = Post.find_all_by_author_id(1)
- PostForAuthor.selected_author = 1
- received = PostForAuthor.all
- assert_equal expected, received
+ def test_default_scope_as_class_method
+ assert_equal [developers(:david).becomes(ClassMethodDeveloperCalledDavid)], ClassMethodDeveloperCalledDavid.all
end
- def test_default_scope_with_thing_that_responds_to_call
- klass = Class.new(ActiveRecord::Base) do
- self.table_name = 'posts'
- end
+ def test_default_scope_with_lambda
+ assert_equal [developers(:david).becomes(LazyLambdaDeveloperCalledDavid)], LazyLambdaDeveloperCalledDavid.all
+ end
- klass.class_eval do
- default_scope Class.new(Struct.new(:klass)) {
- def call
- klass.where(:author_id => 2)
- end
- }.new(self)
- end
+ def test_default_scope_with_block
+ assert_equal [developers(:david).becomes(LazyBlockDeveloperCalledDavid)], LazyBlockDeveloperCalledDavid.all
+ end
- records = klass.all
- assert_equal 3, records.length
- assert_equal 2, records.first.author_id
+ def test_default_scope_with_callable
+ assert_equal [developers(:david).becomes(CallableDeveloperCalledDavid)], CallableDeveloperCalledDavid.all
end
def test_default_scope_is_unscoped_on_find
@@ -360,53 +345,20 @@ class DefaultScopingTest < ActiveRecord::TestCase
def test_default_scoping_with_threads
2.times do
- Thread.new { assert_equal ['salary DESC'], DeveloperOrderedBySalary.scoped.order_values }.join
+ Thread.new { assert DeveloperOrderedBySalary.scoped.to_sql.include?('salary DESC') }.join
end
end
- def test_default_scoping_with_inheritance
- # Inherit a class having a default scope and define a new default scope
- klass = Class.new(DeveloperOrderedBySalary)
- klass.send :default_scope, :limit => 1
-
- # Scopes added on children should append to parent scope
- assert_equal 1, klass.scoped.limit_value
- assert_equal ['salary DESC'], klass.scoped.order_values
-
- # Parent should still have the original scope
- assert_nil DeveloperOrderedBySalary.scoped.limit_value
- assert_equal ['salary DESC'], DeveloperOrderedBySalary.scoped.order_values
- end
-
- def test_default_scope_called_twice_merges_conditions
- Developer.destroy_all
- Developer.create!(:name => "David", :salary => 80000)
- Developer.create!(:name => "David", :salary => 100000)
- Developer.create!(:name => "Brian", :salary => 100000)
-
- klass = Class.new(Developer)
- klass.__send__ :default_scope, :conditions => { :name => "David" }
- klass.__send__ :default_scope, :conditions => { :salary => 100000 }
- assert_equal 1, klass.count
- assert_equal "David", klass.first.name
- assert_equal 100000, klass.first.salary
+ def test_default_scope_with_inheritance
+ wheres = InheritedPoorDeveloperCalledJamis.scoped.where_values_hash
+ assert_equal "Jamis", wheres[:name]
+ assert_equal 50000, wheres[:salary]
end
- def test_default_scope_called_twice_in_different_place_merges_where_clause
- Developer.destroy_all
- Developer.create!(:name => "David", :salary => 80000)
- Developer.create!(:name => "David", :salary => 100000)
- Developer.create!(:name => "Brian", :salary => 100000)
-
- klass = Class.new(Developer)
- klass.class_eval do
- default_scope where("name = 'David'")
- default_scope where("salary = 100000")
- end
-
- assert_equal 1, klass.count
- assert_equal "David", klass.first.name
- assert_equal 100000, klass.first.salary
+ def test_default_scope_with_multiple_calls
+ wheres = MultiplePoorDeveloperCalledJamis.scoped.where_values_hash
+ assert_equal "Jamis", wheres[:name]
+ assert_equal 50000, wheres[:salary]
end
def test_method_scope
@@ -449,12 +401,6 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal expected, received
end
- def test_default_scope_using_relation
- posts = PostWithComment.scoped
- assert_equal 2, posts.count
- assert_equal posts(:thinking), posts.first
- end
-
def test_create_attribute_overwrites_default_scoping
assert_equal 'David', PoorDeveloperCalledJamis.create!(:name => 'David').name
assert_equal 200000, PoorDeveloperCalledJamis.create!(:name => 'David', :salary => 200000).salary
@@ -503,4 +449,25 @@ class DefaultScopingTest < ActiveRecord::TestCase
jamis = PoorDeveloperCalledJamis.create_with(:name => 'Aaron').create_with(nil).new
assert_equal 'Jamis', jamis.name
end
+
+ def test_unscoped_with_named_scope_should_not_have_default_scope
+ assert_equal [DeveloperCalledJamis.find(developers(:poor_jamis).id)], DeveloperCalledJamis.poor
+
+ assert DeveloperCalledJamis.unscoped.poor.include?(developers(:david).becomes(DeveloperCalledJamis))
+ assert_equal 10, DeveloperCalledJamis.unscoped.poor.length
+ end
+
+ def test_multiple_default_scope_calls_are_deprecated
+ klass = Class.new(ActiveRecord::Base)
+
+ assert_not_deprecated do
+ klass.send(:default_scope, :foo => :bar)
+ end
+
+ assert_deprecated do
+ klass.send(:default_scope, :foo => :bar)
+ end
+
+ assert_equal 2, klass.default_scopes.length
+ end
end
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 7bdbd773b6..6874bd18f8 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -20,7 +20,7 @@ module ActiveRecord
end
def test_single_values
- assert_equal [:limit, :offset, :lock, :readonly, :create_with, :from].map(&:to_s).sort,
+ assert_equal [:limit, :offset, :lock, :readonly, :create_with, :from, :reorder].map(&:to_s).sort,
Relation::SINGLE_VALUE_METHODS.map(&:to_s).sort
end
diff --git a/activerecord/test/models/bulb.rb b/activerecord/test/models/bulb.rb
index 7178bb0d00..c68d008c26 100644
--- a/activerecord/test/models/bulb.rb
+++ b/activerecord/test/models/bulb.rb
@@ -1,14 +1,12 @@
class Bulb < ActiveRecord::Base
-
- default_scope :conditions => {:name => 'defaulty' }
-
+ default_scope where(:name => 'defaulty')
belongs_to :car
- attr_reader :scoped_methods_after_initialize
+ attr_reader :scope_after_initialize
- after_initialize :record_scoped_methods_after_initialize
- def record_scoped_methods_after_initialize
- @scoped_methods_after_initialize = self.class.scoped_methods.dup
+ after_initialize :record_scope_after_initialize
+ def record_scope_after_initialize
+ @scope_after_initialize = self.class.scoped
end
end
diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb
index e7db3d3423..b036f0f5c9 100644
--- a/activerecord/test/models/car.rb
+++ b/activerecord/test/models/car.rb
@@ -1,6 +1,7 @@
class Car < ActiveRecord::Base
has_many :bulbs
+ has_many :foo_bulbs, :class_name => "Bulb", :conditions => { :name => 'foo' }
has_many :tyres
has_many :engines
has_many :wheels, :as => :wheelable
@@ -18,5 +19,5 @@ class CoolCar < Car
end
class FastCar < Car
- default_scope order('name desc')
+ default_scope :order => 'name desc'
end
diff --git a/activerecord/test/models/categorization.rb b/activerecord/test/models/categorization.rb
index 09489b8ea4..4bd980e606 100644
--- a/activerecord/test/models/categorization.rb
+++ b/activerecord/test/models/categorization.rb
@@ -12,7 +12,6 @@ end
class SpecialCategorization < ActiveRecord::Base
self.table_name = 'categorizations'
-
default_scope where(:special => true)
belongs_to :author
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index 32d060cf09..10701dd6fd 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -1,3 +1,5 @@
+require 'ostruct'
+
module DeveloperProjectsAssociationExtension
def find_most_recent
find(:first, :order => "id DESC")
@@ -87,6 +89,7 @@ end
class DeveloperOrderedBySalary < ActiveRecord::Base
self.table_name = 'developers'
default_scope :order => 'salary DESC'
+
scope :by_name, order('name DESC')
def self.all_ordered_by_name
@@ -98,15 +101,56 @@ end
class DeveloperCalledDavid < ActiveRecord::Base
self.table_name = 'developers'
- default_scope :conditions => "name = 'David'"
+ default_scope where("name = 'David'")
+end
+
+class LazyLambdaDeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope lambda { where(:name => 'David') }
+end
+
+class LazyBlockDeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope { where(:name => 'David') }
+end
+
+class CallableDeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope OpenStruct.new(:call => where(:name => 'David'))
+end
+
+class ClassMethodDeveloperCalledDavid < ActiveRecord::Base
+ self.table_name = 'developers'
+
+ def self.default_scope
+ where(:name => 'David')
+ end
end
class DeveloperCalledJamis < ActiveRecord::Base
self.table_name = 'developers'
- default_scope :conditions => { :name => 'Jamis' }
+ default_scope where(:name => 'Jamis')
+ scope :poor, where('salary < 150000')
end
class PoorDeveloperCalledJamis < ActiveRecord::Base
self.table_name = 'developers'
- default_scope :conditions => { :name => 'Jamis', :salary => 50000 }
+ default_scope where(:name => 'Jamis', :salary => 50000)
+end
+
+class InheritedPoorDeveloperCalledJamis < DeveloperCalledJamis
+ self.table_name = 'developers'
+
+ ActiveSupport::Deprecation.silence do
+ default_scope where(:salary => 50000)
+ end
+end
+
+class MultiplePoorDeveloperCalledJamis < ActiveRecord::Base
+ self.table_name = 'developers'
+ default_scope where(:name => 'Jamis')
+
+ ActiveSupport::Deprecation.silence do
+ default_scope where(:salary => 50000)
+ end
end
diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb
index 0d3f62bb33..5e0f5323e6 100644
--- a/activerecord/test/models/pirate.rb
+++ b/activerecord/test/models/pirate.rb
@@ -34,7 +34,7 @@ class Pirate < ActiveRecord::Base
:after_remove => proc {|p,b| p.ship_log << "after_removing_proc_bird_#{b.id}"}
has_many :birds_with_reject_all_blank, :class_name => "Bird"
- has_one :bulb, :foreign_key => :car_id
+ has_one :foo_bulb, :foreign_key => :car_id, :class_name => "Bulb", :conditions => { :name => 'foo' }
accepts_nested_attributes_for :parrots, :birds, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 82894a3d57..80296032bb 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -7,6 +7,7 @@ class Post < ActiveRecord::Base
scope :containing_the_letter_a, where("body LIKE '%a%'")
scope :ranked_by_comments, order("comments_count DESC")
+
scope :limit_by, lambda {|l| limit(l) }
scope :with_authors_at_address, lambda { |address| {
:conditions => [ 'authors.author_address_id = ?', address.id ],
@@ -142,20 +143,22 @@ class SubStiPost < StiPost
self.table_name = Post.table_name
end
-class PostWithComment < ActiveRecord::Base
- self.table_name = 'posts'
- default_scope where("posts.comments_count > 0").order("posts.comments_count ASC")
+ActiveSupport::Deprecation.silence do
+ class DeprecatedPostWithComment < ActiveRecord::Base
+ self.table_name = 'posts'
+ default_scope where("posts.comments_count > 0").order("posts.comments_count ASC")
+ end
end
class PostForAuthor < ActiveRecord::Base
self.table_name = 'posts'
cattr_accessor :selected_author
- default_scope lambda { where(:author_id => PostForAuthor.selected_author) }
end
class FirstPost < ActiveRecord::Base
self.table_name = 'posts'
default_scope where(:id => 1)
+
has_many :comments, :foreign_key => :post_id
has_one :comment, :foreign_key => :post_id
end
diff --git a/activerecord/test/models/reference.rb b/activerecord/test/models/reference.rb
index e33a0f2acc..c5af0b5d5f 100644
--- a/activerecord/test/models/reference.rb
+++ b/activerecord/test/models/reference.rb
@@ -18,6 +18,6 @@ class Reference < ActiveRecord::Base
end
class BadReference < ActiveRecord::Base
- self.table_name ='references'
- default_scope :conditions => {:favourite => false }
+ self.table_name = 'references'
+ default_scope where(:favourite => false)
end
diff --git a/activerecord/test/models/without_table.rb b/activerecord/test/models/without_table.rb
index 87f80911e1..184ab1649e 100644
--- a/activerecord/test/models/without_table.rb
+++ b/activerecord/test/models/without_table.rb
@@ -1,3 +1,3 @@
class WithoutTable < ActiveRecord::Base
default_scope where(:published => true)
-end \ No newline at end of file
+end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 362475de36..ceadb05644 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -67,6 +67,7 @@ ActiveRecord::Schema.define do
end
create_table :binaries, :force => true do |t|
+ t.string :name
t.binary :data
end
diff --git a/activeresource/lib/active_resource/connection.rb b/activeresource/lib/active_resource/connection.rb
index e286362f87..765575d866 100644
--- a/activeresource/lib/active_resource/connection.rb
+++ b/activeresource/lib/active_resource/connection.rb
@@ -278,7 +278,7 @@ module ActiveResource
def legitimize_auth_type(auth_type)
return :basic if auth_type.nil?
auth_type = auth_type.to_sym
- auth_type.among?(:basic, :digest) ? auth_type : :basic
+ auth_type.in?([:basic, :digest]) ? auth_type : :basic
end
end
end
diff --git a/activeresource/test/cases/http_mock_test.rb b/activeresource/test/cases/http_mock_test.rb
index dfb097315e..cd5155924a 100644
--- a/activeresource/test/cases/http_mock_test.rb
+++ b/activeresource/test/cases/http_mock_test.rb
@@ -193,7 +193,7 @@ class HttpMockTest < ActiveSupport::TestCase
end
def request(method, path, headers = {}, body = nil)
- if method.among?(:put, :post)
+ if method.in?([:put, :post])
@http.send(method, path, body, headers)
else
@http.send(method, path, headers)
diff --git a/activesupport/CHANGELOG b/activesupport/CHANGELOG
index eb027795a8..69e9cbfd42 100644
--- a/activesupport/CHANGELOG
+++ b/activesupport/CHANGELOG
@@ -1,6 +1,8 @@
*Rails 3.1.0 (unreleased)*
-* Add Object#in? to test if an object is included in another object, and Object#among? to test if an object is included in a list of objects which will be passed as arguments. [Prem Sichanugrist, Brian Morearty, John Reitano]
+* Add String#inquiry as a convenience method for turning a string into a StringInquirer object [DHH]
+
+* Add Object#in? to test if an object is included in another object [Prem Sichanugrist, Brian Morearty, John Reitano]
* LocalCache strategy is now a real middleware class, not an anonymous class
posing for pictures.
diff --git a/activesupport/activesupport.gemspec b/activesupport/activesupport.gemspec
index eaecb30090..968d6ff4d0 100644
--- a/activesupport/activesupport.gemspec
+++ b/activesupport/activesupport.gemspec
@@ -16,4 +16,6 @@ Gem::Specification.new do |s|
s.files = Dir['CHANGELOG', 'README.rdoc', 'lib/**/*']
s.require_path = 'lib'
+
+ s.add_dependency('multi_json', '~> 1.0.0.rc3')
end
diff --git a/activesupport/lib/active_support/cache/file_store.rb b/activesupport/lib/active_support/cache/file_store.rb
index bdef47a237..a1376ae52a 100644
--- a/activesupport/lib/active_support/cache/file_store.rb
+++ b/activesupport/lib/active_support/cache/file_store.rb
@@ -21,7 +21,7 @@ module ActiveSupport
end
def clear(options = nil)
- root_dirs = Dir.entries(cache_path).reject{|f| f.among?('.', '..')}
+ root_dirs = Dir.entries(cache_path).reject{|f| f.in?(['.', '..'])}
FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)})
end
@@ -121,7 +121,7 @@ module ActiveSupport
# Lock a file for a block so only one process can modify it at a time.
def lock_file(file_name, &block) # :nodoc:
if File.exist?(file_name)
- File.open(file_name, 'r') do |f|
+ File.open(file_name, 'r+') do |f|
begin
f.flock File::LOCK_EX
yield
@@ -162,7 +162,7 @@ module ActiveSupport
# Delete empty directories in the cache.
def delete_empty_directories(dir)
return if dir == cache_path
- if Dir.entries(dir).reject{|f| f.among?('.', '..')}.empty?
+ if Dir.entries(dir).reject{|f| f.in?(['.', '..'])}.empty?
File.delete(dir) rescue nil
delete_empty_directories(File.dirname(dir))
end
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 6a0bffb6d7..656cba625c 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -413,7 +413,7 @@ module ActiveSupport
# CallbackChain.
#
def __update_callbacks(name, filters = [], block = nil) #:nodoc:
- type = filters.first.among?(:before, :after, :around) ? filters.shift : :before
+ type = filters.first.in?([:before, :after, :around]) ? filters.shift : :before
options = filters.last.is_a?(Hash) ? filters.pop : {}
filters.unshift(block) if block
diff --git a/activesupport/lib/active_support/core_ext/hash/conversions.rb b/activesupport/lib/active_support/core_ext/hash/conversions.rb
index 3005fef44c..61a1d88b0e 100644
--- a/activesupport/lib/active_support/core_ext/hash/conversions.rb
+++ b/activesupport/lib/active_support/core_ext/hash/conversions.rb
@@ -108,7 +108,7 @@ class Hash
raise "can't typecast #{entries.inspect}"
end
end
- elsif value.has_key?("__content__")
+ elsif value['type'] == 'file' || value["__content__"].present?
content = value["__content__"]
if parser = ActiveSupport::XmlMini::PARSING[value["type"]]
parser.arity == 1 ? parser.call(content) : parser.call(content, value)
diff --git a/activesupport/lib/active_support/core_ext/object/inclusion.rb b/activesupport/lib/active_support/core_ext/object/inclusion.rb
index cf89288aed..51cfc62f2b 100644
--- a/activesupport/lib/active_support/core_ext/object/inclusion.rb
+++ b/activesupport/lib/active_support/core_ext/object/inclusion.rb
@@ -5,16 +5,11 @@ class Object
# characters = ["Konata", "Kagami", "Tsukasa"]
# "Konata".in?(characters) # => true
#
+ # This will throw an ArgumentError if the supplied argument doesnt not respond
+ # to +#include?+.
def in?(another_object)
another_object.include?(self)
- end
-
- # Returns true if this object is included in the argument list. Usage:
- #
- # username = "sikachu"
- # username.among?("josevalim", "dhh", "wycats") # => false
- #
- def among?(*objects)
- objects.include?(self)
+ rescue NoMethodError
+ raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
end
end
diff --git a/activesupport/lib/active_support/core_ext/string.rb b/activesupport/lib/active_support/core_ext/string.rb
index 8fb8c31ade..72522d395c 100644
--- a/activesupport/lib/active_support/core_ext/string.rb
+++ b/activesupport/lib/active_support/core_ext/string.rb
@@ -11,3 +11,4 @@ require 'active_support/core_ext/string/output_safety'
require 'active_support/core_ext/string/exclude'
require 'active_support/core_ext/string/encoding'
require 'active_support/core_ext/string/strip'
+require 'active_support/core_ext/string/inquiry'
diff --git a/activesupport/lib/active_support/core_ext/string/inquiry.rb b/activesupport/lib/active_support/core_ext/string/inquiry.rb
new file mode 100644
index 0000000000..604f3bf4dc
--- /dev/null
+++ b/activesupport/lib/active_support/core_ext/string/inquiry.rb
@@ -0,0 +1,13 @@
+require 'active_support/string_inquirer'
+
+class String
+ # Wraps the current string in the ActiveSupport::StringInquirer class,
+ # which gives you a prettier way to test for equality. Example:
+ #
+ # env = "production".inquiry
+ # env.production? # => true
+ # env.development? # => false
+ def inquiry
+ ActiveSupport::StringInquirer.new(self)
+ end
+end
diff --git a/activesupport/lib/active_support/json/backends/jsongem.rb b/activesupport/lib/active_support/json/backends/jsongem.rb
deleted file mode 100644
index 533ba25da3..0000000000
--- a/activesupport/lib/active_support/json/backends/jsongem.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-require 'json' unless defined?(JSON)
-
-module ActiveSupport
- module JSON
- module Backends
- module JSONGem
- ParseError = ::JSON::ParserError
- extend self
-
- # Parses a JSON string or IO and convert it into an object
- def decode(json)
- if json.respond_to?(:read)
- json = json.read
- end
- data = ::JSON.parse(json)
- if ActiveSupport.parse_json_times
- convert_dates_from(data)
- else
- data
- end
- end
-
- private
- def convert_dates_from(data)
- case data
- when nil
- nil
- when DATE_REGEX
- begin
- DateTime.parse(data)
- rescue ArgumentError
- data
- end
- when Array
- data.map! { |d| convert_dates_from(d) }
- when Hash
- data.each do |key, value|
- data[key] = convert_dates_from(value)
- end
- else
- data
- end
- end
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/json/backends/yajl.rb b/activesupport/lib/active_support/json/backends/yajl.rb
deleted file mode 100644
index 58818658c7..0000000000
--- a/activesupport/lib/active_support/json/backends/yajl.rb
+++ /dev/null
@@ -1,44 +0,0 @@
-require 'yajl' unless defined?(Yajl)
-
-module ActiveSupport
- module JSON
- module Backends
- module Yajl
- ParseError = ::Yajl::ParseError
- extend self
-
- # Parses a JSON string or IO and convert it into an object
- def decode(json)
- data = ::Yajl::Parser.new.parse(json)
- if ActiveSupport.parse_json_times
- convert_dates_from(data)
- else
- data
- end
- end
-
- private
- def convert_dates_from(data)
- case data
- when nil
- nil
- when DATE_REGEX
- begin
- DateTime.parse(data)
- rescue ArgumentError
- data
- end
- when Array
- data.map! { |d| convert_dates_from(d) }
- when Hash
- data.each do |key, value|
- data[key] = convert_dates_from(value)
- end
- else
- data
- end
- end
- end
- end
- end
-end
diff --git a/activesupport/lib/active_support/json/backends/yaml.rb b/activesupport/lib/active_support/json/backends/yaml.rb
deleted file mode 100644
index e25e29d36b..0000000000
--- a/activesupport/lib/active_support/json/backends/yaml.rb
+++ /dev/null
@@ -1,113 +0,0 @@
-require 'active_support/core_ext/string/starts_ends_with'
-
-module ActiveSupport
- module JSON
- module Backends
- module Yaml
- ParseError = ::StandardError
- extend self
-
- EXCEPTIONS = [::ArgumentError] # :nodoc:
- begin
- require 'psych'
- EXCEPTIONS << Psych::SyntaxError
- rescue LoadError
- end
-
- # Parses a JSON string or IO and converts it into an object
- def decode(json)
- if json.respond_to?(:read)
- json = json.read
- end
- YAML.load(convert_json_to_yaml(json))
- rescue *EXCEPTIONS => e
- raise ParseError, "Invalid JSON string: '%s'" % json
- end
-
- protected
- # Ensure that ":" and "," are always followed by a space
- def convert_json_to_yaml(json) #:nodoc:
- require 'strscan' unless defined? ::StringScanner
- scanner, quoting, marks, pos, times = ::StringScanner.new(json), false, [], nil, []
- while scanner.scan_until(/(\\['"]|['":,\\]|\\.|[\]])/)
- case char = scanner[1]
- when '"', "'"
- if !quoting
- quoting = char
- pos = scanner.pos
- elsif quoting == char
- if valid_date?(json[pos..scanner.pos-2])
- # found a date, track the exact positions of the quotes so we can
- # overwrite them with spaces later.
- times << pos
- end
- quoting = false
- end
- when ":",",", "]"
- marks << scanner.pos - 1 unless quoting
- when "\\"
- scanner.skip(/\\/)
- end
- end
-
- if marks.empty?
- json.gsub(/\\([\\\/]|u[[:xdigit:]]{4})/) do
- ustr = $1
- if ustr.start_with?('u')
- char = [ustr[1..-1].to_i(16)].pack("U")
- # "\n" needs extra escaping due to yaml formatting
- char == "\n" ? "\\n" : char
- elsif ustr == '\\'
- '\\\\'
- else
- ustr
- end
- end
- else
- left_pos = [-1].push(*marks)
- right_pos = marks << scanner.pos + scanner.rest_size
- output = []
- left_pos.each_with_index do |left, i|
- scanner.pos = left.succ
- chunk = scanner.peek(right_pos[i] - scanner.pos + 1)
- if ActiveSupport.parse_json_times
- # overwrite the quotes found around the dates with spaces
- while times.size > 0 && times[0] <= right_pos[i]
- chunk.insert(times.shift - scanner.pos - 1, '! ')
- end
- end
- chunk.gsub!(/\\([\\\/]|u[[:xdigit:]]{4})/) do
- ustr = $1
- if ustr.start_with?('u')
- char = [ustr[1..-1].to_i(16)].pack("U")
- # "\n" needs extra escaping due to yaml formatting
- char == "\n" ? "\\n" : char
- elsif ustr == '\\'
- '\\\\'
- else
- ustr
- end
- end
- output << chunk
- end
- output = output * " "
-
- output.gsub!(/\\\//, '/')
- output
- end
- end
-
- private
- def valid_date?(date_string)
- begin
- date_string =~ DATE_REGEX && DateTime.parse(date_string)
- rescue ArgumentError
- false
- end
- end
-
- end
- end
- end
-end
-
diff --git a/activesupport/lib/active_support/json/decoding.rb b/activesupport/lib/active_support/json/decoding.rb
index c1f6330c6c..cbeb6c0a28 100644
--- a/activesupport/lib/active_support/json/decoding.rb
+++ b/activesupport/lib/active_support/json/decoding.rb
@@ -1,32 +1,31 @@
require 'active_support/core_ext/module/attribute_accessors'
require 'active_support/core_ext/module/delegation'
+require 'multi_json'
module ActiveSupport
# Look for and parse json strings that look like ISO 8601 times.
mattr_accessor :parse_json_times
module JSON
- # Listed in order of preference.
- DECODERS = %w(Yajl Yaml)
-
class << self
- attr_reader :parse_error
- delegate :decode, :to => :backend
+ def decode(json, options ={})
+ data = MultiJson.decode(json, options)
+ if ActiveSupport.parse_json_times
+ convert_dates_from(data)
+ else
+ data
+ end
+ end
- def backend
- set_default_backend unless defined?(@backend)
- @backend
+ def engine
+ MultiJson.engine
end
+ alias :backend :engine
- def backend=(name)
- if name.is_a?(Module)
- @backend = name
- else
- require "active_support/json/backends/#{name.to_s.downcase}"
- @backend = ActiveSupport::JSON::Backends::const_get(name)
- end
- @parse_error = @backend::ParseError
+ def engine=(name)
+ MultiJson.engine = name
end
+ alias :backend= :engine=
def with_backend(name)
old_backend, self.backend = backend, name
@@ -35,15 +34,30 @@ module ActiveSupport
self.backend = old_backend
end
- def set_default_backend
- DECODERS.find do |name|
+ def parse_error
+ MultiJson::DecodeError
+ end
+
+ private
+
+ def convert_dates_from(data)
+ case data
+ when nil
+ nil
+ when DATE_REGEX
begin
- self.backend = name
- true
- rescue LoadError
- # Try next decoder.
- false
+ DateTime.parse(data)
+ rescue ArgumentError
+ data
end
+ when Array
+ data.map! { |d| convert_dates_from(d) }
+ when Hash
+ data.each do |key, value|
+ data[key] = convert_dates_from(value)
+ end
+ else
+ data
end
end
end
diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb
index 6b9120e51f..3d092529d6 100644
--- a/activesupport/lib/active_support/time_with_zone.rb
+++ b/activesupport/lib/active_support/time_with_zone.rb
@@ -345,7 +345,7 @@ module ActiveSupport
end
def duration_of_variable_length?(obj)
- ActiveSupport::Duration === obj && obj.parts.any? {|p| p[0].among?(:years, :months, :days) }
+ ActiveSupport::Duration === obj && obj.parts.any? {|p| p[0].in?([:years, :months, :days]) }
end
end
end
diff --git a/activesupport/test/core_ext/hash_ext_test.rb b/activesupport/test/core_ext/hash_ext_test.rb
index a0479d45ac..012b956d7f 100644
--- a/activesupport/test/core_ext/hash_ext_test.rb
+++ b/activesupport/test/core_ext/hash_ext_test.rb
@@ -889,6 +889,15 @@ class HashToXmlTest < Test::Unit::TestCase
assert_equal 'application/octet-stream', file.content_type
end
+ def test_tag_with_attrs_and_whitespace
+ xml = <<-XML
+ <blog name="bacon is the best">
+ </blog>
+ XML
+ hash = Hash.from_xml(xml)
+ assert_equal "bacon is the best", hash['blog']['name']
+ end
+
def test_xsd_like_types_from_xml
bacon_xml = <<-EOT
<bacon>
diff --git a/activesupport/test/core_ext/object/inclusion_test.rb b/activesupport/test/core_ext/object/inclusion_test.rb
index 25120deb81..1de857d678 100644
--- a/activesupport/test/core_ext/object/inclusion_test.rb
+++ b/activesupport/test/core_ext/object/inclusion_test.rb
@@ -30,12 +30,6 @@ class InTest < Test::Unit::TestCase
assert !3.in?(s)
end
- def test_either
- assert 1.among?(1,2,3)
- assert !5.among?(1,2,3)
- assert [1,2,3].among?([1,2,3], 2, [3,4,5])
- end
-
module A
end
class B
@@ -49,4 +43,8 @@ class InTest < Test::Unit::TestCase
assert A.in?(C)
assert !A.in?(A)
end
+
+ def test_no_method_catching
+ assert_raise(ArgumentError) { 1.in?(1) }
+ end
end
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index bb865cae91..f0c289a418 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -250,6 +250,11 @@ class StringInflectionsTest < Test::Unit::TestCase
# And changes the original string:
assert_equal original, expected
end
+
+ def test_string_inquiry
+ assert "production".inquiry.production?
+ assert !"production".inquiry.development?
+ end
def test_truncate
assert_equal "Hello World!", "Hello World!".truncate(12)
diff --git a/activesupport/test/json/decoding_test.rb b/activesupport/test/json/decoding_test.rb
index 88cf97de7e..6ccffa59b1 100644
--- a/activesupport/test/json/decoding_test.rb
+++ b/activesupport/test/json/decoding_test.rb
@@ -56,12 +56,9 @@ class TestJSONDecoding < ActiveSupport::TestCase
%q({"a":"Line1\u000aLine2"}) => {"a"=>"Line1\nLine2"}
}
- # load the default JSON backend
- ActiveSupport::JSON.backend = 'Yaml'
-
- backends = %w(Yaml)
- backends << "JSONGem" if defined?(::JSON)
- backends << "Yajl" if defined?(::Yajl)
+ backends = [:ok_json]
+ backends << :json_gem if defined?(::JSON)
+ backends << :yajl if defined?(::Yajl)
backends.each do |backend|
TESTS.each do |json, expected|
diff --git a/activesupport/test/ordered_options_test.rb b/activesupport/test/ordered_options_test.rb
index e48425ca25..b215b60df3 100644
--- a/activesupport/test/ordered_options_test.rb
+++ b/activesupport/test/ordered_options_test.rb
@@ -50,4 +50,30 @@ class OrderedOptionsTest < Test::Unit::TestCase
assert_equal 2, a.size
assert_equal 56, a.else_where
end
+
+ def test_inheritable_options_continues_lookup_in_parent
+ parent = ActiveSupport::OrderedOptions.new
+ parent[:foo] = true
+
+ child = ActiveSupport::InheritableOptions.new(parent)
+ assert child.foo
+ end
+
+ def test_inheritable_options_can_override_parent
+ parent = ActiveSupport::OrderedOptions.new
+ parent[:foo] = :bar
+
+ child = ActiveSupport::InheritableOptions.new(parent)
+ child[:foo] = :baz
+
+ assert_equal :baz, child.foo
+ end
+
+ def test_inheritable_options_inheritable_copy
+ original = ActiveSupport::InheritableOptions.new
+ copy = original.inheritable_copy
+
+ assert copy.kind_of?(original.class)
+ assert_not_equal copy.object_id, original.object_id
+ end
end
diff --git a/activesupport/test/transliterate_test.rb b/activesupport/test/transliterate_test.rb
index 90b40b5478..08e11d4f38 100644
--- a/activesupport/test/transliterate_test.rb
+++ b/activesupport/test/transliterate_test.rb
@@ -16,7 +16,7 @@ class TransliterateTest < Test::Unit::TestCase
# create string with range of Unicode"s western characters with
# diacritics, excluding the division and multiplication signs which for
# some reason or other are floating in the middle of all the letters.
- string = (0xC0..0x17E).to_a.reject {|c| c.among?(0xD7, 0xF7)}.pack("U*")
+ string = (0xC0..0x17E).to_a.reject {|c| c.in?([0xD7, 0xF7])}.pack("U*")
string.each_char do |char|
assert_match %r{^[a-zA-Z']*$}, ActiveSupport::Inflector.transliterate(string)
end
diff --git a/load_paths.rb b/load_paths.rb
index 8f37364629..15345e31e3 100644
--- a/load_paths.rb
+++ b/load_paths.rb
@@ -1,32 +1,9 @@
-begin
- require File.expand_path('../.bundle/environment', __FILE__)
-rescue LoadError
- begin
- # bust gem prelude
- if defined? Gem
- Gem.source_index
- gem 'bundler'
- else
- require 'rubygems'
- end
- require 'bundler'
- Bundler.setup
- rescue LoadError
- module Bundler
- def self.require(*args, &block); end
- def self.method_missing(*args, &block); end
- end
-
- %w(
- actionmailer
- actionpack
- activemodel
- activerecord
- activeresource
- activesupport
- railties
- ).each do |framework|
- $:.unshift File.expand_path("../#{framework}/lib", __FILE__)
- end
- end
+# bust gem prelude
+if defined? Gem
+ Gem.source_index
+ gem 'bundler'
+else
+ require 'rubygems'
end
+require 'bundler'
+Bundler.setup \ No newline at end of file
diff --git a/railties/CHANGELOG b/railties/CHANGELOG
index 90faad6131..a4f0d31971 100644
--- a/railties/CHANGELOG
+++ b/railties/CHANGELOG
@@ -1,5 +1,11 @@
*Rails 3.1.0 (unreleased)*
+* The scaffold controller will now produce SCSS file if Sass is available [Prem Sichanugrist]
+
+* The controller and resource generators will now automatically produce asset stubs (this can be turned off with --skip-assets). These stubs will use Coffee and Sass, if those libraries are available. [DHH]
+
+* jQuery is the new default JavaScript library. [fxn]
+
* Changed scaffold and app generator to create Ruby 1.9 style hash when running on Ruby 1.9 [Prem Sichanugrist]
So instead of creating something like:
diff --git a/railties/guides/rails_guides/textile_extensions.rb b/railties/guides/rails_guides/textile_extensions.rb
index 9beeefb551..352c5e91dd 100644
--- a/railties/guides/rails_guides/textile_extensions.rb
+++ b/railties/guides/rails_guides/textile_extensions.rb
@@ -5,7 +5,7 @@ module RailsGuides
def notestuff(body)
body.gsub!(/^(IMPORTANT|CAUTION|WARNING|NOTE|INFO)[.:](.*)$/) do |m|
css_class = $1.downcase
- css_class = 'warning' if css_class.among?('caution', 'important')
+ css_class = 'warning' if css_class.in?(['caution', 'important'])
result = "<div class='#{css_class}'><p>"
result << $2.strip
@@ -35,7 +35,7 @@ module RailsGuides
def code(body)
body.gsub!(%r{<(yaml|shell|ruby|erb|html|sql|plain)>(.*?)</\1>}m) do |m|
es = ERB::Util.h($2)
- css_class = $1.among?('erb', 'shell') ? 'html' : $1
+ css_class = $1.in?(['erb', 'shell']) ? 'html' : $1
%{<notextile><div class="code_container"><code class="#{css_class}">#{es}</code></div></notextile>}
end
end
diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile
index 8882a5705b..9aab4b6694 100644
--- a/railties/guides/source/active_record_validations_callbacks.textile
+++ b/railties/guides/source/active_record_validations_callbacks.textile
@@ -837,7 +837,7 @@ The selectors to customize the style of error messages are:
* +#errorExplanation p+ - Style for the paragraph that holds the message that appears right below the header of the +div+ element.
* +#errorExplanation ul li+ - Style for the list items with individual error messages.
-Scaffolding for example generates +public/stylesheets/scaffold.css+, which defines the red-based style you saw above.
+Scaffolding for example generates +app/assets/stylesheets/scaffold.css.scss+, which later compiles to +app/assets/stylesheets/scaffold.css+ and defines the red-based style you saw above.
The name of the class and the id can be changed with the +:class+ and +:id+ options, accepted by both helpers.
diff --git a/railties/guides/source/ajax_on_rails.textile b/railties/guides/source/ajax_on_rails.textile
index 25c1e01b55..1566c23414 100644
--- a/railties/guides/source/ajax_on_rails.textile
+++ b/railties/guides/source/ajax_on_rails.textile
@@ -3,14 +3,14 @@ h2. AJAX on Rails
This guide covers the built-in Ajax/JavaScript functionality of Rails (and more); it will enable you to create rich and dynamic AJAX applications with ease! We will cover the following topics:
* Quick introduction to AJAX and related technologies
-* Handling JavaScript the Rails way: Rails helpers, RJS, Prototype and script.aculo.us
+* Handling JavaScript the Rails way: Rails helpers, Prototype and script.aculo.us
* Testing JavaScript functionality
endprologue.
h3. Hello AJAX - a Quick Intro
-If you are a 'show me the code' type of person, you might want to skip this part and jump to the RJS section right away. However, I would really recommend to read it - you'll need the basics of DOM, http requests and other topics discussed here to really understand Ajax on Rails.
+You'll need the basics of DOM, HTTP requests and other topics discussed here to really understand Ajax on Rails.
h4. Asynchronous JavaScript + XML
@@ -62,7 +62,7 @@ link_to_remote "Add to cart",
* The second parameter, the +options+ hash is the most interesting part as it has the AJAX specific stuff:
** *:url* This is the only parameter that is always required to generate the simplest remote link (technically speaking, it is not required, you can pass an empty +options+ hash to +link_to_remote+ - but in this case the URL used for the POST request will be equal to your current URL which is probably not your intention). This URL points to your AJAX action handler. The URL is typically specified by Rails REST view helpers, but you can use the +url_for+ format too.
-** *:update* There are basically two ways of injecting the server response into the page: One is involving RJS and we will discuss it in the next chapter, and the other is specifying a DOM id of the element we would like to update. The above example demonstrates the simplest way of accomplishing this - however, we are in trouble if the server responds with an error message because that will be injected into the page too! However, Rails has a solution for this situation:
+** *:update* Specifying a DOM id of the element we would like to update. The above example demonstrates the simplest way of accomplishing this - however, we are in trouble if the server responds with an error message because that will be injected into the page too! However, Rails has a solution for this situation:
<ruby>
link_to_remote "Add to cart",
@@ -178,12 +178,7 @@ h5. +remote_function+
h5. +update_page+
-
-h3. JavaScript the Rails way: RJS
-
-In the last section we sent some AJAX requests to the server, and inserted the HTML response into the page (with the +:update+ option). However, sometimes a more complicated interaction with the page is needed, which you can either achieve with JavaScript... or with RJS! You are sending JavaScript instructions to the server in both cases, but while in the former case you have to write vanilla JavaScript, in the second you can code Rails, and sit back while Rails generates the JavaScript for you - so basically RJS is a Ruby DSL to write JavaScript in your Rails code.
-
-h4. JavaScript without RJS
+h4. Serving JavaScript
First we'll check out how to send JavaScript to the server manually. You are practically never going to need this, but it's interesting to understand what's going on under the hood.
@@ -198,136 +193,6 @@ end
What happens here is that by specifying the Content-Type header variable, we instruct the browser to evaluate the text we are sending over (rather than displaying it as plain text, which is the default behavior).
-h4. Inline RJS
-
-As we said, the purpose of RJS is to write Ruby which is then auto-magically turned into JavaScript by Rails. The above example didn't look too Ruby-esque so let's see how to do it the Rails way:
-
-<ruby>
-def javascript_test
- render :update do |page|
- page.alert "Hello from inline RJS"
- end
-end
-</ruby>
-
-The above code snippet does exactly the same as the one in the previous section - going about it much more elegantly though. You don't need to worry about headers,write ugly JavaScript code into a string etc. When the first parameter to +render+ is +:update+, Rails expects a block with a single parameter (+page+ in our case, which is the traditional naming convention) which is an instance of the JavaScriptGenerator:"http://api.rubyonrails.org/classes/ActionView/Helpers/PrototypeHelper/JavaScriptGenerator/GeneratorMethods.html" object. As it's name suggests, JavaScriptGenerator is responsible for generating JavaScript from your Ruby code. You can execute multiple method calls on the +page+ instance - it's all turned into JavaScript code and sent to the server with the appropriate Content Type, "text/javascript".
-
-h4. RJS Templates
-
-If you don't want to clutter your controllers with view code (especially when your inline RJS is more than a few lines), you can move your RJS code to a template file. RJS templates should go to the +/app/views/+ directory, just as +.html.erb+ or any other view files of the appropriate controller, conventionally named +js.rjs+.
-
-To rewrite the above example, you can leave the body of the action empty, and create a RJS template named +javascript_test.js.rjs+, containing the following line:
-
-<ruby>
-page.alert "Hello from inline RJS"
-</ruby>
-
-h4. RJS Reference
-
-In this section we'll go through the methods RJS offers.
-
-h5. JavaScriptGenerator Methods
-
-h6. DOM Element Manipulation
-
-It is possible to manipulate multiple elements at once through the +page+ JavaScriptGenerator instance. Let's see this in action:
-
-<ruby>
-page.show :div_one, :div_two
-page.hide :div_one
-page.remove :div_one, :div_two, :div_three
-page.toggle :other_div
-</ruby>
-
-The above methods (+show+, +hide+, +remove+, +toggle+) have the same semantics as the Prototype methods of the same name. You can pass an arbitrary number (but at least one) of DOM ids to these calls.
-
-
-h6. Inserting and Replacing Content
-
-You can insert content into an element on the page with the +insert_html+ method:
-
-<ruby>
-page.insert_html :top, :result, '42'
-</ruby>
-
-The first parameter is the position of the new content relative to the element specified by the second parameter, a DOM id.
-
-Position can be one of these four values:
-
-*** +:before+ Inserts the response text just before the target element.
-*** +:after+ The response is inserted after the target element.
-*** +:top+ Inserts the text into the target element, before it's original content.
-*** +:bottom+ The counterpart of +:top+: the response is inserted after the target element's original content.
-
-The third parameter can either be a string, or a hash of options to be passed to ActionView::Base#render - for example:
-
-<ruby>
-page.insert_html :top, :result, :partial => "the_answer"
-</ruby>
-
-You can replace the contents (innerHTML) of an element with the +replace_html+ method. The only difference is that since it's clear where should the new content go, there is no need for a position parameter - so +replace_html+ takes only two arguments,
-the DOM id of the element you wish to modify and a string or a hash of options to be passed to ActionView::Base#render.
-
-h6. Delay
-
-You can delay the execution of a block of code with +delay+:
-
-<ruby>
-page.delay(10) { page.alert('Hey! Just waited 10 seconds') }
-</ruby>
-
-+delay+ takes one parameter (time to wait in seconds) and a block which will be executed after the specified time has passed - whatever else follows a +page.delay+ line is executed immediately, the delay affects only the code in the block.
-
-h6. Reloading and Redirecting
-
-You can reload the page with the +reload+ method:
-
-<ruby>
-page.reload
-</ruby>
-
-When using AJAX, you can't rely on the standard +redirect_to+ controller method - you have to use the +page+'s instance method, also called +redirect_to+:
-
-<ruby>
-page.redirect_to some_url
-</ruby>
-
-h6. Generating Arbitrary JavaScript
-
-Sometimes even the full power of RJS is not enough to accomplish everything, but you still don't want to drop to pure JavaScript. A nice golden mean is offered by the combination of +<<+, +assign+ and +call+ methods:
-
-<ruby>
- page << "alert('1+1 equals 3')"
-</ruby>
-
-So +<<+ is used to execute an arbitrary JavaScript statement, passed as string to the method. The above code is equivalent to:
-
-<ruby>
- page.assign :result, 3
- page.call :alert, '1+1 equals ' + result
-</ruby>
-
-+assign+ simply assigns a value to a variable. +call+ is similar to +<<+ with a slightly different syntax: the first parameter is the name of the function to call, followed by the list of parameters passed to the function.
-
-h6. Class Proxies
-
-h5. Element Proxies
-
-h5. Collection Proxies
-
-h5. RJS Helpers
-
-
-
-h3. I Want my Yellow Thingy: Quick overview of Script.aculo.us
-
-h4. Introduction
-
-h4. Visual Effects
-
-h4. Drag and Drop
-
-
h3. Testing JavaScript
diff --git a/railties/guides/source/command_line.textile b/railties/guides/source/command_line.textile
index a46087c91b..ad36c6532e 100644
--- a/railties/guides/source/command_line.textile
+++ b/railties/guides/source/command_line.textile
@@ -218,13 +218,13 @@ $ rails generate scaffold HighScore game:string score:integer
create app/views/layouts/
exists test/functional/
create test/unit/
- create public/stylesheets/
+ create app/assets/stylesheets/
create app/views/high_scores/index.html.erb
create app/views/high_scores/show.html.erb
create app/views/high_scores/new.html.erb
create app/views/high_scores/edit.html.erb
create app/views/layouts/high_scores.html.erb
- create public/stylesheets/scaffold.css
+ create app/assets/stylesheets/scaffold.css.scss
create app/controllers/high_scores_controller.rb
create test/functional/high_scores_controller_test.rb
create app/helpers/high_scores_helper.rb
diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index 9ca567129b..53460b8c36 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -291,8 +291,6 @@ h4. Configuring Action View
There are only a few configuration options for Action View, starting with four on +ActionView::Base+:
-* +config.action_view.debug_rjs+ specifies whether RJS responses should be wrapped in a try/catch block that alerts the caught exception (and then re-raises it). The default is +false+.
-
* +config.action_view.field_error_proc+ provides an HTML generator for displaying errors that come from Active Record. The default is <tt>Proc.new{ |html_tag, instance| %Q(%&lt;div class=&quot;field_with_errors&quot;&gt;#{html_tag}&lt;/div&gt;).html_safe }</tt>
* +config.action_view.default_form_builder+ tells Rails which form builder to use by default. The default is +ActionView::Helpers::FormBuilder+.
diff --git a/railties/guides/source/debugging_rails_applications.textile b/railties/guides/source/debugging_rails_applications.textile
index 507b0d7ec9..477ff29dbd 100644
--- a/railties/guides/source/debugging_rails_applications.textile
+++ b/railties/guides/source/debugging_rails_applications.textile
@@ -96,26 +96,6 @@ Will be rendered as follows:
Title: Rails debugging guide
</pre>
-h4. Debugging RJS
-
-Rails has optional built-in support to debug RJS. When enabled, responses are wrapped in a try/catch block that displays the caught exception using +alert()+, and then re-raises it.
-
-The flag to enable RJS debugging in your configuration files is +config.action_view.debug_rjs+:
-
-<ruby>
-config.action_view.debug_rjs = true
-</ruby>
-
-or at any time setting +ActionView::Base.debug_rjs+:
-
-<ruby>
-ActionView::Base.debug_rjs = true
-</ruby>
-
-It is enabled by default in development mode, and disabled in the rest.
-
-TIP: For more information on debugging JavaScript, refer to "Firebug":http://getfirebug.com/, the popular debugger for Firefox.
-
h3. The Logger
It can also be useful to save information to log files at runtime. Rails maintains a separate log file for each runtime environment.
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index af3525636c..366df9cd84 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -368,7 +368,7 @@ The scaffold generator will build 15 files in your application, along with some
|test/functional/posts_controller_test.rb |Functional testing harness for the posts controller|
|test/unit/helpers/posts_helper_test.rb |Unit testing harness for the posts helper|
|config/routes.rb |Edited to include routing information for posts|
-|public/stylesheets/scaffold.css |Cascading style sheet to make the scaffolded views look better|
+|app/assets/stylesheets/scaffold.css.scss |Cascading style sheet to make the scaffolded views look better|
h4. Running a Migration
diff --git a/railties/guides/source/layouts_and_rendering.textile b/railties/guides/source/layouts_and_rendering.textile
index 66e8aa67e8..620df970dc 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), and +.builder+ for Builder (XML generator).
h4. Using +render+
@@ -250,18 +250,6 @@ render :inline =>
"xml.p {'Horrid coding practice!'}", :type => :builder
</ruby>
-h5. Using +render+ with +:update+
-
-You can also render JavaScript-based page updates inline using the +:update+ option to +render+:
-
-<ruby>
-render :update do |page|
- page.replace_html 'warning', "Invalid options supplied"
-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.
-
h5. Rendering Text
You can send plain text - with no markup at all - back to the browser by using the +:text+ option to +render+:
@@ -296,7 +284,7 @@ TIP: You don't need to call +to_xml+ on the object that you want to render. If y
h5. Rendering Vanilla JavaScript
-Rails can render vanilla JavaScript (as an alternative to using +update+ with an +.rjs+ file):
+Rails can render vanilla JavaScript:
<ruby>
render :js => "alert('Hello Rails');"
@@ -707,18 +695,28 @@ To include +http://example.com/main.js+:
<%= javascript_include_tag "http://example.com/main.js" %>
</erb>
-The +defaults+ option loads the Prototype and Scriptaculous libraries:
+The +:defaults+ option loads jQuery by default:
<erb>
<%= javascript_include_tag :defaults %>
</erb>
-The +all+ option loads every JavaScript file in +public/javascripts+, starting with the Prototype and Scriptaculous libraries:
+If the application was generated with "-j prototype" <tt>:defaults</tt> loads Prototype and Scriptaculous. And you can in any case override the expansion in <tt>config/application.rb</tt>:
+
+<ruby>
+config.action_view.javascript_expansions[:defaults] = %w(foo.js bar.js)
+</ruby>
+
+When using <tt>:defaults</tt>, if an <tt>application.js</tt> file exists in <tt>public/javascripts</tt> it will be included as well at then end.
+
+The +:all+ option loads every JavaScript file in +public/javascripts+:
<erb>
<%= javascript_include_tag :all %>
</erb>
+Note that your defaults of choice will be included first, so they will be available to all subsequently included files.
+
You can supply the +:recursive+ option to load files in subfolders of +public/javascripts+ as well:
<erb>
diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile
index 2bd7ddf428..f4c1bde5b1 100644
--- a/railties/guides/source/security.textile
+++ b/railties/guides/source/security.textile
@@ -893,12 +893,6 @@ h4. Ajax Injection
If you use the "in_place_editor plugin":http://dev.rubyonrails.org/browser/plugins/in_place_editing, or actions that return a string, rather than rendering a view, _(highlight)you have to escape the return value in the action_. Otherwise, if the return value contains a XSS string, the malicious code will be executed upon return to the browser. Escape any input value using the h() method.
-h4. RJS Injection
-
--- _Don't forget to escape in JavaScript (RJS) templates, too._
-
-The RJS API generates blocks of JavaScript code based on Ruby code, thus allowing you to manipulate a view or parts of a view from the server side. <em class="highlight">If you allow user input in RJS templates, do escape it using +escape_javascript()+ within JavaScript functions, and in HTML parts using +h()+</em>. Otherwise an attacker could execute arbitrary JavaScript.
-
h4. Command Line Injection
-- _Use user-supplied command line parameters with caution._
diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile
index 3b4cb3d8a5..9b1ef22b97 100644
--- a/railties/guides/source/testing.textile
+++ b/railties/guides/source/testing.textile
@@ -592,7 +592,6 @@ There are more assertions that are primarily used in testing views:
|_.Assertion |_.Purpose|
|+assert_select_email+ |Allows you to make assertions on the body of an e-mail. |
-|+assert_select_rjs+ |Allows you to make assertions on an RJS response. +assert_select_rjs+ has variants which allow you to narrow down on the updated element or even a particular operation on an element.|
|+assert_select_encoded+ |Allows you to make assertions on encoded HTML. It does this by un-encoding the contents of each element and then calling the block with all the un-encoded elements.|
|+css_select(selector)+ or +css_select(element, selector)+ |Returns an array of all the elements selected by the _selector_. In the second variant it first matches the base _element_ and tries to match the _selector_ expression on any of its children. If there are no matches both variants return an empty array.|
diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb
index 1b834275a7..0c3c7737ea 100644
--- a/railties/lib/rails/application.rb
+++ b/railties/lib/rails/application.rb
@@ -50,6 +50,7 @@ module Rails
end
end
+ attr_accessor :assets
delegate :default_url_options, :default_url_options=, :to => :routes
# This method is called just after an application inherits from Rails::Application,
@@ -116,13 +117,10 @@ module Rails
self
end
- alias :build_middleware_stack :app
-
def env_config
@env_config ||= super.merge({
"action_dispatch.parameter_filter" => config.filter_parameters,
"action_dispatch.secret_token" => config.secret_token,
- "action_dispatch.asset_path" => nil,
"action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions
})
end
@@ -139,9 +137,7 @@ module Rails
protected
- def default_asset_path
- nil
- end
+ alias :build_middleware_stack :app
def default_middleware_stack
ActionDispatch::MiddlewareStack.new.tap do |middleware|
@@ -156,8 +152,7 @@ module Rails
end
if config.serve_static_assets
- asset_paths = ActiveSupport::OrderedHash[config.static_asset_paths.to_a.reverse]
- middleware.use ::ActionDispatch::Static, asset_paths
+ middleware.use ::ActionDispatch::Static, paths["public"].first
end
middleware.use ::Rack::Lock unless config.allow_concurrency
@@ -205,4 +200,4 @@ module Rails
require "rails/console/helpers"
end
end
-end
+end \ No newline at end of file
diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb
index 23b0e765ae..bd8c314da6 100644
--- a/railties/lib/rails/application/configuration.rb
+++ b/railties/lib/rails/application/configuration.rb
@@ -4,12 +4,12 @@ require 'rails/engine/configuration'
module Rails
class Application
class Configuration < ::Rails::Engine::Configuration
- attr_accessor :allow_concurrency, :asset_host, :cache_classes, :cache_store,
- :encoding, :consider_all_requests_local, :dependency_loading,
- :filter_parameters, :helpers_paths, :logger,
- :preload_frameworks, :reload_plugins,
- :secret_token, :serve_static_assets, :session_options,
- :time_zone, :whiny_nils, :force_ssl
+ attr_accessor :allow_concurrency, :asset_host, :asset_path, :assets,
+ :cache_classes, :cache_store, :consider_all_requests_local,
+ :dependency_loading, :encoding, :filter_parameters,
+ :force_ssl, :helpers_paths, :logger, :preload_frameworks,
+ :reload_plugins, :secret_token, :serve_static_assets,
+ :session_options, :time_zone, :whiny_nils
attr_writer :log_level
@@ -29,6 +29,12 @@ module Rails
@log_level = nil
@middleware = app_middleware
@generators = app_generators
+
+ @assets = ActiveSupport::OrderedOptions.new
+ @assets.enabled = false
+ @assets.paths = []
+ @assets.precompile = [ /\w+\.(?!js|css)$/, "application.js", "application.css" ]
+ @assets.prefix = "/assets"
end
def compiled_asset_path
@@ -56,6 +62,9 @@ module Rails
paths.add "config/environment", :with => "config/environment.rb"
paths.add "lib/templates"
paths.add "log", :with => "log/#{Rails.env}.log"
+ paths.add "public"
+ paths.add "public/javascripts"
+ paths.add "public/stylesheets"
paths.add "tmp"
paths.add "tmp/cache"
paths
diff --git a/railties/lib/rails/application/finisher.rb b/railties/lib/rails/application/finisher.rb
index a45b61c99c..bf865ce466 100644
--- a/railties/lib/rails/application/finisher.rb
+++ b/railties/lib/rails/application/finisher.rb
@@ -53,6 +53,8 @@ module Rails
end
# Force routes to be loaded just at the end and add it to to_prepare callbacks
+ # This needs to be after the finisher hook to ensure routes added in the hook
+ # are still loaded.
initializer :set_routes_reloader do |app|
reloader = lambda { app.routes_reloader.execute_if_updated }
reloader.call
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index 7108d4157e..4182757346 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -71,7 +71,7 @@ when '--version', '-v'
require 'rails/commands/application'
else
- puts "Error: Command not recognized" unless command.among?('-h', '--help')
+ puts "Error: Command not recognized" unless command.in?(['-h', '--help'])
puts <<-EOT
Usage: rails COMMAND [ARGS]
diff --git a/railties/lib/rails/commands/benchmarker.rb b/railties/lib/rails/commands/benchmarker.rb
index e10ec8fd38..f230f405c0 100644
--- a/railties/lib/rails/commands/benchmarker.rb
+++ b/railties/lib/rails/commands/benchmarker.rb
@@ -1,6 +1,6 @@
require 'active_support/core_ext/object/inclusion'
-if ARGV.first.among?(nil, "-h", "--help")
+if ARGV.first.in?([nil, "-h", "--help"])
puts "Usage: rails benchmarker [times] 'Person.expensive_way' 'Person.another_expensive_way' ..."
exit 1
end
diff --git a/railties/lib/rails/commands/destroy.rb b/railties/lib/rails/commands/destroy.rb
index d082ba592c..2a84e2a6df 100644
--- a/railties/lib/rails/commands/destroy.rb
+++ b/railties/lib/rails/commands/destroy.rb
@@ -3,7 +3,7 @@ require 'active_support/core_ext/object/inclusion'
Rails::Generators.configure!
-if ARGV.first.among?(nil, "-h", "--help")
+if ARGV.first.in?([nil, "-h", "--help"])
Rails::Generators.help 'destroy'
exit
end
diff --git a/railties/lib/rails/commands/generate.rb b/railties/lib/rails/commands/generate.rb
index 789e5ebedb..28c1c56352 100644
--- a/railties/lib/rails/commands/generate.rb
+++ b/railties/lib/rails/commands/generate.rb
@@ -3,7 +3,7 @@ require 'active_support/core_ext/object/inclusion'
Rails::Generators.configure!
-if ARGV.first.among?(nil, "-h", "--help")
+if ARGV.first.in?([nil, "-h", "--help"])
Rails::Generators.help 'generate'
exit
end
diff --git a/railties/lib/rails/commands/profiler.rb b/railties/lib/rails/commands/profiler.rb
index d3195a204e..7959d8a981 100644
--- a/railties/lib/rails/commands/profiler.rb
+++ b/railties/lib/rails/commands/profiler.rb
@@ -1,6 +1,6 @@
require 'active_support/core_ext/object/inclusion'
-if ARGV.first.among?(nil, "-h", "--help")
+if ARGV.first.in?([nil, "-h", "--help"])
$stderr.puts "Usage: rails profiler 'Person.expensive_method(10)' [times] [flat|graph|graph_html]"
exit(1)
end
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index ee265366ff..87385814f7 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -171,32 +171,6 @@ module Rails
#
# Now, +Engine+ will get only requests that were not handled by +Application+.
#
- # == Asset path
- #
- # When you use +Engine+ with its own public directory, you will probably want to copy or symlink it
- # to application's public directory. To simplify generating paths for assets, you can set <tt>asset_path</tt>
- # for an engine:
- #
- # module MyEngine
- # class Engine < Rails::Engine
- # config.asset_path = "/my_engine/%s"
- # end
- # end
- #
- # With such a config, asset paths will be automatically modified inside +Engine+:
- #
- # image_path("foo.jpg") #=> "/my_engine/images/foo.jpg"
- #
- # == Serving static files
- #
- # By default, Rails uses <tt>ActionDispatch::Static</tt> to serve static files in development mode. This is ok
- # while you develop your application, but when you want to deploy it, assets from an engine will not be
- # served by default. You should choose one of the two following strategies:
- #
- # * enable serving static files by setting config.serve_static_assets to true
- # * copy engine's public files to application's public folder with <tt>rake ENGINE_NAME:install:assets</tt>, for example
- # <tt>rake my_engine:install:assets</tt>
- #
# == Engine name
#
# There are some places where an Engine's name is used:
@@ -427,8 +401,7 @@ module Rails
def env_config
@env_config ||= {
- 'action_dispatch.routes' => routes,
- 'action_dispatch.asset_path' => config.asset_path
+ 'action_dispatch.routes' => routes
}
end
@@ -509,13 +482,9 @@ module Rails
require environment if environment
end
- initializer :append_asset_paths do
- config.asset_path ||= default_asset_path
-
- 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
+ initializer :append_assets_path do |app|
+ app.config.assets.paths.unshift *paths["vendor/assets"].existent
+ app.config.assets.paths.unshift *paths["app/assets"].existent
end
initializer :prepend_helpers_path do |app|
@@ -537,42 +506,29 @@ module Rails
rake_tasks do
next if self.is_a?(Rails::Application)
+ next unless has_migrations?
namespace railtie_name do
- desc "Shortcut for running both rake #{railtie_name}:install:migrations and #{railtie_name}:install:assets"
- task :install do
- Rake::Task["#{railtie_name}:install:migrations"].invoke
- Rake::Task["#{railtie_name}:install:assets"].invoke
- end
-
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
-
- desc "Copy assets from #{railtie_name} to application"
- task :assets do
- ENV["FROM"] = railtie_name
- Rake::Task["railties:install:assets"].invoke
- end
end
end
end
protected
- def default_asset_path
- "/#{railtie_name}%s"
- end
-
def routes?
defined?(@routes)
end
+ def has_migrations?
+ paths["db/migrate"].first.present?
+ end
+
def find_root_with_flag(flag, default=nil)
root_path = self.class.called_from
diff --git a/railties/lib/rails/engine/configuration.rb b/railties/lib/rails/engine/configuration.rb
index 4f458b0aee..241db4b0a9 100644
--- a/railties/lib/rails/engine/configuration.rb
+++ b/railties/lib/rails/engine/configuration.rb
@@ -5,7 +5,7 @@ module Rails
class Configuration < ::Rails::Railtie::Configuration
attr_reader :root
attr_writer :middleware, :eager_load_paths, :autoload_once_paths, :autoload_paths
- attr_accessor :plugins, :asset_path
+ attr_accessor :plugins
def initialize(root=nil)
super()
@@ -40,6 +40,7 @@ module Rails
@paths ||= begin
paths = Rails::Paths::Root.new(@root)
paths.add "app", :eager_load => true, :glob => "*"
+ paths.add "app/assets", :glob => "*"
paths.add "app/controllers", :eager_load => true
paths.add "app/helpers", :eager_load => true
paths.add "app/models", :eager_load => true
@@ -55,10 +56,8 @@ module Rails
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/assets", :glob => "*"
paths.add "vendor/plugins"
paths
end
@@ -79,10 +78,6 @@ module Rails
def autoload_paths
@autoload_paths ||= paths.autoload_paths
end
-
- def compiled_asset_path
- asset_path % "" if asset_path
- end
end
end
end
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 29e693dfb0..9be395e989 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -24,9 +24,12 @@ module Rails
:rails => {
:actions => '-a',
:orm => '-o',
+ :javascripts => '-j',
+ :javascript_engine => '-je',
:resource_controller => '-c',
:scaffold_controller => '-c',
:stylesheets => '-y',
+ :stylesheet_engine => '-se',
:template_engine => '-e',
:test_framework => '-t'
},
@@ -43,14 +46,18 @@ module Rails
DEFAULT_OPTIONS = {
:rails => {
+ :assets => true,
:force_plural => false,
:helper => true,
- :orm => nil,
:integration_tool => nil,
+ :javascripts => true,
+ :javascript_engine => nil,
+ :orm => nil,
:performance_tool => nil,
:resource_controller => :controller,
:scaffold_controller => :scaffold_controller,
:stylesheets => true,
+ :stylesheet_engine => nil,
:test_framework => nil,
:template_engine => :erb
},
diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb
index 93db5106ce..481fa95068 100644
--- a/railties/lib/rails/generators/app_base.rb
+++ b/railties/lib/rails/generators/app_base.rb
@@ -10,7 +10,7 @@ module Rails
module Generators
class AppBase < Base
DATABASES = %w( mysql oracle postgresql sqlite3 frontbase ibm_db )
- JAVASCRIPTS = %w( prototype jquery )
+ JAVASCRIPTS = %w( jquery prototype )
attr_accessor :rails_template
add_shebang_option!
@@ -36,11 +36,11 @@ module Rails
class_option :database, :type => :string, :aliases => "-d", :default => "sqlite3",
:desc => "Preconfigure for selected database (options: #{DATABASES.join('/')})"
- class_option :javascript, :type => :string, :aliases => "-j", :default => "prototype",
- :desc => "Preconfigure for selected javascript library (options: #{JAVASCRIPTS.join('/')})"
+ class_option :javascript, :type => :string, :aliases => "-j", :default => "jquery",
+ :desc => "Preconfigure for selected JavaScript library (options: #{JAVASCRIPTS.join('/')})"
class_option :skip_javascript, :type => :boolean, :aliases => "-J", :default => false,
- :desc => "Skip javascript files"
+ :desc => "Skip JavaScript files"
class_option :dev, :type => :boolean, :default => false,
:desc => "Setup the #{name} with Gemfile pointing to your Rails checkout"
@@ -124,30 +124,33 @@ module Rails
entry += "\n# gem 'mysql2', :git => 'git://github.com/brianmario/mysql2.git'"
end
end
- entry
+ entry + "\n"
end
def rails_gemfile_entry
if options.dev?
<<-GEMFILE.strip_heredoc
- 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'
+ 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'
+ gem 'sprockets', :git => "git://github.com/sstephenson/sprockets.git"
GEMFILE
elsif options.edge?
<<-GEMFILE.strip_heredoc
- 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'
+ 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'
+ gem 'sprockets', :git => "git://github.com/sstephenson/sprockets.git"
GEMFILE
else
<<-GEMFILE.strip_heredoc
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'
+ # 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'
+ # gem 'sprockets', :git => "git://github.com/sstephenson/sprockets.git"
GEMFILE
end
end
@@ -162,6 +165,25 @@ module Rails
else options[:database]
end
end
+
+ def gem_for_ruby_debugger
+ if RUBY_VERSION < "1.9.2"
+ "gem 'ruby-debug'"
+ else
+ "gem 'ruby-debug19', :require => 'ruby-debug'"
+ end
+ end
+
+ def gem_for_turn
+ unless RUBY_VERSION < "1.9.2"
+ <<-GEMFILE.strip_heredoc
+ group :test do
+ # Pretty printed test output
+ gem 'turn', :require => false
+ end
+ GEMFILE
+ end
+ end
def bundle_if_dev_or_edge
bundle_command = File.basename(Thor::Util.ruby_command).sub(/ruby/, 'bundle')
@@ -174,6 +196,10 @@ module Rails
def empty_directory_with_gitkeep(destination, config = {})
empty_directory(destination, config)
+ git_keep(destination)
+ end
+
+ def git_keep(destination)
create_file("#{destination}/.gitkeep") unless options[:skip_git]
end
@@ -188,4 +214,4 @@ module Rails
end
end
end
-end
+end \ No newline at end of file
diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb
index 2b0eaea3a4..8d03cb911b 100644
--- a/railties/lib/rails/generators/base.rb
+++ b/railties/lib/rails/generators/base.rb
@@ -165,7 +165,7 @@ module Rails
names.each do |name|
defaults = if options[:type] == :boolean
{ }
- elsif default_value_for_option(name, options).among?(true, false)
+ elsif default_value_for_option(name, options).in?([true, false])
{ :banner => "" }
else
{ :desc => "#{name.to_s.humanize} to be invoked", :banner => "NAME" }
diff --git a/railties/lib/rails/generators/generated_attribute.rb b/railties/lib/rails/generators/generated_attribute.rb
index f85375b2a3..b26161f1d0 100644
--- a/railties/lib/rails/generators/generated_attribute.rb
+++ b/railties/lib/rails/generators/generated_attribute.rb
@@ -45,7 +45,7 @@ module Rails
end
def reference?
- self.type.among?(:references, :belongs_to)
+ self.type.in?([:references, :belongs_to])
end
end
end
diff --git a/railties/lib/rails/generators/rails/app/app_generator.rb b/railties/lib/rails/generators/rails/app/app_generator.rb
index 3f6ff35a86..bc55efa261 100644
--- a/railties/lib/rails/generators/rails/app/app_generator.rb
+++ b/railties/lib/rails/generators/rails/app/app_generator.rb
@@ -46,6 +46,8 @@ module Rails
def app
directory 'app'
+ git_keep 'app/mailers'
+ git_keep 'app/models'
end
def config
@@ -80,45 +82,13 @@ module Rails
end
def log
- empty_directory "log"
-
- inside "log" do
- %w( server production development test ).each do |file|
- create_file "#{file}.log"
- chmod "#{file}.log", 0666, :verbose => false
- end
- end
+ empty_directory_with_gitkeep "log"
end
def public_directory
directory "public", "public", :recursive => false
end
- def images
- directory "public/images"
- end
-
- def stylesheets
- empty_directory_with_gitkeep "public/stylesheets"
- end
-
- def javascripts
- empty_directory "public/javascripts"
-
- unless options[:skip_javascript]
- copy_file "public/javascripts/#{options[:javascript]}.js"
- copy_file "public/javascripts/#{options[:javascript]}_ujs.js", "public/javascripts/rails.js"
-
- if options[:javascript] == "prototype"
- copy_file "public/javascripts/controls.js"
- copy_file "public/javascripts/dragdrop.js"
- copy_file "public/javascripts/effects.js"
- end
- end
-
- copy_file "public/javascripts/application.js"
- end
-
def script
directory "script" do |content|
"#{shebang}\n" + content
@@ -127,19 +97,44 @@ module Rails
end
def test
- directory "test"
+ empty_directory_with_gitkeep "test/fixtures"
+ empty_directory_with_gitkeep "test/functional"
+ empty_directory_with_gitkeep "test/integration"
+ empty_directory_with_gitkeep "test/unit"
+
+ template "test/performance/browsing_test.rb"
+ template "test/test_helper.rb"
end
def tmp
- empty_directory "tmp"
+ empty_directory_with_gitkeep "tmp/cache"
+ end
+
+ def vendor
+ vendor_javascripts
+ vendor_stylesheets
+ vendor_plugins
+ end
- inside "tmp" do
- %w(sessions sockets cache pids).each do |dir|
- empty_directory(dir)
+ def vendor_javascripts
+ if options[:skip_javascript]
+ empty_directory_with_gitkeep "vendor/assets/javascripts"
+ else
+ copy_file "vendor/assets/javascripts/#{options[:javascript]}.js"
+ copy_file "vendor/assets/javascripts/#{options[:javascript]}_ujs.js"
+
+ if options[:javascript] == "prototype"
+ copy_file "vendor/assets/javascripts/controls.js"
+ copy_file "vendor/assets/javascripts/dragdrop.js"
+ copy_file "vendor/assets/javascripts/effects.js"
end
end
end
+ def vendor_stylesheets
+ empty_directory_with_gitkeep "vendor/assets/stylesheets"
+ end
+
def vendor_plugins
empty_directory_with_gitkeep "vendor/plugins"
end
@@ -150,15 +145,14 @@ module Rails
# can change in Ruby 1.8.7 when we FileUtils.cd.
RAILS_DEV_PATH = File.expand_path("../../../../../..", File.dirname(__FILE__))
- RESERVED_NAMES = %w[application destroy benchmarker profiler
- plugin runner test]
+ RESERVED_NAMES = %w[application destroy benchmarker profiler plugin runner test]
class AppGenerator < AppBase
add_shared_options_for "application"
# Add bin/rails options
- class_option :version, :type => :boolean, :aliases => "-v", :group => :rails,
- :desc => "Show Rails version number and quit"
+ class_option :version, :type => :boolean, :aliases => "-v", :group => :rails,
+ :desc => "Show Rails version number and quit"
def initialize(*args)
raise Error, "Options should be given after the application name. For details run: rails --help" if args[0].blank?
@@ -168,7 +162,7 @@ module Rails
if !options[:skip_active_record] && !DATABASES.include?(options[:database])
raise Error, "Invalid value for --database option. Supported for preconfiguration are: #{DATABASES.join(", ")}."
end
-
+
if !options[:skip_javascript] && !JAVASCRIPTS.include?(options[:javascript])
raise Error, "Invalid value for --javascript option. Supported for preconfiguration are: #{JAVASCRIPTS.join(", ")}."
end
@@ -221,18 +215,6 @@ module Rails
build(:public_directory)
end
- def create_public_image_files
- build(:images)
- end
-
- def create_public_stylesheets_files
- build(:stylesheets)
- end
-
- def create_javascript_files
- build(:javascripts)
- end
-
def create_script_files
build(:script)
end
@@ -246,7 +228,7 @@ module Rails
end
def create_vendor_files
- build(:vendor_plugins)
+ build(:vendor)
end
def finish_template
diff --git a/railties/lib/rails/generators/rails/app/templates/Gemfile b/railties/lib/rails/generators/rails/app/templates/Gemfile
index 61daefef90..0cee7deb72 100644
--- a/railties/lib/rails/generators/rails/app/templates/Gemfile
+++ b/railties/lib/rails/generators/rails/app/templates/Gemfile
@@ -4,32 +4,18 @@ source 'http://rubygems.org'
<%= database_gemfile_entry -%>
+# Asset template engines
+<%= "gem 'json'\n" if RUBY_VERSION < "1.9.2" -%>
+gem 'sass', '~> 3.1.0.alpha'
+gem 'coffee-script'
+
# Use unicorn as the web server
# gem 'unicorn'
# Deploy with Capistrano
# gem 'capistrano'
-# To use debugger (ruby-debug for Ruby 1.8.7+, ruby-debug19 for Ruby 1.9.2+)
-# gem 'ruby-debug'
-# gem 'ruby-debug19', :require => 'ruby-debug'
-
-# Bundle the extra gems:
-# gem 'bj'
-# gem 'nokogiri'
-# gem 'sqlite3'
-# gem 'rack-bug', :require => 'rack/bug'
-
-# Bundle gems for the local environment. Make sure to
-# put test-only gems in this group so their generators
-# and rake tasks are available in development mode:
-
-group :development, :test do
- # Depend "turn" for pretty printing test output, but disable autorequire.
- gem 'turn', :require => false
-
- # gem 'webrat'
-end
+# To use debugger
+# <%= gem_for_ruby_debugger %>
-# Needed for guides generation
-# gem "RedCloth", "~> 4.2"
+<%= gem_for_turn -%> \ No newline at end of file
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/images/rails.png b/railties/lib/rails/generators/rails/app/templates/app/assets/images/rails.png
new file mode 100644
index 0000000000..d5edc04e65
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/assets/images/rails.png
Binary files differ
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
new file mode 100644
index 0000000000..fb5e91caf4
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/assets/javascripts/application.js.tt
@@ -0,0 +1,11 @@
+// FIXME: Tell people that this is a manifest file, real code should go into discrete files
+// FIXME: Tell people how Sprockets and CoffeeScript works
+//
+//= require <%= options[:javascript] %>
+//= require <%= options[:javascript] %>_ujs
+<% if options[:javascript] == "prototype" %>
+//= require controls
+//= require dragdrop
+//= require effects
+<% end -%>
+//= require_tree .
diff --git a/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css
new file mode 100644
index 0000000000..ccfff11a5d
--- /dev/null
+++ b/railties/lib/rails/generators/rails/app/templates/app/assets/stylesheets/application.css
@@ -0,0 +1,4 @@
+/*
+ * FIXME: Introduce SCSS & Sprockets
+ *= require_tree .
+*/ \ No newline at end of file
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 6d56c331c1..c63d1b6ac5 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
@@ -2,8 +2,8 @@
<html>
<head>
<title><%= camelized %></title>
- <%%= stylesheet_link_tag :all %>
- <%%= javascript_include_tag :defaults %>
+ <%%= stylesheet_link_tag "application" %>
+ <%%= javascript_include_tag "application" %>
<%%= csrf_meta_tags %>
</head>
<body>
diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb
index b7f64af339..ff8e6e5f3e 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/application.rb
+++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb
@@ -42,10 +42,10 @@ module <%= app_const_base %>
# JavaScript files you want as :defaults (application.js is always included).
<% if options[:skip_javascript] -%>
config.action_view.javascript_expansions[:defaults] = %w()
-<% elsif options[:javascript] == 'jquery' -%>
- config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
+<% elsif options[:javascript] == 'prototype' -%>
+ config.action_view.javascript_expansions[:defaults] = %w(prototype effects dragdrop controls rails)
<% else -%>
- # config.action_view.javascript_expansions[:defaults] = %w(jquery rails)
+ # config.action_view.javascript_expansions[:defaults] = %w(prototype effects dragdrop controls rails)
<% end -%>
<% if options[:skip_test_unit] -%>
@@ -62,5 +62,8 @@ module <%= app_const_base %>
# Enable IdentityMap for Active Record, to disable set to false or remove the line below.
config.active_record.identity_map = true
<% end -%>
+
+ # Enable the asset pipeline
+ config.assets.enabled = true
end
end
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
index bdb897ad33..41b2374eda 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/development.rb.tt
@@ -11,7 +11,6 @@
# Show full error reports and disable caching
config.consider_all_requests_local = true
- config.action_view.debug_rjs = true
config.action_controller.perform_caching = false
# Don't care if the mailer can't send
diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
index 874cc403ba..80951cf73a 100644
--- a/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/config/environments/production.rb.tt
@@ -30,6 +30,9 @@
# Enable serving of images, stylesheets, and javascripts from an asset server
# config.action_controller.asset_host = "http://assets.example.com"
+ # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
+ # config.assets.precompile += %w( search.js )
+
# Disable delivery errors, bad email addresses will be ignored
# config.action_mailer.raise_delivery_errors = false
diff --git a/railties/lib/rails/generators/rails/app/templates/public/index.html b/railties/lib/rails/generators/rails/app/templates/public/index.html
index 13a203dd08..9d9811a5bf 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/index.html
+++ b/railties/lib/rails/generators/rails/app/templates/public/index.html
@@ -59,7 +59,7 @@
#header {
- background-image: url("images/rails.png");
+ background-image: url("/assets/rails.png");
background-repeat: no-repeat;
background-position: top left;
height: 64px;
diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/application.js b/railties/lib/rails/generators/rails/app/templates/public/javascripts/application.js
deleted file mode 100644
index fe4577696b..0000000000
--- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/application.js
+++ /dev/null
@@ -1,2 +0,0 @@
-// Place your application-specific JavaScript functions and classes here
-// This file is automatically included by javascript_include_tag :defaults
diff --git a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb
index a8f7aeac7d..a8f7aeac7d 100644
--- a/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb.tt
+++ b/railties/lib/rails/generators/rails/app/templates/test/test_helper.rb
diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/controls.js b/railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/controls.js
index 7392fb664c..7392fb664c 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/controls.js
+++ b/railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/controls.js
diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/dragdrop.js b/railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/dragdrop.js
index 15c6dbca68..15c6dbca68 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/dragdrop.js
+++ b/railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/dragdrop.js
diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/effects.js b/railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/effects.js
index c81e6c7d5f..c81e6c7d5f 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/effects.js
+++ b/railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/effects.js
diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/jquery.js b/railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/jquery.js
index aa3a4f34fd..aa3a4f34fd 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/jquery.js
+++ b/railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/jquery.js
diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/jquery_ujs.js b/railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/jquery_ujs.js
index 4dcb3779a2..4dcb3779a2 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/jquery_ujs.js
+++ b/railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/jquery_ujs.js
diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/prototype.js b/railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/prototype.js
index 474b2231bb..474b2231bb 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/prototype.js
+++ b/railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/prototype.js
diff --git a/railties/lib/rails/generators/rails/app/templates/public/javascripts/prototype_ujs.js b/railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/prototype_ujs.js
index 2cd1220786..2cd1220786 100644
--- a/railties/lib/rails/generators/rails/app/templates/public/javascripts/prototype_ujs.js
+++ b/railties/lib/rails/generators/rails/app/templates/vendor/assets/javascripts/prototype_ujs.js
diff --git a/railties/lib/rails/generators/rails/assets/USAGE b/railties/lib/rails/generators/rails/assets/USAGE
new file mode 100644
index 0000000000..adebfd7e6f
--- /dev/null
+++ b/railties/lib/rails/generators/rails/assets/USAGE
@@ -0,0 +1,20 @@
+Description:
+ Stubs out a new asset placeholders. Pass the asset name, either CamelCased
+ or under_scored.
+
+ To create assets within a folder, specify the assets name as a
+ path like 'parent/name'.
+
+ This generates a JavaScript stub in app/assets/javascripts and a stylesheet
+ stub in app/assets/stylesheets.
+
+ If CoffeeScript is available, JavaScripts will be generated with the .coffee extension.
+ If Sass 3 is available, stylesheets will be generated with the .scss extension.
+
+Example:
+ `rails generate assets posts`
+
+ Posts assets.
+ Javascript: app/assets/javascripts/posts.js
+ Stylesheet: app/assets/stylesheets/posts.css
+
diff --git a/railties/lib/rails/generators/rails/assets/assets_generator.rb b/railties/lib/rails/generators/rails/assets/assets_generator.rb
new file mode 100644
index 0000000000..80beb7abfe
--- /dev/null
+++ b/railties/lib/rails/generators/rails/assets/assets_generator.rb
@@ -0,0 +1,39 @@
+module Rails
+ module Generators
+ class AssetsGenerator < NamedBase
+ class_option :javascripts, :type => :boolean, :desc => "Generate javascripts"
+ class_option :stylesheets, :type => :boolean, :desc => "Generate stylesheets"
+
+ class_option :javascript_engine, :desc => "Engine for javascripts"
+ class_option :stylesheet_engine, :desc => "Engine for stylesheets"
+
+ def create_javascript_files
+ return unless options.javascripts?
+ copy_file "javascript.#{javascript_extension}",
+ File.join('app/assets/javascripts', class_path, "#{asset_name}.#{javascript_extension}")
+ end
+
+ def create_stylesheet_files
+ return unless options.stylesheets?
+ copy_file "stylesheet.#{stylesheet_extension}",
+ File.join('app/assets/stylesheets', class_path, "#{asset_name}.#{stylesheet_extension}")
+ end
+
+ protected
+
+ def asset_name
+ file_name
+ end
+
+ def javascript_extension
+ options.javascript_engine.present? ?
+ "js.#{options.javascript_engine}" : "js"
+ end
+
+ def stylesheet_extension
+ options.stylesheet_engine.present? ?
+ "css.#{options.stylesheet_engine}" : "css"
+ end
+ end
+ end
+end
diff --git a/railties/lib/rails/generators/rails/assets/templates/javascript.js b/railties/lib/rails/generators/rails/assets/templates/javascript.js
new file mode 100644
index 0000000000..dee720facd
--- /dev/null
+++ b/railties/lib/rails/generators/rails/assets/templates/javascript.js
@@ -0,0 +1,2 @@
+// Place all the behaviors and hooks related to the matching controller here.
+// All this logic will automatically be available in application.js.
diff --git a/railties/lib/rails/generators/rails/assets/templates/javascript.js.coffee b/railties/lib/rails/generators/rails/assets/templates/javascript.js.coffee
new file mode 100644
index 0000000000..761567942f
--- /dev/null
+++ b/railties/lib/rails/generators/rails/assets/templates/javascript.js.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/
diff --git a/railties/lib/rails/generators/rails/assets/templates/stylesheet.css b/railties/lib/rails/generators/rails/assets/templates/stylesheet.css
new file mode 100644
index 0000000000..7594abf268
--- /dev/null
+++ b/railties/lib/rails/generators/rails/assets/templates/stylesheet.css
@@ -0,0 +1,4 @@
+/*
+ Place all the styles related to the matching controller here.
+ They will automatically be included in application.css.
+*/
diff --git a/railties/lib/rails/generators/rails/assets/templates/stylesheet.css.scss b/railties/lib/rails/generators/rails/assets/templates/stylesheet.css.scss
new file mode 100644
index 0000000000..ba95e217cc
--- /dev/null
+++ b/railties/lib/rails/generators/rails/assets/templates/stylesheet.css.scss
@@ -0,0 +1,5 @@
+/*
+ Place all the styles related to the matching controller here.
+ They will automatically be included in application.css.
+ You can use Sass (SCSS) here: http://sass-lang.com/
+*/
diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb
index 9788c0d0bc..74aa0432a8 100644
--- a/railties/lib/rails/generators/rails/controller/controller_generator.rb
+++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb
@@ -14,7 +14,7 @@ module Rails
end
end
- hook_for :template_engine, :test_framework, :helper
+ hook_for :template_engine, :test_framework, :helper, :assets
end
end
end
diff --git a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
index 3cf8410d1e..81563f81d3 100644
--- a/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb
@@ -9,10 +9,15 @@ module Rails
end
def app
- if options[:mountable]
+ if mountable?
directory "app"
template "#{app_templates_dir}/app/views/layouts/application.html.erb.tt",
"app/views/layouts/#{name}/application.html.erb"
+ elsif full?
+ empty_directory_with_gitkeep "app/models"
+ empty_directory_with_gitkeep "app/controllers"
+ empty_directory_with_gitkeep "app/views"
+ empty_directory_with_gitkeep "app/helpers"
end
end
@@ -61,8 +66,12 @@ task :default => :test
end
end
+ PASSTHROUGH_OPTIONS = [
+ :skip_active_record, :skip_javascript, :database, :javascript, :quiet, :pretend, :force, :skip
+ ]
+
def generate_test_dummy(force = false)
- opts = (options || {}).slice(:skip_active_record, :skip_javascript, :database, :javascript, :quiet, :pretend, :force, :skip)
+ opts = (options || {}).slice(*PASSTHROUGH_OPTIONS)
opts[:force] = force
invoke Rails::Generators::AppGenerator,
@@ -94,26 +103,36 @@ task :default => :test
end
def stylesheets
- empty_directory_with_gitkeep "public/stylesheets" if options[:mountable]
+ if mountable?
+ copy_file "#{app_templates_dir}/app/assets/stylesheets/application.css",
+ "app/assets/stylesheets/application.css"
+ elsif full?
+ empty_directory_with_gitkeep "app/assets/stylesheets"
+ end
end
def javascripts
- return unless options[:mountable]
+ return if options.skip_javascript?
- empty_directory "#{app_templates_dir}/public/javascripts"
-
- unless options[:skip_javascript]
- copy_file "#{app_templates_dir}/public/javascripts/#{options[:javascript]}.js", "public/javascripts/#{options[:javascript]}.js"
- copy_file "#{app_templates_dir}/public/javascripts/#{options[:javascript]}_ujs.js", "public/javascripts/rails.js"
+ if mountable?
+ copy_file "#{app_templates_dir}/app/assets/javascripts/application.js.tt",
+ "app/assets/javascripts/application.js"
+ copy_file "#{app_templates_dir}/vendor/assets/javascripts/#{options[:javascript]}.js",
+ "vendor/assets/javascripts/#{options[:javascript]}.js"
+ copy_file "#{app_templates_dir}/vendor/assets/javascripts/#{options[:javascript]}_ujs.js",
+ "vendor/assets/javascripts/#{options[:javascript]}_ujs.js"
if options[:javascript] == "prototype"
- copy_file "#{app_templates_dir}/public/javascripts/controls.js", "public/javascripts/controls.js"
- copy_file "#{app_templates_dir}/public/javascripts/dragdrop.js", "public/javascripts/dragdrop.js"
- copy_file "#{app_templates_dir}/public/javascripts/effects.js", "public/javascripts/effects.js"
+ copy_file "#{app_templates_dir}/vendor/assets/javascripts/controls.js",
+ "vendor/assets/javascripts/controls.js"
+ copy_file "#{app_templates_dir}/vendor/assets/javascripts/dragdrop.js",
+ "vendor/assets/javascripts/dragdrop.js"
+ copy_file "#{app_templates_dir}/vendor/assets/javascripts/effects.js",
+ "vendor/assets/javascripts/effects.js"
end
+ elsif full?
+ empty_directory_with_gitkeep "app/assets/javascripts"
end
-
- copy_file "#{app_templates_dir}/public/javascripts/application.js", "public/javascripts/application.js"
end
def script(force = false)
@@ -130,17 +149,17 @@ task :default => :test
alias_method :plugin_path, :app_path
- class_option :dummy_path, :type => :string, :default => "test/dummy",
- :desc => "Create dummy application at given path"
+ class_option :dummy_path, :type => :string, :default => "test/dummy",
+ :desc => "Create dummy application at given path"
- class_option :full, :type => :boolean, :default => false,
- :desc => "Generate rails engine with integration tests"
+ class_option :full, :type => :boolean, :default => false,
+ :desc => "Generate rails engine with integration tests"
- class_option :mountable, :type => :boolean, :default => false,
- :desc => "Generate mountable isolated application"
+ class_option :mountable, :type => :boolean, :default => false,
+ :desc => "Generate mountable isolated application"
- class_option :skip_gemspec, :type => :boolean, :default => false,
- :desc => "Skip gemspec file"
+ class_option :skip_gemspec, :type => :boolean, :default => false,
+ :desc => "Skip gemspec file"
def initialize(*args)
raise Error, "Options should be given after the plugin name. For details run: rails plugin --help" if args[0].blank?
@@ -200,6 +219,7 @@ task :default => :test
public_task :apply_rails_template, :bundle_if_dev_or_edge
protected
+
def app_templates_dir
"../../app/templates"
end
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/app/models/.empty_directory b/railties/lib/rails/generators/rails/plugin_new/templates/app/models/.empty_directory
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/app/models/.empty_directory
diff --git a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
index 779f933785..6eef0dbe5b 100644
--- a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
+++ b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb
@@ -6,8 +6,27 @@ module Rails
remove_hook_for :resource_controller
remove_class_option :actions
+ class_option :stylesheets, :type => :boolean, :desc => "Generate stylesheets"
+ class_option :stylesheet_engine, :desc => "Engine for stylesheets"
+
hook_for :scaffold_controller, :required => true
- hook_for :stylesheets
+
+ def copy_stylesheets_file
+ if behavior == :invoke && options.stylesheets?
+ template "scaffold.#{stylesheet_extension}", "app/assets/stylesheets/scaffold.#{stylesheet_extension}"
+ end
+ end
+
+ hook_for :assets do |assets|
+ invoke assets, [controller_name]
+ end
+
+ private
+
+ def stylesheet_extension
+ options.stylesheet_engine.present? ?
+ "css.#{options.stylesheet_engine}" : "css"
+ end
end
end
end
diff --git a/railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css b/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css
index 1ae7000299..1ae7000299 100644
--- a/railties/lib/rails/generators/rails/stylesheets/templates/scaffold.css
+++ b/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css
diff --git a/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css.scss b/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css.scss
new file mode 100644
index 0000000000..45116b53f6
--- /dev/null
+++ b/railties/lib/rails/generators/rails/scaffold/templates/scaffold.css.scss
@@ -0,0 +1,58 @@
+body { background-color: #fff; color: #333; }
+
+body, p, ol, ul, td {
+ font-family: verdana, arial, helvetica, sans-serif;
+ font-size: 13px;
+ line-height: 18px;
+}
+
+pre {
+ background-color: #eee;
+ padding: 10px;
+ font-size: 11px;
+}
+
+a {
+ color: #000;
+ &:visited { color: #666; }
+ &:hover { color: #fff; background-color:#000; }
+}
+
+div.field, div.actions {
+ margin-bottom: 10px;
+}
+
+#notice {
+ color: green;
+}
+
+.field_with_errors {
+ padding: 2px;
+ background-color: red;
+ display: table;
+}
+
+#error_explanation {
+ width: 450px;
+ border: 2px solid red;
+ padding: 7px;
+ padding-bottom: 0;
+ margin-bottom: 20px;
+ background-color: #f0f0f0;
+
+ h2 {
+ text-align: left;
+ font-weight: bold;
+ padding: 5px 5px 5px 15px;
+ font-size: 12px;
+ margin: -7px;
+ margin-bottom: 0px;
+ background-color: #c00;
+ color: #fff;
+ }
+
+ ul li {
+ font-size: 12px;
+ list-style: square;
+ }
+} \ No newline at end of file
diff --git a/railties/lib/rails/generators/rails/stylesheets/USAGE b/railties/lib/rails/generators/rails/stylesheets/USAGE
deleted file mode 100644
index 59e5495d0b..0000000000
--- a/railties/lib/rails/generators/rails/stylesheets/USAGE
+++ /dev/null
@@ -1,5 +0,0 @@
-Description:
- Copies scaffold stylesheets to public/stylesheets/.
-
-Examples:
- `rails generate stylesheets`
diff --git a/railties/lib/rails/generators/rails/stylesheets/stylesheets_generator.rb b/railties/lib/rails/generators/rails/stylesheets/stylesheets_generator.rb
deleted file mode 100644
index ce68443c39..0000000000
--- a/railties/lib/rails/generators/rails/stylesheets/stylesheets_generator.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-module Rails
- module Generators
- class StylesheetsGenerator < Base
- def copy_stylesheets_file
- template "scaffold.css", "public/stylesheets/scaffold.css" if behavior == :invoke
- end
- end
- end
-end
diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb
index 2c7b5bc048..bfd2a73aeb 100644
--- a/railties/lib/rails/railtie/configuration.rb
+++ b/railties/lib/rails/railtie/configuration.rb
@@ -67,13 +67,6 @@ module Rails
super || @@options.key?(name.to_sym)
end
- # static_asset_paths is a Hash containing asset_paths
- # with associated public folders, like:
- # { "/" => "/app/public", "/my_engine" => "app/engines/my_engine/public" }
- def static_asset_paths
- @@static_asset_paths ||= ActiveSupport::OrderedHash.new
- end
-
private
def method_missing(name, *args, &blk)
diff --git a/railties/lib/rails/source_annotation_extractor.rb b/railties/lib/rails/source_annotation_extractor.rb
index 302206bbcd..6d6e7f8b59 100644
--- a/railties/lib/rails/source_annotation_extractor.rb
+++ b/railties/lib/rails/source_annotation_extractor.rb
@@ -30,7 +30,7 @@ class SourceAnnotationExtractor
# Prints all annotations with tag +tag+ under the root directories +app+, +lib+,
# and +test+ (recursively). Only filenames with extension +.builder+, +.rb+,
- # +.rxml+, +.rjs+, +.rhtml+, or +.erb+ are taken into account. The +options+
+ # +.rxml+, +.rhtml+, or +.erb+ are taken into account. The +options+
# hash is passed to each annotation's +to_s+.
#
# This class method is the single entry point for the rake tasks.
@@ -47,7 +47,7 @@ class SourceAnnotationExtractor
# Returns a hash that maps filenames under +dirs+ (recursively) to arrays
# with their annotations. Only files with annotations are included, and only
- # those with extension +.builder+, +.rb+, +.rxml+, +.rjs+, +.rhtml+, and +.erb+
+ # those with extension +.builder+, +.rb+, +.rxml+, +.rhtml+, and +.erb+
# are taken into account.
def find(dirs=%w(app lib test))
dirs.inject({}) { |h, dir| h.update(find_in(dir)) }
@@ -55,7 +55,7 @@ class SourceAnnotationExtractor
# Returns a hash that maps filenames under +dir+ (recursively) to arrays
# with their annotations. Only files with annotations are included, and only
- # those with extension +.builder+, +.rb+, +.rxml+, +.rjs+, +.rhtml+, and +.erb+
+ # those with extension +.builder+, +.rb+, +.rxml+, +.rhtml+, and +.erb+
# are taken into account.
def find_in(dir)
results = {}
diff --git a/railties/lib/rails/tasks.rb b/railties/lib/rails/tasks.rb
index af52014728..166d518f7c 100644
--- a/railties/lib/rails/tasks.rb
+++ b/railties/lib/rails/tasks.rb
@@ -3,6 +3,7 @@ $VERBOSE = nil
# Load Rails rakefile extensions
%w(
annotations
+ assets
documentation
framework
log
@@ -11,7 +12,6 @@ $VERBOSE = nil
routes
statistics
tmp
- railties
).each do |task|
load "rails/tasks/#{task}.rake"
end
diff --git a/railties/lib/rails/tasks/assets.rake b/railties/lib/rails/tasks/assets.rake
new file mode 100644
index 0000000000..158df31749
--- /dev/null
+++ b/railties/lib/rails/tasks/assets.rake
@@ -0,0 +1,10 @@
+namespace :assets do
+ desc "Compile all the assets named in config.assets.precompile"
+ task :precompile => :environment do
+ # Give assets access to asset_path
+ ActionView::Helpers::SprocketsHelper
+
+ assets = Rails.application.config.assets.precompile
+ Rails.application.assets.precompile(*assets)
+ end
+end
diff --git a/railties/lib/rails/tasks/railties.rake b/railties/lib/rails/tasks/railties.rake
deleted file mode 100644
index 16703879cf..0000000000
--- a/railties/lib/rails/tasks/railties.rake
+++ /dev/null
@@ -1,29 +0,0 @@
-namespace :railties do
- namespace :install do
- # desc "Copies missing assets from Railties (e.g. plugins, engines). You can specify Railties to use with FROM=railtie1,railtie2"
- task :assets => :rails_env do
- require 'rails/generators/base'
- Rails.application.initialize!
-
- to_load = ENV["FROM"].blank? ? :all : ENV["FROM"].split(",").map {|n| n.strip }
- app_public_path = Rails.application.paths["public"].first
-
- 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["public"].first) &&
- (assets_dir = railtie.config.compiled_asset_path) && File.exist?(path)
-
- Rails::Generators::Base.source_root(path)
- copier = Rails::Generators::Base.new
- Dir[File.join(path, "**/*")].each do |file|
- relative = file.gsub(/^#{path}\//, '')
- if File.file?(file)
- copier.copy_file relative, File.join(app_public_path, assets_dir, relative)
- end
- end
- end
- end
- end
- end
-end
diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb
index 43f2fbd71c..3ef06c7f25 100644
--- a/railties/test/generators/app_generator_test.rb
+++ b/railties/test/generators/app_generator_test.rb
@@ -8,6 +8,8 @@ DEFAULT_APP_FILES = %w(
Gemfile
Rakefile
config.ru
+ app/assets/javascripts
+ app/assets/stylesheets
app/controllers
app/helpers
app/mailers
@@ -22,8 +24,6 @@ DEFAULT_APP_FILES = %w(
lib/tasks
log
public/images
- public/javascripts
- public/stylesheets
script/rails
test/fixtures
test/functional
@@ -31,11 +31,9 @@ DEFAULT_APP_FILES = %w(
test/performance
test/unit
vendor
+ vendor/assets
vendor/plugins
- tmp/sessions
- tmp/sockets
tmp/cache
- tmp/pids
)
class AppGeneratorTest < Rails::Generators::TestCase
@@ -49,8 +47,9 @@ class AppGeneratorTest < Rails::Generators::TestCase
def test_application_controller_and_layout_files
run_generator
- assert_file "app/views/layouts/application.html.erb", /stylesheet_link_tag :all/
- assert_no_file "public/stylesheets/application.css"
+ assert_file "app/views/layouts/application.html.erb", /stylesheet_link_tag\s+"application"/
+ assert_file "app/views/layouts/application.html.erb", /javascript_include_tag\s+"application"/
+ assert_file "app/assets/stylesheets/application.css"
end
def test_invalid_application_name_raises_an_error
@@ -146,43 +145,40 @@ class AppGeneratorTest < Rails::Generators::TestCase
assert_file "config/application.rb", /#\s+require\s+["']active_record\/railtie["']/
end
- def test_prototype_and_test_unit_are_added_by_default
+ def test_jquery_and_test_unit_are_added_by_default
run_generator
- assert_file "config/application.rb", /#\s+config\.action_view\.javascript_expansions\[:defaults\]\s+=\s+%w\(jquery rails\)/
- assert_file "public/javascripts/application.js"
- assert_file "public/javascripts/prototype.js"
- assert_file "public/javascripts/rails.js"
- assert_file "public/javascripts/controls.js"
- assert_file "public/javascripts/dragdrop.js"
- assert_file "public/javascripts/effects.js"
+ assert_file "config/application.rb", /#\s+config\.action_view\.javascript_expansions\[:defaults\]\s+=\s+%w\(prototype effects dragdrop controls rails\)/
+ assert_file "app/assets/javascripts/application.js"
+ assert_file "vendor/assets/javascripts/jquery.js"
+ assert_file "vendor/assets/javascripts/jquery_ujs.js"
assert_file "test"
end
def test_javascript_is_skipped_if_required
run_generator [destination_root, "--skip-javascript"]
assert_file "config/application.rb", /^\s+config\.action_view\.javascript_expansions\[:defaults\]\s+=\s+%w\(\)/
- assert_file "public/javascripts/application.js"
- assert_no_file "public/javascripts/prototype.js"
- assert_no_file "public/javascripts/rails.js"
+ assert_file "app/assets/javascripts/application.js"
+ assert_no_file "vendor/assets/javascripts/jquery.js"
+ assert_no_file "vendor/assets/javascripts/jquery_ujs.js"
end
def test_config_prototype_javascript_library
run_generator [destination_root, "-j", "prototype"]
- assert_file "config/application.rb", /#\s+config\.action_view\.javascript_expansions\[:defaults\]\s+=\s+%w\(jquery rails\)/
- assert_file "public/javascripts/application.js"
- assert_file "public/javascripts/prototype.js"
- assert_file "public/javascripts/controls.js"
- assert_file "public/javascripts/dragdrop.js"
- assert_file "public/javascripts/effects.js"
- assert_file "public/javascripts/rails.js", /prototype/
+ assert_file "config/application.rb", /^\s+config\.action_view\.javascript_expansions\[:defaults\]\s+=\s+%w\(prototype effects dragdrop controls rails\)/
+ assert_file "app/assets/javascripts/application.js"
+ assert_file "vendor/assets/javascripts/prototype.js"
+ assert_file "vendor/assets/javascripts/effects.js"
+ assert_file "vendor/assets/javascripts/dragdrop.js"
+ assert_file "vendor/assets/javascripts/controls.js"
+ assert_file "vendor/assets/javascripts/prototype_ujs.js", /prototype/
end
def test_config_jquery_javascript_library
run_generator [destination_root, "-j", "jquery"]
- assert_file "config/application.rb", /^\s+config\.action_view\.javascript_expansions\[:defaults\]\s+=\s+%w\(jquery rails\)/
- assert_file "public/javascripts/application.js"
- assert_file "public/javascripts/jquery.js"
- assert_file "public/javascripts/rails.js", /jQuery/
+ assert_file "config/application.rb", /#\s+config\.action_view\.javascript_expansions\[:defaults\]\s+=\s+%w\(prototype effects dragdrop controls rails\)/
+ assert_file "app/assets/javascripts/application.js"
+ assert_file "vendor/assets/javascripts/jquery.js"
+ assert_file "vendor/assets/javascripts/jquery_ujs.js", /jQuery/
end
def test_template_from_dir_pwd
diff --git a/railties/test/generators/assets_generator_test.rb b/railties/test/generators/assets_generator_test.rb
new file mode 100644
index 0000000000..e99f0f092a
--- /dev/null
+++ b/railties/test/generators/assets_generator_test.rb
@@ -0,0 +1,26 @@
+require 'generators/generators_test_helper'
+require 'rails/generators/rails/assets/assets_generator'
+
+# FOXME: Silence the 'Could not find task "using_coffee?"' message in tests due to the public stub
+class AssetsGeneratorTest < Rails::Generators::TestCase
+ include GeneratorsTestHelper
+ arguments %w(posts)
+
+ def test_assets
+ run_generator
+ assert_file "app/assets/javascripts/posts.js.coffee"
+ assert_file "app/assets/stylesheets/posts.css.scss"
+ end
+
+ def test_skipping_assets
+ content = run_generator ["posts", "--no-stylesheets", "--no-javascripts"]
+ assert_no_file "app/assets/javascripts/posts.js.coffee"
+ assert_no_file "app/assets/stylesheets/posts.css.scss"
+ end
+
+ def test_vanilla_assets
+ run_generator ["posts", "--no-javascript-engine", "--no-stylesheet-engine"]
+ assert_file "app/assets/javascripts/posts.js"
+ assert_file "app/assets/stylesheets/posts.css"
+ end
+end
diff --git a/railties/test/generators/controller_generator_test.rb b/railties/test/generators/controller_generator_test.rb
index be99dc068d..2dfc91c683 100644
--- a/railties/test/generators/controller_generator_test.rb
+++ b/railties/test/generators/controller_generator_test.rb
@@ -37,6 +37,12 @@ class ControllerGeneratorTest < Rails::Generators::TestCase
assert_no_file "test/unit/helpers/account_helper_test.rb"
end
+ def test_invokes_assets
+ run_generator
+ assert_file "app/assets/javascripts/account.js.coffee"
+ assert_file "app/assets/stylesheets/account.css.scss"
+ end
+
def test_invokes_default_test_framework
run_generator
assert_file "test/functional/account_controller_test.rb"
diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb
index d61a02d32f..eb56e8d1a4 100644
--- a/railties/test/generators/namespaced_generators_test.rb
+++ b/railties/test/generators/namespaced_generators_test.rb
@@ -248,7 +248,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
assert_file "test/unit/helpers/test_app/product_lines_helper_test.rb"
# Stylesheets
- assert_file "public/stylesheets/scaffold.css"
+ assert_file "app/assets/stylesheets/scaffold.css.scss"
end
def test_scaffold_on_revoke
@@ -279,7 +279,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
assert_no_file "test/unit/helpers/test_app/product_lines_helper_test.rb"
# Stylesheets (should not be removed)
- assert_file "public/stylesheets/scaffold.css"
+ assert_file "app/assets/stylesheets/scaffold.css.scss"
end
def test_scaffold_with_namespace_on_invoke
@@ -320,7 +320,7 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
assert_file "test/unit/helpers/test_app/admin/roles_helper_test.rb"
# Stylesheets
- assert_file "public/stylesheets/scaffold.css"
+ assert_file "app/assets/stylesheets/scaffold.css.scss"
end
def test_scaffold_with_namespace_on_revoke
@@ -352,6 +352,6 @@ class NamespacedScaffoldGeneratorTest < NamespacedGeneratorTestCase
assert_no_file "test/unit/helpers/test_app/admin/roles_helper_test.rb"
# Stylesheets (should not be removed)
- assert_file "public/stylesheets/scaffold.css"
+ assert_file "app/assets/stylesheets/scaffold.css.scss"
end
end
diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb
index 3c11c8dbaf..d20335ad95 100644
--- a/railties/test/generators/plugin_new_generator_test.rb
+++ b/railties/test/generators/plugin_new_generator_test.rb
@@ -95,41 +95,33 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
def test_skipping_javascripts_without_mountable_option
run_generator
- assert_no_file "public/javascripts/prototype.js"
- assert_no_file "public/javascripts/rails.js"
- assert_no_file "public/javascripts/controls.js"
- assert_no_file "public/javascripts/dragdrop.js"
- assert_no_file "public/javascripts/dragdrop.js"
- assert_no_file "public/javascripts/application.js"
+ assert_no_file "app/assets/javascripts/application.js"
+ assert_no_file "vendor/assets/javascripts/jquery.js"
+ assert_no_file "vendor/assets/javascripts/jquery_ujs.js"
end
def test_javascripts_generation
run_generator [destination_root, "--mountable"]
- assert_file "public/javascripts/rails.js"
- assert_file "public/javascripts/prototype.js"
- assert_file "public/javascripts/controls.js"
- assert_file "public/javascripts/dragdrop.js"
- assert_file "public/javascripts/dragdrop.js"
- assert_file "public/javascripts/application.js"
+ assert_file "app/assets/javascripts/application.js"
+ assert_file "vendor/assets/javascripts/jquery.js"
+ assert_file "vendor/assets/javascripts/jquery_ujs.js"
end
def test_skip_javascripts
run_generator [destination_root, "--skip-javascript", "--mountable"]
- assert_no_file "public/javascripts/prototype.js"
- assert_no_file "public/javascripts/rails.js"
- assert_no_file "public/javascripts/controls.js"
- assert_no_file "public/javascripts/dragdrop.js"
- assert_no_file "public/javascripts/dragdrop.js"
+ assert_no_file "app/assets/javascripts/application.js"
+ assert_no_file "vendor/assets/javascripts/jquery.js"
+ assert_no_file "vendor/assets/javascripts/jquery_ujs.js"
end
- def test_ensure_that_javascript_option_is_passed_to_app_generator
- run_generator [destination_root, "--javascript", "jquery"]
- assert_file "test/dummy/public/javascripts/jquery.js"
- end
-
- def test_ensure_that_skip_javascript_option_is_passed_to_app_generator
- run_generator [destination_root, "--skip_javascript"]
- assert_no_file "test/dummy/public/javascripts/prototype.js"
+ def test_config_prototype_javascript_library
+ run_generator [destination_root, "-j", "prototype", "--mountable"]
+ assert_file "app/assets/javascripts/application.js"
+ assert_file "vendor/assets/javascripts/prototype.js"
+ assert_file "vendor/assets/javascripts/effects.js"
+ assert_file "vendor/assets/javascripts/dragdrop.js"
+ assert_file "vendor/assets/javascripts/controls.js"
+ assert_file "vendor/assets/javascripts/prototype_ujs.js", /prototype/
end
def test_template_from_dir_pwd
@@ -153,6 +145,12 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
def test_creating_engine_in_full_mode
run_generator [destination_root, "--full"]
+ assert_file "app/assets/javascripts"
+ assert_file "app/assets/stylesheets"
+ assert_file "app/models"
+ assert_file "app/controllers"
+ assert_file "app/views"
+ assert_file "app/helpers"
assert_file "config/routes.rb", /Rails.application.routes.draw do/
assert_file "lib/bukkits/engine.rb", /module Bukkits\n class Engine < Rails::Engine\n end\nend/
assert_file "lib/bukkits.rb", /require "bukkits\/engine"/
@@ -227,3 +225,4 @@ protected
silence(:stdout){ generator.send(*args, &block) }
end
end
+
diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb
index df787f61ba..4b07f8bcbe 100644
--- a/railties/test/generators/scaffold_generator_test.rb
+++ b/railties/test/generators/scaffold_generator_test.rb
@@ -79,8 +79,10 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_file "app/helpers/product_lines_helper.rb"
assert_file "test/unit/helpers/product_lines_helper_test.rb"
- # Stylesheets
- assert_file "public/stylesheets/scaffold.css"
+ # Assets
+ assert_file "app/assets/stylesheets/scaffold.css.scss"
+ assert_file "app/assets/javascripts/product_lines.js.coffee"
+ assert_file "app/assets/stylesheets/product_lines.css.scss"
end
def test_scaffold_on_revoke
@@ -110,8 +112,10 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_no_file "app/helpers/product_lines_helper.rb"
assert_no_file "test/unit/helpers/product_lines_helper_test.rb"
- # Stylesheets (should not be removed)
- assert_file "public/stylesheets/scaffold.css"
+ # Assets
+ assert_file "app/assets/stylesheets/scaffold.css.scss", /&:visited/
+ assert_no_file "app/assets/javascripts/product_lines.js.coffee"
+ assert_no_file "app/assets/stylesheets/product_lines.css.scss"
end
def test_scaffold_with_namespace_on_invoke
@@ -184,8 +188,10 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_file "app/helpers/admin/roles_helper.rb"
assert_file "test/unit/helpers/admin/roles_helper_test.rb"
- # Stylesheets
- assert_file "public/stylesheets/scaffold.css"
+ # Assets
+ assert_file "app/assets/stylesheets/scaffold.css.scss", /&:visited/
+ assert_file "app/assets/javascripts/admin/roles.js.coffee"
+ assert_file "app/assets/stylesheets/admin/roles.css.scss"
end
def test_scaffold_with_namespace_on_revoke
@@ -216,8 +222,10 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_no_file "app/helpers/admin/roles_helper.rb"
assert_no_file "test/unit/helpers/admin/roles_helper_test.rb"
- # Stylesheets (should not be removed)
- assert_file "public/stylesheets/scaffold.css"
+ # Assets
+ assert_file "app/assets/stylesheets/scaffold.css.scss"
+ assert_no_file "app/assets/javascripts/admin/roles.js.coffee"
+ assert_no_file "app/assets/stylesheets/admin/roles.css.scss"
end
def test_scaffold_generator_on_revoke_does_not_mutilate_legacy_map_parameter
@@ -235,6 +243,34 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase
assert_file "config/routes.rb", /\.routes\.draw do\s*\|map\|\s*$/
end
+ def test_scaffold_generator_no_assets
+ run_generator [ "posts", "--no-assets" ]
+ assert_file "app/assets/stylesheets/scaffold.css.scss"
+ assert_no_file "app/assets/javascripts/posts.js.coffee"
+ assert_no_file "app/assets/stylesheets/posts.css.scss"
+ end
+
+ def test_scaffold_generator_no_stylesheets
+ run_generator [ "posts", "--no-stylesheets" ]
+ assert_no_file "app/assets/stylesheets/scaffold.css.scss"
+ assert_file "app/assets/javascripts/posts.js.coffee"
+ assert_no_file "app/assets/stylesheets/posts.css.scss"
+ end
+
+ def test_scaffold_generator_no_javascripts
+ run_generator [ "posts", "--no-javascripts" ]
+ assert_file "app/assets/stylesheets/scaffold.css.scss"
+ assert_no_file "app/assets/javascripts/posts.js.coffee"
+ assert_file "app/assets/stylesheets/posts.css.scss"
+ end
+
+ def test_scaffold_generator_no_negines
+ run_generator [ "posts", "--no-javascript-engine", "--no-stylesheet-engine" ]
+ assert_file "app/assets/stylesheets/scaffold.css"
+ assert_file "app/assets/javascripts/posts.js"
+ assert_file "app/assets/stylesheets/posts.css"
+ end
+
def test_scaffold_generator_outputs_error_message_on_missing_attribute_type
content = capture(:stderr) { run_generator ["post", "title:string", "body"]}
assert_match(/Missing type for attribute 'body'/, content)
diff --git a/railties/test/generators/stylesheets_generator_test.rb b/railties/test/generators/stylesheets_generator_test.rb
deleted file mode 100644
index aaeb686daa..0000000000
--- a/railties/test/generators/stylesheets_generator_test.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require 'generators/generators_test_helper'
-require 'rails/generators/rails/stylesheets/stylesheets_generator'
-
-class StylesheetsGeneratorTest < Rails::Generators::TestCase
- include GeneratorsTestHelper
-
- def test_copy_stylesheets
- run_generator
- assert_file "public/stylesheets/scaffold.css"
- end
-
- def test_stylesheets_are_not_deleted_on_revoke
- run_generator
- run_generator [], :behavior => :revoke
- assert_file "public/stylesheets/scaffold.css"
- end
-end
diff --git a/railties/test/isolation/abstract_unit.rb b/railties/test/isolation/abstract_unit.rb
index c5b1cb9a80..f2261540ca 100644
--- a/railties/test/isolation/abstract_unit.rb
+++ b/railties/test/isolation/abstract_unit.rb
@@ -90,6 +90,7 @@ module TestHelpers
end
module Generation
+ # Build an application by invoking the generator and going through the whole stack.
def build_app(options = {})
FileUtils.rm_rf(app_path)
FileUtils.cp_r(tmp_path('app_template'), app_path)
@@ -115,6 +116,8 @@ module TestHelpers
add_to_config 'config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"; config.session_store :cookie_store, :key => "_myapp_session"; config.active_support.deprecation = :log'
end
+ # Make a very basic app, without creating the whole directory structure.
+ # This is faster and simpler than the method above.
def make_basic_app
require "rails"
require "action_controller/railtie"
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 20797a2b0c..7605984684 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -1,8 +1,7 @@
require "isolation/abstract_unit"
require "railties/shared_tests"
-require 'stringio'
-require 'rack/test'
-require 'rack/file'
+require "stringio"
+require "rack/test"
module RailtiesTest
class EngineTest < Test::Unit::TestCase
@@ -188,6 +187,7 @@ module RailtiesTest
end
RUBY
+ require "rack/file"
boot_rails
env = Rack::MockRequest.env_for("/")
@@ -199,194 +199,6 @@ module RailtiesTest
assert_equal Rails.application.routes, env['action_dispatch.routes']
end
- test "it allows to set asset_path" do
- @plugin.write "lib/bukkits.rb", <<-RUBY
- class Bukkits
- class Engine < ::Rails::Engine
- end
- end
- RUBY
-
-
- @plugin.write "config/routes.rb", <<-RUBY
- Bukkits::Engine.routes.draw do
- match "/foo" => "foo#index"
- end
- RUBY
-
- @plugin.write "app/controllers/foo_controller.rb", <<-RUBY
- class FooController < ActionController::Base
- def index
- render :index
- end
- end
- RUBY
-
- @plugin.write "app/views/foo/index.html.erb", <<-ERB
- <%= image_path("foo.png") %>
- <%= javascript_include_tag("foo") %>
- <%= stylesheet_link_tag("foo") %>
- ERB
-
- app_file "config/routes.rb", <<-RUBY
- Rails.application.routes.draw do
- mount Bukkits::Engine => "/bukkits"
- end
- RUBY
-
- add_to_config 'config.asset_path = "/omg%s"'
-
- boot_rails
-
- # should set asset_path with engine name by default
- assert_equal "/bukkits_engine%s", ::Bukkits::Engine.config.asset_path
-
- ::Bukkits::Engine.config.asset_path = "/bukkits%s"
-
- get("/bukkits/foo")
- stripped_body = last_response.body.split("\n").map(&:strip).join
-
- expected = "/omg/bukkits/images/foo.png" +
- "<script src=\"/omg/bukkits/javascripts/foo.js\" type=\"text/javascript\"></script>" +
- "<link href=\"/omg/bukkits/stylesheets/foo.css\" media=\"screen\" rel=\"stylesheet\" type=\"text/css\" />"
- assert_equal expected, stripped_body
- end
-
- test "default application's asset_path" do
- @plugin.write "config/routes.rb", <<-RUBY
- Bukkits::Engine.routes.draw do
- match "/foo" => "foo#index"
- end
- RUBY
-
- @plugin.write "app/controllers/foo_controller.rb", <<-RUBY
- class FooController < ActionController::Base
- def index
- render :inline => '<%= image_path("foo.png") %>'
- end
- end
- RUBY
-
- app_file "config/routes.rb", <<-RUBY
- AppTemplate::Application.routes.draw do
- mount Bukkits::Engine => "/bukkits"
- end
- RUBY
-
- boot_rails
-
- get("/bukkits/foo")
- assert_equal "/bukkits/images/foo.png", last_response.body.strip
- end
-
- test "engine's files are served via ActionDispatch::Static" do
- add_to_config "config.serve_static_assets = true"
-
- @plugin.write "lib/bukkits.rb", <<-RUBY
- class Bukkits
- class Engine < ::Rails::Engine
- engine_name :bukkits
- end
- end
- RUBY
-
- @plugin.write "public/bukkits.html", "/bukkits/bukkits.html"
- app_file "public/app.html", "/app.html"
- app_file "public/bukkits/file_from_app.html", "/bukkits/file_from_app.html"
-
- boot_rails
-
- get("/app.html")
- assert_equal File.read(File.join(app_path, "public/app.html")), last_response.body
-
- get("/bukkits/bukkits.html")
- assert_equal File.read(File.join(@plugin.path, "public/bukkits.html")), last_response.body
-
- get("/bukkits/file_from_app.html")
- assert_equal File.read(File.join(app_path, "public/bukkits/file_from_app.html")), last_response.body
- end
-
- test "an applications files are given priority over an engines files when served via ActionDispatch::Static" do
- add_to_config "config.serve_static_assets = true"
-
- @plugin.write "lib/bukkits.rb", <<-RUBY
- class Bukkits
- class Engine < ::Rails::Engine
- engine_name :bukkits
- end
- end
- RUBY
-
- app_file "config/routes.rb", <<-RUBY
- AppTemplate::Application.routes.draw do
- mount Bukkits::Engine => "/bukkits"
- end
- RUBY
-
- @plugin.write "public/bukkits.html", "in engine"
-
- app_file "public/bukkits/bukkits.html", "in app"
-
- boot_rails
-
- get('/bukkits/bukkits.html')
-
- assert_equal 'in app', last_response.body.strip
- end
-
- test "shared engine should include application's helpers and own helpers" do
- app_file "config/routes.rb", <<-RUBY
- AppTemplate::Application.routes.draw do
- match "/foo" => "bukkits/foo#index", :as => "foo"
- match "/foo/show" => "bukkits/foo#show"
- match "/foo/bar" => "bukkits/foo#bar"
- end
- RUBY
-
- app_file "app/helpers/some_helper.rb", <<-RUBY
- module SomeHelper
- def something
- "Something... Something... Something..."
- end
- end
- RUBY
-
- @plugin.write "app/helpers/bar_helper.rb", <<-RUBY
- module BarHelper
- def bar
- "It's a bar."
- end
- end
- RUBY
-
- @plugin.write "app/controllers/bukkits/foo_controller.rb", <<-RUBY
- class Bukkits::FooController < ActionController::Base
- def index
- render :inline => "<%= something %>"
- end
-
- def show
- render :text => foo_path
- end
-
- def bar
- render :inline => "<%= bar %>"
- end
- end
- RUBY
-
- boot_rails
-
- get("/foo")
- assert_equal "Something... Something... Something...", last_response.body
-
- get("/foo/show")
- assert_equal "/foo", last_response.body
-
- get("/foo/bar")
- assert_equal "It's a bar.", last_response.body
- end
-
test "isolated engine should include only its own routes and helpers" do
@plugin.write "lib/bukkits.rb", <<-RUBY
module Bukkits
@@ -772,70 +584,6 @@ module RailtiesTest
assert_equal Bukkits::Engine.instance, Rails::Engine.find(engine_path)
end
- test "ensure that engine properly sets assets directories" do
- add_to_config("config.action_dispatch.show_exceptions = false")
- add_to_config("config.serve_static_assets = true")
-
- @plugin.write "lib/bukkits.rb", <<-RUBY
- module Bukkits
- class Engine < ::Rails::Engine
- isolate_namespace Bukkits
- end
- end
- RUBY
-
- @plugin.write "public/stylesheets/foo.css", ""
- @plugin.write "public/javascripts/foo.js", ""
-
- @plugin.write "app/views/layouts/bukkits/application.html.erb", <<-RUBY
- <%= stylesheet_link_tag :all %>
- <%= javascript_include_tag :all %>
- <%= yield %>
- RUBY
-
- @plugin.write "app/controllers/bukkits/home_controller.rb", <<-RUBY
- module Bukkits
- class HomeController < ActionController::Base
- def index
- render :text => "Good morning!", :layout => "bukkits/application"
- end
- end
- end
- RUBY
-
- @plugin.write "config/routes.rb", <<-RUBY
- Bukkits::Engine.routes.draw do
- match "/home" => "home#index"
- end
- RUBY
-
- app_file "config/routes.rb", <<-RUBY
- Rails.application.routes.draw do
- mount Bukkits::Engine => "/bukkits"
- end
- RUBY
-
- require 'rack/test'
- extend Rack::Test::Methods
-
- boot_rails
-
- require "#{rails_root}/config/environment"
-
- assert_equal File.join(@plugin.path, "public"), Bukkits::HomeController.assets_dir
- assert_equal File.join(@plugin.path, "public/stylesheets"), Bukkits::HomeController.stylesheets_dir
- assert_equal File.join(@plugin.path, "public/javascripts"), Bukkits::HomeController.javascripts_dir
-
- assert_equal File.join(app_path, "public"), ActionController::Base.assets_dir
- assert_equal File.join(app_path, "public/stylesheets"), ActionController::Base.stylesheets_dir
- assert_equal File.join(app_path, "public/javascripts"), ActionController::Base.javascripts_dir
-
- get "/bukkits/home"
-
- assert_match %r{bukkits/stylesheets/foo.css}, last_response.body
- assert_match %r{bukkits/javascripts/foo.js}, last_response.body
- end
-
private
def app
Rails.application
diff --git a/railties/test/railties/shared_tests.rb b/railties/test/railties/shared_tests.rb
index 3eb79d57c8..b2b18938ae 100644
--- a/railties/test/railties/shared_tests.rb
+++ b/railties/test/railties/shared_tests.rb
@@ -10,42 +10,16 @@ module RailtiesTest
@app ||= Rails.application
end
- def test_install_migrations_and_assets
- @plugin.write "public/javascripts/foo.js", "doSomething()"
+ def test_serving_sprockets_assets
+ @plugin.write "app/assets/javascripts/engine.js.coffee", "square = (x) -> x * x"
- @plugin.write "db/migrate/1_create_users.rb", <<-RUBY
- class CreateUsers < ActiveRecord::Migration
- end
- RUBY
-
- app_file "db/migrate/1_create_sessions.rb", <<-RUBY
- class CreateSessions < ActiveRecord::Migration
- end
- RUBY
-
- add_to_config "ActiveRecord::Base.timestamped_migrations = false"
-
- Dir.chdir(app_path) do
- `rake bukkits:install`
- assert File.exists?("#{app_path}/db/migrate/2_create_users.rb")
- assert File.exists?(app_path("public/bukkits/javascripts/foo.js"))
- end
- end
-
- def test_copying_assets
- @plugin.write "public/javascripts/foo.js", "doSomething()"
- @plugin.write "public/stylesheets/foo.css", "h1 { font-size: 10000px }"
- @plugin.write "public/images/img.png", ""
-
- Dir.chdir(app_path) do
- `rake bukkits:install:assets --trace`
+ boot_rails
+ require 'rack/test'
+ require 'coffee_script'
+ extend Rack::Test::Methods
- assert File.exists?(app_path("public/bukkits/javascripts/foo.js"))
- assert_equal "doSomething()\n", File.read(app_path("public/bukkits/javascripts/foo.js"))
- assert File.exists?(app_path("public/bukkits/stylesheets/foo.css"))
- assert_equal "h1 { font-size: 10000px }\n", File.read(app_path("public/bukkits/stylesheets/foo.css"))
- assert File.exists?(app_path("public/bukkits/images/img.png"))
- end
+ get "/assets/engine.js"
+ assert_match "square = function(x) {", last_response.body
end
def test_copying_migrations
diff --git a/tasks/release.rb b/tasks/release.rb
index a605fed160..01950b227d 100644
--- a/tasks/release.rb
+++ b/tasks/release.rb
@@ -61,6 +61,32 @@ directory "dist"
end
end
+namespace :changelog do
+ task :release_date do
+ FRAMEWORKS.each do |fw|
+ require 'date'
+ replace = '\1(' + Date.today.strftime('%B %d, %Y') + ')'
+ fname = File.join fw, 'CHANGELOG'
+
+ contents = File.read(fname).sub(/^([^(]*)\(unreleased\)/, replace)
+ File.open(fname, 'wb') { |f| f.write contents }
+ end
+ end
+
+ task :release_summary do
+ FRAMEWORKS.each do |fw|
+ puts "## #{fw}"
+ fname = File.join fw, 'CHANGELOG'
+ contents = File.readlines fname
+ contents.shift
+ changes = []
+ changes << contents.shift until contents.first =~ /^\*Rails \d+\.\d+\.\d+/
+ puts changes.reject { |change| change.strip.empty? }.join
+ puts
+ end
+ end
+end
+
namespace :all do
task :build => FRAMEWORKS.map { |f| "#{f}:build" } + ['rails:build']
task :install => FRAMEWORKS.map { |f| "#{f}:install" } + ['rails:install']