dm-core 0.9.2 → 0.9.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.autotest +26 -0
 - data/{CHANGELOG → History.txt} +78 -77
 - data/Manifest.txt +123 -0
 - data/{README → README.txt} +0 -0
 - data/Rakefile +29 -0
 - data/SPECS +63 -0
 - data/TODO +1 -0
 - data/lib/dm-core.rb +6 -1
 - data/lib/dm-core/adapters/data_objects_adapter.rb +29 -32
 - data/lib/dm-core/adapters/mysql_adapter.rb +1 -1
 - data/lib/dm-core/adapters/postgres_adapter.rb +1 -1
 - data/lib/dm-core/adapters/sqlite3_adapter.rb +2 -2
 - data/lib/dm-core/associations.rb +26 -0
 - data/lib/dm-core/associations/many_to_many.rb +34 -25
 - data/lib/dm-core/associations/many_to_one.rb +4 -4
 - data/lib/dm-core/associations/one_to_many.rb +48 -13
 - data/lib/dm-core/associations/one_to_one.rb +4 -4
 - data/lib/dm-core/associations/relationship.rb +144 -42
 - data/lib/dm-core/associations/relationship_chain.rb +31 -24
 - data/lib/dm-core/auto_migrations.rb +0 -4
 - data/lib/dm-core/collection.rb +40 -7
 - data/lib/dm-core/dependency_queue.rb +31 -0
 - data/lib/dm-core/hook.rb +2 -2
 - data/lib/dm-core/is.rb +2 -2
 - data/lib/dm-core/logger.rb +10 -10
 - data/lib/dm-core/model.rb +94 -41
 - data/lib/dm-core/property.rb +72 -41
 - data/lib/dm-core/property_set.rb +8 -14
 - data/lib/dm-core/query.rb +34 -9
 - data/lib/dm-core/repository.rb +0 -0
 - data/lib/dm-core/resource.rb +13 -13
 - data/lib/dm-core/scope.rb +25 -2
 - data/lib/dm-core/type.rb +3 -3
 - data/lib/dm-core/types/discriminator.rb +10 -8
 - data/lib/dm-core/types/object.rb +4 -0
 - data/lib/dm-core/types/paranoid_boolean.rb +15 -4
 - data/lib/dm-core/types/paranoid_datetime.rb +15 -4
 - data/lib/dm-core/version.rb +3 -0
 - data/script/all +5 -0
 - data/script/performance.rb +191 -0
 - data/script/profile.rb +86 -0
 - data/spec/integration/association_spec.rb +288 -204
 - data/spec/integration/association_through_spec.rb +9 -3
 - data/spec/integration/associations/many_to_many_spec.rb +97 -31
 - data/spec/integration/associations/many_to_one_spec.rb +41 -6
 - data/spec/integration/associations/one_to_many_spec.rb +18 -2
 - data/spec/integration/auto_migrations_spec.rb +0 -0
 - data/spec/integration/collection_spec.rb +89 -42
 - data/spec/integration/dependency_queue_spec.rb +58 -0
 - data/spec/integration/model_spec.rb +67 -8
 - data/spec/integration/postgres_adapter_spec.rb +19 -20
 - data/spec/integration/property_spec.rb +17 -8
 - data/spec/integration/query_spec.rb +273 -191
 - data/spec/integration/resource_spec.rb +108 -10
 - data/spec/integration/strategic_eager_loading_spec.rb +138 -0
 - data/spec/integration/transaction_spec.rb +3 -3
 - data/spec/integration/type_spec.rb +121 -0
 - data/spec/lib/logging_helper.rb +18 -0
 - data/spec/lib/model_loader.rb +91 -0
 - data/spec/lib/publicize_methods.rb +28 -0
 - data/spec/models/vehicles.rb +34 -0
 - data/spec/models/zoo.rb +48 -0
 - data/spec/spec.opts +3 -0
 - data/spec/spec_helper.rb +25 -62
 - data/spec/unit/adapters/data_objects_adapter_spec.rb +1 -0
 - data/spec/unit/associations/many_to_many_spec.rb +3 -0
 - data/spec/unit/associations/many_to_one_spec.rb +9 -1
 - data/spec/unit/associations/one_to_many_spec.rb +12 -4
 - data/spec/unit/associations/relationship_spec.rb +19 -15
 - data/spec/unit/associations_spec.rb +37 -0
 - data/spec/unit/collection_spec.rb +8 -0
 - data/spec/unit/data_mapper_spec.rb +14 -0
 - data/spec/unit/model_spec.rb +2 -2
 - data/spec/unit/property_set_spec.rb +0 -13
 - data/spec/unit/property_spec.rb +92 -21
 - data/spec/unit/query_spec.rb +49 -4
 - data/spec/unit/resource_spec.rb +122 -60
 - data/spec/unit/scope_spec.rb +11 -0
 - data/tasks/ci.rb +68 -0
 - data/tasks/dm.rb +63 -0
 - data/tasks/doc.rb +20 -0
 - data/tasks/hoe.rb +38 -0
 - data/tasks/install.rb +20 -0
 - metadata +63 -22
 
| 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            gem 'do_sqlite3', '=0.9. 
     | 
| 
      
 1 
     | 
    
         
            +
            gem 'do_sqlite3', '=0.9.3'
         
     | 
| 
       2 
2 
     | 
    
         
             
            require 'do_sqlite3'
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            module DataMapper
         
     | 
| 
         @@ -41,7 +41,7 @@ module DataMapper 
     | 
|
| 
       41 
41 
     | 
    
         
             
                    end
         
     | 
| 
       42 
42 
     | 
    
         | 
| 
       43 
43 
     | 
    
         
             
                    module SQL
         
     | 
| 
       44 
     | 
    
         
            -
                      private
         
     | 
| 
      
 44 
     | 
    
         
            +
            #          private  ## This cannot be private for current migrations
         
     | 
| 
       45 
45 
     | 
    
         | 
| 
       46 
46 
     | 
    
         
             
                      # TODO: move to dm-more/dm-migrations
         
     | 
| 
       47 
47 
     | 
    
         
             
                      def supports_serial?
         
     | 
    
        data/lib/dm-core/associations.rb
    CHANGED
    
    | 
         @@ -17,6 +17,32 @@ module DataMapper 
     | 
|
| 
       17 
17 
     | 
    
         
             
                class UnsavedParentError < RuntimeError
         
     | 
| 
       18 
18 
     | 
    
         
             
                end
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
      
 20 
     | 
    
         
            +
                # Returns all relationships that are many-to-one for this model.
         
     | 
| 
      
 21 
     | 
    
         
            +
                #
         
     | 
| 
      
 22 
     | 
    
         
            +
                # Used to find the relationships that require properties in any Repository.
         
     | 
| 
      
 23 
     | 
    
         
            +
                #
         
     | 
| 
      
 24 
     | 
    
         
            +
                # Example:
         
     | 
| 
      
 25 
     | 
    
         
            +
                # class Plur
         
     | 
| 
      
 26 
     | 
    
         
            +
                #   include DataMapper::Resource
         
     | 
