Skip to content

Commit

Permalink
Almost there.
Browse files Browse the repository at this point in the history
  • Loading branch information
metaskills committed Dec 4, 2011
1 parent 50f03a2 commit 548c82e
Show file tree
Hide file tree
Showing 16 changed files with 351 additions and 228 deletions.
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ defined on the original has_many. For instance:

## Todo List

* Add more GroupedScope::Grouping code.
* Add polymorphic support.
* Add :through support.
* Raise errors and/or support :finder_sql/:counter_sql.
Expand Down
22 changes: 22 additions & 0 deletions TODO
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,21 @@

Notes:


>> User.reflections[:columns].primary_key_column
=> #<ActiveRecord::ConnectionAdapters::PostgreSQLColumn:0x007fdcd859b868 @name="id", @sql_type="integer", @null=false, @limit=nil, @precision=nil, @scale=nil, @type=:integer, @default=nil, @primary=true, @coder=nil>
>> User.reflections[:columns].association_foreign_key
=> "column_id"
>> User.reflections[:columns].association_primary_key
=> "id"
>> User.reflections[:columns].active_record_primary_key
=> "id"
>> User.reflections[:columns].active_record
=> User(14 columns)




equalities = wheres.grep(Arel::Nodes::Equality)

>> ActiveRecord::Associations::AssociationScope.new(User.first.association(:columns)).scope.where_values
Expand Down Expand Up @@ -62,3 +77,10 @@
https://github.com/thoughtbot/appraisal

* Redo for rails 3.1 tests.

* Kill &block stuff.





7 changes: 4 additions & 3 deletions lib/grouped_scope.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
require 'active_record/version'
require 'grouped_scope/errors'
require 'grouped_scope/grouping'
require 'grouped_scope/self_grouping'

require 'grouped_scope/arish/associations/association_scope'
require 'grouped_scope/arish/reflection'
require 'grouped_scope/arish/associations/collection_association'
require 'grouped_scope/arish/associations/builder/grouped_collection_association'
require 'grouped_scope/arish/associations/association_scope'
require 'grouped_scope/arish/relation/predicate_builer'
require 'grouped_scope/arish/base'

136 changes: 127 additions & 9 deletions lib/grouped_scope/arish/associations/association_scope.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,140 @@ module Arish
module Associations
class AssociationScope < ActiveRecord::Associations::AssociationScope


private

def add_constraints(scope)
super(scope).tap do |s|
case reflection.macro
when :has_many

# s.where_values
# [#<Arel::Nodes::Equality:0x007ffa41eb9c70 @left=#<struct Arel::Attributes::Attribute relation=#<Arel::Table:0x007ffa41eb9f40 @name="columns", @engine=ActiveRecord::Base, @columns=nil, @aliases=[], @table_alias=nil, @primary_key=nil>, name="user_id">, @right=8>]

end if reflection.grouped_scope?
tables = construct_tables

chain.each_with_index do |reflection, i|
table, foreign_table = tables.shift, tables.first

if reflection.source_macro == :has_and_belongs_to_many
join_table = tables.shift

scope = scope.joins(join(
join_table,
table[reflection.association_primary_key].
in(join_table[reflection.association_foreign_key])
))

table, foreign_table = join_table, tables.first
end

if reflection.source_macro == :belongs_to
if reflection.options[:polymorphic]
key = reflection.association_primary_key(klass)
else
key = reflection.association_primary_key
end

foreign_key = reflection.foreign_key
else
key = reflection.foreign_key
foreign_key = reflection.active_record_primary_key
end

conditions = self.conditions[i]

if reflection == chain.last
# GroupedScope changed this line.
# scope = scope.where(table[key].eq(owner[foreign_key]))
scope = scope.where(table[key].in(owner.group.ids))

if reflection.type
scope = scope.where(table[reflection.type].eq(owner.class.base_class.name))
end

conditions.each do |condition|
if options[:through] && condition.is_a?(Hash)
condition = { table.name => condition }
end

scope = scope.where(interpolate(condition))
end
else
# GroupedScope changed this line.
# constraint = table[key].eq(foreign_table[foreign_key])
constraint = table[key].in(owner.group.ids)

if reflection.type
type = chain[i + 1].klass.base_class.name
constraint = constraint.and(table[reflection.type].eq(type))
end

scope = scope.joins(join(foreign_table, constraint))

unless conditions.empty?
scope = scope.where(sanitize(conditions, table))
end
end
end

scope
end

end
end
end
end

=begin
# Initial #add_constraints Idea
# -----------------------------
super(scope).tap do |s|
case reflection.macro
when :has_many
...
end if reflection.grouped_scope?
end
# Simple AssociationScope Methods
# -------------------------------
as = ActiveRecord::Associations::AssociationScope.new(User.first.association(:columns)) ; as.object_id
as.klass # => Column(3 columns)
as.owner # => #<User id: 8, ...>
as.active_record # => User(14 columns)
# Reflection Stuff For Join Through
# ---------------------------------
as.reflection
# => #<ActiveRecord::Reflection::AssociationReflection:0x007fdcd890bd18 @macro=:has_many, @name=:columns, @options={:order=>"position", :extend=>[]}, @active_record=User(14 columns), @plural_name="columns", @collection=true, @class_name="Column", @klass=Column(3 columns), @foreign_key="user_id", @active_record_primary_key="id", @type=nil>
as.reflection.association_primary_key # => "id"
as.reflection.association_foreign_key # => "column_id"
# How Tables Work In #add_constraints
# -----------------------------------
tables = as.send(:construct_tables)
table = tables.first
key = as.reflection.foreign_key # => "user_id"
foreign_key = as.reflection.active_record_primary_key # => "id"
table[key].eq(as.owner[foreign_key]).to_sql # => "columns"."user_id" = 8
table[key].in([1,2,3]).to_sql # => "columns"."user_id" IN (1, 2, 3)
table[key].in(as.owner.group.ids).to_sql # =>
s = as.scope ; s.object_id
equalities = s.where_values.grep(Arel::Nodes::Equality)
e = equalities.first
# => #<Arel::Nodes::Equality:0x007fdcd8546ed0 @left=#<struct Arel::Attributes::Attribute relation=#<Arel::Table:0x007fdcd85471a0 @name="columns", @engine=ActiveRecord::Base, @columns=nil, @aliases=[], @table_alias=nil, @primary_key=nil>, name="user_id">, @right=8>
=end
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,24 @@ module GroupedScope
module Arish
module Associations
module Builder
class GroupedScope
class GroupedCollectionAssociation

