ardm-core 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (259) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +29 -0
  3. data/.document +5 -0
  4. data/.gitignore +35 -0
  5. data/.travis.yml +23 -0
  6. data/.yardopts +1 -0
  7. data/Gemfile +63 -0
  8. data/LICENSE +20 -0
  9. data/README.rdoc +237 -0
  10. data/Rakefile +4 -0
  11. data/VERSION +1 -0
  12. data/ardm-core.gemspec +25 -0
  13. data/lib/ardm-core.rb +1 -0
  14. data/lib/dm-core.rb +285 -0
  15. data/lib/dm-core/adapters.rb +222 -0
  16. data/lib/dm-core/adapters/abstract_adapter.rb +236 -0
  17. data/lib/dm-core/adapters/in_memory_adapter.rb +113 -0
  18. data/lib/dm-core/associations/many_to_many.rb +496 -0
  19. data/lib/dm-core/associations/many_to_one.rb +296 -0
  20. data/lib/dm-core/associations/one_to_many.rb +345 -0
  21. data/lib/dm-core/associations/one_to_one.rb +86 -0
  22. data/lib/dm-core/associations/relationship.rb +663 -0
  23. data/lib/dm-core/backwards.rb +13 -0
  24. data/lib/dm-core/collection.rb +1514 -0
  25. data/lib/dm-core/core_ext/kernel.rb +23 -0
  26. data/lib/dm-core/core_ext/pathname.rb +6 -0
  27. data/lib/dm-core/core_ext/symbol.rb +10 -0
  28. data/lib/dm-core/identity_map.rb +7 -0
  29. data/lib/dm-core/model.rb +869 -0
  30. data/lib/dm-core/model/hook.rb +102 -0
  31. data/lib/dm-core/model/is.rb +32 -0
  32. data/lib/dm-core/model/property.rb +253 -0
  33. data/lib/dm-core/model/relationship.rb +377 -0
  34. data/lib/dm-core/model/scope.rb +89 -0
  35. data/lib/dm-core/property.rb +839 -0
  36. data/lib/dm-core/property/binary.rb +22 -0
  37. data/lib/dm-core/property/boolean.rb +31 -0
  38. data/lib/dm-core/property/class.rb +24 -0
  39. data/lib/dm-core/property/date.rb +45 -0
  40. data/lib/dm-core/property/date_time.rb +44 -0
  41. data/lib/dm-core/property/decimal.rb +50 -0
  42. data/lib/dm-core/property/discriminator.rb +46 -0
  43. data/lib/dm-core/property/float.rb +28 -0
  44. data/lib/dm-core/property/integer.rb +32 -0
  45. data/lib/dm-core/property/lookup.rb +29 -0
  46. data/lib/dm-core/property/numeric.rb +40 -0
  47. data/lib/dm-core/property/object.rb +28 -0
  48. data/lib/dm-core/property/serial.rb +13 -0
  49. data/lib/dm-core/property/string.rb +50 -0
  50. data/lib/dm-core/property/text.rb +12 -0
  51. data/lib/dm-core/property/time.rb +46 -0
  52. data/lib/dm-core/property/typecast/numeric.rb +32 -0
  53. data/lib/dm-core/property/typecast/time.rb +33 -0
  54. data/lib/dm-core/property_set.rb +177 -0
  55. data/lib/dm-core/query.rb +1444 -0
  56. data/lib/dm-core/query/conditions/comparison.rb +910 -0
  57. data/lib/dm-core/query/conditions/operation.rb +720 -0
  58. data/lib/dm-core/query/direction.rb +36 -0
  59. data/lib/dm-core/query/operator.rb +35 -0
  60. data/lib/dm-core/query/path.rb +114 -0
  61. data/lib/dm-core/query/sort.rb +39 -0
  62. data/lib/dm-core/relationship_set.rb +72 -0
  63. data/lib/dm-core/repository.rb +226 -0
  64. data/lib/dm-core/resource.rb +1228 -0
  65. data/lib/dm-core/resource/persistence_state.rb +75 -0
  66. data/lib/dm-core/resource/persistence_state/clean.rb +40 -0
  67. data/lib/dm-core/resource/persistence_state/deleted.rb +30 -0
  68. data/lib/dm-core/resource/persistence_state/dirty.rb +96 -0
  69. data/lib/dm-core/resource/persistence_state/immutable.rb +34 -0
  70. data/lib/dm-core/resource/persistence_state/persisted.rb +29 -0
  71. data/lib/dm-core/resource/persistence_state/transient.rb +78 -0
  72. data/lib/dm-core/spec/lib/adapter_helpers.rb +54 -0
  73. data/lib/dm-core/spec/lib/collection_helpers.rb +20 -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 +173 -0
  78. data/lib/dm-core/spec/shared/adapter_spec.rb +326 -0
  79. data/lib/dm-core/spec/shared/public/property_spec.rb +229 -0
  80. data/lib/dm-core/spec/shared/resource_spec.rb +1236 -0
  81. data/lib/dm-core/spec/shared/sel_spec.rb +111 -0
  82. data/lib/dm-core/spec/shared/semipublic/property_spec.rb +134 -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 +402 -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 +12 -0
  102. data/lib/dm-core/support/logger.rb +199 -0
  103. data/lib/dm-core/support/mash.rb +176 -0
  104. data/lib/dm-core/support/naming_conventions.rb +90 -0
  105. data/lib/dm-core/support/ordered_set.rb +380 -0
  106. data/lib/dm-core/support/subject.rb +33 -0
  107. data/lib/dm-core/support/subject_set.rb +250 -0
  108. data/lib/dm-core/version.rb +3 -0
  109. data/script/performance.rb +275 -0
  110. data/script/profile.rb +218 -0
  111. data/spec/lib/rspec_immediate_feedback_formatter.rb +54 -0
  112. data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +68 -0
  113. data/spec/public/associations/many_to_many_spec.rb +197 -0
  114. data/spec/public/associations/many_to_one_spec.rb +83 -0
  115. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +40 -0
  116. data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +49 -0
  117. data/spec/public/associations/one_to_many_spec.rb +81 -0
  118. data/spec/public/associations/one_to_one_spec.rb +176 -0
  119. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +46 -0
  120. data/spec/public/collection_spec.rb +69 -0
  121. data/spec/public/finalize_spec.rb +76 -0
  122. data/spec/public/model/hook_spec.rb +246 -0
  123. data/spec/public/model/property_spec.rb +88 -0
  124. data/spec/public/model/relationship_spec.rb +1040 -0
  125. data/spec/public/model_spec.rb +458 -0
  126. data/spec/public/property/binary_spec.rb +41 -0
  127. data/spec/public/property/boolean_spec.rb +22 -0
  128. data/spec/public/property/class_spec.rb +28 -0
  129. data/spec/public/property/date_spec.rb +22 -0
  130. data/spec/public/property/date_time_spec.rb +22 -0
  131. data/spec/public/property/decimal_spec.rb +23 -0
  132. data/spec/public/property/discriminator_spec.rb +135 -0
  133. data/spec/public/property/float_spec.rb +22 -0
  134. data/spec/public/property/integer_spec.rb +22 -0
  135. data/spec/public/property/object_spec.rb +107 -0
  136. data/spec/public/property/serial_spec.rb +22 -0
  137. data/spec/public/property/string_spec.rb +22 -0
  138. data/spec/public/property/text_spec.rb +63 -0
  139. data/spec/public/property/time_spec.rb +22 -0
  140. data/spec/public/property_spec.rb +341 -0
  141. data/spec/public/resource_spec.rb +284 -0
  142. data/spec/public/sel_spec.rb +53 -0
  143. data/spec/public/setup_spec.rb +145 -0
  144. data/spec/public/shared/association_collection_shared_spec.rb +309 -0
  145. data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
  146. data/spec/public/shared/collection_shared_spec.rb +1669 -0
  147. data/spec/public/shared/finder_shared_spec.rb +1629 -0
  148. data/spec/rcov.opts +6 -0
  149. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  150. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +12 -0
  151. data/spec/semipublic/associations/many_to_many_spec.rb +94 -0
  152. data/spec/semipublic/associations/many_to_one_spec.rb +63 -0
  153. data/spec/semipublic/associations/one_to_many_spec.rb +55 -0
  154. data/spec/semipublic/associations/one_to_one_spec.rb +53 -0
  155. data/spec/semipublic/associations/relationship_spec.rb +200 -0
  156. data/spec/semipublic/associations_spec.rb +177 -0
  157. data/spec/semipublic/collection_spec.rb +110 -0
  158. data/spec/semipublic/model_spec.rb +96 -0
  159. data/spec/semipublic/property/binary_spec.rb +13 -0
  160. data/spec/semipublic/property/boolean_spec.rb +47 -0
  161. data/spec/semipublic/property/class_spec.rb +33 -0
  162. data/spec/semipublic/property/date_spec.rb +43 -0
  163. data/spec/semipublic/property/date_time_spec.rb +46 -0
  164. data/spec/semipublic/property/decimal_spec.rb +83 -0
  165. data/spec/semipublic/property/discriminator_spec.rb +19 -0
  166. data/spec/semipublic/property/float_spec.rb +82 -0
  167. data/spec/semipublic/property/integer_spec.rb +82 -0
  168. data/spec/semipublic/property/lookup_spec.rb +29 -0
  169. data/spec/semipublic/property/serial_spec.rb +13 -0
  170. data/spec/semipublic/property/string_spec.rb +13 -0
  171. data/spec/semipublic/property/text_spec.rb +31 -0
  172. data/spec/semipublic/property/time_spec.rb +50 -0
  173. data/spec/semipublic/property_spec.rb +114 -0
  174. data/spec/semipublic/query/conditions/comparison_spec.rb +1501 -0
  175. data/spec/semipublic/query/conditions/operation_spec.rb +1294 -0
  176. data/spec/semipublic/query/path_spec.rb +471 -0
  177. data/spec/semipublic/query_spec.rb +3777 -0
  178. data/spec/semipublic/resource/state/clean_spec.rb +88 -0
  179. data/spec/semipublic/resource/state/deleted_spec.rb +78 -0
  180. data/spec/semipublic/resource/state/dirty_spec.rb +156 -0
  181. data/spec/semipublic/resource/state/immutable_spec.rb +105 -0
  182. data/spec/semipublic/resource/state/transient_spec.rb +162 -0
  183. data/spec/semipublic/resource/state_spec.rb +230 -0
  184. data/spec/semipublic/resource_spec.rb +23 -0
  185. data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
  186. data/spec/semipublic/shared/resource_shared_spec.rb +199 -0
  187. data/spec/semipublic/shared/resource_state_shared_spec.rb +79 -0
  188. data/spec/semipublic/shared/subject_shared_spec.rb +79 -0
  189. data/spec/spec.opts +5 -0
  190. data/spec/spec_helper.rb +37 -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 +28 -0
  248. data/spec/unit/hook_spec.rb +1235 -0
  249. data/spec/unit/lazy_array_spec.rb +1949 -0
  250. data/spec/unit/mash_spec.rb +312 -0
  251. data/spec/unit/module_spec.rb +71 -0
  252. data/spec/unit/object_spec.rb +38 -0
  253. data/spec/unit/try_dup_spec.rb +46 -0
  254. data/tasks/ci.rake +1 -0
  255. data/tasks/db.rake +11 -0
  256. data/tasks/spec.rake +38 -0
  257. data/tasks/yard.rake +9 -0
  258. data/tasks/yardstick.rake +19 -0
  259. metadata +491 -0
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+ require 'dm-core/support/subject_set'
3
+ require 'unit/data_mapper/subject_set/shared/named_spec'
4
+
5
+ describe 'DataMapper::SubjectSet#named?' do
6
+ before :all do
7
+
8
+ class ::Person
9
+ attr_reader :name
10
+ def initialize(name)
11
+ @name = name
12
+ end
13
+ end
14
+
15
+ end
16
+
17
+ subject { set.named?(name) }
18
+
19
+ let(:entry) { Person.new(name) }
20
+ let(:name ) { 'Alice' }
21
+
22
+ context 'when no entry with the given name is present' do
23
+ let(:set) { DataMapper::SubjectSet.new([]) }
24
+
25
+ it_should_behave_like 'DataMapper::SubjectSet#named? when no entry with the given name is present'
26
+ end
27
+
28
+ context 'when an entry with the given name is present' do
29
+ let(:set) { DataMapper::SubjectSet.new([ entry ]) }
30
+
31
+ it_should_behave_like 'DataMapper::SubjectSet#named? when an entry with the given name is present'
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ require 'unit/data_mapper/ordered_set/shared/append_spec'
2
+
3
+ shared_examples_for 'DataMapper::SubjectSet#<< when appending a not yet included entry' do
4
+ it_should_behave_like 'DataMapper::OrderedSet#<< when appending a not yet included entry'
5
+ end
6
+
7
+ shared_examples_for 'DataMapper::SubjectSet#<< when updating an entry with the same cache key and the new entry is already included' do
8
+ it_should_behave_like 'DataMapper::OrderedSet#<< when updating an already included entry'
9
+ end
10
+
11
+ shared_examples_for 'DataMapper::SubjectSet#<< when updating an entry with the same cache key and the new entry is not yet included' do
12
+ its(:entries) { should_not include(entry1) }
13
+ its(:entries) { should include(entry2) }
14
+
15
+ it 'should insert the new entry at the old position' do
16
+ subject.entries.index(entry2).should == @old_index
17
+ end
18
+ end
@@ -0,0 +1,9 @@
1
+ require 'unit/data_mapper/ordered_set/shared/clear_spec'
2
+
3
+ shared_examples_for 'DataMapper::SubjectSet#clear when no entries are present' do
4
+ it_should_behave_like 'DataMapper::OrderedSet#clear when no entries are present'
5
+ end
6
+
7
+ shared_examples_for 'DataMapper::SubjectSet#clear when entries are present' do
8
+ it_should_behave_like 'DataMapper::OrderedSet#clear when entries are present'
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'unit/data_mapper/ordered_set/shared/delete_spec'
2
+
3
+ shared_examples_for 'DataMapper::SubjectSet#delete when deleting an already included entry' do
4
+ it_should_behave_like 'DataMapper::OrderedSet#delete when deleting an already included entry'
5
+ end
6
+
7
+ shared_examples_for 'DataMapper::SubjectSet#delete when deleting a not yet included entry' do
8
+ it_should_behave_like 'DataMapper::OrderedSet#delete when deleting a not yet included entry'
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'unit/data_mapper/ordered_set/shared/each_spec'
2
+
3
+ shared_examples_for 'DataMapper::SubjectSet' do
4
+ it_should_behave_like 'DataMapper::OrderedSet'
5
+ end
6
+
7
+ shared_examples_for 'DataMapper::SubjectSet#each' do
8
+ it_should_behave_like 'DataMapper::OrderedSet#each'
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'unit/data_mapper/ordered_set/shared/empty_spec'
2
+
3
+ shared_examples_for 'DataMapper::SubjectSet#empty? with no entries in it' do
4
+ it_should_behave_like 'DataMapper::OrderedSet#empty? with no entries in it'
5
+ end
6
+
7
+ shared_examples_for 'DataMapper::SubjectSet#empty? with entries in it' do
8
+ it_should_behave_like 'DataMapper::OrderedSet#empty? with entries in it'
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'unit/data_mapper/ordered_set/shared/entries_spec'
2
+
3
+ shared_examples_for 'DataMapper::SubjectSet#entries with no entries' do
4
+ it_should_behave_like 'DataMapper::OrderedSet#entries with no entries'
5
+ end
6
+
7
+ shared_examples_for 'DataMapper::SubjectSet#entries with entries' do
8
+ it_should_behave_like 'DataMapper::OrderedSet#entries with entries'
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'DataMapper::SubjectSet#[] when the entry with the given name is not present' do
4
+ it { should be_nil }
5
+ end
6
+
7
+ shared_examples_for 'DataMapper::SubjectSet#[] when the entry with the given name is present' do
8
+ it { should == entry }
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'unit/data_mapper/ordered_set/shared/include_spec'
2
+
3
+ shared_examples_for 'DataMapper::SubjectSet#include? when the entry is present' do
4
+ it_should_behave_like 'DataMapper::OrderedSet#include? when the entry is present'
5
+ end
6
+
7
+ shared_examples_for 'DataMapper::SubjectSet#include? when the entry is not present' do
8
+ it_should_behave_like 'DataMapper::OrderedSet#include? when the entry is not present'
9
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'DataMapper::SubjectSet#named? when no entry with the given name is present' do
4
+ it { should be(false) }
5
+ end
6
+
7
+ shared_examples_for 'DataMapper::SubjectSet#named? when an entry with the given name is present' do
8
+ it { should be(true) }
9
+ end
@@ -0,0 +1,13 @@
1
+ require 'unit/data_mapper/ordered_set/shared/size_spec'
2
+
3
+ shared_examples_for 'DataMapper::SubjectSet#size when no entry is present' do
4
+ it_should_behave_like 'DataMapper::OrderedSet#size when no entry is present'
5
+ end
6
+
7
+ shared_examples_for 'DataMapper::SubjectSet#size when 1 entry is present' do
8
+ it_should_behave_like 'DataMapper::OrderedSet#size when 1 entry is present'
9
+ end
10
+
11
+ shared_examples_for 'DataMapper::SubjectSet#size when more than 1 entry is present' do
12
+ it_should_behave_like 'DataMapper::OrderedSet#size when more than 1 entry is present'
13
+ end
@@ -0,0 +1,9 @@
1
+ require 'unit/data_mapper/ordered_set/shared/to_ary_spec'
2
+
3
+ shared_examples_for 'DataMapper::SubjectSet#to_ary when no entries are present' do
4
+ it_should_behave_like 'DataMapper::OrderedSet#to_ary when no entries are present'
5
+ end
6
+
7
+ shared_examples_for 'DataMapper::SubjectSet#to_ary when entries are present' do
8
+ it_should_behave_like 'DataMapper::OrderedSet#to_ary when entries are present'
9
+ end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'DataMapper::SubjectSet#values_at when one name is given and no entry with the given name is present' do
4
+ its(:size) { should == given_names.size }
5
+
6
+ it 'should contain nil values for the names not found' do
7
+ subject.compact.should be_empty
8
+ end
9
+ end
10
+
11
+ shared_examples_for 'DataMapper::SubjectSet#values_at when one name is given and an entry with the given name is present' do
12
+ its(:size) { should == given_names.size }
13
+
14
+ it { should include(entry1) }
15
+ end
16
+
17
+ shared_examples_for 'DataMapper::SubjectSet#values_at when more than one name is given and no entry with any of the given names is present' do
18
+ its(:size) { should == given_names.size }
19
+
20
+ it 'should contain nil values for the names not found' do
21
+ subject.compact.should be_empty
22
+ end
23
+ end
24
+
25
+ shared_examples_for 'DataMapper::SubjectSet#values_at when more than one name is given and one entry with one of the given names is present' do
26
+ it { should include(entry1) }
27
+
28
+ its(:size) { should == given_names.size }
29
+
30
+ it 'should contain nil values for the names not found' do
31
+ subject.compact.size.should == 1
32
+ end
33
+ end
34
+
35
+ shared_examples_for 'DataMapper::SubjectSet#values_at when more than one name is given and an entry for every given name is present' do
36
+ it { should include(entry1) }
37
+ it { should include(entry2) }
38
+
39
+ its(:size) { should == given_names.size }
40
+
41
+ it 'should not contain any nil values' do
42
+ subject.compact.size.should == given_names.size
43
+ end
44
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ require 'dm-core/support/subject_set'
3
+ require 'unit/data_mapper/subject_set/shared/size_spec'
4
+
5
+ describe 'DataMapper::SubjectSet#size' do
6
+ before :all do
7
+
8
+ class ::Person
9
+ attr_reader :name
10
+ def initialize(name)
11
+ @name = name
12
+ end
13
+ end
14
+
15
+ end
16
+
17
+ subject { set.size }
18
+
19
+ let(:entry1) { Person.new('Alice') }
20
+ let(:entry2) { Person.new('Bob' ) }
21
+
22
+ context 'when no entry is present' do
23
+ let(:set) { DataMapper::SubjectSet.new }
24
+
25
+ it_should_behave_like 'DataMapper::SubjectSet#size when no entry is present'
26
+ end
27
+
28
+ context 'when 1 entry is present' do
29
+ let(:set) { DataMapper::SubjectSet.new(entries) }
30
+ let(:entries) { [ entry1 ] }
31
+
32
+ it_should_behave_like 'DataMapper::SubjectSet#size when 1 entry is present'
33
+ end
34
+
35
+ context 'when more than 1 entry is present' do
36
+ let(:set) { DataMapper::SubjectSet.new(entries) }
37
+ let(:entries) { [ entry1, entry2 ] }
38
+ let(:expected_size) { entries.size }
39
+
40
+ it_should_behave_like 'DataMapper::SubjectSet#size when more than 1 entry is present'
41
+ end
42
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require 'dm-core/support/subject_set'
3
+ require 'unit/data_mapper/subject_set/shared/to_ary_spec'
4
+
5
+ describe 'DataMapper::SubjectSet#to_ary' do
6
+ before :all do
7
+
8
+ class ::Person
9
+ attr_reader :name
10
+ def initialize(name)
11
+ @name = name
12
+ end
13
+ end
14
+
15
+ end
16
+
17
+ subject { set.to_ary }
18
+
19
+ let(:set) { DataMapper::SubjectSet.new(entries) }
20
+ let(:entry1) { Person.new('Alice') }
21
+ let(:entry2) { Person.new('Bob' ) }
22
+
23
+ context 'when no entries are present' do
24
+ let(:entries) { [] }
25
+
26
+ it_should_behave_like 'DataMapper::SubjectSet#to_ary when no entries are present'
27
+ end
28
+
29
+ context 'when entries are present' do
30
+ let(:entries) { [ entry1, entry2 ] }
31
+
32
+ it_should_behave_like 'DataMapper::SubjectSet#to_ary when entries are present'
33
+ end
34
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+ require 'dm-core/support/subject_set'
3
+ require 'unit/data_mapper/subject_set/shared/values_at_spec'
4
+
5
+ describe 'DataMapper::SubjectSet#values_at' do
6
+ before :all do
7
+
8
+ class ::Person
9
+ attr_reader :name
10
+ def initialize(name)
11
+ @name = name
12
+ end
13
+ end
14
+
15
+ end
16
+
17
+ subject { set.values_at(*given_names) }
18
+
19
+ let(:set) { DataMapper::SubjectSet.new(entries) }
20
+ let(:entry1) { Person.new('Alice') }
21
+ let(:entry2) { Person.new('Bob' ) }
22
+
23
+ context 'when one name is given and no entry with the given name is present' do
24
+ let(:given_names) { [ 'Alice' ] }
25
+ let(:entries) { [] }
26
+
27
+ it_should_behave_like 'DataMapper::SubjectSet#values_at when one name is given and no entry with the given name is present'
28
+ end
29
+
30
+ context 'when one name is given and an entry with the given name is present' do
31
+ let(:given_names) { [ 'Alice' ] }
32
+ let(:entries) { [ entry1 ] }
33
+
34
+ it_should_behave_like 'DataMapper::SubjectSet#values_at when one name is given and an entry with the given name is present'
35
+ end
36
+
37
+ context 'when more than one name is given and no entry with any of the given names is present' do
38
+ let(:given_names) { [ 'Alice', 'Bob' ] }
39
+ let(:entries) { [] }
40
+
41
+ it_should_behave_like 'DataMapper::SubjectSet#values_at when more than one name is given and no entry with any of the given names is present'
42
+ end
43
+
44
+ context 'when more than one name is given and one entry with one of the given names is present' do
45
+ let(:given_names) { [ 'Alice', 'Bob' ] }
46
+ let(:entries) { [ entry1 ] }
47
+
48
+ it_should_behave_like 'DataMapper::SubjectSet#values_at when more than one name is given and one entry with one of the given names is present'
49
+ end
50
+
51
+ context 'when more than one name is given and an entry for every given name is present' do
52
+ let(:given_names) { [ 'Alice', 'Bob' ] }
53
+ let(:entries) { [ entry1, entry2 ] }
54
+
55
+ it_should_behave_like 'DataMapper::SubjectSet#values_at when more than one name is given and an entry for every given name is present'
56
+ end
57
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+ require 'dm-core/support/ext/hash'
3
+ require 'dm-core/support/mash'
4
+
5
+ describe DataMapper::Ext::Hash, "only" do
6
+ before do
7
+ @hash = { :one => 'ONE', 'two' => 'TWO', 3 => 'THREE', 4 => nil }
8
+ end
9
+
10
+ it "should return a hash with only the given key(s)" do
11
+ DataMapper::Ext::Hash.only(@hash, :not_in_there).should == {}
12
+ DataMapper::Ext::Hash.only(@hash, 4).should == {4 => nil}
13
+ DataMapper::Ext::Hash.only(@hash, :one).should == { :one => 'ONE' }
14
+ DataMapper::Ext::Hash.only(@hash, :one, 3).should == { :one => 'ONE', 3 => 'THREE' }
15
+ end
16
+ end
17
+
18
+
19
+ describe Hash, 'to_mash' do
20
+ before do
21
+ @hash = Hash.new(10)
22
+ end
23
+
24
+ it "copies default Hash value to Mash" do
25
+ @mash = DataMapper::Ext::Hash.to_mash(@hash)
26
+ @mash[:merb].should == 10
27
+ end
28
+ end
@@ -0,0 +1,1235 @@
1
+ require 'spec_helper'
2
+ require 'dm-core/support/hook'
3
+
4
+ describe DataMapper::Hook do
5
+
6
+ before do
7
+ @module = Module.new do
8
+ def greet; greetings_from_module; end;
9
+ end
10
+
11
+ @class = Class.new do
12
+ include DataMapper::Hook
13
+
14
+ def hookable; end;
15
+ def self.clakable; end;
16
+ def ambiguous; hi_mom!; end;
17
+ def self.ambiguous; hi_dad!; end;
18
+ end
19
+
20
+ @another_class = Class.new do
21
+ include DataMapper::Hook
22
+ end
23
+
24
+ @other = Class.new do
25
+ include DataMapper::Hook
26
+
27
+ def hookable; end
28
+ def self.clakable; end;
29
+ end
30
+
31
+ @class.register_instance_hooks :hookable
32
+ @class.register_class_hooks :clakable
33
+ end
34
+
35
+ #
36
+ # Specs out how hookable methods are registered
37
+ #
38
+ describe "explicit hookable method registration" do
39
+
40
+ describe "for class methods" do
41
+
42
+ it "shouldn't confuse instance method hooks and class method hooks" do
43
+ @class.register_instance_hooks :ambiguous
44
+ @class.register_class_hooks :ambiguous
45
+
46
+ @class.should_receive(:hi_dad!)
47
+ @class.ambiguous
48
+ end
49
+
50
+ it "should be able to register multiple hookable methods at once" do
51
+ %w(method_one method_two method_three).each do |method|
52
+ @another_class.class_eval %(def self.#{method}; end;)
53
+ end
54
+
55
+ @another_class.register_class_hooks :method_one, :method_two, :method_three
56
+ @another_class.class_hooks.should have_key(:method_one)
57
+ @another_class.class_hooks.should have_key(:method_two)
58
+ @another_class.class_hooks.should have_key(:method_three)
59
+ end
60
+
61
+ it "should not allow a method that does not exist to be registered as hookable" do
62
+ lambda { @another_class.register_class_hooks :method_one }.should raise_error(ArgumentError)
63
+ end
64
+
65
+ it "should allow hooks to be registered on methods from module extensions" do
66
+ @class.extend(@module)
67
+ @class.register_class_hooks :greet
68
+ @class.class_hooks[:greet].should_not be_nil
69
+ end
70
+
71
+ it "should allow modules to register hooks in the self.extended method" do
72
+ @module.class_eval do
73
+ def self.extended(base)
74
+ base.register_class_hooks :greet
75
+ end
76
+ end
77
+ @class.extend(@module)
78
+ @class.class_hooks[:greet].should_not be_nil
79
+ end
80
+
81
+ it "should be able to register protected methods as hooks" do
82
+ @class.class_eval %{protected; def self.protected_hookable; end;}
83
+ lambda { @class.register_class_hooks(:protected_hookable) }.should_not raise_error(ArgumentError)
84
+ end
85
+
86
+ it "should not be able to register private methods as hooks" do
87
+ @class.class_eval %{class << self; private; def private_hookable; end; end;}
88
+ lambda { @class.register_class_hooks(:private_hookable) }.should raise_error(ArgumentError)
89
+ end
90
+
91
+ it "should allow advising methods ending in ? or !" do
92
+ @class.class_eval do
93
+ def self.hookable!; two!; end;
94
+ def self.hookable?; three!; end;
95
+ register_class_hooks :hookable!, :hookable?
96
+ end
97
+ @class.before_class_method(:hookable!) { one! }
98
+ @class.after_class_method(:hookable?) { four! }
99
+
100
+ @class.should_receive(:one!).once.ordered
101
+ @class.should_receive(:two!).once.ordered
102
+ @class.should_receive(:three!).once.ordered
103
+ @class.should_receive(:four!).once.ordered
104
+
105
+ @class.hookable!
106
+ @class.hookable?
107
+ end
108
+
109
+ it "should allow hooking methods ending in ?, ! or = with method hooks" do
110
+ @class.class_eval do
111
+ def self.before_hookable!; one!; end;
112
+ def self.hookable!; two!; end;
113
+ def self.hookable?; three!; end;
114
+ def self.after_hookable?; four!; end;
115
+ register_class_hooks :hookable!, :hookable?
116
+ end
117
+ @class.before_class_method(:hookable!, :before_hookable!)
118
+ @class.after_class_method(:hookable?, :after_hookable?)
119
+
120
+ @class.should_receive(:one!).once.ordered
121
+ @class.should_receive(:two!).once.ordered
122
+ @class.should_receive(:three!).once.ordered
123
+ @class.should_receive(:four!).once.ordered
124
+
125
+ @class.hookable!
126
+ @class.hookable?
127
+ end
128
+
129
+ it "should allow hooking methods that have single character names" do
130
+ @class.class_eval do
131
+ def self.a; end;
132
+ def self.b; end;
133
+ end
134
+
135
+ @class.before_class_method(:a) { omg! }
136
+ @class.before_class_method(:b) { hi2u! }
137
+
138
+ @class.should_receive(:omg!).once.ordered
139
+ @class.should_receive(:hi2u!).once.ordered
140
+ @class.a
141
+ @class.b
142
+ end
143
+ end
144
+
145
+ describe "for instance methods" do
146
+
147
+ it "shouldn't confuse instance method hooks and class method hooks" do
148
+ @class.register_instance_hooks :ambiguous
149
+ @class.register_class_hooks :ambiguous
150
+
151
+ inst = @class.new
152
+ inst.should_receive(:hi_mom!)
153
+ inst.ambiguous
154
+ end
155
+
156
+ it "should be able to register multiple hookable methods at once" do
157
+ %w(method_one method_two method_three).each do |method|
158
+ @another_class.send(:define_method, method) {}
159
+ end
160
+
161
+ @another_class.register_instance_hooks :method_one, :method_two, :method_three
162
+ @another_class.instance_hooks.should have_key(:method_one)
163
+ @another_class.instance_hooks.should have_key(:method_two)
164
+ @another_class.instance_hooks.should have_key(:method_three)
165
+ end
166
+
167
+ it "should not allow a method that does not exist to be registered as hookable" do
168
+ lambda { @another_class.register_instance_hooks :method_one }.should raise_error(ArgumentError)
169
+ end
170
+
171
+ it "should allow hooks to be registered on included module methods" do
172
+ @class.send(:include, @module)
173
+ @class.register_instance_hooks :greet
174
+ @class.instance_hooks[:greet].should_not be_nil
175
+ end
176
+
177
+ it "should allow modules to register hooks in the self.included method" do
178
+ @module.class_eval do
179
+ def self.included(base)
180
+ base.register_instance_hooks :greet
181
+ end
182
+ end
183
+ @class.send(:include, @module)
184
+ @class.instance_hooks[:greet].should_not be_nil
185
+ end
186
+
187
+ it "should be able to register protected methods as hooks" do
188
+ @class.class_eval %{protected; def protected_hookable; end;}
189
+ lambda { @class.register_instance_hooks(:protected_hookable) }.should_not raise_error(ArgumentError)
190
+ end
191
+
192
+ it "should not be able to register private methods as hooks" do
193
+ @class.class_eval %{private; def private_hookable; end;}
194
+ lambda { @class.register_instance_hooks(:private_hookable) }.should raise_error(ArgumentError)
195
+ end
196
+
197
+ it "should allow hooking methods ending in ? or ! with block hooks" do
198
+ @class.class_eval do
199
+ def hookable!; two!; end;
200
+ def hookable?; three!; end;
201
+ register_instance_hooks :hookable!, :hookable?
202
+ end
203
+ @class.before(:hookable!) { one! }
204
+ @class.after(:hookable?) { four! }
205
+
206
+ inst = @class.new
207
+ inst.should_receive(:one!).once.ordered
208
+ inst.should_receive(:two!).once.ordered
209
+ inst.should_receive(:three!).once.ordered
210
+ inst.should_receive(:four!).once.ordered
211
+
212
+ inst.hookable!
213
+ inst.hookable?
214
+ end
215
+
216
+ it "should allow hooking methods ending in ?, ! or = with method hooks" do
217
+ @class.class_eval do
218
+ def before_hookable(val); one!; end;
219
+ def hookable=(val); two!; end;
220
+ def hookable?; three!; end;
221
+ def after_hookable?; four!; end;
222
+ register_instance_hooks :hookable=, :hookable?
223
+ end
224
+ @class.before(:hookable=, :before_hookable)
225
+ @class.after(:hookable?, :after_hookable?)
226
+
227
+ inst = @class.new
228
+ inst.should_receive(:one!).once.ordered
229
+ inst.should_receive(:two!).once.ordered
230
+ inst.should_receive(:three!).once.ordered
231
+ inst.should_receive(:four!).once.ordered
232
+
233
+ inst.hookable = 'hello'
234
+ inst.hookable?
235
+ end
236
+
237
+ it "should allow hooking methods that have single character names" do
238
+ @class.class_eval do
239
+ def a; end;
240
+ def b; end;
241
+ end
242
+
243
+ @class.before(:a) { omg! }
244
+ @class.before(:b) { hi2u! }
245
+
246
+ inst = @class.new
247
+ inst.should_receive(:omg!).once.ordered
248
+ inst.should_receive(:hi2u!).once.ordered
249
+ inst.a
250
+ inst.b
251
+ end
252
+ end
253
+
254
+ end
255
+
256
+ describe "implicit hookable method registration" do
257
+
258
+ describe "for class methods" do
259
+ it "should implicitly register the method as hookable" do
260
+ @class.class_eval %{def self.implicit_hook; end;}
261
+ @class.before_class_method(:implicit_hook) { hello }
262
+
263
+ @class.should_receive(:hello)
264
+ @class.implicit_hook
265
+ end
266
+ end
267
+
268
+ describe "for instance methods" do
269
+ it "should implicitly register the method as hookable" do
270
+ @class.class_eval %{def implicit_hook; end;}
271
+ @class.before(:implicit_hook) { hello }
272
+
273
+ inst = @class.new
274
+ inst.should_receive(:hello)
275
+ inst.implicit_hook
276
+ end
277
+
278
+ it 'should not overwrite methods included by modules after the hook is declared' do
279
+ my_module = Module.new do
280
+ # Just another module
281
+ @another_module = Module.new do
282
+ def some_method; "Hello " + super; end;
283
+ end
284
+
285
+ def some_method; "world"; end;
286
+
287
+ def self.included(base)
288
+ base.before(:some_method, :a_method)
289
+ base.send(:include, @another_module)
290
+ end
291
+ end
292
+
293
+ @class.class_eval { include my_module }
294
+
295
+ inst = @class.new
296
+ inst.should_receive(:a_method)
297
+ inst.some_method.should == "Hello world"
298
+ end
299
+ end
300
+
301
+ end
302
+
303
+ describe "hook method registration" do
304
+
305
+ describe "for class methods" do
306
+ it "should complain when only one argument is passed" do
307
+ lambda { @class.before_class_method(:clakable) }.should raise_error(ArgumentError)
308
+ lambda { @class.after_class_method(:clakable) }.should raise_error(ArgumentError)
309
+ end
310
+
311
+ it "should complain when target_method is not a symbol" do
312
+ lambda { @class.before_class_method("clakable", :ambiguous) }.should raise_error(ArgumentError)
313
+ lambda { @class.after_class_method("clakable", :ambiguous) }.should raise_error(ArgumentError)
314
+ end
315
+
316
+ it "should complain when method_sym is not a symbol" do
317
+ lambda { @class.before_class_method(:clakable, "ambiguous") }.should raise_error(ArgumentError)
318
+ lambda { @class.after_class_method(:clakable, "ambiguous") }.should raise_error(ArgumentError)
319
+ end
320
+
321
+ it "should not allow methods ending in = to be hooks" do
322
+ lambda { @class.before_class_method(:clakable, :annoying=) }.should raise_error(ArgumentError)
323
+ lambda { @class.after_class_method(:clakable, :annoying=) }.should raise_error(ArgumentError)
324
+ end
325
+ end
326
+
327
+ describe "for instance methods" do
328
+ it "should complain when only one argument is passed" do
329
+ lambda { @class.before(:hookable) }.should raise_error(ArgumentError)
330
+ lambda { @class.after(:hookable) }.should raise_error(ArgumentError)
331
+ end
332
+
333
+ it "should complain when target_method is not a symbol" do
334
+ lambda { @class.before("hookable", :ambiguous) }.should raise_error(ArgumentError)
335
+ lambda { @class.after("hookable", :ambiguous) }.should raise_error(ArgumentError)
336
+ end
337
+
338
+ it "should complain when method_sym is not a symbol" do
339
+ lambda { @class.before(:hookable, "ambiguous") }.should raise_error(ArgumentError)
340
+ lambda { @class.after(:hookable, "ambiguous") }.should raise_error(ArgumentError)
341
+ end
342
+
343
+ it "should not allow methods ending in = to be hooks" do
344
+ lambda { @class.before(:hookable, :annoying=) }.should raise_error(ArgumentError)
345
+ lambda { @class.after(:hookable, :annoying=) }.should raise_error(ArgumentError)
346
+ end
347
+ end
348
+
349
+ end
350
+
351
+ #
352
+ # Specs out how hook methods / blocks are invoked when there is no inheritance
353
+ # involved
354
+ #
355
+ describe "hook invocation without inheritance" do
356
+
357
+ describe "for class methods" do
358
+ it 'should run an advice block' do
359
+ @class.before_class_method(:clakable) { hi_mom! }
360
+ @class.should_receive(:hi_mom!)
361
+ @class.clakable
362
+ end
363
+
364
+ it 'should run an advice method' do
365
+ @class.class_eval %{def self.before_method; hi_mom!; end;}
366
+ @class.before_class_method(:clakable, :before_method)
367
+
368
+ @class.should_receive(:hi_mom!)
369
+ @class.clakable
370
+ end
371
+
372
+ it "should not pass any of the hookable method's arguments if the hook block does not accept arguments" do
373
+ @class.class_eval do
374
+ def self.method_with_args(one, two, three); end;
375
+ before_class_method(:method_with_args) { hi_mom! }
376
+ end
377
+
378
+ @class.should_receive(:hi_mom!)
379
+ @class.method_with_args(1, 2, 3)
380
+ end
381
+
382
+ it "should not pass any of the hookable method's arguments if the hook method does not accept arguments" do
383
+ @class.class_eval do
384
+ def self.method_with_args(one, two, three); end;
385
+ def self.before_method_with_args; hi_mom!; end;
386
+ before_class_method(:method_with_args, :before_method_with_args)
387
+ end
388
+
389
+ @class.should_receive(:hi_mom!)
390
+ @class.method_with_args(1, 2, 3)
391
+ end
392
+
393
+ it "should not pass any of the hookable method's arguments if the hook is declared after it is registered and does not accept arguments" do
394
+ @class.class_eval do
395
+ def self.method_with_args(one, two, three); end;
396
+ before_class_method(:method_with_args, :before_method_with_args)
397
+ def self.before_method_with_args; hi_mom!; end;
398
+ end
399
+
400
+ @class.should_receive(:hi_mom!)
401
+ @class.method_with_args(1, 2, 3)
402
+ end
403
+
404
+ it "should not pass any of the hookable method's arguments if the hook has been re-defined not to accept arguments" do
405
+ @class.class_eval do
406
+ def self.method_with_args(one, two, three); end;
407
+ def self.before_method_with_args(one, two, three); hi_mom!; end;
408
+ before_class_method(:method_with_args, :before_method_with_args)
409
+ orig_verbose, $VERBOSE = $VERBOSE, false
410
+ def self.before_method_with_args; hi_dad!; end;
411
+ $VERBOSE = orig_verbose
412
+ end
413
+
414
+ @class.should_not_receive(:hi_mom1)
415
+ @class.should_receive(:hi_dad!)
416
+ @class.method_with_args(1, 2, 3)
417
+ end
418
+
419
+ it 'should pass the hookable method arguments to the hook method if the hook method takes arguments' do
420
+ @class.class_eval do
421
+ def self.hook_this(word, lol); end;
422
+ register_class_hooks(:hook_this)
423
+ def self.before_hook_this(word, lol); hi_mom!(word, lol); end;
424
+ before_class_method(:hook_this, :before_hook_this)
425
+ end
426
+
427
+ @class.should_receive(:hi_mom!).with("omg", "hi2u")
428
+ @class.hook_this("omg", "hi2u")
429
+ end
430
+
431
+ it "should pass the hookable method arguments to the hook block if the hook block takes arguments" do
432
+ @class.class_eval do
433
+ def self.method_with_args(word, lol); end;
434
+ before_class_method(:method_with_args) { |one, two| hi_mom!(one, two) }
435
+ end
436
+
437
+ @class.should_receive(:hi_mom!).with('omg', 'hi2u')
438
+ @class.method_with_args('omg', 'hi2u')
439
+ end
440
+
441
+ it 'should pass the hookable method arguments to the hook method if the hook method was re-defined to accept arguments' do
442
+ @class.class_eval do
443
+ def self.method_with_args(word, lol); end;
444
+ def self.before_method_with_args; hi_mom!; end;
445
+ before_class_method(:method_with_args, :before_method_with_args)
446
+ orig_verbose, $VERBOSE = $VERBOSE, false
447
+ def self.before_method_with_args(word, lol); hi_dad!(word, lol); end;
448
+ $VERBOSE = orig_verbose
449
+ end
450
+
451
+ @class.should_not_receive(:hi_mom!)
452
+ @class.should_receive(:hi_dad!).with("omg", "hi2u")
453
+ @class.method_with_args("omg", "hi2u")
454
+ end
455
+
456
+ it 'should work with glob arguments (or whatever you call em)' do
457
+ @class.class_eval do
458
+ def self.hook_this(*args); end;
459
+ def self.before_hook_this(*args); hi_mom!(*args); end;
460
+ before_class_method(:hook_this, :before_hook_this)
461
+ end
462
+
463
+ @class.should_receive(:hi_mom!).with("omg", "hi2u", "lolercoaster")
464
+ @class.hook_this("omg", "hi2u", "lolercoaster")
465
+ end
466
+
467
+ it 'should allow the use of before and after together' do
468
+ @class.class_eval %{def self.before_hook; first!; end;}
469
+ @class.before_class_method(:clakable, :before_hook)
470
+ @class.after_class_method(:clakable) { last! }
471
+
472
+ @class.should_receive(:first!).once.ordered
473
+ @class.should_receive(:last!).once.ordered
474
+ @class.clakable
475
+ end
476
+
477
+ it 'should be able to use private methods as hooks' do
478
+ @class.class_eval do
479
+ class << self
480
+ private
481
+ def nike; doit!; end;
482
+ end
483
+ before_class_method(:clakable, :nike)
484
+ end
485
+
486
+ @class.should_receive(:doit!)
487
+ @class.clakable
488
+ end
489
+ end
490
+
491
+ describe "for instance methods" do
492
+ it 'should run an advice block' do
493
+ @class.before(:hookable) { hi_mom! }
494
+
495
+ inst = @class.new
496
+ inst.should_receive(:hi_mom!)
497
+ inst.hookable
498
+ end
499
+
500
+ it 'should run an advice method' do
501
+ @class.send(:define_method, :before_method) { hi_mom! }
502
+ @class.before(:hookable, :before_method)
503
+
504
+ inst = @class.new
505
+ inst.should_receive(:hi_mom!)
506
+ inst.hookable
507
+ end
508
+
509
+ it "should not pass any of the hookable method's arguments if the hook block does not accept arguments" do
510
+ @class.class_eval do
511
+ def method_with_args(one, two, three); end;
512
+ before(:method_with_args) { hi_mom! }
513
+ end
514
+
515
+ inst = @class.new
516
+ inst.should_receive(:hi_mom!)
517
+ inst.method_with_args(1, 2, 3)
518
+ end
519
+
520
+ it "should not pass any of the hookable method's arguments if the hook method does not accept arguments" do
521
+ @class.class_eval do
522
+ def method_with_args(one, two, three); end;
523
+ def before_method_with_args; hi_mom!; end;
524
+ before(:method_with_args, :before_method_with_args)
525
+ end
526
+
527
+ inst = @class.new
528
+ inst.should_receive(:hi_mom!)
529
+ inst.method_with_args(1, 2, 3)
530
+ end
531
+
532
+ it "should not pass any of the hookable method's arguments if the hook is declared after it is registered and does not accept arguments" do
533
+ @class.class_eval do
534
+ def method_with_args(one, two, three); end;
535
+ before(:method_with_args, :before_method_with_args)
536
+ def before_method_with_args; hi_mom!; end;
537
+ end
538
+
539
+ inst = @class.new
540
+ inst.should_receive(:hi_mom!)
541
+ inst.method_with_args(1, 2, 3)
542
+ end
543
+
544
+ it "should not pass any of the hookable method's arguments if the hook has been re-defined not to accept arguments" do
545
+ @class.class_eval do
546
+ def method_with_args(one, two, three); end;
547
+ def before_method_with_args(one, two, three); hi_mom!; end;
548
+ before(:method_with_args, :before_method_with_args)
549
+ orig_verbose, $VERBOSE = $VERBOSE, false
550
+ def before_method_with_args; hi_dad!; end;
551
+ $VERBOSE = orig_verbose
552
+ end
553
+
554
+ inst = @class.new
555
+ inst.should_not_receive(:hi_mom1)
556
+ inst.should_receive(:hi_dad!)
557
+ inst.method_with_args(1, 2, 3)
558
+ end
559
+
560
+ it 'should pass the hookable method arguments to the hook method if the hook method takes arguments' do
561
+ @class.class_eval do
562
+ def method_with_args(word, lol); end;
563
+ def before_method_with_args(one, two); hi_mom!(one, two); end;
564
+ before(:method_with_args, :before_method_with_args)
565
+ end
566
+
567
+ inst = @class.new
568
+ inst.should_receive(:hi_mom!).with("omg", "hi2u")
569
+ inst.method_with_args("omg", "hi2u")
570
+ end
571
+
572
+ it "should pass the hookable method arguments to the hook block if the hook block takes arguments" do
573
+ @class.class_eval do
574
+ def method_with_args(word, lol); end;
575
+ before(:method_with_args) { |one, two| hi_mom!(one, two) }
576
+ end
577
+
578
+ inst = @class.new
579
+ inst.should_receive(:hi_mom!).with('omg', 'hi2u')
580
+ inst.method_with_args('omg', 'hi2u')
581
+ end
582
+
583
+ it 'should pass the hookable method arguments to the hook method if the hook method was re-defined to accept arguments' do
584
+ @class.class_eval do
585
+ def method_with_args(word, lol); end;
586
+ def before_method_with_args; hi_mom!; end;
587
+ before(:method_with_args, :before_method_with_args)
588
+ orig_verbose, $VERBOSE = $VERBOSE, false
589
+ def before_method_with_args(word, lol); hi_dad!(word, lol); end;
590
+ $VERBOSE = orig_verbose
591
+ end
592
+
593
+ inst = @class.new
594
+ inst.should_not_receive(:hi_mom!)
595
+ inst.should_receive(:hi_dad!).with("omg", "hi2u")
596
+ inst.method_with_args("omg", "hi2u")
597
+ end
598
+
599
+ it "should not pass the method return value to the after hook if the method does not take arguments" do
600
+ @class.class_eval do
601
+ def method_with_ret_val; 'hello'; end;
602
+ def after_method_with_ret_val; hi_mom!; end;
603
+ after(:method_with_ret_val, :after_method_with_ret_val)
604
+ end
605
+
606
+ inst = @class.new
607
+ inst.should_receive(:hi_mom!)
608
+ inst.method_with_ret_val
609
+ end
610
+
611
+ it 'should work with glob arguments (or whatever you call em)' do
612
+ @class.class_eval do
613
+ def hook_this(*args); end;
614
+ def before_hook_this(*args); hi_mom!(*args) end;
615
+ before(:hook_this, :before_hook_this)
616
+ end
617
+
618
+ inst = @class.new
619
+ inst.should_receive(:hi_mom!).with("omg", "hi2u", "lolercoaster")
620
+ inst.hook_this("omg", "hi2u", "lolercoaster")
621
+ end
622
+
623
+ it 'should allow the use of before and after together' do
624
+ @class.class_eval %{def after_hook; last!; end;}
625
+ @class.before(:hookable) { first! }
626
+ @class.after(:hookable, :after_hook)
627
+
628
+ inst = @class.new
629
+ inst.should_receive(:first!).once.ordered
630
+ inst.should_receive(:last!).once.ordered
631
+ inst.hookable
632
+ end
633
+
634
+ it 'should be able to use private methods as hooks' do
635
+ @class.class_eval %{private; def nike; doit!; end;}
636
+ @class.before(:hookable, :nike)
637
+
638
+ inst = @class.new
639
+ inst.should_receive(:doit!)
640
+ inst.hookable
641
+ end
642
+ end
643
+
644
+ end
645
+
646
+ describe "hook invocation with class inheritance" do
647
+
648
+ describe "for class methods" do
649
+ it 'should run an advice block when the class is inherited' do
650
+ @class.before_class_method(:clakable) { hi_mom! }
651
+ @child = Class.new(@class)
652
+ @child.should_receive(:hi_mom!)
653
+ @child.clakable
654
+ end
655
+
656
+ it 'should run an advice block on child class when hook is registered in parent after inheritance' do
657
+ @child = Class.new(@class)
658
+ @class.before_class_method(:clakable) { hi_mom! }
659
+ @child.should_receive(:hi_mom!)
660
+ @child.clakable
661
+ end
662
+
663
+ it 'should be able to declare advice methods in child classes' do
664
+ @class.class_eval %{def self.before_method; hi_dad!; end;}
665
+ @class.before_class_method(:clakable, :before_method)
666
+
667
+ @child = Class.new(@class) do
668
+ def self.child; hi_mom!; end;
669
+ before_class_method(:clakable, :child)
670
+ end
671
+
672
+ @child.should_receive(:hi_dad!).once.ordered
673
+ @child.should_receive(:hi_mom!).once.ordered
674
+ @child.clakable
675
+ end
676
+
677
+ it "should not execute hooks added in the child classes when in the parent class" do
678
+ @child = Class.new(@class) { def self.child; hi_mom!; end; }
679
+ @child.before_class_method(:clakable, :child)
680
+ @class.should_not_receive(:hi_mom!)
681
+ @class.clakable
682
+ end
683
+
684
+ it 'should not call the hook stack if the hookable method is overwritten and does not call super' do
685
+ @class.before_class_method(:clakable) { hi_mom! }
686
+ @child = Class.new(@class) do
687
+ def self.clakable; end;
688
+ end
689
+
690
+ @child.should_not_receive(:hi_mom!)
691
+ @child.clakable
692
+ end
693
+
694
+ it 'should not call hooks defined in the child class for a hookable method in a parent if the child overwrites the hookable method without calling super' do
695
+ @child = Class.new(@class) do
696
+ before_class_method(:clakable) { hi_mom! }
697
+ def self.clakable; end;
698
+ end
699
+
700
+ @child.should_not_receive(:hi_mom!)
701
+ @child.clakable
702
+ end
703
+
704
+ it 'should not call hooks defined in child class even if hook method exists in parent' do
705
+ @class.class_eval %{def self.hello_world; hello_world!; end;}
706
+ @child = Class.new(@class) do
707
+ before_class_method(:clakable, :hello_world)
708
+ end
709
+
710
+ @class.should_not_receive(:hello_world!)
711
+ @class.clakable
712
+ end
713
+ end
714
+
715
+ describe "for instance methods" do
716
+ it 'should run an advice block when the class is inherited' do
717
+ @inherited_class = Class.new(@class)
718
+ @class.before(:hookable) { hi_dad! }
719
+
720
+ inst = @inherited_class.new
721
+ inst.should_receive(:hi_dad!)
722
+ inst.hookable
723
+ end
724
+
725
+ it 'should run an advice block on child class when hook is registered in parent after inheritance' do
726
+ @child = Class.new(@class)
727
+ @class.before(:hookable) { hi_mom! }
728
+
729
+ inst = @child.new
730
+ inst.should_receive(:hi_mom!)
731
+ inst.hookable
732
+ end
733
+
734
+ it 'should be able to declare advice methods in child classes' do
735
+ @class.send(:define_method, :before_method) { hi_dad! }
736
+ @class.before(:hookable, :before_method)
737
+
738
+ @child = Class.new(@class) do
739
+ def child; hi_mom!; end;
740
+ before :hookable, :child
741
+ end
742
+
743
+ inst = @child.new
744
+ inst.should_receive(:hi_dad!).once.ordered
745
+ inst.should_receive(:hi_mom!).once.ordered
746
+ inst.hookable
747
+ end
748
+
749
+ it "should not execute hooks added in the child classes when in parent class" do
750
+ @child = Class.new(@class)
751
+ @child.send(:define_method, :child) { hi_mom! }
752
+ @child.before(:hookable, :child)
753
+
754
+ inst = @class.new
755
+ inst.should_not_receive(:hi_mom!)
756
+ inst.hookable
757
+ end
758
+
759
+ it 'should not call the hook stack if the hookable method is overwritten and does not call super' do
760
+ @class.before(:hookable) { hi_mom! }
761
+ @child = Class.new(@class) do
762
+ def hookable; end;
763
+ end
764
+
765
+ inst = @child.new
766
+ inst.should_not_receive(:hi_mom!)
767
+ inst.hookable
768
+ end
769
+
770
+ it 'should not call hooks defined in the child class for a hookable method in a parent if the child overwrites the hookable method without calling super' do
771
+ @child = Class.new(@class) do
772
+ before(:hookable) { hi_mom! }
773
+ def hookable; end;
774
+ end
775
+
776
+ inst = @child.new
777
+ inst.should_not_receive(:hi_mom!)
778
+ inst.hookable
779
+ end
780
+
781
+ it 'should not call hooks defined in child class even if hook method exists in parent' do
782
+ @class.send(:define_method, :hello_world) { hello_world! }
783
+ @child = Class.new(@class) do
784
+ before(:hookable, :hello_world)
785
+ end
786
+
787
+ inst = @class.new
788
+ inst.should_not_receive(:hello_world!)
789
+ inst.hookable
790
+ end
791
+
792
+ it 'should call different hooks in different children when they are defined there' do
793
+ @class.send(:define_method, :hello_world) {}
794
+
795
+ @child1 = Class.new(@class) do
796
+ before(:hello_world){ hi_dad! }
797
+ end
798
+
799
+ @child2 = Class.new(@class) do
800
+ before(:hello_world){ hi_mom! }
801
+ end
802
+
803
+ @child3 = Class.new(@child1) do
804
+ before(:hello_world){ hi_grandma! }
805
+ end
806
+
807
+ inst1 = @child1.new
808
+ inst2 = @child2.new
809
+ inst3 = @child3.new
810
+ inst1.should_receive(:hi_dad!).once
811
+ inst2.should_receive(:hi_mom!).once
812
+ inst3.should_receive(:hi_dad!).once
813
+ inst3.should_receive(:hi_grandma!).once
814
+ inst1.hello_world
815
+ inst2.hello_world
816
+ inst3.hello_world
817
+ end
818
+
819
+ end
820
+
821
+ end
822
+
823
+ describe "hook invocation with module inclusions / extensions" do
824
+
825
+ describe "for class methods" do
826
+ it "should not overwrite methods included by extensions after the hook is declared" do
827
+ @module.class_eval do
828
+ @another_module = Module.new do
829
+ def greet; greetings_from_another_module; super; end;
830
+ end
831
+
832
+ def self.extended(base)
833
+ base.before_class_method(:clakable, :greet)
834
+ base.extend(@another_module)
835
+ end
836
+ end
837
+
838
+ @class.extend(@module)
839
+ @class.should_receive(:greetings_from_another_module).once.ordered
840
+ @class.should_receive(:greetings_from_module).once.ordered
841
+ @class.clakable
842
+ end
843
+ end
844
+
845
+ describe "for instance methods" do
846
+ it 'should not overwrite methods included by modules after the hook is declared' do
847
+ @module.class_eval do
848
+ @another_module = Module.new do
849
+ def greet; greetings_from_another_module; super; end;
850
+ end
851
+
852
+ def self.included(base)
853
+ base.before(:hookable, :greet)
854
+ base.send(:include, @another_module)
855
+ end
856
+ end
857
+
858
+ @class.send(:include, @module)
859
+
860
+ inst = @class.new
861
+ inst.should_receive(:greetings_from_another_module).once.ordered
862
+ inst.should_receive(:greetings_from_module).once.ordered
863
+ inst.hookable
864
+ end
865
+ end
866
+
867
+ end
868
+
869
+ describe "hook invocation with unrelated classes" do
870
+
871
+ describe "for class methods" do
872
+ it "should not execute hooks registered in an unrelated class" do
873
+ @class.before_class_method(:clakable) { hi_mom! }
874
+
875
+ @other.should_not_receive(:hi_mom!)
876
+ @other.clakable
877
+ end
878
+ end
879
+
880
+ describe "for instance methods" do
881
+ it "should not execute hooks registered in an unrelated class" do
882
+ @class.before(:hookable) { hi_mom! }
883
+
884
+ inst = @other.new
885
+ inst.should_not_receive(:hi_mom!)
886
+ inst.hookable
887
+ end
888
+ end
889
+
890
+ end
891
+
892
+ describe "using before hook" do
893
+
894
+ describe "for class methods" do
895
+
896
+ it 'should run the advice before the advised method' do
897
+ @class.class_eval %{def self.hook_me; second!; end;}
898
+ @class.register_class_hooks(:hook_me)
899
+ @class.before_class_method(:hook_me, :first!)
900
+
901
+ @class.should_receive(:first!).ordered
902
+ @class.should_receive(:second!).ordered
903
+ @class.hook_me
904
+ end
905
+
906
+ it 'should execute all advices once in order' do
907
+ @class.before_class_method(:clakable, :hook_1)
908
+ @class.before_class_method(:clakable, :hook_2)
909
+ @class.before_class_method(:clakable, :hook_3)
910
+
911
+ @class.should_receive(:hook_1).once.ordered
912
+ @class.should_receive(:hook_2).once.ordered
913
+ @class.should_receive(:hook_3).once.ordered
914
+ @class.clakable
915
+ end
916
+ end
917
+
918
+ describe "for instance methods" do
919
+
920
+ it 'should run the advice before the advised method' do
921
+ @class.class_eval %{
922
+ def hook_me; second!; end;
923
+ }
924
+ @class.register_instance_hooks(:hook_me)
925
+ @class.before(:hook_me, :first!)
926
+
927
+ inst = @class.new
928
+ inst.should_receive(:first!).ordered
929
+ inst.should_receive(:second!).ordered
930
+ inst.hook_me
931
+ end
932
+
933
+ it 'should execute all advices once in order' do
934
+ @class.before(:hookable, :hook_1)
935
+ @class.before(:hookable, :hook_2)
936
+ @class.before(:hookable, :hook_3)
937
+
938
+ inst = @class.new
939
+ inst.should_receive(:hook_1).once.ordered
940
+ inst.should_receive(:hook_2).once.ordered
941
+ inst.should_receive(:hook_3).once.ordered
942
+ inst.hookable
943
+ end
944
+ end
945
+
946
+ end
947
+
948
+ describe 'using after hook' do
949
+
950
+ describe "for class methods" do
951
+
952
+ it 'should run the advice after the advised method' do
953
+ @class.class_eval %{def self.hook_me; first!; end;}
954
+ @class.register_class_hooks(:hook_me)
955
+ @class.after_class_method(:hook_me, :second!)
956
+
957
+ @class.should_receive(:first!).ordered
958
+ @class.should_receive(:second!).ordered
959
+ @class.hook_me
960
+ end
961
+
962
+ it 'should execute all advices once in order' do
963
+ @class.after_class_method(:clakable, :hook_1)
964
+ @class.after_class_method(:clakable, :hook_2)
965
+ @class.after_class_method(:clakable, :hook_3)
966
+
967
+ @class.should_receive(:hook_1).once.ordered
968
+ @class.should_receive(:hook_2).once.ordered
969
+ @class.should_receive(:hook_3).once.ordered
970
+ @class.clakable
971
+ end
972
+
973
+ it "the advised method should still return its normal value" do
974
+ @class.class_eval %{def self.hello; "hello world"; end;}
975
+ @class.register_class_hooks(:hello)
976
+ @class.after_class_method(:hello) { "BAM" }
977
+
978
+ @class.hello.should == "hello world"
979
+ end
980
+
981
+ it "should pass the return value to a hook method" do
982
+ @class.class_eval do
983
+ def self.with_return_val; 'hello'; end;
984
+ def self.after_with_return_val(retval); retval.should == 'hello'; end;
985
+ after_class_method(:with_return_val, :after_with_return_val)
986
+ end
987
+
988
+ @class.with_return_val
989
+ end
990
+
991
+ it "should pass the return value to a hook block" do
992
+ @class.class_eval do
993
+ def self.with_return_val; 'hello'; end;
994
+ after_class_method(:with_return_val) { |ret| ret.should == 'hello' }
995
+ end
996
+
997
+ @class.with_return_val
998
+ end
999
+
1000
+ it "should pass the return value and method arguments to a hook block" do
1001
+ @class.class_eval do
1002
+ def self.with_args_and_return_val(world); 'hello'; end;
1003
+ after_class_method(:with_args_and_return_val) do |hello, world|
1004
+ hello.should == "hello"
1005
+ world.should == "world"
1006
+ end
1007
+ end
1008
+
1009
+ @class.with_args_and_return_val('world')
1010
+ end
1011
+ end
1012
+
1013
+ describe "for instance methods" do
1014
+
1015
+ it 'should run the advice after the advised method' do
1016
+ @class.class_eval %{def hook_me; first!; end;}
1017
+ @class.register_instance_hooks(:hook_me)
1018
+ @class.after(:hook_me, :second!)
1019
+
1020
+ inst = @class.new
1021
+ inst.should_receive(:first!).ordered
1022
+ inst.should_receive(:second!).ordered
1023
+ inst.hook_me
1024
+ end
1025
+
1026
+ it 'should execute all advices once in order' do
1027
+ @class.after(:hookable, :hook_1)
1028
+ @class.after(:hookable, :hook_2)
1029
+ @class.after(:hookable, :hook_3)
1030
+
1031
+ inst = @class.new
1032
+ inst.should_receive(:hook_1).once.ordered
1033
+ inst.should_receive(:hook_2).once.ordered
1034
+ inst.should_receive(:hook_3).once.ordered
1035
+ inst.hookable
1036
+ end
1037
+
1038
+ it "the advised method should still return its normal value" do
1039
+ @class.class_eval %{def hello; "hello world"; end;}
1040
+ @class.register_instance_hooks(:hello)
1041
+ @class.after(:hello) { "BAM" }
1042
+
1043
+ @class.new.hello.should == "hello world"
1044
+ end
1045
+
1046
+ it "should return nil if an after hook throws :halt without a return value" do
1047
+ @class.class_eval %{def with_value; "hello"; end;}
1048
+ @class.register_instance_hooks(:with_value)
1049
+ @class.after(:with_value) { throw :halt }
1050
+
1051
+ @class.new.with_value.should be_nil
1052
+ end
1053
+
1054
+ it "should pass the return value to a hook method" do
1055
+ @class.class_eval do
1056
+ def with_return_val; 'hello'; end;
1057
+ def after_with_return_val(retval); retval.should == 'hello'; end;
1058
+ after(:with_return_val, :after_with_return_val)
1059
+ end
1060
+
1061
+ @class.new.with_return_val
1062
+ end
1063
+
1064
+ it "should pass the return value to a hook block" do
1065
+ @class.class_eval do
1066
+ def with_return_val; 'hello'; end;
1067
+ after(:with_return_val) { |ret| ret.should == 'hello' }
1068
+ end
1069
+
1070
+ @class.new.with_return_val
1071
+ end
1072
+
1073
+ it "should pass the return value and method arguments to a hook block" do
1074
+ @class.class_eval do
1075
+ def with_args_and_return_val(world); 'hello'; end;
1076
+ after(:with_args_and_return_val) do |hello, world|
1077
+ hello.should == "hello"
1078
+ world.should == "world"
1079
+ end
1080
+ end
1081
+
1082
+ @class.new.with_args_and_return_val('world')
1083
+ end
1084
+ end
1085
+
1086
+ end
1087
+
1088
+ describe 'aborting' do
1089
+
1090
+ describe "for class methods" do
1091
+ it "should catch :halt from a before hook and abort the advised method" do
1092
+ @class.class_eval %{def self.no_love; love_me!; end;}
1093
+ @class.register_class_hooks :no_love
1094
+ @class.before_class_method(:no_love) { maybe! }
1095
+ @class.before_class_method(:no_love) { throw :halt }
1096
+ @class.before_class_method(:no_love) { what_about_me? }
1097
+
1098
+ @class.should_receive(:maybe!)
1099
+ @class.should_not_receive(:what_about_me?)
1100
+ @class.should_not_receive(:love_me!)
1101
+ lambda { @class.no_love }.should_not throw_symbol(:halt)
1102
+ end
1103
+
1104
+ it "should not run after hooks if a before hook throws :halt" do
1105
+ @class.before_class_method(:clakable) { throw :halt }
1106
+ @class.after_class_method(:clakable) { bam! }
1107
+
1108
+ @class.should_not_receive(:bam!)
1109
+ lambda { @class.clakable }.should_not throw_symbol(:halt)
1110
+ end
1111
+
1112
+ it "should return nil from the hookable method if a before hook throws :halt" do
1113
+ @class.class_eval %{def self.with_value; "hello"; end;}
1114
+ @class.register_class_hooks(:with_value)
1115
+ @class.before_class_method(:with_value) { throw :halt }
1116
+
1117
+ @class.with_value.should be_nil
1118
+ end
1119
+
1120
+ it "should catch :halt from an after hook and cease the advice" do
1121
+ @class.after_class_method(:clakable) { throw :halt }
1122
+ @class.after_class_method(:clakable) { never_see_me! }
1123
+
1124
+ @class.should_not_receive(:never_see_me!)
1125
+ lambda { @class.clakable }.should_not throw_symbol(:halt)
1126
+ end
1127
+
1128
+ it "should return nil if an after hook throws :halt without a return value" do
1129
+ @class.class_eval %{def self.with_value; "hello"; end;}
1130
+ @class.register_class_hooks(:with_value)
1131
+ @class.after_class_method(:with_value) { throw :halt }
1132
+
1133
+ @class.with_value.should be_nil
1134
+ end
1135
+ end
1136
+
1137
+ describe "for instance methods" do
1138
+ it "should catch :halt from a before hook and abort the advised method" do
1139
+ @class.class_eval %{def no_love; love_me!; end;}
1140
+ @class.register_instance_hooks :no_love
1141
+ @class.before(:no_love) { maybe! }
1142
+ @class.before(:no_love) { throw :halt }
1143
+ @class.before(:no_love) { what_about_me? }
1144
+
1145
+ inst = @class.new
1146
+ inst.should_receive(:maybe!)
1147
+ inst.should_not_receive(:what_about_me?)
1148
+ inst.should_not_receive(:love_me!)
1149
+ lambda { inst.no_love }.should_not throw_symbol(:halt)
1150
+ end
1151
+
1152
+ it "should not run after hooks if a before hook throws :halt" do
1153
+ @class.before(:hookable) { throw :halt }
1154
+ @class.after(:hookable) { bam! }
1155
+
1156
+ inst = @class.new
1157
+ inst.should_not_receive(:bam!)
1158
+ lambda { inst.hookable }.should_not throw_symbol(:halt)
1159
+ end
1160
+
1161
+ it "should return nil from the hookable method if a before hook throws :halt" do
1162
+ @class.class_eval %{def with_value; "hello"; end;}
1163
+ @class.register_instance_hooks(:with_value)
1164
+ @class.before(:with_value) { throw :halt }
1165
+
1166
+ @class.new.with_value.should be_nil
1167
+ end
1168
+
1169
+ it "should catch :halt from an after hook and cease the advice" do
1170
+ @class.after(:hookable) { throw :halt }
1171
+ @class.after(:hookable) { never_see_me! }
1172
+
1173
+ inst = @class.new
1174
+ inst.should_not_receive(:never_see_me!)
1175
+ lambda { inst.hookable }.should_not throw_symbol(:halt)
1176
+ end
1177
+ end
1178
+
1179
+ end
1180
+
1181
+ describe 'aborting with return values' do
1182
+
1183
+ describe "for class methods" do
1184
+
1185
+ it "should be able to abort from a before hook with a return value" do
1186
+ @class.before_class_method(:clakable) { throw :halt, 'omg' }
1187
+ @class.clakable.should == 'omg'
1188
+ end
1189
+
1190
+ it "should be able to abort from an after hook with a return value" do
1191
+ @class.after_class_method(:clakable) { throw :halt, 'omg' }
1192
+ @class.clakable.should == 'omg'
1193
+ end
1194
+
1195
+ end
1196
+
1197
+ describe "for instance methods" do
1198
+
1199
+ it "should be able to abort from a before hook with a return value" do
1200
+ @class.before(:hookable) { throw :halt, 'omg' }
1201
+
1202
+ inst = @class.new
1203
+ inst.hookable.should == 'omg'
1204
+ end
1205
+
1206
+ it "should be able to abort from an after hook with a return value" do
1207
+ @class.after(:hookable) { throw :halt, 'omg' }
1208
+
1209
+ inst = @class.new
1210
+ inst.hookable.should == 'omg'
1211
+ end
1212
+
1213
+ end
1214
+
1215
+ end
1216
+
1217
+ describe "helper methods" do
1218
+ it 'should generate the correct argument signature' do
1219
+ @class.class_eval do
1220
+ def some_method(a, b, c)
1221
+ [a, b, c]
1222
+ end
1223
+
1224
+ def yet_another(a, *heh)
1225
+ [a, *heh]
1226
+ end
1227
+ end
1228
+
1229
+ @class.args_for(@class.instance_method(:hookable)).should == "&block"
1230
+ @class.args_for(@class.instance_method(:some_method)).should == "_1, _2, _3, &block"
1231
+ @class.args_for(@class.instance_method(:yet_another)).should == "_1, *args, &block"
1232
+ end
1233
+ end
1234
+
1235
+ end