diff options
43 files changed, 324 insertions, 179 deletions
@@ -61,12 +61,12 @@ platforms :ruby do end platforms :jruby do - git 'git://github.com/jruby/activerecord-jdbc-adapter.git' do - gem 'activerecord-jdbcsqlite3-adapter' - group :db do - gem 'activerecord-jdbcmysql-adapter' - gem 'activerecord-jdbcpostgresql-adapter' - end + gem 'json' + gem 'activerecord-jdbcsqlite3-adapter', '>= 1.3.0' + + group :db do + gem 'activerecord-jdbcmysql-adapter', '>= 1.3.0' + gem 'activerecord-jdbcpostgresql-adapter', '>= 1.3.0' end end diff --git a/actionmailer/lib/action_mailer/base.rb b/actionmailer/lib/action_mailer/base.rb index 9dcd4bf554..ada86fbc4f 100644 --- a/actionmailer/lib/action_mailer/base.rb +++ b/actionmailer/lib/action_mailer/base.rb @@ -373,6 +373,8 @@ module ActionMailer include AbstractController::AssetPaths include AbstractController::Callbacks + self.protected_instance_variables = [:@_action_has_layout] + helper ActionMailer::MailHelper private_class_method :new #:nodoc: @@ -385,10 +387,6 @@ module ActionMailer parts_order: [ "text/plain", "text/enriched", "text/html" ] }.freeze - def self.default_protected_instance_vars - super.concat [:@_action_has_layout] - end - class << self # Register one or more Observers which will be notified when mail is delivered. def register_observers(*observers) diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index a84ed17bd4..af5de815bb 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -114,11 +114,6 @@ module AbstractController end end - # Define some internal variables that should not be propagated to the view. - def self.default_protected_instance_vars - [] - end - abstract! # Calls the action going through the entire action dispatch stack. diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb index 41f19fba78..4596aad46c 100644 --- a/actionpack/lib/abstract_controller/rendering.rb +++ b/actionpack/lib/abstract_controller/rendering.rb @@ -13,8 +13,15 @@ module AbstractController module Rendering extend ActiveSupport::Concern - def self.default_protected_instance_vars - super.concat [:@_action_name, :@_response_body, :@_formats, :@_prefixes, :@_config] + included do + class_attribute :protected_instance_variables + self.protected_instance_variables = [] + end + + # Normalize arguments, options and then delegates render_to_body and + # sticks the result in self.response_body. + # :api: public + def render(*args, &block) end # Raw rendering of a template to a string. @@ -29,32 +36,37 @@ module AbstractController # overridden in order to still return a string. # :api: plugin def render_to_string(*args, &block) + options = _normalize_render(*args, &block) + render_to_body(options) end # Raw rendering of a template. - # :api: plugin - def render_to_body(options = {}) - end - - # Normalize arguments, options and then delegates render_to_body and - # sticks the result in self.response_body. # :api: public - def render(*args, &block) + def render_to_body(options = {}) + _process_options(options) + _render_template(options) end # Return Content-Type of rendered content # :api: public def rendered_format + Mime::TEXT end + DEFAULT_PROTECTED_INSTANCE_VARIABLES = %w( + @_action_name @_response_body @_formats @_prefixes @_config + @_view_context_class @_view_renderer @_lookup_context + ) + # This method should return a hash with assigns. # You can overwrite this configuration per controller. # :api: public def view_assigns hash = {} - (instance_variables - self.class.default_protected_instance_vars).each do |name| - hash[name[1..-1]] = instance_variable_get(name) - end + variables = instance_variables + variables -= protected_instance_variables + variables -= DEFAULT_PROTECTED_INSTANCE_VARIABLES + variables.each { |name| hash[name[1..-1]] = instance_variable_get(name) } hash end @@ -76,5 +88,41 @@ module AbstractController def _process_options(options) options end + + # Normalize args and options. + # :api: private + def _normalize_render(*args, &block) + options = _normalize_args(*args, &block) + _normalize_options(options) + options + end + end + + # Basic rendering implements the most minimal rendering layer. + # It only supports rendering :text and :nothing. Passing any other option will + # result in `UnsupportedOperationError` exception. For more functionality like + # different formats, layouts etc. you should use `ActionView` gem. + module BasicRendering + extend ActiveSupport::Concern + + # Render text or nothing (empty string) to response_body + # :api: public + def render(*args, &block) + super(*args, &block) + opts = args.first + if opts.has_key?(:text) && opts[:text].present? + self.response_body = opts[:text] + elsif opts.has_key?(:nothing) && opts[:nothing] + self.response_body = " " + else + raise UnsupportedOperationError + end + end + + class UnsupportedOperationError < StandardError + def initialize + super "Unsupported render operation. BasicRendering supports only :text and :nothing options. For more, you need to include ActionView." + end + end end end diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index a00eafc9ed..df416908f0 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -14,7 +14,7 @@ module ActionController # metal = Class.new(Metal) do include AbstractController::Rendering - include ActionController::BasicRendering + include AbstractController::BasicRendering end # Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed @@ -261,12 +261,11 @@ module ActionController include mod end - def self.default_protected_instance_vars - super.concat [ - :@_status, :@_headers, :@_params, :@_env, :@_response, :@_request, - :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout - ] - end + # Define some internal variables that should not be propagated to the view. + self.protected_instance_variables = [ + :@_status, :@_headers, :@_params, :@_env, :@_response, :@_request, + :@_view_runtime, :@_stream, :@_url_options, :@_action_has_layout + ] ActiveSupport.run_load_hooks(:action_controller, self) end diff --git a/actionpack/lib/action_controller/metal/force_ssl.rb b/actionpack/lib/action_controller/metal/force_ssl.rb index b8afce42c9..a2cb6d1e66 100644 --- a/actionpack/lib/action_controller/metal/force_ssl.rb +++ b/actionpack/lib/action_controller/metal/force_ssl.rb @@ -48,7 +48,7 @@ module ActionController # You can pass any of the following options to affect the redirect status and response # * <tt>status</tt> - Redirect with a custom status (default is 301 Moved Permanently) # * <tt>flash</tt> - Set a flash message when redirecting - # * <tt>alert</tt> - Set a alert message when redirecting + # * <tt>alert</tt> - Set an alert message when redirecting # * <tt>notice</tt> - Set a notice message when redirecting # # ==== Action Options diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb index 21224b9c3b..abcc9d4acf 100644 --- a/actionpack/lib/action_controller/metal/rendering.rb +++ b/actionpack/lib/action_controller/metal/rendering.rb @@ -1,36 +1,4 @@ module ActionController - # Basic rendering implements the most minimal rendering layer. - # It only supports rendering :text and :nothing. Passing any other option will - # result in `UnsupportedOperationError` exception. For more functionality like - # different formats, layouts etc. you should use `ActionView` gem. - module BasicRendering - extend ActiveSupport::Concern - - # Render text or nothing (empty string) to response_body - # :api: public - def render(*args, &block) - super(*args, &block) - opts = args.first - if opts.has_key?(:text) && opts[:text].present? - self.response_body = opts[:text] - elsif opts.has_key?(:nothing) && opts[:nothing] - self.response_body = " " - else - raise UnsupportedOperationError - end - end - - def rendered_format - Mime::TEXT - end - - class UnsupportedOperationError < StandardError - def initialize - super "Unsupported render operation. BasicRendering supports only :text and :nothing options. For more, you need to include ActionView." - end - end - end - module Rendering extend ActiveSupport::Concern diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb index d3696cbb8a..5247e61a23 100644 --- a/actionpack/lib/action_dispatch/http/response.rb +++ b/actionpack/lib/action_dispatch/http/response.rb @@ -41,7 +41,7 @@ module ActionDispatch # :nodoc: # Get and set headers for this response. attr_accessor :header - + alias_method :headers=, :header= alias_method :headers, :header @@ -181,9 +181,9 @@ module ActionDispatch # :nodoc: end alias_method :status_message, :message - def respond_to?(method) + def respond_to?(method, include_private = false) if method.to_s == 'to_path' - stream.respond_to?(:to_path) + stream.respond_to?(method) else super end diff --git a/actionpack/test/controller/filters_test.rb b/actionpack/test/controller/filters_test.rb index e58a1aadb8..3b5d7ef446 100644 --- a/actionpack/test/controller/filters_test.rb +++ b/actionpack/test/controller/filters_test.rb @@ -17,7 +17,7 @@ class ActionController::Base def assigns(key = nil) assigns = {} instance_variables.each do |ivar| - next if ActionController::Base.default_protected_instance_vars.include?(ivar) + next if ActionController::Base.protected_instance_variables.include?(ivar) assigns[ivar[1..-1]] = instance_variable_get(ivar) end diff --git a/actionpack/test/dispatch/response_test.rb b/actionpack/test/dispatch/response_test.rb index 2fbe7358f9..4501ea095c 100644 --- a/actionpack/test/dispatch/response_test.rb +++ b/actionpack/test/dispatch/response_test.rb @@ -212,6 +212,11 @@ class ResponseTest < ActiveSupport::TestCase ActionDispatch::Response.default_headers = nil end end + + test "respond_to? accepts include_private" do + assert_not @response.respond_to?(:method_missing) + assert @response.respond_to?(:method_missing, true) + end end class ResponseIntegrationTest < ActionDispatch::IntegrationTest diff --git a/actionview/lib/action_view/helpers/asset_url_helper.rb b/actionview/lib/action_view/helpers/asset_url_helper.rb index 0b957adb91..c830ab23e3 100644 --- a/actionview/lib/action_view/helpers/asset_url_helper.rb +++ b/actionview/lib/action_view/helpers/asset_url_helper.rb @@ -143,9 +143,9 @@ module ActionView "#{source}#{tail}" end - alias_method :path_to_asset, :asset_path # aliased to avoid conflicts with a asset_path named route + alias_method :path_to_asset, :asset_path # aliased to avoid conflicts with an asset_path named route - # Computes the full URL to a asset in the public directory. This + # Computes the full URL to an asset in the public directory. This # will use +asset_path+ internally, so most of their behaviors # will be the same. def asset_url(source, options = {}) diff --git a/actionview/lib/action_view/helpers/cache_helper.rb b/actionview/lib/action_view/helpers/cache_helper.rb index 2a38e5c446..b3af1d4da4 100644 --- a/actionview/lib/action_view/helpers/cache_helper.rb +++ b/actionview/lib/action_view/helpers/cache_helper.rb @@ -4,7 +4,7 @@ module ActionView module CacheHelper # This helper exposes a method for caching fragments of a view # rather than an entire action or page. This technique is useful - # caching pieces like menus, lists of newstopics, static HTML + # caching pieces like menus, lists of new topics, static HTML # fragments, and so on. This method takes a block that contains # the content you wish to cache. # diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb index db9c4ef501..c1945cc0c5 100644 --- a/actionview/lib/action_view/rendering.rb +++ b/actionview/lib/action_view/rendering.rb @@ -84,20 +84,6 @@ module ActionView self.response_body = render_to_body(options) end - # Raw rendering of a template to a string. - # :api: public - def render_to_string(*args, &block) - options = _normalize_render(*args, &block) - render_to_body(options) - end - - # Raw rendering of a template. - # :api: public - def render_to_body(options = {}) - _process_options(options) - _render_template(options) - end - # Find and renders a template based on the options given. # :api: private def _render_template(options) #:nodoc: @@ -109,20 +95,8 @@ module ActionView Mime[lookup_context.rendered_format] end - def default_protected_instance_vars - super.concat([:@_view_context_class, :@_view_renderer, :@_lookup_context]) - end - private - # Normalize args and options. - # :api: private - def _normalize_render(*args, &block) - options = _normalize_args(*args, &block) - _normalize_options(options) - options - end - # Normalize args by converting render "foo" to render :action => "foo" and # render "foo/bar" to render :file => "foo/bar". # :api: private diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index c7928e503b..b79b9a87c6 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Re-use `order` argument pre-processing for `reorder`. + + *Paul Nikitochkin* + * Fix PredicateBuilder so polymorhic association keys in `where` clause can also accept not only `ActiveRecord::Base` direct descendances (decorated models, for example). diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index 8eec4f56af..e1fa5225b5 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -50,8 +50,8 @@ module ActiveRecord # Checks whether record is different to the current target, without loading it def different_target?(record) - if record.nil? - owner[reflection.foreign_key] + if record.nil? + owner[reflection.foreign_key] else record.id != owner[reflection.foreign_key] end diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb index 34de1a1f32..1059fc032d 100644 --- a/activerecord/lib/active_record/associations/builder/association.rb +++ b/activerecord/lib/active_record/associations/builder/association.rb @@ -17,7 +17,7 @@ module ActiveRecord::Associations::Builder end self.extensions = [] - VALID_OPTIONS = [:class_name, :foreign_key, :validate] + VALID_OPTIONS = [:class_name, :class, :foreign_key, :validate] attr_reader :name, :scope, :options diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb index 2317e34bc0..6cc9d6c079 100644 --- a/activerecord/lib/active_record/associations/preloader.rb +++ b/activerecord/lib/active_record/associations/preloader.rb @@ -85,9 +85,11 @@ module ActiveRecord def initialize(records, associations, preload_scope = nil) @records = Array.wrap(records).compact.uniq @associations = Array.wrap(associations) - @preload_scope = preload_scope || Relation.create(nil, nil) + @preload_scope = preload_scope || NULL_RELATION end + NULL_RELATION = Struct.new(:values).new({}) + def run unless records.empty? associations.each { |association| preload(association) } diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb index 10238555f0..02dc464536 100644 --- a/activerecord/lib/active_record/associations/singular_association.rb +++ b/activerecord/lib/active_record/associations/singular_association.rb @@ -42,7 +42,6 @@ module ActiveRecord scope.first.tap { |record| set_inverse_instance(record) } end - # Implemented by subclasses def replace(record) raise NotImplementedError, "Subclasses must implement a replace(record) method" end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b06add096f..04e3dd49e7 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -18,6 +18,7 @@ require 'arel' require 'active_record/errors' require 'active_record/log_subscriber' require 'active_record/explain_subscriber' +require 'active_record/relation/delegation' module ActiveRecord #:nodoc: # = Active Record @@ -290,6 +291,7 @@ module ActiveRecord #:nodoc: extend Translation extend DynamicMatchers extend Explain + extend Delegation::DelegateCache include Persistence include ReadonlyAttributes diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 5245fc130a..150a90f5db 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -121,6 +121,7 @@ module ActiveRecord @scope = scope @options = options @active_record = active_record + @klass = options[:class] @plural_name = active_record.pluralize_table_names ? name.to_s.pluralize : name.to_s end diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 52a538e5b5..27c04b0952 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -161,8 +161,7 @@ module ActiveRecord result = result.map do |attributes| values = klass.initialize_attributes(attributes).values - iter = columns.each - values.map { |value| iter.next.type_cast value } + columns.zip(values).map { |column, value| column.type_cast value } end columns.one? ? result.map!(&:first) : result end diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index b6f80ac5c7..e28938f9d0 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -1,8 +1,33 @@ -require 'thread' -require 'thread_safe' +require 'active_support/concern' module ActiveRecord module Delegation # :nodoc: + module DelegateCache + def relation_delegate_class(klass) # :nodoc: + @relation_delegate_cache[klass] + end + + def initialize_relation_delegate_cache # :nodoc: + @relation_delegate_cache = cache = {} + [ + ActiveRecord::Relation, + ActiveRecord::Associations::CollectionProxy, + ActiveRecord::AssociationRelation + ].each do |klass| + delegate = Class.new(klass) { + include ClassSpecificRelation + } + const_set klass.name.gsub('::', '_'), delegate + cache[klass] = delegate + end + end + + def inherited(child_class) + child_class.initialize_relation_delegate_cache + super + end + end + extend ActiveSupport::Concern # This module creates compiled delegation methods dynamically at runtime, which makes @@ -71,32 +96,14 @@ module ActiveRecord end module ClassMethods # :nodoc: - @@subclasses = ThreadSafe::Cache.new(:initial_capacity => 2) - def create(klass, *args) relation_class_for(klass).new(klass, *args) end private - # Cache the constants in @@subclasses because looking them up via const_get - # make instantiation significantly slower. + def relation_class_for(klass) - if klass && (klass_name = klass.name) - my_cache = @@subclasses.compute_if_absent(self) { ThreadSafe::Cache.new } - # This hash is keyed by klass.name to avoid memory leaks in development mode - my_cache.compute_if_absent(klass_name) do - # Cache#compute_if_absent guarantees that the block will only executed once for the given klass_name - subclass_name = "#{name.gsub('::', '_')}_#{klass_name.gsub('::', '_')}" - - if const_defined?(subclass_name) - const_get(subclass_name) - else - const_set(subclass_name, Class.new(self) { include ClassSpecificRelation }) - end - end - else - ActiveRecord::Relation - end + klass.relation_delegate_class(self) end end diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index c08158d38b..530c47d0d0 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -107,11 +107,11 @@ module ActiveRecord bind_values = filter_binds(lhs_binds, removed) + rhs_binds conn = relation.klass.connection - bviter = bind_values.each.with_index + bv_index = 0 where_values.map! do |node| if Arel::Nodes::Equality === node && Arel::Nodes::BindParam === node.right - (column, _), i = bviter.next - substitute = conn.substitute_at column, i + substitute = conn.substitute_at(bind_values[bv_index].first, bv_index) + bv_index += 1 Arel::Nodes::Equality.new(node.left, substitute) else node diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 9f2a039d94..1c6ea94c0b 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -289,17 +289,7 @@ module ActiveRecord end def order!(*args) # :nodoc: - args.flatten! - validate_order_args(args) - - references = args.grep(String) - references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact! - references!(references) if references.any? - - # if a symbol is given we prepend the quoted table name - args.map! do |arg| - arg.is_a?(Symbol) ? Arel::Nodes::Ascending.new(klass.arel_table[arg]) : arg - end + preprocess_order_args(args) self.order_values += args self @@ -320,8 +310,7 @@ module ActiveRecord end def reorder!(*args) # :nodoc: - args.flatten! - validate_order_args(args) + preprocess_order_args(args) self.reordering_value = true self.order_values = args @@ -1036,6 +1025,20 @@ module ActiveRecord end end + def preprocess_order_args(order_args) + order_args.flatten! + validate_order_args(order_args) + + references = order_args.grep(String) + references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact! + references!(references) if references.any? + + # if a symbol is given we prepend the quoted table name + order_args.map! do |arg| + arg.is_a?(Symbol) ? Arel::Nodes::Ascending.new(klass.arel_table[arg]) : arg + end + end + # Checks to make sure that the arguments are not blank. Note that if some # blank-like object were initially passed into the query method, then this # method will not raise an error. diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb index 253368ae5b..d0f1cb5b75 100644 --- a/activerecord/lib/active_record/result.rb +++ b/activerecord/lib/active_record/result.rb @@ -93,7 +93,21 @@ module ActiveRecord # used as keys in ActiveRecord::Base's @attributes hash columns = @columns.map { |c| c.dup.freeze } @rows.map { |row| - Hash[columns.zip(row)] + # In the past we used Hash[columns.zip(row)] + # though elegant, the verbose way is much more efficient + # both time and memory wise cause it avoids a big array allocation + # this method is called a lot and needs to be micro optimised + hash = {} + + index = 0 + length = columns.length + + while index < length + hash[columns[index]] = row[index] + index += 1 + end + + hash } end end diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb index a5d6aad3f0..01fec31544 100644 --- a/activerecord/lib/active_record/scoping/default.rb +++ b/activerecord/lib/active_record/scoping/default.rb @@ -100,11 +100,7 @@ module ActiveRecord elsif default_scopes.any? evaluate_default_scope do default_scopes.inject(relation) do |default_scope, scope| - if !scope.is_a?(Relation) && scope.respond_to?(:call) - default_scope.merge(unscoped { scope.call }) - else - default_scope.merge(scope) - end + default_scope.merge(unscoped { scope.call }) end end end diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 28bf48f4fd..8d3d6962fc 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -1172,8 +1172,11 @@ class EagerAssociationTest < ActiveRecord::TestCase } end - test "works in combination with order(:symbol)" do - author = Author.includes(:posts).references(:posts).order(:name).where('posts.title IS NOT NULL').first + test "works in combination with order(:symbol) and reorder(:symbol)" do + author = Author.includes(:posts).references(:posts).order(:name).find_by('posts.title IS NOT NULL') + assert_equal authors(:bob), author + + author = Author.includes(:posts).references(:posts).reorder(:name).find_by('posts.title IS NOT NULL') assert_equal authors(:bob), author end end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index ce51853bf3..4c0fa88917 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -45,6 +45,24 @@ class HasManyAssociationsTest < ActiveRecord::TestCase Client.destroyed_client_ids.clear end + def test_anonymous_has_many + developer = Class.new(ActiveRecord::Base) { + self.table_name = 'developers' + dev = self + + developer_project = Class.new(ActiveRecord::Base) { + self.table_name = 'developers_projects' + belongs_to :developer, :class => dev + } + has_many :developer_projects, :class => developer_project, :foreign_key => 'developer_id' + } + dev = developer.first + named = Developer.find(dev.id) + assert_operator dev.developer_projects.count, :>, 0 + assert_equal named.projects.map(&:id).sort, + dev.developer_projects.map(&:project_id).sort + end + def test_create_from_association_should_respect_default_scope car = Car.create(:name => 'honda') assert_equal 'honda', car.name diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index 45087fdf8a..508faa9437 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -28,7 +28,8 @@ require 'models/club' class HasManyThroughAssociationsTest < ActiveRecord::TestCase fixtures :posts, :readers, :people, :comments, :authors, :categories, :taggings, :tags, :owners, :pets, :toys, :jobs, :references, :companies, :members, :author_addresses, - :subscribers, :books, :subscriptions, :developers, :categorizations, :essays + :subscribers, :books, :subscriptions, :developers, :categorizations, :essays, + :categories_posts # Dummies to force column loads so query counts are clean. def setup @@ -36,6 +37,33 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase Reader.create :person_id => 0, :post_id => 0 end + def make_model(name) + Class.new(ActiveRecord::Base) { define_singleton_method(:name) { name } } + end + + def test_singleton_has_many_through + book = make_model "Book" + subscription = make_model "Subscription" + subscriber = make_model "Subscriber" + + subscriber.primary_key = 'nick' + subscription.belongs_to :book, class: book + subscription.belongs_to :subscriber, class: subscriber + + book.has_many :subscriptions, class: subscription + book.has_many :subscribers, through: :subscriptions, class: subscriber + + anonbook = book.first + namebook = Book.find anonbook.id + + assert_operator anonbook.subscribers.count, :>, 0 + anonbook.subscribers.each do |s| + assert_instance_of subscriber, s + end + assert_equal namebook.subscribers.map(&:id).sort, + anonbook.subscribers.map(&:id).sort + end + def test_pk_is_not_required_for_join post = Post.includes(:scategories).first post2 = Post.includes(:categories).first diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb index 9b1abc3b81..cf3c07845c 100644 --- a/activerecord/test/cases/associations/nested_through_associations_test.rb +++ b/activerecord/test/cases/associations/nested_through_associations_test.rb @@ -371,7 +371,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase prev_default_scope = Club.default_scopes [:includes, :preload, :joins, :eager_load].each do |q| - Club.default_scopes = [Club.send(q, :category)] + Club.default_scopes = [proc { Club.send(q, :category) }] assert_equal categories(:general), members(:groucho).reload.club_category end ensure diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index c91cf89f6d..dc2de7b523 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1324,6 +1324,35 @@ class BasicsTest < ActiveRecord::TestCase assert_equal 1, post.comments.length end + def test_marshal_between_processes + skip "fork isn't supported" unless Process.respond_to?(:fork) + + # Define a new model to ensure there are no caches + if self.class.const_defined?("Post", false) + flunk "there should be no post constant" + end + + self.class.const_set("Post", Class.new(ActiveRecord::Base) { + has_many :comments + }) + + rd, wr = IO.pipe + + ActiveRecord::Base.connection_handler.clear_all_connections! + + fork do + rd.close + post = Post.new + post.comments.build + wr.write Marshal.dump(post) + wr.close + end + + wr.close + assert Marshal.load rd.read + rd.close + end + def test_marshalling_new_record_round_trip_with_associations post = Post.new post.comments.build diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb index f73e449610..1e6ccecfab 100644 --- a/activerecord/test/cases/dup_test.rb +++ b/activerecord/test/cases/dup_test.rb @@ -126,7 +126,7 @@ module ActiveRecord def test_dup_with_default_scope prev_default_scopes = Topic.default_scopes - Topic.default_scopes = [Topic.where(:approved => true)] + Topic.default_scopes = [proc { Topic.where(:approved => true) }] topic = Topic.new(:approved => false) assert !topic.dup.approved?, "should not be overridden by default scopes" ensure diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index f99801c437..a327b0d3e5 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -9,6 +9,10 @@ module ActiveRecord fixtures :posts, :comments, :authors class FakeKlass < Struct.new(:table_name, :name) + extend ActiveRecord::Delegation::DelegateCache + + inherited self + def self.connection Post.connection end @@ -76,8 +80,8 @@ module ActiveRecord end def test_table_name_delegates_to_klass - relation = Relation.new FakeKlass.new('foo'), :b - assert_equal 'foo', relation.table_name + relation = Relation.new FakeKlass.new('posts'), :b + assert_equal 'posts', relation.table_name end def test_scope_for_create @@ -177,8 +181,12 @@ module ActiveRecord end test 'merging a hash interpolates conditions' do - klass = stub_everything - klass.stubs(:sanitize_sql).with(['foo = ?', 'bar']).returns('foo = bar') + klass = Class.new(FakeKlass) do + def self.sanitize_sql(args) + raise unless args == ['foo = ?', 'bar'] + 'foo = bar' + end + end relation = Relation.new(klass, :b) relation.merge!(where: ['foo = ?', 'bar']) @@ -210,6 +218,9 @@ module ActiveRecord class RelationMutationTest < ActiveSupport::TestCase class FakeKlass < Struct.new(:table_name, :name) + extend ActiveRecord::Delegation::DelegateCache + inherited self + def arel_table Post.arel_table end @@ -217,6 +228,10 @@ module ActiveRecord def connection Post.connection end + + def relation_delegate_class(klass) + self.class.relation_delegate_class(klass) + end end def relation @@ -296,6 +311,15 @@ module ActiveRecord assert relation.reordering_value end + test '#reorder! with symbol prepends the table name' do + assert relation.reorder!(:name).equal?(relation) + node = relation.order_values.first + + assert node.ascending? + assert_equal :name, node.expr.name + assert_equal "posts", node.expr.relation.name + end + test 'reverse_order!' do assert relation.reverse_order!.equal?(relation) assert relation.reverse_order_value diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index e1a760d240..b3ca45c4c8 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1344,6 +1344,24 @@ class RelationTest < ActiveRecord::TestCase assert_equal [], scope.references_values end + def test_automatically_added_reorder_references + scope = Post.reorder('comments.body') + assert_equal %w(comments), scope.references_values + + scope = Post.reorder('comments.body', 'yaks.body') + assert_equal %w(comments yaks), scope.references_values + + # Don't infer yaks, let's not go down that road again... + scope = Post.reorder('comments.body, yaks.body') + assert_equal %w(comments), scope.references_values + + scope = Post.reorder('comments.body asc') + assert_equal %w(comments), scope.references_values + + scope = Post.reorder('foo(comments.body)') + assert_equal [], scope.references_values + end + def test_presence topics = Topic.all diff --git a/activesupport/lib/active_support/inflector/methods.rb b/activesupport/lib/active_support/inflector/methods.rb index d063fcb49c..ffdb7b53c4 100644 --- a/activesupport/lib/active_support/inflector/methods.rb +++ b/activesupport/lib/active_support/inflector/methods.rb @@ -36,7 +36,7 @@ module ActiveSupport # string. # # If passed an optional +locale+ parameter, the word will be - # pluralized using rules defined for that language. By default, + # singularized using rules defined for that language. By default, # this parameter is set to <tt>:en</tt>. # # 'posts'.singularize # => "post" diff --git a/guides/source/asset_pipeline.md b/guides/source/asset_pipeline.md index 8fa86cb516..72aff1e0dd 100644 --- a/guides/source/asset_pipeline.md +++ b/guides/source/asset_pipeline.md @@ -765,8 +765,8 @@ headers. For Apache: ```apache -# The Expires* directives requires the Apache module `mod_expires` to be -# enabled. +# The Expires* directives requires the Apache module +# `mod_expires` to be enabled. <Location /assets/> # Use of ETag is discouraged when Last-Modified is present Header unset ETag FileETag None diff --git a/guides/source/development_dependencies_install.md b/guides/source/development_dependencies_install.md index 12b192fa87..c4e5789a1a 100644 --- a/guides/source/development_dependencies_install.md +++ b/guides/source/development_dependencies_install.md @@ -86,7 +86,6 @@ And if you are on Fedora or CentOS, you're done with $ sudo yum install sqlite3 sqlite3-devel ``` -<<<<<<< HEAD If you are on Arch Linux, you will need to run: ```bash @@ -101,8 +100,6 @@ For FreeBSD users, you're done with: Or compile the `databases/sqlite3` port. -======= ->>>>>>> ec8ef1e1055c4e1598da13f49d30261f07f4a9b4 Get a recent version of [Bundler](http://gembundler.com/) ```bash @@ -158,13 +155,20 @@ $ cd railties $ TEST_DIR=generators bundle exec rake test ``` -You can run any single test separately too: +You can run the tests for a particular file by using: ```bash $ cd actionpack $ bundle exec ruby -Itest test/template/form_helper_test.rb ``` +Or, you can run a single test in a particular file: + +```bash +$ cd actionpack +$ bundle exec ruby -Itest path/to/test.rb -n test_name +``` + ### Active Record Setup The test suite of Active Record attempts to run four times: once for SQLite3, once for each of the two MySQL gems (`mysql` and `mysql2`), and once for PostgreSQL. We are going to see now how to set up the environment for them. @@ -191,7 +195,6 @@ $ sudo yum install mysql-server mysql-devel $ sudo yum install postgresql-server postgresql-devel ``` -<<<<<<< HEAD If you are running Arch Linux, MySQL isn't supported anymore so you will need to use MariaDB instead (see [this announcement](https://www.archlinux.org/news/mariadb-replaces-mysql-in-repositories/)): @@ -211,8 +214,6 @@ Or install them through ports (they are located under the `databases` folder). If you run into troubles during the installation of MySQL, please see [the MySQL documentation](http://dev.mysql.com/doc/refman/5.1/en/freebsd-installation.html). -======= ->>>>>>> ec8ef1e1055c4e1598da13f49d30261f07f4a9b4 After that, run: ```bash diff --git a/guides/source/migrations.md b/guides/source/migrations.md index 6100fc89c8..414ce46a4e 100644 --- a/guides/source/migrations.md +++ b/guides/source/migrations.md @@ -692,10 +692,15 @@ Neither of these Rake tasks do anything you could not do with `db:migrate`. They are simply more convenient, since you do not need to explicitly specify the version to migrate to. +### Setup the Database + +The `rake db:setup` task will create the database, load the schema and initialize +it with the seed data. + ### Resetting the Database -The `rake db:reset` task will drop the database, recreate it and load the -current schema into it. +The `rake db:reset` task will drop the database and set it up again. This is +functionally equivalent to `rake db:drop db:setup`. NOTE: This is not the same as running all the migrations. It will only use the contents of the current schema.rb file. If a migration can't be rolled back, diff --git a/railties/lib/rails/engine/railties.rb b/railties/lib/rails/engine/railties.rb index 595256f36a..9969a1475d 100644 --- a/railties/lib/rails/engine/railties.rb +++ b/railties/lib/rails/engine/railties.rb @@ -16,8 +16,6 @@ module Rails def -(others) _all - others end - - delegate :engines, to: "self.class" end end end diff --git a/railties/lib/rails/generators/rails/controller/controller_generator.rb b/railties/lib/rails/generators/rails/controller/controller_generator.rb index 822f35fb42..ef84447df9 100644 --- a/railties/lib/rails/generators/rails/controller/controller_generator.rb +++ b/railties/lib/rails/generators/rails/controller/controller_generator.rb @@ -32,18 +32,18 @@ module Rails # namespace :foo do # namespace :bar do namespace_ladder = class_path.each_with_index.map do |ns, i| - %{#{" " * i * 2}namespace :#{ns} do\n } + indent("namespace :#{ns} do\n", i * 2) end.join # Create route # get "baz/index" - route = %{#{" " * depth * 2}get "#{file_name}/#{action}"\n} + route = indent(%{get "#{file_name}/#{action}"\n}, depth * 2) # Create `end` ladder # end # end end_ladder = (1..depth).reverse_each.map do |i| - "#{" " * i * 2}end\n" + indent("end\n", i * 2) end.join # Combine the 3 parts to generate complete route entry diff --git a/railties/lib/rails/railtie/configuration.rb b/railties/lib/rails/railtie/configuration.rb index 0cbbf04da2..eb3b2d8ef4 100644 --- a/railties/lib/rails/railtie/configuration.rb +++ b/railties/lib/rails/railtie/configuration.rb @@ -80,7 +80,7 @@ module Rails to_prepare_blocks << blk if blk end - def respond_to?(name) + def respond_to?(name, include_private = false) super || @@options.key?(name.to_sym) end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index c51488e0e1..03a735b1c1 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -679,5 +679,12 @@ module ApplicationTests end assert_equal Logger::INFO, Rails.logger.level end + + test "respond_to? accepts include_private" do + make_basic_app + + assert_not Rails.configuration.respond_to?(:method_missing) + assert Rails.configuration.respond_to?(:method_missing, true) + end end end diff --git a/railties/test/generators/namespaced_generators_test.rb b/railties/test/generators/namespaced_generators_test.rb index a4d8b3d1b0..e17925ff65 100644 --- a/railties/test/generators/namespaced_generators_test.rb +++ b/railties/test/generators/namespaced_generators_test.rb @@ -44,7 +44,7 @@ class NamespacedControllerGeneratorTest < NamespacedGeneratorTestCase end end - def test_helpr_is_also_namespaced + def test_helper_is_also_namespaced run_generator assert_file "app/helpers/test_app/account_helper.rb", /module TestApp/, / module AccountHelper/ assert_file "test/helpers/test_app/account_helper_test.rb", /module TestApp/, / class AccountHelperTest/ |