Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions lib/tenacity/association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ class Association

# Should this association disable foreign key like constraints
attr_reader :disable_foreign_key_constraints

# Filter records based on a defined condition. At this time only activerecord supports this
attr_reader :conditions

def initialize(type, name, source, options={})
@type = type
Expand All @@ -58,6 +61,7 @@ def initialize(type, name, source, options={})
@polymorphic = options[:polymorphic]
@as = options[:as]
@disable_foreign_key_constraints = options[:disable_foreign_key_constraints]
@conditions = options[:conditions]
end

# The name of the association
Expand Down
2 changes: 1 addition & 1 deletion lib/tenacity/associations/belongs_to.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def _t_cleanup_belongs_to_association(association)
def belongs_to_associate(association)
associate_id = self.send(association.foreign_key)
clazz = association.associate_class(self)
clazz._t_find(associate_id)
clazz._t_find(associate_id,association)
end

def set_belongs_to_associate(association, associate)
Expand Down
8 changes: 4 additions & 4 deletions lib/tenacity/associations/has_many.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def _t_get_associate_ids(association)
else
foreign_key = association.foreign_key(self.class)
associate_id = self.class._t_serialize_ids(self.id, association)
ids = association.associate_class._t_find_all_ids_by_associate(foreign_key, associate_id)
ids = association.associate_class._t_find_all_ids_by_associate(foreign_key, associate_id, association)
self.class._t_serialize_ids(ids, association)
end
end
Expand All @@ -38,7 +38,7 @@ def has_many_associates(association)
ids = _t_get_associate_ids(association)
pruned_ids = prune_associate_ids(association, ids)
clazz = association.associate_class
clazz._t_find_bulk(pruned_ids)
clazz._t_find_bulk(pruned_ids,association)
end

def set_has_many_associates(association, associates)
Expand All @@ -52,7 +52,7 @@ def has_many_associate_ids(association)

def set_has_many_associate_ids(association, associate_ids)
clazz = association.associate_class
instance_variable_set(_t_ivar_name(association), clazz._t_find_bulk(associate_ids))
instance_variable_set(_t_ivar_name(association), clazz._t_find_bulk(associate_ids,association))
end

def save_without_callback
Expand Down Expand Up @@ -125,7 +125,7 @@ def save_associate(associate)
def get_current_associates(record, association)
clazz = association.associate_class
property_name = association.foreign_key(record.class)
clazz._t_find_all_by_associate(property_name, _t_serialize(record.id, association))
clazz._t_find_all_by_associate(property_name, _t_serialize(record.id, association),association)
end

def destroy_orphaned_associates(association, old_associates, associates)
Expand Down
2 changes: 1 addition & 1 deletion lib/tenacity/associations/has_one.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def _t_cleanup_has_one_association(association)

def has_one_associate(association)
clazz = association.associate_class
clazz._t_find_first_by_associate(association.foreign_key(self.class), _t_serialize(self.id, association))
clazz._t_find_first_by_associate(association.foreign_key(self.class), _t_serialize(self.id, association),association)
end

def set_has_one_associate(association, associate)
Expand Down
40 changes: 30 additions & 10 deletions lib/tenacity/orm_ext/activerecord.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,25 +52,45 @@ def _t_id_type
@_t_id_type_clazz ||= Kernel.const_get(columns.find{ |x| x.primary }.type.to_s.capitalize)
end

def _t_find(id)
find_by_id(_t_serialize(id))
def _t_merge_association_conditions(base_conditions, association , boolean_operator="AND")
new_conditions = association.conditions unless association == nil
merged_condition = "(#{sanitize_sql(base_conditions)})"
if merged_condition != nil
merged_condition = "#{merged_condition} #{boolean_operator} (#{sanitize_sql(new_conditions)})" if new_conditions != nil
else
merged_condition = new_conditions
end

merged_condition
end

def _t_find(id, association = nil )
if association != nil and association.conditions != nil
find_by_id(_t_serialize(id), association.conditions )
else
find_by_id(_t_serialize(id) )
end
end

