From ccf8311123a448fb82a568a57974d58b6b990837 Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Wed, 31 Mar 2010 15:52:20 -0400 Subject: Fix mapping of bigint/smallint/uuid columns in postgresql adapter. Signed-off-by: Emilio Tagua --- .../connection_adapters/abstract/schema_definitions.rb | 3 ++- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 046825d43f..6c477e48ce 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -23,7 +23,8 @@ module ActiveRecord # # +name+ is the column's name, such as supplier_id in supplier_id int(11). # +default+ is the type-casted default value, such as +new+ in sales_stage varchar(20) default 'new'. - # +sql_type+ is only used to extract the column's length, if necessary. For example +60+ in company_name varchar(60). + # +sql_type+ is used to extract the column's length, if necessary. For example +60+ in company_name varchar(60). + # It will be mapped to one of the standard Rails SQL types in the type attribute. # +null+ determines if this column allows +NULL+ values. def initialize(name, default, sql_type = nil, null = true) @name, @sql_type, @null = name, sql_type, null diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index a6042e1382..2395a744a3 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -114,6 +114,12 @@ module ActiveRecord # Object identifier types when /^oid$/ :integer + # UUID type + when /^uuid$/ + :string + # Small and big integer types + when /^(?:small|big)int$/ + :integer # Pass through all types that are not specific to PostgreSQL. else super -- cgit v1.2.3 From 98bf00d50d81c522f747131da7071d02815652f3 Mon Sep 17 00:00:00 2001 From: Ernie Miller Date: Wed, 31 Mar 2010 17:05:34 -0400 Subject: Add tests for postgresql column type mapping updates [#4270 state:resolved] Signed-off-by: Emilio Tagua --- activerecord/test/cases/column_definition_test.rb | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/column_definition_test.rb b/activerecord/test/cases/column_definition_test.rb index fc9a0ac96e..8b6c019d50 100644 --- a/activerecord/test/cases/column_definition_test.rb +++ b/activerecord/test/cases/column_definition_test.rb @@ -67,4 +67,21 @@ class ColumnDefinitionTest < ActiveRecord::TestCase assert !text_column.has_default? end end + + if current_adapter?(:PostgreSQLAdapter) + def test_bigint_column_should_map_to_integer + bigint_column = ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new('number', nil, "bigint") + assert_equal bigint_column.type, :integer + end + + def test_smallint_column_should_map_to_integer + smallint_column = ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new('number', nil, "smallint") + assert_equal smallint_column.type, :integer + end + + def test_uuid_column_should_map_to_string + uuid_column = ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new('unique_id', nil, "uuid") + assert_equal uuid_column.type, :string + end + end end -- cgit v1.2.3 From e8292abbcd581f2fdad368fc5760416071b4b67f Mon Sep 17 00:00:00 2001 From: Doug Richardson Date: Wed, 31 Mar 2010 23:53:26 -0700 Subject: Read postgresql encoding using string key instead of symbol [#4304 state:resolved] Signed-off-by: wycats --- activerecord/lib/active_record/railties/databases.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 06485b9033..a107befef3 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -84,7 +84,7 @@ namespace :db do end end when 'postgresql' - @encoding = config[:encoding] || ENV['CHARSET'] || 'utf8' + @encoding = config['encoding'] || ENV['CHARSET'] || 'utf8' schema_search_path = config['schema_search_path'] || 'public' first_in_schema_search_path = schema_search_path.split(',').first.strip begin -- cgit v1.2.3 From d868cb4f8a84b6275b6407b20bb3ba939a53681a Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 1 Apr 2010 13:36:45 -0700 Subject: Prep for beta2, depend on latest Bundler --- activerecord/lib/active_record/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index eaf5dc6545..cf78ec6e12 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -3,7 +3,7 @@ module ActiveRecord MAJOR = 3 MINOR = 0 TINY = 0 - BUILD = "beta1" + BUILD = "beta2" STRING = [MAJOR, MINOR, TINY, BUILD].join('.') end -- cgit v1.2.3 From d956003fa499d22689db8a10712fc7bcb9dbc5c8 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Thu, 1 Apr 2010 13:46:04 -0700 Subject: Neat and tidy, I say --- activerecord/activerecord.gemspec | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord') diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 8121530ab3..59caa53be0 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -6,6 +6,7 @@ Gem::Specification.new do |s| s.version = version s.summary = 'Object-relational mapper framework (part of Rails).' s.description = 'Databases on Rails. Build a persistent domain model by mapping database tables to Ruby classes. Strong conventions for associations, validations, aggregations, migrations, and testing come baked-in.' + s.required_ruby_version = '>= 1.8.7' s.author = 'David Heinemeier Hansson' -- cgit v1.2.3 From bc7da9b77d6347eeccefa2c735b2f236a08eea57 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 2 Apr 2010 15:28:09 +0100 Subject: Consistency when using Relation constants --- activerecord/lib/active_record/relation/spawn_methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index a17de1bdbb..63246079cb 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -6,7 +6,7 @@ module ActiveRecord merged_relation = clone return merged_relation unless r - (ActiveRecord::Relation::ASSOCIATION_METHODS + ActiveRecord::Relation::MULTI_VALUE_METHODS).reject {|m| [:joins, :where].include?(m)}.each do |method| + (Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).reject {|m| [:joins, :where].include?(m)}.each do |method| unless (value = r.send(:"#{method}_values")).blank? merged_relation.send(:"#{method}_values=", value) end @@ -26,7 +26,7 @@ module ActiveRecord merged_relation.where_values = merged_wheres - ActiveRecord::Relation::SINGLE_VALUE_METHODS.reject {|m| m == :lock}.each do |method| + Relation::SINGLE_VALUE_METHODS.reject {|m| m == :lock}.each do |method| unless (value = r.send(:"#{method}_value")).nil? merged_relation.send(:"#{method}_value=", value) end -- cgit v1.2.3 From b77dd218ce845f01753d02fcbc2605c9a5ee93e1 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 2 Apr 2010 17:34:48 +0100 Subject: Add Relation extensions --- activerecord/CHANGELOG | 10 +++++++++ activerecord/lib/active_record/named_scope.rb | 7 ++---- activerecord/lib/active_record/relation.rb | 8 +++++-- .../lib/active_record/relation/query_methods.rb | 25 ++++++++++++++++++---- .../lib/active_record/relation/spawn_methods.rb | 11 ++++++---- activerecord/test/cases/relations_test.rb | 16 ++++++++++++++ activerecord/test/models/post.rb | 6 ++++++ 7 files changed, 68 insertions(+), 15 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG b/activerecord/CHANGELOG index e379f4f967..e0625c3dbb 100644 --- a/activerecord/CHANGELOG +++ b/activerecord/CHANGELOG @@ -1,5 +1,15 @@ *Rails 3.0.0 [Beta 2] (pending)* +* Add Relation extensions. [Pratik Naik] + + users = User.where(:admin => true).extending(User::AdminPowers) + + latest_users = User.order('created_at DESC') do + def posts_count + Post.count(:user_id => to_a.map(&:id)) + end + end + * To prefix the table names of all models in a module, define self.table_name_prefix on the module. #4032 [Andrew White] * Silenced "SHOW FIELDS" and "SET SQL_AUTO_IS_NULL=0" statements from the MySQL driver to improve log signal to noise ration in development [DHH] diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 9abf979cd0..3456332227 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -133,19 +133,16 @@ module ActiveRecord delegate :scopes, :with_scope, :with_exclusive_scope, :scoped_methods, :scoped, :to => :klass def self.init(klass, options, &block) - relation = new(klass, klass.arel_table) + relation = new(klass, klass.arel_table, &block) scope = if options.is_a?(Hash) - klass.scoped.apply_finder_options(options.except(:extend)) + klass.scoped.apply_finder_options(options) else options ? klass.scoped.merge(options) : klass.scoped end relation = relation.merge(scope) - Array.wrap(options[:extend]).each {|extension| relation.send(:extend, extension) } if options.is_a?(Hash) - relation.send(:extend, Module.new(&block)) if block_given? - relation.current_scoped_methods_when_defined = klass.send(:current_scoped_methods) relation end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 8577ec58f7..dde20532af 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -4,7 +4,7 @@ module ActiveRecord class Relation JoinOperation = Struct.new(:relation, :join_class, :on) ASSOCIATION_METHODS = [:includes, :eager_load, :preload] - MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having] + MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having, :extends] SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :create_with, :from] include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches @@ -13,8 +13,9 @@ module ActiveRecord delegate :insert, :to => :arel attr_reader :table, :klass + attr_accessor :extensions - def initialize(klass, table) + def initialize(klass, table, &block) @klass, @table = klass, table @implicit_readonly = nil @@ -22,6 +23,9 @@ module ActiveRecord SINGLE_VALUE_METHODS.each {|v| instance_variable_set(:"@#{v}_value", nil)} (ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])} + @extensions = [] + + apply_modules(Module.new(&block)) if block_given? end def new(*args, &block) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index e224781016..b5e8b7570a 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -10,8 +10,9 @@ module ActiveRecord next if [:where, :having].include?(query_method) class_eval <<-CEVAL - def #{query_method}(*args) + def #{query_method}(*args, &block) new_relation = clone + new_relation.send(:apply_modules, Module.new(&block)) if block_given? value = Array.wrap(args.flatten).reject {|x| x.blank? } new_relation.#{query_method}_values += value if value.present? new_relation @@ -21,8 +22,9 @@ module ActiveRecord [:where, :having].each do |query_method| class_eval <<-CEVAL - def #{query_method}(*args) + def #{query_method}(*args, &block) new_relation = clone + new_relation.send(:apply_modules, Module.new(&block)) if block_given? value = build_where(*args) new_relation.#{query_method}_values += [*value] if value.present? new_relation @@ -34,8 +36,9 @@ module ActiveRecord attr_accessor :"#{query_method}_value" class_eval <<-CEVAL - def #{query_method}(value = true) + def #{query_method}(value = true, &block) new_relation = clone + new_relation.send(:apply_modules, Module.new(&block)) if block_given? new_relation.#{query_method}_value = value new_relation end @@ -43,8 +46,16 @@ module ActiveRecord end end - def lock(locks = true) + def extending(*modules) + new_relation = clone + new_relation.send :apply_modules, *modules + new_relation + end + + def lock(locks = true, &block) relation = clone + relation.send(:apply_modules, Module.new(&block)) if block_given? + case locks when String, TrueClass, NilClass clone.tap {|new_relation| new_relation.lock_value = locks || true } @@ -191,6 +202,12 @@ module ActiveRecord private + def apply_modules(modules) + values = Array.wrap(modules) + @extensions += values if values.present? + values.each {|extension| extend(extension) } + end + def reverse_sql_order(order_query) order_query.to_s.split(/,/).each { |s| if s.match(/\s(asc|ASC)$/) diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 63246079cb..8fdd64afcc 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -7,9 +7,8 @@ module ActiveRecord return merged_relation unless r (Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).reject {|m| [:joins, :where].include?(m)}.each do |method| - unless (value = r.send(:"#{method}_values")).blank? - merged_relation.send(:"#{method}_values=", value) - end + value = r.send(:"#{method}_values") + merged_relation.send(:"#{method}_values=", value) if value.present? end merged_relation = merged_relation.joins(r.joins_values) @@ -34,6 +33,9 @@ module ActiveRecord merged_relation.lock_value = r.lock_value unless merged_relation.lock_value + # Apply scope extension modules + merged_relation.send :apply_modules, r.extensions + merged_relation end @@ -69,7 +71,7 @@ module ActiveRecord result end - VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, + VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, :extend, :order, :select, :readonly, :group, :having, :from, :lock ] def apply_finder_options(options) @@ -84,6 +86,7 @@ module ActiveRecord 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 end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 1e345399f5..7b9e680c02 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -572,4 +572,20 @@ class RelationTest < ActiveRecord::TestCase assert_equal Post.all, all_posts.all end + def test_anonymous_extension + relation = Post.where(:author_id => 1).order('id ASC') do + def author + 'lifo' + end + end + + assert_equal "lifo", relation.author + assert_equal "lifo", relation.limit(1).author + end + + def test_named_extension + relation = Post.where(:author_id => 1).order('id ASC').extending(Post::NamedExtension) + assert_equal "lifo", relation.author + assert_equal "lifo", relation.limit(1).author + end end diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 704313649a..d092c4bf09 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -1,4 +1,10 @@ class Post < ActiveRecord::Base + module NamedExtension + def author + 'lifo' + end + end + scope :containing_the_letter_a, where("body LIKE '%a%'") scope :ranked_by_comments, order("comments_count DESC") scope :limit_by, lambda {|l| limit(l) } -- cgit v1.2.3 From 83ebe6224f546d2154ad4ed72bb3f2c9b07de678 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 2 Apr 2010 17:39:47 +0100 Subject: Oops :extends is not a MULTI_VALUE_METHOD --- activerecord/lib/active_record/relation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index dde20532af..3ca27f06ea 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -4,7 +4,7 @@ module ActiveRecord class Relation JoinOperation = Struct.new(:relation, :join_class, :on) ASSOCIATION_METHODS = [:includes, :eager_load, :preload] - MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having, :extends] + MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having] SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :create_with, :from] include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches -- cgit v1.2.3 From 0be31f85639cf2f536c558819ef3ee45ba7d83a3 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 2 Apr 2010 17:48:06 +0100 Subject: Scope#current_scoped_methods_when_defined is no longer needed --- activerecord/lib/active_record/named_scope.rb | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 3456332227..4203e36239 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -128,8 +128,6 @@ module ActiveRecord end class Scope < Relation - attr_accessor :current_scoped_methods_when_defined - delegate :scopes, :with_scope, :with_exclusive_scope, :scoped_methods, :scoped, :to => :klass def self.init(klass, options, &block) @@ -141,10 +139,7 @@ module ActiveRecord options ? klass.scoped.merge(options) : klass.scoped end - relation = relation.merge(scope) - - relation.current_scoped_methods_when_defined = klass.send(:current_scoped_methods) - relation + relation.merge(scope) end def first(*args) @@ -178,13 +173,7 @@ module ActiveRecord def method_missing(method, *args, &block) if klass.respond_to?(method) - with_scope(self) do - if current_scoped_methods_when_defined && !scoped_methods.include?(current_scoped_methods_when_defined) && !scopes.include?(method) - with_scope(current_scoped_methods_when_defined) { klass.send(method, *args, &block) } - else - klass.send(method, *args, &block) - end - end + with_scope(self) { klass.send(method, *args, &block) } else super end -- cgit v1.2.3 From ee07950c03bf8aab703191d73165eb206f9f55ef Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 2 Apr 2010 17:48:57 +0100 Subject: Scope#method_missing can safely rely on Relation#method_missing --- activerecord/lib/active_record/named_scope.rb | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 4203e36239..3efcff11ca 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -169,16 +169,6 @@ module ActiveRecord end end - private - - def method_missing(method, *args, &block) - if klass.respond_to?(method) - with_scope(self) { klass.send(method, *args, &block) } - else - super - end - end - end end -- cgit v1.2.3 From 62fe16932c9b7c3044017900114193e06814fd0c Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 2 Apr 2010 17:58:13 +0100 Subject: Make Relation#first and Relation#last behave like named scope's --- activerecord/lib/active_record/named_scope.rb | 16 ---------------- .../lib/active_record/relation/finder_methods.rb | 20 ++++++++++++++++++-- 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 3efcff11ca..be26b1f47f 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -142,22 +142,6 @@ module ActiveRecord relation.merge(scope) end - def first(*args) - if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash)) - to_a.first(*args) - else - args.first.present? ? apply_finder_options(args.first).first : super - end - end - - def last(*args) - if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash)) - to_a.last(*args) - else - args.first.present? ? apply_finder_options(args.first).last : super - end - end - def ==(other) case other when Scope diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 37aaac0894..a26f1c0ac8 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -106,13 +106,29 @@ module ActiveRecord # 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) - args.any? ? apply_finder_options(args.first).first : find_first + if args.any? + if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash)) + to_a.first(*args) + else + apply_finder_options(args.first).first + end + else + find_first + end end # A convenience wrapper for find(:last, *args). You can pass in all the # same arguments to this method as you can to find(:last). def last(*args) - args.any? ? apply_finder_options(args.first).last : find_last + if args.any? + if args.first.kind_of?(Integer) || (loaded? && !args.first.kind_of?(Hash)) + to_a.last(*args) + else + apply_finder_options(args.first).last + end + else + find_last + end end # A convenience wrapper for find(:all, *args). You can pass in all the -- cgit v1.2.3 From cfa283201e079b4f700eb915490bcfa18451b11e Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 2 Apr 2010 18:57:01 +0100 Subject: Goodbye ActiveRecord::NamedScope::Scope --- activerecord/lib/active_record/named_scope.rb | 52 +++++++--------------- activerecord/lib/active_record/relation.rb | 11 +++++ .../active_record/relation/predicate_builder.rb | 2 +- 3 files changed, 29 insertions(+), 36 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index be26b1f47f..d56f7afb74 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -25,7 +25,8 @@ module ActiveRecord # You can define a scope that applies to all finders using ActiveRecord::Base.default_scope. def scoped(options = {}, &block) if options.present? - Scope.init(self, options, &block) + relation = scoped.apply_finder_options(options) + block_given? ? relation.extending(Module.new(&block)) : relation else current_scoped_methods ? unscoped.merge(current_scoped_methods) : unscoped.clone end @@ -107,13 +108,22 @@ module ActiveRecord end scopes[name] = lambda do |parent_scope, *args| - Scope.init(parent_scope, case options - when Hash, Relation - options - when Proc - options.call(*args) - end, &block) + scope_options = case options + when Hash, Relation + options + when Proc + options.call(*args) + end + + relation = if scope_options.is_a?(Hash) + parent_scope.scoped.apply_finder_options(scope_options) + else + scope_options ? parent_scope.scoped.merge(scope_options) : parent_scope.scoped + end + + block_given? ? relation.extending(Module.new(&block)) : relation end + singleton_class.instance_eval do define_method name do |*args| scopes[name].call(self, *args) @@ -127,33 +137,5 @@ module ActiveRecord end end - class Scope < Relation - delegate :scopes, :with_scope, :with_exclusive_scope, :scoped_methods, :scoped, :to => :klass - - def self.init(klass, options, &block) - relation = new(klass, klass.arel_table, &block) - - scope = if options.is_a?(Hash) - klass.scoped.apply_finder_options(options) - else - options ? klass.scoped.merge(options) : klass.scoped - end - - relation.merge(scope) - end - - def ==(other) - case other - when Scope - to_sql == other.to_sql - when Relation - other == self - when Array - to_a == other.to_a - end - end - - end - end end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 3ca27f06ea..69d04d7375 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -311,11 +311,22 @@ module ActiveRecord @should_eager_load ||= (@eager_load_values.any? || (@includes_values.any? && references_eager_loaded_tables?)) end + def ==(other) + case other + when Relation + other.to_sql == to_sql + when Array + to_a == other.to_a + end + end + protected def method_missing(method, *args, &block) if Array.method_defined?(method) to_a.send(method, *args, &block) + elsif @klass.scopes[method] + merge(@klass.send(method, *args, &block)) elsif @klass.respond_to?(method) @klass.send(:with_scope, self) { @klass.send(method, *args, &block) } elsif arel.respond_to?(method) diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 711df16bf1..717756418c 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -23,7 +23,7 @@ module ActiveRecord attribute = table[column] case value - when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope + when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::Relation values = value.to_a attribute.in(values) when Range -- cgit v1.2.3 From 13eb2c87e6b8296ca2ce2da36eb746e2b94a563b Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Fri, 2 Apr 2010 18:57:20 +0100 Subject: Make Relation#inspect less noisy --- activerecord/lib/active_record/relation.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 69d04d7375..4e62187449 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -320,6 +320,10 @@ module ActiveRecord end end + def inspect + to_a.inspect + end + protected def method_missing(method, *args, &block) -- cgit v1.2.3 From 684e4d39d60d87273ea74f9a076b3ea308fc2ffe Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 3 Apr 2010 01:22:42 +0100 Subject: Remove unnecessary argument for creating scopes --- activerecord/lib/active_record/named_scope.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index d56f7afb74..86a18f8b6b 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -107,7 +107,7 @@ module ActiveRecord "Overwriting existing method #{self.name}.#{name}." end - scopes[name] = lambda do |parent_scope, *args| + scopes[name] = lambda do |*args| scope_options = case options when Hash, Relation options @@ -116,9 +116,9 @@ module ActiveRecord end relation = if scope_options.is_a?(Hash) - parent_scope.scoped.apply_finder_options(scope_options) + scoped.apply_finder_options(scope_options) else - scope_options ? parent_scope.scoped.merge(scope_options) : parent_scope.scoped + scope_options ? scoped.merge(scope_options) : scoped end block_given? ? relation.extending(Module.new(&block)) : relation @@ -126,7 +126,7 @@ module ActiveRecord singleton_class.instance_eval do define_method name do |*args| - scopes[name].call(self, *args) + scopes[name].call(*args) end end end -- cgit v1.2.3 From 41a2ba652a6b4b4eb3d12b9a2d55cb5b1eb5581a Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 3 Apr 2010 01:35:10 +0100 Subject: Improve named scope lambda --- activerecord/lib/active_record/named_scope.rb | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 86a18f8b6b..50c57783b2 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -99,7 +99,7 @@ module ActiveRecord # # expected_options = { :conditions => { :colored => 'red' } } # assert_equal expected_options, Shirt.colored('red').proxy_options - def scope(name, options = {}, &block) + def scope(name, scope_options = {}, &block) name = name.to_sym if !scopes[name] && respond_to?(name, true) @@ -108,19 +108,10 @@ module ActiveRecord end scopes[name] = lambda do |*args| - scope_options = case options - when Hash, Relation - options - when Proc - options.call(*args) - end - - relation = if scope_options.is_a?(Hash) - scoped.apply_finder_options(scope_options) - else - scope_options ? scoped.merge(scope_options) : scoped - end + options = scope_options.is_a?(Proc) ? scope_options.call(*args) : scope_options + relation = scoped + relation = options.is_a?(Hash) ? relation.apply_finder_options(options) : scoped.merge(options) if options block_given? ? relation.extending(Module.new(&block)) : relation end -- cgit v1.2.3 From c6372d604952a8eef16ce73a06814aa143b94779 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 3 Apr 2010 01:49:01 +0100 Subject: Improve scope docs --- activerecord/lib/active_record/named_scope.rb | 44 +++++++++------------------ 1 file changed, 14 insertions(+), 30 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 50c57783b2..632322b517 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -8,16 +8,15 @@ module ActiveRecord extend ActiveSupport::Concern module ClassMethods - # Returns a relation if invoked without any arguments. + # Returns an anonymous scope. # # 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) + # fruits = Fruit.scoped + # fruits = fruits.where(:colour => 'red') if options[:red_only] + # fruits = fruits.limit(10) if limited? # # Anonymous \scopes tend to be useful when procedurally generating complex queries, where passing # intermediate values (scopes) around as first-class objects is convenient. @@ -37,21 +36,21 @@ module ActiveRecord end # Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query, - # such as :conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions. + # such as where(:color => :red).select('shirts.*').includes(:washing_instructions). # # class Shirt < ActiveRecord::Base - # scope :red, :conditions => {:color => 'red'} - # scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true] + # scope :red, where(:color => 'red') + # scope :dry_clean_only, joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true) # end # # The above calls to scope define class methods Shirt.red and Shirt.dry_clean_only. Shirt.red, - # in effect, represents the query Shirt.find(:all, :conditions => {:color => 'red'}). + # in effect, represents the query Shirt.where(:color => 'red'). # # Unlike Shirt.find(...), however, the object returned by Shirt.red is not an Array; it resembles the association object - # constructed by a has_many declaration. For instance, you can invoke Shirt.red.find(:first), Shirt.red.count, - # Shirt.red.find(:all, :conditions => {:size => 'small'}). Also, just - # as with the association objects, named \scopes act like an Array, implementing Enumerable; Shirt.red.each(&block), - # Shirt.red.first, and Shirt.red.inject(memo, &block) all behave as if Shirt.red really was an Array. + # constructed by a has_many declaration. For instance, you can invoke Shirt.red.first, Shirt.red.count, + # Shirt.red.where(:size => 'small'). Also, just as with the association objects, named \scopes act like an Array, + # implementing Enumerable; Shirt.red.each(&block), Shirt.red.first, and Shirt.red.inject(memo, &block) + # all behave as if Shirt.red really was an Array. # # These named \scopes are composable. For instance, Shirt.red.dry_clean_only will produce all shirts that are both red and dry clean only. # Nested finds and calculations also work with these compositions: Shirt.red.dry_clean_only.count returns the number of garments @@ -70,9 +69,7 @@ module ActiveRecord # Named \scopes can also be procedural: # # class Shirt < ActiveRecord::Base - # scope :colored, lambda { |color| - # { :conditions => { :color => color } } - # } + # scope :colored, lambda {|color| where(:color => color) } # end # # In this example, Shirt.colored('puce') finds all puce shirts. @@ -80,25 +77,12 @@ module ActiveRecord # Named \scopes can also have extensions, just as with has_many declarations: # # class Shirt < ActiveRecord::Base - # scope :red, :conditions => {:color => 'red'} do + # scope :red, where(:color => 'red') do # def dom_id # 'red_shirts' # end # end # end - # - # - # For testing complex named \scopes, you can examine the scoping options using the - # proxy_options method on the proxy itself. - # - # class Shirt < ActiveRecord::Base - # scope :colored, lambda { |color| - # { :conditions => { :color => color } } - # } - # end - # - # expected_options = { :conditions => { :colored => 'red' } } - # assert_equal expected_options, Shirt.colored('red').proxy_options def scope(name, scope_options = {}, &block) name = name.to_sym -- cgit v1.2.3 From 386b7bfd9d78a6d8c8bc7cc4a310df806ad0ba57 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Fri, 2 Apr 2010 20:12:50 -0700 Subject: Remove an unused argument --- activerecord/lib/active_record/associations.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 7406daf837..40022a614a 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1493,14 +1493,13 @@ module ActiveRecord # The +extra_conditions+ parameter, which is not used within the main # Active Record codebase, is meant to allow plugins to define extra # finder conditions. - def configure_dependency_for_has_many(reflection, extra_conditions = nil) + def configure_dependency_for_has_many(reflection) if reflection.options.include?(:dependent) # Add polymorphic type if the :as option is present dependent_conditions = [] dependent_conditions << "#{reflection.primary_key_name} = \#{record.#{reflection.name}.send(:owner_quoted_id)}" dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as] dependent_conditions << sanitize_sql(reflection.options[:conditions], reflection.table_name) if reflection.options[:conditions] - dependent_conditions << extra_conditions if extra_conditions dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ") dependent_conditions = dependent_conditions.gsub('@', '\@') case reflection.options[:dependent] -- cgit v1.2.3 From 13004d4f849682772060371273fda3312dd3b884 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Fri, 2 Apr 2010 22:33:57 -0700 Subject: Make the query built by has_many ...., :dependent => :____ lazy since all the information is not really available yet. --- activerecord/lib/active_record/associations.rb | 72 ++++++++------------------ activerecord/lib/active_record/reflection.rb | 10 ++++ 2 files changed, 32 insertions(+), 50 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 40022a614a..7d628005dd 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1495,13 +1495,6 @@ module ActiveRecord # finder conditions. def configure_dependency_for_has_many(reflection) if reflection.options.include?(:dependent) - # Add polymorphic type if the :as option is present - dependent_conditions = [] - dependent_conditions << "#{reflection.primary_key_name} = \#{record.#{reflection.name}.send(:owner_quoted_id)}" - dependent_conditions << "#{reflection.options[:as]}_type = '#{base_class.name}'" if reflection.options[:as] - dependent_conditions << sanitize_sql(reflection.options[:conditions], reflection.table_name) if reflection.options[:conditions] - dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ") - dependent_conditions = dependent_conditions.gsub('@', '\@') case reflection.options[:dependent] when :destroy method_name = "has_many_dependent_destroy_for_#{reflection.name}".to_sym @@ -1510,51 +1503,30 @@ module ActiveRecord end before_destroy method_name when :delete_all - # before_destroy do |record| - # self.class.send(:delete_all_has_many_dependencies, - # record, - # "posts", - # Post, - # %@...@) # this is a string literal like %(...) - # end - # end - module_eval <<-CALLBACK - before_destroy do |record| - self.class.send(:delete_all_has_many_dependencies, - record, - "#{reflection.name}", - #{reflection.class_name}, - %@#{dependent_conditions}@) - end - CALLBACK + before_destroy do |record| + self.class.send(:delete_all_has_many_dependencies, + record, + reflection.name, + reflection.klass, + reflection.dependent_conditions(record, self.class)) + end when :nullify - # before_destroy do |record| - # self.class.send(:nullify_has_many_dependencies, - # record, - # "posts", - # Post, - # "user_id", - # %@...@) # this is a string literal like %(...) - # end - # end - module_eval <<-CALLBACK - before_destroy do |record| - self.class.send(:nullify_has_many_dependencies, - record, - "#{reflection.name}", - #{reflection.class_name}, - "#{reflection.primary_key_name}", - %@#{dependent_conditions}@) - end - CALLBACK - when :restrict - method_name = "has_many_dependent_restrict_for_#{reflection.name}".to_sym - define_method(method_name) do - unless send(reflection.name).empty? - raise DeleteRestrictionError.new(reflection) - end + before_destroy do |record| + self.class.send(:nullify_has_many_dependencies, + record, + reflection.name, + reflection.klass, + reflection.primary_key_name, + reflection.dependent_conditions(record, self.class)) + end + when :restrict + method_name = "has_many_dependent_restrict_for_#{reflection.name}".to_sym + define_method(method_name) do + unless send(reflection.name).empty? + raise DeleteRestrictionError.new(reflection) end - before_destroy method_name + end + before_destroy method_name else raise ArgumentError, "The :dependent option expects either :destroy, :delete_all, :nullify or :restrict (#{reflection.options[:dependent].inspect})" end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 5e8fc104cb..2a3f3f8713 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -277,6 +277,16 @@ module ActiveRecord !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many) end + def dependent_conditions(record, base_class) + dependent_conditions = [] + dependent_conditions << "#{primary_key_name} = #{record.send(name).send(:owner_quoted_id)}" + dependent_conditions << "#{options[:as]}_type = '#{base_class.name}'" if options[:as] + dependent_conditions << klass.send(:sanitize_sql, options[:conditions]) if options[:conditions] + dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ") + dependent_conditions = dependent_conditions.gsub('@', '\@') + dependent_conditions + end + private def derive_class_name class_name = name.to_s.camelize -- cgit v1.2.3 From 52ffaa182ea323af120dc5687d7547004167d0da Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Sat, 3 Apr 2010 09:19:56 -0700 Subject: Sanitize association conditions using the correct class --- .../lib/active_record/associations/through_association_scope.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index 6f0f698f1e..1d2f323112 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -135,7 +135,7 @@ module ActiveRecord def build_through_conditions conditions = @reflection.through_reflection.options[:conditions] if conditions.is_a?(Hash) - interpolate_sql(sanitize_sql(conditions)).gsub( + interpolate_sql(@reflection.through_reflection.klass.send(:sanitize_sql, conditions)).gsub( @reflection.quoted_table_name, @reflection.through_reflection.quoted_table_name) elsif conditions -- cgit v1.2.3 From 6e18fa0375b7116328fbee920c074006962a52e8 Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Sat, 3 Apr 2010 09:21:44 -0700 Subject: Raise a StatementInvalid error when trying to build a condition with hash keys that do not correspond to columns. --- activerecord/lib/active_record/relation/predicate_builder.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 717756418c..d0efa2189d 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -20,7 +20,9 @@ module ActiveRecord table = Arel::Table.new(table_name, :engine => @engine) end - attribute = table[column] + unless attribute = table[column] + raise StatementInvalid, "No attribute named `#{column}` exists for table `#{table.name}`" + end case value when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::Relation -- cgit v1.2.3 From 467d251c3dcbd3e4dd1e785a21d63535b795a64c Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Sat, 3 Apr 2010 09:54:15 -0700 Subject: Bring back +extra_conditions+. This effectively reverts 386b7bfd9d78a6d8c8bc7cc4a310df806ad0ba57 --- activerecord/lib/active_record/associations.rb | 6 +++--- activerecord/lib/active_record/reflection.rb | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 7d628005dd..20a8754b7c 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1493,7 +1493,7 @@ module ActiveRecord # The +extra_conditions+ parameter, which is not used within the main # Active Record codebase, is meant to allow plugins to define extra # finder conditions. - def configure_dependency_for_has_many(reflection) + def configure_dependency_for_has_many(reflection, extra_conditions = nil) if reflection.options.include?(:dependent) case reflection.options[:dependent] when :destroy @@ -1508,7 +1508,7 @@ module ActiveRecord record, reflection.name, reflection.klass, - reflection.dependent_conditions(record, self.class)) + reflection.dependent_conditions(record, self.class, extra_conditions)) end when :nullify before_destroy do |record| @@ -1517,7 +1517,7 @@ module ActiveRecord reflection.name, reflection.klass, reflection.primary_key_name, - reflection.dependent_conditions(record, self.class)) + reflection.dependent_conditions(record, self.class, extra_conditions)) end when :restrict method_name = "has_many_dependent_restrict_for_#{reflection.name}".to_sym diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 2a3f3f8713..0e48e229b3 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -277,11 +277,12 @@ module ActiveRecord !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many) end - def dependent_conditions(record, base_class) + def dependent_conditions(record, base_class, extra_conditions) dependent_conditions = [] dependent_conditions << "#{primary_key_name} = #{record.send(name).send(:owner_quoted_id)}" dependent_conditions << "#{options[:as]}_type = '#{base_class.name}'" if options[:as] dependent_conditions << klass.send(:sanitize_sql, options[:conditions]) if options[:conditions] + dependent_conditions << extra_conditions if extra_conditions dependent_conditions = dependent_conditions.collect {|where| "(#{where})" }.join(" AND ") dependent_conditions = dependent_conditions.gsub('@', '\@') dependent_conditions -- cgit v1.2.3