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,21 @@
1
+ module DataMapper
2
+ module Spec
3
+ module CollectionHelpers
4
+ module GroupMethods
5
+ def self.extended(base)
6
+ base.class_inheritable_accessor :loaded
7
+ base.loaded = false
8
+ super
9
+ end
10
+
11
+ def should_not_be_a_kicker(ivar = :@articles)
12
+ unless loaded
13
+ it 'should not be a kicker' do
14
+ instance_variable_get(ivar).should_not be_loaded
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,38 @@
1
+ class CounterAdapter < DataMapper::Adapters::AbstractAdapter
2
+ instance_methods.each do |method|
3
+ next if method =~ /\A__/ ||
4
+ %w[ send class dup object_id kind_of? instance_of? respond_to? equal? freeze frozen? should should_not instance_variables instance_variable_set instance_variable_get instance_variable_defined? remove_instance_variable extend inspect copy_object ].include?(method.to_s)
5
+ undef_method method
6
+ end
7
+
8
+ attr_reader :counts
9
+
10
+ def kind_of?(klass)
11
+ super || @adapter.kind_of?(klass)
12
+ end
13
+
14
+ def instance_of?(klass)
15
+ super || @adapter.instance_of?(klass)
16
+ end
17
+
18
+ def respond_to?(method, include_private = false)
19
+ super || @adapter.respond_to?(method, include_private)
20
+ end
21
+
22
+ private
23
+
24
+ def initialize(adapter)
25
+ @counts = Hash.new { |hash, key| hash[key] = 0 }
26
+ @adapter = adapter
27
+ @count = 0
28
+ end
29
+
30
+ def increment_count_for(method)
31
+ @counts[method] += 1
32
+ end
33
+
34
+ def method_missing(method, *args, &block)
35
+ increment_count_for(method)
36
+ @adapter.send(method, *args, &block)
37
+ end
38
+ end
@@ -0,0 +1,50 @@
1
+ module DataMapper
2
+ module Spec
3
+ module PendingHelpers
4
+
5
+ def pending_if(*args)
6
+ message, boolean = parse_args(*args)
7
+
8
+ if boolean
9
+ pending(message) { yield }
10
+ else
11
+ yield
12
+ end
13
+ end
14
+
15
+ def rescue_if(*args)
16
+ message, boolean = parse_args(*args)
17
+
18
+ if boolean
19
+ raised = nil
20
+ begin
21
+ yield
22
+ raised = false
23
+ rescue Exception
24
+ raised = true
25
+ end
26
+
27
+ raise "should have raised: #{message || 'TODO'}" if raised == false
28
+ else
29
+ yield
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def parse_args(*args)
36
+ case args.map { |arg| arg.class }
37
+ when [ String, TrueClass ], [ String, FalseClass ] then args
38
+ when [ String, NilClass ] then [ args.first, false ]
39
+ when [ String ] then [ args.first, true ]
40
+ when [ TrueClass ], [ FalseClass ] then [ '', args.first ]
41
+ when [ NilClass ] then [ '', false ]
42
+ when [] then [ '', true ] # defaults
43
+ else
44
+ raise ArgumentError, "Invalid arguments: #{args.inspect}"
45
+ end
46
+ end
47
+
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,74 @@
1
+ module DataMapper
2
+ module Spec
3
+
4
+ module Helpers
5
+ def reset_raise_on_save_failure(object)
6
+ object.instance_eval do
7
+ if defined?(@raise_on_save_failure)
8
+ remove_instance_variable(:@raise_on_save_failure)
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ # global model cleanup
15
+ def self.cleanup_models
16
+ descendants = DataMapper::Model.descendants.to_a
17
+
18
+ while model = descendants.shift
19
+ model_name = model.name.to_s.strip
20
+
21
+ unless model_name.empty? || model_name[0] == ?#
22
+ parts = model_name.split('::')
23
+ constant_name = parts.pop.to_sym
24
+ base = parts.empty? ? Object : DataMapper::Ext::Object.full_const_get(parts.join('::'))
25
+
26
+ base.class_eval { remove_const(constant_name) if const_defined?(constant_name) }
27
+ end
28
+
29
+ remove_ivars(model)
30
+ model.instance_methods(false).each { |method| model.send(:undef_method, method) }
31
+
32
+ end
33
+
34
+ DataMapper::Model.descendants.clear
35
+ end
36
+
37
+ def self.remove_ivars(object, instance_variables = object.instance_variables)
38
+ seen = {}
39
+ stack = instance_variables.map { |var| [ object, var ] }
40
+
41
+ while node = stack.pop
42
+ object, ivar = node
43
+
44
+ # skip "global" and non-DM objects
45
+ next if object.kind_of?(DataMapper::Logger) ||
46
+ object.kind_of?(DataMapper::DescendantSet) ||
47
+ object.kind_of?(DataMapper::Adapters::AbstractAdapter) ||
48
+ object.class.name[0, 13] == 'DataObjects::'
49
+
50
+ # skip classes and modules in the DataMapper namespace
51
+ next if object.kind_of?(Module) &&
52
+ !object.name.nil? &&
53
+ object.name[0, 12] == 'DataMapper::'
54
+
55
+ # skip when the ivar is no longer defined in the object
56
+ next unless object.instance_variable_defined?(ivar)
57
+
58
+ value = object.instance_variable_get(ivar)
59
+
60
+ # skip descendant sets
61
+ next if value.kind_of?(DataMapper::DescendantSet)
62
+
63
+ object.__send__(:remove_instance_variable, ivar) unless object.frozen?
64
+
65
+ # skip when the value was seen
66
+ next if seen.key?(value.object_id)
67
+ seen[value.object_id] = true
68
+
69
+ stack.concat value.instance_variables.map { |ivar| [ value, ivar ] }
70
+ end
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,174 @@
1
+ require 'dm-core'
2
+
3
+ module DataMapper
4
+ module Spec
5
+
6
+ class << self
7
+
8
+ def root
9
+ @root ||= default_root
10
+ end
11
+
12
+ def root=(path)
13
+ @root = Pathname(path)
14
+ end
15
+
16
+ %w[setup setup! adapter adapter_name].each do |action|
17
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
18
+ def #{action}(kind = :default)
19
+ perform_action(kind, :#{action})
20
+ end
21
+ RUBY
22
+ end
23
+
24
+ def configure
25
+ @configured = begin
26
+ setup_logger
27
+ require_plugins
28
+ require_spec_adapter
29
+ true
30
+ end
31
+ end
32
+
33
+ def configured?
34
+ @configured
35
+ end
36
+
37
+ def setup_logger
38
+ if log = ENV['LOG']
39
+ logger = DataMapper::Logger.new(log_stream(log), :debug)
40
+ logger.auto_flush = true
41
+ end
42
+ end
43
+
44
+ def require_spec_adapter
45
+ desired_adapter = (ENV['ADAPTER'] || ENV['ADAPTERS'])
46
+ if desired_adapter.nil? || desired_adapter == 'in_memory'
47
+ ENV['ADAPTER_SUPPORTS'] = 'all'
48
+ Adapters.use(Adapters::InMemoryAdapter)
49
+ else
50
+ require "dm-#{desired_adapter}-adapter/spec/setup"
51
+ end
52
+ end
53
+
54
+ def require_plugins
55
+ adapter = (ENV['ADAPTER'] || ENV['ADAPTERS'])
56
+ plugins = ENV['PLUGINS'] || ENV['PLUGIN']
57
+ plugins = plugins.to_s.split(/[,\s]+/)
58
+ unless adapter == 'in_memory'
59
+ plugins.push('dm-migrations')
60
+ end
61
+ plugins.uniq.each { |plugin| require plugin }
62
+ end
63
+
64
+ def spec_adapters
65
+ @spec_adapters ||= {}
66
+ end
67
+
68
+ private
69
+
70
+ def perform_action(kind, action)
71
+ configure unless configured?
72
+ spec_adapters[kind].send(action)
73
+ end
74
+
75
+ def default_root
76
+ Pathname(Dir.pwd).join('spec')
77
+ end
78
+
79
+ def log_stream(log)
80
+ log == 'file' ? root.join('log/dm.log') : $stdout
81
+ end
82
+
83
+ end
84
+
85
+ module Adapters
86
+
87
+ def self.use(adapter_class)
88
+ Spec.spec_adapters[:default] = adapter_class.new(:default)
89
+ Spec.spec_adapters[:alternate] = adapter_class.new(:alternate)
90
+ end
91
+
92
+ class Adapter
93
+
94
+ attr_reader :name
95
+
96
+ def initialize(name)
97
+ @name = name.to_sym
98
+ end
99
+
100
+ def adapter
101
+ @adapter ||= setup!
102
+ end
103
+
104
+ alias_method :setup, :adapter
105
+
106
+ def setup!
107
+ adapter = DataMapper.setup(name, connection_uri)
108
+ test_connection(adapter)
109
+ adapter
110
+ rescue Exception => e
111
+ puts "Could not connect to the database using '#{connection_uri}' because of: #{e.inspect}"
112
+ end
113
+
114
+ def adapter_name
115
+ @adapter_name ||= infer_adapter_name
116
+ end
117
+
118
+ def connection_uri
119
+ "#{adapter_name}://#{username}:#{password}@#{host}/#{storage_name}"
120
+ end
121
+
122
+ def storage_name
123
+ send("#{name}_storage_name")
124
+ end
125
+
126
+ def default_storage_name
127
+ "datamapper_default_tests"
128
+ end
129
+
130
+ def alternate_storage_name
131
+ "datamapper_alternate_tests"
132
+ end
133
+
134
+ def username
135
+ ENV.fetch('DM_DB_USER', 'datamapper')
136
+ end
137
+
138
+ def password
139
+ ENV.fetch('DM_DB_PASSWORD', 'datamapper')
140
+ end
141
+
142
+ def host
143
+ ENV.fetch('DM_DB_HOST', 'localhost')
144
+ end
145
+
146
+ # Test the connection
147
+ #
148
+ # Overwrite this method if you need to perform custom connection testing
149
+ #
150
+ # @raise [Exception]
151
+ def test_connection(adapter)
152
+ if adapter.respond_to?(:select)
153
+ adapter.select('SELECT 1')
154
+ end
155
+ end
156
+
157
+ private
158
+
159
+ def infer_adapter_name
160
+ demodulized = DataMapper::Inflector.demodulize(self.class.name.chomp('Adapter'))
161
+ DataMapper::Inflector.underscore(demodulized).freeze
162
+ end
163
+
164
+ end
165
+
166
+ class InMemoryAdapter < Adapter
167
+ def connection_uri
168
+ { :adapter => :in_memory }
169
+ end
170
+ end
171
+
172
+ end
173
+ end
174
+ end
@@ -0,0 +1,341 @@
1
+ share_examples_for 'An Adapter' do
2
+
3
+ def self.adapter_supports?(*methods)
4
+
5
+ # FIXME obviously this needs a real fix!
6
+ # --------------------------------------
7
+ # Probably, delaying adapter_supports?
8
+ # to be executed after DataMapper.setup
9
+ # has been called will solve our current
10
+ # problem with described_type() being nil
11
+ # for as long as DataMapper.setup wasn't
12
+ # called
13
+ return true if ENV['ADAPTER_SUPPORTS'] == 'all'
14
+
15
+ methods.all? do |method|
16
+ # TODO: figure out a way to see if the instance method is only inherited
17
+ # from the Abstract Adapter, and not defined in it's class. If that is
18
+ # the case return false
19
+
20
+ # CRUD methods can be inherited from parent class
21
+ described_type.instance_methods.any? { |instance_method| method.to_s == instance_method.to_s }
22
+ end
23
+ end
24
+
25
+ # Hack to detect cases a let(:heffalump_model) is not present
26
+ unless instance_methods.map(&:to_s).include?('heffalump_model')
27
+ # This is the default Heffalup model. You can replace it with your own
28
+ # (using let/let!) # but # be shure the replacement provides the required
29
+ # properties.
30
+ let(:heffalump_model) do
31
+ model = Class.new do
32
+ include DataMapper::Resource
33
+ property :id, DataMapper::Property::Serial
34
+ property :color, DataMapper::Property::String
35
+ property :num_spots, DataMapper::Property::Integer
36
+ property :striped, DataMapper::Property::Boolean
37
+
38
+ # This is needed for DataMapper.finalize
39
+ def self.name; 'Heffalump'; end
40
+ end
41
+
42
+ DataMapper.finalize
43
+
44
+ model
45
+ end
46
+ end
47
+
48
+ before :all do
49
+ raise '+#adapter+ should be defined in a let(:adapter) block' unless respond_to?(:adapter)
50
+ raise '+#repository+ should be defined in a let(:repository) block' unless respond_to?(:repository)
51
+
52
+ DataMapper.finalize
53
+
54
+ # create all tables and constraints before each spec
55
+ if repository.respond_to?(:auto_migrate!)
56
+ heffalump_model.auto_migrate!
57
+ end
58
+ end
59
+
60
+ if adapter_supports?(:create)
61
+ describe '#create' do
62
+ it 'should not raise any errors' do
63
+ lambda {
64
+ heffalump_model.create(:color => 'peach')
65
+ }.should_not raise_error
66
+ end
67
+
68
+ it 'should set the identity field for the resource' do
69
+ heffalump = heffalump_model.new(:color => 'peach')
70
+ heffalump.id.should be_nil
71
+ heffalump.save
72
+ heffalump.id.should_not be_nil
73
+ end
74
+ end
75
+ else
76
+ it 'needs to support #create'
77
+ end
78
+
79
+ if adapter_supports?(:read)
80
+ describe '#read' do
81
+ before :all do
82
+ @heffalump = heffalump_model.create(:color => 'brownish hue')
83
+ #just going to borrow this, so I can check the return values
84
+ @query = heffalump_model.all.query
85
+ end
86
+
87
+ it 'should not raise any errors' do
88
+ lambda {
89
+ heffalump_model.all()
90
+ }.should_not raise_error
91
+ end
92
+
93
+ it 'should return stuff' do
94
+ heffalump_model.all.should be_include(@heffalump)
95
+ end
96
+ end
97
+ else
98
+ it 'needs to support #read'
99
+ end
100
+
101
+ if adapter_supports?(:update)
102
+ describe '#update' do
103
+ before do
104
+ @heffalump = heffalump_model.create(:color => 'indigo')
105
+ end
106
+
107
+ it 'should not raise any errors' do
108
+ lambda {
109
+ @heffalump.color = 'violet'
110
+ @heffalump.save
111
+ }.should_not raise_error
112
+ end
113
+
114
+ it 'should not alter the identity field' do
115
+ id = @heffalump.id
116
+ @heffalump.color = 'violet'
117
+ @heffalump.save
118
+ @heffalump.id.should == id
119
+ end
120
+
121
+ it 'should update altered fields' do
122
+ @heffalump.color = 'violet'
123
+ @heffalump.save
124
+ heffalump_model.get(*@heffalump.key).color.should == 'violet'
125
+ end
126
+
127
+ it 'should not alter other fields' do
128
+ color = @heffalump.color
129
+ @heffalump.num_spots = 3
130
+ @heffalump.save
131
+ heffalump_model.get(*@heffalump.key).color.should == color
132
+ end
133
+ end
134
+ else
135
+ it 'needs to support #update'
136
+ end
137
+
138
+ if adapter_supports?(:delete)
139
+ describe '#delete' do
140
+ before do
141
+ @heffalump = heffalump_model.create(:color => 'forest green')
142
+ end
143
+
144
+ it 'should not raise any errors' do
145
+ lambda {
146
+ @heffalump.destroy
147
+ }.should_not raise_error
148
+ end
149
+
150
+ it 'should delete the requested resource' do
151
+ id = @heffalump.id
152
+ @heffalump.destroy
153
+ heffalump_model.get(id).should be_nil
154
+ end
155
+ end
156
+ else
157
+ it 'needs to support #delete'
158
+ end
159
+
160
+ if adapter_supports?(:read, :create)
161
+ describe 'query matching' do
162
+ before :all do
163
+ @red = heffalump_model.create(:color => 'red')
164
+ @two = heffalump_model.create(:num_spots => 2)
165
+ @five = heffalump_model.create(:num_spots => 5)
166
+ end
167
+
168
+ describe 'conditions' do
169
+ describe 'eql' do
170
+ it 'should be able to search for objects included in an inclusive range of values' do
171
+ heffalump_model.all(:num_spots => 1..5).should be_include(@five)
172
+ end
173
+
174
+ it 'should be able to search for objects included in an exclusive range of values' do
175
+ heffalump_model.all(:num_spots => 1...6).should be_include(@five)
176
+ end
177
+
178
+ it 'should not be able to search for values not included in an inclusive range of values' do
179
+ heffalump_model.all(:num_spots => 1..4).should_not be_include(@five)
180
+ end
181
+
182
+ it 'should not be able to search for values not included in an exclusive range of values' do
183
+ heffalump_model.all(:num_spots => 1...5).should_not be_include(@five)
184
+ end
185
+ end
186
+
187
+ describe 'not' do
188
+ it 'should be able to search for objects with not equal value' do
189
+ heffalump_model.all(:color.not => 'red').should_not be_include(@red)
190
+ end
191
+
192
+ it 'should include objects that are not like the value' do
193
+ heffalump_model.all(:color.not => 'black').should be_include(@red)
194
+ end
195
+
196
+ it 'should be able to search for objects with not nil value' do
197
+ heffalump_model.all(:color.not => nil).should be_include(@red)
198
+ end
199
+
200
+ it 'should not include objects with a nil value' do
201
+ heffalump_model.all(:color.not => nil).should_not be_include(@two)
202
+ end
203
+
204
+ it 'should be able to search for object with a nil value using required properties' do
205
+ heffalump_model.all(:id.not => nil).should == [ @red, @two, @five ]
206
+ end
207
+
208
+ it 'should be able to search for objects not in an empty list (match all)' do
209
+ heffalump_model.all(:color.not => []).should == [ @red, @two, @five ]
210
+ end
211
+
212
+ it 'should be able to search for objects in an empty list and another OR condition (match none on the empty list)' do
213
+ heffalump_model.all(
214
+ :conditions => DataMapper::Query::Conditions::Operation.new(
215
+ :or,
216
+ DataMapper::Query::Conditions::Comparison.new(:in, heffalump_model.properties[:color], []),
217
+ DataMapper::Query::Conditions::Comparison.new(:in, heffalump_model.properties[:num_spots], [5])
218
+ )
219
+ ).should == [ @five ]
220
+ end
221
+
222
+ it 'should be able to search for objects not included in an array of values' do
223
+ heffalump_model.all(:num_spots.not => [ 1, 3, 5, 7 ]).should be_include(@two)
224
+ end
225
+
226
+ it 'should be able to search for objects not included in an array of values' do
227
+ heffalump_model.all(:num_spots.not => [ 1, 3, 5, 7 ]).should_not be_include(@five)
228
+ end
229
+
230
+ it 'should be able to search for objects not included in an inclusive range of values' do
231
+ heffalump_model.all(:num_spots.not => 1..4).should be_include(@five)
232
+ end
233
+
234
+ it 'should be able to search for objects not included in an exclusive range of values' do
235
+ heffalump_model.all(:num_spots.not => 1...5).should be_include(@five)
236
+ end
237
+
238
+ it 'should not be able to search for values not included in an inclusive range of values' do
239
+ heffalump_model.all(:num_spots.not => 1..5).should_not be_include(@five)
240
+ end
241
+
242
+ it 'should not be able to search for values not included in an exclusive range of values' do
243
+ heffalump_model.all(:num_spots.not => 1...6).should_not be_include(@five)
244
+ end
245
+ end
246
+
247
+ describe 'like' do
248
+ it 'should be able to search for objects that match value' do
249
+ heffalump_model.all(:color.like => '%ed').should be_include(@red)
250
+ end
251
+
252
+ it 'should not search for objects that do not match the value' do
253
+ heffalump_model.all(:color.like => '%blak%').should_not be_include(@red)
254
+ end
255
+ end
256
+
257
+ describe 'regexp' do
258
+ before do
259
+ if (defined?(DataMapper::Adapters::SqliteAdapter) && adapter.kind_of?(DataMapper::Adapters::SqliteAdapter) ||
260
+ defined?(DataMapper::Adapters::SqlserverAdapter) && adapter.kind_of?(DataMapper::Adapters::SqlserverAdapter))
261
+ pending 'delegate regexp matches to same system that the InMemory and YAML adapters use'
262
+ end
263
+ end
264
+
265
+ it 'should be able to search for objects that match value' do
266
+ heffalump_model.all(:color => /ed/).should be_include(@red)
267
+ end
268
+
269
+ it 'should not be able to search for objects that do not match the value' do
270
+ heffalump_model.all(:color => /blak/).should_not be_include(@red)
271
+ end
272
+
273
+ it 'should be able to do a negated search for objects that match value' do
274
+ heffalump_model.all(:color.not => /blak/).should be_include(@red)
275
+ end
276
+
277
+ it 'should not be able to do a negated search for objects that do not match value' do
278
+ heffalump_model.all(:color.not => /ed/).should_not be_include(@red)
279
+ end
280
+
281
+ end
282
+
283
+ describe 'gt' do
284
+ it 'should be able to search for objects with value greater than' do
285
+ heffalump_model.all(:num_spots.gt => 1).should be_include(@two)
286
+ end
287
+
288
+ it 'should not find objects with a value less than' do
289
+ heffalump_model.all(:num_spots.gt => 3).should_not be_include(@two)
290
+ end
291
+ end
292
+
293
+ describe 'gte' do
294
+ it 'should be able to search for objects with value greater than' do
295
+ heffalump_model.all(:num_spots.gte => 1).should be_include(@two)
296
+ end
297
+
298
+ it 'should be able to search for objects with values equal to' do
299
+ heffalump_model.all(:num_spots.gte => 2).should be_include(@two)
300
+ end
301
+
302
+ it 'should not find objects with a value less than' do
303
+ heffalump_model.all(:num_spots.gte => 3).should_not be_include(@two)
304
+ end
305
+ end
306
+
307
+ describe 'lt' do
308
+ it 'should be able to search for objects with value less than' do
309
+ heffalump_model.all(:num_spots.lt => 3).should be_include(@two)
310
+ end
311
+
312
+ it 'should not find objects with a value less than' do
313
+ heffalump_model.all(:num_spots.gt => 2).should_not be_include(@two)
314
+ end
315
+ end
316
+
317
+ describe 'lte' do
318
+ it 'should be able to search for objects with value less than' do
319
+ heffalump_model.all(:num_spots.lte => 3).should be_include(@two)
320
+ end
321
+
322
+ it 'should be able to search for objects with values equal to' do
323
+ heffalump_model.all(:num_spots.lte => 2).should be_include(@two)
324
+ end
325
+
326
+ it 'should not find objects with a value less than' do
327
+ heffalump_model.all(:num_spots.lte => 1).should_not be_include(@two)
328
+ end
329
+ end
330
+ end
331
+
332
+ describe 'limits' do
333
+ it 'should be able to limit the objects' do
334
+ heffalump_model.all(:limit => 2).length.should == 2
335
+ end
336
+ end
337
+ end
338
+ else
339
+ it 'needs to support #read and #create to test query matching'
340
+ end
341
+ end