def _t_find_bulk(ids)
def _t_find_bulk(ids, association = nil )
return [] if ids.nil? || ids.empty?
find(:all, :conditions => ["id in (?)", _t_serialize_ids(ids)])
internal_condition = _t_merge_association_conditions( ["id in (?)", _t_serialize_ids(ids)] , association )
find(:all, :conditions => internal_condition )
end

def _t_find_first_by_associate(property, id)
find(:first, :conditions => ["#{property} = ?", _t_serialize(id)])
def _t_find_first_by_associate(property, id, association = nil )
internal_condition = _t_merge_association_conditions( ["#{property} = ?", _t_serialize(id)] , association )
find(:first, :conditions => internal_condition )
end

def _t_find_all_by_associate(property, id)
find(:all, :conditions => ["#{property} = ?", _t_serialize(id)])
def _t_find_all_by_associate(property, id, association = nil )
internal_condition = _t_merge_association_conditions( ["#{property} = ?", _t_serialize(id)] , association )
find(:all, :conditions => internal_condition )
end

def _t_find_all_ids_by_associate(property, id)
connection.select_values("SELECT id FROM #{table_name} WHERE #{property} = #{_t_serialize_id_for_sql(id)}")
def _t_find_all_ids_by_associate(property, id, association = nil )
internal_condition = _t_merge_association_conditions( ["#{property} = ?", _t_serialize(id)] , association )
connection.select_values("SELECT id FROM #{table_name} WHERE #{internal_condition}")
end

def _t_initialize_has_one_association(association)
Expand Down
10 changes: 5 additions & 5 deletions lib/tenacity/orm_ext/couchrest.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ def _t_id_type
String
end

def _t_find(id)
def _t_find(id, association = nil)
(id.nil? || id.strip == "") ? nil : get(_t_serialize(id))
end

def _t_find_bulk(ids)
def _t_find_bulk(ids, association = nil)
return [] if ids.nil? || ids.empty?
ids = [ids] unless ids.class == Array

Expand All @@ -88,15 +88,15 @@ def _t_find_bulk(ids)
docs.reject { |doc| doc.nil? }
end

def _t_find_first_by_associate(property, id)
def _t_find_first_by_associate(property, id, association = nil)
self.send("by_#{property}", :key => _t_serialize(id)).first
end

def _t_find_all_by_associate(property, id)
def _t_find_all_by_associate(property, id, association = nil)
self.send("by_#{property}", :key => _t_serialize(id))
end

def _t_find_all_ids_by_associate(property, id)
def _t_find_all_ids_by_associate(property, id, association = nil)
results = self.send("by_#{property}", :key => _t_serialize(id), :include_docs => false)
results['rows'].map { |r| r['id'] }
end
Expand Down
10 changes: 5 additions & 5 deletions lib/tenacity/orm_ext/datamapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,24 +52,24 @@ def _t_id_type
@_t_id_type_clazz ||= properties.find{ |x| x.key? }.primitive
end

def _t_find(id)
def _t_find(id, association = nil)
get(_t_serialize(id))
end

def _t_find_bulk(ids)
def _t_find_bulk(ids, association = nil)
return [] if ids.nil? || ids == []
all(:id => _t_serialize_ids(ids))
end

def _t_find_first_by_associate(property, id)
def _t_find_first_by_associate(property, id, association = nil)
first(property => _t_serialize(id))
end

def _t_find_all_by_associate(property, id)
def _t_find_all_by_associate(property, id, association = nil)
all(property => _t_serialize(id))
end

def _t_find_all_ids_by_associate(property, id)
def _t_find_all_ids_by_associate(property, id, association = nil)
repository.adapter.select("SELECT id from #{storage_names[:default]} WHERE #{property} = #{_t_serialize_id_for_sql(id)}")
end

Expand Down
10 changes: 5 additions & 5 deletions lib/tenacity/orm_ext/mongo_mapper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,23 +49,23 @@ def _t_id_type
String
end

