datamapper-dm-core 0.9.11 → 0.10.0

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.
Files changed (192) hide show
  1. data/.autotest +17 -14
  2. data/.gitignore +3 -1
  3. data/FAQ +6 -5
  4. data/History.txt +5 -39
  5. data/Manifest.txt +67 -76
  6. data/QUICKLINKS +1 -1
  7. data/README.txt +21 -15
  8. data/Rakefile +16 -15
  9. data/SPECS +2 -29
  10. data/TODO +1 -1
  11. data/dm-core.gemspec +11 -15
  12. data/lib/dm-core/adapters/abstract_adapter.rb +182 -185
  13. data/lib/dm-core/adapters/data_objects_adapter.rb +482 -534
  14. data/lib/dm-core/adapters/in_memory_adapter.rb +90 -69
  15. data/lib/dm-core/adapters/mysql_adapter.rb +22 -115
  16. data/lib/dm-core/adapters/oracle_adapter.rb +249 -0
  17. data/lib/dm-core/adapters/postgres_adapter.rb +7 -173
  18. data/lib/dm-core/adapters/sqlite3_adapter.rb +4 -97
  19. data/lib/dm-core/adapters/yaml_adapter.rb +116 -0
  20. data/lib/dm-core/adapters.rb +135 -16
  21. data/lib/dm-core/associations/many_to_many.rb +372 -90
  22. data/lib/dm-core/associations/many_to_one.rb +220 -73
  23. data/lib/dm-core/associations/one_to_many.rb +319 -255
  24. data/lib/dm-core/associations/one_to_one.rb +66 -53
  25. data/lib/dm-core/associations/relationship.rb +560 -158
  26. data/lib/dm-core/collection.rb +1104 -381
  27. data/lib/dm-core/core_ext/kernel.rb +12 -0
  28. data/lib/dm-core/core_ext/symbol.rb +10 -0
  29. data/lib/dm-core/identity_map.rb +4 -34
  30. data/lib/dm-core/migrations.rb +1283 -0
  31. data/lib/dm-core/model/descendant_set.rb +81 -0
  32. data/lib/dm-core/model/hook.rb +45 -0
  33. data/lib/dm-core/model/is.rb +32 -0
  34. data/lib/dm-core/model/property.rb +248 -0
  35. data/lib/dm-core/model/relationship.rb +335 -0
  36. data/lib/dm-core/model/scope.rb +90 -0
  37. data/lib/dm-core/model.rb +570 -369
  38. data/lib/dm-core/property.rb +753 -280
  39. data/lib/dm-core/property_set.rb +141 -98
  40. data/lib/dm-core/query/conditions/comparison.rb +814 -0
  41. data/lib/dm-core/query/conditions/operation.rb +247 -0
  42. data/lib/dm-core/query/direction.rb +43 -0
  43. data/lib/dm-core/query/operator.rb +42 -0
  44. data/lib/dm-core/query/path.rb +102 -0
  45. data/lib/dm-core/query/sort.rb +45 -0
  46. data/lib/dm-core/query.rb +974 -492
  47. data/lib/dm-core/repository.rb +147 -107
  48. data/lib/dm-core/resource.rb +644 -429
  49. data/lib/dm-core/spec/adapter_shared_spec.rb +294 -0
  50. data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +106 -0
  51. data/lib/dm-core/support/chainable.rb +20 -0
  52. data/lib/dm-core/support/deprecate.rb +12 -0
  53. data/lib/dm-core/support/equalizer.rb +23 -0
  54. data/lib/dm-core/support/logger.rb +13 -0
  55. data/lib/dm-core/{naming_conventions.rb → support/naming_conventions.rb} +6 -6
  56. data/lib/dm-core/transaction.rb +333 -92
  57. data/lib/dm-core/type.rb +98 -60
  58. data/lib/dm-core/types/boolean.rb +1 -1
  59. data/lib/dm-core/types/discriminator.rb +34 -20
  60. data/lib/dm-core/types/object.rb +7 -4
  61. data/lib/dm-core/types/paranoid_boolean.rb +11 -9
  62. data/lib/dm-core/types/paranoid_datetime.rb +11 -9
  63. data/lib/dm-core/types/serial.rb +3 -3
  64. data/lib/dm-core/types/text.rb +3 -4
  65. data/lib/dm-core/version.rb +1 -1
  66. data/lib/dm-core.rb +106 -110
  67. data/script/performance.rb +102 -109
  68. data/script/profile.rb +169 -38
  69. data/spec/lib/adapter_helpers.rb +105 -0
  70. data/spec/lib/collection_helpers.rb +18 -0
  71. data/spec/lib/counter_adapter.rb +34 -0
  72. data/spec/lib/pending_helpers.rb +27 -0
  73. data/spec/lib/rspec_immediate_feedback_formatter.rb +53 -0
  74. data/spec/public/associations/many_to_many_spec.rb +193 -0
  75. data/spec/public/associations/many_to_one_spec.rb +73 -0
  76. data/spec/public/associations/one_to_many_spec.rb +77 -0
  77. data/spec/public/associations/one_to_one_spec.rb +156 -0
  78. data/spec/public/collection_spec.rb +65 -0
  79. data/spec/public/model/relationship_spec.rb +924 -0
  80. data/spec/public/model_spec.rb +159 -0
  81. data/spec/public/property_spec.rb +829 -0
  82. data/spec/public/resource_spec.rb +71 -0
  83. data/spec/public/sel_spec.rb +44 -0
  84. data/spec/public/setup_spec.rb +145 -0
  85. data/spec/public/shared/association_collection_shared_spec.rb +317 -0
  86. data/spec/public/shared/collection_shared_spec.rb +1723 -0
  87. data/spec/public/shared/finder_shared_spec.rb +1619 -0
  88. data/spec/public/shared/resource_shared_spec.rb +924 -0
  89. data/spec/public/shared/sel_shared_spec.rb +112 -0
  90. data/spec/public/transaction_spec.rb +129 -0
  91. data/spec/public/types/discriminator_spec.rb +130 -0
  92. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  93. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +12 -0
  94. data/spec/semipublic/adapters/mysql_adapter_spec.rb +17 -0
  95. data/spec/semipublic/adapters/oracle_adapter_spec.rb +194 -0
  96. data/spec/semipublic/adapters/postgres_adapter_spec.rb +17 -0
  97. data/spec/semipublic/adapters/sqlite3_adapter_spec.rb +17 -0
  98. data/spec/semipublic/adapters/yaml_adapter_spec.rb +12 -0
  99. data/spec/semipublic/associations/many_to_one_spec.rb +53 -0
  100. data/spec/semipublic/associations/relationship_spec.rb +194 -0
  101. data/spec/semipublic/associations_spec.rb +177 -0
  102. data/spec/semipublic/collection_spec.rb +142 -0
  103. data/spec/semipublic/property_spec.rb +61 -0
  104. data/spec/semipublic/query/conditions_spec.rb +528 -0
  105. data/spec/semipublic/query/path_spec.rb +443 -0
  106. data/spec/semipublic/query_spec.rb +2626 -0
  107. data/spec/semipublic/resource_spec.rb +47 -0
  108. data/spec/semipublic/shared/resource_shared_spec.rb +126 -0
  109. data/spec/spec.opts +3 -1
  110. data/spec/spec_helper.rb +80 -57
  111. data/tasks/ci.rb +19 -31
  112. data/tasks/dm.rb +43 -48
  113. data/tasks/doc.rb +8 -11
  114. data/tasks/gemspec.rb +5 -5
  115. data/tasks/hoe.rb +15 -16
  116. data/tasks/install.rb +8 -10
  117. metadata +72 -93
  118. data/lib/dm-core/associations/relationship_chain.rb +0 -81
  119. data/lib/dm-core/associations.rb +0 -207
  120. data/lib/dm-core/auto_migrations.rb +0 -105
  121. data/lib/dm-core/dependency_queue.rb +0 -32
  122. data/lib/dm-core/hook.rb +0 -11
  123. data/lib/dm-core/is.rb +0 -16
  124. data/lib/dm-core/logger.rb +0 -232
  125. data/lib/dm-core/migrations/destructive_migrations.rb +0 -17
  126. data/lib/dm-core/migrator.rb +0 -29
  127. data/lib/dm-core/scope.rb +0 -58
  128. data/lib/dm-core/support/array.rb +0 -13
  129. data/lib/dm-core/support/assertions.rb +0 -8
  130. data/lib/dm-core/support/errors.rb +0 -23
  131. data/lib/dm-core/support/kernel.rb +0 -11
  132. data/lib/dm-core/support/symbol.rb +0 -41
  133. data/lib/dm-core/support.rb +0 -7
  134. data/lib/dm-core/type_map.rb +0 -80
  135. data/lib/dm-core/types.rb +0 -19
  136. data/script/all +0 -4
  137. data/spec/integration/association_spec.rb +0 -1382
  138. data/spec/integration/association_through_spec.rb +0 -203
  139. data/spec/integration/associations/many_to_many_spec.rb +0 -449
  140. data/spec/integration/associations/many_to_one_spec.rb +0 -163
  141. data/spec/integration/associations/one_to_many_spec.rb +0 -188
  142. data/spec/integration/auto_migrations_spec.rb +0 -413
  143. data/spec/integration/collection_spec.rb +0 -1073
  144. data/spec/integration/data_objects_adapter_spec.rb +0 -32
  145. data/spec/integration/dependency_queue_spec.rb +0 -46
  146. data/spec/integration/model_spec.rb +0 -197
  147. data/spec/integration/mysql_adapter_spec.rb +0 -85
  148. data/spec/integration/postgres_adapter_spec.rb +0 -731
  149. data/spec/integration/property_spec.rb +0 -253
  150. data/spec/integration/query_spec.rb +0 -514
  151. data/spec/integration/repository_spec.rb +0 -61
  152. data/spec/integration/resource_spec.rb +0 -513
  153. data/spec/integration/sqlite3_adapter_spec.rb +0 -352
  154. data/spec/integration/sti_spec.rb +0 -273
  155. data/spec/integration/strategic_eager_loading_spec.rb +0 -156
  156. data/spec/integration/transaction_spec.rb +0 -75
  157. data/spec/integration/type_spec.rb +0 -275
  158. data/spec/lib/logging_helper.rb +0 -18
  159. data/spec/lib/mock_adapter.rb +0 -27
  160. data/spec/lib/model_loader.rb +0 -100
  161. data/spec/lib/publicize_methods.rb +0 -28
  162. data/spec/models/content.rb +0 -16
  163. data/spec/models/vehicles.rb +0 -34
  164. data/spec/models/zoo.rb +0 -48
  165. data/spec/unit/adapters/abstract_adapter_spec.rb +0 -133
  166. data/spec/unit/adapters/adapter_shared_spec.rb +0 -15
  167. data/spec/unit/adapters/data_objects_adapter_spec.rb +0 -632
  168. data/spec/unit/adapters/in_memory_adapter_spec.rb +0 -98
  169. data/spec/unit/adapters/postgres_adapter_spec.rb +0 -133
  170. data/spec/unit/associations/many_to_many_spec.rb +0 -32
  171. data/spec/unit/associations/many_to_one_spec.rb +0 -159
  172. data/spec/unit/associations/one_to_many_spec.rb +0 -393
  173. data/spec/unit/associations/one_to_one_spec.rb +0 -7
  174. data/spec/unit/associations/relationship_spec.rb +0 -71
  175. data/spec/unit/associations_spec.rb +0 -242
  176. data/spec/unit/auto_migrations_spec.rb +0 -111
  177. data/spec/unit/collection_spec.rb +0 -182
  178. data/spec/unit/data_mapper_spec.rb +0 -35
  179. data/spec/unit/identity_map_spec.rb +0 -126
  180. data/spec/unit/is_spec.rb +0 -80
  181. data/spec/unit/migrator_spec.rb +0 -33
  182. data/spec/unit/model_spec.rb +0 -321
  183. data/spec/unit/naming_conventions_spec.rb +0 -36
  184. data/spec/unit/property_set_spec.rb +0 -90
  185. data/spec/unit/property_spec.rb +0 -753
  186. data/spec/unit/query_spec.rb +0 -571
  187. data/spec/unit/repository_spec.rb +0 -93
  188. data/spec/unit/resource_spec.rb +0 -649
  189. data/spec/unit/scope_spec.rb +0 -142
  190. data/spec/unit/transaction_spec.rb +0 -493
  191. data/spec/unit/type_map_spec.rb +0 -114
  192. data/spec/unit/type_spec.rb +0 -119
