sbf-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 (259) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +29 -0
  3. data/.document +5 -0
  4. data/.gitignore +44 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +468 -0
  7. data/.travis.yml +57 -0
  8. data/.yardopts +1 -0
  9. data/Gemfile +70 -0
  10. data/LICENSE +20 -0
  11. data/README.md +269 -0
  12. data/Rakefile +4 -0
  13. data/dm-core.gemspec +21 -0
  14. data/lib/dm-core/adapters/abstract_adapter.rb +233 -0
  15. data/lib/dm-core/adapters/in_memory_adapter.rb +110 -0
  16. data/lib/dm-core/adapters.rb +249 -0
  17. data/lib/dm-core/associations/many_to_many.rb +477 -0
  18. data/lib/dm-core/associations/many_to_one.rb +282 -0
  19. data/lib/dm-core/associations/one_to_many.rb +332 -0
  20. data/lib/dm-core/associations/one_to_one.rb +84 -0
  21. data/lib/dm-core/associations/relationship.rb +650 -0
  22. data/lib/dm-core/backwards.rb +11 -0
  23. data/lib/dm-core/collection.rb +1486 -0
  24. data/lib/dm-core/core_ext/kernel.rb +21 -0
  25. data/lib/dm-core/core_ext/pathname.rb +4 -0
  26. data/lib/dm-core/core_ext/symbol.rb +10 -0
  27. data/lib/dm-core/identity_map.rb +6 -0
  28. data/lib/dm-core/model/hook.rb +99 -0
  29. data/lib/dm-core/model/is.rb +30 -0
  30. data/lib/dm-core/model/property.rb +244 -0
  31. data/lib/dm-core/model/relationship.rb +366 -0
  32. data/lib/dm-core/model/scope.rb +87 -0
  33. data/lib/dm-core/model.rb +876 -0
  34. data/lib/dm-core/property/binary.rb +19 -0
  35. data/lib/dm-core/property/boolean.rb +35 -0
  36. data/lib/dm-core/property/class.rb +23 -0
  37. data/lib/dm-core/property/date.rb +45 -0
  38. data/lib/dm-core/property/date_time.rb +44 -0
  39. data/lib/dm-core/property/decimal.rb +47 -0
  40. data/lib/dm-core/property/discriminator.rb +40 -0
  41. data/lib/dm-core/property/float.rb +27 -0
  42. data/lib/dm-core/property/integer.rb +32 -0
  43. data/lib/dm-core/property/invalid_value_error.rb +17 -0
  44. data/lib/dm-core/property/lookup.rb +26 -0
  45. data/lib/dm-core/property/numeric.rb +35 -0
  46. data/lib/dm-core/property/object.rb +33 -0
  47. data/lib/dm-core/property/serial.rb +13 -0
  48. data/lib/dm-core/property/string.rb +47 -0
  49. data/lib/dm-core/property/text.rb +12 -0
  50. data/lib/dm-core/property/time.rb +46 -0
  51. data/lib/dm-core/property/typecast/numeric.rb +32 -0
  52. data/lib/dm-core/property/typecast/time.rb +33 -0
  53. data/lib/dm-core/property.rb +856 -0
  54. data/lib/dm-core/property_set.rb +177 -0
  55. data/lib/dm-core/query/conditions/comparison.rb +886 -0
  56. data/lib/dm-core/query/conditions/operation.rb +710 -0
  57. data/lib/dm-core/query/direction.rb +33 -0
  58. data/lib/dm-core/query/operator.rb +34 -0
  59. data/lib/dm-core/query/path.rb +113 -0
  60. data/lib/dm-core/query/sort.rb +38 -0
  61. data/lib/dm-core/query.rb +1352 -0
  62. data/lib/dm-core/relationship_set.rb +69 -0
  63. data/lib/dm-core/repository.rb +226 -0
  64. data/lib/dm-core/resource/persistence_state/clean.rb +36 -0
  65. data/lib/dm-core/resource/persistence_state/deleted.rb +26 -0
  66. data/lib/dm-core/resource/persistence_state/dirty.rb +91 -0
  67. data/lib/dm-core/resource/persistence_state/immutable.rb +32 -0
  68. data/lib/dm-core/resource/persistence_state/persisted.rb +25 -0
  69. data/lib/dm-core/resource/persistence_state/transient.rb +87 -0
  70. data/lib/dm-core/resource/persistence_state.rb +70 -0
  71. data/lib/dm-core/resource.rb +1220 -0
  72. data/lib/dm-core/spec/lib/adapter_helpers.rb +63 -0
  73. data/lib/dm-core/spec/lib/collection_helpers.rb +21 -0
  74. data/lib/dm-core/spec/lib/counter_adapter.rb +38 -0
  75. data/lib/dm-core/spec/lib/pending_helpers.rb +50 -0
  76. data/lib/dm-core/spec/lib/spec_helper.rb +74 -0
  77. data/lib/dm-core/spec/setup.rb +164 -0
  78. data/lib/dm-core/spec/shared/adapter_spec.rb +366 -0
  79. data/lib/dm-core/spec/shared/public/property_spec.rb +229 -0
  80. data/lib/dm-core/spec/shared/resource_spec.rb +1221 -0
  81. data/lib/dm-core/spec/shared/sel_spec.rb +111 -0
  82. data/lib/dm-core/spec/shared/semipublic/property_spec.rb +184 -0
  83. data/lib/dm-core/spec/shared/semipublic/query/conditions/abstract_comparison_spec.rb +261 -0
  84. data/lib/dm-core/support/assertions.rb +8 -0
  85. data/lib/dm-core/support/chainable.rb +18 -0
  86. data/lib/dm-core/support/deprecate.rb +12 -0
  87. data/lib/dm-core/support/descendant_set.rb +89 -0
  88. data/lib/dm-core/support/equalizer.rb +48 -0
  89. data/lib/dm-core/support/ext/array.rb +22 -0
  90. data/lib/dm-core/support/ext/blank.rb +25 -0
  91. data/lib/dm-core/support/ext/hash.rb +67 -0
  92. data/lib/dm-core/support/ext/module.rb +47 -0
  93. data/lib/dm-core/support/ext/object.rb +57 -0
  94. data/lib/dm-core/support/ext/string.rb +24 -0
  95. data/lib/dm-core/support/ext/try_dup.rb +12 -0
  96. data/lib/dm-core/support/hook.rb +388 -0
  97. data/lib/dm-core/support/inflections.rb +60 -0
  98. data/lib/dm-core/support/inflector/inflections.rb +211 -0
  99. data/lib/dm-core/support/inflector/methods.rb +151 -0
  100. data/lib/dm-core/support/lazy_array.rb +451 -0
  101. data/lib/dm-core/support/local_object_space.rb +13 -0
  102. data/lib/dm-core/support/logger.rb +201 -0
  103. data/lib/dm-core/support/mash.rb +176 -0
  104. data/lib/dm-core/support/naming_conventions.rb +109 -0
  105. data/lib/dm-core/support/ordered_set.rb +381 -0
  106. data/lib/dm-core/support/subject.rb +33 -0
  107. data/lib/dm-core/support/subject_set.rb +251 -0
  108. data/lib/dm-core/version.rb +3 -0
  109. data/lib/dm-core.rb +274 -0
  110. data/script/performance.rb +275 -0
  111. data/script/profile.rb +218 -0
  112. data/spec/lib/rspec_immediate_feedback_formatter.rb +54 -0
  113. data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +69 -0
  114. data/spec/public/associations/many_to_many_spec.rb +197 -0
  115. data/spec/public/associations/many_to_one_spec.rb +83 -0
  116. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +40 -0
  117. data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +49 -0
  118. data/spec/public/associations/one_to_many_spec.rb +81 -0
  119. data/spec/public/associations/one_to_one_spec.rb +176 -0
  120. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +46 -0
  121. data/spec/public/collection_spec.rb +69 -0
  122. data/spec/public/finalize_spec.rb +77 -0
  123. data/spec/public/model/hook_spec.rb +245 -0
  124. data/spec/public/model/property_spec.rb +91 -0
  125. data/spec/public/model/relationship_spec.rb +1040 -0
  126. data/spec/public/model_spec.rb +456 -0
  127. data/spec/public/property/binary_spec.rb +43 -0
  128. data/spec/public/property/boolean_spec.rb +21 -0
  129. data/spec/public/property/class_spec.rb +27 -0
  130. data/spec/public/property/date_spec.rb +21 -0
  131. data/spec/public/property/date_time_spec.rb +21 -0
  132. data/spec/public/property/decimal_spec.rb +23 -0
  133. data/spec/public/property/discriminator_spec.rb +134 -0
  134. data/spec/public/property/float_spec.rb +22 -0
  135. data/spec/public/property/integer_spec.rb +22 -0
  136. data/spec/public/property/object_spec.rb +117 -0
  137. data/spec/public/property/serial_spec.rb +22 -0
  138. data/spec/public/property/string_spec.rb +21 -0
  139. data/spec/public/property/text_spec.rb +62 -0
  140. data/spec/public/property/time_spec.rb +21 -0
  141. data/spec/public/property_spec.rb +333 -0
  142. data/spec/public/resource/state_spec.rb +72 -0
  143. data/spec/public/resource_spec.rb +289 -0
  144. data/spec/public/sel_spec.rb +53 -0
  145. data/spec/public/setup_spec.rb +145 -0
  146. data/spec/public/shared/association_collection_shared_spec.rb +309 -0
  147. data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
  148. data/spec/public/shared/collection_shared_spec.rb +1637 -0
  149. data/spec/public/shared/finder_shared_spec.rb +1647 -0
  150. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  151. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +13 -0
  152. data/spec/semipublic/associations/many_to_many_spec.rb +94 -0
  153. data/spec/semipublic/associations/many_to_one_spec.rb +63 -0
  154. data/spec/semipublic/associations/one_to_many_spec.rb +55 -0
  155. data/spec/semipublic/associations/one_to_one_spec.rb +53 -0
  156. data/spec/semipublic/associations/relationship_spec.rb +200 -0
  157. data/spec/semipublic/associations_spec.rb +177 -0
  158. data/spec/semipublic/collection_spec.rb +110 -0
  159. data/spec/semipublic/model_spec.rb +96 -0
  160. data/spec/semipublic/property/binary_spec.rb +13 -0
  161. data/spec/semipublic/property/boolean_spec.rb +47 -0
  162. data/spec/semipublic/property/class_spec.rb +33 -0
  163. data/spec/semipublic/property/date_spec.rb +43 -0
  164. data/spec/semipublic/property/date_time_spec.rb +46 -0
  165. data/spec/semipublic/property/decimal_spec.rb +83 -0
  166. data/spec/semipublic/property/discriminator_spec.rb +19 -0
  167. data/spec/semipublic/property/float_spec.rb +82 -0
  168. data/spec/semipublic/property/integer_spec.rb +82 -0
  169. data/spec/semipublic/property/lookup_spec.rb +29 -0
  170. data/spec/semipublic/property/serial_spec.rb +13 -0
  171. data/spec/semipublic/property/string_spec.rb +13 -0
  172. data/spec/semipublic/property/text_spec.rb +31 -0
  173. data/spec/semipublic/property/time_spec.rb +50 -0
  174. data/spec/semipublic/property_spec.rb +114 -0
  175. data/spec/semipublic/query/conditions/comparison_spec.rb +1502 -0
  176. data/spec/semipublic/query/conditions/operation_spec.rb +1296 -0
  177. data/spec/semipublic/query/path_spec.rb +471 -0
  178. data/spec/semipublic/query_spec.rb +3665 -0
  179. data/spec/semipublic/resource/state/clean_spec.rb +89 -0
  180. data/spec/semipublic/resource/state/deleted_spec.rb +79 -0
  181. data/spec/semipublic/resource/state/dirty_spec.rb +163 -0
  182. data/spec/semipublic/resource/state/immutable_spec.rb +107 -0
  183. data/spec/semipublic/resource/state/transient_spec.rb +163 -0
  184. data/spec/semipublic/resource/state_spec.rb +230 -0
  185. data/spec/semipublic/resource_spec.rb +23 -0
  186. data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
  187. data/spec/semipublic/shared/resource_shared_spec.rb +198 -0
  188. data/spec/semipublic/shared/resource_state_shared_spec.rb +91 -0
  189. data/spec/semipublic/shared/subject_shared_spec.rb +79 -0
  190. data/spec/spec_helper.rb +34 -0
  191. data/spec/support/core_ext/hash.rb +10 -0
  192. data/spec/support/core_ext/inheritable_attributes.rb +46 -0
  193. data/spec/support/properties/huge_integer.rb +17 -0
  194. data/spec/unit/array_spec.rb +23 -0
  195. data/spec/unit/blank_spec.rb +73 -0
  196. data/spec/unit/data_mapper/ordered_set/append_spec.rb +26 -0
  197. data/spec/unit/data_mapper/ordered_set/clear_spec.rb +24 -0
  198. data/spec/unit/data_mapper/ordered_set/delete_spec.rb +28 -0
  199. data/spec/unit/data_mapper/ordered_set/each_spec.rb +19 -0
  200. data/spec/unit/data_mapper/ordered_set/empty_spec.rb +20 -0
  201. data/spec/unit/data_mapper/ordered_set/entries_spec.rb +22 -0
  202. data/spec/unit/data_mapper/ordered_set/eql_spec.rb +51 -0
  203. data/spec/unit/data_mapper/ordered_set/equal_value_spec.rb +84 -0
  204. data/spec/unit/data_mapper/ordered_set/hash_spec.rb +12 -0
  205. data/spec/unit/data_mapper/ordered_set/include_spec.rb +23 -0
  206. data/spec/unit/data_mapper/ordered_set/index_spec.rb +28 -0
  207. data/spec/unit/data_mapper/ordered_set/initialize_spec.rb +32 -0
  208. data/spec/unit/data_mapper/ordered_set/merge_spec.rb +36 -0
  209. data/spec/unit/data_mapper/ordered_set/shared/append_spec.rb +24 -0
  210. data/spec/unit/data_mapper/ordered_set/shared/clear_spec.rb +9 -0
  211. data/spec/unit/data_mapper/ordered_set/shared/delete_spec.rb +25 -0
  212. data/spec/unit/data_mapper/ordered_set/shared/each_spec.rb +17 -0
  213. data/spec/unit/data_mapper/ordered_set/shared/empty_spec.rb +9 -0
  214. data/spec/unit/data_mapper/ordered_set/shared/entries_spec.rb +9 -0
  215. data/spec/unit/data_mapper/ordered_set/shared/include_spec.rb +9 -0
  216. data/spec/unit/data_mapper/ordered_set/shared/index_spec.rb +13 -0
  217. data/spec/unit/data_mapper/ordered_set/shared/initialize_spec.rb +28 -0
  218. data/spec/unit/data_mapper/ordered_set/shared/merge_spec.rb +28 -0
  219. data/spec/unit/data_mapper/ordered_set/shared/size_spec.rb +13 -0
  220. data/spec/unit/data_mapper/ordered_set/shared/to_ary_spec.rb +11 -0
  221. data/spec/unit/data_mapper/ordered_set/size_spec.rb +27 -0
  222. data/spec/unit/data_mapper/ordered_set/to_ary_spec.rb +23 -0
  223. data/spec/unit/data_mapper/subject_set/append_spec.rb +47 -0
  224. data/spec/unit/data_mapper/subject_set/clear_spec.rb +34 -0
  225. data/spec/unit/data_mapper/subject_set/delete_spec.rb +40 -0
  226. data/spec/unit/data_mapper/subject_set/each_spec.rb +30 -0
  227. data/spec/unit/data_mapper/subject_set/empty_spec.rb +31 -0
  228. data/spec/unit/data_mapper/subject_set/entries_spec.rb +31 -0
  229. data/spec/unit/data_mapper/subject_set/get_spec.rb +34 -0
  230. data/spec/unit/data_mapper/subject_set/include_spec.rb +32 -0
  231. data/spec/unit/data_mapper/subject_set/named_spec.rb +33 -0
  232. data/spec/unit/data_mapper/subject_set/shared/append_spec.rb +18 -0
  233. data/spec/unit/data_mapper/subject_set/shared/clear_spec.rb +9 -0
  234. data/spec/unit/data_mapper/subject_set/shared/delete_spec.rb +9 -0
  235. data/spec/unit/data_mapper/subject_set/shared/each_spec.rb +9 -0
  236. data/spec/unit/data_mapper/subject_set/shared/empty_spec.rb +9 -0
  237. data/spec/unit/data_mapper/subject_set/shared/entries_spec.rb +9 -0
  238. data/spec/unit/data_mapper/subject_set/shared/get_spec.rb +9 -0
  239. data/spec/unit/data_mapper/subject_set/shared/include_spec.rb +9 -0
  240. data/spec/unit/data_mapper/subject_set/shared/named_spec.rb +9 -0
  241. data/spec/unit/data_mapper/subject_set/shared/size_spec.rb +13 -0
  242. data/spec/unit/data_mapper/subject_set/shared/to_ary_spec.rb +9 -0
  243. data/spec/unit/data_mapper/subject_set/shared/values_at_spec.rb +44 -0
  244. data/spec/unit/data_mapper/subject_set/size_spec.rb +42 -0
  245. data/spec/unit/data_mapper/subject_set/to_ary_spec.rb +34 -0
  246. data/spec/unit/data_mapper/subject_set/values_at_spec.rb +57 -0
  247. data/spec/unit/hash_spec.rb +27 -0
  248. data/spec/unit/hook_spec.rb +1216 -0
  249. data/spec/unit/inflections_spec.rb +14 -0
  250. data/spec/unit/lazy_array_spec.rb +1949 -0
  251. data/spec/unit/mash_spec.rb +289 -0
  252. data/spec/unit/module_spec.rb +70 -0
  253. data/spec/unit/object_spec.rb +38 -0
  254. data/spec/unit/try_dup_spec.rb +46 -0
  255. data/tasks/ci.rake +1 -0
  256. data/tasks/spec.rake +18 -0
  257. data/tasks/yard.rake +9 -0
  258. data/tasks/yardstick.rake +19 -0
  259. metadata +323 -0
