Skip to content

Commit

Permalink
Now overrides ActiveRecord::Relation methods #to_a and #apply_finder_…
Browse files Browse the repository at this point in the history
…options instead of ActiveRecord::Base#find, so the fetching of all properties actually works when using Relation methods such as #includes and #where.
  • Loading branch information
DouweM committed Sep 4, 2011
1 parent 9572c4d commit 51aa7dc
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 84 deletions.
49 changes: 1 addition & 48 deletions lib/citier/class_methods.rb
Expand Up @@ -2,6 +2,7 @@ module Citier
module ClassMethods
# any method placed here will apply to classes
def acts_as_citier(options = {})
set_acts_as_citier(true)

# Option for setting the inheritance columns, default value = 'type'
db_type_field = (options[:db_type_field] || :type).to_s
Expand Down Expand Up @@ -41,54 +42,6 @@ def acts_as_citier(options = {})

citier_debug("table_name -> #{self.table_name}")

def self.find(*args) #overrides find to get all attributes
# With option :no_children set to true, only records of type self will be returned.
# So Root.all(:no_children => true) won't return Child records.
options = args.last.is_a?(Hash) ? args.last : {}
no_children = options.delete(:no_children)
self_type = self.superclass == ActiveRecord::Base ? nil : self.name
return self.where(:type => self_type).find(*args) if no_children

tuples = super

# If the tuple is already of this class's type, we don't need to reload it.
return tuples if tuples.kind_of?(Array) ? tuples.all? { |tuple| tuple.class == self } : (tuples.class == self)

# In case of multiple tuples, find the correct ones using one query per type.
if tuples.kind_of?(Array)
found_records = []
ids_wanted = {}

# Map all the ids wanted per type
tuples.each do |tuple|
if tuple.class == self # We don't need to find the record again if this is already the correct one
found_records << tuple
next
end

type_ids_wanted = ids_wanted[tuple.class]
type_ids_wanted ||= ids_wanted[tuple.class] = []
type_ids_wanted << tuple.id
end

# Find all wanted records
ids_wanted.each do |type, ids|
found_records.push(*type.find(ids))
end

# Make a new array with the found records at the right places
tuples.each do |tuple|
found_record = found_records.find { |found| found.id == tuple.id }
tuple.force_attributes(found_record.instance_variable_get(:@attributes), :merge => true, :clear_caches => false)
end

return tuples
end

# In case of only one tuple, return it reloaded.
return tuples.reload
end

# Add the functions required for root classes only
send :include, Citier::RootInstanceMethods
end
Expand Down
21 changes: 9 additions & 12 deletions lib/citier/core_ext.rb
@@ -1,4 +1,12 @@
class ActiveRecord::Base
class ActiveRecord::Base

def self.set_acts_as_citier(citier)
@acts_as_citier = citier
end

def self.acts_as_citier?
@acts_as_citier || false
end

def self.[](column_name)
arel_table[column_name]
Expand All @@ -7,17 +15,6 @@ def self.[](column_name)
def is_new_record(state)
@new_record = state
end

# For some reason need to override this so it uses my modified find function which reloads each object to pull in all properties.
def self.all(*args)
return find(:all, *args)
end
def self.first(*args)
return find(:first, *args)
end
def self.last(*args)
return find(:last, *args)
end

def self.create_class_writable(class_reference) #creation of a new class which inherits from ActiveRecord::Base
Class.new(ActiveRecord::Base) do
Expand Down
104 changes: 80 additions & 24 deletions lib/citier/relation_methods.rb
Expand Up @@ -3,33 +3,89 @@ class Relation

alias_method :relation_delete_all, :delete_all
def delete_all(conditions = nil)
if conditions
relation_delete_all(conditions)
else
deleted = true
ids = nil
c = @klass
bind_values.each do |bind_value|
if bind_value[0].name == "id"
ids = bind_value[1]
break
end
return relation_delete_all(conditions) if !@klass.acts_as_citier?

return relation_delete_all(conditions) if conditions

deleted = true
ids = nil
c = @klass

bind_values.each do |bind_value|
if bind_value[0].name == "id"
ids = bind_value[1]
break
end
ids ||= where_values_hash["id"] || where_values_hash[:id]
where_hash = ids ? { :id => ids } : nil

deleted &= c.base_class.where(where_hash).relation_delete_all
while c.superclass != ActiveRecord::Base
if c.const_defined?(:Writable)
citier_debug("Deleting back up hierarchy #{c}")
deleted &= c::Writable.where(where_hash).delete_all
end
c = c.superclass
end
ids ||= where_values_hash["id"] || where_values_hash[:id]
where_hash = ids ? { :id => ids } : nil

deleted &= c.base_class.where(where_hash).relation_delete_all
while c.superclass != ActiveRecord::Base
if c.const_defined?(:Writable)
citier_debug("Deleting back up hierarchy #{c}")
deleted &= c::Writable.where(where_hash).delete_all
end
deleted
c = c.superclass
end

deleted
end

alias_method :relation_to_a, :to_a
def to_a
return relation_to_a if !@klass.acts_as_citier?

records = relation_to_a

c = @klass

if records.all? { |record| record.class == c }
return records
end

full_records = []
ids_wanted = {}

# Map all the ids wanted per type
records.each do |record|
if record.class == c # We don't need to find the record again if this is already the correct one
full_records << record
next
end

ids_wanted[record.class] ||= []
ids_wanted[record.class] << record.id
end

# Find all wanted records
ids_wanted.each do |type_class, ids|
full_records.push(*type_class.find(ids))
end

# Make a new array with the found records at the right places
records.each do |record|
full_record = full_records.find { |full_record| full_record.id == record.id }
record.force_attributes(full_record.instance_variable_get(:@attributes), :merge => true, :clear_caches => false)
end

return records
end

alias_method :relation_apply_finder_options, :apply_finder_options
def apply_finder_options(options)
return relation_apply_finder_options(options) if !@klass.acts_as_citier?

puts "Using custom apply_finder_options!"
# With option :no_children set to true, only records of type self will be returned.
# So Root.all(:no_children => true) won't return Child records.
no_children = options.delete(:no_children)
if no_children
self_type = self.superclass == ActiveRecord::Base ? nil : self.name
options[:conditions] = (options[:conditions] || {}).merge( :type => self_type )
end

relation_apply_finder_options(options)
end

end
end

0 comments on commit 51aa7dc

Please sign in to comment.