class << self

def build(model, *associations)
def build(model, *association_names)
association_names.each do |name|
ungrouped_reflection = find_ungrouped_reflection(model, name)
grouped_reflection = model.send ungrouped_reflection.macro, :"grouped_scope_#{name}", ungrouped_reflection.options
options = ungrouped_reflection_options(ungrouped_reflection)
grouped_reflection = model.send ungrouped_reflection.macro, :"grouped_scope_#{name}", options
grouped_reflection.grouped_scope = true
model.grouped_reflections = model.grouped_reflections.merge(name => grouped_reflection)
end
define_grouped_scope_reader
define_grouped_scope_reader(model)
end

private

def define_grouped_scope_reader
def define_grouped_scope_reader(model)
model.send(:define_method, :group) do
@group ||= GroupedScope::SelfGroupping.new(self)
end
Expand All @@ -26,13 +28,19 @@ def define_grouped_scope_reader
def find_ungrouped_reflection(model, name)
reflection = model.reflections[name.to_sym]
if reflection.blank? || [:has_many, :has_and_belongs_to_many].exclude?(reflection.macro)
msg = "Cannot create a group scope for #{name.inspect}. Either the reflection is blank or not supported." +
msg = "Cannot create a group scope for #{name.inspect}. Either the reflection is blank or not supported. " +
"Make sure to call grouped_scope after the association you are trying to extend has been defined."
raise ArgumentError, msg
end
reflection
end

def ungrouped_reflection_options(ungrouped_reflection)
ungrouped_reflection.options.dup.tap do |options|
options[:class_name] = ungrouped_reflection.class_name
end
end

end

end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ module CollectionAssociation
extend ActiveSupport::Concern

module InstanceMethods

def association_scope
if klass
@association_scope ||= GroupedScope::Associations::AssociationScope.new(self).scope
if reflection.grouped_scope?
@association_scope ||= GroupedScope::Arish::Associations::AssociationScope.new(self).scope if klass
else
super
end
end

Expand Down
6 changes: 3 additions & 3 deletions lib/grouped_scope/arish/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ module Base
extend ActiveSupport::Concern

included do
class_attribute :grouped_scopes, :instance_reader => false, :instance_writer => false
self.grouped_scopes = {}
class_attribute :grouped_reflections, :instance_reader => false, :instance_writer => false
self.grouped_reflections = {}.freeze
end

module ClassMethods

def grouped_scope(*association_names)
GroupedScope::Arish::Associations::Builder::GroupedScope.build(self, *association_names)
GroupedScope::Arish::Associations::Builder::GroupedCollectionAssociation.build(self, *association_names)
end

end
Expand Down
27 changes: 27 additions & 0 deletions lib/grouped_scope/arish/relation/predicate_builer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
module GroupedScope
module Arish
module PredicateBuilder

extend ActiveSupport::Concern

included do
singleton_class.alias_method_chain :build_from_hash, :grouped_scope
end

module ClassMethods

def build_from_hash_with_grouped_scope(engine, attributes, default_table)
attributes.select{ |k,v| GroupedScope::SelfGroupping === v }.each do |kv|
k, v = kv
attributes[k] = v.ids
end
build_from_hash_without_grouped_scope(engine, attributes, default_table)
end

end

end
end
end

ActiveRecord::PredicateBuilder.send :include, GroupedScope::Arish::PredicateBuilder
16 changes: 8 additions & 8 deletions lib/grouped_scope/self_grouping.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def quoted_ids
end

def respond_to?(method, include_private=false)
super || !proxy_class.grouped_scopes[method].blank?
super || proxy_class.grouped_reflections[method].present?
end


Expand All @@ -36,12 +36,12 @@ def group_proxy
@group_proxy ||= find_selves(group_scope_options)
end

def all_grouped?
proxy_owner.all_grouped? rescue false
def grouped?
proxy_owner.group_id.present?
end

def no_group?
proxy_owner.group_id.blank?
def all_grouped?
proxy_owner.all_grouped? rescue false
end

def find_selves(options={})
Expand All @@ -50,7 +50,7 @@ def find_selves(options={})

def group_scope_options
return {} if all_grouped?
conditions = no_group? ? { primary_key => proxy_owner.id } : { :group_id => proxy_owner.group_id }
conditions = grouped? ? { :group_id => proxy_owner.group_id } : { primary_key => proxy_owner.id }
{ :conditions => conditions }
end

Expand All @@ -66,8 +66,8 @@ def proxy_class
private

def method_missing(method, *args, &block)
if proxy_class.grouped_scopes[method]
proxy_owner.send("grouped_scope_#{method}", *args, &block)
if proxy_class.grouped_reflections[method]
proxy_owner.send :"grouped_scope_#{method}", *args, &block
else
super
end
Expand Down
Loading

0 comments on commit 548c82e

Please sign in to comment.