@@ -0,0 +1,111 @@
1
+ shared_examples 'A Collection supporting Strategic Eager Loading' do
2
+ describe 'using SEL when looping within a loop' do
3
+ before :all do
4
+ @many_to_many = @articles.kind_of?(DataMapper::Associations::ManyToMany::Collection)
5
+ end
6
+
7
+ before :all do
8
+ attributes = {}
9
+
10
+ unless @many_to_many
11
+ attributes[:author] = @author
12
+ end
13
+
14
+ @revision = @article.revisions.create(attributes.merge(:title => 'Revision'))
15
+
16
+ @new_article = @article_model.create(attributes.merge(:title => 'Sample Article'))
17
+ @new_revision = @new_article.revisions.create(attributes.merge(:title => 'New Revision'))
18
+ end
19
+
20
+ before :all do
21
+ @original_adapter = @adapter
22
+
23
+ @adapter.singleton_class.class_eval do
24
+ def eql?(other)
25
+ super || self == other
26
+ end
27
+ end
28
+
29
+ @adapter = DataMapper::Repository.adapters[@adapter.name] = CounterAdapter.new(@adapter)
30
+ @repository.instance_variable_set(:@adapter, @adapter)
31
+ @articles.instance_variable_get(:@query).instance_variable_set(:@repository, @repository)
32
+ end
33
+
34
+ before :all do
35
+ @results = []
36
+
37
+ @articles.each do |article|
38
+ article.revisions.each do |revision|
39
+ @results << [ article, revision ]
40
+ end
41
+ end
42
+ end
43
+
44
+ after :all do
45
+ @adapter = @original_adapter
46
+ end
47
+
48
+ it "only executes the Adapter#read #{loaded ? 'once' : 'twice'}" do
49
+ expect(@adapter.counts[:read]).to eq (loaded ? 1 : 2)
50
+ end
51
+
52
+ it 'returns the expected results' do
53
+ # if the collection is already loaded, then when it iterates it will
54
+ # not know about the newly added articles and their revisions
55
+ if loaded
56
+ expect(@results).to eq [[@article, @revision]]
57
+ else
58
+ pending 'TODO: make m:m not kick when delegating to the relationship' if @many_to_many
59
+
60
+ expect(@results).to eq [[@article, @revision], [@new_article, @new_revision]]
61
+ end
62
+ end
63
+ end
64
+ end
65
+
66
+ shared_examples 'A Resource supporting Strategic Eager Loading' do
67
+ describe 'using SEL when inside a Collection' do
68
+ before :all do
69
+ @referrer = @user_model.create(:name => 'Referrer', :comment => @comment)
70
+
71
+ @user.update(:referrer => @referrer)
72
+
73
+ @new_user = @user_model.create(:name => 'Another User', :referrer => @referrer, :comment => @comment)
74
+ end
75
+
76
+ before :all do
77
+ @original_adapter = @adapter
78
+
79
+ @adapter.singleton_class.class_eval do
80
+ def eql?(other)
81
+ super || other == self
82
+ end
83
+ end
84
+
85
+ @adapter = DataMapper::Repository.adapters[@adapter.name] = CounterAdapter.new(@adapter)
86
+ @repository.instance_variable_set(:@adapter, @adapter)
87
+ end
88
+
89
+ before :all do
90
+ @results = @user_model.all.map do |user|
91
+ [ user, user.referrer ]
92
+ end
93
+
94
+ # some storage engines return the data in a different order
95
+ @results.sort!
96
+ end
97
+
98
+ after :all do
99
+ @adapter = @original_adapter
100
+ end
101
+
102
+ it 'only executes the Adapter#read twice' do
103
+ expect(@adapter.counts[:read]).to eq 2
104
+ end
105
+
106
+ it 'returns the expected results' do
107
+ # results are ordered alphabetically by the User name
108
+ expect(@results).to eq [[@new_user, @referrer], [@referrer, nil], [@user, @referrer]]
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,184 @@
1
+ shared_examples 'A semipublic Property' do
2
+ before :all do
3
+ %w(@type @name @value @other_value).each do |ivar|
4
+ raise "+#{ivar}+ should be defined in before block" unless instance_variable_defined?(ivar)
5
+ end
6
+
7
+ module ::Blog
8
+ class Article
9
+ include DataMapper::Resource
10
+ property :id, Serial
11
+ end
12
+ end
13
+
14
+ @model = Blog::Article
15
+ @options ||= {}
16
+ @property = @type.new(@model, @name, @options)
17
+ end
18
+
19
+ describe '.new' do
20
+ describe 'when provided no options' do
21
+ it 'returns a Property' do
22
+ expect(@property).to be_kind_of(@type)
23
+ end
24
+
25
+ it 'sets the load_as' do
26
+ expect(@property.load_as).to be(@type.load_as)
27
+ end
28
+
29
+ it 'sets the model' do
30
+ expect(@property.model).to equal(@model)
31
+ end
32
+
33
+ it 'sets the options to the default' do
34
+ expect(@property.options).to eq @type.options.merge(@options)
35
+ end
36
+ end
37
+
38
+ %i(index unique_index unique lazy).each do |attribute|
39
+ [true, false, :title, [:title]].each do |value|
40
+ describe "when provided #{(options = {attribute => value}).inspect}" do
41
+ before :all do
42
+ @property = @type.new(@model, @name, @options.merge(options))
43
+ end
44
+
45
+ it 'returns a Property' do
46
+ expect(@property).to be_kind_of(@type)
47
+ end
48
+
49
+ it 'sets the model' do
50
+ expect(@property.model).to equal(@model)
51
+ end
52
+
53
+ it 'sets the load_as' do
54
+ expect(@property.load_as).to be(@type.load_as)
55
+ end
56
+
57
+ it "sets the options to #{options.inspect}" do
58
+ expect(@property.options).to eq @type.options.merge(@options.merge(options))
59
+ end
60
+ end
61
+ end
62
+
63
+ [[], nil].each do |value|
64
+ describe "when provided #{(invalid_options = {attribute => value}).inspect}" do
65
+ it 'raises an exception' do
66
+ expect {
67
+ @type.new(@model, @name, @options.merge(invalid_options))
68
+ }.to raise_error(ArgumentError, "options[#{attribute.inspect}] must be either true, false, a Symbol or an Array of Symbols")
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ describe '#load' do
76
+ subject { @property.load(@value) }
77
+
78
+ before do
79
+ expect(@property).to receive(:typecast).with(@value).and_return(@value)
80
+ end
81
+
82
+ it { is_expected.to eql(@value) }
83
+ end
84
+
85
+ describe '#typecast' do
86
+ describe "when is able to do typecasting on it's own" do
87
+ it 'delegates all the work to the type' do
88
+ return_value = double(@other_value)
89
+ expect(@property).to receive(:typecast_to_primitive).with(@invalid_value).and_return(return_value)
90
+ @property.typecast(@invalid_value)
91
+ end
92
+ end
93
+
94
+ describe 'when value is nil' do
95
+ it 'returns value unchanged' do
96
+ expect(@property.typecast(nil)).to be(nil)
97
+ end
98
+
99
+ describe 'when value is a Ruby primitive' do
100
+ it 'returns value unchanged' do
101
+ expect(@property.typecast(@value)).to eq @value
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ describe '#valid?' do
108
+ describe 'when provided a valid value' do
109
+ it 'returns true' do
110
+ expect(@property.valid?(@value)).to be(true)
111
+ end
112
+ end
113
+
114
+ describe 'when provide an invalid value' do
115
+ it 'returns false' do
116
+ expect(@property.valid?(@invalid_value)).to be(false)
117
+ end
118
+ end
119
+
120
+ describe 'when provide a nil value when required' do
121
+ it 'returns false' do
122
+ @property = @type.new(@model, @name, @options.merge(required: true))
123
+ expect(@property.valid?(nil)).to be(false)
124
+ end
125
+ end
126
+
127
+ describe 'when provide a nil value when not required' do
128
+ it 'returns false' do
129
+ @property = @type.new(@model, @name, @options.merge(required: false))
130
+ expect(@property.valid?(nil)).to be(true)
131
+ end
132
+ end
133
+ end
134
+
135
+ describe '#assert_valid_value' do
136
+ subject do
137
+ @property.assert_valid_value(value)
138
+ end
139
+
140
+ shared_examples 'assert_valid_value on invalid value' do
141
+ it 'raises DataMapper::Property::InvalidValueError' do
142
+ expect { subject }.to(raise_error(DataMapper::Property::InvalidValueError) do |error|
143
+ expect(error.property).to eq @property
144
+ end)
145
+ end
146
+ end
147
+
148
+ describe 'when provided a valid value' do
149
+ let(:value) { @value }
150
+
151
+ it 'returns true' do
152
+ expect(subject).to be(true)
153
+ end
154
+ end
155
+
156
+ describe 'when provide an invalid value' do
157
+ let(:value) { @invalid_value }
158
+
159
+ it_behaves_like 'assert_valid_value on invalid value'
160
+ end
161
+
162
+ describe 'when provide a nil value when required' do
163
+ before do
164
+ @property = @type.new(@model, @name, @options.merge(required: true))
165
+ end
166
+
167
+ let(:value) { nil }
168
+
169
+ it_behaves_like 'assert_valid_value on invalid value'
170
+ end
171
+
172
+ describe 'when provide a nil value when not required' do
173
+ before do
174
+ @property = @type.new(@model, @name, @options.merge(required: false))
175
+ end
176
+
177
+ let(:value) { nil }
178
+
179
+ it 'returns true' do
180
+ expect(subject).to be(true)
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,261 @@
1
+ shared_examples 'DataMapper::Query::Conditions::AbstractComparison' do
2
+ before :all do
3
+ module ::Blog
4
+ class Article
5
+ include DataMapper::Resource
6
+
7
+ property :id, Serial
8
+ property :title, String, :required => true
9
+
10
+ belongs_to :parent, self, :required => false
11
+ has n, :children, self, :inverse => :parent
12
+ end
13
+ end
14
+
15
+ DataMapper.finalize
16
+
17
+ @model = Blog::Article
18
+ end
19
+
20
+ before do
21
+ class ::OtherComparison < DataMapper::Query::Conditions::AbstractComparison
22
+ slug :other
23
+ end
24
+ end
25
+
26
+ before do
27
+ @relationship = @model.relationships[:parent]
28
+ end
29
+
30
+ it { expect(subject.class).to respond_to(:new) }
31
+
32
+ describe '.new' do
33
+ subject { @comparison.class.new(@property, @value) }
34
+
35
+ it { is_expected.to be_kind_of(@comparison.class) }
36
+
37
+ it { expect(subject.subject).to equal(@property) }
38
+
39
+ it { expect(subject.value).to eq @value }
40
+ end
41
+
42
+ it { expect(subject.class).to respond_to(:slug) }
43
+
44
+ describe '.slug' do
45
+ describe 'with no arguments' do
46
+ subject { @comparison.class.slug }
47
+
48
+ it { is_expected.to eq @slug }
49
+ end
50
+
51
+ describe 'with an argument' do
52
+ subject { @comparison.class.slug(:other) }
53
+
54
+ it { is_expected.to eq :other }
55
+
56
+ # reset the slug
57
+ after { @comparison.class.slug(@slug) }
58
+ end
59
+ end
60
+
61
+ it { is_expected.to respond_to(:==) }
62
+
63
+ describe '#==' do
64
+ describe 'when the other AbstractComparison is equal' do
65
+ # artificially modify the object so #== will throw an
66
+ # exception if the equal? branch is not followed when heckling
67
+ before { @comparison.singleton_class.send(:undef_method, :slug) }
68
+
69
+ subject { @comparison == @comparison }
70
+
71
+ it { is_expected.to be(true) }
72
+ end
73
+
74
+ describe 'when the other AbstractComparison is the same class' do
75
+ subject { @comparison == DataMapper::Query::Conditions::Comparison.new(@slug, @property, @value) }
76
+
77
+ it { is_expected.to be(true) }
78
+ end
79
+
80
+ describe 'when the other AbstractComparison is a different class' do
81
+ subject { @comparison == DataMapper::Query::Conditions::Comparison.new(:other, @property, @value) }
82
+
83
+ it { is_expected.to be(false) }
84
+ end
85
+
86
+ describe 'when the other AbstractComparison is the same class, with different property' do
87
+ subject { @comparison == DataMapper::Query::Conditions::Comparison.new(@slug, @other_property, @value) }
88
+
89
+ it { is_expected.to be(false) }
90
+ end
91
+
92
+ describe 'when the other AbstractComparison is the same class, with different value' do
93
+ subject { @comparison == DataMapper::Query::Conditions::Comparison.new(@slug, @property, @other_value) }
94
+
95
+ it { is_expected.to be(false) }
96
+ end
97
+ end
98
+
99
+ it { is_expected.to respond_to(:eql?) }
100
+
101
+ describe '#eql?' do
102
+ describe 'when the other AbstractComparison is equal' do
103
+ # artificially modify the object so #eql? will throw an
104
+ # exception if the equal? branch is not followed when heckling
105
+ before { @comparison.singleton_class.send(:undef_method, :slug) }
106
+
107
+ subject { @comparison.eql?(@comparison) }
108
+
109
+ it { is_expected.to be(true) }
110
+ end
111
+
112
+ describe 'when the other AbstractComparison is the same class' do
113
+ subject { @comparison.eql?(DataMapper::Query::Conditions::Comparison.new(@slug, @property, @value)) }
114
+
115
+ it { is_expected.to be(true) }
116
+ end
117
+
118
+ describe 'when the other AbstractComparison is a different class' do
119
+ subject { @comparison.eql?(DataMapper::Query::Conditions::Comparison.new(:other, @property, @value)) }
120
+
121
+ it { is_expected.to be(false) }
122
+ end
123
+
124
+ describe 'when the other AbstractComparison is the same class, with different property' do
125
+ subject { @comparison.eql?(DataMapper::Query::Conditions::Comparison.new(@slug, @other_property, @value)) }
126
+
127
+ it { is_expected.to be(false) }
128
+ end
129
+
130
+ describe 'when the other AbstractComparison is the same class, with different value' do
131
+ subject { @comparison.eql?(DataMapper::Query::Conditions::Comparison.new(@slug, @property, @other_value)) }
132
+
133
+ it { is_expected.to be(false) }
134
+ end
135
+ end
136
+
137
+ it { is_expected.to respond_to(:hash) }
138
+
139
+ describe '#hash' do
140
+ subject { @comparison.hash }
141
+
142
+ it 'matches the same AbstractComparison with the same property and value' do
143
+ is_expected.to eq DataMapper::Query::Conditions::Comparison.new(@slug, @property, @value).hash
144
+ end
145
+
146
+ it 'does not match the same AbstractComparison with different property' do
147
+ is_expected.not_to eq DataMapper::Query::Conditions::Comparison.new(@slug, @other_property, @value).hash
148
+ end
149
+
150
+ it 'does not match the same AbstractComparison with different value' do
151
+ is_expected.not_to eq DataMapper::Query::Conditions::Comparison.new(@slug, @property, @other_value).hash
152
+ end
153
+
154
+ it 'does not match a different AbstractComparison with the same property and value' do
155
+ is_expected.not_to eq @other.hash
156
+ end
157
+
158
+ it 'does not match a different AbstractComparison with different property' do
159
+ is_expected.not_to eq @other.class.new(@other_property, @value).hash
160
+ end
161
+
162
+ it 'does not match a different AbstractComparison with different value' do
163
+ is_expected.not_to eq @other.class.new(@property, @other_value).hash
164
+ end
165
+ end
166
+
167
+ it { is_expected.to respond_to(:loaded_value) }
168
+
169
+ describe '#loaded_value' do
170
+ subject { @comparison.loaded_value }
171
+
172
+ it { is_expected.to eq @value }
173
+ end
174
+
175
+ it { is_expected.to respond_to(:parent) }
176
+
177
+ describe '#parent' do
178
+ subject { @comparison.parent }
179
+
180
+ describe 'is nil by default' do
181
+ it { is_expected.to be_nil }
182
+ end
183
+
184
+ describe 'relates to parent operation' do
185
+ before do
186
+ @operation = DataMapper::Query::Conditions::Operation.new(:and)
187
+ @comparison.parent = @operation
188
+ end
189
+
190
+ it { is_expected.to be_equal(@operation) }
191
+ end
192
+ end
193
+
194
+ it { is_expected.to respond_to(:parent=) }
195
+
196
+ describe '#parent=' do
197
+ before do
198
+ @operation = DataMapper::Query::Conditions::Operation.new(:and)
199
+ end
200
+
201
+ subject { @comparison.parent = @operation }
202
+
203
+ it { is_expected.to equal(@operation) }
204
+
205
+ it 'changes the parent' do
206
+ expect(method(:subject)).to change(@comparison, :parent)
207
+ .from(nil)
208
+ .to(@operation)
209
+ end
210
+ end
211
+
212
+ it { is_expected.to respond_to(:property?) }
213
+
214
+ describe '#property?' do
215
+ subject { @comparison.property? }
216
+
217
+ it { is_expected.to be(true) }
218
+ end
219
+
220
+ it { is_expected.to respond_to(:slug) }
221
+
222
+ describe '#slug' do
223
+ subject { @comparison.slug }
224
+
225
+ it { is_expected.to eq @slug }
226
+ end
227
+
228
+ it { is_expected.to respond_to(:subject) }
229
+
230
+ describe '#subject' do
231
+ subject { @comparison.subject }
232
+
233
+ it { is_expected.to be_equal(@property) }
234
+ end
235
+
236
+ it { is_expected.to respond_to(:valid?) }
237
+
238
+ describe '#valid?' do
239
+ subject { @comparison.valid? }
240
+
241
+ describe 'when the value is valid for the subject' do
242
+ it { is_expected.to be(true) }
243
+ end
244
+
245
+ describe 'when the value is not valid for the subject' do
246
+ before do
247
+ @comparison = DataMapper::Query::Conditions::Comparison.new(@slug, @property, nil)
248
+ end
249
+
250
+ it { is_expected.to be(false) }
251
+ end
252
+ end
253
+
254
+ it { is_expected.to respond_to(:value) }
255
+
256
+ describe '#value' do
257
+ subject { @comparison.value }
258
+
259
+ it { is_expected.to eq @value }
260
+ end
261
+ end
@@ -0,0 +1,8 @@
1
+ module DataMapper
2
+ module Assertions
3
+ def assert_kind_of(name, value, *klasses)
4
+ klasses.each { |k| return if value.kind_of?(k) }
5
+ raise ArgumentError, "+#{name}+ should be #{klasses.map { |k| k.name } * ' or '}, but was #{value.class.name}", caller(2)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,18 @@
1
+ module DataMapper
2
+ module Chainable
3
+
4
+ # @api private
5
+ def chainable(&block)
6
+ mod = Module.new(&block)
7
+ include mod
8
+ mod
9
+ end
10
+
11
+ # @api private
12
+ def extendable(&block)
13
+ mod = Module.new(&block)
14
+ extend mod
15
+ mod
16
+ end
17
+ end # module Chainable
18
+ end # module DataMapper
@@ -0,0 +1,12 @@
1
+ module DataMapper
2
+ module Deprecate
3
+ def deprecate(old_method, new_method)
4
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
5
+ def #{old_method}(*args, &block)
6
+ warn "\#{self.class}##{old_method} is deprecated, use \#{self.class}##{new_method} instead (\#{caller.first})"
7
+ send(#{new_method.inspect}, *args, &block)
8
+ end
9
+ RUBY
10
+ end
11
+ end # module Deprecate
12
+ end # module DataMapper
@@ -0,0 +1,89 @@
1
+ module DataMapper
2
+ class DescendantSet
3
+ include Enumerable
4
+
5
+ # Initialize a DescendantSet instance
6
+ #
7
+ # @param [#to_ary] descendants
8
+ # initialize with the descendants
9
+ #
10
+ # @api private
11
+ def initialize(descendants = [])
12
+ @descendants = SubjectSet.new(descendants)
13
+ end
14
+
15
+ # Copy a DescendantSet instance
16
+ #
17
+ # @param [DescendantSet] original
18
+ # the original descendants
19
+ #
20
+ # @api private
21
+ def initialize_copy(original)
22
+ @descendants = @descendants.dup
23
+ end
24
+
25
+ # Add a descendant
26
+ #
27
+ # @param [Module] descendant
28
+ #
29
+ # @return [DescendantSet]
30
+ # self
31
+ #
32
+ # @api private
33
+ def <<(descendant)
34
+ @descendants << descendant
35
+ self
36
+ end
37
+
38
+ # Remove a descendant
39
+ #
40
+ # Also removes from all descendants
41
+ #
42
+ # @param [Module] descendant
43
+ #
44
+ # @return [DescendantSet]
45
+ # self
46
+ #
47
+ # @api private
48
+ def delete(descendant)
49
+ @descendants.delete(descendant)
50
+ each { |d| d.descendants.delete(descendant) }
51
+ end
52
+
53
+ # Iterate over each descendant
54
+ #
55
+ # @yield [descendant]
56
+ # @yieldparam [Module] descendant
57
+ #
58
+ # @return [DescendantSet]
59
+ # self
60
+ #
61
+ # @api private
62
+ def each
63
+ @descendants.each do |descendant|
64
+ yield descendant
65
+ descendant.descendants.each { |dd| yield dd }
66
+ end
67
+ self
68
+ end
69
+
70
+ # Test if there are any descendants
71
+ #
72
+ # @return [Boolean]
73
+ #
74
+ # @api private
75
+ def empty?
76
+ @descendants.empty?
77
+ end
78
+
79
+ # Removes all entries and returns self
80
+ #
81
+ # @return [DescendantSet] self
82
+ #
83
+ # @api private
84
+ def clear
85
+ @descendants.clear
86
+ end
87
+
88
+ end # class DescendantSet
89
+ end # module DataMapper