mirror of
https://github.com/github/rails.git
synced 2026-04-26 03:00:59 -04:00
Add Relation extensions
This commit is contained in:
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)$/)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) }
|
||||
|
||||
Reference in New Issue
Block a user