From 38aeb1528c376f7a058beea6db0a328720b85f01 Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 23 Dec 2009 14:55:12 -0800 Subject: Moving out some framework specific initializers into the framework libraries. --- activerecord/lib/active_record/rails.rb | 51 +++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 activerecord/lib/active_record/rails.rb (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/rails.rb b/activerecord/lib/active_record/rails.rb new file mode 100644 index 0000000000..4071385563 --- /dev/null +++ b/activerecord/lib/active_record/rails.rb @@ -0,0 +1,51 @@ +# For now, action_controller must always be present with +# rails, so let's make sure that it gets required before +# here. This is needed for correctly setting up the middleware. +# In the future, this might become an optional require. +require "action_controller/rails" + +module ActiveRecord + class Plugin < Rails::Plugin + plugin_name :active_record + + initializer "active_record.set_configs" do |app| + app.config.active_record.each do |k,v| + ActiveRecord::Base.send "#{k}=", v + end + end + + # This sets the database configuration from Configuration#database_configuration + # and then establishes the connection. + initializer "active_record.initialize_database" do |app| + ActiveRecord::Base.configurations = app.config.database_configuration + ActiveRecord::Base.establish_connection + end + + initializer "active_record.initialize_timezone" do + ActiveRecord::Base.time_zone_aware_attributes = true + ActiveRecord::Base.default_timezone = :utc + end + + # Setup database middleware after initializers have run + initializer "active_record.initialize_database_middleware" do |app| + middleware = app.config.middleware + if middleware.include?(ActiveRecord::SessionStore) + middleware.insert_before ActiveRecord::SessionStore, ActiveRecord::ConnectionAdapters::ConnectionManagement + middleware.insert_before ActiveRecord::SessionStore, ActiveRecord::QueryCache + else + middleware.use ActiveRecord::ConnectionAdapters::ConnectionManagement + middleware.use ActiveRecord::QueryCache + end + end + + initializer "active_record.load_observers" do + ActiveRecord::Base.instantiate_observers + end + + # TODO: ActiveRecord::Base.logger should delegate to its own config.logger + initializer "active_record.logger" do + ActiveRecord::Base.logger ||= Rails.logger + end + + end +end \ No newline at end of file -- cgit v1.2.3 From d2bd71a145ddc5e3e3750edc9a09eab742aaf02a Mon Sep 17 00:00:00 2001 From: Carlhuda Date: Wed, 23 Dec 2009 17:01:07 -0800 Subject: Finish moving config.frameworks-dependent code to the framework plugin --- activerecord/lib/active_record/notifications.rb | 5 ----- activerecord/lib/active_record/rails.rb | 8 ++++++++ 2 files changed, 8 insertions(+), 5 deletions(-) delete mode 100644 activerecord/lib/active_record/notifications.rb (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/notifications.rb b/activerecord/lib/active_record/notifications.rb deleted file mode 100644 index 562a5b91f4..0000000000 --- a/activerecord/lib/active_record/notifications.rb +++ /dev/null @@ -1,5 +0,0 @@ -require 'active_support/notifications' - -ActiveSupport::Notifications.subscribe("sql") do |name, before, after, result, instrumenter_id, payload| - ActiveRecord::Base.connection.log_info(payload[:sql], name, after - before) -end diff --git a/activerecord/lib/active_record/rails.rb b/activerecord/lib/active_record/rails.rb index 4071385563..ddbc555113 100644 --- a/activerecord/lib/active_record/rails.rb +++ b/activerecord/lib/active_record/rails.rb @@ -47,5 +47,13 @@ module ActiveRecord ActiveRecord::Base.logger ||= Rails.logger end + initializer "active_record.notifications" do + require 'active_support/notifications' + + ActiveSupport::Notifications.subscribe("sql") do |name, before, after, result, instrumenter_id, payload| + ActiveRecord::Base.connection.log_info(payload[:sql], name, after - before) + end + end + end end \ No newline at end of file -- cgit v1.2.3 From 2e79ec71a542c2d2e7bedbe12eda0b5e177fb0e0 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 01:31:11 +0530 Subject: Model.scoped now returns a relation if invoked without any arguments Example : posts = Post.scoped posts.size # Fires "select count(*) from posts" and returns the count posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects --- activerecord/lib/active_record/named_scope.rb | 30 +++++++++++++++++---------- 1 file changed, 19 insertions(+), 11 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index bbe2d1f205..321871104c 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -6,18 +6,26 @@ module ActiveRecord module NamedScope extend ActiveSupport::Concern - # All subclasses of ActiveRecord::Base have one named scope: - # * scoped - which allows for the creation of anonymous \scopes, on the fly: Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions) - # - # These anonymous \scopes tend to be useful when procedurally generating complex queries, where passing - # intermediate values (scopes) around as first-class objects is convenient. - # - # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope. - included do - named_scope :scoped, lambda { |scope| scope } - end - module ClassMethods + # Returns a relation if invoked without any arguments. + # + # posts = Post.scoped + # posts.size # Fires "select count(*) from posts" and returns the count + # posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects + # + # Returns an anonymous named scope if any options are supplied. + # + # shirts = Shirt.scoped(:conditions => {:color => 'red'}) + # shirts = shirts.scoped(:include => :washing_instructions) + # + # Anonymous \scopes tend to be useful when procedurally generating complex queries, where passing + # intermediate values (scopes) around as first-class objects is convenient. + # + # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope. + def scoped(options = {}, &block) + options.present? ? Scope.new(self, options, &block) : arel_table + end + def scopes read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {}) end -- cgit v1.2.3 From a7fd564ab197a376336ebfa8bceaf14553e7628f Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 02:53:10 +0530 Subject: Add Model.select/group/order/limit/joins/conditions/preload/eager_load class methods returning a lazy relation. Examples : posts = Post.select('id).order('name') # Returns a lazy relation posts.each {|p| puts p.id } # Fires "select id from posts order by name" --- activerecord/lib/active_record/base.rb | 10 ++--- activerecord/lib/active_record/named_scope.rb | 21 +++++++++- activerecord/lib/active_record/relation.rb | 56 +++++++++++++++------------ 3 files changed, 55 insertions(+), 32 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 321bba466e..03324b7f24 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -13,6 +13,7 @@ require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/string/behavior' require 'active_support/core_ext/object/metaclass' +require 'active_support/core_ext/module/delegation' module ActiveRecord #:nodoc: # Generic Active Record exception class. @@ -650,6 +651,8 @@ module ActiveRecord #:nodoc: end end + delegate :select, :group, :order, :limit, :joins, :conditions, :preload, :eager_load, :to => :arel_table + # A convenience wrapper for find(:first, *args). You can pass in all the # same arguments to this method as you can to find(:first). def first(*args) @@ -1514,13 +1517,8 @@ module ActiveRecord #:nodoc: "(#{segments.join(') AND (')})" unless segments.empty? end - def arel_table(table = nil) - table = table_name if table.blank? - if @arel_table.nil? || @arel_table.name != table - @arel_table = Relation.new(self, Arel::Table.new(table)) - end - @arel_table + Relation.new(self, Arel::Table.new(table || table_name)) end private diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 321871104c..38d54fa8ec 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -23,7 +23,26 @@ module ActiveRecord # # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope. def scoped(options = {}, &block) - options.present? ? Scope.new(self, options, &block) : arel_table + if options.present? + Scope.new(self, options, &block) + else + if !scoped?(:find) + relation = arel_table + else + relation = construct_finder_arel + include_associations = scope(:find, :include) + + if include_associations.present? + if references_eager_loaded_tables?(options) + relation.eager_load(include_associations) + else + relation.preload(include_associations) + end + end + end + + relation + end end def scopes diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 5f0eec754f..28f04e5d85 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -4,20 +4,20 @@ module ActiveRecord delegate :length, :collect, :find, :map, :each, :to => :to_a attr_reader :relation, :klass - def initialize(klass, relation) + def initialize(klass, relation, readonly = false, preload = [], eager_load = []) @klass, @relation = klass, relation - @readonly = false - @associations_to_preload = [] - @eager_load_associations = [] + @readonly = readonly + @associations_to_preload = preload + @eager_load_associations = eager_load end - def preload(association) - @associations_to_preload += association + def preload(associations) + @associations_to_preload << associations self end - def eager_load(association) - @eager_load_associations += association + def eager_load(associations) + @eager_load_associations += Array.wrap(associations) self end @@ -45,7 +45,7 @@ module ActiveRecord @klass.find_by_sql(@relation.to_sql) end - @klass.send(:preload_associations, records, @associations_to_preload) unless @associations_to_preload.empty? + @associations_to_preload.each {|associations| @klass.send(:preload_associations, records, associations) } records.each { |record| record.readonly! } if @readonly records @@ -57,27 +57,27 @@ module ActiveRecord end def select(selects) - selects.blank? ? self : Relation.new(@klass, @relation.project(selects)) + selects.blank? ? self : create_new_relation(@relation.project(selects)) end def group(groups) - groups.blank? ? self : Relation.new(@klass, @relation.group(groups)) + groups.blank? ? self : create_new_relation(@relation.group(groups)) end def order(orders) - orders.blank? ? self : Relation.new(@klass, @relation.order(orders)) + orders.blank? ? self : create_new_relation(@relation.order(orders)) end def limit(limits) - limits.blank? ? self : Relation.new(@klass, @relation.take(limits)) + limits.blank? ? self : create_new_relation(@relation.take(limits)) end def offset(offsets) - offsets.blank? ? self : Relation.new(@klass, @relation.skip(offsets)) + offsets.blank? ? self : create_new_relation(@relation.skip(offsets)) end def on(join) - join.blank? ? self : Relation.new(@klass, @relation.on(join)) + join.blank? ? self : create_new_relation(@relation.on(join)) end def joins(join, join_type = nil) @@ -96,7 +96,7 @@ module ActiveRecord else @relation.join(join, join_type) end - Relation.new(@klass, join) + create_new_relation(join) end end @@ -105,7 +105,7 @@ module ActiveRecord self else conditions = @klass.send(:merge_conditions, conditions) if [String, Hash, Array].include?(conditions.class) - Relation.new(@klass, @relation.where(conditions)) + create_new_relation(@relation.where(conditions)) end end @@ -114,14 +114,20 @@ module ActiveRecord end private - def method_missing(method, *args, &block) - if @relation.respond_to?(method) - @relation.send(method, *args, &block) - elsif Array.method_defined?(method) - to_a.send(method, *args, &block) - else - super - end + + def method_missing(method, *args, &block) + if @relation.respond_to?(method) + @relation.send(method, *args, &block) + elsif Array.method_defined?(method) + to_a.send(method, *args, &block) + else + super end + end + + def create_new_relation(relation) + Relation.new(@klass, relation, @readonly, @associations_to_preload, @eager_load_associations) + end + end end -- cgit v1.2.3 From 95274b28d95ad85ada25eb0c697ce5650ae488a9 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 03:50:57 +0530 Subject: Rename Model.conditions and relation.conditions to .where --- activerecord/lib/active_record/associations.rb | 6 +++--- .../associations/has_and_belongs_to_many_association.rb | 2 +- .../lib/active_record/associations/has_many_association.rb | 2 +- activerecord/lib/active_record/base.rb | 12 ++++++------ activerecord/lib/active_record/calculations.rb | 2 +- activerecord/lib/active_record/relation.rb | 2 +- 6 files changed, 13 insertions(+), 13 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 8dcb3a7711..0ed46046ff 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1717,9 +1717,9 @@ module ActiveRecord select(column_aliases(join_dependency)). group(construct_group(options[:group], options[:having], scope)). order(construct_order(options[:order], scope)). - conditions(construct_conditions(options[:conditions], scope)) + where(construct_conditions(options[:conditions], scope)) - relation = relation.conditions(construct_arel_limited_ids_condition(options, join_dependency)) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) + relation = relation.where(construct_arel_limited_ids_condition(options, join_dependency)) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) relation = relation.limit(construct_limit(options[:limit], scope)) if using_limitable_reflections?(join_dependency.reflections) relation @@ -1757,7 +1757,7 @@ module ActiveRecord end relation = relation.joins(construct_join(options[:joins], scope)). - conditions(construct_conditions(options[:conditions], scope)). + where(construct_conditions(options[:conditions], scope)). group(construct_group(options[:group], options[:having], scope)). order(construct_order(options[:order], scope)). limit(construct_limit(options[:limit], scope)). diff --git a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb index b01faa5212..9569b0c6f9 100644 --- a/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb +++ b/activerecord/lib/active_record/associations/has_and_belongs_to_many_association.rb @@ -71,7 +71,7 @@ module ActiveRecord records.each { |record| @owner.connection.delete(interpolate_sql(sql, record)) } else relation = arel_table(@reflection.options[:join_table]) - relation.conditions(relation[@reflection.primary_key_name].eq(@owner.id). + relation.where(relation[@reflection.primary_key_name].eq(@owner.id). and(Arel::Predicates::In.new(relation[@reflection.association_foreign_key], records.map(&:id))) ).delete end diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index cd31b0e211..be74ddfcf0 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -70,7 +70,7 @@ module ActiveRecord @reflection.klass.delete(records.map { |record| record.id }) else relation = arel_table(@reflection.table_name) - relation.conditions(relation[@reflection.primary_key_name].eq(@owner.id). + relation.where(relation[@reflection.primary_key_name].eq(@owner.id). and(Arel::Predicates::In.new(relation[@reflection.klass.primary_key], records.map(&:id))) ).update(relation[@reflection.primary_key_name] => nil) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 03324b7f24..3c41d16f63 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -651,7 +651,7 @@ module ActiveRecord #:nodoc: end end - delegate :select, :group, :order, :limit, :joins, :conditions, :preload, :eager_load, :to => :arel_table + delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :to => :arel_table # A convenience wrapper for find(:first, *args). You can pass in all the # same arguments to this method as you can to find(:first). @@ -885,7 +885,7 @@ module ActiveRecord #:nodoc: relation = arel_table if conditions = construct_conditions(conditions, scope) - relation = relation.conditions(Arel::SqlLiteral.new(conditions)) + relation = relation.where(Arel::SqlLiteral.new(conditions)) end relation = if options.has_key?(:limit) || (scope && scope[:limit]) @@ -948,7 +948,7 @@ module ActiveRecord #:nodoc: # associations or call your before_* or +after_destroy+ callbacks, use the +destroy_all+ method instead. def delete_all(conditions = nil) if conditions - arel_table.conditions(Arel::SqlLiteral.new(construct_conditions(conditions, scope(:find)))).delete + arel_table.where(Arel::SqlLiteral.new(construct_conditions(conditions, scope(:find)))).delete else arel_table.delete end @@ -1689,7 +1689,7 @@ module ActiveRecord #:nodoc: # TODO add lock to Arel relation = arel_table(options[:from]). joins(construct_join(options[:joins], scope)). - conditions(construct_conditions(options[:conditions], scope)). + where(construct_conditions(options[:conditions], scope)). select(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). group(construct_group(options[:group], options[:having], scope)). order(construct_order(options[:order], scope)). @@ -2564,7 +2564,7 @@ module ActiveRecord #:nodoc: # be made (since they can't be persisted). def destroy unless new_record? - self.class.arel_table.conditions(self.class.arel_table[self.class.primary_key].eq(id)).delete + self.class.arel_table.where(self.class.arel_table[self.class.primary_key].eq(id)).delete end @destroyed = true @@ -2851,7 +2851,7 @@ module ActiveRecord #:nodoc: def update(attribute_names = @attributes.keys) attributes_with_values = arel_attributes_values(false, false, attribute_names) return 0 if attributes_with_values.empty? - self.class.arel_table.conditions(self.class.arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) + self.class.arel_table.where(self.class.arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) end # Creates a record with values matching those of the instance attributes diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 40242333e5..fcba23dc0d 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -148,7 +148,7 @@ module ActiveRecord else relation = arel_table(options[:from]). joins(construct_join(options[:joins], scope)). - conditions(construct_conditions(options[:conditions], scope)). + where(construct_conditions(options[:conditions], scope)). order(options[:order]). limit(options[:limit]). offset(options[:offset]) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 28f04e5d85..6cc2befdf3 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -100,7 +100,7 @@ module ActiveRecord end end - def conditions(conditions) + def where(conditions) if conditions.blank? self else -- cgit v1.2.3 From a73fa9356feb82b739dd734b2b305f1f08f24337 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 04:07:50 +0530 Subject: Stop supporting blank arguments to AR#relation query methods --- activerecord/lib/active_record/relation.rb | 46 ++++++++++++------------------ 1 file changed, 19 insertions(+), 27 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 6cc2befdf3..5c0b8c6f83 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -57,56 +57,48 @@ module ActiveRecord end def select(selects) - selects.blank? ? self : create_new_relation(@relation.project(selects)) + create_new_relation(@relation.project(selects)) end def group(groups) - groups.blank? ? self : create_new_relation(@relation.group(groups)) + create_new_relation(@relation.group(groups)) end def order(orders) - orders.blank? ? self : create_new_relation(@relation.order(orders)) + create_new_relation(@relation.order(orders)) end def limit(limits) - limits.blank? ? self : create_new_relation(@relation.take(limits)) + create_new_relation(@relation.take(limits)) end def offset(offsets) - offsets.blank? ? self : create_new_relation(@relation.skip(offsets)) + create_new_relation(@relation.skip(offsets)) end def on(join) - join.blank? ? self : create_new_relation(@relation.on(join)) + create_new_relation(@relation.on(join)) end def joins(join, join_type = nil) - if join.blank? - self - else - join = case join - when String - @relation.join(join) - when Hash, Array, Symbol - if @klass.send(:array_of_strings?, join) - @relation.join(join.join(' ')) - else - @relation.join(@klass.send(:build_association_joins, join)) - end + join = case join + when String + @relation.join(join) + when Hash, Array, Symbol + if @klass.send(:array_of_strings?, join) + @relation.join(join.join(' ')) else - @relation.join(join, join_type) - end - create_new_relation(join) + @relation.join(@klass.send(:build_association_joins, join)) + end + else + @relation.join(join, join_type) end + create_new_relation(join) end def where(conditions) - if conditions.blank? - self - else - conditions = @klass.send(:merge_conditions, conditions) if [String, Hash, Array].include?(conditions.class) - create_new_relation(@relation.where(conditions)) - end + conditions = @klass.send(:merge_conditions, conditions) if [String, Hash, Array].include?(conditions.class) + create_new_relation(@relation.where(conditions)) end def respond_to?(method) -- cgit v1.2.3 From feb8b20eb552eee678448233672733c6e7ca9571 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 12:39:44 +0530 Subject: Add Relation#all as an alias for to_a --- activerecord/lib/active_record/relation.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 5c0b8c6f83..9aa45fb294 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -51,6 +51,8 @@ module ActiveRecord records end + alias all to_a + def first @relation = @relation.take(1) to_a.first -- cgit v1.2.3 From 187fbe5cbadf9d6fe5009a4a436fac28bcf8c99c Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 12:57:34 +0530 Subject: Add support for multiple arguments to .where finder --- activerecord/lib/active_record/relation.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 9aa45fb294..40ccdd4696 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -98,8 +98,13 @@ module ActiveRecord create_new_relation(join) end - def where(conditions) - conditions = @klass.send(:merge_conditions, conditions) if [String, Hash, Array].include?(conditions.class) + def where(*args) + if [String, Hash, Array].include?(args.first.class) + conditions = @klass.send(:merge_conditions, args.size > 1 ? Array.wrap(args) : args.first) + else + conditions = args.first + end + create_new_relation(@relation.where(conditions)) end -- cgit v1.2.3 From 284d186cf4e055cefb2f8482d016e3bd09e9c341 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 14:26:02 +0530 Subject: Make sure the relations are always immutable --- activerecord/lib/active_record/relation.rb | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 40ccdd4696..3dedf44190 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -12,18 +12,15 @@ module ActiveRecord end def preload(associations) - @associations_to_preload << associations - self + create_new_relation(@relation, @readonly, @associations_to_preload + Array.wrap(associations)) end def eager_load(associations) - @eager_load_associations += Array.wrap(associations) - self + create_new_relation(@relation, @readonly, @associations_to_preload, @eager_load_associations + Array.wrap(associations)) end def readonly - @readonly = true - self + create_new_relation(@relation, true) end def to_a @@ -124,8 +121,8 @@ module ActiveRecord end end - def create_new_relation(relation) - Relation.new(@klass, relation, @readonly, @associations_to_preload, @eager_load_associations) + def create_new_relation(relation, readonly = @readonly, preload = @associations_to_preload, eager_load = @eager_load_associations) + Relation.new(@klass, relation, readonly, preload, eager_load) end end -- cgit v1.2.3 From 9d3d60c64a9ce69b9932f5c543112199e576c2ad Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 14:40:45 +0530 Subject: Ensure preload and eager_load finder methods accept multiple arguments --- activerecord/lib/active_record/relation.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 3dedf44190..c02acba786 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -11,11 +11,11 @@ module ActiveRecord @eager_load_associations = eager_load end - def preload(associations) + def preload(*associations) create_new_relation(@relation, @readonly, @associations_to_preload + Array.wrap(associations)) end - def eager_load(associations) + def eager_load(*associations) create_new_relation(@relation, @readonly, @associations_to_preload, @eager_load_associations + Array.wrap(associations)) end -- cgit v1.2.3 From 3c5a7dcaf55f427f3a48e206feb06410d011ca4f Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 15:07:00 +0530 Subject: Cache the loaded relations --- activerecord/lib/active_record/relation.rb | 75 +++++++++++++++++------------- 1 file changed, 43 insertions(+), 32 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index c02acba786..a030ba29fa 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -9,6 +9,7 @@ module ActiveRecord @readonly = readonly @associations_to_preload = preload @eager_load_associations = eager_load + @loaded = false end def preload(*associations) @@ -23,38 +24,6 @@ module ActiveRecord create_new_relation(@relation, true) end - def to_a - records = if @eager_load_associations.any? - catch :invalid_query do - return @klass.send(:find_with_associations, { - :select => @relation.send(:select_clauses).join(', '), - :joins => @relation.joins(relation), - :group => @relation.send(:group_clauses).join(', '), - :order => @relation.send(:order_clauses).join(', '), - :conditions => @relation.send(:where_clauses).join("\n\tAND "), - :limit => @relation.taken, - :offset => @relation.skipped - }, - ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_associations, nil)) - end - [] - else - @klass.find_by_sql(@relation.to_sql) - end - - @associations_to_preload.each {|associations| @klass.send(:preload_associations, records, associations) } - records.each { |record| record.readonly! } if @readonly - - records - end - - alias all to_a - - def first - @relation = @relation.take(1) - to_a.first - end - def select(selects) create_new_relation(@relation.project(selects)) end @@ -109,6 +78,48 @@ module ActiveRecord @relation.respond_to?(method) || Array.method_defined?(method) || super end + def to_a + return @records if loaded? + + @records = if @eager_load_associations.any? + catch :invalid_query do + return @klass.send(:find_with_associations, { + :select => @relation.send(:select_clauses).join(', '), + :joins => @relation.joins(relation), + :group => @relation.send(:group_clauses).join(', '), + :order => @relation.send(:order_clauses).join(', '), + :conditions => @relation.send(:where_clauses).join("\n\tAND "), + :limit => @relation.taken, + :offset => @relation.skipped + }, + ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_associations, nil)) + end + [] + else + @klass.find_by_sql(@relation.to_sql) + end + + @associations_to_preload.each {|associations| @klass.send(:preload_associations, @records, associations) } + @records.each { |record| record.readonly! } if @readonly + + @loaded = true + @records + end + + alias all to_a + + def first + if loaded? + @records.first + else + @first ||= limit(1).to_a[0] + end + end + + def loaded? + @loaded + end + private def method_missing(method, *args, &block) -- cgit v1.2.3 From 9a9f97af2815469e6f28dee9b88577251ef1b832 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 15:28:23 +0530 Subject: Add relation.reload to force reloading the records --- activerecord/lib/active_record/relation.rb | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index a030ba29fa..853103a606 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -120,6 +120,12 @@ module ActiveRecord @loaded end + def reload + @loaded = false + @records = @first = nil + self + end + private def method_missing(method, *args, &block) -- cgit v1.2.3 From c6258ee313653bc54e94e0008f1c098ed68aa3f4 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 26 Dec 2009 19:15:05 +0530 Subject: Ensure all the finder methods respect scoping --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3c41d16f63..b6d73265a8 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -651,7 +651,7 @@ module ActiveRecord #:nodoc: end end - delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :to => :arel_table + delegate :select, :group, :order, :limit, :joins, :where, :preload, :eager_load, :to => :scoped # A convenience wrapper for find(:first, *args). You can pass in all the # same arguments to this method as you can to find(:first). -- cgit v1.2.3 From f3741506981d8d4aafb28066f5a3a0509a4da846 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 00:11:31 +0530 Subject: Ensure Model.scoped adds type conditions for STI models --- activerecord/lib/active_record/named_scope.rb | 1 + activerecord/lib/active_record/relation.rb | 25 ++++++++++++++----------- 2 files changed, 15 insertions(+), 11 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 38d54fa8ec..96d361093f 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -28,6 +28,7 @@ module ActiveRecord else if !scoped?(:find) relation = arel_table + relation = relation.where(type_condition) if finder_needs_type_condition? else relation = construct_finder_arel include_associations = scope(:find, :include) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 853103a606..b6800b07b7 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -49,19 +49,22 @@ module ActiveRecord end def joins(join, join_type = nil) - join = case join - when String - @relation.join(join) - when Hash, Array, Symbol - if @klass.send(:array_of_strings?, join) - @relation.join(join.join(' ')) - else - @relation.join(@klass.send(:build_association_joins, join)) - end + return self if join.blank? + + join_relation = case join + when String + @relation.join(join) + when Hash, Array, Symbol + if @klass.send(:array_of_strings?, join) + @relation.join(join.join(' ')) else - @relation.join(join, join_type) + @relation.join(@klass.send(:build_association_joins, join)) + end + else + @relation.join(join, join_type) end - create_new_relation(join) + + create_new_relation(join_relation) end def where(*args) -- cgit v1.2.3 From 83f24afd44ecd6fb6e38d5e8a6830dfca4bf5ffe Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 00:21:24 +0530 Subject: Add new finder methods to association collection. --- activerecord/lib/active_record/associations.rb | 4 ++-- .../associations/association_collection.rb | 19 +++++++++++++++++-- .../lib/active_record/autosave_association.rb | 4 ++-- 3 files changed, 21 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 0ed46046ff..2735bc5141 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1382,9 +1382,9 @@ module ActiveRecord if reflection.through_reflection && reflection.source_reflection.belongs_to? through = reflection.through_reflection primary_key = reflection.source_reflection.primary_key_name - send(through.name).all(:select => "DISTINCT #{through.quoted_table_name}.#{primary_key}").map!(&:"#{primary_key}") + send(through.name).select("DISTINCT #{through.quoted_table_name}.#{primary_key}").map!(&:"#{primary_key}") else - send(reflection.name).all(:select => "#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map!(&:id) + send(reflection.name).select("#{reflection.quoted_table_name}.#{reflection.klass.primary_key}").map!(&:id) end end end diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 25e329c0c1..b85d76e8d3 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -20,7 +20,22 @@ module ActiveRecord super construct_sql end - + + delegate :group, :order, :limit, :joins, :where, :preload, :eager_load, :to => :scoped + + def select(select = nil, &block) + if block_given? + load_target + @target.select(&block) + else + scoped.select(select) + end + end + + def scoped + with_scope(construct_scope) { @reflection.klass.scoped } + end + def find(*args) options = args.extract_options! @@ -383,7 +398,7 @@ module ActiveRecord loaded if target target end - + def method_missing(method, *args) if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method)) if block_given? diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 8f37fcd515..c0d8904bc8 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -221,9 +221,9 @@ module ActiveRecord if new_record association elsif association.loaded? - autosave ? association : association.select { |record| record.new_record? } + autosave ? association : association.find_all { |record| record.new_record? } else - autosave ? association.target : association.target.select { |record| record.new_record? } + autosave ? association.target : association.target.find_all { |record| record.new_record? } end end -- cgit v1.2.3 From f6f416c58e805604390314e2e8e5ecf6a0a78b4f Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 03:25:29 +0530 Subject: Add find_by_* and find_all_by_* finders to ActiveRecord::Relation --- activerecord/lib/active_record/relation.rb | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index b6800b07b7..f9ca6cbbd2 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -136,6 +136,20 @@ module ActiveRecord @relation.send(method, *args, &block) elsif Array.method_defined?(method) to_a.send(method, *args, &block) + elsif match = DynamicFinderMatch.match(method) + attributes = match.attribute_names + super unless @klass.send(:all_attributes_exists?, attributes) + + if match.finder? + conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h} + result = where(conditions).send(match.finder) + + if match.bang? && result.blank? + raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}" + else + result + end + end else super end -- cgit v1.2.3 From 8829d6ecc6b3e1a36a41decaf8237f6024be2c06 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 13:15:00 +0530 Subject: Make Model.find_by_* and Model.find_all_by_* use relations and remove dynamic method caching --- activerecord/lib/active_record/base.rb | 76 ++++++++------------------- activerecord/lib/active_record/named_scope.rb | 18 ++----- activerecord/lib/active_record/relation.rb | 22 ++++---- 3 files changed, 38 insertions(+), 78 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b6d73265a8..31737043eb 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -671,20 +671,10 @@ module ActiveRecord #:nodoc: options = args.extract_options! if options.empty? && !scoped?(:find) - relation = arel_table + arel_table else - relation = construct_finder_arel(options) - include_associations = merge_includes(scope(:find, :include), options[:include]) - - if include_associations.any? - if references_eager_loaded_tables?(options) - relation.eager_load(include_associations) - else - relation.preload(include_associations) - end - end + construct_finder_arel_with_includes(options) end - relation end # Executes a custom SQL query against your database and returns all the results. The results will @@ -1687,6 +1677,8 @@ module ActiveRecord #:nodoc: def construct_finder_arel(options = {}, scope = scope(:find)) # TODO add lock to Arel + validate_find_options(options) + relation = arel_table(options[:from]). joins(construct_join(options[:joins], scope)). where(construct_conditions(options[:conditions], scope)). @@ -1701,6 +1693,21 @@ module ActiveRecord #:nodoc: relation end + def construct_finder_arel_with_includes(options = {}) + relation = construct_finder_arel(options) + include_associations = merge_includes(scope(:find, :include), options[:include]) + + if include_associations.any? + if references_eager_loaded_tables?(options) + relation = relation.eager_load(include_associations) + else + relation = relation.preload(include_associations) + end + end + + relation + end + def construct_finder_sql(options, scope = scope(:find)) construct_finder_arel(options, scope).to_sql end @@ -1853,48 +1860,9 @@ module ActiveRecord #:nodoc: attribute_names = match.attribute_names super unless all_attributes_exists?(attribute_names) if match.finder? - finder = match.finder - bang = match.bang? - # def self.find_by_login_and_activated(*args) - # options = args.extract_options! - # attributes = construct_attributes_from_arguments( - # [:login,:activated], - # args - # ) - # finder_options = { :conditions => attributes } - # validate_find_options(options) - # set_readonly_option!(options) - # - # if options[:conditions] - # with_scope(:find => finder_options) do - # find(:first, options) - # end - # else - # find(:first, options.merge(finder_options)) - # end - # end - self.class_eval %{ - def self.#{method_id}(*args) - options = args.extract_options! - attributes = construct_attributes_from_arguments( - [:#{attribute_names.join(',:')}], - args - ) - finder_options = { :conditions => attributes } - validate_find_options(options) - set_readonly_option!(options) - - #{'result = ' if bang}if options[:conditions] - with_scope(:find => finder_options) do - find(:#{finder}, options) - end - else - find(:#{finder}, options.merge(finder_options)) - end - #{'result || raise(RecordNotFound, "Couldn\'t find #{name} with #{attributes.to_a.collect { |pair| pair.join(\' = \') }.join(\', \')}")' if bang} - end - }, __FILE__, __LINE__ - send(method_id, *arguments) + options = arguments.extract_options! + relation = options.any? ? construct_finder_arel_with_includes(options) : scoped + relation.send :find_by_attributes, match, attribute_names, *arguments elsif match.instantiator? instantiator = match.instantiator # def self.find_or_create_by_user_id(*args) diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 96d361093f..a6336e762a 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -26,23 +26,11 @@ module ActiveRecord if options.present? Scope.new(self, options, &block) else - if !scoped?(:find) - relation = arel_table - relation = relation.where(type_condition) if finder_needs_type_condition? + unless scoped?(:find) + finder_needs_type_condition? ? arel_table.where(type_condition) : arel_table else - relation = construct_finder_arel - include_associations = scope(:find, :include) - - if include_associations.present? - if references_eager_loaded_tables?(options) - relation.eager_load(include_associations) - else - relation.preload(include_associations) - end - end + construct_finder_arel_with_includes end - - relation end end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index f9ca6cbbd2..bcae352cc6 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -129,7 +129,7 @@ module ActiveRecord self end - private + protected def method_missing(method, *args, &block) if @relation.respond_to?(method) @@ -141,20 +141,24 @@ module ActiveRecord super unless @klass.send(:all_attributes_exists?, attributes) if match.finder? - conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h} - result = where(conditions).send(match.finder) - - if match.bang? && result.blank? - raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}" - else - result - end + find_by_attributes(match, attributes, *args) end else super end end + def find_by_attributes(match, attributes, *args) + conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h} + result = where(conditions).send(match.finder) + + if match.bang? && result.blank? + raise RecordNotFound, "Couldn't find #{@klass.name} with #{conditions.to_a.collect {|p| p.join(' = ')}.join(', ')}" + else + result + end + end + def create_new_relation(relation, readonly = @readonly, preload = @associations_to_preload, eager_load = @eager_load_associations) Relation.new(@klass, relation, readonly, preload, eager_load) end -- cgit v1.2.3 From d511de0261003aae4913e3c24f76df6e03a35916 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 14:28:19 +0530 Subject: Add find_or_create_by_* and find_or_initialize_by_* to relations --- activerecord/lib/active_record/relation.rb | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index bcae352cc6..f17b889c53 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -142,6 +142,8 @@ module ActiveRecord if match.finder? find_by_attributes(match, attributes, *args) + elsif match.instantiator? + find_or_instantiator_by_attributes(match, attributes, *args, &block) end else super @@ -159,6 +161,27 @@ module ActiveRecord end end + def find_or_instantiator_by_attributes(match, attributes, *args) + guard_protected_attributes = false + + attributes_for_create = conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h} + + if args[0].is_a?(Hash) + guard_protected_attributes = true + conditions = args[0].with_indifferent_access.slice(*attributes).symbolize_keys + end + + record = where(conditions).first + + unless record + record = @klass.new { |r| r.send(:attributes=, attributes_for_create, guard_protected_attributes) } + yield(record) if block_given? + record.save if match.instantiator == :create + end + + record + end + def create_new_relation(relation, readonly = @readonly, preload = @associations_to_preload, eager_load = @eager_load_associations) Relation.new(@klass, relation, readonly, preload, eager_load) end -- cgit v1.2.3 From 85770ec7139fcba985310d239d4c57cfe6f6c60b Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 14:46:38 +0530 Subject: Make Model.find_or_create_by_* and find_or_initialize_by_* use relations and remove method caching --- activerecord/lib/active_record/base.rb | 55 +----------------------------- activerecord/lib/active_record/relation.rb | 7 ++-- 2 files changed, 5 insertions(+), 57 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 31737043eb..cba1e0ebe6 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1864,60 +1864,7 @@ module ActiveRecord #:nodoc: relation = options.any? ? construct_finder_arel_with_includes(options) : scoped relation.send :find_by_attributes, match, attribute_names, *arguments elsif match.instantiator? - instantiator = match.instantiator - # def self.find_or_create_by_user_id(*args) - # guard_protected_attributes = false - # - # if args[0].is_a?(Hash) - # guard_protected_attributes = true - # attributes = args[0].with_indifferent_access - # find_attributes = attributes.slice(*[:user_id]) - # else - # find_attributes = attributes = construct_attributes_from_arguments([:user_id], args) - # end - # - # options = { :conditions => find_attributes } - # set_readonly_option!(options) - # - # record = find(:first, options) - # - # if record.nil? - # record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) } - # yield(record) if block_given? - # record.save - # record - # else - # record - # end - # end - self.class_eval %{ - def self.#{method_id}(*args) - guard_protected_attributes = false - - if args[0].is_a?(Hash) - guard_protected_attributes = true - attributes = args[0].with_indifferent_access - find_attributes = attributes.slice(*[:#{attribute_names.join(',:')}]) - else - find_attributes = attributes = construct_attributes_from_arguments([:#{attribute_names.join(',:')}], args) - end - - options = { :conditions => find_attributes } - set_readonly_option!(options) - - record = find(:first, options) - - if record.nil? - record = self.new { |r| r.send(:attributes=, attributes, guard_protected_attributes) } - #{'yield(record) if block_given?'} - #{'record.save' if instantiator == :create} - record - else - record - end - end - }, __FILE__, __LINE__ - send(method_id, *arguments, &block) + scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block end elsif match = DynamicScopeMatch.match(method_id) attribute_names = match.attribute_names diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index f17b889c53..89b9b9b4ff 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -164,11 +164,12 @@ module ActiveRecord def find_or_instantiator_by_attributes(match, attributes, *args) guard_protected_attributes = false - attributes_for_create = conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h} - if args[0].is_a?(Hash) guard_protected_attributes = true - conditions = args[0].with_indifferent_access.slice(*attributes).symbolize_keys + attributes_for_create = args[0].with_indifferent_access + conditions = attributes_for_create.slice(*attributes).symbolize_keys + else + attributes_for_create = conditions = attributes.inject({}) {|h, a| h[a] = args[attributes.index(a)]; h} end record = where(conditions).first -- cgit v1.2.3 From 1efc8edb5f8799c48ba1992e07a8566bb12b7224 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 14:50:33 +0530 Subject: Fix dynamic finder docs --- activerecord/lib/active_record/base.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index cba1e0ebe6..e619e41329 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1842,9 +1842,8 @@ module ActiveRecord #:nodoc: end # Enables dynamic finders like find_by_user_name(user_name) and find_by_user_name_and_password(user_name, password) - # that are turned into find(:first, :conditions => ["user_name = ?", user_name]) and - # find(:first, :conditions => ["user_name = ? AND password = ?", user_name, password]) respectively. Also works for - # find(:all) by using find_all_by_amount(50) that is turned into find(:all, :conditions => ["amount = ?", 50]). + # that are turned into where(:user_name => user_name).first and where(:user_name => user_name, :password => :password).first + # respectively. Also works for all by using find_all_by_amount(50) that is turned into where(:amount => 50).all. # # It's even possible to use all the additional parameters to +find+. For example, the full interface for +find_all_by_amount+ # is actually find_all_by_amount(amount, options). -- cgit v1.2.3 From 81608cf8fa61973d65f24efbc4ae4931016888e7 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 15:06:45 +0530 Subject: Make Model.all return an array rather than a relation for consistency. Use Model.scoped to get a relation --- activerecord/lib/active_record/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index e619e41329..2008dea5e9 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -671,9 +671,9 @@ module ActiveRecord #:nodoc: options = args.extract_options! if options.empty? && !scoped?(:find) - arel_table + arel_table.to_a else - construct_finder_arel_with_includes(options) + construct_finder_arel_with_includes(options).to_a end end -- cgit v1.2.3 From d92c4a84023bc0c8dd75869c9b4d5e50277f4650 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 27 Dec 2009 16:14:04 +0530 Subject: Add find(ids) to relations --- activerecord/lib/active_record/relation.rb | 63 +++++++++++++++++++++++++++++- 1 file changed, 62 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 89b9b9b4ff..c927270eee 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -91,7 +91,7 @@ module ActiveRecord :joins => @relation.joins(relation), :group => @relation.send(:group_clauses).join(', '), :order => @relation.send(:order_clauses).join(', '), - :conditions => @relation.send(:where_clauses).join("\n\tAND "), + :conditions => where_clause, :limit => @relation.taken, :offset => @relation.skipped }, @@ -111,6 +111,25 @@ module ActiveRecord alias all to_a + def find(*ids, &block) + return to_a.find(&block) if block_given? + + expects_array = ids.first.kind_of?(Array) + return ids.first if expects_array && ids.first.empty? + + ids = ids.flatten.compact.uniq + + case ids.size + when 0 + raise RecordNotFound, "Couldn't find #{@klass.name} without an ID" + when 1 + result = find_one(ids.first) + expects_array ? [ result ] : result + else + find_some(ids) + end + end + def first if loaded? @records.first @@ -183,9 +202,51 @@ module ActiveRecord record end + def find_one(id) + record = where(@klass.primary_key => id).first + + unless record + conditions = where_clause(', ') + conditions = " [WHERE #{conditions}]" if conditions.present? + raise RecordNotFound, "Couldn't find #{@klass.name} with ID=#{id}#{conditions}" + end + + record + end + + def find_some(ids) + result = where(@klass.primary_key => ids).all + + expected_size = + if @relation.taken && ids.size > @relation.taken + @relation.taken + else + ids.size + end + + # 11 ids with limit 3, offset 9 should give 2 results. + if @relation.skipped && (ids.size - @relation.skipped < expected_size) + expected_size = ids.size - @relation.skipped + end + + if result.size == expected_size + result + else + conditions = where_clause(', ') + conditions = " [WHERE #{conditions}]" if conditions.present? + + error = "Couldn't find all #{@klass.name.pluralize} with IDs " + error << "(#{ids.join(", ")})#{conditions} (found #{result.size} results, but was looking for #{expected_size})" + raise RecordNotFound, error + end + end + def create_new_relation(relation, readonly = @readonly, preload = @associations_to_preload, eager_load = @eager_load_associations) Relation.new(@klass, relation, readonly, preload, eager_load) end + def where_clause(join_string = "\n\tAND ") + @relation.send(:where_clauses).join(join_string) + end end end -- cgit v1.2.3