public
Fork of sam/dm-core
Description: DataMapper - Core
Homepage: http://datamapper.org
Clone URL: git://github.com/somebee/dm-core.git
somebee (author)
Wed May 14 18:08:25 -0700 2008
commit  1c7c5ba4893a9d19b3b84e95bb3f6f92de00f802
tree    d086b2ddb75adad02f00ffb91eb61cc4941a8029
parent  6160981583de998b75d7ae925c1864c77370e176
dm-core / lib / data_mapper / associations / one_to_many.rb
100644 151 lines (123 sloc) 4.466 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
require 'forwardable'
 
module DataMapper
  module Associations
    module OneToMany
      OPTIONS = [ :class_name, :child_key, :parent_key, :min, :max ]
 
      private
 
      def one_to_many(name, options = {})
        raise ArgumentError, "+name+ should be a Symbol (or Hash for +through+ support), but was #{name.class}", caller unless Symbol === name || Hash === name
        raise ArgumentError, "+options+ should be a Hash, but was #{options.class}", caller unless Hash === options
 
        child_model_name = options.fetch(:class_name, DataMapper::Inflection.classify(name))
 
        relationship = relationships(repository.name)[name] = Relationship.new(
          DataMapper::Inflection.underscore(self.name.split('::').last).to_sym,
          repository.name,
          child_model_name,
          self.name,
          options
        )
 
        class_eval <<-EOS, __FILE__, __LINE__
def #{name}
@#{name}_association ||= begin
relationship = self.class.relationships(repository.name)[#{name.inspect}]
raise ArgumentError.new("Relationship #{name.inspect} does not exist") unless relationship
association = Proxy.new(relationship, self)
parent_associations << association
association
end
end
 
def #{name}=(children)
#{name}.replace(children)
end
EOS
 
        relationship
      end
 
      class Proxy
        instance_methods.each { |m| undef_method m unless %w[ __id__ __send__ class kind_of? should should_not ].include?(m) }
 
        def replace(resources)
          each { |resource| remove_resource(resource) }
          append_resource(resources)
          children.replace(resources)
          self
        end
 
        def push(*resources)
          append_resource(resources)
          children.push(*resources)
          self
        end
 
        def unshift(*resources)
          append_resource(resources)
          children.unshift(*resources)
          self
        end
 
        def <<(resource)
          append_resource([ resource ])
          children << resource
          self
        end
 
        def pop
          remove_resource(children.pop)
        end
 
        def shift
          remove_resource(children.shift)
        end
 
        def delete(resource, &block)
          remove_resource(children.delete(resource, &block))
        end
 
        def delete_at(index)
          remove_resource(children.delete_at(index))
        end
 
        def clear
          each { |resource| remove_resource(resource) }
          children.clear
          self
        end
 
        def save
          save_resources(@dirty_children)
          @dirty_children = []
          self
        end
 
        private
 
        def initialize(relationship, parent_resource)
# raise ArgumentError, "+relationship+ should be a DataMapper::Association::Relationship, but was #{relationship.class}", caller unless Relationship === relationship
# raise ArgumentError, "+parent_resource+ should be a DataMapper::Resource, but was #{parent_resource.class}", caller unless Resource === parent_resource
 
          @relationship = relationship
          @parent_resource = parent_resource
          @dirty_children = []
        end
 
        def children
          @children ||= @relationship.get_children(@parent_resource)
        end
 
        def remove_resource(resource)
          begin
            repository(@relationship.repository_name) do
              @relationship.attach_parent(resource, nil)
              resource.save
            end
          rescue
            children << resource
            raise
          end
          resource
        end
 
        def append_resource(resources = [])
          if @parent_resource.new_record?
            @dirty_children.push(*resources)
          else
            save_resources(resources)
          end
        end
 
        def save_resources(resources = [])
          repository(@parent_resource.repository.name) do
            resources.each do |resource|
              @relationship.attach_parent(resource, @parent_resource)
              resource.save
            end
          end
        end
 
        def method_missing(method, *args, &block)
          children.__send__(method, *args, &block)
        end
      end # class Proxy
    end # module OneToMany
  end # module Associations
end # module DataMapper