def _t_find(id)
def _t_find(id, association = nil)
find(_t_serialize(id))
end

def _t_find_bulk(ids=[])
def _t_find_bulk(ids=[], association = nil)
find(_t_serialize_ids(ids))
end

def _t_find_first_by_associate(property, id)
def _t_find_first_by_associate(property, id, association = nil)
first(property => _t_serialize(id))
end

def _t_find_all_by_associate(property, id)
def _t_find_all_by_associate(property, id, association = nil)
all(property => _t_serialize(id))
end

def _t_find_all_ids_by_associate(property, id)
def _t_find_all_ids_by_associate(property, id, association = nil)
results = collection.find({property => _t_serialize(id)}, {:fields => 'id'}).to_a
results.map { |r| r['_id'] }
end
Expand Down
10 changes: 5 additions & 5 deletions lib/tenacity/orm_ext/mongoid.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,28 +49,28 @@ def _t_id_type
String
end

def _t_find(id)
def _t_find(id, association = nil)
(id.nil? || id.to_s.strip == "") ? nil : find(_t_serialize(id))
rescue ::Mongoid::Errors::DocumentNotFound
nil
end

def _t_find_bulk(ids)
def _t_find_bulk(ids, association = nil)
docs = find(_t_serialize_ids(ids))
docs.respond_to?(:each) ? docs : [docs]
rescue ::Mongoid::Errors::DocumentNotFound
[]
end

def _t_find_first_by_associate(property, id)
def _t_find_first_by_associate(property, id, association = nil)
find(:first, :conditions => { property => _t_serialize(id) })
end

def _t_find_all_by_associate(property, id)
def _t_find_all_by_associate(property, id, association = nil)
find(:all, :conditions => { property => _t_serialize(id) })
end

def _t_find_all_ids_by_associate(property, id)
def _t_find_all_ids_by_associate(property, id, association = nil)
results = collection.find({property => _t_serialize(id)}, {:fields => 'id'}).to_a
results.map { |r| r['_id'] }
end
Expand Down
12 changes: 6 additions & 6 deletions lib/tenacity/orm_ext/ripple.rb
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,17 @@ def _t_id_type
String
end

def _t_find(id)
def _t_find(id, association = nil)
find(_t_serialize(id))
end

def _t_find_bulk(ids)
def _t_find_bulk(ids, association = nil)
objects = find(_t_serialize_ids(ids)) || []
objects = [objects] unless objects.respond_to?(:each)
objects.reject(&:nil?)
end

def _t_find_first_by_associate(property, id)
def _t_find_first_by_associate(property, id, association = nil)
bucket = ::Ripple.client.bucket(_t_bucket_name(property))
if bucket.exist?(id)
object = bucket.get(id)
Expand All @@ -77,11 +77,11 @@ def _t_find_first_by_associate(property, id)
end
end

def _t_find_all_by_associate(property, id)
find(_t_find_all_ids_by_associate(property, id)) || []
def _t_find_all_by_associate(property, id, association = nil)
find(_t_find_all_ids_by_associate(property, id, association)) || []
end

def _t_find_all_ids_by_associate(property, id)
def _t_find_all_ids_by_associate(property, id, association = nil)
bucket = ::Ripple.client.bucket(_t_bucket_name(property))
if bucket.exist?(id)
object = bucket.get(id)
Expand Down
10 changes: 5 additions & 5 deletions lib/tenacity/orm_ext/sequel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -58,24 +58,24 @@ def _t_id_type
@_t_id_type_clazz ||= Kernel.const_get(db_schema.values.find{ |x| x[:primary_key] == true }[:type].to_s.capitalize)
end

def _t_find(id)
def _t_find(id, association = nil)
self[_t_serialize(id)]
end

def _t_find_bulk(ids)
def _t_find_bulk(ids, association = nil)
return [] if ids.nil? || ids.empty?
filter(:id => _t_serialize_ids(ids)).to_a
end