@@ -1,107 +1,254 @@
1
1
  module DataMapper
2
2
  module Associations
3
- module ManyToOne
4
- extend Assertions
5
-
6
- # Setup many to one relationship between two models
7
- # -
8
- # @api private
9
- def self.setup(name, model, options = {})
10
- assert_kind_of 'name', name, Symbol
11
- assert_kind_of 'model', model, Model
12
- assert_kind_of 'options', options, Hash
13
-
14
- repository_name = model.repository.name
15
-
16
- model.class_eval <<-EOS, __FILE__, __LINE__
17
- def #{name}
18
- #{name}_association.nil? ? nil : #{name}_association
19
- end
3
+ module ManyToOne #:nodoc:
4
+ # Relationship class with implementation specific
5
+ # to n side of 1 to n association
6
+ class Relationship < Associations::Relationship
7
+ OPTIONS = superclass::OPTIONS.dup << :nullable
8
+
9
+ # TODO: document
10
+ # @api semipublic
11
+ alias source_repository_name child_repository_name
12
+
13
+ # TODO: document
14
+ # @api semipublic
15
+ alias source_model child_model
16
+
17
+ # TODO: document
18
+ # @api semipublic
19
+ alias target_repository_name parent_repository_name
20
+
21
+ # TODO: document
22
+ # @api semipublic
23
+ alias target_model parent_model
24
+
25
+ # TODO: document
26
+ # @api semipublic
27
+ alias target_key parent_key
28
+
29
+ # TODO: document
30
+ # @api semipublic
31
+ def nullable?
32
+ @nullable
33
+ end
20
34
 
