ghost_dm-core 1.3.0.beta

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 (254) hide show
  1. data/.autotest +29 -0
  2. data/.document +5 -0
  3. data/.gitignore +35 -0
  4. data/.yardopts +1 -0
  5. data/Gemfile +65 -0
  6. data/LICENSE +20 -0
  7. data/README.md +269 -0
  8. data/Rakefile +4 -0
  9. data/dm-core.gemspec +24 -0
  10. data/lib/dm-core.rb +292 -0
  11. data/lib/dm-core/adapters.rb +222 -0
  12. data/lib/dm-core/adapters/abstract_adapter.rb +237 -0
  13. data/lib/dm-core/adapters/in_memory_adapter.rb +113 -0
  14. data/lib/dm-core/associations/many_to_many.rb +499 -0
  15. data/lib/dm-core/associations/many_to_one.rb +290 -0
  16. data/lib/dm-core/associations/one_to_many.rb +348 -0
  17. data/lib/dm-core/associations/one_to_one.rb +86 -0
  18. data/lib/dm-core/associations/relationship.rb +663 -0
  19. data/lib/dm-core/backwards.rb +13 -0
  20. data/lib/dm-core/collection.rb +1515 -0
  21. data/lib/dm-core/core_ext/kernel.rb +23 -0
  22. data/lib/dm-core/core_ext/pathname.rb +6 -0
  23. data/lib/dm-core/core_ext/symbol.rb +10 -0
  24. data/lib/dm-core/identity_map.rb +7 -0
  25. data/lib/dm-core/model.rb +874 -0
  26. data/lib/dm-core/model/hook.rb +103 -0
  27. data/lib/dm-core/model/is.rb +32 -0
  28. data/lib/dm-core/model/property.rb +249 -0
  29. data/lib/dm-core/model/relationship.rb +378 -0
  30. data/lib/dm-core/model/scope.rb +89 -0
  31. data/lib/dm-core/property.rb +866 -0
  32. data/lib/dm-core/property/binary.rb +21 -0
  33. data/lib/dm-core/property/boolean.rb +20 -0
  34. data/lib/dm-core/property/class.rb +17 -0
  35. data/lib/dm-core/property/date.rb +10 -0
  36. data/lib/dm-core/property/date_time.rb +10 -0
  37. data/lib/dm-core/property/decimal.rb +36 -0
  38. data/lib/dm-core/property/discriminator.rb +44 -0
  39. data/lib/dm-core/property/float.rb +16 -0
  40. data/lib/dm-core/property/integer.rb +22 -0
  41. data/lib/dm-core/property/invalid_value_error.rb +22 -0
  42. data/lib/dm-core/property/lookup.rb +27 -0
  43. data/lib/dm-core/property/numeric.rb +38 -0
  44. data/lib/dm-core/property/object.rb +34 -0
  45. data/lib/dm-core/property/serial.rb +14 -0
  46. data/lib/dm-core/property/string.rb +38 -0
  47. data/lib/dm-core/property/text.rb +9 -0
  48. data/lib/dm-core/property/time.rb +10 -0
  49. data/lib/dm-core/property_set.rb +177 -0
  50. data/lib/dm-core/query.rb +1366 -0
  51. data/lib/dm-core/query/conditions/comparison.rb +911 -0
  52. data/lib/dm-core/query/conditions/operation.rb +721 -0
  53. data/lib/dm-core/query/direction.rb +36 -0
  54. data/lib/dm-core/query/operator.rb +35 -0
  55. data/lib/dm-core/query/path.rb +114 -0
  56. data/lib/dm-core/query/sort.rb +39 -0
  57. data/lib/dm-core/relationship_set.rb +72 -0
  58. data/lib/dm-core/repository.rb +226 -0
  59. data/lib/dm-core/resource.rb +1214 -0
  60. data/lib/dm-core/resource/persistence_state.rb +75 -0
  61. data/lib/dm-core/resource/persistence_state/clean.rb +40 -0
  62. data/lib/dm-core/resource/persistence_state/deleted.rb +30 -0
  63. data/lib/dm-core/resource/persistence_state/dirty.rb +96 -0
  64. data/lib/dm-core/resource/persistence_state/immutable.rb +34 -0
  65. data/lib/dm-core/resource/persistence_state/persisted.rb +29 -0
  66. data/lib/dm-core/resource/persistence_state/transient.rb +80 -0
  67. data/lib/dm-core/spec/lib/adapter_helpers.rb +64 -0
  68. data/lib/dm-core/spec/lib/collection_helpers.rb +21 -0
  69. data/lib/dm-core/spec/lib/counter_adapter.rb +38 -0
  70. data/lib/dm-core/spec/lib/pending_helpers.rb +50 -0
  71. data/lib/dm-core/spec/lib/spec_helper.rb +74 -0
  72. data/lib/dm-core/spec/setup.rb +174 -0
  73. data/lib/dm-core/spec/shared/adapter_spec.rb +341 -0
  74. data/lib/dm-core/spec/shared/public/property_spec.rb +229 -0
  75. data/lib/dm-core/spec/shared/resource_spec.rb +1232 -0
  76. data/lib/dm-core/spec/shared/sel_spec.rb +111 -0
  77. data/lib/dm-core/spec/shared/semipublic/property_spec.rb +176 -0
  78. data/lib/dm-core/spec/shared/semipublic/query/conditions/abstract_comparison_spec.rb +261 -0
  79. data/lib/dm-core/support/assertions.rb +8 -0
  80. data/lib/dm-core/support/chainable.rb +18 -0
  81. data/lib/dm-core/support/deprecate.rb +12 -0
  82. data/lib/dm-core/support/descendant_set.rb +89 -0
  83. data/lib/dm-core/support/equalizer.rb +48 -0
  84. data/lib/dm-core/support/ext/array.rb +22 -0
  85. data/lib/dm-core/support/ext/blank.rb +25 -0
  86. data/lib/dm-core/support/ext/hash.rb +67 -0
  87. data/lib/dm-core/support/ext/module.rb +47 -0
  88. data/lib/dm-core/support/ext/object.rb +57 -0
  89. data/lib/dm-core/support/ext/string.rb +24 -0
  90. data/lib/dm-core/support/ext/try_dup.rb +12 -0
  91. data/lib/dm-core/support/hook.rb +405 -0
  92. data/lib/dm-core/support/inflections.rb +60 -0
  93. data/lib/dm-core/support/inflector/inflections.rb +211 -0
  94. data/lib/dm-core/support/inflector/methods.rb +151 -0
  95. data/lib/dm-core/support/lazy_array.rb +451 -0
  96. data/lib/dm-core/support/local_object_space.rb +13 -0
  97. data/lib/dm-core/support/logger.rb +201 -0
  98. data/lib/dm-core/support/mash.rb +176 -0
  99. data/lib/dm-core/support/naming_conventions.rb +90 -0
  100. data/lib/dm-core/support/ordered_set.rb +380 -0
  101. data/lib/dm-core/support/subject.rb +33 -0
  102. data/lib/dm-core/support/subject_set.rb +250 -0
  103. data/lib/dm-core/version.rb +3 -0
  104. data/script/performance.rb +275 -0
  105. data/script/profile.rb +218 -0
  106. data/spec/lib/rspec_immediate_feedback_formatter.rb +54 -0
  107. data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +68 -0
  108. data/spec/public/associations/many_to_many_spec.rb +197 -0
  109. data/spec/public/associations/many_to_one_spec.rb +83 -0
  110. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +40 -0
  111. data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +49 -0
  112. data/spec/public/associations/one_to_many_spec.rb +81 -0
  113. data/spec/public/associations/one_to_one_spec.rb +176 -0
  114. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +46 -0
  115. data/spec/public/collection_spec.rb +69 -0
  116. data/spec/public/finalize_spec.rb +76 -0
  117. data/spec/public/model/hook_spec.rb +246 -0
  118. data/spec/public/model/property_spec.rb +88 -0
  119. data/spec/public/model/relationship_spec.rb +1040 -0
  120. data/spec/public/model_spec.rb +462 -0
  121. data/spec/public/property/binary_spec.rb +41 -0
  122. data/spec/public/property/boolean_spec.rb +22 -0
  123. data/spec/public/property/class_spec.rb +28 -0
  124. data/spec/public/property/date_spec.rb +22 -0
  125. data/spec/public/property/date_time_spec.rb +22 -0
  126. data/spec/public/property/decimal_spec.rb +23 -0
  127. data/spec/public/property/discriminator_spec.rb +135 -0
  128. data/spec/public/property/float_spec.rb +22 -0
  129. data/spec/public/property/integer_spec.rb +22 -0
  130. data/spec/public/property/object_spec.rb +107 -0
  131. data/spec/public/property/serial_spec.rb +22 -0
  132. data/spec/public/property/string_spec.rb +22 -0
  133. data/spec/public/property/text_spec.rb +63 -0
  134. data/spec/public/property/time_spec.rb +22 -0
  135. data/spec/public/property_spec.rb +341 -0
  136. data/spec/public/resource_spec.rb +288 -0
  137. data/spec/public/sel_spec.rb +53 -0
  138. data/spec/public/setup_spec.rb +145 -0
  139. data/spec/public/shared/association_collection_shared_spec.rb +309 -0
  140. data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
  141. data/spec/public/shared/collection_shared_spec.rb +1667 -0
  142. data/spec/public/shared/finder_shared_spec.rb +1629 -0
  143. data/spec/rcov.opts +6 -0
  144. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  145. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +13 -0
  146. data/spec/semipublic/associations/many_to_many_spec.rb +94 -0
  147. data/spec/semipublic/associations/many_to_one_spec.rb +63 -0
  148. data/spec/semipublic/associations/one_to_many_spec.rb +55 -0
  149. data/spec/semipublic/associations/one_to_one_spec.rb +53 -0
  150. data/spec/semipublic/associations/relationship_spec.rb +200 -0
  151. data/spec/semipublic/associations_spec.rb +177 -0
  152. data/spec/semipublic/collection_spec.rb +110 -0
  153. data/spec/semipublic/model_spec.rb +96 -0
  154. data/spec/semipublic/property/binary_spec.rb +13 -0
  155. data/spec/semipublic/property/boolean_spec.rb +47 -0
  156. data/spec/semipublic/property/class_spec.rb +33 -0
  157. data/spec/semipublic/property/date_spec.rb +43 -0
  158. data/spec/semipublic/property/date_time_spec.rb +46 -0
  159. data/spec/semipublic/property/decimal_spec.rb +83 -0
  160. data/spec/semipublic/property/discriminator_spec.rb +19 -0
  161. data/spec/semipublic/property/float_spec.rb +82 -0
  162. data/spec/semipublic/property/integer_spec.rb +82 -0
  163. data/spec/semipublic/property/lookup_spec.rb +29 -0
  164. data/spec/semipublic/property/serial_spec.rb +13 -0
  165. data/spec/semipublic/property/string_spec.rb +13 -0
  166. data/spec/semipublic/property/text_spec.rb +31 -0
  167. data/spec/semipublic/property/time_spec.rb +50 -0
  168. data/spec/semipublic/property_spec.rb +114 -0
  169. data/spec/semipublic/query/conditions/comparison_spec.rb +1501 -0
  170. data/spec/semipublic/query/conditions/operation_spec.rb +1294 -0
  171. data/spec/semipublic/query/path_spec.rb +471 -0
  172. data/spec/semipublic/query_spec.rb +3682 -0
  173. data/spec/semipublic/resource/state/clean_spec.rb +88 -0
  174. data/spec/semipublic/resource/state/deleted_spec.rb +78 -0
  175. data/spec/semipublic/resource/state/dirty_spec.rb +162 -0
  176. data/spec/semipublic/resource/state/immutable_spec.rb +105 -0
  177. data/spec/semipublic/resource/state/transient_spec.rb +162 -0
  178. data/spec/semipublic/resource/state_spec.rb +230 -0
  179. data/spec/semipublic/resource_spec.rb +23 -0
  180. data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
  181. data/spec/semipublic/shared/resource_shared_spec.rb +199 -0
  182. data/spec/semipublic/shared/resource_state_shared_spec.rb +79 -0
  183. data/spec/semipublic/shared/subject_shared_spec.rb +79 -0
  184. data/spec/spec.opts +5 -0
  185. data/spec/spec_helper.rb +38 -0
  186. data/spec/support/core_ext/hash.rb +10 -0
  187. data/spec/support/core_ext/inheritable_attributes.rb +46 -0
  188. data/spec/support/properties/huge_integer.rb +17 -0
  189. data/spec/unit/array_spec.rb +23 -0
  190. data/spec/unit/blank_spec.rb +73 -0
  191. data/spec/unit/data_mapper/ordered_set/append_spec.rb +26 -0
  192. data/spec/unit/data_mapper/ordered_set/clear_spec.rb +24 -0
  193. data/spec/unit/data_mapper/ordered_set/delete_spec.rb +28 -0
  194. data/spec/unit/data_mapper/ordered_set/each_spec.rb +19 -0
  195. data/spec/unit/data_mapper/ordered_set/empty_spec.rb +20 -0
  196. data/spec/unit/data_mapper/ordered_set/entries_spec.rb +22 -0
  197. data/spec/unit/data_mapper/ordered_set/eql_spec.rb +51 -0
  198. data/spec/unit/data_mapper/ordered_set/equal_value_spec.rb +84 -0
  199. data/spec/unit/data_mapper/ordered_set/hash_spec.rb +12 -0
  200. data/spec/unit/data_mapper/ordered_set/include_spec.rb +23 -0
  201. data/spec/unit/data_mapper/ordered_set/index_spec.rb +28 -0
  202. data/spec/unit/data_mapper/ordered_set/initialize_spec.rb +32 -0
  203. data/spec/unit/data_mapper/ordered_set/merge_spec.rb +36 -0
  204. data/spec/unit/data_mapper/ordered_set/shared/append_spec.rb +24 -0
  205. data/spec/unit/data_mapper/ordered_set/shared/clear_spec.rb +9 -0
  206. data/spec/unit/data_mapper/ordered_set/shared/delete_spec.rb +25 -0
  207. data/spec/unit/data_mapper/ordered_set/shared/each_spec.rb +17 -0
  208. data/spec/unit/data_mapper/ordered_set/shared/empty_spec.rb +9 -0
  209. data/spec/unit/data_mapper/ordered_set/shared/entries_spec.rb +9 -0
  210. data/spec/unit/data_mapper/ordered_set/shared/include_spec.rb +9 -0
  211. data/spec/unit/data_mapper/ordered_set/shared/index_spec.rb +13 -0
  212. data/spec/unit/data_mapper/ordered_set/shared/initialize_spec.rb +28 -0
  213. data/spec/unit/data_mapper/ordered_set/shared/merge_spec.rb +28 -0
  214. data/spec/unit/data_mapper/ordered_set/shared/size_spec.rb +13 -0
  215. data/spec/unit/data_mapper/ordered_set/shared/to_ary_spec.rb +11 -0
  216. data/spec/unit/data_mapper/ordered_set/size_spec.rb +27 -0
  217. data/spec/unit/data_mapper/ordered_set/to_ary_spec.rb +23 -0
  218. data/spec/unit/data_mapper/subject_set/append_spec.rb +47 -0
  219. data/spec/unit/data_mapper/subject_set/clear_spec.rb +34 -0
  220. data/spec/unit/data_mapper/subject_set/delete_spec.rb +40 -0
  221. data/spec/unit/data_mapper/subject_set/each_spec.rb +30 -0
  222. data/spec/unit/data_mapper/subject_set/empty_spec.rb +31 -0
  223. data/spec/unit/data_mapper/subject_set/entries_spec.rb +31 -0
  224. data/spec/unit/data_mapper/subject_set/get_spec.rb +34 -0
  225. data/spec/unit/data_mapper/subject_set/include_spec.rb +32 -0
  226. data/spec/unit/data_mapper/subject_set/named_spec.rb +33 -0
  227. data/spec/unit/data_mapper/subject_set/shared/append_spec.rb +18 -0
  228. data/spec/unit/data_mapper/subject_set/shared/clear_spec.rb +9 -0
  229. data/spec/unit/data_mapper/subject_set/shared/delete_spec.rb +9 -0
  230. data/spec/unit/data_mapper/subject_set/shared/each_spec.rb +9 -0
  231. data/spec/unit/data_mapper/subject_set/shared/empty_spec.rb +9 -0
  232. data/spec/unit/data_mapper/subject_set/shared/entries_spec.rb +9 -0
  233. data/spec/unit/data_mapper/subject_set/shared/get_spec.rb +9 -0
  234. data/spec/unit/data_mapper/subject_set/shared/include_spec.rb +9 -0
  235. data/spec/unit/data_mapper/subject_set/shared/named_spec.rb +9 -0
  236. data/spec/unit/data_mapper/subject_set/shared/size_spec.rb +13 -0
  237. data/spec/unit/data_mapper/subject_set/shared/to_ary_spec.rb +9 -0
  238. data/spec/unit/data_mapper/subject_set/shared/values_at_spec.rb +44 -0
  239. data/spec/unit/data_mapper/subject_set/size_spec.rb +42 -0
  240. data/spec/unit/data_mapper/subject_set/to_ary_spec.rb +34 -0
  241. data/spec/unit/data_mapper/subject_set/values_at_spec.rb +57 -0
  242. data/spec/unit/hash_spec.rb +28 -0
  243. data/spec/unit/hook_spec.rb +1235 -0
  244. data/spec/unit/inflections_spec.rb +16 -0
  245. data/spec/unit/lazy_array_spec.rb +1949 -0
  246. data/spec/unit/mash_spec.rb +312 -0
  247. data/spec/unit/module_spec.rb +71 -0
  248. data/spec/unit/object_spec.rb +38 -0
  249. data/spec/unit/try_dup_spec.rb +46 -0
  250. data/tasks/ci.rake +1 -0
  251. data/tasks/spec.rake +38 -0
  252. data/tasks/yard.rake +9 -0
  253. data/tasks/yardstick.rake +19 -0
  254. metadata +365 -0