def _t_find_first_by_associate(property, id)
def _t_find_first_by_associate(property, id, association = nil)
first(property.to_sym => _t_serialize(id))
end

def _t_find_all_by_associate(property, id)
def _t_find_all_by_associate(property, id, association = nil)
filter(property.to_sym => _t_serialize(id)).to_a
end

def _t_find_all_ids_by_associate(property, id)
def _t_find_all_ids_by_associate(property, id, association = nil)
results = db["SELECT id FROM #{table_name} WHERE #{property} = #{_t_serialize_id_for_sql(id)}"].all
results.map { |r| r[:id] }
end
Expand Down
12 changes: 6 additions & 6 deletions lib/tenacity/orm_ext/toystore.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,23 +53,23 @@ def _t_id_type
String
end

def _t_find(id)
def _t_find(id, association = nil)
(id.nil? || id.to_s.strip == "") ? nil : get(_t_serialize(id))
end

def _t_find_bulk(ids)
def _t_find_bulk(ids, association = nil)
get_multi(_t_serialize_ids(ids)).compact
end

def _t_find_first_by_associate(property, id)
def _t_find_first_by_associate(property, id, association = nil)
send("first_by_#{property}", id)
end

def _t_find_all_by_associate(property, id)
get_multi(_t_find_all_ids_by_associate(property, id))
def _t_find_all_by_associate(property, id, association = nil)
get_multi(_t_find_all_ids_by_associate(property, id, association))
end

def _t_find_all_ids_by_associate(property, id)
def _t_find_all_ids_by_associate(property, id, association = nil)
get_index(property.to_sym, id)
end

Expand Down
30 changes: 30 additions & 0 deletions test/association_features/has_many_test.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,36 @@
require 'test_helper'

class HasManyTest < Test::Unit::TestCase

context "A class with a has_many conditional association to another class" do
setup do
setup_fixtures
setup_couchdb_fixtures

@car = ActiveRecordCar.create
@driver_seet = ActiveRecordSeet.create(:back => false, :is_driver => true )
@front_seets = [@driver_seet, ActiveRecordSeet.create(:back => false)]
@back_seets = [ActiveRecordSeet.create(:back => true), ActiveRecordSeet.create(:back => true)]

@car.active_record_front_seets = @front_seets
@car.active_record_back_seets = @back_seets
@car.save
end

should "memoize the conditional association" do
assert_equal @driver_seet, @car.active_record_driver_seet(true)
assert_equal @front_seets, @car.active_record_front_seets
assert_equal @back_seets, @car.active_record_back_seets

other_seets = [ActiveRecordSeet.create(:back => false), ActiveRecordSeet.create(:back => false)]
assert_equal @front_seets, ActiveRecordCar.find(@car.id).active_record_front_seets
ActiveRecordCar.find(@car.id).update_attribute(:active_record_front_seets, other_seets)
assert_equal other_seets, ActiveRecordCar.find(@car.id).active_record_front_seets

assert_equal @front_seets, @car.active_record_front_seets
assert_equal other_seets, @car.active_record_front_seets(true)
end
end

context "A class with a has_many association to another class" do
setup do
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/active_record_car.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@ class ActiveRecordCar < ActiveRecord::Base

t_has_one :couch_rest_windshield, :foreign_key => :car_id
t_has_one :active_record_engine, :foreign_key => 'car_id', :dependent => :nullify
t_has_many :active_record_front_seets , :class_name => "ActiveRecordSeet", :foreign_key => 'car_id' ,:conditions => ["back = ?",false]
t_has_many :active_record_back_seets , :class_name => "ActiveRecordSeet", :foreign_key => 'car_id' ,:conditions => {:back => true}
t_has_one :active_record_driver_seet , :class_name => "ActiveRecordSeet", :foreign_key => 'car_id' ,:conditions => "is_driver = 't' AND back = 'f'"
t_has_many :couch_rest_doors, :foreign_key => 'automobile_id', :dependent => :delete_all
end
Loading