| 
      
 27 
     | 
    
         
            +
                #   def self.default_repository_name
         
     | 
| 
      
 28 
     | 
    
         
            +
                #     :plur_db
         
     | 
| 
      
 29 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 30 
     | 
    
         
            +
                #   repository(:plupp_db) do
         
     | 
| 
      
 31 
     | 
    
         
            +
                #     has 1, :plupp
         
     | 
| 
      
 32 
     | 
    
         
            +
                #   end
         
     | 
| 
      
 33 
     | 
    
         
            +
                # end
         
     | 
| 
      
 34 
     | 
    
         
            +
                #
         
     | 
| 
      
 35 
     | 
    
         
            +
                # This resource has a many-to-one to the Plupp resource residing in the :plupp_db repository,
         
     | 
| 
      
 36 
     | 
    
         
            +
                # but the Plur resource needs the plupp_id property no matter what repository itself lives in,
         
     | 
| 
      
 37 
     | 
    
         
            +
                # ie we need to create that property when we migrate etc.
         
     | 
| 
      
 38 
     | 
    
         
            +
                #
         
     | 
| 
      
 39 
     | 
    
         
            +
                # Used in DataMapper::Model.properties_with_subclasses
         
     | 
| 
      
 40 
     | 
    
         
            +
                #
         
     | 
| 
      
 41 
     | 
    
         
            +
                # @api private
         
     | 
| 
      
 42 
     | 
    
         
            +
                def many_to_one_relationships
         
     | 
| 
      
 43 
     | 
    
         
            +
                  @relationships.values.collect do |rels| rels.values end.flatten.select do |relationship| relationship.child_model == self end
         
     | 
| 
      
 44 
     | 
    
         
            +
                end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
       20 
46 
     | 
    
         
             
                def relationships(repository_name = default_repository_name)
         
     | 
| 
       21 
47 
     | 
    
         
             
                  @relationships ||= Hash.new { |h,k| h[k] = k == Repository.default_name ? {} : h[Repository.default_name].dup }
         
     | 
| 
       22 
48 
     | 
    
         
             
                  @relationships[repository_name]
         
     | 
| 
         @@ -28,7 +28,7 @@ module DataMapper 
     | 
|
| 
       28 
28 
     | 
    
         
             
                      def #{name}_association
         
     | 
| 
       29 
29 
     | 
    
         
             
                        @#{name}_association ||= begin
         
     | 
| 
       30 