@@ -0,0 +1,380 @@
1
+ module DataMapper
2
+
3
+ # An ordered set of things
4
+ #
5
+ # {OrderedSet} implements set behavior and keeps
6
+ # track of the order in which entries were added.
7
+ #
8
+ # {OrderedSet} allows to inject a class that implements
9
+ # {OrderedSet::Cache::API} at construction time, and will
10
+ # use that cache implementation to enforce set semantics
11
+ # and perform internal caching of insertion order.
12
+ #
13
+ # @see OrderedSet::Cache::API
14
+ # @see OrderedSet::Cache
15
+ # @see SubjectSet::NameCache
16
+ #
17
+ # @api private
18
+ class OrderedSet
19
+
20
+ # The default cache used by {OrderedSet}
21
+ #
22
+ # Uses a {Hash} as internal storage and enforces set semantics
23
+ # by calling #eql? and #hash on the set's entries.
24
+ #
25
+ # @api private
26
+ class Cache
27
+
28
+ # The default implementation of the {API} that {OrderedSet} expects from
29
+ # the cache object that it uses to
30
+ #
31
+ # 1. keep track of insertion order
32
+ # 2. enforce set semantics.
33
+ #
34
+ # Classes including {API} must customize the behavior of the cache in 2 ways:
35
+ #
36
+ # They must determine the value to use as cache key and thus set discriminator,
37
+ # by implementing the {#key_for} method. The {#key_for} method accepts an arbitrary
38
+ # object as param and the method is free to return whatever value from that method.
39
+ # Obviously this will most likely be some attribute or value otherwise derived from
40
+ # the object that got passed in.
41
+ #
42
+ # They must determine which objects are valid set entries by overwriting the
43
+ # {#valid?} method. The {#valid?} method accepts an arbitrary object as param and
44
+ # the overwriting method must return either true or false.
45
+ #
46
+ # The motivation behind this is that set semantics cannot always be enforced
47
+ # by calling {#eql?} and {#hash} on the set's entries. For example, two entries
48
+ # might be considered unique wrt the set if their names are the same, but other
49
+ # internal state differs. This is exactly the case for {DataMapper::Property} and
50
+ # {DataMapper::Associations::Relationship} objects.
51
+ #
52
+ # @see DataMapper::SubjectSet::NameCache
53
+ #
54
+ # @api private
55
+ module API
56
+
57
+ # Initialize a new Cache
58
+ #
59
+ # @api private
60
+ def initialize
61
+ @cache = {}
62
+ end
63
+
64
+ # Tests if the given entry qualifies to be added to the cache
65
+ #
66
+ # @param [Object] entry
67
+ # the entry to be checked
68
+ #
69
+ # @return [Boolean]
70
+ # true if the entry qualifies to be added to the cache
71
+ #
72
+ # @api private
73
+ def valid?(entry)
74
+ raise NotImplementedError, "#{self}#valid? must be implemented"
75
+ end
76
+
77
+ # Given an entry, return the key to be used in the cache
78
+ #
79
+ # @param [Object] entry
80
+ # the entry to get the key for
81
+ #
82
+ # @return [Object, nil]
83
+ # a value derived from the entry that is used as key in the cache
84
+ #
85
+ # @api private
86
+ def key_for(entry)
87
+ raise NotImplementedError, "#{self}#key_for must be implemented"
88
+ end
89
+
90
+ # Check if the entry exists in the cache
91
+ #
92
+ # @param [Object] entry
93
+ # the entry to test for
94
+ #
95
+ # @return [Boolean]
96
+ # true if entry is included in the cache
97
+ #
98
+ # @api private
99
+ def include?(entry)
100
+ @cache.has_key?(key_for(entry))
101
+ end
102
+
103
+ # Return the index for the entry in the cache
104
+ #
105
+ # @param [Object] entry
106
+ # the entry to get the index for
107
+ #
108
+ # @return [Integer, nil]
109
+ # the index for the entry, or nil if it does not exist
110
+ #
111
+ # @api private
112
+ def [](entry)
113
+ @cache[key_for(entry)]
114
+ end
115
+
116
+ # Set the index for the entry in the cache
117
+ #
118
+ # @param [Object] entry
119
+ # the entry to set the index for
120
+ # @param [Integer] index
121
+ # the index to assign to the given entry
122
+ #
123
+ # @return [Integer]
124
+ # the given index for the entry
125
+ #
126
+ # @api private
127
+ def []=(entry, index)
128
+ if valid?(entry)
129
+ @cache[key_for(entry)] = index
130
+ end
131
+ end
132
+
133
+ # Delete an entry from the cache
134
+ #
135
+ # @param [Object] entry
136
+ # the entry to delete from the cache
137
+ #
138
+ # @return [API] self
139
+ #
140
+ # @api private
141
+ def delete(entry)
142
+ deleted_index = @cache.delete(key_for(entry))
143
+ if deleted_index
144
+ @cache.each do |key, index|
145
+ @cache[key] -= 1 if index > deleted_index
146
+ end
147
+ end
148
+ deleted_index
149
+ end
150
+
151
+ # Removes all entries and returns self
152
+ #
153
+ # @return [API] self
154
+ #
155
+ # @api private
156
+ def clear
157
+ @cache.clear
158
+ self
159
+ end
160
+
161
+ end # module API
162
+
163
+ include API
164
+
165
+ # Tests if the given entry qualifies to be added to the cache
166
+ #
167
+ # @param [Object] entry
168
+ # the entry to be checked
169
+ #
170
+ # @return [true] true
171
+ #
172
+ # @api private
173
+ def valid?(entry)
174
+ true
175
+ end
176
+
177
+ # Given an entry, return the key to be used in the cache
178
+ #
179
+ # @param [Object] entry
180
+ # the entry to get the key for
181
+ #
182
+ # @return [Object]
183
+ # the passed in entry
184
+ #
185
+ # @api private
186
+ def key_for(entry)
187
+ entry
188
+ end
189
+
190
+ end # class Cache
191
+
192
+ include Enumerable
193
+ extend Equalizer
194
+
195
+ # This set's entries
196
+ #
197
+ # The order in this Array is not guaranteed
198
+ # to be the order in which the entries were
199
+ # inserted. Use #each to access the entries
200
+ # in insertion order.
201
+ #
202
+ # @return [Array]
203
+ # this set's entries
204
+ #
205
+ # @api private
206
+ attr_reader :entries
207
+
208
+ equalize :entries
209
+
210
+ # Initialize an OrderedSet
211
+ #
212
+ # @param [#each] entries
213
+ # the entries to initialize this set with
214
+ # @param [Class<Cache::API>] cache
215
+ # the cache implementation to use
216
+ #
217
+ # @api private
218
+ def initialize(entries = [], cache = Cache)
219
+ @cache = cache.new
220
+ @entries = []
221
+ merge(entries.to_ary)
222
+ end
223
+
224
+ # Initialize a copy of OrderedSet
225
+ #
226
+ # @api private
227
+ def initialize_copy(*)
228
+ @cache = @cache.dup
229
+ @entries = @entries.dup
230
+ end
231
+
232
+ # Get the entry at the given index
233
+ #
234
+ # @param [Integer] index
235
+ # the index of the desired entry
236
+ #
237
+ # @return [Object, nil]
238
+ # the entry at the given index, or nil if no entry is present
239
+ #
240
+ # @api private
241
+ def [](index)
242
+ entries[index]
243
+ end
244
+
245
+ # Add or update an entry in the set
246
+ #
247
+ # If the entry to add isn't part of the set already,
248
+ # it will be added. If an entry with the same cache
249
+ # key as the entry to add is part of the set already,
250
+ # it will be replaced with the given entry.
251
+ #
252
+ # @param [Object] entry
253
+ # the entry to be added
254
+ #
255
+ # @return [OrderedSet] self
256
+ #
257
+ # @api private
258
+ def <<(entry)
259
+ if index = @cache[entry]
260
+ entries[index] = entry
261
+ else
262
+ @cache[entry] = size
263
+ entries << entry
264
+ end
265
+ self
266
+ end
267
+
268
+ # Merge with another Enumerable object
269
+ #
270
+ # @param [#each] other
271
+ # the Enumerable to merge with this OrderedSet
272
+ #
273
+ # @return [OrderedSet] self
274
+ #
275
+ # @api private
276
+ def merge(other)
277
+ other.each { |entry| self << entry }
278
+ self
279
+ end
280
+
281
+ # Delete an entry from this OrderedSet
282
+ #
283
+ # @param [Object] entry
284
+ # the entry to delete
285
+ #
286
+ # @return [Object, nil]
287
+ # the deleted entry or nil
288
+ #
289
+ # @api private
290
+ def delete(entry)
291
+ if index = @cache.delete(entry)
292
+ entries.delete_at(index)
293
+ end
294
+ end
295
+
296
+ # Removes all entries and returns self
297
+ #
298
+ # @return [OrderedSet] self
299
+ #
300
+ # @api private
301
+ def clear
302
+ @cache.clear
303
+ entries.clear
304
+ self
305
+ end
306
+
307
+ # Iterate over each entry in the set
308
+ #
309
+ # @yield [entry]
310
+ # all entries in the set
311
+ #
312
+ # @yieldparam [Object] entry
313
+ # an entry in the set
314
+ #
315
+ # @return [OrderedSet] self
316
+ #
317
+ # @api private
318
+ def each
319
+ entries.each { |entry| yield(entry) }
320
+ self
321
+ end
322
+
323
+ # The number of entries
324
+ #
325
+ # @return [Integer]
326
+ # the number of entries
327
+ #
328
+ # @api private
329
+ def size
330
+ entries.size
331
+ end
332
+
333
+ # Check if there are any entries
334
+ #
335
+ # @return [Boolean]
336
+ # true if the set is empty
337
+ #
338
+ # @api private
339
+ def empty?
340
+ entries.empty?
341
+ end
342
+
343
+ # Check if the entry exists in the set
344
+ #
345
+ # @param [Object] entry
346
+ # the entry to test for
347
+ #
348
+ # @return [Boolean]
349
+ # true if entry is included in the set
350
+ #
351
+ # @api private
352
+ def include?(entry)
353
+ entries.include?(entry)
354
+ end
355
+
356
+ # Return the index for the entry in the set
357
+ #
358
+ # @param [Object] entry
359
+ # the entry to check the set for
360
+ #
361
+ # @return [Integer, nil]
362
+ # the index for the entry, or nil if it does not exist
363
+ #
364
+ # @api private
365
+ def index(entry)
366
+ @cache[entry]
367
+ end
368
+
369
+ # Convert the OrderedSet into an Array
370
+ #
371
+ # @return [Array]
372
+ # an array containing all the OrderedSet's entries
373
+ #
374
+ # @api private
375
+ def to_ary
376
+ entries
377
+ end
378
+
379
+ end # class OrderedSet
380
+ end # module DataMapper
@@ -0,0 +1,33 @@
1
+ module DataMapper
2
+ module Subject
3
+ # Returns a default value of the subject for given resource
4
+ #
5
+ # When default value is a callable object, it is called with resource
6
+ # and subject passed as arguments.
7
+ #
8
+ # @param [Resource] resource
9
+ # the model instance for which the default is to be set
10
+ #
11
+ # @return [Object]
12
+ # the default value of this subject for +resource+
13
+ #
14
+ # @api semipublic
15
+ def default_for(resource)
16
+ if @default.respond_to?(:call)
17
+ @default.call(resource, self)
18
+ else
19
+ DataMapper::Ext.try_dup(@default)
20
+ end
21
+ end
22
+
23
+ # Returns true if the subject has a default value
24
+ #
25
+ # @return [Boolean]
26
+ # true if the subject has a default value
27
+ #
28
+ # @api semipublic
29
+ def default?
30
+ @options.key?(:default)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,250 @@
1
+ module DataMapper
2
+
3
+ # An insertion ordered set of named objects
4
+ #
5
+ # {SubjectSet} uses {DataMapper::OrderedSet}
6
+ # under the hood to keep track of a set of
7
+ # entries. In DataMapper code, a subject
8
+ # can be either a {DataMapper::Property}, or
9
+ # a {DataMapper::Associations::Relationship}.
10
+ #
11
+ # All entries added to instances of this
12
+ # class must respond to the {#name} method
13
+ #
14
+ # The motivation behind this is that we
15
+ # use this class as a base to keep track
16
+ # properties and relationships.
17
+ # The following constraints apply for these
18
+ # types of objects: {Property} names must be
19
+ # unique within any model.
20
+ # {Associations::Relationship} names must be
21
+ # unique within any model
22
+ #
23
+ # When adding an entry with a name that
24
+ # already exists, the already existing
25
+ # entry will be replaced with the new
26
+ # entry with the same name. This is because
27
+ # we want to be able to update properties,
28
+ # and relationship during the course of
29
+ # initializing our application.
30
+ #
31
+ # This also happens to be consistent with
32
+ # the way ruby handles redefining methods,
33
+ # where the last definitions "wins".
34
+ #
35
+ # Furthermore, the builtin ruby {Set#<<} method
36
+ # also updates the old object if a new object
37
+ # gets added.
38
+ #
39
+ # @api private
40
+ class SubjectSet
41
+
42
+ # An {OrderedSet::Cache::API} implementation that establishes
43
+ # set semantics based on the name of its entries. The cache
44
+ # uses the entries' names as cache key and refuses to add
45
+ # entries that don't respond_to?(:name).
46
+ #
47
+ # @see OrderedSet::Cache::API
48
+ #
49
+ # @api private
50
+ class NameCache
51
+
52
+ include OrderedSet::Cache::API
53
+
54
+ # Tests if the given entry qualifies to be added to the cache
55
+ #
56
+ # @param [#name] entry
57
+ # the entry to be checked
58
+ #
59
+ # @return [Boolean]
60
+ # true if the entry respond_to?(:name)
61
+ #
62
+ # @api private
63
+ def valid?(entry)
64
+ entry.respond_to?(:name)
65
+ end
66
+
67
+ # Given an entry, return the key to be used in the cache
68
+ #
69
+ # @param [#name] entry
70
+ # the entry to get the key for
71
+ #
72
+ # @return [#to_s, nil]
73
+ # the entry's name or nil if the entry isn't #valid?
74
+ #
75
+ # @api private
76
+ def key_for(entry)
77
+ valid?(entry) ? entry.name : nil
78
+ end
79
+
80
+ end # class NameCache
81
+
82
+ include Enumerable
83
+
84
+ # The elements in the SubjectSet
85
+ #
86
+ # @return [OrderedSet]
87
+ #
88
+ # @api private
89
+ attr_reader :entries
90
+
91
+ # Initialize a SubjectSet
92
+ #
93
+ # @param [Enumerable<#name>] entries
94
+ # the entries to initialize this set with
95
+ #
96
+ # @api private
97
+ def initialize(entries = [])
98
+ @entries = OrderedSet.new(entries, NameCache)
99
+ end
100
+
101
+ # Initialize a copy of a SubjectSet
102
+ #
103
+ # @api private
104
+ def initialize_copy(*)
105
+ @entries = @entries.dup
106
+ end
107
+
108
+ # Make sure that entry is part of this SubjectSet
109
+ #
110
+ # If an entry with the same name already exists, it
111
+ # will be updated. If no such named entry exists, it
112
+ # will be added.
113
+ #
114
+ # @param [#name] entry
115
+ # the entry to be added
116
+ #
117
+ # @return [SubjectSet] self
118
+ #
119
+ # @api private
120
+ def <<(entry)
121
+ entries << entry
122
+ self
123
+ end
124
+
125
+ # Delete an entry from this SubjectSet
126
+ #
127
+ # @param [#name] entry
128
+ # the entry to delete
129
+ #
130
+ # @return [#name, nil]
131
+ # the deleted entry or nil
132
+ #
133
+ # @api private
134
+ def delete(entry)
135
+ entries.delete(entry)
136
+ end
137
+
138
+ # Removes all entries and returns self
139
+ #
140
+ # @return [SubjectSet] self
141
+ #
142
+ # @api private
143
+ def clear
144
+ entries.clear
145
+ self
146
+ end
147
+
148
+ # Test if the given entry is included in this SubjectSet
149
+ #
150
+ # @param [#name] entry
151
+ # the entry to test for
152
+ #
153
+ # @return [Boolean]
154
+ # true if the entry is included in this SubjectSet
155
+ #
156
+ # @api private
157
+ def include?(entry)
158
+ entries.include?(entry)
159
+ end
160
+
161
+ # Tests wether the SubjectSet contains a entry named name
162
+ #
163
+ # @param [#to_s] name
164
+ # the entry name to test for
165
+ #
166
+ # @return [Boolean]
167
+ # true if the SubjectSet contains a entry named name
168
+ #
169
+ # @api private
170
+ def named?(name)
171
+ !self[name].nil?
172
+ end
173
+
174
+ # Check if there are any entries
175
+ #
176
+ # @return [Boolean]
177
+ # true if the set contains at least one entry
178
+ #
179
+ # @api private
180
+ def empty?
181
+ entries.empty?
182
+ end
183
+
184
+ # Lookup an entry in the SubjectSet based on a given name
185
+ #
186
+ # @param [#to_s] name
187
+ # the name of the entry
188
+ #
189
+ # @return [Object, nil]
190
+ # the entry having the given name, or nil if not found
191
+ #
192
+ # @api private
193
+ def [](name)
194
+ name = name.to_s
195
+ entries.detect { |entry| entry.name.to_s == name }
196
+ end
197
+
198
+ # Iterate over each entry in the set
199
+ #
200
+ # @yield [entry]
201
+ # each entry in the set
202
+ #
203
+ # @yieldparam [#name] entry
204
+ # an entry in the set
205
+ #
206
+ # @return [SubjectSet] self
207
+ #
208
+ # @api private
209
+ def each
210
+ entries.each { |entry| yield(entry) }
211
+ self
212
+ end
213
+
214
+ # All entries (or nil values) that have any of the given names
215
+ #
216
+ # @param [Enumerable<#to_s>] names
217
+ # the names of the desired entries
218
+ #
219
+ # @return [Array<#name, nil>]
220
+ # an array containing entries whose names match any of the given
221
+ # names, or nil values for those names with no matching entries
222
+ # in the set
223
+ #
224
+ # @api private
225
+ def values_at(*names)
226
+ names.map { |name| self[name] }
227
+ end
228
+
229
+ # Get the number of elements inside this SubjectSet
230
+ #
231
+ # @return [Integer]
232
+ # the number of elements
233
+ #
234
+ # @api private
235
+ def size
236
+ entries.size
237
+ end
238
+
239
+ # Convert the SubjectSet into an Array
240
+ #
241
+ # @return [Array]
242
+ # an array containing all the SubjectSet's entries
243
+ #
244
+ # @api private
245
+ def to_ary
246
+ to_a
247
+ end
248
+
249
+ end # class SubjectSet
250
+ end # module DataMapper