aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/examples/performance.rb19
-rw-r--r--activerecord/lib/active_record/attribute_methods.rb4
-rw-r--r--activerecord/lib/active_record/attribute_methods/read.rb24
-rw-r--r--activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb5
-rw-r--r--activerecord/lib/active_record/base.rb32
-rw-r--r--activerecord/lib/active_record/relation.rb4
-rw-r--r--activerecord/lib/active_record/relation/batches.rb2
-rw-r--r--activerecord/lib/active_record/relation/calculations.rb9
-rw-r--r--activerecord/lib/active_record/relation/finder_methods.rb2
-rw-r--r--activerecord/lib/active_record/relation/predicate_builder.rb4
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb74
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb43
-rw-r--r--activerecord/lib/active_record/serialization.rb2
-rw-r--r--activerecord/lib/active_record/validations/uniqueness.rb5
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb27
-rw-r--r--activerecord/test/cases/base_test.rb6
-rw-r--r--activerecord/test/cases/json_serialization_test.rb7
-rw-r--r--activerecord/test/cases/method_scoping_test.rb2
-rw-r--r--activerecord/test/cases/relations_test.rb21
-rw-r--r--activerecord/test/models/subject.rb10
20 files changed, 178 insertions, 124 deletions
diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb
index a985cfcb66..ccd60c6c69 100644
--- a/activerecord/examples/performance.rb
+++ b/activerecord/examples/performance.rb
@@ -25,7 +25,7 @@ conn[:socket] = Pathname.glob(%w[
/tmp/mysql.sock
/var/mysql/mysql.sock
/var/run/mysqld/mysqld.sock
-]).find { |path| path.socket? }
+]).find { |path| path.socket? }.to_s
ActiveRecord::Base.establish_connection(conn)
@@ -155,6 +155,23 @@ RBench.run(TIMES) do
ar { Exhibit.transaction { Exhibit.new } }
end
+ report 'Model.find(id)' do
+ id = Exhibit.first.id
+ ar { Exhibit.find(id) }
+ end
+
+ report 'Model.find_by_sql' do
+ ar { Exhibit.find_by_sql("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}").first }
+ end
+
+ report 'Model.log', (TIMES * 10) do
+ ar { Exhibit.connection.send(:log, "hello", "world") {} }
+ end
+
+ report 'AR.execute(query)', (TIMES / 2) do
+ ar { ActiveRecord::Base.connection.execute("Select * from exhibits where id = #{(rand * 1000 + 1).to_i}") }
+ end
+
summary 'Total'
end
diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb
index 56e18eced0..7297af9f79 100644
--- a/activerecord/lib/active_record/attribute_methods.rb
+++ b/activerecord/lib/active_record/attribute_methods.rb
@@ -48,13 +48,13 @@ module ActiveRecord
end
def respond_to?(*args)
- self.class.define_attribute_methods
+ self.class.define_attribute_methods unless self.class.attribute_methods_generated?
super
end
protected
def attribute_method?(attr_name)
- attr_name == 'id' || attributes.include?(attr_name)
+ attr_name == 'id' || @attributes.include?(attr_name)
end
end
end
diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb
index 3da3d9d8cc..01699746d8 100644
--- a/activerecord/lib/active_record/attribute_methods/read.rb
+++ b/activerecord/lib/active_record/attribute_methods/read.rb
@@ -26,8 +26,7 @@ module ActiveRecord
# Returns the attributes which are cached. By default time related columns
# with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
def cached_attributes
- @cached_attributes ||=
- columns.select{|c| attribute_types_cached_by_default.include?(c.type)}.map{|col| col.name}.to_set
+ @cached_attributes ||= columns.select { |c| cacheable_column?(c) }.map { |col| col.name }.to_set
end
# Returns +true+ if the provided attribute is being cached.
@@ -37,7 +36,7 @@ module ActiveRecord
protected
def define_method_attribute(attr_name)
- if self.serialized_attributes[attr_name]
+ if serialized_attributes.include?(attr_name)
define_read_method_for_serialized_attribute(attr_name)
else
define_read_method(attr_name.to_sym, attr_name, columns_hash[attr_name])
@@ -49,9 +48,14 @@ module ActiveRecord
end
private
+ def cacheable_column?(column)
+ serialized_attributes.include?(column.name) || attribute_types_cached_by_default.include?(column.type)
+ end
+
# Define read method for serialized attribute.
def define_read_method_for_serialized_attribute(attr_name)
- generated_attribute_methods.module_eval("def #{attr_name}; unserialize_attribute('#{attr_name}'); end", __FILE__, __LINE__)
+ access_code = "@attributes_cache['#{attr_name}'] ||= unserialize_attribute('#{attr_name}')"
+ generated_attribute_methods.module_eval("def #{attr_name}; #{access_code}; end", __FILE__, __LINE__)
end
# Define an attribute reader method. Cope with nil column.
@@ -66,13 +70,19 @@ module ActiveRecord
if cache_attribute?(attr_name)
access_code = "@attributes_cache['#{attr_name}'] ||= (#{access_code})"
end
- generated_attribute_methods.module_eval("def #{symbol}; #{access_code}; end", __FILE__, __LINE__)
+ generated_attribute_methods.module_eval("def _#{symbol}; #{access_code}; end; alias #{symbol} _#{symbol}", __FILE__, __LINE__)
end
end
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
# "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)).
def read_attribute(attr_name)
+ send "_#{attr_name}"
+ rescue NoMethodError
+ _read_attribute attr_name
+ end
+
+ def _read_attribute(attr_name)
attr_name = attr_name.to_s
attr_name = self.class.primary_key if attr_name == 'id'
if !(value = @attributes[attr_name]).nil?
@@ -85,14 +95,12 @@ module ActiveRecord
else
value
end
- else
- nil
end
end
# Returns true if the attribute is of a text column and marked for serialization.
def unserializable_attribute?(attr_name, column)
- column.text? && self.class.serialized_attributes[attr_name]
+ column.text? && self.class.serialized_attributes.include?(attr_name)
end
# Returns the unserialized object of the attribute.
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 a258b3f431..d640b26b74 100644
--- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
+++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb
@@ -19,12 +19,13 @@ module ActiveRecord
def define_method_attribute(attr_name)
if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
method_body, line = <<-EOV, __LINE__ + 1
- def #{attr_name}(reload = false)
+ def _#{attr_name}(reload = false)
cached = @attributes_cache['#{attr_name}']
return cached if cached && !reload
- time = read_attribute('#{attr_name}')
+ time = _read_attribute('#{attr_name}')
@attributes_cache['#{attr_name}'] = time.acts_like?(:time) ? time.in_time_zone : time
end
+ alias #{attr_name} _#{attr_name}
EOV
generated_attribute_methods.module_eval(method_body, __FILE__, line)
else
diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb
index 47394de3c1..5da4eb169b 100644
--- a/activerecord/lib/active_record/base.rb
+++ b/activerecord/lib/active_record/base.rb
@@ -415,6 +415,11 @@ module ActiveRecord #:nodoc:
class_inheritable_accessor :default_scoping, :instance_writer => false
self.default_scoping = []
+ # 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
+ self.serialized_attributes = {}
+
class << self # Class methods
delegate :find, :first, :last, :all, :destroy, :destroy_all, :exists?, :delete, :delete_all, :update, :update_all, :to => :scoped
delegate :find_each, :find_in_batches, :to => :scoped
@@ -526,12 +531,6 @@ module ActiveRecord #:nodoc:
serialized_attributes[attr_name.to_s] = class_name
end
- # Returns a hash of all the attributes that have been specified for serialization as
- # keys and their class restriction as values.
- def serialized_attributes
- read_inheritable_attribute(:attr_serialized) or write_inheritable_attribute(:attr_serialized, {})
- end
-
# Guesses the table name (in forced lower-case) based on the name of the class in the
# inheritance hierarchy descending directly from ActiveRecord::Base. So if the hierarchy
# looks like: Reply < Message < ActiveRecord::Base, then Message is used
@@ -588,7 +587,7 @@ module ActiveRecord #:nodoc:
# Defines the column name for use with single table inheritance. Use
# <tt>set_inheritance_column</tt> to set a different value.
def inheritance_column
- @inheritance_column ||= "type".freeze
+ @inheritance_column ||= "type"
end
# Lazy-set the sequence name to the connection's default. This method
@@ -664,7 +663,7 @@ module ActiveRecord #:nodoc:
# Returns a hash of column objects for the table associated with this class.
def columns_hash
- @columns_hash ||= columns.inject({}) { |hash, column| hash[column.name] = column; hash }
+ @columns_hash ||= Hash[columns.map { |column| [column.name, column] }]
end
# Returns an array of column names as strings.
@@ -923,7 +922,7 @@ module ActiveRecord #:nodoc:
end
def construct_finder_arel(options = {}, scope = nil)
- relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options) : unscoped.merge(options)
+ relation = options.is_a?(Hash) ? unscoped.apply_finder_options(options) : options
relation = scope.merge(relation) if scope
relation
end
@@ -1080,9 +1079,9 @@ module ActiveRecord #:nodoc:
if method_scoping.is_a?(Hash)
# Dup first and second level of hash (method and params).
- method_scoping = method_scoping.inject({}) do |hash, (method, params)|
- hash[method] = (params == true) ? params : params.dup
- hash
+ method_scoping = method_scoping.dup
+ method_scoping.each do |method, params|
+ method_scoping[method] = params.dup unless params == true
end
method_scoping.assert_valid_keys([ :find, :create ])
@@ -1685,8 +1684,8 @@ MSG
if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name))
value = read_attribute(name)
- if value && ((self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))) || value.is_a?(Hash) || value.is_a?(Array))
- value = value.to_yaml
+ if value && self.class.serialized_attributes.key?(name)
+ value = YAML.dump value
end
attrs[self.class.arel_table[name]] = value
end
@@ -1793,10 +1792,7 @@ MSG
end
def quote_columns(quoter, hash)
- hash.inject({}) do |quoted, (name, value)|
- quoted[quoter.quote_column_name(name)] = value
- quoted
- end
+ Hash[hash.map { |name, value| [quoter.quote_column_name(name), value] }]
end
def quoted_comma_pair_list(quoter, hash)
diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb
index c678ef3bcc..478f1e8ef1 100644
--- a/activerecord/lib/active_record/relation.rb
+++ b/activerecord/lib/active_record/relation.rb
@@ -76,7 +76,9 @@ module ActiveRecord
@records
end
- def as_json(options = nil) to_a end #:nodoc:
+ def as_json(options = nil) #:nodoc:
+ to_a.as_json(options)
+ end
# Returns size of the records.
def size
diff --git a/activerecord/lib/active_record/relation/batches.rb b/activerecord/lib/active_record/relation/batches.rb
index d7494ebb5a..5584397439 100644
--- a/activerecord/lib/active_record/relation/batches.rb
+++ b/activerecord/lib/active_record/relation/batches.rb
@@ -50,7 +50,7 @@ module ActiveRecord
def find_in_batches(options = {})
relation = self
- if orders.present? || taken.present?
+ unless arel.orders.blank? && arel.taken.blank?
ActiveRecord::Base.logger.warn("Scoped order and limit are ignored, it's forced to be batch order and batch size")
end
diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb
index ea364a3b30..398ab75b69 100644
--- a/activerecord/lib/active_record/relation/calculations.rb
+++ b/activerecord/lib/active_record/relation/calculations.rb
@@ -175,18 +175,17 @@ module ActiveRecord
end
distinct = options[:distinct] || distinct
- column_name = :all if column_name.blank? && operation == "count"
if @group_values.any?
- return execute_grouped_calculation(operation, column_name)
+ execute_grouped_calculation(operation, column_name)
else
- return execute_simple_calculation(operation, column_name, distinct)
+ execute_simple_calculation(operation, column_name, distinct)
end
end
def execute_simple_calculation(operation, column_name, distinct) #:nodoc:
column = if @klass.column_names.include?(column_name.to_s)
- Arel::Attribute.new(@klass.unscoped, column_name)
+ Arel::Attribute.new(@klass.unscoped.table, column_name)
else
Arel::SqlLiteral.new(column_name == :all ? "*" : column_name.to_s)
end
@@ -211,7 +210,7 @@ module ActiveRecord
select_statement = if operation == 'count' && column_name == :all
"COUNT(*) AS count_all"
else
- Arel::Attribute.new(@klass.unscoped, column_name).send(operation).as(aggregate_alias).to_sql
+ Arel::Attribute.new(@klass.unscoped.table, column_name).send(operation).as(aggregate_alias).to_sql
end
select_statement << ", #{group_field} AS #{group_alias}"
diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb
index 4ffb552690..ede1c8821e 100644
--- a/activerecord/lib/active_record/relation/finder_methods.rb
+++ b/activerecord/lib/active_record/relation/finder_methods.rb
@@ -230,7 +230,7 @@ module ActiveRecord
end
def find_by_attributes(match, attributes, *args)
- conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h}
+ conditions = Hash[attributes.map {|a| [a, args[attributes.index(a)]]}]
result = where(conditions).send(match.finder)
if match.bang? && result.blank?
diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb
index 5cea2328e8..0d1307d87e 100644
--- a/activerecord/lib/active_record/relation/predicate_builder.rb
+++ b/activerecord/lib/active_record/relation/predicate_builder.rb
@@ -24,7 +24,9 @@ module ActiveRecord
case value
when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::Relation
- values = value.to_a
+ values = value.to_a.map { |x|
+ x.respond_to?(:quoted_id) ? x.quoted_id : x
+ }
attribute.in(values)
when Range, Arel::Relation
attribute.in(value)
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 6005105cc9..de57725af2 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -12,48 +12,50 @@ module ActiveRecord
def includes(*args)
args.reject! {|a| a.blank? }
+ return clone if args.empty?
+
relation = clone
- relation.includes_values = (relation.includes_values + args).flatten.uniq if args.present?
+ relation.includes_values = (relation.includes_values + args).flatten.uniq
relation
end
def eager_load(*args)
relation = clone
- relation.eager_load_values += args if args.present?
+ relation.eager_load_values += args unless args.blank?
relation
end
def preload(*args)
relation = clone
- relation.preload_values += args if args.present?
+ relation.preload_values += args unless args.blank?
relation
end
- def select(*args)
+ def select(value = Proc.new)
if block_given?
- to_a.select {|*block_args| yield(*block_args) }
+ to_a.select {|*block_args| value.call(*block_args) }
else
relation = clone
- relation.select_values += args if args.present?
+ relation.select_values += [value]
relation
end
end
def group(*args)
relation = clone
- relation.group_values += args.flatten if args.present?
+ relation.group_values += args.flatten unless args.blank?
relation
end
def order(*args)
relation = clone
- relation.order_values += args.flatten if args.present?
+ relation.order_values += args.flatten unless args.blank?
relation
end
def reorder(*args)
relation = clone
- if args.present?
+ unless args.blank?
relation.order_values = args
relation.reorder_flag = true
end
@@ -63,31 +65,21 @@ module ActiveRecord
def joins(*args)
relation = clone
- if args.present?
- args.flatten!
- relation.joins_values += args if args.present?
- end
+ args.flatten!
+ relation.joins_values += args unless args.blank?
relation
end
def where(opts, *rest)
relation = clone
-
- if opts.present? && value = build_where(opts, rest)
- relation.where_values += Array.wrap(value)
- end
-
+ relation.where_values += build_where(opts, rest) unless opts.blank?
relation
end
def having(*args)
relation = clone
-
- if args.present? && value = build_where(*args)
- relation.having_values += Array.wrap(value)
- end
-
+ relation.having_values += build_where(*args) unless args.blank?
relation
end
@@ -138,7 +130,7 @@ module ActiveRecord
modules << Module.new(&block) if block_given?
relation = clone
- relation.send(:apply_modules, *modules)
+ relation.send(:apply_modules, modules.flatten)
relation
end
@@ -196,14 +188,14 @@ module ActiveRecord
end
end
- arel = arel.having(*@having_values.uniq.select{|h| h.present?}) unless @having_values.empty?
+ arel = arel.having(*@having_values.uniq.reject{|h| h.blank?}) unless @having_values.empty?
arel = arel.take(@limit_value) if @limit_value
arel = arel.skip(@offset_value) if @offset_value
- arel = arel.group(*@group_values.uniq.select{|g| g.present?}) unless @group_values.empty?
+ arel = arel.group(*@group_values.uniq.reject{|g| g.blank?}) unless @group_values.empty?
- arel = arel.order(*@order_values.uniq.select{|o| o.present?}) unless @order_values.empty?
+ arel = arel.order(*@order_values.uniq.reject{|o| o.blank?}) unless @order_values.empty?
arel = build_select(arel, @select_values.uniq)
@@ -216,19 +208,18 @@ module ActiveRecord
def build_where(opts, other = [])
case opts
when String, Array
- @klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))
+ [@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
when Hash
attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
PredicateBuilder.new(table.engine).build_from_hash(attributes, table)
else
- opts
+ [opts]
end
end
private
def build_joins(relation, joins)
- joined_associations = []
association_joins = []
joins = @joins_values.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq
@@ -259,11 +250,8 @@ module ActiveRecord
end
end
- to_join.each do |tj|
- unless joined_associations.detect {|ja| ja[0] == tj[0] && ja[1] == tj[1] && ja[2] == tj[2] }
- joined_associations << tj
- relation = relation.join(tj[0], tj[1]).on(*tj[2])
- end
+ to_join.uniq.each do |left, join_class, right|
+ relation = relation.join(left, join_class).on(*right)
end
relation.join(custom_joins)
@@ -274,10 +262,13 @@ module ActiveRecord
@implicit_readonly = false
# TODO: fix this ugly hack, we should refactor the callers to get an ARel compatible array.
# Before this change we were passing to ARel the last element only, and ARel is capable of handling an array
- if selects.all? {|s| s.is_a?(String) || !s.is_a?(Arel::Expression) } && !(selects.last =~ /^COUNT\(/)
- arel.project(*selects)
+ case select = selects.last
+ when Arel::Expression, Arel::SqlLiteral
+ arel.project(select)
+ when /^COUNT\(/
+ arel.project(Arel::SqlLiteral.new(select))
else
- arel.project(selects.last)
+ arel.project(*selects)
end
else
arel.project(Arel::SqlLiteral.new(@klass.quoted_table_name + '.*'))
@@ -285,9 +276,10 @@ module ActiveRecord
end
def apply_modules(modules)
- values = Array.wrap(modules)
- @extensions += values if values.present?
- values.each {|extension| extend(extension) }
+ unless modules.empty?
+ @extensions += modules
+ modules.each {|extension| extend(extension) }
+ end
end
def reverse_sql_order(order_query)
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index a6cf59fbf1..9ecdb99bee 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -5,6 +5,7 @@ module ActiveRecord
def merge(r)
merged_relation = clone
return merged_relation unless r
+ return to_a & r if r.is_a?(Array)
Relation::ASSOCIATION_METHODS.each do |method|
value = r.send(:"#{method}_values")
@@ -64,12 +65,12 @@ module ActiveRecord
def except(*skips)
result = self.class.new(@klass, table)
- (Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).each do |method|
- result.send(:"#{method}_values=", send(:"#{method}_values")) unless skips.include?(method)
+ ((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) - skips).each do |method|
+ result.send(:"#{method}_values=", send(:"#{method}_values"))
end
- Relation::SINGLE_VALUE_METHODS.each do |method|
- result.send(:"#{method}_value=", send(:"#{method}_value")) unless skips.include?(method)
+ (Relation::SINGLE_VALUE_METHODS - skips).each do |method|
+ result.send(:"#{method}_value=", send(:"#{method}_value"))
end
result
@@ -78,14 +79,12 @@ module ActiveRecord
def only(*onlies)
result = self.class.new(@klass, table)
- onlies.each do |only|
- if (Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).include?(only)
- result.send(:"#{only}_values=", send(:"#{only}_values"))
- elsif Relation::SINGLE_VALUE_METHODS.include?(only)
- result.send(:"#{only}_value=", send(:"#{only}_value"))
- else
- raise "Invalid argument : #{only}"
- end
+ ((Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS) & onlies).each do |method|
+ result.send(:"#{method}_values=", send(:"#{method}_values"))
+ end
+
+ (Relation::SINGLE_VALUE_METHODS & onlies).each do |method|
+ result.send(:"#{method}_value=", send(:"#{method}_value"))
end
result
@@ -99,22 +98,16 @@ module ActiveRecord
return relation unless options
options.assert_valid_keys(VALID_FIND_OPTIONS)
+ finders = options.dup
+ finders.delete_if { |key, value| value.nil? }
- [:joins, :select, :group, :having, :limit, :offset, :from, :lock].each do |finder|
- if value = options[finder]
- relation = relation.send(finder, value)
- end
- end
-
- relation = relation.readonly(options[:readonly]) if options.key? :readonly
-
- [:group, :order].each do |finder|
- relation.send("#{finder}_values=", relation.send("#{finder}_values") + Array.wrap(options[finder])) if options.has_key?(finder)
+ ([:joins, :select, :group, :order, :having, :limit, :offset, :from, :lock, :readonly] & finders.keys).each do |finder|
+ relation = relation.send(finder, finders[finder])
end
- relation = relation.where(options[:conditions]) if options.has_key?(:conditions)
- relation = relation.includes(options[:include]) if options.has_key?(:include)
- relation = relation.extending(options[:extend]) if options.has_key?(:extend)
+ relation = relation.where(finders[:conditions]) if options.has_key?(:conditions)
+ relation = relation.includes(finders[:include]) if options.has_key?(:include)
+ relation = relation.extending(finders[:extend]) if options.has_key?(:extend)
relation
end
diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb
index 6ec406316a..ad3f7afd6f 100644
--- a/activerecord/lib/active_record/serialization.rb
+++ b/activerecord/lib/active_record/serialization.rb
@@ -5,7 +5,7 @@ module ActiveRecord #:nodoc:
include ActiveModel::Serializers::JSON
def serializable_hash(options = nil)
- options ||= {}
+ options = options.try(:clone) || {}
options[:except] = Array.wrap(options[:except]).map { |n| n.to_s }
options[:except] |= Array.wrap(self.class.inheritance_column)
diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb
index cc3d123cea..cb1d2ae421 100644
--- a/activerecord/lib/active_record/validations/uniqueness.rb
+++ b/activerecord/lib/active_record/validations/uniqueness.rb
@@ -17,6 +17,11 @@ module ActiveRecord
table = finder_class.unscoped
table_name = record.class.quoted_table_name
+
+ if value && record.class.serialized_attributes.key?(attribute.to_s)
+ value = YAML.dump value
+ end
+
sql, params = mount_sql_and_params(finder_class, table_name, attribute, value)
relation = table.where(sql, *params)
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 9b3cc47c79..1750bf004a 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -419,12 +419,8 @@ class AttributeMethodsTest < ActiveRecord::TestCase
Topic.instance_variable_set "@cached_attributes", nil
end
- def test_time_related_columns_are_actually_cached
- column_types = %w(datetime timestamp time date).map(&:to_sym)
- column_names = Topic.columns.select{|c| column_types.include?(c.type) }.map(&:name)
-
- assert_equal column_names.sort, Topic.cached_attributes.sort
- assert_equal time_related_columns_on_topic.sort, Topic.cached_attributes.sort
+ def test_cacheable_columns_are_actually_cached
+ assert_equal cached_columns.sort, Topic.cached_attributes.sort
end
def test_accessing_cached_attributes_caches_the_converted_values_and_nothing_else
@@ -435,8 +431,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert cache.empty?
all_columns = Topic.columns.map(&:name)
- cached_columns = time_related_columns_on_topic
- uncached_columns = all_columns - cached_columns
+ uncached_columns = all_columns - cached_columns
all_columns.each do |attr_name|
attribute_gets_cached = Topic.cache_attribute?(attr_name)
@@ -546,7 +541,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = @target.new(:title => "The pros and cons of programming naked.")
assert !topic.respond_to?(:title)
exception = assert_raise(NoMethodError) { topic.title }
- assert_equal "Attempt to call private method", exception.message
+ assert_match %r(^Attempt to call private method), exception.message
assert_equal "I'm private", topic.send(:title)
end
@@ -556,7 +551,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = @target.new
assert !topic.respond_to?(:title=)
exception = assert_raise(NoMethodError) { topic.title = "Pants"}
- assert_equal "Attempt to call private method", exception.message
+ assert_match %r(^Attempt to call private method), exception.message
topic.send(:title=, "Very large pants")
end
@@ -566,7 +561,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = @target.new(:title => "Isaac Newton's pants")
assert !topic.respond_to?(:title?)
exception = assert_raise(NoMethodError) { topic.title? }
- assert_equal "Attempt to call private method", exception.message
+ assert_match %r(^Attempt to call private method), exception.message
assert topic.send(:title?)
end
@@ -594,8 +589,16 @@ class AttributeMethodsTest < ActiveRecord::TestCase
private
+ def cached_columns
+ @cached_columns ||= (time_related_columns_on_topic + serialized_columns_on_topic).map(&:name)
+ end
+
def time_related_columns_on_topic
- Topic.columns.select{|c| [:time, :date, :datetime, :timestamp].include?(c.type)}.map(&:name)
+ Topic.columns.select { |c| [:time, :date, :datetime, :timestamp].include?(c.type) }
+ end
+
+ def serialized_columns_on_topic
+ Topic.columns.select { |c| Topic.serialized_attributes.include?(c.name) }
end
def in_time_zone(zone)
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 07b06e15a3..dcb1da7d91 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -909,9 +909,13 @@ class BasicsTest < ActiveRecord::TestCase
MyObject = Struct.new :attribute1, :attribute2
def test_serialized_attribute
+ Topic.serialize("content", MyObject)
+
myobj = MyObject.new('value1', 'value2')
topic = Topic.create("content" => myobj)
- Topic.serialize("content", MyObject)
+ assert_equal(myobj, topic.content)
+
+ topic.reload
assert_equal(myobj, topic.content)
end
diff --git a/activerecord/test/cases/json_serialization_test.rb b/activerecord/test/cases/json_serialization_test.rb
index a5736b227d..5da7f9e1b9 100644
--- a/activerecord/test/cases/json_serialization_test.rb
+++ b/activerecord/test/cases/json_serialization_test.rb
@@ -82,6 +82,13 @@ class JsonSerializationTest < ActiveRecord::TestCase
assert_match %r{"label":"Has cheezburger"}, methods_json
assert_match %r{"favorite_quote":"Constraints are liberating"}, methods_json
end
+
+ def test_serializable_hash_should_not_modify_options_in_argument
+ options = { :only => :name }
+ @contact.serializable_hash(options)
+
+ assert_nil options[:except]
+ end
end
class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb
index 312628a3e3..ffe16ffdfa 100644
--- a/activerecord/test/cases/method_scoping_test.rb
+++ b/activerecord/test/cases/method_scoping_test.rb
@@ -400,7 +400,7 @@ class NestedScopingTest < ActiveRecord::TestCase
Developer.send(:with_scope, :find => { :conditions => "salary < 100000" }) do
Developer.send(:with_scope, :find => { :offset => 1, :order => 'id asc' }) do
# Oracle adapter does not generated space after asc therefore trailing space removed from regex
- assert_sql(/ORDER BY id asc/) do
+ assert_sql(/ORDER BY\s+id asc/) do
assert_equal(poor_jamis, Developer.find(:first, :order => 'id asc'))
end
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index e21e1cb635..649fe79fd3 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -465,6 +465,10 @@ class RelationTest < ActiveRecord::TestCase
assert davids.loaded?
end
+ def test_select_argument_error
+ assert_raises(ArgumentError) { Developer.select }
+ end
+
def test_relation_merging
devs = Developer.where("salary >= 80000") & Developer.limit(2) & Developer.order('id ASC').where("id < 3")
assert_equal [developers(:david), developers(:jamis)], devs.to_a
@@ -643,6 +647,17 @@ class RelationTest < ActiveRecord::TestCase
assert_equal Post.all, all_posts.all
end
+ def test_only
+ relation = Post.where(:author_id => 1).order('id ASC').limit(1)
+ assert_equal [posts(:welcome)], relation.all
+
+ author_posts = relation.only(:where)
+ assert_equal Post.where(:author_id => 1).all, author_posts.all
+
+ all_posts = relation.only(:limit)
+ assert_equal Post.limit(1).all.first, all_posts.first
+ end
+
def test_anonymous_extension
relation = Post.where(:author_id => 1).order('id ASC').extending do
def author
@@ -684,5 +699,11 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 'zyke', car.name
end
+ def test_intersection_with_array
+ relation = Author.where(:name => "David")
+ rails_author = relation.first
+ assert_equal [rails_author], [rails_author] & relation
+ assert_equal [rails_author], relation & [rails_author]
+ end
end
diff --git a/activerecord/test/models/subject.rb b/activerecord/test/models/subject.rb
index 1b9d8107f8..d4b8b91de8 100644
--- a/activerecord/test/models/subject.rb
+++ b/activerecord/test/models/subject.rb
@@ -1,12 +1,16 @@
# used for OracleSynonymTest, see test/synonym_test_oracle.rb
#
class Subject < ActiveRecord::Base
+
+ # added initialization of author_email_address in the same way as in Topic class
+ # as otherwise synonym test was failing
+ after_initialize :set_email_address
+
protected
- # added initialization of author_email_address in the same way as in Topic class
- # as otherwise synonym test was failing
- def after_initialize
+ def set_email_address
if self.new_record?
self.author_email_address = 'test@test.com'
end
end
+
end