21
- def #{name}=(parent)
22
- #{name}_association.replace(parent)
23
- end
35
+ # Returns a set of keys that identify child model
36
+ #
37
+ # @return [DataMapper::PropertySet] a set of properties that identify child model
38
+ # @api private
39
+ def child_key
40
+ return @child_key if defined?(@child_key)
41
+
42
+ repository_name = child_repository_name || parent_repository_name
43
+ properties = child_model.properties(repository_name)
24
44
 
25
- private
45
+ child_key = parent_key.zip(@child_properties || []).map do |parent_property, property_name|
46
+ property_name ||= "#{name}_#{parent_property.name}".to_sym
26
47
 
27
- def #{name}_association
28
- @#{name}_association ||= begin
29
- unless relationship = model.relationships(#{repository_name.inspect})[:#{name}]
30
- raise ArgumentError, "Relationship #{name.inspect} does not exist in \#{model}"
48
+ properties[property_name] || begin
49
+ # create the property within the correct repository
50
+ DataMapper.repository(repository_name) do
51
+ type = parent_property.send(parent_property.type == DataMapper::Types::Boolean ? :type : :primitive)
52
+ child_model.property(property_name, type, child_key_options(parent_property))
31
53
  end
32
- association = Proxy.new(relationship, self)
33
- child_associations << association
34
- association
35
54
  end
36
55
  end
37
- EOS
38
-
39
- model.relationships(repository_name)[name] = Relationship.new(
40
- name,
41
- repository_name,
42
- model,
43
- options.fetch(:class_name, Extlib::Inflection.classify(name)),
44
- options
45
- )
46
- end
47
-
48
- class Proxy
49
- include Assertions
50
56
 
51
- instance_methods.each { |m| undef_method m unless %w[ __id__ __send__ object_id kind_of? respond_to? assert_kind_of should should_not instance_variable_set instance_variable_get ].include?(m.to_s) }
57
+ @child_key = properties.class.new(child_key).freeze
58
+ end
52
59
 
53
- def replace(parent)
54
- @parent = parent
55
- @relationship.attach_parent(@child, @parent)
56
- self
60
+ # TODO: document
61
+ # @api semipublic
62
+ alias source_key child_key
63
+
64
+ # Returns a Resoruce for this relationship with a given source
65
+ #
66
+ # @param [Resource] source
67
+ # A Resource to scope the collection with
68
+ # @param [Query] other_query (optional)
69
+ # A Query to further scope the collection with
70
+ #
71
+ # @return [Resource]
72
+ # The resource scoped to the relationship, source and query
73
+ #
74
+ # @api private
75
+ def resource_for(source, other_query = nil)
76
+ query = query_for(source, other_query)
77
+
78
+ # TODO: lookup the resource in the Identity Map, and make sure
79
+ # it matches the query criteria, otherwise perform the query
80
+
81
+ target_model.first(query)
57
82
  end
58
83
 
59
- def save
60
- return false if @parent.nil?
61
- return true unless parent.new_record?
84
+ # Loads and returns association target (ex.: author) for given source resource
85
+ # (ex.: article)
86
+ #
87
+ # @param source [DataMapper::Resource]
88
+ # Child object (ex.: instance of article)
89
+ # @param other_query [DataMapper::Query]
90
+ # Query options
91
+ #
92
+ # @api semipublic
93
+ def get(source, other_query = nil)
94
+ assert_kind_of 'source', source, source_model
95
+
96
+ lazy_load(source) unless loaded?(source)
97
+
98
+ resource = get!(source)
99
+ if other_query.nil? || query_for(source, other_query).conditions.matches?(resource)
100
+ resource
101
+ end
102
+ end
62
103
 
63
- @relationship.with_repository(parent) do
64
- result = parent.save
65
- @relationship.child_key.set(@child, @relationship.parent_key.get(parent)) if result
66
- result
104
+ # Sets value of association target (ex.: author) for given source resource
105
+ # (ex.: article)
106
+ #
107
+ # @param source [DataMapper::Resource]
108
+ # Child object (ex.: instance of article)
109
+ #
110
+ # @param source [DataMapper::Resource]
111
+ # Parent object (ex.: instance of author)
112
+ #
113
+ # @api semipublic
114
+ def set(source, target)
115
+ assert_kind_of 'source', source, source_model
116
+ assert_kind_of 'target', target, target_model, Hash, NilClass
117
+
118
+ if target.kind_of?(Hash)
119
+ target = target_model.new(target)
67
120
  end
121
+
122
+ source_key.set(source, target.nil? ? [] : target_key.get(target))
123
+ set!(source, target)
68
124
  end
69
125
 
70
- def reload
71
- @parent = nil
72
- self
126
+ # TODO: document
127
+ # @api private
128
+ def inherited_by(model)
129
+ model.relationships(source_repository_name)[name] ||
130
+ self.class.new(name, model, parent_model_name, options_with_inverse)
131
+ end
132
+
133
+ private
134
+
135
+ # Initializes the relationship, always using max cardinality of 1.
136
+ #
137
+ # @api semipublic
138
+ def initialize(name, source_model, target_model, options = {})
139
+ @nullable = options.fetch(:nullable, false)
140
+ target_model ||= Extlib::Inflection.camelize(name)
141
+ options = { :min => @nullable ? 0 : 1, :max => 1 }.update(options)
142
+ super
73
143
  end
74
144
 
75
- def kind_of?(klass)
76
- super || parent.kind_of?(klass)
145
+ # Dynamically defines reader method for source side of association
146
+ # (for instance, method article for model Paragraph)
147
+ #
148
+ # @api semipublic
149
+ def create_reader
150
+ return if source_model.resource_method_defined?(name.to_s)
151
+
152
+ source_model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
153
+ public # TODO: make this configurable
154
+
155
+ # FIXME: if the writer is used, caching nil in the ivar
156
+ # and then the FK(s) are set, the cache in the writer should
157
+ # be cleared.
158
+
159
+ def #{name}(query = nil) # def article(query = nil)
160
+ relationships[#{name.inspect}].get(self, query) # relationships["article"].get(self, query)
161
+ end # end
162
+ RUBY
77
163
  end
78
164
 
79
- def respond_to?(method, include_private = false)
80
- super || parent.respond_to?(method, include_private)
165
+ # Dynamically defines writer method for source side of association
166
+ # (for instance, method article= for model Paragraph)
167
+ #
168
+ # @api semipublic
169
+ def create_writer
170
+ writer_name = "#{name}="
171
+
172
+ return if source_model.resource_method_defined?(writer_name)
173
+
174
+ source_model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
175
+ public # TODO: make this configurable
176
+ def #{writer_name}(target) # def article=(target)
177
+ relationships[#{name.inspect}].set(self, target) # relationships["article"].set(self, target)
178
+ end # end
179
+ RUBY
81
180
  end
82
181
 
83
- def instance_variable_get(variable)
84
- super || parent.instance_variable_get(variable)
182
+ # Loads association target and sets resulting value on
183
+ # given source resource
184
+ #
185
+ # @param [Resource] source
186
+ # the source resource for the association
187
+ #
188
+ # @return [undefined]
189
+ #
190
+ # @api private
191
+ def lazy_load(source)
192
+ return unless source_key.get(source).all?
193
+
194
+ # SEL: load all related resources in the source collection
195
+ if source.saved? && source.collection.size > 1
196
+ eager_load(source.collection)
197
+ end
198
+
199
+ unless loaded?(source)
200
+ set!(source, resource_for(source))
201
+ end
85
202
  end
86
203
 
87
- private
204
+ # Sets the association targets in the resource
205
+ #
206
+ # @param [Resource] source
207
+ # the source to set
208
+ # @param [Array(Resource)] targets
209
+ # the target resource for the association
210
+ # @param [Query, Hash] query
211
+ # not used
212
+ #
213
+ # @return [undefined]
214
+ #
215
+ # @api private
216
+ def eager_load_targets(source, targets, query)
217
+ set(source, targets.first)
218
+ end
88
219
 
89
- def initialize(relationship, child)
90
- assert_kind_of 'relationship', relationship, Relationship
91
- assert_kind_of 'child', child, Resource
220
+ # Returns the inverse relationship class
221
+ #
222
+ # @api private
223
+ def inverse_class
224
+ OneToMany::Relationship
225
+ end
92
226
 
93
- @relationship = relationship
94
- @child = child
227
+ # Returns the inverse relationship name
228
+ #
229
+ # @api private
230
+ def inverse_name
231
+ super || Extlib::Inflection.underscore(Extlib::Inflection.demodulize(source_model.name)).pluralize.to_sym
95
232
  end
96
233
 
97
- def parent
98
- @parent ||= @relationship.get_parent(@child)
234
+ # TODO: document
235
+ # @api private
236
+ def child_key_options(parent_property)
237
+ options = parent_property.options.only(:length, :precision, :scale).update(:index => name, :nullable => nullable?)
238
+
239
+ if parent_property.primitive == Integer && parent_property.min && parent_property.max
240
+ options.update(:min => parent_property.min, :max => parent_property.max)
241
+ end
242
+
243
+ options
99
244
  end
100
245
 
101
- def method_missing(method, *args, &block)
102
- parent.__send__(method, *args, &block)
246
+ # TODO: document
247
+ # @api private
248
+ def child_properties
249
+ child_key.map { |property| property.name }
103
250
  end
104
- end # class Proxy
251
+ end # class Relationship
105
252
  end # module ManyToOne
106
253
  end # module Associations
107
254
  end # module DataMapper