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