aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXavier Noria <fxn@hashref.com>2010-11-21 03:30:06 +0100
committerXavier Noria <fxn@hashref.com>2010-11-21 03:30:06 +0100
commit6af08ab6d835d1c2aa8648a0d3a4a86e678c3f52 (patch)
tree96bd668692b62d3829322232877b70c9011ce446
parentf326221c701e4f9d991e3eadcc793a73795fb218 (diff)
parent7c51d1fcf9dc504c2dfd6e7184bbe8186f09819d (diff)
downloadrails-6af08ab6d835d1c2aa8648a0d3a4a86e678c3f52.tar.gz
rails-6af08ab6d835d1c2aa8648a0d3a4a86e678c3f52.tar.bz2
rails-6af08ab6d835d1c2aa8648a0d3a4a86e678c3f52.zip
Merge branch 'master' of git://github.com/rails/rails
-rw-r--r--.gitignore1
-rw-r--r--Gemfile2
-rw-r--r--Rakefile56
-rw-r--r--actionmailer/lib/action_mailer/test_case.rb7
-rw-r--r--actionmailer/lib/action_mailer/version.rb4
-rw-r--r--actionpack/CHANGELOG2
-rw-r--r--actionpack/actionpack.gemspec2
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb8
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb3
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb47
-rw-r--r--actionpack/lib/action_controller/railties/paths.rb4
-rw-r--r--actionpack/lib/action_controller/test_case.rb6
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb4
-rw-r--r--actionpack/lib/action_dispatch/http/upload.rb10
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb3
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb29
-rw-r--r--actionpack/lib/action_pack/version.rb4
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb503
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb133
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb153
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb173
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb144
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb4
-rw-r--r--actionpack/lib/action_view/railtie.rb4
-rw-r--r--actionpack/lib/action_view/template.rb3
-rw-r--r--actionpack/test/abstract_unit.rb8
-rw-r--r--actionpack/test/activerecord/active_record_store_test.rb12
-rw-r--r--actionpack/test/controller/action_pack_assertions_test.rb8
-rw-r--r--actionpack/test/controller/routing_test.rb14
-rw-r--r--actionpack/test/dispatch/request_test.rb3
-rw-r--r--actionpack/test/dispatch/uploaded_file_test.rb12
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb49
-rw-r--r--actionpack/test/template/render_test.rb9
-rw-r--r--activemodel/activemodel.gemspec2
-rw-r--r--activemodel/lib/active_model/version.rb4
-rw-r--r--activemodel/test/cases/helper.rb6
-rw-r--r--activerecord/CHANGELOG27
-rw-r--r--activerecord/lib/active_record/association_preload.rb26
-rw-r--r--activerecord/lib/active_record/associations.rb13
-rw-r--r--activerecord/lib/active_record/associations/association_collection.rb31
-rw-r--r--activerecord/lib/active_record/associations/association_proxy.rb4
-rw-r--r--activerecord/lib/active_record/attribute_methods/dirty.rb3
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb6
-rw-r--r--activerecord/lib/active_record/base.rb43
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb5
-rw-r--r--activerecord/lib/active_record/migration.rb293
-rw-r--r--activerecord/lib/active_record/migration/command_recorder.rb91
-rw-r--r--activerecord/lib/active_record/named_scope.rb10
-rw-r--r--activerecord/lib/active_record/nested_attributes.rb13
-rw-r--r--activerecord/lib/active_record/reflection.rb20
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb39
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb5
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb28
-rw-r--r--activerecord/lib/active_record/schema.rb9
-rw-r--r--activerecord/lib/active_record/timestamp.rb8
-rw-r--r--activerecord/lib/active_record/version.rb4
-rw-r--r--activerecord/lib/rails/generators/active_record/migration/templates/migration.rb4
-rw-r--r--activerecord/lib/rails/generators/active_record/model/templates/migration.rb4
-rw-r--r--activerecord/lib/rails/generators/active_record/session_migration/templates/migration.rb4
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb3
-rw-r--r--activerecord/test/cases/associations/eager_test.rb52
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb21
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb4
-rw-r--r--activerecord/test/cases/associations_test.rb10
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb2
-rw-r--r--activerecord/test/cases/autosave_association_test.rb6
-rw-r--r--activerecord/test/cases/base_test.rb12
-rw-r--r--activerecord/test/cases/calculations_test.rb13
-rw-r--r--activerecord/test/cases/finder_test.rb2
-rw-r--r--activerecord/test/cases/fixtures_test.rb6
-rw-r--r--activerecord/test/cases/helper.rb5
-rw-r--r--activerecord/test/cases/invertible_migration_test.rb57
-rw-r--r--activerecord/test/cases/migration/command_recorder_test.rb108
-rw-r--r--activerecord/test/cases/migration_test.rb78
-rw-r--r--activerecord/test/cases/named_scope_test.rb2
-rw-r--r--activerecord/test/cases/persistence_test.rb3
-rw-r--r--activerecord/test/cases/query_cache_test.rb1
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb2
-rw-r--r--activerecord/test/cases/relations_test.rb46
-rw-r--r--activerecord/test/cases/timestamp_test.rb2
-rw-r--r--activerecord/test/cases/transactions_test.rb2
-rw-r--r--activerecord/test/cases/validations/association_validation_test.rb4
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb4
-rw-r--r--activeresource/lib/active_resource/base.rb8
-rw-r--r--activeresource/lib/active_resource/version.rb4
-rw-r--r--activeresource/test/cases/base_test.rb6
-rw-r--r--activesupport/lib/active_support/callbacks.rb22
-rw-r--r--activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb92
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_param.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/object/to_query.rb4
-rw-r--r--activesupport/lib/active_support/core_ext/string/output_safety.rb22
-rw-r--r--activesupport/lib/active_support/i18n.rb1
-rw-r--r--activesupport/lib/active_support/version.rb4
-rw-r--r--activesupport/lib/active_support/xml_mini/nokogirisax.rb3
-rw-r--r--activesupport/test/core_ext/class/class_inheritable_attributes_test.rb5
-rw-r--r--activesupport/test/core_ext/object/to_query_test.rb8
-rw-r--r--activesupport/test/core_ext/string_ext_test.rb69
-rw-r--r--railties/CHANGELOG4
-rw-r--r--railties/guides/assets/stylesheets/main.css2
-rw-r--r--railties/guides/rails_guides/helpers.rb9
-rw-r--r--railties/guides/source/action_controller_overview.textile2
-rw-r--r--railties/guides/source/action_mailer_basics.textile2
-rw-r--r--railties/guides/source/action_view_overview.textile2
-rw-r--r--railties/guides/source/active_record_querying.textile2
-rw-r--r--railties/guides/source/active_record_validations_callbacks.textile2
-rw-r--r--railties/guides/source/active_support_core_extensions.textile2
-rw-r--r--railties/guides/source/association_basics.textile2
-rw-r--r--railties/guides/source/caching_with_rails.textile1
-rw-r--r--railties/guides/source/command_line.textile3
-rw-r--r--railties/guides/source/configuring.textile2
-rw-r--r--railties/guides/source/contribute.textile2
-rw-r--r--railties/guides/source/contributing_to_rails.textile4
-rw-r--r--railties/guides/source/debugging_rails_applications.textile2
-rw-r--r--railties/guides/source/form_helpers.textile2
-rw-r--r--railties/guides/source/generators.textile2
-rw-r--r--railties/guides/source/getting_started.textile2
-rw-r--r--railties/guides/source/i18n.textile5
-rw-r--r--railties/guides/source/index.html.erb14
-rw-r--r--railties/guides/source/initialization.textile10
-rw-r--r--railties/guides/source/layout.html.erb25
-rw-r--r--railties/guides/source/layouts_and_rendering.textile2
-rw-r--r--railties/guides/source/migrations.textile2
-rw-r--r--railties/guides/source/performance_testing.textile2
-rw-r--r--railties/guides/source/plugins.textile2
-rw-r--r--railties/guides/source/rails_application_templates.textile2
-rw-r--r--railties/guides/source/rails_on_rack.textile2
-rw-r--r--railties/guides/source/routing.textile2
-rw-r--r--railties/guides/source/security.textile2
-rw-r--r--railties/guides/source/testing.textile2
-rw-r--r--railties/lib/rails/commands.rb17
-rw-r--r--railties/lib/rails/commands/plugin_new.rb6
-rw-r--r--railties/lib/rails/engine.rb5
-rw-r--r--railties/lib/rails/generators.rb1
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb2
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb2
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/index.html.erb2
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/new.html.erb2
-rw-r--r--railties/lib/rails/generators/erb/scaffold/templates/show.html.erb2
-rw-r--r--railties/lib/rails/generators/named_base.rb43
-rw-r--r--railties/lib/rails/generators/rails/plugin/plugin_generator.rb7
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/plugin_new_generator.rb17
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/app/controllers/%name%/application_controller.rb.tt2
-rw-r--r--railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb2
-rw-r--r--railties/lib/rails/test_help.rb11
-rw-r--r--railties/lib/rails/version.rb4
-rw-r--r--railties/test/application/generators_test.rb5
-rw-r--r--railties/test/application/initializers/frameworks_test.rb60
-rw-r--r--railties/test/application/rake_test.rb16
-rw-r--r--railties/test/generators/migration_generator_test.rb12
-rw-r--r--railties/test/generators/model_generator_test.rb6
-rw-r--r--railties/test/generators/plugin_generator_test.rb18
-rw-r--r--railties/test/generators/plugin_new_generator_test.rb7
-rw-r--r--railties/test/generators_test.rb6
-rw-r--r--railties/test/railties/engine_test.rb36
-rw-r--r--release.rb11
-rw-r--r--tasks/release.rb96
-rw-r--r--version.rb4
157 files changed, 2077 insertions, 1269 deletions
diff --git a/.gitignore b/.gitignore
index 2dfdf96e7f..a18fba3780 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@ activesupport/doc
activemodel/test/fixtures/fixture_database.sqlite3
actionpack/test/tmp
activesupport/test/fixtures/isolation_test
+dist
railties/test/500.html
railties/test/fixtures/tmp
railties/test/initializer/root/log
diff --git a/Gemfile b/Gemfile
index 3aaf9a6123..4e13331626 100644
--- a/Gemfile
+++ b/Gemfile
@@ -39,7 +39,7 @@ end
platforms :ruby do
gem 'json'
gem 'yajl-ruby'
- gem "nokogiri", ">= 1.4.3.1"
+ gem "nokogiri", ">= 1.4.4"
# AR
gem "sqlite3-ruby", "~> 1.3.1", :require => 'sqlite3'
diff --git a/Rakefile b/Rakefile
index 5065fb286b..8c72312f28 100644
--- a/Rakefile
+++ b/Rakefile
@@ -3,7 +3,16 @@ require 'rdoc'
require 'rake'
require 'rdoc/task'
-require 'rake/gempackagetask'
+require 'net/http'
+
+$:.unshift File.expand_path('..', __FILE__)
+require "tasks/release"
+
+desc "Build gem files for all projects"
+task :build => "all:build"
+
+desc "Release all gems to gemcutter and create a tag"
+task :release => "all:release"
# RDoc skips some files in the Rails tree due to its binary? predicate. This is a quick
# hack for edge docs, until we decide which is the correct way to address this issue.
@@ -54,27 +63,6 @@ task :smoke do
system %(cd activerecord && #{$0} sqlite3:isolated_test)
end
-spec = eval(File.read('rails.gemspec'))
-Rake::GemPackageTask.new(spec) do |pkg|
- pkg.gem_spec = spec
-end
-
-desc "Release all gems to gemcutter. Package rails, package & push components, then push rails"
-task :release => :release_projects do
- require 'rake/gemcutter'
- Rake::Gemcutter::Tasks.new(spec).define
- Rake::Task['gem:push'].invoke
-end
-
-desc "Release all components to gemcutter."
-task :release_projects => :package do
- errors = []
- PROJECTS.each do |project|
- system(%(cd #{project} && #{$0} release)) || errors << project
- end
- fail("Errors in #{errors.join(', ')}") unless errors.empty?
-end
-
desc "Install gems for all projects."
task :install => :gem do
version = File.read("RAILS_VERSION").strip
@@ -172,3 +160,27 @@ task :update_versions do
end
end
end
+
+#
+# We have a webhook configured in Github that gets invoked after pushes.
+# This hook triggers the following tasks:
+#
+# * updates the local checkout
+# * updates Rails Contributors
+# * generates and publishes edge docs
+# * if there's a new stable tag, generates and publishes stable docs
+#
+# Everything is automated and you do NOT need to run this task normally.
+#
+# We publish a new version by tagging, and pushing a tag does not trigger
+# that webhook. Stable docs would be updated by any subsequent regular
+# push, but if you want that to happen right away just run this.
+#
+desc 'Publishes docs, run this AFTER a new stable tag has been pushed'
+task :publish_docs do
+ Net::HTTP.new('rails-hooks.hashref.com').start do |http|
+ request = Net::HTTP::Post.new('/rails-master-hook')
+ response = http.request(request)
+ puts response.body
+ end
+end
diff --git a/actionmailer/lib/action_mailer/test_case.rb b/actionmailer/lib/action_mailer/test_case.rb
index f4d1bb59f1..63e18147f6 100644
--- a/actionmailer/lib/action_mailer/test_case.rb
+++ b/actionmailer/lib/action_mailer/test_case.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/class/attribute'
+
module ActionMailer
class NonInferrableMailerError < ::StandardError
def initialize(name)
@@ -15,11 +17,11 @@ module ActionMailer
module ClassMethods
def tests(mailer)
- write_inheritable_attribute(:mailer_class, mailer)
+ self._mailer_class = mailer
end
def mailer_class
- if mailer = read_inheritable_attribute(:mailer_class)
+ if mailer = self._mailer_class
mailer
else
tests determine_default_mailer(name)
@@ -65,6 +67,7 @@ module ActionMailer
end
included do
+ class_attribute :_mailer_class
setup :initialize_test_deliveries
setup :set_expected_mail
end
diff --git a/actionmailer/lib/action_mailer/version.rb b/actionmailer/lib/action_mailer/version.rb
index 7dd3d401b1..27eb6c2b9b 100644
--- a/actionmailer/lib/action_mailer/version.rb
+++ b/actionmailer/lib/action_mailer/version.rb
@@ -3,8 +3,8 @@ module ActionMailer
MAJOR = 3
MINOR = 1
TINY = 0
- BUILD = "beta"
+ PRE = "beta"
- STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
end
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG
index db02beee35..74d017cc3d 100644
--- a/actionpack/CHANGELOG
+++ b/actionpack/CHANGELOG
@@ -1,5 +1,7 @@
*Rails 3.1.0 (unreleased)*
+* Added config.action_controller.include_all_helpers. By default 'helper :all' is done in ActionController::Base, which includes all the helpers by default. Setting include_all_helpers to false will result in including only application_helper and helper corresponding to controller (like foo_helper for foo_controller). [Piotr Sarnacki]
+
* Added a convenience idiom to generate HTML5 data-* attributes in tag helpers from a :data hash of options:
tag("div", :data => {:name => 'Stephen', :city_state => %w(Chicago IL)})
diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec
index c2af3dfaf5..44967631ff 100644
--- a/actionpack/actionpack.gemspec
+++ b/actionpack/actionpack.gemspec
@@ -22,7 +22,7 @@ Gem::Specification.new do |s|
s.add_dependency('activesupport', version)
s.add_dependency('activemodel', version)
s.add_dependency('rack-cache', '~> 0.5.3')
- s.add_dependency('builder', '~> 2.1.2')
+ s.add_dependency('builder', '~> 3.0.0')
s.add_dependency('i18n', '~> 0.4.2')
s.add_dependency('rack', '~> 1.2.1')
s.add_dependency('rack-test', '~> 0.5.6')
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 475785a4b4..91b75273fa 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -119,7 +119,7 @@ module AbstractController
controller_path
end
- private
+ private
# This method should return a hash with assigns.
# You can overwrite this configuration per controller.
@@ -128,7 +128,7 @@ module AbstractController
hash = {}
variables = instance_variable_names
variables -= protected_instance_variables if respond_to?(:protected_instance_variables)
- variables.each { |name| hash[name.to_s[1..-1]] = instance_variable_get(name) }
+ variables.each { |name| hash[name.to_s[1, name.length]] = instance_variable_get(name) }
hash
end
@@ -138,13 +138,13 @@ module AbstractController
case action
when NilClass
when Hash
- options, action = action, nil
+ options = action
when String, Symbol
action = action.to_s
key = action.include?(?/) ? :file : :action
options[key] = action
else
- options.merge!(:partial => action)
+ options[:partial] = action
end
options
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index d14831b763..91a88ab68a 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -53,8 +53,9 @@ module ActionController
include AbstractController::Helpers
included do
- config_accessor :helpers_path
+ config_accessor :helpers_path, :include_all_helpers
self.helpers_path ||= []
+ self.include_all_helpers = true
end
module ClassMethods
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index e524e546ad..14cc547dd0 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -20,36 +20,35 @@ module ActionController
private
- # Normalize arguments by catching blocks and setting them on :update.
- def _normalize_args(action=nil, options={}, &blk) #:nodoc:
- options = super
- options[:update] = blk if block_given?
- options
- end
-
- # Normalize both text and status options.
- def _normalize_options(options) #:nodoc:
- if options.key?(:text) && options[:text].respond_to?(:to_text)
- options[:text] = options[:text].to_text
- end
+ # Normalize arguments by catching blocks and setting them on :update.
+ def _normalize_args(action=nil, options={}, &blk) #:nodoc:
+ options = super
+ options[:update] = blk if block_given?
+ options
+ end
- if options[:status]
- options[:status] = Rack::Utils.status_code(options[:status])
- end
+ # Normalize both text and status options.
+ def _normalize_options(options) #:nodoc:
+ if options.key?(:text) && options[:text].respond_to?(:to_text)
+ options[:text] = options[:text].to_text
+ end
- super
+ if options[:status]
+ options[:status] = Rack::Utils.status_code(options[:status])
end
- # Process controller specific options, as status, content-type and location.
- def _process_options(options) #:nodoc:
- status, content_type, location = options.values_at(:status, :content_type, :location)
+ super
+ end
- self.status = status if status
- self.content_type = content_type if content_type
- self.headers["Location"] = url_for(location) if location
+ # Process controller specific options, as status, content-type and location.
+ def _process_options(options) #:nodoc:
+ status, content_type, location = options.values_at(:status, :content_type, :location)
- super
- end
+ self.status = status if status
+ self.content_type = content_type if content_type
+ self.headers["Location"] = url_for(location) if location
+ super
+ end
end
end
diff --git a/actionpack/lib/action_controller/railties/paths.rb b/actionpack/lib/action_controller/railties/paths.rb
index 7a59d4f2f3..699c44c62c 100644
--- a/actionpack/lib/action_controller/railties/paths.rb
+++ b/actionpack/lib/action_controller/railties/paths.rb
@@ -13,7 +13,9 @@ module ActionController
end
klass.helpers_path = paths
- klass.helper :all if klass.superclass == ActionController::Base
+ if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers
+ klass.helper :all
+ end
end
end
end
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 2b2f647d32..0f43527a56 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -1,6 +1,7 @@
require 'rack/session/abstract/id'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/to_query'
+require 'active_support/core_ext/class/attribute'
module ActionController
module TemplateAssertions
@@ -325,11 +326,11 @@ module ActionController
def controller_class=(new_class)
prepare_controller_class(new_class) if new_class
- write_inheritable_attribute(:controller_class, new_class)
+ self._controller_class = new_class
end
def controller_class
- if current_controller_class = read_inheritable_attribute(:controller_class)
+ if current_controller_class = self._controller_class
current_controller_class
else
self.controller_class = determine_default_controller_class(name)
@@ -442,6 +443,7 @@ module ActionController
included do
include ActionController::TemplateAssertions
include ActionDispatch::Assertions
+ class_attribute :_controller_class
setup :setup_controller_request_and_response
end
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
index dceddb9b80..3e5d23b5c1 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
@@ -1,5 +1,5 @@
require 'set'
-require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/class/attribute'
module HTML
class Sanitizer
@@ -60,7 +60,7 @@ module HTML
class WhiteListSanitizer < Sanitizer
[:protocol_separator, :uri_attributes, :allowed_attributes, :allowed_tags, :allowed_protocols, :bad_tags,
:allowed_css_properties, :allowed_css_keywords, :shorthand_css_properties].each do |attr|
- class_inheritable_accessor attr, :instance_writer => false
+ class_attribute attr, :instance_writer => false
end
# A regular expression of the valid characters used to separate protocols like
diff --git a/actionpack/lib/action_dispatch/http/upload.rb b/actionpack/lib/action_dispatch/http/upload.rb
index 84e58d7d6a..37effade4f 100644
--- a/actionpack/lib/action_dispatch/http/upload.rb
+++ b/actionpack/lib/action_dispatch/http/upload.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/object/blank'
-
module ActionDispatch
module Http
class UploadedFile
@@ -13,6 +11,14 @@ module ActionDispatch
raise(ArgumentError, ':tempfile is required') unless @tempfile
end
+ def open
+ @tempfile.open
+ end
+
+ def path
+ @tempfile.path
+ end
+
def read(*args)
@tempfile.read(*args)
end
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 4da82f958a..9c9eed2c6d 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -2,6 +2,7 @@ module ActionDispatch
module Http
module URL
mattr_accessor :tld_length
+ self.tld_length = 1
# Returns the complete URL used for this request.
def url
@@ -67,7 +68,7 @@ module ActionDispatch
# Returns the \domain part of a \host, such as "rubyonrails.org" in "www.rubyonrails.org". You can specify
# a different <tt>tld_length</tt>, such as 2 to catch rubyonrails.co.uk in "www.rubyonrails.co.uk".
- def domain(tld_length = 1)
+ def domain(tld_length = @@tld_length)
return nil unless named_host?(host)
host.split('.').last(1 + tld_length).join('.')
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 63a22ad105..879ccc5791 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -66,6 +66,18 @@ module ActionDispatch
end
@options.merge!(default_controller_and_action(to_shorthand))
+
+ requirements.each do |name, requirement|
+ # segment_keys.include?(k.to_s) || k == :controller
+ next unless Regexp === requirement && !constraints[name]
+
+ if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
+ raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
+ end
+ if requirement.multiline?
+ raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
+ end
+ end
end
# match "account/overview"
@@ -113,15 +125,6 @@ module ActionDispatch
@requirements ||= (@options[:constraints].is_a?(Hash) ? @options[:constraints] : {}).tap do |requirements|
requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints]
@options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) }
-
- requirements.each do |_, requirement|
- if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
- raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
- end
- if requirement.multiline?
- raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
- end
- end
end
end
@@ -164,10 +167,10 @@ module ActionDispatch
raise ArgumentError, "missing :action"
end
- { :controller => controller, :action => action }.tap do |hash|
- hash.delete(:controller) if hash[:controller].blank?
- hash.delete(:action) if hash[:action].blank?
- end
+ hash = {}
+ hash[:controller] = controller unless controller.blank?
+ hash[:action] = action unless action.blank?
+ hash
end
end
diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb
index 73e16daa29..170ceb299a 100644
--- a/actionpack/lib/action_pack/version.rb
+++ b/actionpack/lib/action_pack/version.rb
@@ -3,8 +3,8 @@ module ActionPack
MAJOR = 3
MINOR = 1
TINY = 0
- BUILD = "beta"
+ PRE = "beta"
- STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index db5f5f301a..f6b2d4f3f4 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -1,10 +1,6 @@
-require 'thread'
-require 'cgi'
-require 'action_view/helpers/url_helper'
-require 'action_view/helpers/tag_helper'
-require 'active_support/core_ext/file'
-require 'active_support/core_ext/object/blank'
-require 'active_support/core_ext/string/output_safety'
+require 'action_view/helpers/asset_tag_helpers/javascript_tag_helpers'
+require 'action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers'
+require 'action_view/helpers/asset_tag_helpers/asset_paths'
module ActionView
# = Action View Asset Tag Helpers
@@ -195,20 +191,8 @@ module ActionView
# RewriteEngine On
# RewriteRule ^/release-\d+/(images|javascripts|stylesheets)/(.*)$ /$1/$2 [L]
module AssetTagHelper
- mattr_reader :javascript_expansions
- @@javascript_expansions = { }
-
- mattr_reader :stylesheet_expansions
- @@stylesheet_expansions = {}
-
- # You can enable or disable the asset tag timestamps cache.
- # With the cache enabled, the asset tag helper methods will make fewer
- # expensive file system calls. However this prevents you from modifying
- # any asset files while the server is running.
- #
- # ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
- mattr_accessor :cache_asset_timestamps
-
+ include JavascriptTagHelpers
+ include StylesheetTagHelpers
# Returns a link tag that browsers and news readers can use to auto-detect
# an RSS or ATOM feed. The +type+ can either be <tt>:rss</tt> (default) or
# <tt>:atom</tt>. Control the link options in url_for format using the
@@ -242,260 +226,6 @@ module ActionView
)
end
- # Computes the path to a javascript asset in the public javascripts directory.
- # If the +source+ filename has no extension, .js will be appended (except for explicit URIs)
- # Full paths from the document root will be passed through.
- # Used internally by javascript_include_tag to build the script path.
- #
- # ==== Examples
- # javascript_path "xmlhr" # => /javascripts/xmlhr.js
- # javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js
- # javascript_path "/dir/xmlhr" # => /dir/xmlhr.js
- # 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)
- compute_public_path(source, 'javascripts', 'js')
- 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.
- #
- # ==== Examples
- # 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 "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.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>
- #
- # * = 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:
- #
- # 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>
- #
- # Note that the default javascript files will be included first. So Prototype and Scriptaculous are 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>:
- #
- # javascript_include_tag :all, :recursive => true
- #
- # == 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).
- #
- # ==== 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>
- #
- # 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>
- #
- # javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when config.perform_caching is true =>
- # <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)
- options = sources.extract_options!.stringify_keys
- concat = options.delete("concat")
- cache = concat || options.delete("cache")
- recursive = options.delete("recursive")
-
- if concat || (config.perform_caching && cache)
- joined_javascript_name = (cache == true ? "all" : cache) + ".js"
- joined_javascript_path = File.join(joined_javascript_name[/^#{File::SEPARATOR}/] ? config.assets_dir : config.javascripts_dir, joined_javascript_name)
-
- unless config.perform_caching && File.exists?(joined_javascript_path)
- write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive))
- end
- javascript_src_tag(joined_javascript_name, options)
- else
- sources = expand_javascript_sources(sources, recursive)
- ensure_javascript_sources!(sources) if cache
- sources.collect { |source| javascript_src_tag(source, options) }.join("\n").html_safe
- end
- end
-
- # Register one or more javascript files to be included when <tt>symbol</tt>
- # is passed to <tt>javascript_include_tag</tt>. This method is typically intended
- # to be called from plugin initialization to register javascript files
- # that the plugin installed in <tt>public/javascripts</tt>.
- #
- # ActionView::Helpers::AssetTagHelper.register_javascript_expansion :monkey => ["head", "body", "tail"]
- #
- # javascript_include_tag :monkey # =>
- # <script type="text/javascript" src="/javascripts/head.js"></script>
- # <script type="text/javascript" src="/javascripts/body.js"></script>
- # <script type="text/javascript" src="/javascripts/tail.js"></script>
- def self.register_javascript_expansion(expansions)
- @@javascript_expansions.merge!(expansions)
- end
-
- # Register one or more stylesheet files to be included when <tt>symbol</tt>
- # is passed to <tt>stylesheet_link_tag</tt>. This method is typically intended
- # to be called from plugin initialization to register stylesheet files
- # that the plugin installed in <tt>public/stylesheets</tt>.
- #
- # ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion :monkey => ["head", "body", "tail"]
- #
- # stylesheet_link_tag :monkey # =>
- # <link href="/stylesheets/head.css" media="screen" rel="stylesheet" type="text/css" />
- # <link href="/stylesheets/body.css" media="screen" rel="stylesheet" type="text/css" />
- # <link href="/stylesheets/tail.css" media="screen" rel="stylesheet" type="text/css" />
- def self.register_stylesheet_expansion(expansions)
- @@stylesheet_expansions.merge!(expansions)
- end
-
- # Computes the path to a stylesheet asset in the public stylesheets directory.
- # If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
- # Full paths from the document root will be passed through.
- # Used internally by +stylesheet_link_tag+ to build the stylesheet path.
- #
- # ==== Examples
- # stylesheet_path "style" # => /stylesheets/style.css
- # stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css
- # stylesheet_path "/dir/style.css" # => /dir/style.css
- # 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)
- compute_public_path(source, 'stylesheets', 'css')
- end
- alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
-
- # Returns a stylesheet link tag for the sources specified as arguments. If
- # you don't specify an extension, <tt>.css</tt> will be appended automatically.
- # You can modify the link attributes by passing a hash as the last argument.
- #
- # ==== Examples
- # stylesheet_link_tag "style" # =>
- # <link href="/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />
- #
- # stylesheet_link_tag "style.css" # =>
- # <link href="/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />
- #
- # stylesheet_link_tag "http://www.railsapplication.com/style.css" # =>
- # <link href="http://www.railsapplication.com/style.css" media="screen" rel="stylesheet" type="text/css" />
- #
- # stylesheet_link_tag "style", :media => "all" # =>
- # <link href="/stylesheets/style.css" media="all" rel="stylesheet" type="text/css" />
- #
- # stylesheet_link_tag "style", :media => "print" # =>
- # <link href="/stylesheets/style.css" media="print" rel="stylesheet" type="text/css" />
- #
- # stylesheet_link_tag "random.styles", "/css/stylish" # =>
- # <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" type="text/css" />
- # <link href="/css/stylish.css" media="screen" rel="stylesheet" type="text/css" />
- #
- # You can also include all styles in the stylesheets directory using <tt>:all</tt> as the source:
- #
- # stylesheet_link_tag :all # =>
- # <link href="/stylesheets/style1.css" media="screen" rel="stylesheet" type="text/css" />
- # <link href="/stylesheets/styleB.css" media="screen" rel="stylesheet" type="text/css" />
- # <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" type="text/css" />
- #
- # If you want Rails to search in all the subdirectories under stylesheets, you should explicitly set <tt>:recursive</tt>:
- #
- # stylesheet_link_tag :all, :recursive => true
- #
- # == Caching multiple stylesheets into one
- #
- # You can also cache multiple stylesheets into one file, which requires less HTTP connections and can better be
- # compressed by gzip (leading to faster transfers). Caching will only happen if config.perform_caching
- # is set to true (which is the case by default for the Rails production environment, but not for the development
- # environment). Examples:
- #
- # ==== Examples
- # stylesheet_link_tag :all, :cache => true # when config.perform_caching is false =>
- # <link href="/stylesheets/style1.css" media="screen" rel="stylesheet" type="text/css" />
- # <link href="/stylesheets/styleB.css" media="screen" rel="stylesheet" type="text/css" />
- # <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" type="text/css" />
- #
- # stylesheet_link_tag :all, :cache => true # when config.perform_caching is true =>
- # <link href="/stylesheets/all.css" media="screen" rel="stylesheet" type="text/css" />
- #
- # stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when config.perform_caching is false =>
- # <link href="/stylesheets/shop.css" media="screen" rel="stylesheet" type="text/css" />
- # <link href="/stylesheets/cart.css" media="screen" rel="stylesheet" type="text/css" />
- # <link href="/stylesheets/checkout.css" media="screen" rel="stylesheet" type="text/css" />
- #
- # stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when config.perform_caching is true =>
- # <link href="/stylesheets/payment.css" media="screen" rel="stylesheet" type="text/css" />
- #
- # The <tt>:recursive</tt> option is also available for caching:
- #
- # stylesheet_link_tag :all, :cache => true, :recursive => true
- #
- # To force concatenation (even in development mode) set <tt>:concat</tt> to true. This is useful if
- # you have too many stylesheets for IE to load.
- #
- # stylesheet_link_tag :all, :concat => true
- #
- def stylesheet_link_tag(*sources)
- options = sources.extract_options!.stringify_keys
- concat = options.delete("concat")
- cache = concat || options.delete("cache")
- recursive = options.delete("recursive")
-
- if concat || (config.perform_caching && cache)
- joined_stylesheet_name = (cache == true ? "all" : cache) + ".css"
- joined_stylesheet_path = File.join(joined_stylesheet_name[/^#{File::SEPARATOR}/] ? config.assets_dir : config.stylesheets_dir, joined_stylesheet_name)
-
- unless config.perform_caching && File.exists?(joined_stylesheet_path)
- write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive))
- end
- stylesheet_tag(joined_stylesheet_name, options)
- else
- sources = expand_stylesheet_sources(sources, recursive)
- ensure_stylesheet_sources!(sources) if cache
- sources.collect { |source| stylesheet_tag(source, options) }.join("\n").html_safe
- end
- end
-
# Web browsers cache favicons. If you just throw a <tt>favicon.ico</tt> into the document
# root of your application and it changes later, clients that have it in their cache
# won't see the update. Using this helper prevents that because it appends an asset ID:
@@ -544,7 +274,7 @@ 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)
- compute_public_path(source, 'images')
+ asset_paths.compute_public_path(source, 'images')
end
alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
@@ -559,7 +289,7 @@ 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)
- compute_public_path(source, 'videos')
+ asset_paths.compute_public_path(source, 'videos')
end
alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
@@ -569,12 +299,12 @@ module ActionView
#
# ==== Examples
# audio_path("horse") # => /audios/horse
- # audio_path("horse.wav") # => /audios/horse.avi
- # audio_path("sounds/horse.wav") # => /audios/sounds/horse.avi
- # audio_path("/sounds/horse.wav") # => /sounds/horse.avi
+ # audio_path("horse.wav") # => /audios/horse.wav
+ # audio_path("sounds/horse.wav") # => /audios/sounds/horse.wav
+ # 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)
- compute_public_path(source, 'audios')
+ asset_paths.compute_public_path(source, 'audios')
end
alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
@@ -703,215 +433,8 @@ module ActionView
private
- def rewrite_extension(source, dir, ext)
- source_ext = File.extname(source)
-
- if source_ext.empty?
- "#{source}.#{ext}"
- elsif ext != source_ext[1..-1]
- with_ext = "#{source}.#{ext}"
- with_ext if File.exist?(File.join(config.assets_dir, dir, with_ext))
- end || source
- end
-
- def rewrite_host_and_protocol(source, has_request)
- host = compute_asset_host(source)
- if has_request && host && !is_uri?(host)
- host = "#{controller.request.protocol}#{host}"
- end
- "#{host}#{source}"
- end
-
- def rewrite_relative_url_root(source, relative_url_root)
- relative_url_root && !source.starts_with?("#{relative_url_root}/") ? "#{relative_url_root}#{source}" : source
- end
-
- # Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
- # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
- # 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)
- 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)
-
- 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
-
- source
- end
-
- def is_uri?(path)
- path =~ %r{^[-a-z]+://|^cid:}
- end
-
- # Pick an asset host for this source. Returns +nil+ if no host is set,
- # the host if no wildcard is set, the host interpolated with the
- # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
- # or the value returned from invoking the proc if it's a proc or the value from
- # invoking call if it's an object responding to call.
- def compute_asset_host(source)
- if host = config.asset_host
- if host.is_a?(Proc) || host.respond_to?(:call)
- case host.is_a?(Proc) ? host.arity : host.method(:call).arity
- when 2
- request = controller.respond_to?(:request) && controller.request
- host.call(source, request)
- else
- host.call(source)
- end
- else
- (host =~ /%d/) ? host % (source.hash % 4) : host
- end
- end
- end
-
- @@asset_timestamps_cache = {}
- @@asset_timestamps_cache_guard = Mutex.new
-
- # Use the RAILS_ASSET_ID environment variable or the source's
- # modification time as its cache-busting asset id.
- def rails_asset_id(source)
- if asset_id = ENV["RAILS_ASSET_ID"]
- asset_id
- else
- if @@cache_asset_timestamps && (asset_id = @@asset_timestamps_cache[source])
- asset_id
- else
- path = File.join(config.assets_dir, source)
- asset_id = File.exist?(path) ? File.mtime(path).to_i.to_s : ''
-
- if @@cache_asset_timestamps
- @@asset_timestamps_cache_guard.synchronize do
- @@asset_timestamps_cache[source] = asset_id
- end
- end
-
- asset_id
- end
- end
- end
-
- # 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)
- if path && path.respond_to?(:call)
- return path.call(source)
- elsif path && path.is_a?(String)
- return path % [source]
- end
-
- asset_id = rails_asset_id(source)
- if asset_id.empty?
- source
- else
- "#{source}?#{asset_id}"
- end
- end
-
- def javascript_src_tag(source, options)
- content_tag("script", "", { "type" => Mime::JS, "src" => path_to_javascript(source) }.merge(options))
- end
-
- def stylesheet_tag(source, options)
- tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => ERB::Util.html_escape(path_to_stylesheet(source)) }.merge(options), false, false)
- end
-
- def compute_javascript_paths(*args)
- expand_javascript_sources(*args).collect { |source| compute_public_path(source, 'javascripts', 'js', false) }
- end
-
- def compute_stylesheet_paths(*args)
- expand_stylesheet_sources(*args).collect { |source| compute_public_path(source, 'stylesheets', 'css', false) }
- end
-
- def expand_javascript_sources(sources, recursive = false)
- if sources.include?(:all)
- all_javascript_files = (collect_asset_files(config.javascripts_dir, ('**' if recursive), '*.js') - ['application']) << 'application'
- ((determine_source(:defaults, @@javascript_expansions).dup & all_javascript_files) + all_javascript_files).uniq
- else
- expanded_sources = sources.collect do |source|
- determine_source(source, @@javascript_expansions)
- end.flatten
- expanded_sources << "application" if sources.include?(:defaults) && File.exist?(File.join(config.javascripts_dir, "application.js"))
- expanded_sources
- end
- end
-
- def expand_stylesheet_sources(sources, recursive)
- if sources.first == :all
- collect_asset_files(config.stylesheets_dir, ('**' if recursive), '*.css')
- else
- sources.collect do |source|
- determine_source(source, @@stylesheet_expansions)
- end.flatten
- end
- end
-
- def determine_source(source, collection)
- case source
- when Symbol
- collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}")
- else
- source
- end
- end
-
- def ensure_stylesheet_sources!(sources)
- sources.each do |source|
- asset_file_path!(path_to_stylesheet(source))
- end
- return sources
- end
-
- def ensure_javascript_sources!(sources)
- sources.each do |source|
- asset_file_path!(path_to_javascript(source))
- end
- return sources
- end
-
- def join_asset_file_contents(paths)
- paths.collect { |path| File.read(asset_file_path!(path, true)) }.join("\n\n")
- end
-
- def write_asset_file_contents(joined_asset_path, asset_paths)
-
- FileUtils.mkdir_p(File.dirname(joined_asset_path))
- File.atomic_write(joined_asset_path) { |cache| cache.write(join_asset_file_contents(asset_paths)) }
-
- # Set mtime to the latest of the combined files to allow for
- # consistent ETag without a shared filesystem.
- mt = asset_paths.map { |p| File.mtime(asset_file_path(p)) }.max
- File.utime(mt, mt, joined_asset_path)
- end
-
- def asset_file_path(path)
- File.join(config.assets_dir, path.split('?').first)
- end
-
- def asset_file_path!(path, error_if_file_is_uri = false)
- if is_uri?(path)
- raise(Errno::ENOENT, "Asset file #{path} is uri and cannot be merged into single file") if error_if_file_is_uri
- else
- absolute_path = asset_file_path(path)
- raise(Errno::ENOENT, "Asset file not found at '#{absolute_path}'" ) unless File.exist?(absolute_path)
- return absolute_path
- end
- end
-
- def collect_asset_files(*path)
- dir = path.first
-
- Dir[File.join(*path.compact)].collect do |file|
- file[-(file.size - dir.size - 1)..-1].sub(/\.\w+$/, '')
- end.sort
+ def asset_paths
+ @asset_paths ||= AssetPaths.new(config, controller)
end
end
end
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
new file mode 100644
index 0000000000..15f8e10b7f
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb
@@ -0,0 +1,133 @@
+require 'active_support/core_ext/class/attribute'
+require 'active_support/core_ext/string/inflections'
+require 'active_support/core_ext/file'
+require 'action_view/helpers/tag_helper'
+
+module ActionView
+ module Helpers
+ module AssetTagHelper
+
+ class AssetIncludeTag
+ attr_reader :config, :asset_paths
+
+ class_attribute :expansions
+ self.expansions = { }
+
+ def initialize(config, asset_paths)
+ @config = config
+ @asset_paths = asset_paths
+ end
+
+ def asset_name
+ raise NotImplementedError
+ end
+
+ def extension
+ raise NotImplementedError
+ end
+
+ def custom_dir
+ raise NotImplementedError
+ end
+
+ def asset_tag(source, options)
+ raise NotImplementedError
+ end
+
+ def include_tag(*sources)
+ options = sources.extract_options!.stringify_keys
+ concat = options.delete("concat")
+ cache = concat || options.delete("cache")
+ recursive = options.delete("recursive")
+
+ if concat || (config.perform_caching && cache)
+ joined_name = (cache == true ? "all" : cache) + ".#{extension}"
+ joined_path = File.join((joined_name[/^#{File::SEPARATOR}/] ? config.assets_dir : custom_dir), joined_name)
+ unless config.perform_caching && File.exists?(joined_path)
+ write_asset_file_contents(joined_path, compute_paths(sources, recursive))
+ end
+ asset_tag(joined_name, options)
+ else
+ sources = expand_sources(sources, recursive)
+ ensure_sources!(sources) if cache
+ sources.collect { |source| asset_tag(source, options) }.join("\n").html_safe
+ end
+ end
+
+
+ private
+
+ def path_to_asset(source, include_host = true)
+ asset_paths.compute_public_path(source, asset_name.to_s.pluralize, extension, include_host)
+ end
+
+ def compute_paths(*args)
+ expand_sources(*args).collect { |source| asset_paths.compute_public_path(source, asset_name.pluralize, extension, false) }
+ end
+
+ def expand_sources(sources, recursive)
+ if sources.first == :all
+ collect_asset_files(custom_dir, ('**' if recursive), "*.#{extension}")
+ else
+ sources.collect do |source|
+ determine_source(source, expansions)
+ end.flatten
+ end
+ end
+
+ def ensure_sources!(sources)
+ sources.each do |source|
+ asset_file_path!(path_to_asset(source, false))
+ end
+ return sources
+ end
+
+ def collect_asset_files(*path)
+ dir = path.first
+
+ Dir[File.join(*path.compact)].collect do |file|
+ file[-(file.size - dir.size - 1)..-1].sub(/\.\w+$/, '')
+ end.sort
+ end
+
+ def determine_source(source, collection)
+ case source
+ when Symbol
+ collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}")
+ else
+ source
+ end
+ end
+
+ def join_asset_file_contents(paths)
+ paths.collect { |path| File.read(asset_file_path!(path, true)) }.join("\n\n")
+ end
+
+ def write_asset_file_contents(joined_asset_path, asset_paths)
+ FileUtils.mkdir_p(File.dirname(joined_asset_path))
+ File.atomic_write(joined_asset_path) { |cache| cache.write(join_asset_file_contents(asset_paths)) }
+
+ # Set mtime to the latest of the combined files to allow for
+ # consistent ETag without a shared filesystem.
+ mt = asset_paths.map { |p| File.mtime(asset_file_path(p)) }.max
+ File.utime(mt, mt, joined_asset_path)
+ end
+
+ def asset_file_path(path)
+ File.join(config.assets_dir, path.split('?').first)
+ end
+
+ def asset_file_path!(path, error_if_file_is_uri = false)
+ if asset_paths.is_uri?(path)
+ raise(Errno::ENOENT, "Asset file #{path} is uri and cannot be merged into single file") if error_if_file_is_uri
+ else
+ absolute_path = asset_file_path(path)
+ raise(Errno::ENOENT, "Asset file not found at '#{absolute_path}'" ) unless File.exist?(absolute_path)
+ return absolute_path
+ end
+ end
+ end
+
+ end
+ end
+end
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
new file mode 100644
index 0000000000..b4e61f2034
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
@@ -0,0 +1,153 @@
+require 'active_support/core_ext/file'
+
+module ActionView
+ module Helpers
+ module AssetTagHelper
+
+ class AssetPaths
+ # You can enable or disable the asset tag ids cache.
+ # With the cache enabled, the asset tag helper methods will make fewer
+ # expensive file system calls (the default implementation checks the file
+ # system timestamp). However this prevents you from modifying any asset
+ # files while the server is running.
+ #
+ # ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids = false
+ mattr_accessor :cache_asset_ids
+
+ attr_reader :config, :controller
+
+ def initialize(config, controller)
+ @config = config
+ @controller = controller
+ end
+
+ # Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
+ # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
+ # 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)
+ 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)
+
+ 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
+
+ source
+ end
+
+ # Add or change an asset id in the asset id cache. This can be used
+ # for SASS on Heroku.
+ # :api: public
+ def add_to_asset_ids_cache(source, asset_id)
+ self.asset_ids_cache_guard.synchronize do
+ self.asset_ids_cache[source] = asset_id
+ end
+ end
+
+ def is_uri?(path)
+ path =~ %r{^[-a-z]+://|^cid:}
+ end
+
+ private
+
+ def rewrite_extension(source, dir, ext)
+ source_ext = File.extname(source)
+
+ source_with_ext = if source_ext.empty?
+ "#{source}.#{ext}"
+ elsif ext != source_ext[1..-1]
+ with_ext = "#{source}.#{ext}"
+ with_ext if File.exist?(File.join(config.assets_dir, dir, with_ext))
+ end
+
+ source_with_ext || source
+ end
+
+ # 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)
+ if path && path.respond_to?(:call)
+ return path.call(source)
+ elsif path && path.is_a?(String)
+ return path % [source]
+ end
+
+ asset_id = rails_asset_id(source)
+ if asset_id.empty?
+ source
+ else
+ "#{source}?#{asset_id}"
+ end
+ end
+
+ mattr_accessor :asset_ids_cache
+ self.asset_ids_cache = {}
+
+ mattr_accessor :asset_ids_cache_guard
+ self.asset_ids_cache_guard = Mutex.new
+
+ # Use the RAILS_ASSET_ID environment variable or the source's
+ # modification time as its cache-busting asset id.
+ def rails_asset_id(source)
+ if asset_id = ENV["RAILS_ASSET_ID"]
+ asset_id
+ else
+ if self.cache_asset_ids && (asset_id = self.asset_ids_cache[source])
+ asset_id
+ else
+ path = File.join(config.assets_dir, source)
+ asset_id = File.exist?(path) ? File.mtime(path).to_i.to_s : ''
+
+ if self.cache_asset_ids
+ add_to_asset_ids_cache(source, asset_id)
+ end
+
+ asset_id
+ end
+ end
+ end
+
+ def rewrite_relative_url_root(source, relative_url_root)
+ relative_url_root && !source.starts_with?("#{relative_url_root}/") ? "#{relative_url_root}#{source}" : source
+ end
+
+ def rewrite_host_and_protocol(source, has_request)
+ host = compute_asset_host(source)
+ if has_request && host && !is_uri?(host)
+ host = "#{controller.request.protocol}#{host}"
+ end
+ "#{host}#{source}"
+ end
+
+ # Pick an asset host for this source. Returns +nil+ if no host is set,
+ # the host if no wildcard is set, the host interpolated with the
+ # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
+ # or the value returned from invoking the proc if it's a proc or the value from
+ # invoking call if it's an object responding to call.
+ def compute_asset_host(source)
+ if host = config.asset_host
+ if host.is_a?(Proc) || host.respond_to?(:call)
+ case host.is_a?(Proc) ? host.arity : host.method(:call).arity
+ when 2
+ request = controller.respond_to?(:request) && controller.request
+ host.call(source, request)
+ else
+ host.call(source)
+ end
+ else
+ (host =~ /%d/) ? host % (source.hash % 4) : host
+ end
+ end
+ end
+ end
+
+ end
+ end
+end \ No newline at end of file
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
new file mode 100644
index 0000000000..6581e1d6f2
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
@@ -0,0 +1,173 @@
+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
+ module Helpers
+ module AssetTagHelper
+
+ class JavascriptIncludeTag < AssetIncludeTag
+ include TagHelper
+
+ def asset_name
+ 'javascript'
+ end
+
+ def extension
+ 'js'
+ end
+
+ def asset_tag(source, options)
+ content_tag("script", "", { "type" => Mime::JS, "src" => path_to_asset(source) }.merge(options))
+ end
+
+ def custom_dir
+ config.javascripts_dir
+ end
+
+ private
+
+ def expand_sources(sources, recursive = false)
+ if sources.include?(:all)
+ all_asset_files = (collect_asset_files(custom_dir, ('**' if recursive), "*.#{extension}") - ['application']) << 'application'
+ ((determine_source(:defaults, expansions).dup & all_asset_files) + all_asset_files).uniq
+ else
+ expanded_sources = sources.collect do |source|
+ determine_source(source, expansions)
+ end.flatten
+ expanded_sources << "application" if sources.include?(:defaults) && File.exist?(File.join(custom_dir, "application.#{extension}"))
+ expanded_sources
+ end
+ end
+ end
+
+
+ module JavascriptTagHelpers
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ # Register one or more javascript files to be included when <tt>symbol</tt>
+ # is passed to <tt>javascript_include_tag</tt>. This method is typically intended
+ # to be called from plugin initialization to register javascript files
+ # that the plugin installed in <tt>public/javascripts</tt>.
+ #
+ # ActionView::Helpers::AssetTagHelper.register_javascript_expansion :monkey => ["head", "body", "tail"]
+ #
+ # javascript_include_tag :monkey # =>
+ # <script type="text/javascript" src="/javascripts/head.js"></script>
+ # <script type="text/javascript" src="/javascripts/body.js"></script>
+ # <script type="text/javascript" src="/javascripts/tail.js"></script>
+ def register_javascript_expansion(expansions)
+ JavascriptIncludeTag.expansions.merge!(expansions)
+ end
+ end
+
+ # Computes the path to a javascript asset in the public javascripts directory.
+ # If the +source+ filename has no extension, .js will be appended (except for explicit URIs)
+ # Full paths from the document root will be passed through.
+ # Used internally by javascript_include_tag to build the script path.
+ #
+ # ==== Examples
+ # javascript_path "xmlhr" # => /javascripts/xmlhr.js
+ # javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js
+ # javascript_path "/dir/xmlhr" # => /dir/xmlhr.js
+ # 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')
+ 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.
+ #
+ # ==== Examples
+ # 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 "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.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>
+ #
+ # * = 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:
+ #
+ # 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>
+ #
+ # Note that the default javascript files will be included first. So Prototype and Scriptaculous are 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>:
+ #
+ # javascript_include_tag :all, :recursive => true
+ #
+ # == 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).
+ #
+ # ==== 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>
+ #
+ # 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>
+ #
+ # javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when config.perform_caching is true =>
+ # <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)
+ end
+
+ end
+
+ end
+ end
+end \ No newline at end of file
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
new file mode 100644
index 0000000000..d02b28d7f6
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
@@ -0,0 +1,144 @@
+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
+ module Helpers
+ module AssetTagHelper
+
+ class StylesheetIncludeTag < AssetIncludeTag
+ include TagHelper
+
+ def asset_name
+ 'stylesheet'
+ end
+
+ def extension
+ 'css'
+ end
+
+ def asset_tag(source, options)
+ tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => ERB::Util.html_escape(path_to_asset(source)) }.merge(options), false, false)
+ end
+
+ def custom_dir
+ config.stylesheets_dir
+ end
+ end
+
+
+ module StylesheetTagHelpers
+ extend ActiveSupport::Concern
+
+ module ClassMethods
+ # Register one or more stylesheet files to be included when <tt>symbol</tt>
+ # is passed to <tt>stylesheet_link_tag</tt>. This method is typically intended
+ # to be called from plugin initialization to register stylesheet files
+ # that the plugin installed in <tt>public/stylesheets</tt>.
+ #
+ # ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion :monkey => ["head", "body", "tail"]
+ #
+ # stylesheet_link_tag :monkey # =>
+ # <link href="/stylesheets/head.css" media="screen" rel="stylesheet" type="text/css" />
+ # <link href="/stylesheets/body.css" media="screen" rel="stylesheet" type="text/css" />
+ # <link href="/stylesheets/tail.css" media="screen" rel="stylesheet" type="text/css" />
+ def register_stylesheet_expansion(expansions)
+ StylesheetIncludeTag.expansions.merge!(expansions)
+ end
+ end
+
+ # Computes the path to a stylesheet asset in the public stylesheets directory.
+ # If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
+ # Full paths from the document root will be passed through.
+ # Used internally by +stylesheet_link_tag+ to build the stylesheet path.
+ #
+ # ==== Examples
+ # stylesheet_path "style" # => /stylesheets/style.css
+ # stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css
+ # stylesheet_path "/dir/style.css" # => /dir/style.css
+ # 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')
+ end
+ alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
+
+ # Returns a stylesheet link tag for the sources specified as arguments. If
+ # you don't specify an extension, <tt>.css</tt> will be appended automatically.
+ # You can modify the link attributes by passing a hash as the last argument.
+ #
+ # ==== Examples
+ # stylesheet_link_tag "style" # =>
+ # <link href="/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />
+ #
+ # stylesheet_link_tag "style.css" # =>
+ # <link href="/stylesheets/style.css" media="screen" rel="stylesheet" type="text/css" />
+ #
+ # stylesheet_link_tag "http://www.railsapplication.com/style.css" # =>
+ # <link href="http://www.railsapplication.com/style.css" media="screen" rel="stylesheet" type="text/css" />
+ #
+ # stylesheet_link_tag "style", :media => "all" # =>
+ # <link href="/stylesheets/style.css" media="all" rel="stylesheet" type="text/css" />
+ #
+ # stylesheet_link_tag "style", :media => "print" # =>
+ # <link href="/stylesheets/style.css" media="print" rel="stylesheet" type="text/css" />
+ #
+ # stylesheet_link_tag "random.styles", "/css/stylish" # =>
+ # <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" type="text/css" />
+ # <link href="/css/stylish.css" media="screen" rel="stylesheet" type="text/css" />
+ #
+ # You can also include all styles in the stylesheets directory using <tt>:all</tt> as the source:
+ #
+ # stylesheet_link_tag :all # =>
+ # <link href="/stylesheets/style1.css" media="screen" rel="stylesheet" type="text/css" />
+ # <link href="/stylesheets/styleB.css" media="screen" rel="stylesheet" type="text/css" />
+ # <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" type="text/css" />
+ #
+ # If you want Rails to search in all the subdirectories under stylesheets, you should explicitly set <tt>:recursive</tt>:
+ #
+ # stylesheet_link_tag :all, :recursive => true
+ #
+ # == Caching multiple stylesheets into one
+ #
+ # You can also cache multiple stylesheets into one file, which requires less HTTP connections and can better be
+ # compressed by gzip (leading to faster transfers). Caching will only happen if config.perform_caching
+ # is set to true (which is the case by default for the Rails production environment, but not for the development
+ # environment). Examples:
+ #
+ # ==== Examples
+ # stylesheet_link_tag :all, :cache => true # when config.perform_caching is false =>
+ # <link href="/stylesheets/style1.css" media="screen" rel="stylesheet" type="text/css" />
+ # <link href="/stylesheets/styleB.css" media="screen" rel="stylesheet" type="text/css" />
+ # <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" type="text/css" />
+ #
+ # stylesheet_link_tag :all, :cache => true # when config.perform_caching is true =>
+ # <link href="/stylesheets/all.css" media="screen" rel="stylesheet" type="text/css" />
+ #
+ # stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when config.perform_caching is false =>
+ # <link href="/stylesheets/shop.css" media="screen" rel="stylesheet" type="text/css" />
+ # <link href="/stylesheets/cart.css" media="screen" rel="stylesheet" type="text/css" />
+ # <link href="/stylesheets/checkout.css" media="screen" rel="stylesheet" type="text/css" />
+ #
+ # stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when config.perform_caching is true =>
+ # <link href="/stylesheets/payment.css" media="screen" rel="stylesheet" type="text/css" />
+ #
+ # The <tt>:recursive</tt> option is also available for caching:
+ #
+ # stylesheet_link_tag :all, :cache => true, :recursive => true
+ #
+ # To force concatenation (even in development mode) set <tt>:concat</tt> to true. This is useful if
+ # you have too many stylesheets for IE to load.
+ #
+ # stylesheet_link_tag :all, :concat => true
+ #
+ def stylesheet_link_tag(*sources)
+ @stylesheet_include ||= StylesheetIncludeTag.new(config, asset_paths)
+ @stylesheet_include.include_tag(*sources)
+ end
+
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index 8c300ec745..ef5bbd8ae3 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -2,7 +2,7 @@ require 'cgi'
require 'action_view/helpers/date_helper'
require 'action_view/helpers/tag_helper'
require 'action_view/helpers/form_tag_helper'
-require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/output_safety'
@@ -1117,7 +1117,7 @@ module ActionView
class FormBuilder #:nodoc:
# The methods which wrap a form helper call.
- class_inheritable_accessor :field_helpers
+ class_attribute :field_helpers
self.field_helpers = (FormHelper.instance_method_names - ['form_for'])
attr_accessor :object_name, :object, :options
diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb
index 33dfcbb803..71cd1a788a 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -8,10 +8,10 @@ module ActionView
config.action_view.stylesheet_expansions = {}
config.action_view.javascript_expansions = { :defaults => ['prototype', 'effects', 'dragdrop', 'controls', 'rails'] }
- initializer "action_view.cache_asset_timestamps" do |app|
+ initializer "action_view.cache_asset_ids" do |app|
unless app.config.cache_classes
ActiveSupport.on_load(:action_view) do
- ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
+ ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids = false
end
end
end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index dbb1532cd4..6c6d659246 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -269,7 +269,8 @@ module ActionView
end
end
- code = @handler.call(self)
+ arity = @handler.respond_to?(:arity) ? @handler.arity : @handler.method(:call).arity
+ code = arity == 1 ? @handler.call(self) : @handler.call(self, view)
# Make sure that the resulting String to be evalled is in the
# encoding of the code
diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb
index 92597e40ff..60534a9746 100644
--- a/actionpack/test/abstract_unit.rb
+++ b/actionpack/test/abstract_unit.rb
@@ -36,14 +36,6 @@ require 'active_record'
require 'action_controller/caching'
require 'action_controller/caching/sweeping'
-begin
- require 'ruby-debug'
- Debugger.settings[:autoeval] = true
- Debugger.start
-rescue LoadError
- # Debugging disabled. `gem install ruby-debug` to enable.
-end
-
require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late
module Rails
diff --git a/actionpack/test/activerecord/active_record_store_test.rb b/actionpack/test/activerecord/active_record_store_test.rb
index 7c595d1b89..f0fb113860 100644
--- a/actionpack/test/activerecord/active_record_store_test.rb
+++ b/actionpack/test/activerecord/active_record_store_test.rb
@@ -23,6 +23,7 @@ class ActiveRecordStoreTest < ActionDispatch::IntegrationTest
def call_reset_session
session[:foo]
reset_session
+ reset_session if params[:twice]
session[:foo] = "baz"
head :ok
end
@@ -94,6 +95,17 @@ class ActiveRecordStoreTest < ActionDispatch::IntegrationTest
end
end
+ def test_calling_reset_session_twice_does_not_raise_errors
+ with_test_route_set do
+ get '/call_reset_session', :twice => "true"
+ assert_response :success
+
+ get '/get_session_value'
+ assert_response :success
+ assert_equal 'foo: "baz"', response.body
+ end
+ end
+
def test_setting_session_value_after_session_reset
with_test_route_set do
get '/set_session_value'
diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb
index 5a210aa9ec..7f3d943bba 100644
--- a/actionpack/test/controller/action_pack_assertions_test.rb
+++ b/actionpack/test/controller/action_pack_assertions_test.rb
@@ -171,6 +171,14 @@ class ActionPackAssertionsControllerTest < ActionController::TestCase
end
end
+ def test_string_constraint
+ with_routing do |set|
+ set.draw do
+ match "photos", :to => 'action_pack_assertions#nothing', :constraints => {:subdomain => "admin"}
+ end
+ end
+ end
+
def test_assert_redirect_to_named_route_failure
with_routing do |set|
set.draw do
diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb
index ecfa13d4ba..cd067b7d18 100644
--- a/actionpack/test/controller/routing_test.rb
+++ b/actionpack/test/controller/routing_test.rb
@@ -834,6 +834,14 @@ class RouteSetTest < ActiveSupport::TestCase
assert_equal({:controller => 'pages', :action => 'show', :id => '10'}, set.recognize_path('/page/10'))
end
+ def test_route_constraints_on_request_object_with_anchors_are_valid
+ assert_nothing_raised do
+ set.draw do
+ match 'page/:id' => 'pages#show', :constraints => { :host => /^foo$/ }
+ end
+ end
+ end
+
def test_route_constraints_with_anchor_chars_are_invalid
assert_raise ArgumentError do
set.draw do
@@ -1436,7 +1444,7 @@ class RouteSetTest < ActiveSupport::TestCase
end
def test_expand_array_build_query_string
- assert_uri_equal '/foo?x[]=1&x[]=2', default_route_set.generate({:controller => 'foo', :x => [1, 2]})
+ assert_uri_equal '/foo?x%5B%5D=1&x%5B%5D=2', default_route_set.generate({:controller => 'foo', :x => [1, 2]})
end
def test_escape_spaces_build_query_string_selected_keys
@@ -1736,9 +1744,9 @@ class RackMountIntegrationTests < ActiveSupport::TestCase
assert_equal '/posts', @routes.generate({:controller => 'posts'}, {:controller => 'posts', :action => 'index'})
assert_equal '/posts/create', @routes.generate({:action => 'create'}, {:controller => 'posts'})
assert_equal '/posts?foo=bar', @routes.generate(:controller => 'posts', :foo => 'bar')
- assert_equal '/posts?foo[]=bar&foo[]=baz', @routes.generate(:controller => 'posts', :foo => ['bar', 'baz'])
+ assert_equal '/posts?foo%5B%5D=bar&foo%5B%5D=baz', @routes.generate(:controller => 'posts', :foo => ['bar', 'baz'])
assert_equal '/posts?page=2', @routes.generate(:controller => 'posts', :page => 2)
- assert_equal '/posts?q[foo][a]=b', @routes.generate(:controller => 'posts', :q => { :foo => { :a => 'b'}})
+ assert_equal '/posts?q%5Bfoo%5D%5Ba%5D=b', @routes.generate(:controller => 'posts', :q => { :foo => { :a => 'b'}})
assert_equal '/news.rss', @routes.generate(:controller => 'news', :action => 'index', :format => 'rss')
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index 04709e5346..4764a8c2a8 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -96,6 +96,9 @@ class RequestTest < ActiveSupport::TestCase
request = stub_request 'HTTP_HOST' => "www.rubyonrails.co.uk"
assert_equal "rubyonrails.co.uk", request.domain(2)
+ request = stub_request 'HTTP_HOST' => "www.rubyonrails.co.uk", :tld_length => 2
+ assert_equal "rubyonrails.co.uk", request.domain
+
request = stub_request 'HTTP_HOST' => "192.168.1.200"
assert_nil request.domain
diff --git a/actionpack/test/dispatch/uploaded_file_test.rb b/actionpack/test/dispatch/uploaded_file_test.rb
index b51697b930..e2a7f1bad7 100644
--- a/actionpack/test/dispatch/uploaded_file_test.rb
+++ b/actionpack/test/dispatch/uploaded_file_test.rb
@@ -28,6 +28,18 @@ module ActionDispatch
assert_equal 'foo', uf.tempfile
end
+ def test_delegates_path_to_tempfile
+ tf = Class.new { def path; 'thunderhorse' end }
+ uf = Http::UploadedFile.new(:tempfile => tf.new)
+ assert_equal 'thunderhorse', uf.path
+ end
+
+ def test_delegates_open_to_tempfile
+ tf = Class.new { def open; 'thunderhorse' end }
+ uf = Http::UploadedFile.new(:tempfile => tf.new)
+ assert_equal 'thunderhorse', uf.open
+ end
+
def test_delegates_to_tempfile
tf = Class.new { def read; 'thunderhorse' end }
uf = Http::UploadedFile.new(:tempfile => tf.new)
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index caf1c694c8..fbcc99a17a 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -274,7 +274,7 @@ class AssetTagHelperTest < ActionView::TestCase
end
def test_reset_javascript_expansions
- ActionView::Helpers::AssetTagHelper.javascript_expansions.clear
+ JavascriptIncludeTag.expansions.clear
assert_raise(ArgumentError) { javascript_include_tag(:defaults) }
end
@@ -306,7 +306,6 @@ class AssetTagHelperTest < ActionView::TestCase
ENV["RAILS_ASSET_ID"] = ""
assert stylesheet_link_tag('dir/file').html_safe?
assert stylesheet_link_tag('dir/other/file', 'dir/file2').html_safe?
- assert stylesheet_tag('dir/file', {}).html_safe?
end
def test_custom_stylesheet_expansions
@@ -681,6 +680,26 @@ class AssetTagHelperTest < ActionView::TestCase
FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
end
+ def test_caching_javascript_include_tag_with_named_paths_and_relative_url_root_when_caching_off
+ ENV["RAILS_ASSET_ID"] = ""
+ @controller.config.relative_url_root = "/collaboration/hieraki"
+ config.perform_caching = false
+
+ assert_dom_equal(
+ %(<script src="/collaboration/hieraki/javascripts/robber.js" type="text/javascript"></script>),
+ javascript_include_tag('robber', :cache => true)
+ )
+
+ assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))
+
+ assert_dom_equal(
+ %(<script src="/collaboration/hieraki/javascripts/robber.js" type="text/javascript"></script>),
+ javascript_include_tag('robber', :cache => "money", :recursive => true)
+ )
+
+ assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))
+ end
+
def test_caching_javascript_include_tag_when_caching_off
ENV["RAILS_ASSET_ID"] = ""
config.perform_caching = false
@@ -908,6 +927,30 @@ class AssetTagHelperTest < ActionView::TestCase
FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
end
+
+ def test_caching_stylesheet_link_tag_with_named_paths_and_relative_url_root_when_caching_off
+ ENV["RAILS_ASSET_ID"] = ""
+ @controller.config.relative_url_root = "/collaboration/hieraki"
+ config.perform_caching = false
+
+ assert_dom_equal(
+ %(<link href="/collaboration/hieraki/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />),
+ stylesheet_link_tag('robber', :cache => true)
+ )
+
+ assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))
+
+ assert_dom_equal(
+ %(<link href="/collaboration/hieraki/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />),
+ stylesheet_link_tag('robber', :cache => "money")
+ )
+
+ assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))
+ end
+
+
+
+
def test_caching_stylesheet_include_tag_when_caching_off
ENV["RAILS_ASSET_ID"] = ""
config.perform_caching = false
@@ -949,7 +992,7 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
@request = Struct.new(:protocol).new("gopher://")
@controller.request = @request
- ActionView::Helpers::AssetTagHelper.javascript_expansions.clear
+ JavascriptIncludeTag.expansions.clear
end
def url_for(options)
diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb
index d2bf45a63a..8087429d62 100644
--- a/actionpack/test/template/render_test.rb
+++ b/actionpack/test/template/render_test.rb
@@ -221,6 +221,15 @@ 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_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/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec
index 2d1263407f..318d71a610 100644
--- a/activemodel/activemodel.gemspec
+++ b/activemodel/activemodel.gemspec
@@ -20,6 +20,6 @@ Gem::Specification.new do |s|
s.has_rdoc = true
s.add_dependency('activesupport', version)
- s.add_dependency('builder', '~> 2.1.2')
+ s.add_dependency('builder', '~> 3.0.0')
s.add_dependency('i18n', '~> 0.4.2')
end
diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb
index ece4431225..23ba42bf35 100644
--- a/activemodel/lib/active_model/version.rb
+++ b/activemodel/lib/active_model/version.rb
@@ -3,8 +3,8 @@ module ActiveModel
MAJOR = 3
MINOR = 1
TINY = 0
- BUILD = "beta"
+ PRE = "beta"
- STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
end
diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb
index a81584bbad..01f0158678 100644
--- a/activemodel/test/cases/helper.rb
+++ b/activemodel/test/cases/helper.rb
@@ -12,9 +12,3 @@ ActiveSupport::Deprecation.debug = true
require 'rubygems'
require 'test/unit'
-
-begin
- require 'ruby-debug'
- Debugger.start
-rescue LoadError
-end
diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG
index 11eb47917d..a3e3051b96 100644
--- a/activerecord/CHANGELOG
+++ b/activerecord/CHANGELOG
@@ -1,5 +1,32 @@
*Rails 3.1.0 (unreleased)*
+* Migrations can be defined as reversible, meaning that the migration system
+will figure out how to reverse your migration. To use reversible migrations,
+just define the "change" method. For example:
+
+ class MyMigration < ActiveRecord::Migration
+ def change
+ create_table(:horses) do
+ t.column :content, :text
+ t.column :remind_at, :datetime
+ end
+ end
+ end
+
+Some things cannot be automatically reversed for you. If you know how to
+reverse those things, you should define 'up' and 'down' in your migration. If
+you define something in `change` that cannot be reversed, an
+IrreversibleMigration exception will be raised when going down.
+
+* Migrations should use instance methods rather than class methods:
+ class FooMigration < ActiveRecord::Migration
+ def up
+ ...
+ end
+ end
+
+ [Aaron Patterson]
+
* has_one maintains the association with separate after_create/after_update instead
of a single after_save. [fxn]
diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb
index 911a5155fd..9743b1b4a7 100644
--- a/activerecord/lib/active_record/association_preload.rb
+++ b/activerecord/lib/active_record/association_preload.rb
@@ -193,13 +193,17 @@ module ActiveRecord
conditions = "t0.#{reflection.primary_key_name} #{in_or_equals_for_ids(ids)}"
conditions << append_conditions(reflection, preload_options)
- associated_records = reflection.klass.unscoped.where([conditions, ids]).
+ associated_records_proxy = reflection.klass.unscoped.
includes(options[:include]).
joins("INNER JOIN #{connection.quote_table_name options[:join_table]} t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}").
select("#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as the_parent_record_id").
- order(options[:order]).to_a
+ order(options[:order])
- set_association_collection_records(id_to_record_map, reflection.name, associated_records, 'the_parent_record_id')
+ all_associated_records = associated_records(ids) do |some_ids|
+ associated_records_proxy.where([conditions, ids]).to_a
+ end
+
+ set_association_collection_records(id_to_record_map, reflection.name, all_associated_records, 'the_parent_record_id')
end
def preload_has_one_association(records, reflection, preload_options={})
@@ -358,13 +362,14 @@ module ActiveRecord
find_options = {
:select => preload_options[:select] || options[:select] || Arel::SqlLiteral.new("#{table_name}.*"),
:include => preload_options[:include] || options[:include],
- :conditions => [conditions, ids],
:joins => options[:joins],
:group => preload_options[:group] || options[:group],
:order => preload_options[:order] || options[:order]
}
- reflection.klass.scoped.apply_finder_options(find_options).to_a
+ associated_records(ids) do |some_ids|
+ reflection.klass.scoped.apply_finder_options(find_options.merge(:conditions => [conditions, some_ids])).to_a
+ end
end
@@ -382,6 +387,17 @@ module ActiveRecord
def in_or_equals_for_ids(ids)
ids.size > 1 ? "IN (?)" : "= ?"
end
+
+ # Some databases impose a limit on the number of ids in a list (in Oracle its 1000)
+ # Make several smaller queries if necessary or make one query if the adapter supports it
+ def associated_records(ids)
+ max_ids_in_a_list = connection.ids_in_list_limit || ids.size
+ records = []
+ ids.each_slice(max_ids_in_a_list) do |some_ids|
+ records += yield(some_ids)
+ end
+ records
+ end
end
end
end
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 08a4ebfd7e..7da38cd03f 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -4,6 +4,7 @@ require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/string/conversions'
require 'active_support/core_ext/module/remove_method'
+require 'active_support/core_ext/class/attribute'
module ActiveRecord
class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
@@ -1810,12 +1811,12 @@ module ActiveRecord
callbacks.each do |callback_name|
full_callback_name = "#{callback_name}_for_#{association_name}"
defined_callbacks = options[callback_name.to_sym]
- if options.has_key?(callback_name.to_sym)
- class_inheritable_reader full_callback_name.to_sym
- write_inheritable_attribute(full_callback_name.to_sym, [defined_callbacks].flatten)
- else
- write_inheritable_attribute(full_callback_name.to_sym, [])
- end
+
+ full_callback_value = options.has_key?(callback_name.to_sym) ? [defined_callbacks].flatten : []
+
+ # TODO : why do i need method_defined? I think its because of the inheritance chain
+ class_attribute full_callback_name.to_sym unless method_defined?(full_callback_name)
+ self.send("#{full_callback_name}=", full_callback_value)
end
end
diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb
index 5b34ac907c..774103342a 100644
--- a/activerecord/lib/active_record/associations/association_collection.rb
+++ b/activerecord/lib/active_record/associations/association_collection.rb
@@ -182,7 +182,7 @@ module ActiveRecord
unless options.blank?
raise ArgumentError, "If finder_sql/counter_sql is used then options cannot be passed"
end
-
+
@reflection.klass.count_by_sql(custom_counter_sql)
else
@@ -332,13 +332,10 @@ module ActiveRecord
end
def uniq(collection = self)
- seen = Set.new
- collection.map do |record|
- unless seen.include?(record.id)
- seen << record.id
- record
- end
- end.compact
+ seen = {}
+ collection.find_all do |record|
+ seen[record.id] = true unless seen.key?(record.id)
+ end
end
# Replace this collection with +other_array+
@@ -375,14 +372,16 @@ module ActiveRecord
def load_target
if @owner.persisted? || foreign_key_present
begin
- if !loaded?
+ unless loaded?
if @target.is_a?(Array) && @target.any?
@target = find_target.map do |f|
i = @target.index(f)
if i
@target.delete_at(i).tap do |t|
keys = ["id"] + t.changes.keys + (f.attribute_names - t.attribute_names)
- t.attributes = f.attributes.except(*keys)
+ f.attributes.except(*keys).each do |k,v|
+ t.send("#{k}=", v)
+ end
end
else
f
@@ -409,11 +408,7 @@ module ActiveRecord
end
if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
- if block_given?
- super { |*block_args| yield(*block_args) }
- else
- super
- end
+ super
elsif @reflection.klass.scopes[method]
@_named_scopes_cache ||= {}
@_named_scopes_cache[method] ||= {}
@@ -436,10 +431,10 @@ module ActiveRecord
# replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
counter_sql = @reflection.options[:finder_sql].sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
end
-
+
interpolate_sql(counter_sql)
end
-
+
def custom_finder_sql
interpolate_sql(@reflection.options[:finder_sql])
end
@@ -535,7 +530,7 @@ module ActiveRecord
def callbacks_for(callback_name)
full_callback_name = "#{callback_name}_for_#{@reflection.name}"
- @owner.class.read_inheritable_attribute(full_callback_name.to_sym) || []
+ @owner.class.send(full_callback_name.to_sym) || []
end
def ensure_owner_is_not_new
diff --git a/activerecord/lib/active_record/associations/association_proxy.rb b/activerecord/lib/active_record/associations/association_proxy.rb
index 09bba213ce..7cd04a1ad5 100644
--- a/activerecord/lib/active_record/associations/association_proxy.rb
+++ b/activerecord/lib/active_record/associations/association_proxy.rb
@@ -211,12 +211,12 @@ module ActiveRecord
:create => construct_create_scope
}
end
-
+
# Implemented by subclasses
def construct_find_scope
raise NotImplementedError
end
-
+
# Implemented by (some) subclasses
def construct_create_scope
{}
diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb
index 439880c1fa..c19a33faa8 100644
--- a/activerecord/lib/active_record/attribute_methods/dirty.rb
+++ b/activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -1,3 +1,4 @@
+require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/object/blank'
module ActiveRecord
@@ -88,7 +89,7 @@ module ActiveRecord
end
def clone_with_time_zone_conversion_attribute?(attr, old)
- old.class.name == "Time" && time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(attr.to_sym)
+ old.class.name == "Time" && time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(attr.to_sym)
end
end
end
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 d640b26b74..dc2785b6bf 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/class/attribute'
+
module ActiveRecord
module AttributeMethods
module TimeZoneConversion
@@ -7,7 +9,7 @@ module ActiveRecord
cattr_accessor :time_zone_aware_attributes, :instance_writer => false
self.time_zone_aware_attributes = false
- class_inheritable_accessor :skip_time_zone_conversion_for_attributes, :instance_writer => false
+ class_attribute :skip_time_zone_conversion_for_attributes, :instance_writer => false
self.skip_time_zone_conversion_for_attributes = []
end
@@ -54,7 +56,7 @@ module ActiveRecord
private
def create_time_zone_conversion_attribute?(name, column)
- time_zone_aware_attributes && !skip_time_zone_conversion_for_attributes.include?(name.to_sym) && [:datetime, :timestamp].include?(column.type)
+ time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) && [:datetime, :timestamp].include?(column.type)
end
end
end
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index d7a7101404..aa028c6f36 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -7,7 +7,7 @@ require 'active_support/time'
require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/class/delegating_attributes'
-require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/hash/deep_merge'
require 'active_support/core_ext/hash/indifferent_access'
@@ -412,7 +412,7 @@ module ActiveRecord #:nodoc:
self.store_full_sti_class = true
# Stores the default scope for the class
- class_inheritable_accessor :default_scoping, :instance_writer => false
+ class_attribute :default_scoping, :instance_writer => false
self.default_scoping = []
# Returns a hash of all the attributes that have been specified for serialization as
@@ -420,6 +420,9 @@ module ActiveRecord #:nodoc:
class_attribute :serialized_attributes
self.serialized_attributes = {}
+ class_attribute :_attr_readonly, :instance_writer => false
+ self._attr_readonly = []
+
class << self # Class methods
delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped
delegate :find_each, :find_in_batches, :to => :scoped
@@ -504,12 +507,12 @@ module ActiveRecord #:nodoc:
# Attributes listed as readonly will be used to create a new record but update operations will
# ignore these fields.
def attr_readonly(*attributes)
- write_inheritable_attribute(:attr_readonly, Set.new(attributes.map { |a| a.to_s }) + (readonly_attributes || []))
+ self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || [])
end
# Returns an array of all the attributes that have been specified as readonly.
def readonly_attributes
- read_inheritable_attribute(:attr_readonly) || []
+ self._attr_readonly
end
# If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
@@ -724,10 +727,6 @@ module ActiveRecord #:nodoc:
@arel_engine = @relation = @arel_table = nil
end
- def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc:
- descendants.each { |klass| klass.reset_inheritable_attributes; klass.reset_column_information }
- end
-
def attribute_method?(attribute)
super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, '')))
end
@@ -736,15 +735,12 @@ module ActiveRecord #:nodoc:
def lookup_ancestors #:nodoc:
klass = self
classes = [klass]
+ return classes if klass == ActiveRecord::Base
+
while klass != klass.base_class
classes << klass = klass.superclass
end
classes
- rescue
- # OPTIMIZE this rescue is to fix this test: ./test/cases/reflection_test.rb:56:in `test_human_name_for_column'
- # Apparently the method base_class causes some trouble.
- # It now works for sure.
- [self]
end
# Set the i18n scope to overwrite ActiveModel.
@@ -1128,7 +1124,8 @@ MSG
# Article.create.published # => true
def default_scope(options = {})
reset_scoped_methods
- self.default_scoping << construct_finder_arel(options, default_scoping.pop)
+ default_scoping = self.default_scoping.dup
+ self.default_scoping = default_scoping << construct_finder_arel(options, default_scoping.pop)
end
def current_scoped_methods #:nodoc:
@@ -1285,7 +1282,7 @@ MSG
# ["name='%s' and group_id='%s'", "foo'bar", 4] returns "name='foo''bar' and group_id='4'"
def sanitize_sql_array(ary)
statement, *values = ary
- if values.first.is_a?(Hash) and statement =~ /:\w+/
+ if values.first.is_a?(Hash) && statement =~ /:\w+/
replace_named_bind_variables(statement, values.first)
elsif statement.include?('?')
replace_bind_variables(statement, values)
@@ -1581,12 +1578,20 @@ MSG
self.class.columns_hash[name.to_s]
end
- # Returns true if the +comparison_object+ is the same object, or is of the same type and has the same id.
+ # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
+ # is of the same type and +self+ has an ID and it is equal to +comparison_object.id+.
+ #
+ # Note that new records are different from any other record by definition, unless the
+ # other record is the receiver itself. Besides, if you fetch existing records with
+ # +select+ and leave the ID out, you're on your own, this predicate will return false.
+ #
+ # Note also that destroying a record preserves its ID in the model instance, so deleted
+ # models are still comparable.
def ==(comparison_object)
comparison_object.equal?(self) ||
- persisted? &&
- (comparison_object.instance_of?(self.class) &&
- comparison_object.id == id)
+ comparison_object.instance_of?(self.class) &&
+ id.present? &&
+ comparison_object.id == id
end
# Delegates to ==
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index f3fba9a3a9..ada1560ce2 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -91,6 +91,11 @@ module ActiveRecord
false
end
+ # Does this adapter restrict the number of ids you can use in a list. Oracle has a limit of 1000.
+ def ids_in_list_limit
+ nil
+ end
+
# QUOTING ==================================================
# Override to return the quoted table name. Defaults to column quoting.
diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb
index a4c09b654a..f6321f1499 100644
--- a/activerecord/lib/active_record/migration.rb
+++ b/activerecord/lib/active_record/migration.rb
@@ -1,6 +1,3 @@
-require 'active_support/core_ext/kernel/singleton_class'
-require 'active_support/core_ext/module/aliasing'
-
module ActiveRecord
# Exception that can be raised to stop migrations from going backwards.
class IrreversibleMigration < ActiveRecordError
@@ -43,11 +40,11 @@ module ActiveRecord
# Example of a simple migration:
#
# class AddSsl < ActiveRecord::Migration
- # def self.up
+ # def up
# add_column :accounts, :ssl_enabled, :boolean, :default => 1
# end
#
- # def self.down
+ # def down
# remove_column :accounts, :ssl_enabled
# end
# end
@@ -63,7 +60,7 @@ module ActiveRecord
# Example of a more complex migration that also needs to initialize data:
#
# class AddSystemSettings < ActiveRecord::Migration
- # def self.up
+ # def up
# create_table :system_settings do |t|
# t.string :name
# t.string :label
@@ -77,7 +74,7 @@ module ActiveRecord
# :value => 1
# end
#
- # def self.down
+ # def down
# drop_table :system_settings
# end
# end
@@ -138,7 +135,7 @@ module ActiveRecord
# in the <tt>db/migrate/</tt> directory where <tt>timestamp</tt> is the
# UTC formatted date and time that the migration was generated.
#
- # You may then edit the <tt>self.up</tt> and <tt>self.down</tt> methods of
+ # You may then edit the <tt>up</tt> and <tt>down</tt> methods of
# MyNewMigration.
#
# There is a special syntactic shortcut to generate migrations that add fields to a table.
@@ -147,11 +144,11 @@ module ActiveRecord
#
# This will generate the file <tt>timestamp_add_fieldname_to_tablename</tt>, which will look like this:
# class AddFieldnameToTablename < ActiveRecord::Migration
- # def self.up
+ # def up
# add_column :tablenames, :fieldname, :string
# end
#
- # def self.down
+ # def down
# remove_column :tablenames, :fieldname
# end
# end
@@ -179,11 +176,11 @@ module ActiveRecord
# Not all migrations change the schema. Some just fix the data:
#
# class RemoveEmptyTags < ActiveRecord::Migration
- # def self.up
+ # def up
# Tag.find(:all).each { |tag| tag.destroy if tag.pages.empty? }
# end
#
- # def self.down
+ # def down
# # not much we can do to restore deleted data
# raise ActiveRecord::IrreversibleMigration, "Can't recover the deleted tags"
# end
@@ -192,12 +189,12 @@ module ActiveRecord
# Others remove columns when they migrate up instead of down:
#
# class RemoveUnnecessaryItemAttributes < ActiveRecord::Migration
- # def self.up
+ # def up
# remove_column :items, :incomplete_items_count
# remove_column :items, :completed_items_count
# end
#
- # def self.down
+ # def down
# add_column :items, :incomplete_items_count
# add_column :items, :completed_items_count
# end
@@ -206,11 +203,11 @@ module ActiveRecord
# And sometimes you need to do something in SQL not abstracted directly by migrations:
#
# class MakeJoinUnique < ActiveRecord::Migration
- # def self.up
+ # def up
# execute "ALTER TABLE `pages_linked_pages` ADD UNIQUE `page_id_linked_page_id` (`page_id`,`linked_page_id`)"
# end
#
- # def self.down
+ # def down
# execute "ALTER TABLE `pages_linked_pages` DROP INDEX `page_id_linked_page_id`"
# end
# end
@@ -223,7 +220,7 @@ module ActiveRecord
# latest column data from after the new column was added. Example:
#
# class AddPeopleSalary < ActiveRecord::Migration
- # def self.up
+ # def up
# add_column :people, :salary, :integer
# Person.reset_column_information
# Person.find(:all).each do |p|
@@ -243,7 +240,7 @@ module ActiveRecord
# You can also insert your own messages and benchmarks by using the +say_with_time+
# method:
#
- # def self.up
+ # def up
# ...
# say_with_time "Updating salaries..." do
# Person.find(:all).each do |p|
@@ -286,143 +283,201 @@ module ActiveRecord
#
# In application.rb.
#
+ # == Reversible Migrations
+ #
+ # Starting with Rails 3.1, you will be able to define reversible migrations.
+ # Reversible migrations are migrations that know how to go +down+ for you.
+ # You simply supply the +up+ logic, and the Migration system will figure out
+ # how to execute the down commands for you.
+ #
+ # To define a reversible migration, define the +change+ method in your
+ # migration like this:
+ #
+ # class TenderloveMigration < ActiveRecord::Migration
+ # def change
+ # create_table(:horses) do
+ # t.column :content, :text
+ # t.column :remind_at, :datetime
+ # end
+ # end
+ # end
+ #
+ # This migration will create the horses table for you on the way up, and
+ # automatically figure out how to drop the table on the way down.
+ #
+ # Some commands like +remove_column+ cannot be reversed. If you care to
+ # define how to move up and down in these cases, you should define the +up+
+ # and +down+ methods as before.
+ #
+ # If a command cannot be reversed, an
+ # <tt>ActiveRecord::IrreversibleMigration</tt> exception will be raised when
+ # the migration is moving down.
+ #
+ # For a list of commands that are reversible, please see
+ # <tt>ActiveRecord::Migration::CommandRecorder</tt>.
class Migration
- @@verbose = true
- cattr_accessor :verbose
+ autoload :CommandRecorder, 'active_record/migration/command_recorder'
class << self
- def up_with_benchmarks #:nodoc:
- migrate(:up)
- end
+ attr_accessor :delegate # :nodoc:
+ end
- def down_with_benchmarks #:nodoc:
- migrate(:down)
- end
+ def self.method_missing(name, *args, &block) # :nodoc:
+ (delegate || superclass.delegate).send(name, *args, &block)
+ end
- # Execute this migration in the named direction
- def migrate(direction)
- return unless respond_to?(direction)
+ cattr_accessor :verbose
- case direction
- when :up then announce "migrating"
- when :down then announce "reverting"
- end
+ attr_accessor :name, :version
- result = nil
- time = Benchmark.measure { result = send("#{direction}_without_benchmarks") }
+ def initialize
+ @name = self.class.name
+ @version = nil
+ @connection = nil
+ end
- case direction
- when :up then announce "migrated (%.4fs)" % time.real; write
- when :down then announce "reverted (%.4fs)" % time.real; write
- end
+ # instantiate the delegate object after initialize is defined
+ self.verbose = true
+ self.delegate = new
- result
- end
+ def up
+ self.class.delegate = self
+ return unless self.class.respond_to?(:up)
+ self.class.up
+ end
+
+ def down
+ self.class.delegate = self
+ return unless self.class.respond_to?(:down)
+ self.class.down
+ end
- # Because the method added may do an alias_method, it can be invoked
- # recursively. We use @ignore_new_methods as a guard to indicate whether
- # it is safe for the call to proceed.
- def singleton_method_added(sym) #:nodoc:
- return if defined?(@ignore_new_methods) && @ignore_new_methods
+ # Execute this migration in the named direction
+ def migrate(direction)
+ return unless respond_to?(direction)
- begin
- @ignore_new_methods = true
+ case direction
+ when :up then announce "migrating"
+ when :down then announce "reverting"
+ end
- case sym
- when :up, :down
- singleton_class.send(:alias_method_chain, sym, "benchmarks")
+ time = nil
+ ActiveRecord::Base.connection_pool.with_connection do |conn|
+ @connection = conn
+ if respond_to?(:change)
+ if direction == :down
+ recorder = CommandRecorder.new(@connection)
+ suppress_messages do
+ @connection = recorder
+ change
+ end
+ @connection = conn
+ time = Benchmark.measure {
+ recorder.inverse.each do |cmd, args|
+ send(cmd, *args)
+ end
+ }
+ else
+ time = Benchmark.measure { change }
end
- ensure
- @ignore_new_methods = false
+ else
+ time = Benchmark.measure { send(direction) }
end
+ @connection = nil
end
- def write(text="")
- puts(text) if verbose
+ case direction
+ when :up then announce "migrated (%.4fs)" % time.real; write
+ when :down then announce "reverted (%.4fs)" % time.real; write
end
+ end
- def announce(message)
- version = defined?(@version) ? @version : nil
+ def write(text="")
+ puts(text) if verbose
+ end
- text = "#{version} #{name}: #{message}"
- length = [0, 75 - text.length].max
- write "== %s %s" % [text, "=" * length]
- end
+ def announce(message)
+ text = "#{version} #{name}: #{message}"
+ length = [0, 75 - text.length].max
+ write "== %s %s" % [text, "=" * length]
+ end
- def say(message, subitem=false)
- write "#{subitem ? " ->" : "--"} #{message}"
- end
+ def say(message, subitem=false)
+ write "#{subitem ? " ->" : "--"} #{message}"
+ end
- def say_with_time(message)
- say(message)
- result = nil
- time = Benchmark.measure { result = yield }
- say "%.4fs" % time.real, :subitem
- say("#{result} rows", :subitem) if result.is_a?(Integer)
- result
- end
+ def say_with_time(message)
+ say(message)
+ result = nil
+ time = Benchmark.measure { result = yield }
+ say "%.4fs" % time.real, :subitem
+ say("#{result} rows", :subitem) if result.is_a?(Integer)
+ result
+ end
- def suppress_messages
- save, self.verbose = verbose, false
- yield
- ensure
- self.verbose = save
- end
+ def suppress_messages
+ save, self.verbose = verbose, false
+ yield
+ ensure
+ self.verbose = save
+ end
- def connection
- ActiveRecord::Base.connection
- end
+ def connection
+ @connection || ActiveRecord::Base.connection
+ end
- def method_missing(method, *arguments, &block)
- arg_list = arguments.map{ |a| a.inspect } * ', '
+ def method_missing(method, *arguments, &block)
+ arg_list = arguments.map{ |a| a.inspect } * ', '
- say_with_time "#{method}(#{arg_list})" do
- unless arguments.empty? || method == :execute
- arguments[0] = Migrator.proper_table_name(arguments.first)
- end
- connection.send(method, *arguments, &block)
+ say_with_time "#{method}(#{arg_list})" do
+ unless arguments.empty? || method == :execute
+ arguments[0] = Migrator.proper_table_name(arguments.first)
end
+ return super unless connection.respond_to?(method)
+ connection.send(method, *arguments, &block)
end
+ end
- def copy(destination, sources, options = {})
- copied = []
-
- destination_migrations = ActiveRecord::Migrator.migrations(destination)
- last = destination_migrations.last
- sources.each do |name, path|
- source_migrations = ActiveRecord::Migrator.migrations(path)
+ def copy(destination, sources, options = {})
+ copied = []
- source_migrations.each do |migration|
- source = File.read(migration.filename)
- source = "# This migration comes from #{name} (originally #{migration.version})\n#{source}"
+ FileUtils.mkdir_p(destination) unless File.exists?(destination)
- if duplicate = destination_migrations.detect { |m| m.name == migration.name }
- options[:on_skip].call(name, migration) if File.read(duplicate.filename) != source && options[:on_skip]
- next
- end
+ destination_migrations = ActiveRecord::Migrator.migrations(destination)
+ last = destination_migrations.last
+ sources.each do |name, path|
+ source_migrations = ActiveRecord::Migrator.migrations(path)
- migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
- new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.rb")
- old_path, migration.filename = migration.filename, new_path
- last = migration
+ source_migrations.each do |migration|
+ source = File.read(migration.filename)
+ source = "# This migration comes from #{name} (originally #{migration.version})\n#{source}"
- FileUtils.cp(old_path, migration.filename)
- copied << migration
- options[:on_copy].call(name, migration, old_path) if options[:on_copy]
- destination_migrations << migration
+ if duplicate = destination_migrations.detect { |m| m.name == migration.name }
+ options[:on_skip].call(name, migration) if File.read(duplicate.filename) != source && options[:on_skip]
+ next
end
- end
- copied
- end
+ migration.version = next_migration_number(last ? last.version + 1 : 0).to_i
+ new_path = File.join(destination, "#{migration.version}_#{migration.name.underscore}.rb")
+ old_path, migration.filename = migration.filename, new_path
+ last = migration
- def next_migration_number(number)
- if ActiveRecord::Base.timestamped_migrations
- [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
- else
- "%.3d" % number
+ FileUtils.cp(old_path, migration.filename)
+ copied << migration
+ options[:on_copy].call(name, migration, old_path) if options[:on_copy]
+ destination_migrations << migration
end
end
+
+ copied
+ end
+
+ def next_migration_number(number)
+ if ActiveRecord::Base.timestamped_migrations
+ [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
+ else
+ "%.3d" % number
+ end
end
end
@@ -449,7 +504,7 @@ module ActiveRecord
def load_migration
require(File.expand_path(filename))
- name.constantize
+ name.constantize.new
end
end
diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb
new file mode 100644
index 0000000000..d7e481905a
--- /dev/null
+++ b/activerecord/lib/active_record/migration/command_recorder.rb
@@ -0,0 +1,91 @@
+module ActiveRecord
+ class Migration
+ # ActiveRecord::Migration::CommandRecorder records commands done during
+ # a migration and knows how to reverse those commands. The CommandRecorder
+ # knows how to invert the following commands:
+ #
+ # * add_column
+ # * add_index
+ # * add_timestamp
+ # * create_table
+ # * remove_timestamps
+ # * rename_column
+ # * rename_index
+ # * rename_table
+ class CommandRecorder
+ attr_accessor :commands, :delegate
+
+ def initialize(delegate = nil)
+ @commands = []
+ @delegate = delegate
+ end
+
+ # record +command+. +command+ should be a method name and arguments.
+ # For example:
+ #
+ # recorder.record(:method_name, [:arg1, arg2])
+ def record(*command)
+ @commands << command
+ end
+
+ # Returns a list that represents commands that are the inverse of the
+ # commands stored in +commands+. For example:
+ #
+ # recorder.record(:rename_table, [:old, :new])
+ # recorder.inverse # => [:rename_table, [:new, :old]]
+ #
+ # This method will raise an IrreversibleMigration exception if it cannot
+ # invert the +commands+.
+ def inverse
+ @commands.reverse.map { |name, args|
+ method = :"invert_#{name}"
+ raise IrreversibleMigration unless respond_to?(method, true)
+ __send__(method, args)
+ }
+ end
+
+ def respond_to?(*args) # :nodoc:
+ super || delegate.respond_to?(*args)
+ end
+
+ def send(method, *args) # :nodoc:
+ return super unless respond_to?(method)
+ record(method, args)
+ end
+
+ private
+ def invert_create_table(args)
+ [:drop_table, args]
+ end
+
+ def invert_rename_table(args)
+ [:rename_table, args.reverse]
+ end
+
+ def invert_add_column(args)
+ [:remove_column, args.first(2)]
+ end
+
+ def invert_rename_index(args)
+ [:rename_index, args.reverse]
+ end
+
+ def invert_rename_column(args)
+ [:rename_column, [args.first] + args.last(2).reverse]
+ end
+
+ def invert_add_index(args)
+ table, columns, _ = *args
+ [:remove_index, [table, {:column => columns}]]
+ end
+
+ def invert_remove_timestamps(args)
+ [:add_timestamps, args]
+ end
+
+ def invert_add_timestamps(args)
+ [:remove_timestamps, args]
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb
index 0b92ba5caa..0f421560f0 100644
--- a/activerecord/lib/active_record/named_scope.rb
+++ b/activerecord/lib/active_record/named_scope.rb
@@ -2,12 +2,18 @@ require 'active_support/core_ext/array'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/kernel/singleton_class'
require 'active_support/core_ext/object/blank'
+require 'active_support/core_ext/class/attribute'
module ActiveRecord
# = Active Record Named \Scopes
module NamedScope
extend ActiveSupport::Concern
+ included do
+ class_attribute :scopes
+ self.scopes = {}
+ end
+
module ClassMethods
# Returns an anonymous \scope.
#
@@ -33,10 +39,6 @@ module ActiveRecord
end
end
- def scopes
- read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
- end
-
# Adds a class method for retrieving and querying objects. A \scope represents a narrowing of a database query,
# such as <tt>where(:color => :red).select('shirts.*').includes(:washing_instructions)</tt>.
#
diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb
index 0c3392263a..f1d3eaed38 100644
--- a/activerecord/lib/active_record/nested_attributes.rb
+++ b/activerecord/lib/active_record/nested_attributes.rb
@@ -2,6 +2,7 @@ require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/object/try'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/hash/indifferent_access'
+require 'active_support/core_ext/class/attribute'
module ActiveRecord
module NestedAttributes #:nodoc:
@@ -11,7 +12,7 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_inheritable_accessor :nested_attributes_options, :instance_writer => false
+ class_attribute :nested_attributes_options, :instance_writer => false
self.nested_attributes_options = {}
end
@@ -268,7 +269,11 @@ module ActiveRecord
if reflection = reflect_on_association(association_name)
reflection.options[:autosave] = true
add_autosave_association_callbacks(reflection)
+
+ nested_attributes_options = self.nested_attributes_options.dup
nested_attributes_options[association_name.to_sym] = options
+ self.nested_attributes_options = nested_attributes_options
+
type = (reflection.collection? ? :collection : :one_to_one)
# def pirate_attributes=(attributes)
@@ -315,7 +320,7 @@ module ActiveRecord
# update_only is true, and a <tt>:_destroy</tt> key set to a truthy value,
# then the existing record will be marked for destruction.
def assign_nested_attributes_for_one_to_one_association(association_name, attributes)
- options = nested_attributes_options[association_name]
+ options = self.nested_attributes_options[association_name]
attributes = attributes.with_indifferent_access
check_existing_record = (options[:update_only] || !attributes['id'].blank?)
@@ -364,7 +369,7 @@ module ActiveRecord
# { :id => '2', :_destroy => true }
# ])
def assign_nested_attributes_for_collection_association(association_name, attributes_collection)
- options = nested_attributes_options[association_name]
+ options = self.nested_attributes_options[association_name]
unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array)
raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})"
@@ -433,7 +438,7 @@ module ActiveRecord
end
def call_reject_if(association_name, attributes)
- case callback = nested_attributes_options[association_name][:reject_if]
+ case callback = self.nested_attributes_options[association_name][:reject_if]
when Symbol
method(callback).arity == 0 ? send(callback) : send(callback, attributes)
when Proc
diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb
index a2260e9a19..a07c321960 100644
--- a/activerecord/lib/active_record/reflection.rb
+++ b/activerecord/lib/active_record/reflection.rb
@@ -1,8 +1,15 @@
+require 'active_support/core_ext/class/attribute'
+
module ActiveRecord
# = Active Record Reflection
module Reflection # :nodoc:
extend ActiveSupport::Concern
+ included do
+ class_attribute :reflections
+ self.reflections = {}
+ end
+
# Reflection enables to interrogate Active Record classes and objects
# about their associations and aggregations. This information can,
# for example, be used in a form builder that takes an Active Record object
@@ -20,18 +27,9 @@ module ActiveRecord
when :composed_of
reflection = AggregateReflection.new(macro, name, options, active_record)
end
- write_inheritable_hash :reflections, name => reflection
- reflection
- end
- # Returns a hash containing all AssociationReflection objects for the current class.
- # Example:
- #
- # Invoice.reflections
- # Account.reflections
- #
- def reflections
- read_inheritable_attribute(:reflections) || write_inheritable_attribute(:reflections, {})
+ self.reflections = self.reflections.merge(name => reflection)
+ reflection
end
# Returns an array of AggregateReflection objects for all the aggregations in the class.
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index 6bf698fe97..c8adaddfca 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -208,14 +208,16 @@ module ActiveRecord
end
def execute_grouped_calculation(operation, column_name, distinct) #:nodoc:
- group_attr = @group_values.first
- association = @klass.reflect_on_association(group_attr.to_sym)
- associated = association && association.macro == :belongs_to # only count belongs_to associations
- group_field = associated ? association.primary_key_name : group_attr
- group_alias = column_alias_for(group_field)
- group_column = column_for(group_field)
+ group_attr = @group_values
+ association = @klass.reflect_on_association(group_attr.first.to_sym)
+ associated = group_attr.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations
+ group_fields = Array(associated ? association.primary_key_name : group_attr)
+ group_aliases = group_fields.map { |field| column_alias_for(field) }
+ group_columns = group_aliases.zip(group_fields).map { |aliaz,field|
+ [aliaz, column_for(field)]
+ }
- group = @klass.connection.adapter_name == 'FrontBase' ? group_alias : group_field
+ group = @klass.connection.adapter_name == 'FrontBase' ? group_aliases : group_fields
if operation == 'count' && column_name == :all
aggregate_alias = 'count_all'
@@ -223,22 +225,33 @@ module ActiveRecord
aggregate_alias = column_alias_for(operation, column_name)
end
- relation = except(:group).group(group)
- relation.select_values = [
- operation_over_aggregate_column(aggregate_column(column_name), operation, distinct).as(aggregate_alias),
- "#{group_field} AS #{group_alias}"
+ select_values = [
+ operation_over_aggregate_column(
+ aggregate_column(column_name),
+ operation,
+ distinct).as(aggregate_alias)
]
+ select_values.concat group_fields.zip(group_aliases).map { |field,aliaz|
+ "#{field} AS #{aliaz}"
+ }
+
+ relation = except(:group).group(group.join(','))
+ relation.select_values = select_values
+
calculated_data = @klass.connection.select_all(relation.to_sql)
if association
- key_ids = calculated_data.collect { |row| row[group_alias] }
+ key_ids = calculated_data.collect { |row| row[group_aliases.first] }
key_records = association.klass.base_class.find(key_ids)
key_records = Hash[key_records.map { |r| [r.id, r] }]
end
ActiveSupport::OrderedHash[calculated_data.map do |row|
- key = type_cast_calculated_value(row[group_alias], group_column)
+ key = group_columns.map { |aliaz, column|
+ type_cast_calculated_value(row[aliaz], column)
+ }
+ key = key.first if key.size == 1
key = key_records[key] if associated
[key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)]
end]
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index c5428dccd6..32c7d08daa 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -25,6 +25,11 @@ module ActiveRecord
attribute.in(values)
when Range, Arel::Relation
attribute.in(value)
+ when ActiveRecord::Base
+ attribute.eq(value.quoted_id)
+ when Class
+ # FIXME: I think we need to deprecate this behavior
+ attribute.eq(value.name)
else
attribute.eq(value)
end
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 9c399d3333..9e7503a60d 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -141,7 +141,7 @@ module ActiveRecord
"#{@klass.table_name}.#{@klass.primary_key} DESC" :
reverse_sql_order(order_clause).join(', ')
- except(:order).order(Arel::SqlLiteral.new(order))
+ except(:order).order(Arel.sql(order))
end
def arel
@@ -174,10 +174,7 @@ module ActiveRecord
arel = build_joins(arel, @joins_values) unless @joins_values.empty?
- (@where_values - ['']).uniq.each do |where|
- where = Arel.sql(where) if String === where
- arel = arel.where(Arel::Nodes::Grouping.new(where))
- end
+ arel = collapse_wheres(arel, (@where_values - ['']).uniq)
arel = arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty?
@@ -198,6 +195,27 @@ module ActiveRecord
private
+ def collapse_wheres(arel, wheres)
+ equalities = wheres.grep(Arel::Nodes::Equality)
+
+ groups = equalities.group_by do |equality|
+ equality.left
+ end
+
+ groups.each do |_, eqls|
+ test = eqls.inject(eqls.shift) do |memo, expr|
+ memo.or(expr)
+ end
+ arel = arel.where(test)
+ end
+
+ (wheres - equalities).each do |where|
+ where = Arel.sql(where) if String === where
+ arel = arel.where(Arel::Nodes::Grouping.new(where))
+ end
+ arel
+ end
+
def build_where(opts, other = [])
case opts
when String, Array
diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb
index c1bc3214ea..c6bb5c1961 100644
--- a/activerecord/lib/active_record/schema.rb
+++ b/activerecord/lib/active_record/schema.rb
@@ -30,9 +30,7 @@ module ActiveRecord
# ActiveRecord::Schema is only supported by database adapters that also
# support migrations, the two features being very similar.
class Schema < Migration
- private_class_method :new
-
- def self.migrations_path
+ def migrations_path
ActiveRecord::Migrator.migrations_path
end
@@ -48,11 +46,12 @@ module ActiveRecord
# ...
# end
def self.define(info={}, &block)
- instance_eval(&block)
+ schema = new
+ schema.instance_eval(&block)
unless info[:version].blank?
initialize_schema_migrations_table
- assume_migrated_upto_version(info[:version], migrations_path)
+ assume_migrated_upto_version(info[:version], schema.migrations_path)
end
end
end
diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb
index 230adf6b2b..2ecbd906bd 100644
--- a/activerecord/lib/active_record/timestamp.rb
+++ b/activerecord/lib/active_record/timestamp.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/class/attribute'
+
module ActiveRecord
# = Active Record Timestamp
#
@@ -29,14 +31,14 @@ module ActiveRecord
extend ActiveSupport::Concern
included do
- class_inheritable_accessor :record_timestamps, :instance_writer => false
+ class_attribute :record_timestamps, :instance_writer => false
self.record_timestamps = true
end
private
def create #:nodoc:
- if record_timestamps
+ if self.record_timestamps
current_time = current_time_from_proper_timezone
all_timestamp_attributes.each do |column|
@@ -61,7 +63,7 @@ module ActiveRecord
end
def should_record_timestamps?
- record_timestamps && (!partial_updates? || changed? || (attributes.keys & self.class.serialized_attributes.keys).present?)
+ self.record_timestamps && (!partial_updates? || changed? || (attributes.keys & self.class.serialized_attributes.keys).present?)
end
def timestamp_attributes_for_update_in_model
diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb
index 89eba15be1..0667be7d23 100644
--- a/activerecord/lib/active_record/version.rb
+++ b/activerecord/lib/active_record/version.rb
@@ -3,8 +3,8 @@ module ActiveRecord
MAJOR = 3
MINOR = 1
TINY = 0
- BUILD = "beta"
+ PRE = "beta"
- STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
end
diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
index 8ac21c1410..126b6f434b 100644
--- a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb
@@ -1,5 +1,5 @@
class <%= migration_class_name %> < ActiveRecord::Migration
- def self.up
+ def up
<% attributes.each do |attribute| -%>
<%- if migration_action -%>
<%= migration_action %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'add' %>, :<%= attribute.type %><% end %>
@@ -7,7 +7,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration
<%- end -%>
end
- def self.down
+ def down
<% attributes.reverse.each do |attribute| -%>
<%- if migration_action -%>
<%= migration_action == 'add' ? 'remove' : 'add' %>_column :<%= table_name %>, :<%= attribute.name %><% if migration_action == 'remove' %>, :<%= attribute.type %><% end %>
diff --git a/activerecord/lib/rails/generators/active_record/model/templates/migration.rb b/activerecord/lib/rails/generators/active_record/model/templates/migration.rb
index 1f68487304..70e064be21 100644
--- a/activerecord/lib/rails/generators/active_record/model/templates/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/model/templates/migration.rb
@@ -1,5 +1,5 @@
class <%= migration_class_name %> < ActiveRecord::Migration
- def self.up
+ def up
create_table :<%= table_name %> do |t|
<% for attribute in attributes -%>
t.<%= attribute.type %> :<%= attribute.name %>
@@ -10,7 +10,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration
end
end
- def self.down
+ def down
drop_table :<%= table_name %>
end
end
diff --git a/activerecord/lib/rails/generators/active_record/session_migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/session_migration/templates/migration.rb
index 919822af7b..8f0bf1ef0d 100644
--- a/activerecord/lib/rails/generators/active_record/session_migration/templates/migration.rb
+++ b/activerecord/lib/rails/generators/active_record/session_migration/templates/migration.rb
@@ -1,5 +1,5 @@
class <%= migration_class_name %> < ActiveRecord::Migration
- def self.up
+ def up
create_table :<%= session_table_name %> do |t|
t.string :session_id, :null => false
t.text :data
@@ -10,7 +10,7 @@ class <%= migration_class_name %> < ActiveRecord::Migration
add_index :<%= session_table_name %>, :updated_at
end
- def self.down
+ def down
drop_table :<%= session_table_name %>
end
end
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index 37c6f354a8..0742e311d9 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -10,7 +10,8 @@ require 'models/reply'
require 'models/person'
class CascadedEagerLoadingTest < ActiveRecord::TestCase
- fixtures :authors, :mixins, :companies, :posts, :topics, :accounts, :comments, :categorizations, :people
+ fixtures :authors, :mixins, :companies, :posts, :topics, :accounts, :comments,
+ :categorizations, :people, :categories
def test_eager_association_loading_with_cascaded_two_levels
authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id")
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 66fb5ac1e1..c00b8a1cde 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -79,6 +79,58 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
end
+ def test_preloading_has_many_in_multiple_queries_with_more_ids_than_database_can_handle
+ Post.connection.expects(:ids_in_list_limit).at_least_once.returns(5)
+ posts = Post.find(:all, :include=>:comments)
+ assert_equal 7, posts.size
+ end
+
+ def test_preloading_has_many_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle
+ Post.connection.expects(:ids_in_list_limit).at_least_once.returns(nil)
+ posts = Post.find(:all, :include=>:comments)
+ assert_equal 7, posts.size
+ end
+
+ def test_preloading_habtm_in_multiple_queries_with_more_ids_than_database_can_handle
+ Post.connection.expects(:ids_in_list_limit).at_least_once.returns(5)
+ posts = Post.find(:all, :include=>:categories)
+ assert_equal 7, posts.size
+ end
+
+ def test_preloading_habtm_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle
+ Post.connection.expects(:ids_in_list_limit).at_least_once.returns(nil)
+ posts = Post.find(:all, :include=>:categories)
+ assert_equal 7, posts.size
+ end
+
+ def test_load_associated_records_in_one_query_when_adapter_has_no_limit
+ Post.connection.expects(:ids_in_list_limit).at_least_once.returns(nil)
+ Post.expects(:i_was_called).with([1,2,3,4,5,6,7]).returns([1])
+ associated_records = Post.send(:associated_records, [1,2,3,4,5,6,7]) do |some_ids|
+ Post.i_was_called(some_ids)
+ end
+ assert_equal [1], associated_records
+ end
+
+ def test_load_associated_records_in_several_queries_when_many_ids_passed
+ Post.connection.expects(:ids_in_list_limit).at_least_once.returns(5)
+ Post.expects(:i_was_called).with([1,2,3,4,5]).returns([1])
+ Post.expects(:i_was_called).with([6,7]).returns([6])
+ associated_records = Post.send(:associated_records, [1,2,3,4,5,6,7]) do |some_ids|
+ Post.i_was_called(some_ids)
+ end
+ assert_equal [1,6], associated_records
+ end
+
+ def test_load_associated_records_in_one_query_when_a_few_ids_passed
+ Post.connection.expects(:ids_in_list_limit).at_least_once.returns(5)
+ Post.expects(:i_was_called).with([1,2,3]).returns([1])
+ associated_records = Post.send(:associated_records, [1,2,3]) do |some_ids|
+ Post.i_was_called(some_ids)
+ end
+ assert_equal [1], associated_records
+ end
+
def test_including_duplicate_objects_from_belongs_to
popular_post = Post.create!(:title => 'foo', :body => "I like cars!")
comment = popular_post.comments.create!(:body => "lol")
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index ecfc769f3a..33c53e695b 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -1282,4 +1282,25 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
comment = post.comments.build
assert post.comments.include?(comment)
end
+
+ def test_load_target_respects_protected_attributes
+ topic = Topic.create!
+ reply = topic.replies.create(:title => "reply 1")
+ reply.approved = false
+ reply.save!
+
+ # Save with a different object instance, so the instance that's still held
+ # in topic.relies doesn't know about the changed attribute.
+ reply2 = Reply.find(reply.id)
+ reply2.approved = true
+ reply2.save!
+
+ # Force loading the collection from the db. This will merge the existing
+ # object (reply) with what gets loaded from the db (which includes the
+ # changed approved attribute). approved is a protected attribute, so if mass
+ # assignment is used, it won't get updated and will still be false.
+ first = topic.replies.to_a.first
+ assert_equal reply.id, first.id
+ assert_equal true, first.approved?
+ end
end
diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb
index fa5c2e49df..081583038f 100644
--- a/activerecord/test/cases/associations/inverse_associations_test.rb
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -551,8 +551,8 @@ class InverseMultipleHasManyInversesForSameModel < ActiveRecord::TestCase
def test_that_we_can_load_associations_that_have_the_same_reciprocal_name_from_different_models
assert_nothing_raised(ActiveRecord::AssociationTypeMismatch) do
i = Interest.find(:first)
- z = i.zine
- m = i.man
+ i.zine
+ i.man
end
end
diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb
index dd8152b219..83c605d2bb 100644
--- a/activerecord/test/cases/associations_test.rb
+++ b/activerecord/test/cases/associations_test.rb
@@ -120,7 +120,7 @@ class AssociationsTest < ActiveRecord::TestCase
def test_force_reload_is_uncached
firm = Firm.create!("name" => "A New Firm, Inc")
- client = Client.create!("name" => "TheClient.com", :firm => firm)
+ Client.create!("name" => "TheClient.com", :firm => firm)
ActiveRecord::Base.cache do
firm.clients.each {}
assert_queries(0) { assert_not_nil firm.clients.each {} }
@@ -270,17 +270,17 @@ class OverridingAssociationsTest < ActiveRecord::TestCase
def test_habtm_association_redefinition_callbacks_should_differ_and_not_inherited
# redeclared association on AR descendant should not inherit callbacks from superclass
- callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many)
+ callbacks = PeopleList.before_add_for_has_and_belongs_to_many
assert_equal([:enlist], callbacks)
- callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many)
+ callbacks = DifferentPeopleList.before_add_for_has_and_belongs_to_many
assert_equal([], callbacks)
end
def test_has_many_association_redefinition_callbacks_should_differ_and_not_inherited
# redeclared association on AR descendant should not inherit callbacks from superclass
- callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_many)
+ callbacks = PeopleList.before_add_for_has_many
assert_equal([:enlist], callbacks)
- callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_many)
+ callbacks = DifferentPeopleList.before_add_for_has_many
assert_equal([], callbacks)
end
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index ab9a65944f..bb0166a60c 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -568,7 +568,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
def test_bulk_update_respects_access_control
privatize("title=(value)")
- assert_raise(ActiveRecord::UnknownAttributeError) { topic = @target.new(:title => "Rants about pants") }
+ assert_raise(ActiveRecord::UnknownAttributeError) { @target.new(:title => "Rants about pants") }
assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { :title => "Ants in pants" } }
end
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 459f9fa55c..b13cb2d7a2 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -355,8 +355,6 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
end
def test_invalid_adding_before_save
- no_of_firms = Firm.count
- no_of_clients = Client.count
new_firm = Firm.new("name" => "A New Firm, Inc")
new_firm.clients_of_firm.concat([c = Client.new, Client.new("name" => "Apple")])
assert !c.persisted?
@@ -461,7 +459,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
def test_build_many_before_save
company = companies(:first_firm)
- new_clients = assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
+ assert_no_queries { company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) }
company.name += '-changed'
assert_queries(3) { assert company.save }
@@ -481,7 +479,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
def test_build_many_via_block_before_save
company = companies(:first_firm)
- new_clients = assert_no_queries do
+ assert_no_queries do
company.clients_of_firm.build([{"name" => "Another Client"}, {"name" => "Another Client II"}]) do |client|
client.name = "changed"
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 9f2b0c9c86..26f388ca46 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -182,7 +182,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_initialize_with_invalid_attribute
begin
- topic = Topic.new({ "title" => "test",
+ Topic.new({ "title" => "test",
"last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31"})
rescue ActiveRecord::MultiparameterAssignmentErrors => ex
assert_equal(1, ex.errors.size)
@@ -397,6 +397,15 @@ class BasicsTest < ActiveRecord::TestCase
assert_not_equal Topic.new, Topic.new
end
+ def test_equality_of_destroyed_records
+ topic_1 = Topic.new(:title => 'test_1')
+ topic_1.save
+ topic_2 = Topic.find(topic_1.id)
+ topic_1.destroy
+ assert_equal topic_1, topic_2
+ assert_equal topic_2, topic_1
+ end
+
def test_hashing
assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ]
end
@@ -963,7 +972,6 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_nil_serialized_attribute_with_class_constraint
- myobj = MyObject.new('value1', 'value2')
topic = Topic.new
assert_nil topic.content
end
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 61fbf01a50..5cb8485b4b 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -54,6 +54,19 @@ class CalculationsTest < ActiveRecord::TestCase
c = Account.sum(:credit_limit, :group => :firm_id)
[1,6,2].each { |firm_id| assert c.keys.include?(firm_id) }
end
+
+ def test_should_group_by_multiple_fields
+ c = Account.count(:all, :group => ['firm_id', :credit_limit])
+ [ [nil, 50], [1, 50], [6, 50], [6, 55], [9, 53], [2, 60] ].each { |firm_and_limit| assert c.keys.include?(firm_and_limit) }
+ end
+
+ def test_should_group_by_multiple_fields_having_functions
+ c = Topic.group(:author_name, 'COALESCE(type, title)').count(:all)
+ assert_equal 1, c[["Carl", "The Third Topic of the day"]]
+ assert_equal 1, c[["Mary", "Reply"]]
+ assert_equal 1, c[["David", "The First Topic"]]
+ assert_equal 1, c[["Carl", "Reply"]]
+ end
def test_should_group_by_summed_field
c = Account.sum(:credit_limit, :group => :firm_id)
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 39ce47d9d6..31e4981a1d 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -949,7 +949,7 @@ class FinderTest < ActiveRecord::TestCase
# http://dev.rubyonrails.org/ticket/6778
def test_find_ignores_previously_inserted_record
- post = Post.create!(:title => 'test', :body => 'it out')
+ Post.create!(:title => 'test', :body => 'it out')
assert_equal [], Post.find_all_by_id(nil)
end
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index d5ef30e137..9ce163a00f 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -58,7 +58,7 @@ class FixturesTest < ActiveRecord::TestCase
end
def test_inserts
- topics = create_fixtures("topics")
+ create_fixtures("topics")
first_row = ActiveRecord::Base.connection.select_one("SELECT * FROM topics WHERE author_name = 'David'")
assert_equal("The First Topic", first_row["title"])
@@ -114,7 +114,7 @@ class FixturesTest < ActiveRecord::TestCase
end
def test_insert_with_datetime
- topics = create_fixtures("tasks")
+ create_fixtures("tasks")
first = Task.find(1)
assert first
end
@@ -240,7 +240,7 @@ if Account.connection.respond_to?(:reset_pk_sequence!)
def test_create_fixtures_resets_sequences_when_not_cached
@instances.each do |instance|
- max_id = create_fixtures(instance.class.table_name).inject(0) do |_max_id, (name, fixture)|
+ max_id = create_fixtures(instance.class.table_name).inject(0) do |_max_id, (_, fixture)|
fixture_id = fixture['id'].to_i
fixture_id > _max_id ? fixture_id : _max_id
end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 52f26b71f5..f9bbc5299b 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -13,11 +13,6 @@ require 'active_record'
require 'active_support/dependencies'
require 'connection'
-begin
- require 'ruby-debug'
-rescue LoadError
-end
-
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb
new file mode 100644
index 0000000000..afec64750e
--- /dev/null
+++ b/activerecord/test/cases/invertible_migration_test.rb
@@ -0,0 +1,57 @@
+require "cases/helper"
+
+module ActiveRecord
+ class InvertibleMigrationTest < ActiveRecord::TestCase
+ class SilentMigration < ActiveRecord::Migration
+ def write(text = '')
+ # sssshhhhh!!
+ end
+ end
+
+ class InvertibleMigration < SilentMigration
+ def change
+ create_table("horses") do |t|
+ t.column :content, :text
+ t.column :remind_at, :datetime
+ end
+ end
+ end
+
+ class NonInvertibleMigration < SilentMigration
+ def change
+ create_table("horses") do |t|
+ t.column :content, :text
+ t.column :remind_at, :datetime
+ end
+ remove_column "horses", :content
+ end
+ end
+
+ def teardown
+ if ActiveRecord::Base.connection.table_exists?("horses")
+ ActiveRecord::Base.connection.drop_table("horses")
+ end
+ end
+
+ def test_no_reverse
+ migration = NonInvertibleMigration.new
+ migration.migrate(:up)
+ assert_raises(IrreversibleMigration) do
+ migration.migrate(:down)
+ end
+ end
+
+ def test_up
+ migration = InvertibleMigration.new
+ migration.migrate(:up)
+ assert migration.connection.table_exists?("horses"), "horses should exist"
+ end
+
+ def test_down
+ migration = InvertibleMigration.new
+ migration.migrate :up
+ migration.migrate :down
+ assert !migration.connection.table_exists?("horses")
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb
new file mode 100644
index 0000000000..ea2292dda5
--- /dev/null
+++ b/activerecord/test/cases/migration/command_recorder_test.rb
@@ -0,0 +1,108 @@
+require "cases/helper"
+
+module ActiveRecord
+ class Migration
+ class CommandRecorderTest < ActiveRecord::TestCase
+ def setup
+ @recorder = CommandRecorder.new
+ end
+
+ def test_respond_to_delegates
+ recorder = CommandRecorder.new(Class.new {
+ def america; end
+ }.new)
+ assert recorder.respond_to?(:america)
+ end
+
+ def test_send_calls_super
+ assert_raises(NoMethodError) do
+ @recorder.send(:create_table, :horses)
+ end
+ end
+
+ def test_send_delegates_to_record
+ recorder = CommandRecorder.new(Class.new {
+ def create_table(name); end
+ }.new)
+ assert recorder.respond_to?(:create_table), 'respond_to? create_table'
+ recorder.send(:create_table, :horses)
+ assert_equal [[:create_table, [:horses]]], recorder.commands
+ end
+
+ def test_unknown_commands_raise_exception
+ @recorder.record :execute, ['some sql']
+ assert_raises(ActiveRecord::IrreversibleMigration) do
+ @recorder.inverse
+ end
+ end
+
+ def test_record
+ @recorder.record :create_table, [:system_settings]
+ assert_equal 1, @recorder.commands.length
+ end
+
+ def test_inverse
+ @recorder.record :create_table, [:system_settings]
+ assert_equal 1, @recorder.inverse.length
+
+ @recorder.record :rename_table, [:old, :new]
+ assert_equal 2, @recorder.inverse.length
+ end
+
+ def test_inverted_commands_are_reveresed
+ @recorder.record :create_table, [:hello]
+ @recorder.record :create_table, [:world]
+ tables = @recorder.inverse.map(&:last)
+ assert_equal [[:world], [:hello]], tables
+ end
+
+ def test_invert_create_table
+ @recorder.record :create_table, [:system_settings]
+ drop_table = @recorder.inverse.first
+ assert_equal [:drop_table, [:system_settings]], drop_table
+ end
+
+ def test_invert_rename_table
+ @recorder.record :rename_table, [:old, :new]
+ rename = @recorder.inverse.first
+ assert_equal [:rename_table, [:new, :old]], rename
+ end
+
+ def test_invert_add_column
+ @recorder.record :add_column, [:table, :column, :type, {}]
+ remove = @recorder.inverse.first
+ assert_equal [:remove_column, [:table, :column]], remove
+ end
+
+ def test_invert_rename_column
+ @recorder.record :rename_column, [:table, :old, :new]
+ rename = @recorder.inverse.first
+ assert_equal [:rename_column, [:table, :new, :old]], rename
+ end
+
+ def test_invert_add_index
+ @recorder.record :add_index, [:table, [:one, :two], {:options => true}]
+ remove = @recorder.inverse.first
+ assert_equal [:remove_index, [:table, {:column => [:one, :two]}]], remove
+ end
+
+ def test_invert_rename_index
+ @recorder.record :rename_index, [:old, :new]
+ rename = @recorder.inverse.first
+ assert_equal [:rename_index, [:new, :old]], rename
+ end
+
+ def test_invert_add_timestamps
+ @recorder.record :add_timestamps, [:table]
+ remove = @recorder.inverse.first
+ assert_equal [:remove_timestamps, [:table]], remove
+ end
+
+ def test_invert_remove_timestamps
+ @recorder.record :remove_timestamps, [:table]
+ add = @recorder.inverse.first
+ assert_equal [:add_timestamps, [:table]], add
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index e6eef805cf..3037d73a1b 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -18,10 +18,11 @@ if ActiveRecord::Base.connection.supports_migrations?
class ActiveRecord::Migration
class <<self
attr_accessor :message_count
- def puts(text="")
- self.message_count ||= 0
- self.message_count += 1
- end
+ end
+
+ def puts(text="")
+ self.class.message_count ||= 0
+ self.class.message_count += 1
end
end
@@ -1165,6 +1166,44 @@ if ActiveRecord::Base.connection.supports_migrations?
assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
end
+ class MockMigration < ActiveRecord::Migration
+ attr_reader :went_up, :went_down
+ def initialize
+ @went_up = false
+ @went_down = false
+ end
+
+ def up
+ @went_up = true
+ super
+ end
+
+ def down
+ @went_down = true
+ super
+ end
+ end
+
+ def test_instance_based_migration_up
+ migration = MockMigration.new
+ assert !migration.went_up, 'have not gone up'
+ assert !migration.went_down, 'have not gone down'
+
+ migration.migrate :up
+ assert migration.went_up, 'have gone up'
+ assert !migration.went_down, 'have not gone down'
+ end
+
+ def test_instance_based_migration_down
+ migration = MockMigration.new
+ assert !migration.went_up, 'have not gone up'
+ assert !migration.went_down, 'have not gone down'
+
+ migration.migrate :down
+ assert !migration.went_up, 'have gone up'
+ assert migration.went_down, 'have not gone down'
+ end
+
def test_migrator_one_up
assert !Person.column_methods_hash.include?(:last_name)
assert !Reminder.table_exists?
@@ -1312,20 +1351,20 @@ if ActiveRecord::Base.connection.supports_migrations?
def test_migrator_verbosity
ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
- assert PeopleHaveLastNames.message_count > 0
+ assert_operator PeopleHaveLastNames.message_count, :>, 0
PeopleHaveLastNames.message_count = 0
ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
- assert PeopleHaveLastNames.message_count > 0
+ assert_operator PeopleHaveLastNames.message_count, :>, 0
PeopleHaveLastNames.message_count = 0
end
def test_migrator_verbosity_off
PeopleHaveLastNames.verbose = false
ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
- assert PeopleHaveLastNames.message_count.zero?
+ assert_equal 0, PeopleHaveLastNames.message_count
ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
- assert PeopleHaveLastNames.message_count.zero?
+ assert_equal 0, PeopleHaveLastNames.message_count
end
def test_migrator_going_down_due_to_version_target
@@ -1947,7 +1986,7 @@ if ActiveRecord::Base.connection.supports_migrations?
@migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
@existing_migrations = Dir[@migrations_path + "/*.rb"]
- Time.travel_to(created_at = Time.utc(2010, 7, 26, 10, 10, 10)) do
+ Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
@@ -1972,7 +2011,7 @@ if ActiveRecord::Base.connection.supports_migrations?
sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps2"
- Time.travel_to(created_at = Time.utc(2010, 7, 26, 10, 10, 10)) do
+ Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
copied = ActiveRecord::Migration.copy(@migrations_path, sources)
assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
@@ -1992,7 +2031,7 @@ if ActiveRecord::Base.connection.supports_migrations?
@migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
@existing_migrations = Dir[@migrations_path + "/*.rb"]
- Time.travel_to(created_at = Time.utc(2010, 2, 20, 10, 10, 10)) do
+ Time.travel_to(Time.utc(2010, 2, 20, 10, 10, 10)) do
ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
assert File.exists?(@migrations_path + "/20100301010102_people_have_hobbies.rb")
assert File.exists?(@migrations_path + "/20100301010103_people_have_descriptions.rb")
@@ -2024,11 +2063,26 @@ if ActiveRecord::Base.connection.supports_migrations?
clear
end
+ def test_copying_migrations_to_non_existing_directory
+ @migrations_path = MIGRATIONS_ROOT + "/non_existing"
+ @existing_migrations = []
+
+ Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
+ assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
+ assert_equal 2, copied.length
+ end
+ ensure
+ clear
+ Dir.delete(@migrations_path)
+ end
+
def test_copying_migrations_to_empty_directory
@migrations_path = MIGRATIONS_ROOT + "/empty"
@existing_migrations = []
- Time.travel_to(created_at = Time.utc(2010, 7, 26, 10, 10, 10)) do
+ Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index fb24c65fff..6ac3e3fc56 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -122,7 +122,7 @@ class NamedScopeTest < ActiveRecord::TestCase
:joins => 'JOIN authors ON authors.id = posts.author_id',
:conditions => [ 'authors.author_address_id = ?', address.id ]
)
- assert_equal posts_with_authors_at_address_titles, Post.with_authors_at_address(address).find(:all, :select => 'title')
+ assert_equal posts_with_authors_at_address_titles.map(&:title), Post.with_authors_at_address(address).find(:all, :select => 'title').map(&:title)
end
def test_scope_with_object
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index 07262f56be..8ca9d626d1 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -192,7 +192,6 @@ class PersistencesTest < ActiveRecord::TestCase
topic = Topic.create("title" => "New Topic") do |t|
t.author_name = "David"
end
- topicReloaded = Topic.find(topic.id)
assert_equal("New Topic", topic.title)
assert_equal("David", topic.author_name)
end
@@ -270,7 +269,7 @@ class PersistencesTest < ActiveRecord::TestCase
end
def test_record_not_found_exception
- assert_raise(ActiveRecord::RecordNotFound) { topicReloaded = Topic.find(99999) }
+ assert_raise(ActiveRecord::RecordNotFound) { Topic.find(99999) }
end
def test_update_all
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 5bb21a54bd..33916c4e46 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -133,7 +133,6 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase
def test_cache_is_expired_by_habtm_delete
ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
ActiveRecord::Base.cache do
- c = Category.find(1)
p = Post.find(1)
assert p.categories.any?
p.categories.delete_all
diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb
index a27e2e72cd..dae9721a63 100644
--- a/activerecord/test/cases/relation_scoping_test.rb
+++ b/activerecord/test/cases/relation_scoping_test.rb
@@ -254,13 +254,11 @@ class HasManyScopingTest< ActiveRecord::TestCase
end
def test_should_maintain_default_scope_on_associations
- person = people(:michael)
magician = BadReference.find(1)
assert_equal [magician], people(:michael).bad_references
end
def test_should_default_scope_on_associations_is_overriden_by_association_conditions
- person = people(:michael)
assert_equal [], people(:michael).fixed_bad_references
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index b44c716db8..535bcd4396 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -426,6 +426,52 @@ class RelationTest < ActiveRecord::TestCase
assert_blank authors.all
end
+ def test_where_with_ar_object
+ author = Author.first
+ authors = Author.scoped.where(:id => author)
+ assert_equal 1, authors.all.length
+ end
+
+ def test_find_with_list_of_ar
+ author = Author.first
+ authors = Author.find([author])
+ assert_equal author, authors.first
+ end
+
+ class Mary < Author; end
+
+ def test_find_by_classname
+ Author.create!(:name => Mary.name)
+ assert_equal 1, Author.where(:name => Mary).size
+ end
+
+ def test_find_by_id_with_list_of_ar
+ author = Author.first
+ authors = Author.find_by_id([author])
+ assert_equal author, authors
+ end
+
+ def test_find_all_using_where_twice_should_or_the_relation
+ david = authors(:david)
+ relation = Author.unscoped
+ relation = relation.where(:name => david.name)
+ relation = relation.where(:name => 'Santiago')
+ relation = relation.where(:id => david.id)
+ assert_equal [david], relation.all
+ end
+
+ def test_find_all_with_multiple_ors
+ david = authors(:david)
+ relation = [
+ { :name => david.name },
+ { :name => 'Santiago' },
+ { :name => 'tenderlove' },
+ ].inject(Author.unscoped) do |memo, param|
+ memo.where(param)
+ end
+ assert_equal [david], relation.all
+ end
+
def test_exists
davids = Author.where(:name => 'David')
assert davids.exists?
diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb
index eb93761fb2..70c098bc6d 100644
--- a/activerecord/test/cases/timestamp_test.rb
+++ b/activerecord/test/cases/timestamp_test.rb
@@ -113,7 +113,7 @@ class TimestampTest < ActiveRecord::TestCase
pet = Pet.first
owner = pet.owner
- owner.update_attribute(:happy_at, (time = 3.days.ago))
+ owner.update_attribute(:happy_at, 3.days.ago)
previously_owner_updated_at = owner.updated_at
pet.name = "I'm a parrot"
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index dd9de3510b..b0ccd71836 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -163,7 +163,7 @@ class TransactionTest < ActiveRecord::TestCase
@first.author_name += '_this_should_not_end_up_in_the_db'
@first.save!
flunk
- rescue => e
+ rescue
assert_equal original_author_name, @first.reload.author_name
assert_equal nbooks_before_save, Book.count
ensure
diff --git a/activerecord/test/cases/validations/association_validation_test.rb b/activerecord/test/cases/validations/association_validation_test.rb
index 1246dd4276..56e345990f 100644
--- a/activerecord/test/cases/validations/association_validation_test.rb
+++ b/activerecord/test/cases/validations/association_validation_test.rb
@@ -17,7 +17,7 @@ class AssociationValidationTest < ActiveRecord::TestCase
o = Owner.new('name' => 'nopets')
assert !o.save
assert o.errors[:pets].any?
- pet = o.pets.build('name' => 'apet')
+ o.pets.build('name' => 'apet')
assert o.valid?
end
@@ -27,7 +27,7 @@ class AssociationValidationTest < ActiveRecord::TestCase
assert !o.save
assert o.errors[:pets].any?
- pet = o.pets.build('name' => 'apet')
+ o.pets.build('name' => 'apet')
assert o.valid?
2.times { o.pets.build('name' => 'apet') }
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index 9a863c25a8..679d67553b 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -60,7 +60,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
def test_validates_uniqueness_with_validates
Topic.validates :title, :uniqueness => true
- t = Topic.create!('title' => 'abc')
+ Topic.create!('title' => 'abc')
t2 = Topic.new('title' => 'abc')
assert !t2.valid?
@@ -201,7 +201,7 @@ class UniquenessValidationTest < ActiveRecord::TestCase
def test_validate_case_sensitive_uniqueness_with_attribute_passed_as_integer
Topic.validates_uniqueness_of(:title, :case_sensitve => true)
- t = Topic.create!('title' => 101)
+ Topic.create!('title' => 101)
t2 = Topic.new('title' => 101)
assert !t2.valid?
diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb
index b5b46d7431..d959fd103a 100644
--- a/activeresource/lib/active_resource/base.rb
+++ b/activeresource/lib/active_resource/base.rb
@@ -1,6 +1,6 @@
require 'active_support'
require 'active_support/core_ext/class/attribute_accessors'
-require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/module/attr_accessor_with_default'
@@ -263,6 +263,8 @@ module ActiveResource
# The logger for diagnosing and tracing Active Resource calls.
cattr_accessor :logger
+ class_attribute :_format
+
class << self
# Creates a schema for this resource - setting the attributes that are
# known prior to fetching an instance from the remote system.
@@ -492,13 +494,13 @@ module ActiveResource
format = mime_type_reference_or_format.is_a?(Symbol) ?
ActiveResource::Formats[mime_type_reference_or_format] : mime_type_reference_or_format
- write_inheritable_attribute(:format, format)
+ self._format = format
connection.format = format if site
end
# Returns the current format, default is ActiveResource::Formats::XmlFormat.
def format
- read_inheritable_attribute(:format) || ActiveResource::Formats::XmlFormat
+ self._format || ActiveResource::Formats::XmlFormat
end
# Sets the number of seconds after which requests to the REST API should time out.
diff --git a/activeresource/lib/active_resource/version.rb b/activeresource/lib/active_resource/version.rb
index e2e0ecccbb..82dcb5d575 100644
--- a/activeresource/lib/active_resource/version.rb
+++ b/activeresource/lib/active_resource/version.rb
@@ -3,8 +3,8 @@ module ActiveResource
MAJOR = 3
MINOR = 1
TINY = 0
- BUILD = "beta"
+ PRE = "beta"
- STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
end
diff --git a/activeresource/test/cases/base_test.rb b/activeresource/test/cases/base_test.rb
index abf4259a54..ab801902ac 100644
--- a/activeresource/test/cases/base_test.rb
+++ b/activeresource/test/cases/base_test.rb
@@ -463,9 +463,9 @@ class BaseTest < Test::Unit::TestCase
assert Person.collection_path(:gender => 'male', :student => true).include?('gender=male')
assert Person.collection_path(:gender => 'male', :student => true).include?('student=true')
- assert_equal '/people.xml?name[]=bob&name[]=your+uncle%2Bme&name[]=&name[]=false', Person.collection_path(:name => ['bob', 'your uncle+me', nil, false])
+ assert_equal '/people.xml?name%5B%5D=bob&name%5B%5D=your+uncle%2Bme&name%5B%5D=&name%5B%5D=false', Person.collection_path(:name => ['bob', 'your uncle+me', nil, false])
- assert_equal '/people.xml?struct[a][]=2&struct[a][]=1&struct[b]=fred', Person.collection_path(:struct => ActiveSupport::OrderedHash[:a, [2,1], 'b', 'fred'])
+ assert_equal '/people.xml?struct%5Ba%5D%5B%5D=2&struct%5Ba%5D%5B%5D=1&struct%5Bb%5D=fred', Person.collection_path(:struct => ActiveSupport::OrderedHash[:a, [2,1], 'b', 'fred'])
end
def test_custom_element_path
@@ -512,7 +512,7 @@ class BaseTest < Test::Unit::TestCase
assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, :person_id => 1, :type => 'work')
assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, 'person_id' => 1, :type => 'work')
assert_equal '/people/1/addresses/1.xml?type=work', StreetAddress.element_path(1, :type => 'work', :person_id => 1)
- assert_equal '/people/1/addresses/1.xml?type[]=work&type[]=play+time', StreetAddress.element_path(1, :person_id => 1, :type => ['work', 'play time'])
+ assert_equal '/people/1/addresses/1.xml?type%5B%5D=work&type%5B%5D=play+time', StreetAddress.element_path(1, :person_id => 1, :type => ['work', 'play time'])
end
def test_custom_element_path_with_prefix_and_parameters
diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb
index 8f8def5922..905dfb040b 100644
--- a/activesupport/lib/active_support/callbacks.rb
+++ b/activesupport/lib/active_support/callbacks.rb
@@ -1,6 +1,6 @@
require 'active_support/descendants_tracker'
require 'active_support/core_ext/array/wrap'
-require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/kernel/reporting'
require 'active_support/core_ext/kernel/singleton_class'
@@ -437,7 +437,7 @@ module ActiveSupport
([self] + ActiveSupport::DescendantsTracker.descendants(self)).each do |target|
chain = target.send("_#{name}_callbacks")
- yield chain, type, filters, options
+ yield target, chain.dup, type, filters, options
target.__define_runner(name)
end
end
@@ -473,7 +473,7 @@ module ActiveSupport
def set_callback(name, *filter_list, &block)
mapped = nil
- __update_callbacks(name, filter_list, block) do |chain, type, filters, options|
+ __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
mapped ||= filters.map do |filter|
Callback.new(chain, filter, type, options.dup, self)
end
@@ -483,6 +483,8 @@ module ActiveSupport
end
options[:prepend] ? chain.unshift(*(mapped.reverse)) : chain.push(*mapped)
+
+ target.send("_#{name}_callbacks=", chain)
end
end
@@ -493,7 +495,7 @@ module ActiveSupport
# end
#
def skip_callback(name, *filter_list, &block)
- __update_callbacks(name, filter_list, block) do |chain, type, filters, options|
+ __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
filters.each do |filter|
filter = chain.find {|c| c.matches?(type, filter) }
@@ -505,6 +507,7 @@ module ActiveSupport
chain.delete(filter)
end
+ target.send("_#{name}_callbacks=", chain)
end
end
@@ -514,12 +517,14 @@ module ActiveSupport
callbacks = send("_#{symbol}_callbacks")
ActiveSupport::DescendantsTracker.descendants(self).each do |target|
- chain = target.send("_#{symbol}_callbacks")
+ chain = target.send("_#{symbol}_callbacks").dup
callbacks.each { |c| chain.delete(c) }
+ target.send("_#{symbol}_callbacks=", chain)
target.__define_runner(symbol)
end
- callbacks.clear
+ self.send("_#{symbol}_callbacks=", callbacks.dup.clear)
+
__define_runner(symbol)
end
@@ -589,9 +594,8 @@ module ActiveSupport
def define_callbacks(*callbacks)
config = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
callbacks.each do |callback|
- extlib_inheritable_reader("_#{callback}_callbacks") do
- CallbackChain.new(callback, config)
- end
+ class_attribute "_#{callback}_callbacks"
+ send("_#{callback}_callbacks=", CallbackChain.new(callback, config))
__define_runner(callback)
end
end
diff --git a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
index af30bfc13a..ca3db2349e 100644
--- a/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
+++ b/activesupport/lib/active_support/core_ext/class/inheritable_attributes.rb
@@ -1,8 +1,10 @@
require 'active_support/core_ext/object/duplicable'
require 'active_support/core_ext/array/extract_options'
+require 'active_support/deprecation'
# Retained for backward compatibility. Methods are now included in Class.
module ClassInheritableAttributes # :nodoc:
+ DEPRECATION_WARNING_MESSAGE = "class_inheritable_attribute is deprecated, please use class_attribute method instead. Notice their behavior are slightly different, so refer to class_attribute documentation first"
end
# It is recommended to use <tt>class_attribute</tt> over methods defined in this file. Please
@@ -36,6 +38,7 @@ end
# Person.new.hair_colors # => NoMethodError
class Class # :nodoc:
def class_inheritable_reader(*syms)
+ ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
options = syms.extract_options!
syms.each do |sym|
next if sym.is_a?(Hash)
@@ -54,6 +57,7 @@ class Class # :nodoc:
end
def class_inheritable_writer(*syms)
+ ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
options = syms.extract_options!
syms.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
@@ -71,6 +75,7 @@ class Class # :nodoc:
end
def class_inheritable_array_writer(*syms)
+ ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
options = syms.extract_options!
syms.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
@@ -88,6 +93,7 @@ class Class # :nodoc:
end
def class_inheritable_hash_writer(*syms)
+ ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
options = syms.extract_options!
syms.each do |sym|
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
@@ -124,6 +130,7 @@ class Class # :nodoc:
end
def write_inheritable_attribute(key, value)
+ ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
@inheritable_attributes = {}
end
@@ -141,10 +148,12 @@ class Class # :nodoc:
end
def read_inheritable_attribute(key)
+ ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
inheritable_attributes[key]
end
def reset_inheritable_attributes
+ ActiveSupport::Deprecation.warn ClassInheritableAttributes::DEPRECATION_WARNING_MESSAGE
@inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
end
@@ -169,86 +178,3 @@ class Class # :nodoc:
alias inherited_without_inheritable_attributes inherited
alias inherited inherited_with_inheritable_attributes
end
-
-class Class
- # Defines class-level inheritable attribute reader. Attributes are available to subclasses,
- # each subclass has a copy of parent's attribute.
- #
- # @param *syms<Array[#to_s]> Array of attributes to define inheritable reader for.
- # @return <Array[#to_s]> Array of attributes converted into inheritable_readers.
- #
- # @api public
- #
- # @todo Do we want to block instance_reader via :instance_reader => false
- # @todo It would be preferable that we do something with a Hash passed in
- # (error out or do the same as other methods above) instead of silently
- # moving on). In particular, this makes the return value of this function
- # less useful.
- def extlib_inheritable_reader(*ivars, &block)
- options = ivars.extract_options!
-
- ivars.each do |ivar|
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def self.#{ivar}
- return @#{ivar} if self.object_id == #{self.object_id} || defined?(@#{ivar})
- ivar = superclass.#{ivar}
- return nil if ivar.nil? && !#{self}.instance_variable_defined?("@#{ivar}")
- @#{ivar} = ivar.duplicable? ? ivar.dup : ivar
- end
- RUBY
- unless options[:instance_reader] == false
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def #{ivar}
- self.class.#{ivar}
- end
- RUBY
- end
- instance_variable_set(:"@#{ivar}", yield) if block_given?
- end
- end
-
- # Defines class-level inheritable attribute writer. Attributes are available to subclasses,
- # each subclass has a copy of parent's attribute.
- #
- # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
- # define inheritable writer for.
- # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
- # @return <Array[#to_s]> An Array of the attributes that were made into inheritable writers.
- #
- # @api public
- #
- # @todo We need a style for class_eval <<-HEREDOC. I'd like to make it
- # class_eval(<<-RUBY, __FILE__, __LINE__), but we should codify it somewhere.
- def extlib_inheritable_writer(*ivars)
- options = ivars.extract_options!
-
- ivars.each do |ivar|
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def self.#{ivar}=(obj)
- @#{ivar} = obj
- end
- RUBY
- unless options[:instance_writer] == false
- self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def #{ivar}=(obj) self.class.#{ivar} = obj end
- RUBY
- end
-
- self.send("#{ivar}=", yield) if block_given?
- end
- end
-
- # Defines class-level inheritable attribute accessor. Attributes are available to subclasses,
- # each subclass has a copy of parent's attribute.
- #
- # @param *syms<Array[*#to_s, Hash{:instance_writer => Boolean}]> Array of attributes to
- # define inheritable accessor for.
- # @option syms :instance_writer<Boolean> if true, instance-level inheritable attribute writer is defined.
- # @return <Array[#to_s]> An Array of attributes turned into inheritable accessors.
- #
- # @api public
- def extlib_inheritable_accessor(*syms, &block)
- extlib_inheritable_reader(*syms)
- extlib_inheritable_writer(*syms, &block)
- end
-end
diff --git a/activesupport/lib/active_support/core_ext/object/to_param.rb b/activesupport/lib/active_support/core_ext/object/to_param.rb
index ecb2bca82c..593f376159 100644
--- a/activesupport/lib/active_support/core_ext/object/to_param.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_param.rb
@@ -1,5 +1,3 @@
-
-
class Object
# Alias of <tt>to_s</tt>.
def to_param
@@ -41,7 +39,7 @@ class Hash
# ==== Examples
# { :name => 'David', :nationality => 'Danish' }.to_param # => "name=David&nationality=Danish"
#
- # { :name => 'David', :nationality => 'Danish' }.to_param('user') # => "user[name]=David&user[nationality]=Danish"
+ # { :name => 'David', :nationality => 'Danish' }.to_query('user') # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
def to_param(namespace = nil)
collect do |key, value|
value.to_query(namespace ? "#{namespace}[#{key}]" : key)
diff --git a/activesupport/lib/active_support/core_ext/object/to_query.rb b/activesupport/lib/active_support/core_ext/object/to_query.rb
index c9981895b4..3f1540f685 100644
--- a/activesupport/lib/active_support/core_ext/object/to_query.rb
+++ b/activesupport/lib/active_support/core_ext/object/to_query.rb
@@ -7,7 +7,7 @@ class Object
# Note: This method is defined as a default implementation for all Objects for Hash#to_query to work.
def to_query(key)
require 'cgi' unless defined?(CGI) && defined?(CGI::escape)
- "#{CGI.escape(key.to_s).gsub(/%(5B|5D)/n) { [$1].pack('H*') }}=#{CGI.escape(to_param.to_s)}"
+ "#{CGI.escape(key.to_s)}=#{CGI.escape(to_param.to_s)}"
end
end
@@ -15,7 +15,7 @@ class Array
# Converts an array into a string suitable for use as a URL query string,
# using the given +key+ as the param name.
#
- # ['Rails', 'coding'].to_query('hobbies') # => "hobbies[]=Rails&hobbies[]=coding"
+ # ['Rails', 'coding'].to_query('hobbies') # => "hobbies%5B%5D=Rails&hobbies%5B%5D=coding"
def to_query(key)
prefix = "#{key}[]"
collect { |value| value.to_query(prefix) }.join '&'
diff --git a/activesupport/lib/active_support/core_ext/string/output_safety.rb b/activesupport/lib/active_support/core_ext/string/output_safety.rb
index 37c206ea3c..bb0f747960 100644
--- a/activesupport/lib/active_support/core_ext/string/output_safety.rb
+++ b/activesupport/lib/active_support/core_ext/string/output_safety.rb
@@ -33,21 +33,23 @@ class ERB
singleton_class.send(:remove_method, :html_escape)
module_function :html_escape
- # A utility method for escaping HTML entities in JSON strings.
- # This method is also aliased as <tt>j</tt>.
+ # A utility method for escaping HTML entities in JSON strings
+ # using \uXXXX JavaScript escape sequences for string literals:
#
- # Note that after this operation is performed the output is not
- # a valid JSON.
+ # json_escape("is a > 0 & a < 10?")
+ # # => is a \u003E 0 \u0026 a \u003C 10?
#
- # In your ERb templates, use this method to escape any HTML entities:
- # <%=j @person.to_json %>
+ # Note that after this operation is performed the output is not
+ # valid JSON. In particular double quotes are removed:
#
- # ==== Example:
- # puts json_escape("{\"name\":\"john\",\"created_at\":\"2010-04-28T01:39:31Z\",\"id\":1}")
+ # json_escape('{"name":"john","created_at":"2010-04-28T01:39:31Z","id":1}')
# # => {name:john,created_at:2010-04-28T01:39:31Z,id:1}
#
- # puts json_escape("is a > 0 & a < 10?")
- # # => is a \u003E 0 \u0026 a \u003C 10?
+ # This method is also aliased as +j+, and available as a helper
+ # in Rails templates:
+ #
+ # <%=j @person.to_json %>
+ #
def json_escape(s)
s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] }
end
diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb
index 4d33f597d9..00ea8813dd 100644
--- a/activesupport/lib/active_support/i18n.rb
+++ b/activesupport/lib/active_support/i18n.rb
@@ -7,4 +7,3 @@ rescue LoadError => e
end
I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml"
-ActiveSupport.run_load_hooks(:i18n)
diff --git a/activesupport/lib/active_support/version.rb b/activesupport/lib/active_support/version.rb
index 9e8b3d7888..690fc7f0fc 100644
--- a/activesupport/lib/active_support/version.rb
+++ b/activesupport/lib/active_support/version.rb
@@ -3,8 +3,8 @@ module ActiveSupport
MAJOR = 3
MINOR = 1
TINY = 0
- BUILD = "beta"
+ PRE = "beta"
- STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
end
diff --git a/activesupport/lib/active_support/xml_mini/nokogirisax.rb b/activesupport/lib/active_support/xml_mini/nokogirisax.rb
index 38c8685390..25afbfcd1c 100644
--- a/activesupport/lib/active_support/xml_mini/nokogirisax.rb
+++ b/activesupport/lib/active_support/xml_mini/nokogirisax.rb
@@ -38,8 +38,7 @@ module ActiveSupport
end
def start_element(name, attrs = [])
- new_hash = { CONTENT_KEY => '' }
- new_hash[attrs.shift] = attrs.shift while attrs.length > 0
+ new_hash = { CONTENT_KEY => '' }.merge(Hash[attrs])
new_hash[HASH_SIZE_KEY] = new_hash.size + 1
case current_hash[name]
diff --git a/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb b/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb
index b284e5ee1c..020dfce56a 100644
--- a/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb
+++ b/activesupport/test/core_ext/class/class_inheritable_attributes_test.rb
@@ -3,9 +3,14 @@ require 'active_support/core_ext/class/inheritable_attributes'
class ClassInheritableAttributesTest < Test::Unit::TestCase
def setup
+ ActiveSupport::Deprecation.silenced = true
@klass = Class.new
end
+ def teardown
+ ActiveSupport::Deprecation.silenced = false
+ end
+
def test_reader_declaration
assert_nothing_raised do
@klass.class_inheritable_reader :a
diff --git a/activesupport/test/core_ext/object/to_query_test.rb b/activesupport/test/core_ext/object/to_query_test.rb
index e28b4cd493..84da52f4bf 100644
--- a/activesupport/test/core_ext/object/to_query_test.rb
+++ b/activesupport/test/core_ext/object/to_query_test.rb
@@ -18,22 +18,22 @@ class ToQueryTest < Test::Unit::TestCase
end
def test_nested_conversion
- assert_query_equal 'person[login]=seckar&person[name]=Nicholas',
+ assert_query_equal 'person%5Blogin%5D=seckar&person%5Bname%5D=Nicholas',
:person => ActiveSupport::OrderedHash[:login, 'seckar', :name, 'Nicholas']
end
def test_multiple_nested
- assert_query_equal 'account[person][id]=20&person[id]=10',
+ assert_query_equal 'account%5Bperson%5D%5Bid%5D=20&person%5Bid%5D=10',
ActiveSupport::OrderedHash[:account, {:person => {:id => 20}}, :person, {:id => 10}]
end
def test_array_values
- assert_query_equal 'person[id][]=10&person[id][]=20',
+ assert_query_equal 'person%5Bid%5D%5B%5D=10&person%5Bid%5D%5B%5D=20',
:person => {:id => [10, 20]}
end
def test_array_values_are_not_sorted
- assert_query_equal 'person[id][]=20&person[id][]=10',
+ assert_query_equal 'person%5Bid%5D%5B%5D=20&person%5Bid%5D%5B%5D=10',
:person => {:id => [20, 10]}
end
diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb
index 8be65c99f2..bb865cae91 100644
--- a/activesupport/test/core_ext/string_ext_test.rb
+++ b/activesupport/test/core_ext/string_ext_test.rb
@@ -321,75 +321,6 @@ class CoreExtStringMultibyteTest < ActiveSupport::TestCase
end
end
-=begin
- string.rb - Interpolation for String.
-
- Copyright (C) 2005-2009 Masao Mutoh
-
- You may redistribute it and/or modify it under the same
- license terms as Ruby.
-=end
-class TestGetTextString < Test::Unit::TestCase
- def test_sprintf
- assert_equal("foo is a number", "%{msg} is a number" % {:msg => "foo"})
- assert_equal("bar is a number", "%s is a number" % ["bar"])
- assert_equal("bar is a number", "%s is a number" % "bar")
- assert_equal("1, test", "%{num}, %{record}" % {:num => 1, :record => "test"})
- assert_equal("test, 1", "%{record}, %{num}" % {:num => 1, :record => "test"})
- assert_equal("1, test", "%d, %s" % [1, "test"])
- assert_equal("test, 1", "%2$s, %1$d" % [1, "test"])
- assert_raise(ArgumentError) { "%-%" % [1] }
- end
-
- def test_percent
- assert_equal("% 1", "%% %<num>d" % {:num => 1.0})
- assert_equal("%{num} %<num>d 1", "%%{num} %%<num>d %<num>d" % {:num => 1})
- end
-
- def test_sprintf_percent_in_replacement
- assert_equal("%<not_translated>s", "%{msg}" % { :msg => '%<not_translated>s', :not_translated => 'should not happen' })
- end
-
- def test_sprintf_lack_argument
- assert_raises(KeyError) { "%{num}, %{record}" % {:record => "test"} }
- assert_raises(KeyError) { "%{record}" % {:num => 1} }
- end
-
- def test_no_placeholder
- # Causes a "too many arguments for format string" warning
- # on 1.8.7 and 1.9 but we still want to make sure the behavior works
- silence_warnings do
- assert_equal("aaa", "aaa" % {:num => 1})
- assert_equal("bbb", "bbb" % [1])
- end
- end
-
- def test_sprintf_ruby19_style
- assert_equal("1", "%<num>d" % {:num => 1})
- assert_equal("0b1", "%<num>#b" % {:num => 1})
- assert_equal("foo", "%<msg>s" % {:msg => "foo"})
- assert_equal("1.000000", "%<num>f" % {:num => 1.0})
- assert_equal(" 1", "%<num>3.0f" % {:num => 1.0})
- assert_equal("100.00", "%<num>2.2f" % {:num => 100.0})
- assert_equal("0x64", "%<num>#x" % {:num => 100.0})
- assert_raise(ArgumentError) { "%<num>,d" % {:num => 100} }
- assert_raise(ArgumentError) { "%<num>/d" % {:num => 100} }
- end
-
- def test_sprintf_old_style
- assert_equal("foo 1.000000", "%s %f" % ["foo", 1.0])
- end
-
- def test_sprintf_mix_unformatted_and_formatted_named_placeholders
- assert_equal("foo 1.000000", "%{name} %<num>f" % {:name => "foo", :num => 1.0})
- end
-
- def test_string_interpolation_raises_an_argument_error_when_mixing_named_and_unnamed_placeholders
- assert_raises(ArgumentError) { "%{name} %f" % [1.0] }
- assert_raises(ArgumentError) { "%{name} %f" % [1.0, 2.0] }
- end
-end
-
class OutputSafetyTest < ActiveSupport::TestCase
def setup
@string = "hello"
diff --git a/railties/CHANGELOG b/railties/CHANGELOG
index fdfdb5eef5..75f1df44e7 100644
--- a/railties/CHANGELOG
+++ b/railties/CHANGELOG
@@ -1,5 +1,7 @@
*Rails 3.1.0 (unreleased)*
+* Added `rails plugin new` command which generates rails plugin with gemspec, tests and dummy application for testing [Piotr Sarnacki]
+
* Added -j parameter with jquery/prototype as options. Now you can create your apps with jQuery using `rails new myapp -j jquery`. The default is still Prototype. [siong1987]
* Added Rack::Etag and Rack::ConditionalGet to the default middleware stack [José Valim]
@@ -20,7 +22,7 @@
* Changed ActionDispatch::Static to allow handling multiple directories [Piotr Sarnacki]
-* Added namespace() method to Engine, which sets Engine as isolated [Piotr Sarnacki]
+* Added isolate_namespace() method to Engine, which sets Engine as isolated [Piotr Sarnacki]
* Include all helpers from plugins and shared engines in application [Piotr Sarnacki]
diff --git a/railties/guides/assets/stylesheets/main.css b/railties/guides/assets/stylesheets/main.css
index 932ee402c4..cf6e9e5cc8 100644
--- a/railties/guides/assets/stylesheets/main.css
+++ b/railties/guides/assets/stylesheets/main.css
@@ -333,7 +333,7 @@ h6 {
padding: 0.125em 0 0.25em 28px;*/
}
-#mainCol dd.ticket, #subCol dd.ticket {
+#mainCol dd.work-in-progress, #subCol dd.work-in-progress {
background: #fff9d8 url(../images/tab_yellow.gif) no-repeat left top;
border: none;
padding: 1.25em 1em 1.25em 48px;
diff --git a/railties/guides/rails_guides/helpers.rb b/railties/guides/rails_guides/helpers.rb
index bf99538696..d466c76c7c 100644
--- a/railties/guides/rails_guides/helpers.rb
+++ b/railties/guides/rails_guides/helpers.rb
@@ -4,19 +4,14 @@ module RailsGuides
link = content_tag(:a, :href => url) { name }
result = content_tag(:dt, link)
- if ticket = options[:ticket]
- result << content_tag(:dd, lh(ticket), :class => 'ticket')
+ if options[:work_in_progress]
+ result << content_tag(:dd, 'Work in progress', :class => 'work-in-progress')
end
result << content_tag(:dd, capture(&block))
result
end
- def lh(id, label = "Lighthouse Ticket")
- url = "http://rails.lighthouseapp.com/projects/16213/tickets/#{id}"
- content_tag(:a, label, :href => url)
- end
-
def author(name, nick, image = 'credits_pic_blank.gif', &block)
image = "images/#{image}"
diff --git a/railties/guides/source/action_controller_overview.textile b/railties/guides/source/action_controller_overview.textile
index b39075f101..0d6c66f168 100644
--- a/railties/guides/source/action_controller_overview.textile
+++ b/railties/guides/source/action_controller_overview.textile
@@ -814,8 +814,6 @@ NOTE: Certain exceptions are only rescuable from the +ApplicationController+ cla
h3. Changelog
-"Lighthouse Ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/17
-
* February 17, 2009: Yet another proofread by Xavier Noria.
* November 4, 2008: First release version by Tore Darell
diff --git a/railties/guides/source/action_mailer_basics.textile b/railties/guides/source/action_mailer_basics.textile
index 8d2ce44e93..b75c528a33 100644
--- a/railties/guides/source/action_mailer_basics.textile
+++ b/railties/guides/source/action_mailer_basics.textile
@@ -508,6 +508,4 @@ In the test we send the email and store the returned object in the +email+ varia
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213/tickets/25
-
* September 30, 2010: Fixed typos and reformatted Action Mailer configuration table for better understanding. "Jaime Iniesta":http://jaimeiniesta.com
diff --git a/railties/guides/source/action_view_overview.textile b/railties/guides/source/action_view_overview.textile
index 54792600a5..051baa660c 100644
--- a/railties/guides/source/action_view_overview.textile
+++ b/railties/guides/source/action_view_overview.textile
@@ -1472,7 +1472,5 @@ You can read more about the Rails Internationalization (I18n) API "here":i18n.ht
h3. Changelog
-"Lighthouse Ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/71
-
* September 3, 2009: Continuing work by Trevor Turk, leveraging the "Action Pack docs":http://ap.rubyonrails.org/ and "What's new in Edge Rails":http://ryandaigle.com/articles/2007/8/3/what-s-new-in-edge-rails-partials-get-layouts
* April 5, 2009: Starting work by Trevor Turk, leveraging Mike Gunderloy's docs
diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile
index 6837c8b11a..328439fdb8 100644
--- a/railties/guides/source/active_record_querying.textile
+++ b/railties/guides/source/active_record_querying.textile
@@ -928,8 +928,6 @@ For options, please see the parent section, "Calculations":#calculations.
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/16
-
* April 7, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com
* February 3, 2010: Update to Rails 3 by "James Miller":credits.html#bensie
* February 7, 2009: Second version by "Pratik":credits.html#lifo
diff --git a/railties/guides/source/active_record_validations_callbacks.textile b/railties/guides/source/active_record_validations_callbacks.textile
index 9ddbe6fadc..9ce0423244 100644
--- a/railties/guides/source/active_record_validations_callbacks.textile
+++ b/railties/guides/source/active_record_validations_callbacks.textile
@@ -1160,8 +1160,6 @@ config.active_record.observers = :mailer_observer
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213/tickets/26-active-record-validations-and-callbacks
-
* July 20, 2010: Fixed typos and rephrased some paragraphs for clarity. "Jaime Iniesta":http://jaimeiniesta.com
* May 24, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com
* May 15, 2010: Validation Errors section updated by "Emili Parreño":http://www.eparreno.com
diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile
index dc1200812e..d41d8b6c3d 100644
--- a/railties/guides/source/active_support_core_extensions.textile
+++ b/railties/guides/source/active_support_core_extensions.textile
@@ -3401,7 +3401,5 @@ NOTE: Defined in +active_support/core_ext/load_error.rb+.
h3. Changelog
-"Lighthouse ticket":https://rails.lighthouseapp.com/projects/16213/tickets/67
-
* August 10, 2010: Starts to take shape, added to the index.
* April 18, 2009: Initial version by "Xavier Noria":credits.html#fxn
diff --git a/railties/guides/source/association_basics.textile b/railties/guides/source/association_basics.textile
index f6d61373e1..14bbe907f3 100644
--- a/railties/guides/source/association_basics.textile
+++ b/railties/guides/source/association_basics.textile
@@ -1876,8 +1876,6 @@ Extensions can refer to the internals of the association proxy using these three
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/11
-
* April 7, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com
* April 19, 2009: Added +:touch+ option to +belongs_to+ associations by "Mike Gunderloy":credits.html#mgunderloy
* February 1, 2009: Added +:autosave+ option "Mike Gunderloy":credits.html#mgunderloy
diff --git a/railties/guides/source/caching_with_rails.textile b/railties/guides/source/caching_with_rails.textile
index 3320b610e7..63c52da32a 100644
--- a/railties/guides/source/caching_with_rails.textile
+++ b/railties/guides/source/caching_with_rails.textile
@@ -368,7 +368,6 @@ h3. Further reading
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/10-guide-to-caching
* May 02, 2009: Formatting cleanups
* April 26, 2009: Clean up typos in submitted patch
diff --git a/railties/guides/source/command_line.textile b/railties/guides/source/command_line.textile
index 752b5926f7..acd105c622 100644
--- a/railties/guides/source/command_line.textile
+++ b/railties/guides/source/command_line.textile
@@ -590,6 +590,3 @@ h5. Miscellaneous Tasks
+rake routes+ will list all of your defined routes, which is useful for tracking down routing problems in your app, or giving you a good overview of the URLs in an app you're trying to get familiar with.
-h3. Changelog
-
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213/tickets/29
diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index 28fff5c11e..e39ccbc4b8 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -277,8 +277,6 @@ Some parts of Rails can also be configured externally by supplying environment v
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/28
-
* August 13, 2009: Updated with config syntax and added general configuration options by "John Pignata"
* January 3, 2009: First reasonably complete draft by "Mike Gunderloy":credits.html#mgunderloy
* November 5, 2008: Rough outline by "Mike Gunderloy":credits.html#mgunderloy
diff --git a/railties/guides/source/contribute.textile b/railties/guides/source/contribute.textile
index 88c5c79e9d..3d4607de1d 100644
--- a/railties/guides/source/contribute.textile
+++ b/railties/guides/source/contribute.textile
@@ -1,6 +1,6 @@
h2. Contribute to the Rails Guides
-Rails Guides aim to improve the Rails documentation and to make the barrier to entry as low as possible. A reasonably experienced developer should be able to use the Guides to come up to speed on Rails quickly. You can track the overall effort at the "Rails Guides Lighthouse":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets. Our sponsors have contributed prizes for those who write an entire guide, but there are many other ways to contribute.
+Rails Guides aim to improve the Rails documentation and to make the barrier to entry as low as possible. A reasonably experienced developer should be able to use the guides to come up to speed on Rails quickly. Our sponsors have contributed prizes for those who write an entire guide, but there are many other ways to contribute.
endprologue.
diff --git a/railties/guides/source/contributing_to_rails.textile b/railties/guides/source/contributing_to_rails.textile
index 7184759610..f501335958 100644
--- a/railties/guides/source/contributing_to_rails.textile
+++ b/railties/guides/source/contributing_to_rails.textile
@@ -189,7 +189,7 @@ h3. Contributing to the Rails Documentation
Another area where you can help out if you're not yet ready to take the plunge to writing Rails core code is with Rails documentation. You can help with the Rails Guides or the Rails API documentation.
-TIP: "docrails":http://github.com/lifo/docrails/tree/master is the documentation branch for Rails with an *open commit policy*. You can simply PM "lifo":http://github.com/lifo on Github and ask for the commit rights. Documentation changes made as part of the "docrails":http://github.com/lifo/docrails/tree/master project are merged back to the Rails master code from time to time. Check out the "original announcement":http://weblog.rubyonrails.org/2008/5/2/help-improve-rails-documentation-on-git-branch for more details.
+TIP: "docrails":http://github.com/lifo/docrails/tree/master is the documentation branch for Rails with an *open commit policy*, it has public write access. Documentation changes made as part of the "docrails":http://github.com/lifo/docrails/tree/master project are merged back to the Rails master code from time to time. Check out the "original announcement":http://weblog.rubyonrails.org/2008/5/2/help-improve-rails-documentation-on-git-branch for more details.
h4. The Rails Guides
@@ -300,8 +300,6 @@ And then...think about your next contribution!
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/64
-
* April 6, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com
* August 1, 2009: Updates/amplifications by "Mike Gunderloy":credits.html#mgunderloy
* March 2, 2009: Initial draft by "Mike Gunderloy":credits.html#mgunderloy
diff --git a/railties/guides/source/debugging_rails_applications.textile b/railties/guides/source/debugging_rails_applications.textile
index adf427147b..6613fad406 100644
--- a/railties/guides/source/debugging_rails_applications.textile
+++ b/railties/guides/source/debugging_rails_applications.textile
@@ -704,8 +704,6 @@ h3. References
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/5
-
* April 4, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com
* November 3, 2008: Accepted for publication. Added RJS, memory leaks and plugins chapters by "Emilio Tagua":credits.html#miloops
* October 19, 2008: Copy editing pass by "Mike Gunderloy":credits.html#mgunderloy
diff --git a/railties/guides/source/form_helpers.textile b/railties/guides/source/form_helpers.textile
index ded82512d3..e178a60307 100644
--- a/railties/guides/source/form_helpers.textile
+++ b/railties/guides/source/form_helpers.textile
@@ -776,8 +776,6 @@ Many apps grow beyond simple forms editing a single object. For example when cre
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/1
-
* April 6, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com
h3. Authors
diff --git a/railties/guides/source/generators.textile b/railties/guides/source/generators.textile
index 0f2cbb76dc..c0d3116fe4 100644
--- a/railties/guides/source/generators.textile
+++ b/railties/guides/source/generators.textile
@@ -363,8 +363,6 @@ Fallbacks allow your generators to have a single responsibility, increasing code
h3. Changelog
-"Lighthouse Ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/102
-
* August 23, 2010: Edit pass by "Xavier Noria":credits.html#fxn
* April 30, 2010: Reviewed by José Valim
diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile
index f3420e37d1..902b7353c0 100644
--- a/railties/guides/source/getting_started.textile
+++ b/railties/guides/source/getting_started.textile
@@ -1469,8 +1469,6 @@ Two very common sources of data that are not UTF-8:
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/2
-
* August 30, 2010: Minor editing after Rails 3 release by "Joost Baaij":http://www.spacebabies.nl
* July 12, 2010: Fixes, editing and updating of code samples by "Jaime Iniesta":http://jaimeiniesta.com
* May 16, 2010: Added a section on configuration gotchas to address common encoding problems that people might have by "Yehuda Katz":http://www.yehudakatz.com
diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile
index 3179748c56..1a83b84004 100644
--- a/railties/guides/source/i18n.textile
+++ b/railties/guides/source/i18n.textile
@@ -889,8 +889,3 @@ fn1. Or, to quote "Wikipedia":http://en.wikipedia.org/wiki/Internationalization_
fn2. Other backends might allow or require to use other formats, e.g. a GetText backend might allow to read GetText files.
fn3. One of these reasons is that we don't want to imply any unnecessary load for applications that do not need any I18n capabilities, so we need to keep the I18n library as simple as possible for English. Another reason is that it is virtually impossible to implement a one-fits-all solution for all problems related to I18n for all existing languages. So a solution that allows us to exchange the entire implementation easily is appropriate anyway. This also makes it much easier to experiment with custom features and extensions.
-
-
-h3. Changelog
-
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213/tickets/23
diff --git a/railties/guides/source/index.html.erb b/railties/guides/source/index.html.erb
index 6b897e3a6a..84fbc53a69 100644
--- a/railties/guides/source/index.html.erb
+++ b/railties/guides/source/index.html.erb
@@ -31,7 +31,7 @@ Ruby on Rails Guides
<div id="subCol">
<dl>
<dd class="warning">Rails Guides are a result of the ongoing <a href="http://hackfest.rubyonrails.org">Guides hackfest</a>, and a work in progress.</dd>
- <dd class="ticket">Guides marked with this icon are currently being worked on. While they might still be useful to you, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections at the respective Lighthouse ticket.</dd>
+ <dd class="work-in-progress">Guides marked with this icon are currently being worked on. While they might still be useful to you, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections to the author.</dd>
</dl>
</div>
<% end %>
@@ -71,7 +71,7 @@ Ruby on Rails Guides
<p>This guide covers the basic layout features of Action Controller and Action View, including rendering and redirecting, using content_for blocks, and working with partials.</p>
<% end %>
-<%= guide("Action View Form Helpers", 'form_helpers.html', :ticket => 1) do %>
+<%= guide("Action View Form Helpers", 'form_helpers.html', :work_in_progress => true) do %>
<p>Guide to using built-in Form helpers.</p>
<% end %>
</dl>
@@ -100,11 +100,11 @@ Ruby on Rails Guides
<p>This guide covers how to add internationalization to your applications. Your application will be able to translate content to different languages, change pluralization rules, use correct date formats for each country and so on.</p>
<% end %>
-<%= guide("Action Mailer Basics", 'action_mailer_basics.html', :ticket => 25) do %>
+<%= guide("Action Mailer Basics", 'action_mailer_basics.html', :work_in_progress => true) do %>
<p>This guide describes how to use Action Mailer to send and receive emails.</p>
<% end %>
-<%= guide("Testing Rails Applications", 'testing.html', :ticket => 8) do %>
+<%= guide("Testing Rails Applications", 'testing.html', :work_in_progress => true) do %>
<p>This is a rather comprehensive guide to doing both unit and functional tests in Rails. It covers everything from &quot;What is a test?&quot; to the testing APIs. Enjoy.</p>
<% end %>
@@ -124,11 +124,11 @@ Ruby on Rails Guides
<p>This guide covers the basic configuration settings for a Rails application.</p>
<% end %>
-<%= guide("Rails Command Line Tools and Rake tasks", 'command_line.html', :ticket => 29) do %>
+<%= guide("Rails Command Line Tools and Rake tasks", 'command_line.html', :work_in_progress => true) do %>
<p>This guide covers the command line tools and rake tasks provided by Rails.</p>
<% end %>
-<%= guide("Caching with Rails", 'caching_with_rails.html', :ticket => 10) do %>
+<%= guide("Caching with Rails", 'caching_with_rails.html', :work_in_progress => true) do %>
<p>Various caching techniques provided by Rails.</p>
<% end %>
</dl>
@@ -136,7 +136,7 @@ Ruby on Rails Guides
<h3>Extending Rails</h3>
<dl>
- <%= guide("The Basics of Creating Rails Plugins", 'plugins.html', :ticket => 32) do %>
+ <%= guide("The Basics of Creating Rails Plugins", 'plugins.html', :work_in_progress => true) do %>
<p>This guide covers how to build a plugin to extend the functionality of Rails.</p>
<% end %>
diff --git a/railties/guides/source/initialization.textile b/railties/guides/source/initialization.textile
index 77c20c8bb0..4cc5f3843f 100644
--- a/railties/guides/source/initialization.textile
+++ b/railties/guides/source/initialization.textile
@@ -1479,10 +1479,10 @@ Next, the Railtie itself is defined:
require "action_view/railties/log_subscriber"
log_subscriber ActionView::Railties::LogSubscriber.new
- initializer "action_view.cache_asset_timestamps" do |app|
+ initializer "action_view.cache_asset_id" do |app|
unless app.config.cache_classes
- ActionView.base_hook do
- ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
+ ActiveSupport.on_load(:action_view) do
+ ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids = false
end
end
end
@@ -1492,7 +1492,7 @@ Next, the Railtie itself is defined:
The +ActionView::LogSubscriber+ sets up a method called +render_template+ which is called when a template is rendered. TODO: Templates only or partials and layouts also? I would imagine these fall under the templates category, but there needs to research to ensure this is correct.
-The sole initializer defined here, _action_view.cache_asset_timestamps_ is responsible for caching the timestamps on the ends of your assets. If you've ever seen a link generated by +image_tag+ or +stylesheet_link_tag+ you would know that I mean that this timestamp is the number after the _?_ in this example: _/javascripts/prototype.js?1265442620_. This initializer will do nothing if +cache_classes+ is set to false in any of your application's configuration. TODO: Elaborate.
+The sole initializer defined here, _action_view.cache_asset_ids_ is responsible for caching the timestamps on the ends of your assets. If you've ever seen a link generated by +image_tag+ or +stylesheet_link_tag+ you would know that I mean that this timestamp is the number after the _?_ in this example: _/javascripts/prototype.js?1265442620_. This initializer will do nothing if +cache_classes+ is set to false in any of your application's configuration. TODO: Elaborate.
h4. Action Mailer Railtie
@@ -3003,7 +3003,7 @@ The +I18n::Railtie+ also defines an +after_initialize+ which we will return to l
**Action View Initializers **
-* action_view.cache_asset_timestamps
+* action_view.cache_asset_ids
**Action Mailer Initializers **
diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb
index d4b87dc45b..bb62506f04 100644
--- a/railties/guides/source/layout.html.erb
+++ b/railties/guides/source/layout.html.erb
@@ -106,6 +106,31 @@
<div class="wrapper">
<div id="mainCol">
<%= yield.html_safe %>
+
+ <h3>Feedback</h3>
+ <p>
+ You're encouraged to help in keeping the quality of this guide.
+ </p>
+ <p>
+ If you see any typos or factual errors you are confident to
+ patch please clone <%= link_to 'docrails', 'https://github.com/lifo/docrails' %>
+ and push the change yourself. That branch of Rails has public write access.
+ Commits are still reviewed, but that happens after you've submitted your
+ contribution. <%= link_to 'docrails', 'https://github.com/lifo/docrails' %> is
+ cross-merged with master periodically.
+ </p>
+ <p>
+ You may also find incomplete content, or stuff that is not up to date.
+ Please do add any missing documentation for master. Check the
+ <%= link_to 'Ruby on Rails Guides Guidelines', 'ruby_on_rails_guides_guidelines.html' %>
+ guide for style and conventions.
+ </p>
+ <p>
+ Issues may also be reported <%= link_to 'in Github', 'https://github.com/lifo/docrails/issues' %>.
+ </p>
+ <p>And last but not least, any kind of discussion regarding Ruby on Rails
+ documentation is very welcome in the <%= link_to 'rubyonrails-docs mailing list', 'http://groups.google.com/group/rubyonrails-docs' %>.
+ </p>
</div>
</div>
</div>
diff --git a/railties/guides/source/layouts_and_rendering.textile b/railties/guides/source/layouts_and_rendering.textile
index 4e26d152bf..80a1fdd38d 100644
--- a/railties/guides/source/layouts_and_rendering.textile
+++ b/railties/guides/source/layouts_and_rendering.textile
@@ -1194,8 +1194,6 @@ There are several ways of getting similar results with different sub-templating
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/15
-
* April 4, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com
* January 25, 2010: Rails 3.0 Update by "Mikel Lindsaar":credits.html#raasdnil
* December 27, 2008: Merge patch from Rodrigo Rosenfeld Rosas covering subtemplates
diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile
index 61d457e351..0d13fbc10a 100644
--- a/railties/guides/source/migrations.textile
+++ b/railties/guides/source/migrations.textile
@@ -590,7 +590,5 @@ Although Active Record does not provide any tools for working directly with such
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/6
-
* July 15, 2010: minor typos corrected by "Jaime Iniesta":http://jaimeiniesta.com
* September 14, 2008: initial version by "Frederick Cheung":credits.html#fcheung
diff --git a/railties/guides/source/performance_testing.textile b/railties/guides/source/performance_testing.textile
index 9dda6d420a..41bdd27e9b 100644
--- a/railties/guides/source/performance_testing.textile
+++ b/railties/guides/source/performance_testing.textile
@@ -524,7 +524,5 @@ Rails has been lucky to have two startups dedicated to Rails specific performanc
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/4
-
* January 9, 2009: Complete rewrite by "Pratik":credits.html#lifo
* September 6, 2008: Initial version by Matthew Bergman
diff --git a/railties/guides/source/plugins.textile b/railties/guides/source/plugins.textile
index d32e7de564..cb43282ace 100644
--- a/railties/guides/source/plugins.textile
+++ b/railties/guides/source/plugins.textile
@@ -1513,7 +1513,5 @@ The final plugin should have a directory structure that looks something like thi
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213/tickets/32-update-plugins-guide
-
* April 4, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com
* November 17, 2008: Major revision by Jeff Dean
diff --git a/railties/guides/source/rails_application_templates.textile b/railties/guides/source/rails_application_templates.textile
index bc7b151dfe..d4b887ad02 100644
--- a/railties/guides/source/rails_application_templates.textile
+++ b/railties/guides/source/rails_application_templates.textile
@@ -233,6 +233,4 @@ git :commit => "-a -m 'Initial commit'"
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/78
-
* April 29, 2009: Initial version by "Pratik":credits.html#lifo
diff --git a/railties/guides/source/rails_on_rack.textile b/railties/guides/source/rails_on_rack.textile
index eaebb05f17..f17e9b4798 100644
--- a/railties/guides/source/rails_on_rack.textile
+++ b/railties/guides/source/rails_on_rack.textile
@@ -237,7 +237,5 @@ h4. Understanding Middlewares
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/58
-
* February 7, 2009: Second version by "Pratik":credits.html#lifo
* January 11, 2009: First version by "Pratik":credits.html#lifo
diff --git a/railties/guides/source/routing.textile b/railties/guides/source/routing.textile
index cc0c3316c8..918279f9eb 100644
--- a/railties/guides/source/routing.textile
+++ b/railties/guides/source/routing.textile
@@ -853,8 +853,6 @@ assert_routing({ :path => "photos", :method => :post }, { :controller => "photos
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/3
-
* April 10, 2010: Updated guide to remove outdated and superfluous information, and to provide information about new features, by "Yehuda Katz":http://www.yehudakatz.com
* April 2, 2010: Updated guide to match new Routing DSL in Rails 3, by "Rizwan Reza":http://www.rizwanreza.com/
* Febuary 1, 2010: Modifies the routing documentation to match new routing DSL in Rails 3, by Prem Sichanugrist
diff --git a/railties/guides/source/security.textile b/railties/guides/source/security.textile
index bc6e6e3390..6f766430c1 100644
--- a/railties/guides/source/security.textile
+++ b/railties/guides/source/security.textile
@@ -979,6 +979,4 @@ The security landscape shifts and it is important to keep up to date, because mi
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/7
-
* November 1, 2008: First approved version by Heiko Webers
diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile
index aae849c369..c292a5d83b 100644
--- a/railties/guides/source/testing.textile
+++ b/railties/guides/source/testing.textile
@@ -940,8 +940,6 @@ The built-in +test/unit+ based testing is not the only way to test Rails applica
h3. Changelog
-"Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/8
-
* April 4, 2010: Fixed document to validate XHTML 1.0 Strict. "Jaime Iniesta":http://jaimeiniesta.com
* November 13, 2008: Revised based on feedback from Pratik Naik by "Akshay Surve":credits.html#asurve (not yet approved for publication)
* October 14, 2008: Edit and formatting pass by "Mike Gunderloy":credits.html#mgunderloy (not yet approved for publication)
diff --git a/railties/lib/rails/commands.rb b/railties/lib/rails/commands.rb
index e3aa6d7c3e..338565247f 100644
--- a/railties/lib/rails/commands.rb
+++ b/railties/lib/rails/commands.rb
@@ -12,14 +12,19 @@ command = aliases[command] || command
case command
when 'generate', 'destroy', 'plugin'
- require APP_PATH
- Rails.application.require_environment!
+ if command == 'plugin' && ARGV.first == 'new'
+ require "rails/commands/plugin_new"
+ else
+ require APP_PATH
+ Rails.application.require_environment!
- if defined?(ENGINE_PATH)
- engine = Rails.application.railties.engines.find { |r| r.root.to_s == ENGINE_PATH }
- Rails.application = engine
+ if defined?(ENGINE_PATH)
+ engine = Rails.application.railties.engines.find { |r| r.root.to_s == ENGINE_PATH }
+ Rails.application = engine
+ end
+
+ require "rails/commands/#{command}"
end
- require "rails/commands/#{command}"
when 'benchmarker', 'profiler'
require APP_PATH
diff --git a/railties/lib/rails/commands/plugin_new.rb b/railties/lib/rails/commands/plugin_new.rb
index 00a7e30902..8baa8ebfd4 100644
--- a/railties/lib/rails/commands/plugin_new.rb
+++ b/railties/lib/rails/commands/plugin_new.rb
@@ -1,9 +1,3 @@
-require 'rails/version'
-if %w(--version -v).include? ARGV.first
- puts "Rails #{Rails::VERSION::STRING}"
- exit(0)
-end
-
if ARGV.first != "new"
ARGV[0] = "--help"
else
diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb
index 466a31c2a0..62fb781c19 100644
--- a/railties/lib/rails/engine.rb
+++ b/railties/lib/rails/engine.rb
@@ -329,6 +329,7 @@ module Rails
class << self
attr_accessor :called_from, :isolated
+ alias :isolated? :isolated
alias :engine_name :railtie_name
def inherited(base)
@@ -368,10 +369,6 @@ module Rails
end
end
end
-
- def isolated?
- !!isolated
- end
end
delegate :middleware, :root, :paths, :to => :config
diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb
index 240810b8bd..27a4007c20 100644
--- a/railties/lib/rails/generators.rb
+++ b/railties/lib/rails/generators.rb
@@ -228,6 +228,7 @@ module Rails
rails = groups.delete("rails")
rails.map! { |n| n.sub(/^rails:/, '') }
rails.delete("app")
+ rails.delete("plugin_new")
print_list("rails", rails)
hidden_namespaces.each {|n| groups.delete(n.to_s) }
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb
index c8ee939ad7..d12b2ff0e5 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb
+++ b/railties/lib/rails/generators/erb/scaffold/templates/_form.html.erb
@@ -1,4 +1,3 @@
-<% without_namespacing do -%>
<%%= form_for(@<%= singular_table_name %>) do |f| %>
<%% if @<%= singular_table_name %>.errors.any? %>
<div id="error_explanation">
@@ -22,4 +21,3 @@
<%%= f.submit %>
</div>
<%% end %>
-<% end -%>
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb
index d1bfcbc429..e58b9fbd08 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb
+++ b/railties/lib/rails/generators/erb/scaffold/templates/edit.html.erb
@@ -1,8 +1,6 @@
-<% without_namespacing do -%>
<h1>Editing <%= singular_table_name %></h1>
<%%= render 'form' %>
<%%= link_to 'Show', @<%= singular_table_name %> %> |
<%%= link_to 'Back', <%= index_helper %>_path %>
-<% end -%>
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
index 435d126ee4..4c46db4d67 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
+++ b/railties/lib/rails/generators/erb/scaffold/templates/index.html.erb
@@ -1,4 +1,3 @@
-<% without_namespacing do -%>
<h1>Listing <%= plural_table_name %></h1>
<table>
@@ -26,4 +25,3 @@
<br />
<%%= link_to 'New <%= human_name %>', new_<%= singular_table_name %>_path %>
-<% end -%>
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb
index fe4d0971c4..02ae4d015e 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb
+++ b/railties/lib/rails/generators/erb/scaffold/templates/new.html.erb
@@ -1,7 +1,5 @@
-<% without_namespacing do -%>
<h1>New <%= singular_table_name %></h1>
<%%= render 'form' %>
<%%= link_to 'Back', <%= index_helper %>_path %>
-<% end -%>
diff --git a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb
index bc3a87b99f..c0e5ccff1e 100644
--- a/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb
+++ b/railties/lib/rails/generators/erb/scaffold/templates/show.html.erb
@@ -1,4 +1,3 @@
-<% without_namespacing do -%>
<p id="notice"><%%= notice %></p>
<% for attribute in attributes -%>
@@ -11,4 +10,3 @@
<%%= link_to 'Edit', edit_<%= singular_table_name %>_path(@<%= singular_table_name %>) %> |
<%%= link_to 'Back', <%= index_helper %>_path %>
-<% end -%>
diff --git a/railties/lib/rails/generators/named_base.rb b/railties/lib/rails/generators/named_base.rb
index badf981d05..e0dde4360f 100644
--- a/railties/lib/rails/generators/named_base.rb
+++ b/railties/lib/rails/generators/named_base.rb
@@ -16,6 +16,14 @@ module Rails
parse_attributes! if respond_to?(:attributes)
end
+ no_tasks do
+ def template(source, *args, &block)
+ inside_template do
+ super
+ end
+ end
+ end
+
protected
attr_reader :file_name
alias :singular_name :file_name
@@ -23,17 +31,9 @@ module Rails
# Wrap block with namespace of current application
# if namespace exists and is not skipped
def module_namespacing(&block)
- inside_namespace do
- content = capture(&block)
- content = wrap_with_namespace(content) if namespaced?
- concat(content)
- end
- end
-
- def without_namespacing(&block)
- inside_namespace do
- concat(capture(&block))
- end
+ content = capture(&block)
+ content = wrap_with_namespace(content) if namespaced?
+ concat(content)
end
def indent(content, multiplier = 2)
@@ -46,12 +46,15 @@ module Rails
"module #{namespace.name}\n#{content}\nend\n"
end
- def inside_namespace
- @inside_namespace = true if namespaced?
- result = yield
- result
+ def inside_template
+ @inside_template = true
+ yield
ensure
- @inside_namespace = false
+ @inside_template = false
+ end
+
+ def inside_template?
+ @inside_template
end
def namespace
@@ -61,11 +64,7 @@ module Rails
end
def namespaced?
- !options[:skip_namespace] && !!namespace
- end
-
- def inside_namespace?
- @inside_namespace
+ !options[:skip_namespace] && namespace
end
def file_path
@@ -73,7 +72,7 @@ module Rails
end
def class_path
- inside_namespace? || !namespaced? ? regular_class_path : namespaced_class_path
+ inside_template? || !namespaced? ? regular_class_path : namespaced_class_path
end
def regular_class_path
diff --git a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
index 40ed2062d3..97f681d826 100644
--- a/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
+++ b/railties/lib/rails/generators/rails/plugin/plugin_generator.rb
@@ -1,3 +1,4 @@
+
require 'rails/generators/rails/generator/generator_generator'
module Rails
@@ -5,6 +6,12 @@ module Rails
class PluginGenerator < NamedBase
class_option :tasks, :desc => "When supplied creates tasks base files."
+ def show_deprecation
+ return unless behavior == :invoke
+ message = "Plugin generator is deprecated, please use 'rails plugin new' command to generate plugin structure."
+ ActiveSupport::Deprecation.warn message
+ end
+
check_class_collision
def create_root_files
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 8fac6fc70a..9c54b98238 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
@@ -102,14 +102,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"
def initialize(*args)
raise Error, "Options should be given after the plugin name. For details run: rails plugin --help" if args[0].blank?
@@ -122,7 +125,7 @@ task :default => :test
def create_root_files
build(:readme)
build(:rakefile)
- build(:gemspec)
+ build(:gemspec) unless options[:skip_gemspec]
build(:license)
build(:gitignore) unless options[:skip_git]
build(:gemfile) unless options[:skip_gemfile]
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/app/controllers/%name%/application_controller.rb.tt b/railties/lib/rails/generators/rails/plugin_new/templates/app/controllers/%name%/application_controller.rb.tt
index f225bc9f7f..448ad7f989 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/app/controllers/%name%/application_controller.rb.tt
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/app/controllers/%name%/application_controller.rb.tt
@@ -1,4 +1,4 @@
module <%= camelized %>
- class ApplicationController < ActiveController::Base
+ class ApplicationController < ActionController::Base
end
end
diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb b/railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb
index dbcaf6b92f..7b61047e77 100644
--- a/railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb
+++ b/railties/lib/rails/generators/rails/plugin_new/templates/test/test_helper.rb
@@ -9,8 +9,6 @@ Rails.backtrace_cleaner.remove_silencers!
<% if full? && !options[:skip_active_record] -%>
# Run any available migration from application
ActiveRecord::Migrator.migrate File.expand_path("../dummy/db/migrate/", __FILE__)
-# and from engine
-ActiveRecord::Migrator.migrate File.expand_path("../../db/migrate/", __FILE__)
<% end -%>
# Load support files
diff --git a/railties/lib/rails/test_help.rb b/railties/lib/rails/test_help.rb
index 38f2f651f4..f81002328f 100644
--- a/railties/lib/rails/test_help.rb
+++ b/railties/lib/rails/test_help.rb
@@ -39,14 +39,3 @@ class ActionDispatch::IntegrationTest
@routes = Rails.application.routes
end
end
-
-begin
- require_library_or_gem 'ruby-debug'
- Debugger.start
- if Debugger.respond_to?(:settings)
- Debugger.settings[:autoeval] = true
- Debugger.settings[:autolist] = 1
- end
-rescue LoadError
- # ruby-debug wasn't available so neither can the debugging be
-end
diff --git a/railties/lib/rails/version.rb b/railties/lib/rails/version.rb
index 0213d46254..b076881c21 100644
--- a/railties/lib/rails/version.rb
+++ b/railties/lib/rails/version.rb
@@ -3,8 +3,8 @@ module Rails
MAJOR = 3
MINOR = 1
TINY = 0
- BUILD = "beta"
+ PRE = "beta"
- STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
end
diff --git a/railties/test/application/generators_test.rb b/railties/test/application/generators_test.rb
index 551e966c85..8b840fffd0 100644
--- a/railties/test/application/generators_test.rb
+++ b/railties/test/application/generators_test.rb
@@ -25,6 +25,11 @@ module ApplicationTests
yield app_const.config
end
+ test "allow running plugin new generator inside Rails app directory" do
+ FileUtils.cd(rails_root){ `ruby script/rails plugin new vendor/plugins/bukkits` }
+ assert File.exist?(File.join(rails_root, "vendor/plugins/bukkits/test/dummy/config/application.rb"))
+ end
+
test "generators default values" do
with_bare_config do |c|
assert_equal(true, c.generators.colorize_logging)
diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb
index 6970ea7b7a..475091f789 100644
--- a/railties/test/application/initializers/frameworks_test.rb
+++ b/railties/test/application/initializers/frameworks_test.rb
@@ -65,6 +65,66 @@ module ApplicationTests
assert_equal ["notify"], Foo.action_methods
end
+ test "allows to not load all helpers for controllers" do
+ add_to_config "config.action_controller.include_all_helpers = false"
+
+ app_file "app/controllers/application_controller.rb", <<-RUBY
+ class ApplicationController < ActionController::Base
+ end
+ RUBY
+
+ app_file "app/controllers/foo_controller.rb", <<-RUBY
+ class FooController < ApplicationController
+ def included_helpers
+ render :inline => "<%= from_app_helper -%> <%= from_foo_helper %>"
+ end
+
+ def not_included_helper
+ render :inline => "<%= respond_to?(:from_bar_helper) -%>"
+ end
+ end
+ RUBY
+
+ app_file "app/helpers/application_helper.rb", <<-RUBY
+ module ApplicationHelper
+ def from_app_helper
+ "from_app_helper"
+ end
+ end
+ RUBY
+
+ app_file "app/helpers/foo_helper.rb", <<-RUBY
+ module FooHelper
+ def from_foo_helper
+ "from_foo_helper"
+ end
+ end
+ RUBY
+
+ app_file "app/helpers/bar_helper.rb", <<-RUBY
+ module BarHelper
+ def from_bar_helper
+ "from_bar_helper"
+ end
+ end
+ RUBY
+
+ app_file "config/routes.rb", <<-RUBY
+ AppTemplate::Application.routes.draw do
+ match "/:controller(/:action)"
+ end
+ RUBY
+
+ require 'rack/test'
+ extend Rack::Test::Methods
+
+ get "/foo/included_helpers"
+ assert_equal "from_app_helper from_foo_helper", last_response.body
+
+ get "/foo/not_included_helper"
+ assert_equal "false", last_response.body
+ end
+
# AD
test "action_dispatch extensions are applied to ActionDispatch" do
add_to_config "config.action_dispatch.tld_length = 2"
diff --git a/railties/test/application/rake_test.rb b/railties/test/application/rake_test.rb
index 719550f9d9..23cd2378c7 100644
--- a/railties/test/application/rake_test.rb
+++ b/railties/test/application/rake_test.rb
@@ -34,6 +34,22 @@ module ApplicationTests
assert_match "SuperMiddleware", Dir.chdir(app_path){ `rake middleware` }
end
+ def test_initializers_are_executed_in_rake_tasks
+ add_to_config <<-RUBY
+ initializer "do_something" do
+ puts "Doing something..."
+ end
+
+ rake_tasks do
+ task :do_nothing => :environment do
+ end
+ end
+ RUBY
+
+ output = Dir.chdir(app_path){ `rake do_nothing` }
+ assert_match "Doing something...", output
+ end
+
def test_code_statistics_sanity
assert_match "Code LOC: 5 Test LOC: 0 Code to Test Ratio: 1:0.0",
Dir.chdir(app_path){ `rake stats` }
diff --git a/railties/test/generators/migration_generator_test.rb b/railties/test/generators/migration_generator_test.rb
index f9d1e42d24..288ec30460 100644
--- a/railties/test/generators/migration_generator_test.rb
+++ b/railties/test/generators/migration_generator_test.rb
@@ -34,12 +34,12 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
run_generator [migration, "title:string", "body:text"]
assert_migration "db/migrate/#{migration}.rb" do |content|
- assert_class_method :up, content do |up|
+ assert_method :up, content do |up|
assert_match /add_column :posts, :title, :string/, up
assert_match /add_column :posts, :body, :text/, up
end
- assert_class_method :down, content do |down|
+ assert_method :down, content do |down|
assert_match /remove_column :posts, :title/, down
assert_match /remove_column :posts, :body/, down
end
@@ -51,12 +51,12 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
run_generator [migration, "title:string", "body:text"]
assert_migration "db/migrate/#{migration}.rb" do |content|
- assert_class_method :up, content do |up|
+ assert_method :up, content do |up|
assert_match /remove_column :posts, :title/, up
assert_match /remove_column :posts, :body/, up
end
- assert_class_method :down, content do |down|
+ assert_method :down, content do |down|
assert_match /add_column :posts, :title, :string/, down
assert_match /add_column :posts, :body, :text/, down
end
@@ -68,11 +68,11 @@ class MigrationGeneratorTest < Rails::Generators::TestCase
run_generator [migration, "title:string", "content:text"]
assert_migration "db/migrate/#{migration}.rb" do |content|
- assert_class_method :up, content do |up|
+ assert_method :up, content do |up|
assert_match /^\s*$/, up
end
- assert_class_method :down, content do |down|
+ assert_method :down, content do |down|
assert_match /^\s*$/, down
end
end
diff --git a/railties/test/generators/model_generator_test.rb b/railties/test/generators/model_generator_test.rb
index 52e08cf2dd..8a0f560bc8 100644
--- a/railties/test/generators/model_generator_test.rb
+++ b/railties/test/generators/model_generator_test.rb
@@ -99,13 +99,13 @@ class ModelGeneratorTest < Rails::Generators::TestCase
run_generator ["product", "name:string", "supplier_id:integer"]
assert_migration "db/migrate/create_products.rb" do |m|
- assert_class_method :up, m do |up|
+ assert_method :up, m do |up|
assert_match /create_table :products/, up
assert_match /t\.string :name/, up
assert_match /t\.integer :supplier_id/, up
end
- assert_class_method :down, m do |down|
+ assert_method :down, m do |down|
assert_match /drop_table :products/, down
end
end
@@ -141,7 +141,7 @@ class ModelGeneratorTest < Rails::Generators::TestCase
run_generator ["account", "--no-timestamps"]
assert_migration "db/migrate/create_accounts.rb" do |m|
- assert_class_method :up, m do |up|
+ assert_method :up, m do |up|
assert_no_match /t.timestamps/, up
end
end
diff --git a/railties/test/generators/plugin_generator_test.rb b/railties/test/generators/plugin_generator_test.rb
index c1f646f7c1..e6686a6af4 100644
--- a/railties/test/generators/plugin_generator_test.rb
+++ b/railties/test/generators/plugin_generator_test.rb
@@ -6,7 +6,7 @@ class PluginGeneratorTest < Rails::Generators::TestCase
arguments %w(plugin_fu)
def test_plugin_skeleton_is_created
- run_generator
+ silence(:stderr) { run_generator }
year = Date.today.year
%w(
@@ -36,30 +36,36 @@ class PluginGeneratorTest < Rails::Generators::TestCase
end
def test_invokes_default_test_framework
- run_generator
+ silence(:stderr) { run_generator }
assert_file "vendor/plugins/plugin_fu/test/plugin_fu_test.rb", /class PluginFuTest < ActiveSupport::TestCase/
assert_file "vendor/plugins/plugin_fu/test/test_helper.rb"
end
def test_logs_if_the_test_framework_cannot_be_found
- content = run_generator ["plugin_fu", "--test-framework=rspec"]
+ content = nil
+ silence(:stderr) { content = run_generator ["plugin_fu", "--test-framework=rspec"] }
assert_match /rspec \[not found\]/, content
end
def test_creates_tasks_if_required
- run_generator ["plugin_fu", "--tasks"]
+ silence(:stderr) { run_generator ["plugin_fu", "--tasks"] }
assert_file "vendor/plugins/plugin_fu/lib/tasks/plugin_fu_tasks.rake"
end
def test_creates_generator_if_required
- run_generator ["plugin_fu", "--generator"]
+ silence(:stderr) { run_generator ["plugin_fu", "--generator"] }
assert_file "vendor/plugins/plugin_fu/lib/generators/templates"
assert_file "vendor/plugins/plugin_fu/lib/generators/plugin_fu_generator.rb",
/class PluginFuGenerator < Rails::Generators::NamedBase/
end
def test_plugin_generator_on_revoke
- run_generator
+ silence(:stderr) { run_generator }
run_generator ["plugin_fu"], :behavior => :revoke
end
+
+ def test_deprecation
+ output = capture(:stderr) { run_generator }
+ assert_match /Plugin generator is deprecated, please use 'rails plugin new' command to generate plugin structure./, output
+ end
end
diff --git a/railties/test/generators/plugin_new_generator_test.rb b/railties/test/generators/plugin_new_generator_test.rb
index 12cae7cfdf..2105585272 100644
--- a/railties/test/generators/plugin_new_generator_test.rb
+++ b/railties/test/generators/plugin_new_generator_test.rb
@@ -135,7 +135,7 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
assert_file "config/routes.rb", /Bukkits::Engine.routes.draw do/
assert_file "lib/bukkits/engine.rb", /isolate_namespace Bukkits/
assert_file "test/dummy/config/routes.rb", /mount Bukkits::Engine => "\/bukkits"/
- assert_file "app/controllers/bukkits/application_controller.rb", /module Bukkits\n class ApplicationController < ActiveController::Base/
+ assert_file "app/controllers/bukkits/application_controller.rb", /module Bukkits\n class ApplicationController < ActionController::Base/
assert_file "app/helpers/bukkits/application_helper.rb", /module Bukkits\n module ApplicationHelper/
end
@@ -146,6 +146,11 @@ class PluginNewGeneratorTest < Rails::Generators::TestCase
assert_no_file "test/dummy"
end
+ def test_skipping_gemspec
+ run_generator [destination_root, "--skip-gemspec"]
+ assert_no_file "bukkits.gemspec"
+ end
+
protected
def action(*args, &block)
diff --git a/railties/test/generators_test.rb b/railties/test/generators_test.rb
index f93800a5ae..346c9ceb9d 100644
--- a/railties/test/generators_test.rb
+++ b/railties/test/generators_test.rb
@@ -102,6 +102,12 @@ class GeneratorsTest < Rails::Generators::TestCase
assert_no_match /^ app$/, output
end
+ def test_rails_generators_help_does_not_include_app_nor_plugin_new
+ output = capture(:stdout){ Rails::Generators.help }
+ assert_no_match /app/, output
+ assert_no_match /plugin_new/, output
+ end
+
def test_rails_generators_with_others_information
output = capture(:stdout){ Rails::Generators.help }
assert_match /Fixjour:/, output
diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb
index 822be24ef1..701b6816c8 100644
--- a/railties/test/railties/engine_test.rb
+++ b/railties/test/railties/engine_test.rb
@@ -224,32 +224,14 @@ module RailtiesTest
end
RUBY
- @plugin.write "app/views/foo/index.html.erb", <<-RUBY
- <%= compute_public_path("/foo", "") %>
+ @plugin.write "app/views/foo/index.html.erb", <<-ERB
<%= image_path("foo.png") %>
<%= javascript_include_tag("foo") %>
<%= stylesheet_link_tag("foo") %>
- RUBY
-
-
- app_file "app/controllers/bar_controller.rb", <<-RUBY
- class BarController < ActionController::Base
- def index
- render :index
- end
- end
- RUBY
-
- app_file "app/views/bar/index.html.erb", <<-RUBY
- <%= compute_public_path("/foo", "") %>
- RUBY
+ ERB
add_to_config 'config.asset_path = "/omg%s"'
- @plugin.write 'public/touch.txt', <<-RUBY
- touch
- RUBY
-
boot_rails
# should set asset_path with engine name by default
@@ -259,11 +241,10 @@ module RailtiesTest
env = Rack::MockRequest.env_for("/foo")
response = Bukkits::Engine.call(env)
- stripped_body = response[2].body.split("\n").map(&:strip).join("\n")
+ stripped_body = response[2].body.split("\n").map(&:strip).join
- expected = "/omg/bukkits/foo\n" +
- "/omg/bukkits/images/foo.png\n" +
- "<script src=\"/omg/bukkits/javascripts/foo.js\" type=\"text/javascript\"></script>\n" +
+ 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
@@ -278,21 +259,18 @@ module RailtiesTest
@plugin.write "app/controllers/foo_controller.rb", <<-RUBY
class FooController < ActionController::Base
def index
+ render :inline => '<%= image_path("foo.png") %>'
end
end
RUBY
- @plugin.write "app/views/foo/index.html.erb", <<-RUBY
- <%= compute_public_path("/foo", "") %>
- RUBY
-
boot_rails
env = Rack::MockRequest.env_for("/foo")
response = Bukkits::Engine.call(env)
stripped_body = response[2].body.strip
- expected = "/bukkits/foo"
+ expected = "/bukkits/images/foo.png"
assert_equal expected, stripped_body
end
diff --git a/release.rb b/release.rb
deleted file mode 100644
index 2076515f0e..0000000000
--- a/release.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-version = ARGV.pop
-
-%w( activesupport activemodel activerecord activeresource actionpack actionmailer railties ).each do |framework|
- puts "Building and pushing #{framework}..."
- `cd #{framework} && gem build #{framework}.gemspec && gem push #{framework}-#{version}.gem && rm #{framework}-#{version}.gem`
-end
-
-puts "Building and pushing Rails..."
-`gem build rails.gemspec`
-`gem push rails-#{version}.gem`
-`rm rails-#{version}.gem` \ No newline at end of file
diff --git a/tasks/release.rb b/tasks/release.rb
new file mode 100644
index 0000000000..a605fed160
--- /dev/null
+++ b/tasks/release.rb
@@ -0,0 +1,96 @@
+FRAMEWORKS = %w( activesupport activemodel activerecord activeresource actionpack actionmailer railties )
+
+root = File.expand_path('../../', __FILE__)
+version = File.read("#{root}/RAILS_VERSION").strip
+tag = "v#{version}"
+
+directory "dist"
+
+(FRAMEWORKS + ['rails']).each do |framework|
+ namespace framework do
+ gem = "dist/#{framework}-#{version}.gem"
+ gemspec = "#{framework}.gemspec"
+
+ task :clean do
+ rm_f gem
+ end
+
+ task :update_version_rb do
+ glob = root.dup
+ glob << "/#{framework}/lib/*" unless framework == "rails"
+ glob << "/version.rb"
+
+ file = Dir[glob].first
+ ruby = File.read(file)
+
+ major, minor, tiny, pre = version.split('.')
+ pre = pre ? pre.inspect : "nil"
+
+ ruby.gsub! /^(\s*)MAJOR = .*?$/, "\\1MAJOR = #{major}"
+ raise "Could not insert MAJOR in #{file}" unless $1
+
+ ruby.gsub! /^(\s*)MINOR = .*?$/, "\\1MINOR = #{minor}"
+ raise "Could not insert MINOR in #{file}" unless $1
+
+ ruby.gsub! /^(\s*)TINY = .*?$/, "\\1TINY = #{tiny}"
+ raise "Could not insert TINY in #{file}" unless $1
+
+ ruby.gsub! /^(\s*)PRE = .*?$/, "\\1PRE = #{pre}"
+ raise "Could not insert PRE in #{file}" unless $1
+
+ File.open(file, 'w') { |f| f.write ruby }
+ end
+
+ task gem => %w(update_version_rb dist) do
+ cmd = ""
+ cmd << "cd #{framework} && " unless framework == "rails"
+ cmd << "gem build #{gemspec} && mv #{framework}-#{version}.gem #{root}/dist/"
+ sh cmd
+ end
+
+ task :build => [:clean, gem]
+ task :install => :build do
+ sh "gem install #{gem}"
+ end
+
+ task :prep_release => [:ensure_clean_state, :build]
+
+ task :push => :build do
+ sh "gem push #{gem}"
+ end
+ end
+end
+
+namespace :all do
+ task :build => FRAMEWORKS.map { |f| "#{f}:build" } + ['rails:build']
+ task :install => FRAMEWORKS.map { |f| "#{f}:install" } + ['rails:install']
+ task :push => FRAMEWORKS.map { |f| "#{f}:push" } + ['rails:push']
+
+ task :ensure_clean_state do
+ unless `git status -s | grep -v RAILS_VERSION`.strip.empty?
+ abort "[ABORTING] `git status` reports a dirty tree. Make sure all changes are committed"
+ end
+
+ unless ENV['SKIP_TAG'] || `git tag | grep #{tag}`.strip.empty?
+ abort "[ABORTING] `git tag` shows that #{tag} already exists. Has this version already\n"\
+ " been released? Git tagging can be skipped by setting SKIP_TAG=1"
+ end
+ end
+
+ task :commit do
+ File.open('dist/commit_message.txt', 'w') do |f|
+ f.puts "# Preparing for #{version} release\n"
+ f.puts
+ f.puts "# UNCOMMENT THE LINE ABOVE TO APPROVE THIS COMMIT"
+ end
+
+ sh "git add . && git commit --verbose --template=dist/commit_message.txt"
+ rm_f "dist/commit_message.txt"
+ end
+
+ task :tag do
+ sh "git tag #{tag}"
+ end
+
+ task :release => %w(ensure_clean_state build commit tag push)
+end
diff --git a/version.rb b/version.rb
index 0213d46254..b076881c21 100644
--- a/version.rb
+++ b/version.rb
@@ -3,8 +3,8 @@ module Rails
MAJOR = 3
MINOR = 1
TINY = 0
- BUILD = "beta"
+ PRE = "beta"
- STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
+ STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
end
end