diff options
Diffstat (limited to 'activerecord')
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 |