diff options
65 files changed, 834 insertions, 524 deletions
diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 0c1228b7c6..c992f1388a 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,5 +1,7 @@ ## Rails 3.2.0 (unreleased) ## +* Depends on rack ~> 1.4.0 *Santiago Pastorino* + * Add :gzip option to `caches_page`. The default option can be configured globally using `page_cache_compression` *Andrey Sitnik* * The ShowExceptions middleware now accepts a exceptions application that is responsible to render an exception when the application fails. The application is invoked with a copy of the exception in `env["action_dispatch.exception"]` and with the PATH_INFO rewritten to the status code. *José Valim* diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index e369277e5a..dbf78a6d94 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -20,7 +20,7 @@ Gem::Specification.new do |s| s.add_dependency('activemodel', version) s.add_dependency('rack-cache', '~> 1.1') s.add_dependency('builder', '~> 3.0.0') - s.add_dependency('rack', '~> 1.3.5') + s.add_dependency('rack', '~> 1.4.0') s.add_dependency('rack-test', '~> 0.6.1') s.add_dependency('journey', '~> 1.0.0.rc1') s.add_dependency('sprockets', '~> 2.1.2') diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb index 56335a22cb..594ba7a7b8 100644 --- a/actionpack/lib/action_controller/metal/http_authentication.rb +++ b/actionpack/lib/action_controller/metal/http_authentication.rb @@ -145,7 +145,7 @@ module ActionController end def encode_credentials(user_name, password) - "Basic #{ActiveSupport::Base64.encode64s("#{user_name}:#{password}")}" + "Basic #{ActiveSupport::Base64.strict_encode64("#{user_name}:#{password}")}" end def authentication_request(controller, realm) @@ -289,7 +289,7 @@ module ActionController t = time.to_i hashed = [t, secret_key] digest = ::Digest::MD5.hexdigest(hashed.join(":")) - ActiveSupport::Base64.encode64("#{t}:#{digest}").gsub("\n", '') + ActiveSupport::Base64.strict_encode64("#{t}:#{digest}") end # Might want a shorter timeout depending on whether the request diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index d0e2a6f39c..9fc308375f 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -32,6 +32,7 @@ module ActiveModel autoload :AttributeMethods autoload :BlockValidator, 'active_model/validator' autoload :Callbacks + autoload :Configuration autoload :Conversion autoload :Dirty autoload :EachValidator, 'active_model/validator' diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index d7ec7e6ca5..71ab1501c8 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -61,7 +61,8 @@ module ActiveModel CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/ included do - class_attribute :attribute_method_matchers, :instance_writer => false + extend ActiveModel::Configuration + config_attribute :attribute_method_matchers self.attribute_method_matchers = [ClassMethods::AttributeMethodMatcher.new] end diff --git a/activemodel/lib/active_model/configuration.rb b/activemodel/lib/active_model/configuration.rb new file mode 100644 index 0000000000..1757c12ebf --- /dev/null +++ b/activemodel/lib/active_model/configuration.rb @@ -0,0 +1,134 @@ +require 'active_support/concern' +require 'active_support/core_ext/class/attribute' +require 'active_support/core_ext/class/attribute_accessors' + +module ActiveModel + # This API is for Rails' internal use and is not currently considered 'public', so + # it may change in the future without warning. + # + # It creates configuration attributes that can be inherited from a module down + # to a class that includes the module. E.g. + # + # module MyModel + # extend ActiveModel::Configuration + # config_attribute :awesome + # self.awesome = true + # end + # + # class Post + # include MyModel + # end + # + # Post.awesome # => true + # + # Post.awesome = false + # Post.awesome # => false + # MyModel.awesome # => true + # + # We assume that the module will have a ClassMethods submodule containing methods + # to be transferred to the including class' singleton class. + # + # Config options can also be defined directly on a class: + # + # class Post + # extend ActiveModel::Configuration + # config_attribute :awesome + # end + # + # So this allows us to define a module that doesn't care about whether it is being + # included in a class or a module: + # + # module Awesomeness + # extend ActiveSupport::Concern + # + # included do + # extend ActiveModel::Configuration + # config_attribute :awesome + # self.awesome = true + # end + # end + # + # class Post + # include Awesomeness + # end + # + # module AwesomeModel + # include Awesomeness + # end + module Configuration #:nodoc: + def config_attribute(name, options = {}) + klass = self.is_a?(Class) ? ClassAttribute : ModuleAttribute + klass.new(self, name, options).define + end + + class Attribute + attr_reader :host, :name, :options + + def initialize(host, name, options) + @host, @name, @options = host, name, options + end + + def instance_writer? + options.fetch(:instance_writer, false) + end + end + + class ClassAttribute < Attribute + def define + if options[:global] + host.cattr_accessor name, :instance_writer => instance_writer? + else + host.class_attribute name, :instance_writer => instance_writer? + end + end + end + + class ModuleAttribute < Attribute + def class_methods + @class_methods ||= begin + if host.const_defined?(:ClassMethods, false) + host.const_get(:ClassMethods) + else + host.const_set(:ClassMethods, Module.new) + end + end + end + + def define + host.singleton_class.class_eval <<-CODE, __FILE__, __LINE__ + attr_accessor :#{name} + def #{name}?; !!#{name}; end + CODE + + name, host = self.name, self.host + + class_methods.class_eval do + define_method(name) { host.send(name) } + define_method("#{name}?") { !!send(name) } + end + + host.class_eval <<-CODE + def #{name}; defined?(@#{name}) ? @#{name} : self.class.#{name}; end + def #{name}?; !!#{name}; end + CODE + + if options[:global] + class_methods.class_eval do + define_method("#{name}=") { |val| host.send("#{name}=", val) } + end + else + class_methods.class_eval <<-CODE, __FILE__, __LINE__ + def #{name}=(val) + singleton_class.class_eval do + remove_possible_method(:#{name}) + define_method(:#{name}) { val } + end + end + CODE + end + + host.send(:attr_writer, name) if instance_writer? + end + end + end +end diff --git a/activemodel/lib/active_model/mass_assignment_security.rb b/activemodel/lib/active_model/mass_assignment_security.rb index c895968f77..9b12d9d281 100644 --- a/activemodel/lib/active_model/mass_assignment_security.rb +++ b/activemodel/lib/active_model/mass_assignment_security.rb @@ -10,11 +10,13 @@ module ActiveModel extend ActiveSupport::Concern included do - class_attribute :_accessible_attributes - class_attribute :_protected_attributes - class_attribute :_active_authorizer + extend ActiveModel::Configuration - class_attribute :_mass_assignment_sanitizer + config_attribute :_accessible_attributes + config_attribute :_protected_attributes + config_attribute :_active_authorizer + + config_attribute :_mass_assignment_sanitizer self.mass_assignment_sanitizer = :logger end @@ -56,7 +58,7 @@ module ActiveModel # You can specify your own sanitizer object eg. MySanitizer.new. # See <tt>ActiveModel::MassAssignmentSecurity::LoggerSanitizer</tt> for example implementation. # - # + # module ClassMethods # Attributes named in this macro are protected from mass-assignment # whenever attributes are sanitized before assignment. A role for the @@ -70,13 +72,13 @@ module ActiveModel # # class Customer # include ActiveModel::MassAssignmentSecurity - # + # # attr_accessor :name, :email, :logins_count - # + # # attr_protected :logins_count # # Suppose that admin can not change email for customer - # attr_protected :logins_count, :email, :as => :admin - # + # attr_protected :logins_count, :email, :as => :admin + # # def assign_attributes(values, options = {}) # sanitize_for_mass_assignment(values, options[:as]).each do |k, v| # send("#{k}=", v) @@ -99,7 +101,7 @@ module ActiveModel # customer.name # => "David" # customer.email # => nil # customer.logins_count # => nil - # + # # customer.email = "c@d.com" # customer.email # => "c@d.com" # diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb index c845440120..63ab8e7edc 100644 --- a/activemodel/lib/active_model/serializers/json.rb +++ b/activemodel/lib/active_model/serializers/json.rb @@ -10,8 +10,9 @@ module ActiveModel included do extend ActiveModel::Naming + extend ActiveModel::Configuration - class_attribute :include_root_in_json + config_attribute :include_root_in_json self.include_root_in_json = true end diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb index 8ed392abca..1779efd526 100644 --- a/activemodel/lib/active_model/validations.rb +++ b/activemodel/lib/active_model/validations.rb @@ -42,9 +42,9 @@ module ActiveModel # module Validations extend ActiveSupport::Concern - include ActiveSupport::Callbacks included do + extend ActiveModel::Callbacks extend ActiveModel::Translation extend HelperMethods @@ -53,7 +53,8 @@ module ActiveModel attr_accessor :validation_context define_callbacks :validate, :scope => :name - class_attribute :_validators + extend ActiveModel::Configuration + config_attribute :_validators self._validators = Hash.new { |h,k| h[k] = [] } end diff --git a/activemodel/test/cases/configuration_test.rb b/activemodel/test/cases/configuration_test.rb new file mode 100644 index 0000000000..a172fa26a3 --- /dev/null +++ b/activemodel/test/cases/configuration_test.rb @@ -0,0 +1,154 @@ +require 'cases/helper' + +class ConfigurationOnModuleTest < ActiveModel::TestCase + def setup + @mod = mod = Module.new do + extend ActiveSupport::Concern + extend ActiveModel::Configuration + + config_attribute :omg + self.omg = "default" + + config_attribute :wtf, global: true + self.wtf = "default" + + config_attribute :boolean + + config_attribute :lol, instance_writer: true + end + + @klass = Class.new do + include mod + end + + @subklass = Class.new(@klass) + end + + test "default" do + assert_equal "default", @mod.omg + assert_equal "default", @klass.omg + assert_equal "default", @klass.new.omg + end + + test "setting" do + @mod.omg = "lol" + assert_equal "lol", @mod.omg + end + + test "setting on class including the module" do + @klass.omg = "lol" + assert_equal "lol", @klass.omg + assert_equal "lol", @klass.new.omg + assert_equal "default", @mod.omg + end + + test "setting on subclass of class including the module" do + @subklass.omg = "lol" + assert_equal "lol", @subklass.omg + assert_equal "default", @klass.omg + assert_equal "default", @mod.omg + end + + test "setting on instance" do + assert !@klass.new.respond_to?(:omg=) + + @klass.lol = "lol" + obj = @klass.new + assert_equal "lol", obj.lol + obj.lol = "omg" + assert_equal "omg", obj.lol + assert_equal "lol", @klass.lol + assert_equal "lol", @klass.new.lol + obj.lol = false + assert !obj.lol? + end + + test "global attribute" do + assert_equal "default", @mod.wtf + assert_equal "default", @klass.wtf + + @mod.wtf = "wtf" + + assert_equal "wtf", @mod.wtf + assert_equal "wtf", @klass.wtf + + @klass.wtf = "lol" + + assert_equal "lol", @mod.wtf + assert_equal "lol", @klass.wtf + end + + test "boolean" do + assert_equal false, @mod.boolean? + assert_equal false, @klass.new.boolean? + @mod.boolean = true + assert_equal true, @mod.boolean? + assert_equal true, @klass.new.boolean? + end +end + +class ConfigurationOnClassTest < ActiveModel::TestCase + def setup + @klass = Class.new do + extend ActiveModel::Configuration + + config_attribute :omg + self.omg = "default" + + config_attribute :wtf, global: true + self.wtf = "default" + + config_attribute :omg2, instance_writer: true + config_attribute :wtf2, instance_writer: true, global: true + end + + @subklass = Class.new(@klass) + end + + test "defaults" do + assert_equal "default", @klass.omg + assert_equal "default", @klass.wtf + assert_equal "default", @subklass.omg + assert_equal "default", @subklass.wtf + end + + test "changing" do + @klass.omg = "lol" + assert_equal "lol", @klass.omg + assert_equal "lol", @subklass.omg + end + + test "changing in subclass" do + @subklass.omg = "lol" + assert_equal "lol", @subklass.omg + assert_equal "default", @klass.omg + end + + test "changing global" do + @klass.wtf = "wtf" + assert_equal "wtf", @klass.wtf + assert_equal "wtf", @subklass.wtf + + @subklass.wtf = "lol" + assert_equal "lol", @klass.wtf + assert_equal "lol", @subklass.wtf + end + + test "instance_writer" do + obj = @klass.new + + @klass.omg2 = "omg" + @klass.wtf2 = "wtf" + + assert_equal "omg", obj.omg2 + assert_equal "wtf", obj.wtf2 + + obj.omg2 = "lol" + obj.wtf2 = "lol" + + assert_equal "lol", obj.omg2 + assert_equal "lol", obj.wtf2 + assert_equal "omg", @klass.omg2 + assert_equal "lol", @klass.wtf2 + end +end diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index 4d143146d5..4f194cb6cf 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -57,9 +57,9 @@ module ActiveRecord autoload :Base autoload :Callbacks - autoload :Configuration autoload :Core autoload :CounterCache + autoload :ConnectionHandling autoload :DynamicMatchers autoload :DynamicFinderMatch autoload :DynamicScopeMatch diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 44b0956e4e..c5834e2fef 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -61,7 +61,7 @@ module ActiveRecord raise DangerousAttributeError, "#{method_name} is defined by ActiveRecord" end - if active_record_super == Base + if [Base, Model].include?(active_record_super) super else # If B < A and A defines its own attribute method, then we don't want to overwrite that. diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index f61e016f46..40e4a97e73 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -13,7 +13,7 @@ module ActiveRecord raise "You cannot include Dirty after Timestamp" end - class_attribute :partial_updates + config_attribute :partial_updates self.partial_updates = true end diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 7d2d1db4b5..964c4123ef 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -5,7 +5,10 @@ module ActiveRecord ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date] - Configuration.define :attribute_types_cached_by_default, ATTRIBUTE_TYPES_CACHED_BY_DEFAULT + included do + config_attribute :attribute_types_cached_by_default, :global => true + self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT + end module ClassMethods # +cache_attributes+ allows you to declare which converted attribute values should diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index 2ffd91f796..0c8e4e4b9a 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -6,7 +6,7 @@ module ActiveRecord included do # Returns a hash of all the attributes that have been specified for serialization as # keys and their class restriction as values. - class_attribute :serialized_attributes + config_attribute :serialized_attributes self.serialized_attributes = {} 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 5e5392441b..2f86e32f41 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -6,10 +6,11 @@ module ActiveRecord module TimeZoneConversion extend ActiveSupport::Concern - Configuration.define :time_zone_aware_attributes, false - included do - class_attribute :skip_time_zone_conversion_for_attributes, :instance_writer => false + config_attribute :time_zone_aware_attributes, :global => true + self.time_zone_aware_attributes = false + + config_attribute :skip_time_zone_conversion_for_attributes self.skip_time_zone_conversion_for_attributes = [] end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 6085df7d9f..d29cf82dad 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -328,9 +328,7 @@ module ActiveRecord #:nodoc: # instances in the current object space. class Base include ActiveRecord::Model - self.connection_handler = ConnectionAdapters::ConnectionHandler.new end end -require 'active_record/connection_adapters/abstract/connection_specification' ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Model::DeprecationProxy) diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index a175bf003c..8b2fc69b00 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -25,7 +25,7 @@ module ActiveRecord # Check out <tt>ActiveRecord::Transactions</tt> for more details about <tt>after_commit</tt> and # <tt>after_rollback</tt>. # - # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that + # Lastly an <tt>after_find</tt> and <tt>after_initialize</tt> callback is triggered for each object that # is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects # are instantiated as well. # @@ -215,24 +215,48 @@ module ActiveRecord # instead of quietly returning +false+. # # == Debugging callbacks - # - # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support + # + # The callback chain is accessible via the <tt>_*_callbacks</tt> method on an object. ActiveModel Callbacks support # <tt>:before</tt>, <tt>:after</tt> and <tt>:around</tt> as values for the <tt>kind</tt> property. The <tt>kind</tt> property # defines what part of the chain the callback runs in. - # - # To find all callbacks in the before_save callback chain: - # + # + # To find all callbacks in the before_save callback chain: + # # Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) } - # + # # Returns an array of callback objects that form the before_save chain. - # + # # To further check if the before_save chain contains a proc defined as <tt>rest_when_dead</tt> use the <tt>filter</tt> property of the callback object: - # + # # Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:rest_when_dead) - # + # # Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model. - # + # module Callbacks + # We can't define callbacks directly on ActiveRecord::Model because + # it is a module. So we queue up the definitions and execute them + # when ActiveRecord::Model is included. + module Register #:nodoc: + def self.extended(base) + base.config_attribute :_callbacks_register + base._callbacks_register = [] + end + + def self.setup(base) + base._callbacks_register.each do |item| + base.send(*item) + end + end + + def define_callbacks(*args) + self._callbacks_register << [:define_callbacks, *args] + end + + def define_model_callbacks(*args) + self._callbacks_register << [:define_model_callbacks, *args] + end + end + extend ActiveSupport::Concern CALLBACKS = [ @@ -242,8 +266,11 @@ module ActiveRecord :before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback ] + module ClassMethods + include ActiveModel::Callbacks + end + included do - extend ActiveModel::Callbacks include ActiveModel::Validations::Callbacks define_model_callbacks :initialize, :find, :touch, :only => :after diff --git a/activerecord/lib/active_record/configuration.rb b/activerecord/lib/active_record/configuration.rb deleted file mode 100644 index d58ed82258..0000000000 --- a/activerecord/lib/active_record/configuration.rb +++ /dev/null @@ -1,36 +0,0 @@ -require 'active_support/concern' - -module ActiveRecord - # This module allows configuration options to be specified in a way such that - # ActiveRecord::Base and ActiveRecord::Model will have access to the same value, - # and will automatically get the appropriate readers and writers defined. - # - # In the future, we should probably move away from defining global config - # directly on ActiveRecord::Base / ActiveRecord::Model. - module Configuration #:nodoc: - extend ActiveSupport::Concern - - module ClassMethods - end - - def self.define(name, default = nil) - singleton_class.send(:attr_accessor, name) - - [self, ClassMethods].each do |klass| - klass.class_eval <<-CODE, __FILE__, __LINE__ - def #{name} - ActiveRecord::Configuration.#{name} - end - CODE - end - - ClassMethods.class_eval <<-CODE, __FILE__, __LINE__ - def #{name}=(val) - ActiveRecord::Configuration.#{name} = val - end - CODE - - send("#{name}=", default) unless default.nil? - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 5749d45a18..ea738cb305 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -370,7 +370,7 @@ connection. For example: ActiveRecord::Base.connection.close def retrieve_connection_pool(klass) pool = @class_to_pool[klass.name] return pool if pool - return nil if ActiveRecord::Base == klass + return nil if ActiveRecord::Model == klass retrieve_connection_pool klass.active_record_super end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb deleted file mode 100644 index 63e4020113..0000000000 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ /dev/null @@ -1,184 +0,0 @@ -require 'active_support/core_ext/module/delegation' - -module ActiveRecord - module Core - class ConnectionSpecification #:nodoc: - attr_reader :config, :adapter_method - def initialize (config, adapter_method) - @config, @adapter_method = config, adapter_method - end - - ## - # Builds a ConnectionSpecification from user input - class Resolver # :nodoc: - attr_reader :config, :klass, :configurations - - def initialize(config, configurations) - @config = config - @configurations = configurations - end - - def spec - case config - when nil - raise AdapterNotSpecified unless defined?(Rails.env) - resolve_string_connection Rails.env - when Symbol, String - resolve_string_connection config.to_s - when Hash - resolve_hash_connection config - end - end - - private - def resolve_string_connection(spec) # :nodoc: - hash = configurations.fetch(spec) do |k| - connection_url_to_hash(k) - end - - raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash - - resolve_hash_connection hash - end - - def resolve_hash_connection(spec) # :nodoc: - spec = spec.symbolize_keys - - raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter) - - begin - require "active_record/connection_adapters/#{spec[:adapter]}_adapter" - rescue LoadError => e - raise LoadError, "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{e.message})", e.backtrace - end - - adapter_method = "#{spec[:adapter]}_connection" - - ConnectionSpecification.new(spec, adapter_method) - end - - def connection_url_to_hash(url) # :nodoc: - config = URI.parse url - adapter = config.scheme - adapter = "postgresql" if adapter == "postgres" - spec = { :adapter => adapter, - :username => config.user, - :password => config.password, - :port => config.port, - :database => config.path.sub(%r{^/},""), - :host => config.host } - spec.reject!{ |_,value| !value } - if config.query - options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys - spec.merge!(options) - end - spec - end - end - end - - # Returns the connection currently associated with the class. This can - # also be used to "borrow" the connection to do database work that isn't - # easily done without going straight to SQL. - def connection - self.class.connection - end - - module ClassMethods - # Establishes the connection to the database. Accepts a hash as input where - # the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case) - # example for regular databases (MySQL, Postgresql, etc): - # - # ActiveRecord::Base.establish_connection( - # :adapter => "mysql", - # :host => "localhost", - # :username => "myuser", - # :password => "mypass", - # :database => "somedatabase" - # ) - # - # Example for SQLite database: - # - # ActiveRecord::Base.establish_connection( - # :adapter => "sqlite", - # :database => "path/to/dbfile" - # ) - # - # Also accepts keys as strings (for parsing from YAML for example): - # - # ActiveRecord::Base.establish_connection( - # "adapter" => "sqlite", - # "database" => "path/to/dbfile" - # ) - # - # Or a URL: - # - # ActiveRecord::Base.establish_connection( - # "postgres://myuser:mypass@localhost/somedatabase" - # ) - # - # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError - # may be returned on an error. - def establish_connection(spec = ENV["DATABASE_URL"]) - resolver = ConnectionSpecification::Resolver.new spec, configurations - spec = resolver.spec - - unless respond_to?(spec.adapter_method) - raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter" - end - - remove_connection - connection_handler.establish_connection name, spec - end - - # Returns the connection currently associated with the class. This can - # also be used to "borrow" the connection to do database work unrelated - # to any of the specific Active Records. - def connection - retrieve_connection - end - - def connection_id - Thread.current['ActiveRecord::Base.connection_id'] - end - - def connection_id=(connection_id) - Thread.current['ActiveRecord::Base.connection_id'] = connection_id - end - - # Returns the configuration of the associated connection as a hash: - # - # ActiveRecord::Base.connection_config - # # => {:pool=>5, :timeout=>5000, :database=>"db/development.sqlite3", :adapter=>"sqlite3"} - # - # Please use only for reading. - def connection_config - connection_pool.spec.config - end - - def connection_pool - connection_handler.retrieve_connection_pool(self) or raise ConnectionNotEstablished - end - - def retrieve_connection - connection_handler.retrieve_connection(self) - end - - # Returns true if Active Record is connected. - def connected? - connection_handler.connected?(self) - end - - def remove_connection(klass = self) - connection_handler.remove_connection(klass) - end - - def clear_active_connections! - connection_handler.clear_active_connections! - end - - delegate :clear_reloadable_connections!, - :clear_all_connections!,:verify_active_connections!, :to => :connection_handler - end - end -end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 5a2493f69d..edea414db7 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -11,6 +11,7 @@ module ActiveRecord extend ActiveSupport::Autoload autoload :Column + autoload :ConnectionSpecification autoload_under 'abstract' do autoload :IndexDefinition, 'active_record/connection_adapters/abstract/schema_definitions' @@ -26,7 +27,6 @@ module ActiveRecord autoload :ConnectionPool autoload :ConnectionHandler, 'active_record/connection_adapters/abstract/connection_pool' autoload :ConnectionManagement, 'active_record/connection_adapters/abstract/connection_pool' - autoload :ConnectionSpecification autoload :QueryCache end diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb new file mode 100644 index 0000000000..3e67c3a2b7 --- /dev/null +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -0,0 +1,79 @@ +module ActiveRecord + module ConnectionAdapters + class ConnectionSpecification #:nodoc: + attr_reader :config, :adapter_method + + def initialize (config, adapter_method) + @config, @adapter_method = config, adapter_method + end + + ## + # Builds a ConnectionSpecification from user input + class Resolver # :nodoc: + attr_reader :config, :klass, :configurations + + def initialize(config, configurations) + @config = config + @configurations = configurations + end + + def spec + case config + when nil + raise AdapterNotSpecified unless defined?(Rails.env) + resolve_string_connection Rails.env + when Symbol, String + resolve_string_connection config.to_s + when Hash + resolve_hash_connection config + end + end + + private + def resolve_string_connection(spec) # :nodoc: + hash = configurations.fetch(spec) do |k| + connection_url_to_hash(k) + end + + raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash + + resolve_hash_connection hash + end + + def resolve_hash_connection(spec) # :nodoc: + spec = spec.symbolize_keys + + raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter) + + begin + require "active_record/connection_adapters/#{spec[:adapter]}_adapter" + rescue LoadError => e + raise LoadError, "Please install the #{spec[:adapter]} adapter: `gem install activerecord-#{spec[:adapter]}-adapter` (#{e.message})", e.backtrace + end + + adapter_method = "#{spec[:adapter]}_connection" + + ConnectionSpecification.new(spec, adapter_method) + end + + def connection_url_to_hash(url) # :nodoc: + config = URI.parse url + adapter = config.scheme + adapter = "postgresql" if adapter == "postgres" + spec = { :adapter => adapter, + :username => config.user, + :password => config.password, + :port => config.port, + :database => config.path.sub(%r{^/},""), + :host => config.host } + spec.reject!{ |_,value| !value } + if config.query + options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys + spec.merge!(options) + end + spec + end + end + end + end +end diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index e51796871a..6086c32dbe 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -4,7 +4,7 @@ gem 'mysql2', '~> 0.3.10' require 'mysql2' module ActiveRecord - module Core::ClassMethods + module ConnectionHandling # Establishes a connection to the database that's used by all Active Record objects. def mysql2_connection(config) config[:username] = 'root' if config[:username].nil? diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 901d8422f2..e432c5af32 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -18,7 +18,7 @@ class Mysql end module ActiveRecord - module Core::ClassMethods + module ConnectionHandling # Establishes a connection to the database that's used by all Active Record objects. def mysql_connection(config) # :nodoc: config = config.symbolize_keys diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 74a9be99bd..d2361b5378 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -7,7 +7,7 @@ gem 'pg', '~> 0.11' require 'pg' module ActiveRecord - module Core::ClassMethods + module ConnectionHandling # Establishes a connection to the database that's used by all Active Record objects def postgresql_connection(config) # :nodoc: config = config.symbolize_keys diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index ac3fb72b6e..ee5d10859c 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -4,7 +4,7 @@ gem 'sqlite3', '~> 1.3.5' require 'sqlite3' module ActiveRecord - module Core::ClassMethods + module ConnectionHandling # sqlite3 adapter reuses sqlite_connection. def sqlite3_connection(config) # :nodoc: # Require database. diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb new file mode 100644 index 0000000000..e9cd3d5e57 --- /dev/null +++ b/activerecord/lib/active_record/connection_handling.rb @@ -0,0 +1,100 @@ +require 'active_support/core_ext/module/delegation' + +module ActiveRecord + module ConnectionHandling + # Establishes the connection to the database. Accepts a hash as input where + # the <tt>:adapter</tt> key must be specified with the name of a database adapter (in lower-case) + # example for regular databases (MySQL, Postgresql, etc): + # + # ActiveRecord::Base.establish_connection( + # :adapter => "mysql", + # :host => "localhost", + # :username => "myuser", + # :password => "mypass", + # :database => "somedatabase" + # ) + # + # Example for SQLite database: + # + # ActiveRecord::Base.establish_connection( + # :adapter => "sqlite", + # :database => "path/to/dbfile" + # ) + # + # Also accepts keys as strings (for parsing from YAML for example): + # + # ActiveRecord::Base.establish_connection( + # "adapter" => "sqlite", + # "database" => "path/to/dbfile" + # ) + # + # Or a URL: + # + # ActiveRecord::Base.establish_connection( + # "postgres://myuser:mypass@localhost/somedatabase" + # ) + # + # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError + # may be returned on an error. + def establish_connection(spec = ENV["DATABASE_URL"]) + resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new spec, configurations + spec = resolver.spec + + unless respond_to?(spec.adapter_method) + raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter" + end + + remove_connection + connection_handler.establish_connection name, spec + end + + # Returns the connection currently associated with the class. This can + # also be used to "borrow" the connection to do database work unrelated + # to any of the specific Active Records. + def connection + retrieve_connection + end + + def connection_id + Thread.current['ActiveRecord::Base.connection_id'] + end + + def connection_id=(connection_id) + Thread.current['ActiveRecord::Base.connection_id'] = connection_id + end + + # Returns the configuration of the associated connection as a hash: + # + # ActiveRecord::Base.connection_config + # # => {:pool=>5, :timeout=>5000, :database=>"db/development.sqlite3", :adapter=>"sqlite3"} + # + # Please use only for reading. + def connection_config + connection_pool.spec.config + end + + def connection_pool + connection_handler.retrieve_connection_pool(self) or raise ConnectionNotEstablished + end + + def retrieve_connection + connection_handler.retrieve_connection(self) + end + + # Returns true if Active Record is connected. + def connected? + connection_handler.connected?(self) + end + + def remove_connection(klass = self) + connection_handler.remove_connection(klass) + end + + def clear_active_connections! + connection_handler.clear_active_connections! + end + + delegate :clear_reloadable_connections!, + :clear_all_connections!,:verify_active_connections!, :to => :connection_handler + end +end diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 4f118e46a9..980f8fe50f 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -4,70 +4,73 @@ module ActiveRecord module Core extend ActiveSupport::Concern - ## - # :singleton-method: - # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, - # which is then passed on to any new database connections made and which can be retrieved on both - # a class and instance level by calling +logger+. - Configuration.define :logger - - ## - # :singleton-method: - # Contains the database configuration - as is typically stored in config/database.yml - - # as a Hash. - # - # For example, the following database.yml... - # - # development: - # adapter: sqlite3 - # database: db/development.sqlite3 - # - # production: - # adapter: sqlite3 - # database: db/production.sqlite3 - # - # ...would result in ActiveRecord::Base.configurations to look like this: - # - # { - # 'development' => { - # 'adapter' => 'sqlite3', - # 'database' => 'db/development.sqlite3' - # }, - # 'production' => { - # 'adapter' => 'sqlite3', - # 'database' => 'db/production.sqlite3' - # } - # } - Configuration.define :configurations, {} - - ## - # :singleton-method: - # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling - # dates and times from the database. This is set to :local by default. - Configuration.define :default_timezone, :local - - ## - # :singleton-method: - # Specifies the format to use when dumping the database schema with Rails' - # Rakefile. If :sql, the schema is dumped as (potentially database- - # specific) SQL statements. If :ruby, the schema is dumped as an - # ActiveRecord::Schema file which can be loaded into any database that - # supports migrations. Use :ruby if you want to have different database - # adapters for, e.g., your development and test environments. - Configuration.define :schema_format, :ruby - - ## - # :singleton-method: - # Specify whether or not to use timestamps for migration versions - Configuration.define :timestamped_migrations, true - included do ## # :singleton-method: - # The connection handler - class_attribute :connection_handler, :instance_writer => false + # Accepts a logger conforming to the interface of Log4r or the default Ruby 1.8+ Logger class, + # which is then passed on to any new database connections made and which can be retrieved on both + # a class and instance level by calling +logger+. + config_attribute :logger, :global => true + + ## + # :singleton-method: + # Contains the database configuration - as is typically stored in config/database.yml - + # as a Hash. + # + # For example, the following database.yml... + # + # development: + # adapter: sqlite3 + # database: db/development.sqlite3 + # + # production: + # adapter: sqlite3 + # database: db/production.sqlite3 + # + # ...would result in ActiveRecord::Base.configurations to look like this: + # + # { + # 'development' => { + # 'adapter' => 'sqlite3', + # 'database' => 'db/development.sqlite3' + # }, + # 'production' => { + # 'adapter' => 'sqlite3', + # 'database' => 'db/production.sqlite3' + # } + # } + config_attribute :configurations, :global => true + self.configurations = {} + + ## + # :singleton-method: + # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling + # dates and times from the database. This is set to :local by default. + config_attribute :default_timezone, :global => true + self.default_timezone = :local - initialize_generated_modules unless self == Base + ## + # :singleton-method: + # Specifies the format to use when dumping the database schema with Rails' + # Rakefile. If :sql, the schema is dumped as (potentially database- + # specific) SQL statements. If :ruby, the schema is dumped as an + # ActiveRecord::Schema file which can be loaded into any database that + # supports migrations. Use :ruby if you want to have different database + # adapters for, e.g., your development and test environments. + config_attribute :schema_format, :global => true + self.schema_format = :ruby + + ## + # :singleton-method: + # Specify whether or not to use timestamps for migration versions + config_attribute :timestamped_migrations, :global => true + self.timestamped_migrations = true + + ## + # :singleton-method: + # The connection handler + config_attribute :connection_handler + self.connection_handler = ConnectionAdapters::ConnectionHandler.new end module ClassMethods @@ -114,13 +117,7 @@ module ActiveRecord end def arel_engine - @arel_engine ||= begin - if self == ActiveRecord::Base - ActiveRecord::Base - else - connection_handler.connection_pools[name] ? self : active_record_super.arel_engine - end - end + @arel_engine ||= connection_handler.connection_pools[name] ? self : active_record_super.arel_engine end private @@ -299,6 +296,13 @@ module ActiveRecord @readonly = true end + # Returns the connection currently associated with the class. This can + # also be used to "borrow" the connection to do database work that isn't + # easily done without going straight to SQL. + def connection + self.class.connection + end + # Returns the contents of the record as a nicely formatted string. def inspect inspection = if @attributes diff --git a/activerecord/lib/active_record/explain.rb b/activerecord/lib/active_record/explain.rb index b5a67afd88..e502d7e52b 100644 --- a/activerecord/lib/active_record/explain.rb +++ b/activerecord/lib/active_record/explain.rb @@ -2,9 +2,11 @@ require 'active_support/core_ext/class/attribute' module ActiveRecord module Explain - # If a query takes longer than these many seconds we log its query plan - # automatically. nil disables this feature. - Configuration.define :auto_explain_threshold_in_seconds + def self.extended(base) + # If a query takes longer than these many seconds we log its query plan + # automatically. nil disables this feature. + base.config_attribute :auto_explain_threshold_in_seconds, :global => true + end # If auto explain is enabled, this method triggers EXPLAIN logging for the # queries triggered by the block if it takes more than the threshold as a diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index ec57151d40..eaa7deac5a 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -6,7 +6,7 @@ module ActiveRecord included do # Determine whether to store the full constant name including namespace when using STI - class_attribute :store_full_sti_class + config_attribute :store_full_sti_class self.store_full_sti_class = true end @@ -17,8 +17,10 @@ module ActiveRecord if sup.abstract_class? sup.descends_from_active_record? + elsif self == Base + false else - sup == Base || !columns_hash.include?(inheritance_column) + [Base, Model].include?(sup) || !columns_hash.include?(inheritance_column) end end @@ -81,18 +83,12 @@ module ActiveRecord instance end - # If this class includes ActiveRecord::Model then it won't have a - # superclass. So this provides a way to get to the 'root' (ActiveRecord::Base), - # through inheritance hierarchy, ending in Base, whether or not that is - # actually an ancestor of the class. + # For internal use. # - # Mainly for internal use. + # If this class includes ActiveRecord::Model then it won't have a + # superclass. So this provides a way to get to the 'root' (ActiveRecord::Model). def active_record_super #:nodoc: - if self == Base || superclass && superclass < Model::Tag - superclass - else - Base - end + superclass < Model ? superclass : Model end protected @@ -100,12 +96,12 @@ module ActiveRecord # Returns the class descending directly from ActiveRecord::Base or an # abstract class, if any, in the inheritance hierarchy. def class_of_active_record_descendant(klass) - unless klass < Model::Tag + unless klass < Model raise ActiveRecordError, "#{name} doesn't belong in a hierarchy descending from ActiveRecord" end sup = klass.active_record_super - if klass == Base || sup == Base || sup.abstract_class? + if [Base, Model].include?(klass) || [Base, Model].include?(sup) || sup.abstract_class? klass else class_of_active_record_descendant(sup) diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index b80d01db81..e643c0d437 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -48,7 +48,10 @@ module ActiveRecord module Optimistic extend ActiveSupport::Concern - Configuration.define :lock_optimistically, true + included do + config_attribute :lock_optimistically, :global => true + self.lock_optimistically = true + end def locking_enabled? #:nodoc: self.class.locking_enabled? diff --git a/activerecord/lib/active_record/model.rb b/activerecord/lib/active_record/model.rb index f87be257db..86de5ab2fa 100644 --- a/activerecord/lib/active_record/model.rb +++ b/activerecord/lib/active_record/model.rb @@ -9,54 +9,74 @@ module ActiveRecord # end # module Model - # So we can recognise an AR class even while self.included is being - # executed. (At that time, klass < Model == false.) - module Tag #:nodoc: + module ClassMethods #:nodoc: + include ActiveSupport::Callbacks::ClassMethods + include ActiveModel::Naming + include QueryCache::ClassMethods + include ActiveSupport::Benchmarkable + include ActiveSupport::DescendantsTracker + + include Querying + include Translation + include DynamicMatchers + include CounterCache + include Explain + include ConnectionHandling end def self.included(base) - return if base < Tag + return if base.singleton_class < ClassMethods base.class_eval do - include Tag + extend ClassMethods + Callbacks::Register.setup(self) + initialize_generated_modules unless self == Base + end + end - include Configuration + extend ActiveModel::Configuration + extend ActiveModel::Callbacks + extend ActiveModel::MassAssignmentSecurity::ClassMethods + extend ActiveModel::AttributeMethods::ClassMethods + extend Callbacks::Register + extend Explain + extend ConnectionHandling - include ActiveRecord::Persistence - extend ActiveModel::Naming - extend QueryCache::ClassMethods - extend ActiveSupport::Benchmarkable - extend ActiveSupport::DescendantsTracker + def self.extend(*modules) + ClassMethods.send(:include, *modules) + end - extend Querying - include ReadonlyAttributes - include ModelSchema - extend Translation - include Inheritance - include Scoping - extend DynamicMatchers - include Sanitization - include Integration - include AttributeAssignment - include ActiveModel::Conversion - include Validations - extend CounterCache - include Locking::Optimistic, Locking::Pessimistic - include AttributeMethods - include Callbacks, ActiveModel::Observing, Timestamp - include Associations - include IdentityMap - include ActiveModel::SecurePassword - extend Explain + include Persistence + include ReadonlyAttributes + include ModelSchema + include Inheritance + include Scoping + include Sanitization + include Integration + include AttributeAssignment + include ActiveModel::Conversion + include Validations + include Locking::Optimistic, Locking::Pessimistic + include AttributeMethods + include Callbacks, ActiveModel::Observing, Timestamp + include Associations + include IdentityMap + include ActiveModel::SecurePassword + include AutosaveAssociation, NestedAttributes + include Aggregations, Transactions, Reflection, Serialization, Store + include Core - # AutosaveAssociation needs to be included before Transactions, because we want - # #save_with_autosave_associations to be wrapped inside a transaction. - include AutosaveAssociation, NestedAttributes - include Aggregations, Transactions, Reflection, Serialization, Store + class << self + def arel_engine + self + end - include Core + def abstract_class? + false + end - self.connection_handler = Base.connection_handler + def inheritance_column + 'type' end end diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index adf85c6436..c1a8583119 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -5,18 +5,18 @@ module ActiveRecord module ModelSchema extend ActiveSupport::Concern - ## - # :singleton-method: - # Accessor for the prefix type that will be prepended to every primary key column name. - # The options are :table_name and :table_name_with_underscore. If the first is specified, - # the Product class will look for "productid" instead of "id" as the primary column. If the - # latter is specified, the Product class will look for "product_id" instead of "id". Remember - # that this is a global setting for all Active Records. - Configuration.define :primary_key_prefix_type - included do ## # :singleton-method: + # Accessor for the prefix type that will be prepended to every primary key column name. + # The options are :table_name and :table_name_with_underscore. If the first is specified, + # the Product class will look for "productid" instead of "id" as the primary column. If the + # latter is specified, the Product class will look for "product_id" instead of "id". Remember + # that this is a global setting for all Active Records. + config_attribute :primary_key_prefix_type, :global => true + + ## + # :singleton-method: # Accessor for the name of the prefix string to prepend to every table name. So if set # to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people", # etc. This is a convenient way of creating a namespace for tables in a shared database. @@ -25,14 +25,14 @@ module ActiveRecord # If you are organising your models within modules you can add a prefix to the models within # a namespace by defining a singleton method in the parent module called table_name_prefix which # returns your chosen prefix. - class_attribute :table_name_prefix, :instance_writer => false + config_attribute :table_name_prefix self.table_name_prefix = "" ## # :singleton-method: # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp", # "people_basecamp"). By default, the suffix is the empty string. - class_attribute :table_name_suffix, :instance_writer => false + config_attribute :table_name_suffix self.table_name_suffix = "" ## @@ -40,7 +40,7 @@ module ActiveRecord # Indicates whether table names should be the pluralized versions of the corresponding class names. # If true, the default table name for a Product class will be +products+. If false, it would just be +product+. # See table_name for the full rules on table/class naming. This is true, by default. - class_attribute :pluralize_table_names, :instance_writer => false + config_attribute :pluralize_table_names self.pluralize_table_names = true end @@ -143,11 +143,7 @@ module ActiveRecord # The name of the column containing the object's class when Single Table Inheritance is used def inheritance_column - if self == Base - 'type' - else - (@inheritance_column ||= nil) || active_record_super.inheritance_column - end + (@inheritance_column ||= nil) || active_record_super.inheritance_column end # Sets the value of inheritance_column diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index d2065d701f..a8ee43b598 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -12,7 +12,7 @@ module ActiveRecord extend ActiveSupport::Concern included do - class_attribute :nested_attributes_options, :instance_writer => false + config_attribute :nested_attributes_options self.nested_attributes_options = {} end diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 08cf9fb504..165d0750dd 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -108,7 +108,7 @@ module ActiveRecord config.after_initialize do ActiveSupport.on_load(:active_record) do - instantiate_observers + ActiveRecord::Base.instantiate_observers ActionDispatch::Reloader.to_prepare do ActiveRecord::Base.instantiate_observers diff --git a/activerecord/lib/active_record/readonly_attributes.rb b/activerecord/lib/active_record/readonly_attributes.rb index bf37ab5f14..836b15e2ce 100644 --- a/activerecord/lib/active_record/readonly_attributes.rb +++ b/activerecord/lib/active_record/readonly_attributes.rb @@ -6,7 +6,7 @@ module ActiveRecord extend ActiveSupport::Concern included do - class_attribute :_attr_readonly, :instance_writer => false + config_attribute :_attr_readonly self._attr_readonly = [] end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 794a0526fb..60b73e9fe5 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -7,7 +7,8 @@ module ActiveRecord extend ActiveSupport::Concern included do - class_attribute :reflections + extend ActiveModel::Configuration + config_attribute :reflections self.reflections = {} end diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index eee198e760..88081edae2 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -15,44 +15,49 @@ module ActiveRecord table = Arel::Table.new(table_name, engine) end - attribute = table[column.to_sym] - - case value - when ActiveRecord::Relation - value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty? - attribute.in(value.arel.ast) - when Array, ActiveRecord::Associations::CollectionProxy - values = value.to_a.map {|x| x.is_a?(ActiveRecord::Model) ? x.id : x} - ranges, values = values.partition {|v| v.is_a?(Range) || v.is_a?(Arel::Relation)} - - array_predicates = ranges.map {|range| attribute.in(range)} + build(table[column.to_sym], value) + end + end + predicates.flatten + end - if values.include?(nil) - values = values.compact - if values.empty? - array_predicates << attribute.eq(nil) - else - array_predicates << attribute.in(values.compact).or(attribute.eq(nil)) - end + private + def self.build(attribute, value) + case value + when ActiveRecord::Relation + value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty? + attribute.in(value.arel.ast) + when Array, ActiveRecord::Associations::CollectionProxy + values = value.to_a.map {|x| x.is_a?(ActiveRecord::Model) ? x.id : x} + ranges, values = values.partition {|v| v.is_a?(Range) || v.is_a?(Arel::Relation)} + + array_predicates = ranges.map {|range| attribute.in(range)} + + if values.include?(nil) + values = values.compact + case values.length + when 0 + array_predicates << attribute.eq(nil) + when 1 + array_predicates << attribute.eq(values.first).or(attribute.eq(nil)) else - array_predicates << attribute.in(values) + array_predicates << attribute.in(values).or(attribute.eq(nil)) end - - array_predicates.inject {|composite, predicate| composite.or(predicate)} - when Range, Arel::Relation - attribute.in(value) - when ActiveRecord::Model - attribute.eq(value.id) - when Class - # FIXME: I think we need to deprecate this behavior - attribute.eq(value.name) else - attribute.eq(value) + array_predicates << attribute.in(values) end + + array_predicates.inject {|composite, predicate| composite.or(predicate)} + when Range, Arel::Relation + attribute.in(value) + when ActiveRecord::Model + attribute.eq(value.id) + when Class + # FIXME: I think we need to deprecate this behavior + attribute.eq(value.name) + else + attribute.eq(value) end end - - predicates.flatten - end end end diff --git a/activerecord/lib/active_record/scoping/default.rb b/activerecord/lib/active_record/scoping/default.rb index 6b5070808a..5f05d146f2 100644 --- a/activerecord/lib/active_record/scoping/default.rb +++ b/activerecord/lib/active_record/scoping/default.rb @@ -7,7 +7,7 @@ module ActiveRecord included do # Stores the default scope for the class - class_attribute :default_scopes, :instance_writer => false + config_attribute :default_scopes self.default_scopes = [] end diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb index 17122740da..0edc3f1dcc 100644 --- a/activerecord/lib/active_record/scoping/named.rb +++ b/activerecord/lib/active_record/scoping/named.rb @@ -118,16 +118,16 @@ module ActiveRecord # when they are used. For example, the following would be incorrect: # # class Post < ActiveRecord::Base - # scope :recent, where('published_at >= ?', Time.now - 1.week) + # scope :recent, where('published_at >= ?', Time.current - 1.week) # end # - # The example above would be 'frozen' to the <tt>Time.now</tt> value when the <tt>Post</tt> + # The example above would be 'frozen' to the <tt>Time.current</tt> value when the <tt>Post</tt> # class was defined, and so the resultant SQL query would always be the same. The correct # way to do this would be via a lambda, which will re-evaluate the scope each time # it is called: # # class Post < ActiveRecord::Base - # scope :recent, lambda { where('published_at >= ?', Time.now - 1.week) } + # scope :recent, lambda { where('published_at >= ?', Time.current - 1.week) } # end # # Named \scopes can also have extensions, just as with <tt>has_many</tt> declarations: diff --git a/activerecord/lib/active_record/timestamp.rb b/activerecord/lib/active_record/timestamp.rb index 0c760e9850..c717fdea47 100644 --- a/activerecord/lib/active_record/timestamp.rb +++ b/activerecord/lib/active_record/timestamp.rb @@ -33,7 +33,7 @@ module ActiveRecord extend ActiveSupport::Concern included do - class_attribute :record_timestamps + config_attribute :record_timestamps, :instance_writer => true self.record_timestamps = true end diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 56c359650e..852fc0e26e 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -69,16 +69,16 @@ module ActiveRecord def test_not_specifying_database_name_for_cross_database_selects begin assert_nothing_raised do - ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['arunit'].except(:database)) + ActiveRecord::Model.establish_connection(ActiveRecord::Base.configurations['arunit'].except(:database)) config = ARTest.connection_config - ActiveRecord::Base.connection.execute( + ActiveRecord::Model.connection.execute( "SELECT #{config['arunit']['database']}.pirates.*, #{config['arunit2']['database']}.courses.* " \ "FROM #{config['arunit']['database']}.pirates, #{config['arunit2']['database']}.courses" ) end ensure - ActiveRecord::Base.establish_connection 'arunit' + ActiveRecord::Model.establish_connection 'arunit' end end end diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb index 2a89430da9..fa2ba8d592 100644 --- a/activerecord/test/cases/adapters/mysql/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql/connection_test.rb @@ -3,13 +3,13 @@ require "cases/helper" class MysqlConnectionTest < ActiveRecord::TestCase def setup super - @connection = ActiveRecord::Base.connection + @connection = ActiveRecord::Model.connection end def test_mysql_reconnect_attribute_after_connection_with_reconnect_true run_without_connection do |orig_connection| - ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => true})) - assert ActiveRecord::Base.connection.raw_connection.reconnect + ActiveRecord::Model.establish_connection(orig_connection.merge({:reconnect => true})) + assert ActiveRecord::Model.connection.raw_connection.reconnect end end @@ -25,8 +25,8 @@ class MysqlConnectionTest < ActiveRecord::TestCase def test_mysql_reconnect_attribute_after_connection_with_reconnect_false run_without_connection do |orig_connection| - ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => false})) - assert !ActiveRecord::Base.connection.raw_connection.reconnect + ActiveRecord::Model.establish_connection(orig_connection.merge({:reconnect => false})) + assert !ActiveRecord::Model.connection.raw_connection.reconnect end end @@ -114,7 +114,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase # Test that MySQL allows multiple results for stored procedures if defined?(Mysql) && Mysql.const_defined?(:CLIENT_MULTI_RESULTS) def test_multi_results - rows = ActiveRecord::Base.connection.select_rows('CALL ten();') + rows = ActiveRecord::Model.connection.select_rows('CALL ten();') assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}" assert @connection.active?, "Bad connection use by 'MysqlAdapter.select_rows'" end @@ -123,11 +123,11 @@ class MysqlConnectionTest < ActiveRecord::TestCase private def run_without_connection - original_connection = ActiveRecord::Base.remove_connection + original_connection = ActiveRecord::Model.remove_connection begin yield original_connection ensure - ActiveRecord::Base.establish_connection(original_connection) + ActiveRecord::Model.establish_connection(original_connection) end end end diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index 26091c713b..8e2b9ca9a5 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -3,7 +3,7 @@ require "cases/helper" class MysqlConnectionTest < ActiveRecord::TestCase def setup super - @connection = ActiveRecord::Base.connection + @connection = ActiveRecord::Model.connection end def test_no_automatic_reconnection_after_timeout @@ -32,11 +32,11 @@ class MysqlConnectionTest < ActiveRecord::TestCase private def run_without_connection - original_connection = ActiveRecord::Base.remove_connection + original_connection = ActiveRecord::Model.remove_connection begin yield original_connection ensure - ActiveRecord::Base.establish_connection(original_connection) + ActiveRecord::Model.establish_connection(original_connection) end end end diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb index 375c207d20..0df9ffc0c5 100644 --- a/activerecord/test/cases/attribute_methods/read_test.rb +++ b/activerecord/test/cases/attribute_methods/read_test.rb @@ -18,7 +18,6 @@ module ActiveRecord def self.active_record_super; Base; end def self.base_class; self; end - include ActiveRecord::Configuration include ActiveRecord::AttributeMethods def self.column_names diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index c465e9b556..80a660717e 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -23,6 +23,7 @@ require 'models/edge' require 'models/joke' require 'models/bulb' require 'models/bird' +require 'models/teapot' require 'rexml/document' require 'active_support/core_ext/exception' require 'bcrypt' @@ -1634,10 +1635,7 @@ class BasicsTest < ActiveRecord::TestCase end def test_descends_from_active_record - # Tries to call Object.abstract_class? - assert_raise(NoMethodError) do - ActiveRecord::Base.descends_from_active_record? - end + assert !ActiveRecord::Base.descends_from_active_record? # Abstract subclass of AR::Base. assert LoosePerson.descends_from_active_record? @@ -1660,6 +1658,10 @@ class BasicsTest < ActiveRecord::TestCase # Concrete subclasses an abstract class which has a type column. assert !SubStiPost.descends_from_active_record? + + assert Teapot.descends_from_active_record? + assert !OtherTeapot.descends_from_active_record? + assert CoolTeapot.descends_from_active_record? end def test_find_on_abstract_base_class_doesnt_use_type_condition @@ -1893,4 +1895,13 @@ class BasicsTest < ActiveRecord::TestCase Bird.stubs(:scoped).returns(mock(:uniq => scope)) assert_equal scope, Bird.uniq end + + def test_active_record_super + assert_equal ActiveRecord::Model, ActiveRecord::Base.active_record_super + assert_equal ActiveRecord::Base, Topic.active_record_super + assert_equal Topic, ImportantTopic.active_record_super + assert_equal ActiveRecord::Model, Teapot.active_record_super + assert_equal Teapot, OtherTeapot.active_record_super + assert_equal ActiveRecord::Model, CoolTeapot.active_record_super + end end diff --git a/activerecord/test/cases/configuration_test.rb b/activerecord/test/cases/configuration_test.rb deleted file mode 100644 index 872f1fc33b..0000000000 --- a/activerecord/test/cases/configuration_test.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'cases/helper' - -class ConfigurationTest < ActiveRecord::TestCase - def test_configuration - @klass = Class.new do - include ActiveRecord::Configuration - end - - ActiveRecord::Configuration.define :omg - - ActiveRecord::Configuration.omg = "omg" - - assert_equal "omg", @klass.new.omg - assert !@klass.new.respond_to?(:omg=) - assert_equal "omg", @klass.omg - - @klass.omg = "wtf" - - assert_equal "wtf", @klass.omg - assert_equal "wtf", @klass.new.omg - ensure - ActiveRecord::Configuration.send(:undef_method, :omg) - ActiveRecord::Configuration::ClassMethods.send(:undef_method, :omg) - ActiveRecord::Configuration::ClassMethods.send(:undef_method, :omg=) - end -end diff --git a/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb b/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb index 7af9079b48..7dc6e8afcb 100644 --- a/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb +++ b/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb @@ -35,7 +35,7 @@ module ActiveRecord end def test_close - pool = ConnectionPool.new(Base::ConnectionSpecification.new({}, nil)) + pool = ConnectionPool.new(ConnectionSpecification.new({}, nil)) pool.connections << adapter adapter.pool = pool diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb index 5f9a742285..e6cb1b9521 100644 --- a/activerecord/test/cases/connection_specification/resolver_test.rb +++ b/activerecord/test/cases/connection_specification/resolver_test.rb @@ -1,7 +1,7 @@ require "cases/helper" module ActiveRecord - module Core + module ConnectionAdapters class ConnectionSpecification class ResolverTest < ActiveRecord::TestCase def resolve(spec) diff --git a/activerecord/test/cases/inclusion_test.rb b/activerecord/test/cases/inclusion_test.rb index f2c442c2e1..8726ba5e51 100644 --- a/activerecord/test/cases/inclusion_test.rb +++ b/activerecord/test/cases/inclusion_test.rb @@ -56,10 +56,13 @@ class InclusionUnitTest < ActiveRecord::TestCase def test_establish_connection assert @klass.respond_to?(:establish_connection) + assert ActiveRecord::Model.respond_to?(:establish_connection) end def test_adapter_connection - assert @klass.respond_to?("#{ActiveRecord::Base.connection_config[:adapter]}_connection") + name = "#{ActiveRecord::Base.connection_config[:adapter]}_connection" + assert @klass.respond_to?(name) + assert ActiveRecord::Model.respond_to?(name) end def test_connection_handler diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index fab858e09c..02df464469 100644 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -65,7 +65,6 @@ class InheritanceTest < ActiveRecord::TestCase end def test_company_descends_from_active_record - assert_raise(NoMethodError) { ActiveRecord::Base.descends_from_active_record? } assert AbstractCompany.descends_from_active_record?, 'AbstractCompany should descend from ActiveRecord::Base' assert Company.descends_from_active_record?, 'Company should descend from ActiveRecord::Base' assert !Class.new(Company).descends_from_active_record?, 'Company subclass should not descend from ActiveRecord::Base' diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb index 296621c0d1..fba3006ebe 100644 --- a/activerecord/test/cases/pooled_connections_test.rb +++ b/activerecord/test/cases/pooled_connections_test.rb @@ -7,17 +7,17 @@ class PooledConnectionsTest < ActiveRecord::TestCase def setup @per_test_teardown = [] - @connection = ActiveRecord::Base.remove_connection + @connection = ActiveRecord::Model.remove_connection end def teardown - ActiveRecord::Base.clear_all_connections! - ActiveRecord::Base.establish_connection(@connection) + ActiveRecord::Model.clear_all_connections! + ActiveRecord::Model.establish_connection(@connection) @per_test_teardown.each {|td| td.call } end def checkout_connections - ActiveRecord::Base.establish_connection(@connection.merge({:pool => 2, :wait_timeout => 0.3})) + ActiveRecord::Model.establish_connection(@connection.merge({:pool => 2, :wait_timeout => 0.3})) @connections = [] @timed_out = 0 @@ -34,15 +34,15 @@ class PooledConnectionsTest < ActiveRecord::TestCase # Will deadlock due to lack of Monitor timeouts in 1.9 def checkout_checkin_connections(pool_size, threads) - ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :wait_timeout => 0.5})) + ActiveRecord::Model.establish_connection(@connection.merge({:pool => pool_size, :wait_timeout => 0.5})) @connection_count = 0 @timed_out = 0 threads.times do Thread.new do begin - conn = ActiveRecord::Base.connection_pool.checkout + conn = ActiveRecord::Model.connection_pool.checkout sleep 0.1 - ActiveRecord::Base.connection_pool.checkin conn + ActiveRecord::Model.connection_pool.checkin conn @connection_count += 1 rescue ActiveRecord::ConnectionTimeoutError @timed_out += 1 @@ -55,13 +55,13 @@ class PooledConnectionsTest < ActiveRecord::TestCase checkout_checkin_connections 1, 2 assert_equal 2, @connection_count assert_equal 0, @timed_out - assert_equal 1, ActiveRecord::Base.connection_pool.connections.size + assert_equal 1, ActiveRecord::Model.connection_pool.connections.size end private def add_record(name) - ActiveRecord::Base.connection_pool.with_connection { Project.create! :name => name } + ActiveRecord::Model.connection_pool.with_connection { Project.create! :name => name } end end unless current_adapter?(:FrontBase) || in_memory_db? diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index 0669707baf..b4d1a631fa 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -188,14 +188,14 @@ class PrimaryKeyWithNoConnectionTest < ActiveRecord::TestCase def test_set_primary_key_with_no_connection return skip("disconnect wipes in-memory db") if in_memory_db? - connection = ActiveRecord::Base.remove_connection + connection = ActiveRecord::Model.remove_connection model = Class.new(ActiveRecord::Base) model.primary_key = 'foo' assert_equal 'foo', model.primary_key - ActiveRecord::Base.establish_connection(connection) + ActiveRecord::Model.establish_connection(connection) assert_equal 'foo', model.primary_key end diff --git a/activerecord/test/cases/unconnected_test.rb b/activerecord/test/cases/unconnected_test.rb index e82ca3f93d..5a69054445 100644 --- a/activerecord/test/cases/unconnected_test.rb +++ b/activerecord/test/cases/unconnected_test.rb @@ -7,13 +7,13 @@ class TestUnconnectedAdapter < ActiveRecord::TestCase self.use_transactional_fixtures = false def setup - @underlying = ActiveRecord::Base.connection - @specification = ActiveRecord::Base.remove_connection + @underlying = ActiveRecord::Model.connection + @specification = ActiveRecord::Model.remove_connection end def teardown @underlying = nil - ActiveRecord::Base.establish_connection(@specification) + ActiveRecord::Model.establish_connection(@specification) load_schema if in_memory_db? end diff --git a/activerecord/test/models/teapot.rb b/activerecord/test/models/teapot.rb index ff18b6a96d..b035b18c1b 100644 --- a/activerecord/test/models/teapot.rb +++ b/activerecord/test/models/teapot.rb @@ -12,6 +12,9 @@ class Teapot include ActiveRecord::Model end +class OtherTeapot < Teapot +end + class OMFGIMATEAPOT def aaahhh "mmm" diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 8706732230..b06175cd3f 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -598,6 +598,7 @@ ActiveRecord::Schema.define do create_table :teapots, :force => true do |t| t.string :name + t.string :type t.timestamps end diff --git a/activerecord/test/support/connection.rb b/activerecord/test/support/connection.rb index 56369da346..60fea46fd3 100644 --- a/activerecord/test/support/connection.rb +++ b/activerecord/test/support/connection.rb @@ -12,9 +12,9 @@ module ARTest def self.connect puts "Using #{connection_name} with Identity Map #{ActiveRecord::IdentityMap.enabled? ? 'on' : 'off'}" - ActiveRecord::Base.logger = ActiveSupport::Logger.new("debug.log") - ActiveRecord::Base.configurations = connection_config - ActiveRecord::Base.establish_connection 'arunit' + ActiveRecord::Model.logger = ActiveSupport::Logger.new("debug.log") + ActiveRecord::Model.configurations = connection_config + ActiveRecord::Model.establish_connection 'arunit' Course.establish_connection 'arunit2' end end diff --git a/activesupport/lib/active_support/base64.rb b/activesupport/lib/active_support/base64.rb index b43d2ce9a3..41a1a3469d 100644 --- a/activesupport/lib/active_support/base64.rb +++ b/activesupport/lib/active_support/base64.rb @@ -3,12 +3,16 @@ require 'base64' module ActiveSupport Base64 = ::Base64 + # *DEPRECATED*: Use +Base64.strict_encode64+ instead. + # # Encodes the value as base64 without the newline breaks. This makes the base64 encoding readily usable as URL parameters # or memcache keys without further processing. # # ActiveSupport::Base64.encode64s("Original unencoded string") # # => "T3JpZ2luYWwgdW5lbmNvZGVkIHN0cmluZw==" def Base64.encode64s(value) - encode64(value).gsub(/\n/, '') + ActiveSupport::Deprecation.warn "encode64s " \ + "is deprecated. Use Base64.strict_encode64 instead", caller + strict_encode64(value) end end diff --git a/activesupport/lib/active_support/callbacks.rb b/activesupport/lib/active_support/callbacks.rb index 2ebafa28dd..0495741c15 100644 --- a/activesupport/lib/active_support/callbacks.rb +++ b/activesupport/lib/active_support/callbacks.rb @@ -378,9 +378,6 @@ module ActiveSupport module ClassMethods # Generate the internal runner method called by +run_callbacks+. def __define_runner(symbol) #:nodoc: - name = __callback_runner_name(nil, symbol) - undef_method(name) if method_defined?(name) - runner_method = "_run_#{symbol}_callbacks" unless private_method_defined?(runner_method) class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 @@ -408,6 +405,11 @@ module ActiveSupport object.send(name, &blk) end + def __reset_runner(symbol) + name = __callback_runner_name(nil, symbol) + undef_method(name) if method_defined?(name) + end + def __callback_runner_name(key, kind) "_run__#{self.name.hash.abs}__#{kind}__#{key.hash.abs}__callbacks" end @@ -423,7 +425,7 @@ module ActiveSupport ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target| chain = target.send("_#{name}_callbacks") yield target, chain.dup, type, filters, options - target.__define_runner(name) + target.__reset_runner(name) end end @@ -537,12 +539,12 @@ module ActiveSupport chain = target.send("_#{symbol}_callbacks").dup callbacks.each { |c| chain.delete(c) } target.send("_#{symbol}_callbacks=", chain) - target.__define_runner(symbol) + target.__reset_runner(symbol) end self.send("_#{symbol}_callbacks=", callbacks.dup.clear) - __define_runner(symbol) + __reset_runner(symbol) end # Define sets of events in the object lifecycle that support callbacks. diff --git a/activesupport/lib/active_support/message_encryptor.rb b/activesupport/lib/active_support/message_encryptor.rb index 7d8a7fb687..476ba0b3d1 100644 --- a/activesupport/lib/active_support/message_encryptor.rb +++ b/activesupport/lib/active_support/message_encryptor.rb @@ -56,7 +56,7 @@ module ActiveSupport encrypted_data = cipher.update(@serializer.dump(value)) encrypted_data << cipher.final - [encrypted_data, iv].map {|v| ActiveSupport::Base64.encode64s(v)}.join("--") + [encrypted_data, iv].map {|v| ActiveSupport::Base64.strict_encode64(v)}.join("--") end def _decrypt(encrypted_message) diff --git a/activesupport/lib/active_support/message_verifier.rb b/activesupport/lib/active_support/message_verifier.rb index 30ac44f6fa..7cb5b1e82d 100644 --- a/activesupport/lib/active_support/message_verifier.rb +++ b/activesupport/lib/active_support/message_verifier.rb @@ -18,7 +18,7 @@ module ActiveSupport # self.current_user = User.find(id) # end # - # By default it uses Marshal to serialize the message. If you want to use another + # By default it uses Marshal to serialize the message. If you want to use another # serialization method, you can set the serializer attribute to something that responds # to dump and load, e.g.: # @@ -44,7 +44,7 @@ module ActiveSupport end def generate(value) - data = ActiveSupport::Base64.encode64s(@serializer.dump(value)) + data = ActiveSupport::Base64.strict_encode64(@serializer.dump(value)) "#{data}--#{generate_digest(data)}" end diff --git a/activesupport/test/core_ext/base64_ext_test.rb b/activesupport/test/core_ext/base64_ext_test.rb index bd0e9f843d..544c990b3c 100644 --- a/activesupport/test/core_ext/base64_ext_test.rb +++ b/activesupport/test/core_ext/base64_ext_test.rb @@ -2,7 +2,9 @@ require 'abstract_unit' class Base64Test < Test::Unit::TestCase def test_no_newline_in_encoded_value - assert_match(/\n/, ActiveSupport::Base64.encode64("oneverylongstringthatwouldnormallybesplitupbynewlinesbytheregularbase64")) - assert_no_match(/\n/, ActiveSupport::Base64.encode64s("oneverylongstringthatwouldnormallybesplitupbynewlinesbytheregularbase64")) + ActiveSupport::Deprecation.silence do + assert_match(/\n/, ActiveSupport::Base64.encode64("oneverylongstringthatwouldnormallybesplitupbynewlinesbytheregularbase64")) + assert_no_match(/\n/, ActiveSupport::Base64.encode64s("oneverylongstringthatwouldnormallybesplitupbynewlinesbytheregularbase64")) + end end end diff --git a/activesupport/test/message_encryptor_test.rb b/activesupport/test/message_encryptor_test.rb index db97c1b80b..11142a358f 100644 --- a/activesupport/test/message_encryptor_test.rb +++ b/activesupport/test/message_encryptor_test.rb @@ -78,7 +78,7 @@ class MessageEncryptorTest < ActiveSupport::TestCase def munge(base64_string) bits = ActiveSupport::Base64.decode64(base64_string) bits.reverse! - ActiveSupport::Base64.encode64s(bits) + ActiveSupport::Base64.strict_encode64(bits) end end diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index ca93f9ef9d..781d7bf47c 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -1,5 +1,6 @@ require 'open-uri' require 'rbconfig' +require 'active_support/core_ext/array/wrap' module Rails module Generators |