30 
     | 
    
         
             
                          unless relationship = model.relationships(#{repository_name.inspect})[#{name.inspect}]
         
     | 
| 
       31 
     | 
    
         
            -
                            raise ArgumentError,  
     | 
| 
      
 31 
     | 
    
         
            +
                            raise ArgumentError, "Relationship #{name.inspect} does not exist in \#{model}"
         
     | 
| 
       32 
32 
     | 
    
         
             
                          end
         
     | 
| 
       33 
33 
     | 
    
         
             
                          association = Proxy.new(relationship, self)
         
     | 
| 
       34 
34 
     | 
    
         
             
                          parent_associations << association
         
     | 
| 
         @@ -40,14 +40,14 @@ module DataMapper 
     | 
|
| 
       40 
40 
     | 
    
         
             
                    opts = options.dup
         
     | 
| 
       41 
41 
     | 
    
         
             
                    opts.delete(:through)
         
     | 
| 
       42 
42 
     | 
    
         
             
                    opts[:child_model]              ||= opts.delete(:class_name)  || Extlib::Inflection.classify(name)
         
     | 
| 
       43 
     | 
    
         
            -
                    opts[:parent_model]             =   model 
     | 
| 
      
 43 
     | 
    
         
            +
                    opts[:parent_model]             =   model
         
     | 
| 
       44 
44 
     | 
    
         
             
                    opts[:repository_name]          =   repository_name
         
     | 
| 
       45 
45 
     | 
    
         
             
                    opts[:remote_relationship_name] ||= opts.delete(:remote_name) || name
         
     | 
| 
       46 
46 
     | 
    
         
             
                    opts[:parent_key]               =   opts[:parent_key]
         
     | 
| 
       47 
47 
     | 
    
         
             
                    opts[:child_key]                =   opts[:child_key]
         
     | 
| 
       48 
48 
     | 
    
         
             
                    opts[:mutable]                  =   true
         
     | 
| 
       49 
49 
     | 
    
         | 
| 
       50 
     | 
    
         
            -
                    names        = [ opts[:child_model], opts[:parent_model] ].sort
         
     | 
| 
      
 50 
     | 
    
         
            +
                    names        = [ opts[:child_model], opts[:parent_model].name ].sort
         
     | 
| 
       51 
51 
     | 
    
         
             
                    model_name   = names.join
         
     | 
| 
       52 
52 
     | 
    
         
             
                    storage_name = Extlib::Inflection.tableize(Extlib::Inflection.pluralize(names[0]) + names[1])
         
     | 
| 
       53 
53 
     | 
    
         | 
| 
         @@ -77,39 +77,50 @@ module DataMapper 
     | 
|
| 
       77 
77 
     | 
    
         
             
                  end
         
     | 
| 
       78 
78 
     | 
    
         | 
| 
       79 
79 
     | 
    
         
             
                  class Proxy < DataMapper::Associations::OneToMany::Proxy
         
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
       81 
     | 
    
         
            -
                    def <<(resource)
         
     | 
| 
       82 
     | 
    
         
            -
                      resource.save if resource.new_record?
         
     | 
| 
       83 
     | 
    
         
            -
                      through = @relationship.child_model.new
         
     | 
| 
       84 
     | 
    
         
            -
                      @relationship.child_key.each_with_index do |key, index|
         
     | 
| 
       85 
     | 
    
         
            -
                        through.send("#{key.name}=", @relationship.parent_key.key[index].get(@parent))
         
     | 
| 
       86 
     | 
    
         
            -
                      end
         
     | 
| 
       87 
     | 
    
         
            -
                      remote_relationship.child_key.each_with_index do |key, index|
         
     | 
| 
       88 
     | 
    
         
            -
                        through.send("#{key.name}=", remote_relationship.parent_key.key[index].get(resource))
         
     | 
| 
       89 
     | 
    
         
            -
                      end
         
     | 
| 
       90 
     | 
    
         
            -
                      near_model << through
         
     | 
| 
       91 
     | 
    
         
            -
                      super
         
     | 
| 
       92 
     | 
    
         
            -
                    end
         
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
80 
     | 
    
         
             
                    def delete(resource)
         
     | 
| 
       95 
     | 
    
         
            -
                      through =  
     | 
| 
       96 
     | 
    
         
            -
                       
     | 
| 
      
 81 
     | 
    
         
            +
                      through = near_association.get(*(@parent.key + resource.key))
         
     | 
| 
      
 82 
     | 
    
         
            +
                      near_association.delete(through)
         
     | 
| 
       97 
83 
     | 
    
         
             
                      orphan_resource(super)
         
     | 
| 
       98 
84 
     | 
    
         
             
                    end
         
     | 
| 
       99 
85 
     | 
    
         | 
| 
       100 
86 
     | 
    
         
             
                    def clear
         
     | 
| 
       101 
     | 
    
         
            -
                       
     | 
| 
      
 87 
     | 
    
         
            +
                      near_association.clear
         
     | 
| 
       102 
88 
     | 
    
         
             
                      super
         
     | 
| 
       103 
89 
     | 
    
         
             
                    end
         
     | 
| 
       104 
90 
     | 
    
         | 
| 
       105 
91 
     | 
    
         
             
                    def destroy
         
     | 
| 
       106 
     | 
    
         
            -
                       
     | 
| 
      
 92 
     | 
    
         
            +
                      near_association.destroy
         
     | 
| 
       107 
93 
     | 
    
         
             
                      super
         
     | 
| 
       108 
94 
     | 
    
         
             
                    end
         
     | 
| 
       109 
95 
     | 
    
         | 
| 
       110 
96 
     | 
    
         
             
                    def save
         
     | 
| 
       111 
97 
     | 
    
         
             
                    end
         
     | 
| 
       112 
98 
     | 
    
         | 
| 
      
 99 
     | 
    
         
            +
                    private
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
                    def new_child(attributes)
         
     | 
| 
      
 102 
     | 
    
         
            +
                      remote_relationship.parent_model.new(attributes)
         
     | 
| 
      
 103 
     | 
    
         
            +
                    end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                    def relate_resource(resource)
         
     | 
| 
      
 106 
     | 
    
         
            +
                      assert_mutable
         
     | 
| 
      
 107 
     | 
    
         
            +
                      add_default_association_values(resource)
         
     | 
| 
      
 108 
     | 
    
         
            +
                      @orphans.delete(resource)
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                      # TODO: fix this so it does not automatically save on append, if possible
         
     | 
| 
      
 111 
     | 
    
         
            +
                      resource.save if resource.new_record?
         
     | 
| 
      
 112 
     | 
    
         
            +
                      through_resource = @relationship.child_model.new
         
     | 
| 
      
 113 
     | 
    
         
            +
                      @relationship.child_key.zip(@relationship.parent_key) do |child_key,parent_key|
         
     | 
| 
      
 114 
     | 
    
         
            +
                        through_resource.send("#{child_key.name}=", parent_key.get(@parent))
         
     | 
| 
      
 115 
     | 
    
         
            +
                      end
         
     | 
| 
      
 116 
     | 
    
         
            +
                      remote_relationship.child_key.zip(remote_relationship.parent_key) do |child_key,parent_key|
         
     | 
| 
      
 117 
     | 
    
         
            +
                        through_resource.send("#{child_key.name}=", parent_key.get(resource))
         
     | 
| 
      
 118 
     | 
    
         
            +
                      end
         
     | 
| 
      
 119 
     | 
    
         
            +
                      near_association << through_resource
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                      resource
         
     | 
| 
      
 122 
     | 
    
         
            +
                    end
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
       113 
124 
     | 
    
         
             
                    def orphan_resource(resource)
         
     | 
| 
       114 
125 
     | 
    
         
             
                      assert_mutable
         
     | 
| 
       115 
126 
     | 
    
         
             
                      @orphans << resource
         
     | 
| 
         @@ -119,14 +130,12 @@ module DataMapper 
     | 
|
| 
       119 
130 
     | 
    
         
             
                    def assert_mutable
         
     | 
| 
       120 
131 
     | 
    
         
             
                    end
         
     | 
| 
       121 
132 
     | 
    
         | 
| 
       122 
     | 
    
         
            -
                    private
         
     | 
| 
       123 
     | 
    
         
            -
             
     | 
| 
       124 
133 
     | 
    
         
             
                    def remote_relationship
         
     | 
| 
       125 
134 
     | 
    
         
             
                      @remote_relationship ||= @relationship.send(:remote_relationship)
         
     | 
| 
       126 
135 
     | 
    
         
             
                    end
         
     | 
| 
       127 
136 
     | 
    
         | 
| 
       128 
     | 
    
         
            -
                    def  
     | 
| 
       129 
     | 
    
         
            -
                      @ 
     | 
| 
      
 137 
     | 
    
         
            +
                    def near_association
         
     | 
| 
      
 138 
     | 
    
         
            +
                      @near_association ||= @parent.send(near_relationship_name)
         
     | 
| 
       130 
139 
     | 
    
         
             
                    end
         
     | 
| 
       131 
140 
     | 
    
         | 
| 
       132 
141 
     | 
    
         
             
                    def near_relationship_name
         
     | 
| 
         @@ -27,7 +27,7 @@ module DataMapper 
     | 
|
| 
       27 
27 
     | 
    
         
             
                      def #{name}_association
         
     | 
| 
       28 
28 
     | 
    
         
             
                        @#{name}_association ||= begin
         
     | 
| 
       29 
29 
     | 
    
         
             
                          unless relationship = model.relationships(#{repository_name.inspect})[:#{name}]
         
     | 
| 
       30 
     | 
    
         
            -
                            raise ArgumentError,  
     | 
| 
      
 30 
     | 
    
         
            +
                            raise ArgumentError, "Relationship #{name.inspect} does not exist in \#{model}"
         
     | 
| 
       31 
31 
     | 
    
         
             
                          end
         
     | 
| 
       32 
32 
     | 
    
         
             
                          association = Proxy.new(relationship, self)
         
     | 
| 
       33 
33 
     | 
    
         
             
                          child_associations << association
         
     | 
| 
         @@ -39,7 +39,7 @@ module DataMapper 
     | 
|
| 
       39 
39 
     | 
    
         
             
                    model.relationships(repository_name)[name] = Relationship.new(
         
     | 
| 
       40 
40 
     | 
    
         
             
                      name,
         
     | 
| 
       41 
41 
     | 
    
         
             
                      repository_name,
         
     | 
| 
       42 
     | 
    
         
            -
                      model 
     | 
| 
      
 42 
     | 
    
         
            +
                      model,
         
     | 
| 
       43 
43 
     | 
    
         
             
                      options.fetch(:class_name, Extlib::Inflection.classify(name)),
         
     | 
| 
       44 
44 
     | 
    
         
             
                      options
         
     | 
| 
       45 
45 
     | 
    
         
             
                    )
         
     | 
| 
         @@ -48,7 +48,7 @@ module DataMapper 
     | 
|
| 
       48 
48 
     | 
    
         
             
                  class Proxy
         
     | 
| 
       49 
49 
     | 
    
         
             
                    include Assertions
         
     | 
| 
       50 
50 
     | 
    
         | 
| 
       51 
     | 
    
         
            -
                    instance_methods.each { |m| undef_method m unless %w[ __id__ __send__ class kind_of? respond_to? assert_kind_of should should_not ].include?(m) }
         
     | 
| 
      
 51 
     | 
    
         
            +
                    instance_methods.each { |m| undef_method m unless %w[ __id__ __send__ class kind_of? respond_to? assert_kind_of should should_not instance_variable_set instance_variable_get ].include?(m) }
         
     | 
| 
       52 
52 
     | 
    
         | 
| 
       53 
53 
     | 
    
         
             
                    def replace(parent)
         
     | 
| 
       54 
54 
     | 
    
         
             
                      @parent = parent
         
     | 
| 
         @@ -60,7 +60,7 @@ module DataMapper 
     | 
|
| 
       60 
60 
     | 
    
         
             
                      return false if @parent.nil?
         
     | 
| 
       61 
61 
     | 
    
         
             
                      return true  unless parent.new_record?
         
     | 
| 
       62 
62 
     | 
    
         | 
| 
       63 
     | 
    
         
            -
                       
     | 
| 
      
 63 
     | 
    
         
            +
                      @relationship.with_repository(parent) do
         
     | 
| 
       64 
64 
     | 
    
         
             
                        parent.save
         
     | 
| 
       65 
65 
     | 
    
         
             
                      end
         
     | 
| 
       66 
66 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -1,5 +1,3 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'forwardable'
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
1 
     | 
    
         
             
            module DataMapper
         
     | 
| 
       4 
2 
     | 
    
         
             
              module Associations
         
     | 
| 
       5 
3 
     | 
    
         
             
                module OneToMany
         
     | 
| 
         @@ -29,7 +27,7 @@ module DataMapper 
     | 
|
| 
       29 
27 
     | 
    
         
             
                      def #{name}_association
         
     | 
| 
       30 
28 
     | 
    
         
             
                        @#{name}_association ||= begin
         
     | 
| 
       31 
29 
     | 
    
         
             
                          unless relationship = model.relationships(#{repository_name.inspect})[#{name.inspect}]
         
     | 
| 
       32 
     | 
    
         
            -
                            raise ArgumentError,  
     | 
| 
      
 30 
     | 
    
         
            +
                            raise ArgumentError, "Relationship #{name.inspect} does not exist in \#{model}"
         
     | 
| 
       33 
31 
     | 
    
         
             
                          end
         
     | 
| 
       34 
32 
     | 
    
         
             
                          association = Proxy.new(relationship, self)
         
     | 
| 
       35 
33 
     | 
    
         
             
                          parent_associations << association
         
     | 
| 
         @@ -41,8 +39,14 @@ module DataMapper 
     | 
|
| 
       41 
39 
     | 
    
         
             
                    model.relationships(repository_name)[name] = if options.has_key?(:through)
         
     | 
| 
       42 
40 
     | 
    
         
             
                      opts = options.dup
         
     | 
| 
       43 
41 
     | 
    
         | 
| 
      
 42 
     | 
    
         
            +
                      if opts.key?(:class_name) && !opts.key?(:child_key)
         
     | 
| 
      
 43 
     | 
    
         
            +
                        warn(<<-EOS.margin)
         
     | 
| 
      
 44 
     | 
    
         
            +
                          You have specified #{model.base_model.name}.has(#{name.inspect}) with :class_name => #{opts[:class_name].inspect}. You probably also want to specify the :child_key option.
         
     | 
| 
      
 45 
     | 
    
         
            +
                        EOS
         
     | 
| 
      
 46 
     | 
    
         
            +
                      end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
       44 
48 
     | 
    
         
             
                      opts[:child_model]            ||= opts.delete(:class_name)  || Extlib::Inflection.classify(name)
         
     | 
| 
       45 
     | 
    
         
            -
                      opts[:parent_model]             =   model 
     | 
| 
      
 49 
     | 
    
         
            +
                      opts[:parent_model]             =   model
         
     | 
| 
       46 
50 
     | 
    
         
             
                      opts[:repository_name]          =   repository_name
         
     | 
| 
       47 
51 
     | 
    
         
             
                      opts[:near_relationship_name]   =   opts.delete(:through)
         
     | 
| 
       48 
52 
     | 
    
         
             
                      opts[:remote_relationship_name] ||= opts.delete(:remote_name) || name
         
     | 
| 
         @@ -52,10 +56,10 @@ module DataMapper 
     | 
|
| 
       52 
56 
     | 
    
         
             
                      RelationshipChain.new( opts )
         
     | 
| 
       53 
57 
     | 
    
         
             
                    else
         
     | 
| 
       54 
58 
     | 
    
         
             
                      Relationship.new(
         
     | 
| 
       55 
     | 
    
         
            -
                         
     | 
| 
      
 59 
     | 
    
         
            +
                        name,
         
     | 
| 
       56 
60 
     | 
    
         
             
                        repository_name,
         
     | 
| 
       57 
61 
     | 
    
         
             
                        options.fetch(:class_name, Extlib::Inflection.classify(name)),
         
     | 
| 
       58 
     | 
    
         
            -
                        model 
     | 
| 
      
 62 
     | 
    
         
            +
                        model,
         
     | 
| 
       59 
63 
     | 
    
         
             
                        options
         
     | 
| 
       60 
64 
     | 
    
         
             
                      )
         
     | 
| 
       61 
65 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -68,7 +72,7 @@ module DataMapper 
     | 
|
| 
       68 
72 
     | 
    
         
             
                  class Proxy
         
     | 
| 
       69 
73 
     | 
    
         
             
                    include Assertions
         
     | 
| 
       70 
74 
     | 
    
         | 
| 
       71 
     | 
    
         
            -
                    instance_methods.each { |m| undef_method m unless %w[ __id__ __send__ class kind_of? respond_to? assert_kind_of should should_not ].include?(m) }
         
     | 
| 
      
 75 
     | 
    
         
            +
                    instance_methods.each { |m| undef_method m unless %w[ __id__ __send__ class kind_of? respond_to? assert_kind_of should should_not instance_variable_set instance_variable_get ].include?(m) }
         
     | 
| 
       72 
76 
     | 
    
         | 
| 
       73 
77 
     | 
    
         
             
                    # FIXME: remove when RelationshipChain#get_children can return a Collection
         
     | 
| 
       74 
78 
     | 
    
         
             
                    def all(query = {})
         
     | 
| 
         @@ -87,6 +91,7 @@ module DataMapper 
     | 
|
| 
       87 
91 
     | 
    
         | 
| 
       88 
92 
     | 
    
         
             
                    def <<(resource)
         
     | 
| 
       89 
93 
     | 
    
         
             
                      assert_mutable
         
     | 
| 
      
 94 
     | 
    
         
            +
                      return self if !resource.new_record? && self.include?(resource)
         
     | 
| 
       90 
95 
     | 
    
         
             
                      super
         
     | 
| 
       91 
96 
     | 
    
         
             
                      relate_resource(resource)
         
     | 
| 
       92 
97 
     | 
    
         
             
                      self
         
     | 
| 
         @@ -94,6 +99,7 @@ module DataMapper 
     | 
|
| 
       94 
99 
     | 
    
         | 
| 
       95 
100 
     | 
    
         
             
                    def push(*resources)
         
     | 
| 
       96 
101 
     | 
    
         
             
                      assert_mutable
         
     | 
| 
      
 102 
     | 
    
         
            +
                      resources.reject { |resource| !resource.new_record? && self.include?(resource) }
         
     | 
| 
       97 
103 
     | 
    
         
             
                      super
         
     | 
| 
       98 
104 
     | 
    
         
             
                      resources.each { |resource| relate_resource(resource) }
         
     | 
| 
       99 
105 
     | 
    
         
             
                      self
         
     | 
| 
         @@ -101,6 +107,7 @@ module DataMapper 
     | 
|
| 
       101 
107 
     | 
    
         | 
| 
       102 
108 
     | 
    
         
             
                    def unshift(*resources)
         
     | 
| 
       103 
109 
     | 
    
         
             
                      assert_mutable
         
     | 
| 
      
 110 
     | 
    
         
            +
                      resources.reject { |resource| !resource.new_record? && self.include?(resource) }
         
     | 
| 
       104 
111 
     | 
    
         
             
                      super
         
     | 
| 
       105 
112 
     | 
    
         
             
                      resources.each { |resource| relate_resource(resource) }
         
     | 
| 
       106 
113 
     | 
    
         
             
                      self
         
     | 
| 
         @@ -109,7 +116,7 @@ module DataMapper 
     | 
|
| 
       109 
116 
     | 
    
         
             
                    def replace(other)
         
     | 
| 
       110 
117 
     | 
    
         
             
                      assert_mutable
         
     | 
| 
       111 
118 
     | 
    
         
             
                      each { |resource| orphan_resource(resource) }
         
     | 
| 
       112 
     | 
    
         
            -
                      other = other.map { |resource| Hash  
     | 
| 
      
 119 
     | 
    
         
            +
                      other = other.map { |resource| resource.kind_of?(Hash) ? new_child(resource) : resource }
         
     | 
| 
       113 
120 
     | 
    
         
             
                      super
         
     | 
| 
       114 
121 
     | 
    
         
             
                      other.each { |resource| relate_resource(resource) }
         
     | 
| 
       115 
122 
     | 
    
         
             
                      self
         
     | 
| 
         @@ -142,10 +149,21 @@ module DataMapper 
     | 
|
| 
       142 
149 
     | 
    
         
             
                      self
         
     | 
| 
       143 
150 
     | 
    
         
             
                    end
         
     | 
| 
       144 
151 
     | 
    
         | 
| 
      
 152 
     | 
    
         
            +
                    def build(attributes = {})
         
     | 
| 
      
 153 
     | 
    
         
            +
                      assert_mutable
         
     | 
| 
      
 154 
     | 
    
         
            +
                      attributes = default_attributes.merge(attributes)
         
     | 
| 
      
 155 
     | 
    
         
            +
                      resource = children.respond_to?(:build) ? super(attributes) : new_child(attributes)
         
     | 
| 
      
 156 
     | 
    
         
            +
                      self << resource
         
     | 
| 
      
 157 
     | 
    
         
            +
                      resource
         
     | 
| 
      
 158 
     | 
    
         
            +
                    end
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
       145 
160 
     | 
    
         
             
                    def create(attributes = {})
         
     | 
| 
       146 
161 
     | 
    
         
             
                      assert_mutable
         
     | 
| 
       147 
162 
     | 
    
         
             
                      raise UnsavedParentError, 'You cannot create until the parent is saved' if @parent.new_record?
         
     | 
| 
       148 
     | 
    
         
            -
                       
     | 
| 
      
 163 
     | 
    
         
            +
                      attributes = default_attributes.merge(attributes)
         
     | 
| 
      
 164 
     | 
    
         
            +
                      resource = children.respond_to?(:create) ? super(attributes) : @relationship.child_model.create(attributes)
         
     | 
| 
      
 165 
     | 
    
         
            +
                      self << resource
         
     | 
| 
      
 166 
     | 
    
         
            +
                      resource
         
     | 
| 
       149 
167 
     | 
    
         
             
                    end
         
     | 
| 
       150 
168 
     | 
    
         | 
| 
       151 
169 
     | 
    
         
             
                    def update(attributes = {})
         
     | 
| 
         @@ -229,15 +247,32 @@ module DataMapper 
     | 
|
| 
       229 
247 
     | 
    
         
             
                      raise ImmutableAssociationError, 'You can not modify this assocation' if children.frozen?
         
     | 
| 
       230 
248 
     | 
    
         
             
                    end
         
     | 
| 
       231 
249 
     | 
    
         | 
| 
       232 
     | 
    
         
            -
                    def  
     | 
| 
       233 
     | 
    
         
            -
                       
     | 
| 
      
 250 
     | 
    
         
            +
                    def default_attributes
         
     | 
| 
      
 251 
     | 
    
         
            +
                      default_attributes = {}
         
     | 
| 
       234 
252 
     | 
    
         | 
| 
       235 
253 
     | 
    
         
             
                      @relationship.query.each do |attribute, value|
         
     | 
| 
       236 
     | 
    
         
            -
                        next if Query::OPTIONS.include?(attribute) || attribute.kind_of?(Query::Operator) 
     | 
| 
      
 254 
     | 
    
         
            +
                        next if Query::OPTIONS.include?(attribute) || attribute.kind_of?(Query::Operator)
         
     | 
| 
      
 255 
     | 
    
         
            +
                        default_attributes[attribute] = value
         
     | 
| 
      
 256 
     | 
    
         
            +
                      end
         
     | 
| 
      
 257 
     | 
    
         
            +
             
     | 
| 
      
 258 
     | 
    
         
            +
                      @relationship.child_key.zip(@relationship.parent_key.get(@parent)) do |property,value|
         
     | 
| 
      
 259 
     | 
    
         
            +
                        default_attributes[property.name] = value
         
     | 
| 
      
 260 
     | 
    
         
            +
                      end
         
     | 
| 
      
 261 
     | 
    
         
            +
             
     | 
| 
      
 262 
     | 
    
         
            +
                      default_attributes
         
     | 
| 
      
 263 
     | 
    
         
            +
                    end
         
     | 
| 
      
 264 
     | 
    
         
            +
             
     | 
| 
      
 265 
     | 
    
         
            +
                    def add_default_association_values(resource)
         
     | 
| 
      
 266 
     | 
    
         
            +
                      default_attributes.each do |attribute, value|
         
     | 
| 
      
 267 
     | 
    
         
            +
                        next if !resource.respond_to?("#{attribute}=") || resource.attribute_loaded?(attribute)
         
     | 
| 
       237 
268 
     | 
    
         
             
                        resource.send("#{attribute}=", value)
         
     | 
| 
       238 
269 
     | 
    
         
             
                      end
         
     | 
| 
       239 
270 
     | 
    
         
             
                    end
         
     | 
| 
       240 
271 
     | 
    
         | 
| 
      
 272 
     | 
    
         
            +
                    def new_child(attributes)
         
     | 
| 
      
 273 
     | 
    
         
            +
                      @relationship.child_model.new(default_attributes.merge(attributes))
         
     | 
| 
      
 274 
     | 
    
         
            +
                    end
         
     | 
| 
      
 275 
     | 
    
         
            +
             
     | 
| 
       241 
276 
     | 
    
         
             
                    def relate_resource(resource)
         
     | 
| 
       242 
277 
     | 
    
         
             
                      assert_mutable
         
     | 
| 
       243 
278 
     | 
    
         
             
                      add_default_association_values(resource)
         
     | 
| 
         @@ -252,7 +287,7 @@ module DataMapper 
     | 
|
| 
       252 
287 
     | 
    
         
             
                    end
         
     | 
| 
       253 
288 
     | 
    
         | 
| 
       254 
289 
     | 
    
         
             
                    def save_resource(resource, parent = @parent)
         
     | 
| 
       255 
     | 
    
         
            -
                       
     | 
| 
      
 290 
     | 
    
         
            +
                      @relationship.with_repository(resource) do |r|
         
     | 
| 
       256 
291 
     | 
    
         
             
                        if parent.nil? && resource.model.respond_to?(:many_to_many)
         
     | 
| 
       257 
292 
     | 
    
         
             
                          resource.destroy
         
     | 
| 
       258 
293 
     | 
    
         
             
                        else
         
     | 
| 
         @@ -27,7 +27,7 @@ module DataMapper 
     | 
|
| 
       27 
27 
     | 
    
         
             
                      def #{name}_association
         
     | 
| 
       28 
28 
     | 
    
         
             
                        @#{name}_association ||= begin
         
     | 
| 
       29 
29 
     | 
    
         
             
                          unless relationship = model.relationships(#{repository_name.inspect})[:#{name}]
         
     | 
| 
       30 
     | 
    
         
            -
                            raise ArgumentError,  
     | 
| 
      
 30 
     | 
    
         
            +
                            raise ArgumentError, "Relationship #{name.inspect} does not exist in \#{model}"
         
     | 
| 
       31 
31 
     | 
    
         
             
                          end
         
     | 
| 
       32 
32 
     | 
    
         
             
                          association = Associations::OneToMany::Proxy.new(relationship, self)
         
     | 
| 
       33 
33 
     | 
    
         
             
                          parent_associations << association
         
     | 
| 
         @@ -39,7 +39,7 @@ module DataMapper 
     | 
|
| 
       39 
39 
     | 
    
         
             
                    model.relationships(repository_name)[name] = if options.has_key?(:through)
         
     | 
| 
       40 
40 
     | 
    
         
             
                      RelationshipChain.new(
         
     | 
| 
       41 
41 
     | 
    
         
             
                        :child_model              => options.fetch(:class_name, Extlib::Inflection.classify(name)),
         
     | 
| 
       42 
     | 
    
         
            -
                        :parent_model             => model 
     | 
| 
      
 42 
     | 
    
         
            +
                        :parent_model             => model,
         
     | 
| 
       43 
43 
     | 
    
         
             
                        :repository_name          => repository_name,
         
     | 
| 
       44 
44 
     | 
    
         
             
                        :near_relationship_name   => options[:through],
         
     | 
| 
       45 
45 
     | 
    
         
             
                        :remote_relationship_name => options.fetch(:remote_name, name),
         
     | 
| 
         @@ -48,10 +48,10 @@ module DataMapper 
     | 
|
| 
       48 
48 
     | 
    
         
             
                      )
         
     | 
| 
       49 
49 
     | 
    
         
             
                    else
         
     | 
| 
       50 
50 
     | 
    
         
             
                      Relationship.new(
         
     | 
| 
       51 
     | 
    
         
            -
                         
     | 
| 
      
 51 
     | 
    
         
            +
                        name,
         
     | 
| 
       52 
52 
     | 
    
         
             
                        repository_name,
         
     | 
| 
       53 
53 
     | 
    
         
             
                        options.fetch(:class_name, Extlib::Inflection.classify(name)),
         
     | 
| 
       54 
     | 
    
         
            -
                        model 
     | 
| 
      
 54 
     | 
    
         
            +
                        model,
         
     | 
| 
       55 
55 
     | 
    
         
             
                        options
         
     | 
| 
       56 
56 
     | 
    
         
             
                      )
         
     | 
| 
       57 
57 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -5,75 +5,166 @@ module DataMapper 
     | 
|
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
                  OPTIONS = [ :class_name, :child_key, :parent_key, :min, :max, :through ]
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
                   
     | 
| 
      
 8 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 9 
     | 
    
         
            +
                  attr_reader :name, :options, :query
         
     | 
| 
       9 
10 
     | 
    
         | 
| 
      
 11 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       10 
12 
     | 
    
         
             
                  def child_key
         
     | 
| 
       11 
13 
     | 
    
         
             
                    @child_key ||= begin
         
     | 
| 
       12 
     | 
    
         
            -
                       
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
                         
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
                           
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
                           
     | 
| 
       22 
     | 
    
         
            -
                             
     | 
| 
       23 
     | 
    
         
            -
                           
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
                             
     | 
| 
      
 14 
     | 
    
         
            +
                      child_key = nil
         
     | 
| 
      
 15 
     | 
    
         
            +
                      child_model.repository.scope do |r|
         
     | 
| 
      
 16 
     | 
    
         
            +
                        model_properties = child_model.properties(r.name)
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                        child_key = parent_key.zip(@child_properties || []).map do |parent_property,property_name|
         
     | 
| 
      
 19 
     | 
    
         
            +
                          # TODO: use something similar to DM::NamingConventions to determine the property name
         
     | 
| 
      
 20 
     | 
    
         
            +
                          parent_name = Extlib::Inflection.underscore(Extlib::Inflection.demodulize(parent_model.base_model.name))
         
     | 
| 
      
 21 
     | 
    
         
            +
                          property_name ||= "#{parent_name}_#{parent_property.name}".to_sym
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                          if model_properties.has_property?(property_name)
         
     | 
| 
      
 24 
     | 
    
         
            +
                            model_properties[property_name]
         
     | 
| 
      
 25 
     | 
    
         
            +
                          else
         
     | 
| 
      
 26 
     | 
    
         
            +
                            options = {}
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                            [ :length, :precision, :scale ].each do |option|
         
     | 
| 
      
 29 
     | 
    
         
            +
                              options[option] = parent_property.send(option)
         
     | 
| 
      
 30 
     | 
    
         
            +
                            end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                            # NOTE: hack to make each many to many child_key a true key,
         
     | 
| 
      
 33 
     | 
    
         
            +
                            # until I can figure out a better place for this check
         
     | 
| 
      
 34 
     | 
    
         
            +
                            if child_model.respond_to?(:many_to_many)
         
     | 
| 
      
 35 
     | 
    
         
            +
                              options[:key] = true
         
     | 
| 
      
 36 
     | 
    
         
            +
                            end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                            child_model.property(property_name, parent_property.primitive, options)
         
     | 
| 
       29 
39 
     | 
    
         
             
                          end
         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
                          child_model.property(property_name, parent_property.primitive, attributes)
         
     | 
| 
       32 
40 
     | 
    
         
             
                        end
         
     | 
| 
       33 
41 
     | 
    
         
             
                      end
         
     | 
| 
       34 
42 
     | 
    
         
             
                      PropertySet.new(child_key)
         
     | 
| 
       35 
43 
     | 
    
         
             
                    end
         
     | 
| 
       36 
44 
     | 
    
         
             
                  end
         
     | 
| 
       37 
45 
     | 
    
         | 
| 
      
 46 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       38 
47 
     | 
    
         
             
                  def parent_key
         
     | 
| 
       39 
48 
     | 
    
         
             
                    @parent_key ||= begin
         
     | 
| 
       40 
     | 
    
         
            -
                      parent_key =  
     | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
      
 49 
     | 
    
         
            +
                      parent_key = nil
         
     | 
| 
      
 50 
     | 
    
         
            +
                      parent_model.repository.scope do |r|
         
     | 
| 
      
 51 
     | 
    
         
            +
                        parent_key = if @parent_properties
         
     | 
| 
      
 52 
     | 
    
         
            +
                          parent_model.properties(r.name).slice(*@parent_properties)
         
     | 
| 
      
 53 
     | 
    
         
            +
                        else
         
     | 
| 
      
 54 
     | 
    
         
            +
                          parent_model.key
         
     | 
| 
      
 55 
     | 
    
         
            +
                        end
         
     | 
| 
       44 
56 
     | 
    
         
             
                      end
         
     | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
       46 
57 
     | 
    
         
             
                      PropertySet.new(parent_key)
         
     | 
| 
       47 
58 
     | 
    
         
             
                    end
         
     | 
| 
       48 
59 
     | 
    
         
             
                  end
         
     | 
| 
       49 
60 
     | 
    
         | 
| 
      
 61 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       50 
62 
     | 
    
         
             
                  def parent_model
         
     | 
| 
       51 
     | 
    
         
            -
                    Class === @parent_model ? @parent_model :  
     | 
| 
      
 63 
     | 
    
         
            +
                    Class === @parent_model ? @parent_model : (Class === @child_model ? @child_model.find_const(@parent_model) : Object.find_const(@parent_model))
         
     | 
| 
      
 64 
     | 
    
         
            +
                  rescue NameError
         
     | 
| 
      
 65 
     | 
    
         
            +
                    raise NameError, "Cannot find the parent_model #{@parent_model} for #{@child_model}"
         
     | 
| 
       52 
66 
     | 
    
         
             
                  end
         
     | 
| 
       53 
67 
     | 
    
         | 
| 
      
 68 
     | 
    
         
            +
                  # @api private
         
     | 
| 
       54 
69 
     | 
    
         
             
                  def child_model
         
     | 
| 
       55 
     | 
    
         
            -
                    Class === @child_model ? @child_model :  
     | 
| 
      
 70 
     | 
    
         
            +
                    Class === @child_model ? @child_model : (Class === @parent_model ? @parent_model.find_const(@child_model) : Object.find_const(@child_model))
         
     | 
| 
      
 71 
     | 
    
         
            +
                  rescue NameError
         
     | 
| 
      
 72 
     | 
    
         
            +
                    raise NameError, "Cannot find the child_model #{@child_model} for #{@parent_model}"
         
     | 
| 
       56 
73 
     | 
    
         
             
                  end
         
     | 
| 
       57 
74 
     | 
    
         | 
| 
       58 
75 
     | 
    
         
             
                  # @api private
         
     | 
| 
       59 
76 
     | 
    
         
             
                  def get_children(parent, options = {}, finder = :all, *args)
         
     | 
| 
       60 
     | 
    
         
            -
                    bind_values 
     | 
| 
       61 
     | 
    
         
            -
                     
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
      
 77 
     | 
    
         
            +
                    bind_values   = parent_key.get(parent)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    parent_values = bind_values.dup
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                    with_repository(child_model) do |r|
         
     | 
| 
      
 81 
     | 
    
         
            +
                      parent_identity_map = parent.repository.identity_map(parent_model)
         
     | 
| 
      
 82 
     | 
    
         
            +
                      child_identity_map  = r.identity_map(child_model)
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                      query_values = parent_identity_map.keys.flatten
         
     | 
| 
      
 85 
     | 
    
         
            +
                      query_values.reject! { |k| child_identity_map[[k]] }
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                      bind_values = query_values unless query_values.empty?
         
     | 
| 
      
 88 
     | 
    
         
            +
                      query = child_key.map { |k| [ k, bind_values ] }.to_hash
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                      collection = child_model.send(finder, *(args.dup << @query.merge(options).merge(query)))
         
     | 
| 
      
 91 
     | 
    
         
            +
                      return collection unless collection.kind_of?(Collection) && collection.any?
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                      grouped_collection = Hash.new { |h,k| h[k] = [] }
         
     | 
| 
      
 94 
     | 
    
         
            +
                      collection.each do |resource|
         
     | 
| 
      
 95 
     | 
    
         
            +
                        grouped_collection[get_parent(resource, parent)] << resource
         
     | 
| 
      
 96 
     | 
    
         
            +
                      end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                      association_accessor = "#{self.name}_association"
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                      ret = nil
         
     | 
| 
      
 101 
     | 
    
         
            +
                      grouped_collection.each do |parent, children|
         
     | 
| 
      
 102 
     | 
    
         
            +
                        association = parent.send(association_accessor)
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
                        query = collection.query.dup
         
     | 
| 
      
 105 
     | 
    
         
            +
                        query.conditions.map! do |operator, property, bind_value|
         
     | 
| 
      
 106 
     | 
    
         
            +
                          if child_key.has_property?(property.name)
         
     | 
| 
      
 107 
     | 
    
         
            +
                            bind_value = *children.map { |child| property.get(child) }.uniq
         
     | 
| 
      
 108 
     | 
    
         
            +
                          end
         
     | 
| 
      
 109 
     | 
    
         
            +
                          [ operator, property, bind_value ]
         
     | 
| 
      
 110 
     | 
    
         
            +
                        end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                        parents_children = Collection.new(query) do |collection|
         
     | 
| 
      
 113 
     | 
    
         
            +
                          children.each { |child| collection.send(:add, child) }
         
     | 
| 
      
 114 
     | 
    
         
            +
                        end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                        if parent_key.get(parent) == parent_values
         
     | 
| 
      
 117 
     | 
    
         
            +
                          ret = parents_children
         
     | 
| 
      
 118 
     | 
    
         
            +
                        else
         
     | 
| 
      
 119 
     | 
    
         
            +
                          association.instance_variable_set(:@children, parents_children)
         
     | 
| 
      
 120 
     | 
    
         
            +
                        end
         
     | 
| 
      
 121 
     | 
    
         
            +
                      end
         
     | 
| 
       63 
122 
     | 
    
         | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
                      child_model.send(finder, *(args << @query.merge(options).merge(query)))
         
     | 
| 
      
 123 
     | 
    
         
            +
                      ret || child_model.send(finder, *(args.dup << @query.merge(options).merge(child_key.zip(parent_values).to_hash)))
         
     | 
| 
       66 
124 
     | 
    
         
             
                    end
         
     | 
| 
       67 
125 
     | 
    
         
             
                  end
         
     | 
| 
       68 
126 
     | 
    
         | 
| 
       69 
127 
     | 
    
         
             
                  # @api private
         
     | 
| 
       70 
     | 
    
         
            -
                  def get_parent(child)
         
     | 
| 
       71 
     | 
    
         
            -
                     
     | 
| 
       72 
     | 
    
         
            -
                    return nil  
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
      
 128 
     | 
    
         
            +
                  def get_parent(child, parent = nil)
         
     | 
| 
      
 129 
     | 
    
         
            +
                    child_value = child_key.get(child)
         
     | 
| 
      
 130 
     | 
    
         
            +
                    return nil unless child_value.nitems == child_value.size
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                    with_repository(parent || parent_model) do
         
     | 
| 
      
 133 
     | 
    
         
            +
                      parent_identity_map = (parent || parent_model).repository.identity_map(parent_model)
         
     | 
| 
      
 134 
     | 
    
         
            +
                      child_identity_map  = child.repository.identity_map(child_model)
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                      if parent = parent_identity_map[child_value]
         
     | 
| 
      
 137 
     | 
    
         
            +
                        return parent
         
     | 
| 
      
 138 
     | 
    
         
            +
                      end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                      children = child_identity_map.values
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
                      bind_values = *children.map { |c| child_key.get(c) }.uniq
         
     | 
| 
      
 143 
     | 
    
         
            +
                      query_values = bind_values.reject { |k| parent_identity_map[[k]] }
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
                      bind_values = query_values unless query_values.empty?
         
     | 
| 
      
 146 
     | 
    
         
            +
                      query = parent_key.map { |k| [ k, bind_values ] }.to_hash
         
     | 
| 
       74 
147 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
      
 148 
     | 
    
         
            +
                      association_accessor = "#{self.name}_association"
         
     | 
| 
      
 149 
     | 
    
         
            +
             
     | 
| 
      
 150 
     | 
    
         
            +
                      collection = parent_model.send(:all, query)
         
     | 
| 
      
 151 
     | 
    
         
            +
                      collection.send(:lazy_load)
         
     | 
| 
      
 152 
     | 
    
         
            +
                      children.each do |c|
         
     | 
| 
      
 153 
     | 
    
         
            +
                        c.send(association_accessor).instance_variable_set(:@parent, collection.get(*child_key.get(c)))
         
     | 
| 
      
 154 
     | 
    
         
            +
                      end
         
     | 
| 
      
 155 
     | 
    
         
            +
                      child.send(association_accessor).instance_variable_get(:@parent)
         
     | 
| 
      
 156 
     | 
    
         
            +
                    end
         
     | 
| 
      
 157 
     | 
    
         
            +
                  end
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 160 
     | 
    
         
            +
                  def with_repository(object = nil, &block)
         
     | 
| 
      
 161 
     | 
    
         
            +
                    other_model = object.model == child_model ? parent_model : child_model if object.respond_to?(:model)
         
     | 
| 
      
 162 
     | 
    
         
            +
                    other_model = object       == child_model ? parent_model : child_model if object.kind_of?(DataMapper::Resource)
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
                    if other_model && other_model.repository == object.repository && object.repository.name != @repository_name
         
     | 
| 
      
 165 
     | 
    
         
            +
                      object.repository.scope(&block)
         
     | 
| 
      
 166 
     | 
    
         
            +
                    else
         
     | 
| 
      
 167 
     | 
    
         
            +
                      repository(@repository_name, &block)
         
     | 
| 
       77 
168 
     | 
    
         
             
                    end
         
     | 
| 
       78 
169 
     | 
    
         
             
                  end
         
     | 
| 
       79 
170 
     | 
    
         | 
| 
         @@ -89,10 +180,10 @@ module DataMapper 
     | 
|
| 
       89 
180 
     | 
    
         
             
                  # http://edocs.bea.com/kodo/docs41/full/html/jdo_overview_mapping_join.html
         
     | 
| 
       90 
181 
     | 
    
         
             
                  # I wash my hands of it!
         
     | 
| 
       91 
182 
     | 
    
         
             
                  def initialize(name, repository_name, child_model, parent_model, options = {})
         
     | 
| 
       92 
     | 
    
         
            -
                    assert_kind_of 'name', 
     | 
| 
       93 
     | 
    
         
            -
                    assert_kind_of 'repository_name', 
     | 
| 
       94 
     | 
    
         
            -
                    assert_kind_of 'child_model', 
     | 
| 
       95 
     | 
    
         
            -
                    assert_kind_of 'parent_model', 
     | 
| 
      
 183 
     | 
    
         
            +
                    assert_kind_of 'name',            name,            Symbol
         
     | 
| 
      
 184 
     | 
    
         
            +
                    assert_kind_of 'repository_name', repository_name, Symbol
         
     | 
| 
      
 185 
     | 
    
         
            +
                    assert_kind_of 'child_model',     child_model,     String, Class
         
     | 
| 
      
 186 
     | 
    
         
            +
                    assert_kind_of 'parent_model',    parent_model,    String, Class
         
     | 
| 
       96 
187 
     | 
    
         | 
| 
       97 
188 
     | 
    
         
             
                    if child_properties = options[:child_key]
         
     | 
| 
       98 
189 
     | 
    
         
             
                      assert_kind_of 'options[:child_key]', child_properties, Array
         
     | 
| 
         @@ -110,6 +201,17 @@ module DataMapper 
     | 
|
| 
       110 
201 
     | 
    
         
             
                    @parent_model      = parent_model
         
     | 
| 
       111 
202 
     | 
    
         
             
                    @parent_properties = parent_properties  # may be nil
         
     | 
| 
       112 
203 
     | 
    
         
             
                    @options           = options
         
     | 
| 
      
 204 
     | 
    
         
            +
             
     | 
| 
      
 205 
     | 
    
         
            +
                    # attempt to load the child_key if the parent and child model constants are defined
         
     | 
| 
      
 206 
     | 
    
         
            +
                    if model_defined?(@child_model) && model_defined?(@parent_model)
         
     | 
| 
      
 207 
     | 
    
         
            +
                      child_key
         
     | 
| 
      
 208 
     | 
    
         
            +
                    end
         
     | 
| 
      
 209 
     | 
    
         
            +
                  end
         
     | 
| 
      
 210 
     | 
    
         
            +
             
     | 
| 
      
 211 
     | 
    
         
            +
                  # @api private
         
     | 
| 
      
 212 
     | 
    
         
            +
                  def model_defined?(model)
         
     | 
| 
      
 213 
     | 
    
         
            +
                    # TODO: figure out other ways to see if the model is loaded
         
     | 
| 
      
 214 
     | 
    
         
            +
                    model.kind_of?(Class)
         
     | 
| 
       113 
215 
     | 
    
         
             
                  end
         
     | 
| 
       114 
216 
     | 
    
         
             
                end # class Relationship
         
     | 
| 
       115 
217 
     | 
    
         
             
              end # module Associations
         
     |