super_callbacks 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3fd64d5b5a8b0fb21cbafcc317f899179a7963e8
4
- data.tar.gz: 7e4297e1e6fcaf6d3ee7bf9f0a4ca496e441968e
3
+ metadata.gz: 1421ece86724a74cf1d418b8d78149445cf42147
4
+ data.tar.gz: 3fbe860e516f3b0f68a4025b2e8200218a2c7a56
5
5
  SHA512:
6
- metadata.gz: e2e56689fd4acb40b21350965389c379a6a8dd6315b0012f148807853dc6500d1f5e9533538ba0870ae054969c9e51b54c4d06056bdcc7fe069e488f67008feb
7
- data.tar.gz: db281bec9a46bf31edb20e4d11b833daa1871c061242f9161bea66fd5f085bfb6b1c8e1ceab7228f5011f18f82932757ad1493c4ae64474eeadc53d19b833f05
6
+ metadata.gz: 336f098ce35d7485319a625558df3e224eaac0e4fe0afc1b5eec0c08c7c1d290ff8ed87e0e7d518609ca0e6ef17bce86196011ca32b2d773d66eb3c0096c50b9
7
+ data.tar.gz: bdc621d66c9712b031b63a313e879fbe112ef448407ff8790f77137b03dc8d43432f8d5040d8f4617f895678eb6339ed266498c95fbf612c252df01317be6928
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.2.10
1
+ 2.2.10
data/README.md CHANGED
@@ -23,7 +23,7 @@
23
23
  Add this line to your application's Gemfile:
24
24
 
25
25
  ```ruby
26
- gem 'super_callbacks', '~> 1.1'
26
+ gem 'super_callbacks', '~> 1.2'
27
27
  ```
28
28
 
29
29
  And then execute:
@@ -425,6 +425,10 @@ The gem is available as open source under the terms of the [MIT License](https:/
425
425
 
426
426
  ## Changelog
427
427
 
428
+ * 1.2.0 (2019-08-15)
429
+ * [Fixed / Supported Nested "Dirty" Changes on nested callbacks for idempotency (Thread-safe)](https://github.com/jrpolidario/super_callbacks/blob/4e9284e1c6150c6d5da536766d51b4e635bb819d/spec/super_callbacks_spec.rb#L487)
430
+ * Housecleaning: segregated super_callbacks.rb into multiple files.
431
+ * Now puts "overriden" warning messages when `SuperCallbacks` is included to a class which already has methods conflicting `SuperCallback`'s DSL-methods.
428
432
  * 1.1.2 (2019-08-14)
429
433
  * [Supported "dirty" checking of instance variable changes](#example-10-dirty-checking-of-instance-variables-changes)
430
434
  * 1.0.3 (2019-08-12)
@@ -1,4 +1,9 @@
1
1
  require 'super_callbacks/version'
2
+ require 'super_callbacks/helpers'
3
+ require 'super_callbacks/prepended'
4
+ require 'super_callbacks/class_methods'
5
+ require 'super_callbacks/instance_methods'
6
+ require 'super_callbacks/class_and_instance_methods'
2
7
 
3
8
  module SuperCallbacks
4
9
  VALID_OPTION_KEYS = [:if].freeze
@@ -7,6 +12,8 @@ module SuperCallbacks
7
12
  # prevent re-including
8
13
  return if base.ancestors.detect { |ancestor| ancestor.is_a? SuperCallbacks::Prepended }
9
14
 
15
+ puts_warning_messages_when_methods_already_defined(base)
16
+
10
17
  base.singleton_class.send :attr_accessor, *[:before_callbacks, :after_callbacks]
11
18
  base.send :attr_accessor, *[:before_callbacks, :after_callbacks]
12
19
  base.extend ClassMethods
@@ -16,239 +23,41 @@ module SuperCallbacks
16
23
  base.send :prepend, Prepended.new
17
24
  end
18
25
 
19
- class Prepended < Module
20
- end
21
-
22
- module Helpers
23
- # (modified) File activesupport/lib/active_support/core_ext/hash/deep_merge.rb, line 18
24
- def self.deep_merge_hashes_and_combine_arrays(this_hash, other_hash, &block)
25
- self.deep_merge_hashes_and_combine_arrays!(this_hash.dup, other_hash, &block)
26
- end
27
-
28
- # (modified) File activesupport/lib/active_support/core_ext/hash/deep_merge.rb, line 23
29
- def self.deep_merge_hashes_and_combine_arrays!(this_hash, other_hash, &block)
30
- this_hash.merge!(other_hash) do |key, this_val, other_val|
31
- if this_val.is_a?(Hash) && other_val.is_a?(Hash)
32
- self.deep_merge_hashes(this_val, other_val, &block)
33
- elsif this_val.is_a?(Array) && other_val.is_a?(Array)
34
- this_val + other_val
35
- elsif block_given?
36
- block.call(key, this_val, other_val)
37
- else
38
- other_val
39
- end
40
- end
41
- end
42
- end
43
-
44
- module ClassAndInstanceMethods
45
- def before!(method_name, *remaining_args, &callback_proc)
46
- raise ArgumentError, "`#{method_name}` is not or not yet defined for #{self}" unless method_defined? method_name
47
- before(method_name, *remaining_args, &callback_proc)
48
- end
49
-
50
- def after!(method_name, *remaining_args, &callback_proc)
51
- raise ArgumentError, "`#{method_name}` is not or not yet defined for #{self}" unless method_defined? method_name
52
- after(method_name, *remaining_args, &callback_proc)
53
- end
54
-
55
- def before(method_name, callback_method_name = nil, options = {}, &callback_proc)
56
- callback_method_name_or_proc = callback_proc || callback_method_name
57
- unless [Symbol, String, Proc].any? { |klass| callback_method_name_or_proc.is_a? klass }
58
- raise ArgumentError, "Only `Symbol`, `String` or `Proc` allowed for `method_name`, but is #{callback_method_name_or_proc.class}"
59
- end
60
-
61
- invalid_option_keys = options.keys - VALID_OPTION_KEYS
62
- unless invalid_option_keys.empty?
63
- raise ArgumentError, "Invalid `options` keys: #{invalid_option_keys}. Valid are only: #{VALID_OPTION_KEYS}"
64
- end
65
- if options[:if] && !([Symbol, String, Proc].any? { |klass| callback_method_name_or_proc.is_a? klass })
66
- raise ArgumentError, "Only `Symbol`, `String` or `Proc` allowed for `options[:if]`, but is #{options[:if].class}"
67
- end
68
-
69
- self.before_callbacks ||= {}
70
- self.before_callbacks[method_name.to_sym] ||= []
71
- self.before_callbacks[method_name.to_sym] << [callback_method_name_or_proc, options[:if]]
72
-
73
- _callbacks_prepended_module_instance = callbacks_prepended_module_instance
74
-
75
- # dont redefine, to save cpu cycles
76
- unless _callbacks_prepended_module_instance.method_defined? method_name
77
- _callbacks_prepended_module_instance.send(:define_method, method_name) do |*args|
78
- begin
79
- @instance_variables_before_change = instance_variables.each_with_object({}) do |instance_variable, hash|
80
- hash[instance_variable] = instance_variable_get(instance_variable)
81
- end
82
-
83
- run_before_callbacks(method_name, *args)
84
- super_value = super(*args)
85
- run_after_callbacks(method_name, *args)
86
- ensure
87
- remove_instance_variable(:@instance_variables_before_change)
88
- end
89
-
90
- super_value
91
- end
92
- end
93
- end
94
-
95
- def after(method_name, callback_method_name = nil, options = {}, &callback_proc)
96
- callback_method_name_or_proc = callback_proc || callback_method_name
97
- unless [Symbol, String, Proc].include? callback_method_name_or_proc.class
98
- raise ArgumentError, "Only `Symbol`, `String` or `Proc` allowed for `method_name`, but is #{callback_method_name_or_proc.class}"
99
- end
100
-
101
- invalid_option_keys = options.keys - VALID_OPTION_KEYS
102
- unless invalid_option_keys.empty?
103
- raise ArgumentError, "Invalid `options` keys: #{invalid_option_keys}. Valid are only: #{VALID_OPTION_KEYS}"
104
- end
105
- if options[:if] && ![Symbol, String, Proc].include?(options[:if].class)
106
- raise ArgumentError, "Only `Symbol`, `String` or `Proc` allowed for `options[:if]`, but is #{options[:if].class}"
107
- end
108
-
109
- self.after_callbacks ||= {}
110
- self.after_callbacks[method_name.to_sym] ||= []
111
- self.after_callbacks[method_name.to_sym] << [callback_method_name_or_proc, options[:if]]
112
-
113
- _callbacks_prepended_module_instance = callbacks_prepended_module_instance
114
-
115
- # dont redefine, to save cpu cycles
116
- unless _callbacks_prepended_module_instance.method_defined? method_name
117
- _callbacks_prepended_module_instance.send(:define_method, method_name) do |*args|
118
- begin
119
- @instance_variables_before_change = instance_variables.each_with_object({}) do |instance_variable, hash|
120
- hash[instance_variable] = instance_variable_get(instance_variable)
121
- end
122
-
123
- run_before_callbacks(method_name, *args)
124
- super_value = super(*args)
125
- run_after_callbacks(method_name, *args)
126
- ensure
127
- remove_instance_variable(:@instance_variables_before_change)
128
- end
129
-
130
- super_value
131
- end
132
- end
133
- end
134
-
135
- def instance_variables_before_change
136
- @instance_variables_before_change
137
- end
138
-
139
- def instance_variable_before_change(instance_variable)
140
- raise ArgumentError, "#{instance_variable} should be a string that starts with `@`" unless instance_variable.to_s.start_with? '@'
141
- raise 'You cannot call this method outside the SuperCallback cycle' if instance_variables_before_change.nil?
142
- instance_variables_before_change[instance_variable.to_sym]
143
- end
144
-
145
- def instance_variable_changed?(instance_variable)
146
- raise ArgumentError, "#{instance_variable} should be a string that starts with `@`" unless instance_variable.to_s.start_with? '@'
147
- raise 'You cannot call this method outside the SuperCallback cycle' if instance_variables_before_change.nil?
148
-
149
- before_change_value = instance_variable_before_change(instance_variable.to_sym)
150
- current_value = instance_variable_get(instance_variable)
151
- before_change_value != current_value
152
- end
153
-
154
- # TODO
155
- # def around
156
- # end
157
- end
158
-
159
- module ClassMethods
160
-
161
- private
162
-
163
- def callbacks_prepended_module_instance
164
- ancestors.reverse.detect { |ancestor| ancestor.is_a? SuperCallbacks::Prepended }
165
- end
166
- end
167
-
168
- module InstanceMethods
169
- # TODO: optimize by instead of dynamically getting all_ancestral_after_callbacks on runtime
170
- # set them immediately when `include` is called on Base class
171
- def run_before_callbacks(method_name, *args)
172
- all_ancestral_before_callbacks = self.class.ancestors.reverse.each_with_object({}) do |ancestor, hash|
173
- SuperCallbacks::Helpers.deep_merge_hashes_and_combine_arrays!(
174
- hash,
175
- ancestor.instance_variable_get(:@before_callbacks) || {}
26
+ private
27
+
28
+ def self.puts_warning_messages_when_methods_already_defined(base)
29
+ overriden_instance_methods = (
30
+ base.instance_methods(false) & (
31
+ (
32
+ SuperCallbacks::ClassAndInstanceMethods.instance_methods(false) +
33
+ SuperCallbacks::ClassAndInstanceMethods.private_instance_methods(false)
34
+ ) |
35
+ (
36
+ SuperCallbacks::InstanceMethods.instance_methods(false) +
37
+ SuperCallbacks::InstanceMethods.private_instance_methods(false)
176
38
  )
177
- end
178
-
179
- singleton_class_before_callbacks = instance_variable_get(:@before_callbacks) || {}
180
-
181
- all_before_callbacks = SuperCallbacks::Helpers.deep_merge_hashes_and_combine_arrays(
182
- all_ancestral_before_callbacks,
183
- singleton_class_before_callbacks
184
39
  )
40
+ ).sort
185
41
 
186
- all_before_callbacks_on_method = all_before_callbacks[method_name] || []
187
-
188
- all_before_callbacks_on_method.each do |before_callback, options_if|
189
- is_condition_truthy = true
190
-
191
- if options_if
192
- is_condition_truthy = instance_exec *args, &options_if
193
- end
194
-
195
- if is_condition_truthy
196
- if before_callback.is_a? Proc
197
- instance_exec *args, &before_callback
198
- else
199
- send before_callback
200
- end
201
- end
202
- end
42
+ unless overriden_instance_methods.empty?
43
+ puts "WARN: SuperCallbacks will override #{base} the following already existing instance methods: #{overriden_instance_methods}"
203
44
  end
204
45
 
205
- # TODO: optimize by instead of dynamically getting all_ancestral_after_callbacks on runtime
206
- # set them immediately when `include` is called on Base class
207
- def run_after_callbacks(method_name, *args)
208
- all_ancestral_after_callbacks = self.class.ancestors.reverse.each_with_object({}) do |ancestor, hash|
209
- SuperCallbacks::Helpers.deep_merge_hashes_and_combine_arrays!(
210
- hash,
211
- ancestor.instance_variable_get(:@after_callbacks) || {}
46
+ overriden_class_methods = (
47
+ base.methods(false) & (
48
+ (
49
+ SuperCallbacks::ClassAndInstanceMethods.instance_methods(false) +
50
+ SuperCallbacks::ClassAndInstanceMethods.private_instance_methods(false)
51
+ ) |
52
+ (
53
+ SuperCallbacks::ClassMethods.instance_methods(false) +
54
+ SuperCallbacks::ClassMethods.private_instance_methods(false)
212
55
  )
213
- end
214
-
215
- singleton_class_after_callbacks = instance_variable_get(:@after_callbacks) || {}
216
-
217
- all_after_callbacks = SuperCallbacks::Helpers.deep_merge_hashes_and_combine_arrays(
218
- all_ancestral_after_callbacks,
219
- singleton_class_after_callbacks
220
56
  )
57
+ ).sort
221
58
 
222
- all_after_callbacks_on_method = all_after_callbacks[method_name] || []
223
-
224
- all_after_callbacks_on_method.each do |after_callback, options_if|
225
- is_condition_truthy = true
226
-
227
- if options_if
228
- is_condition_truthy = instance_exec *args, &options_if
229
- end
230
-
231
- if is_condition_truthy
232
- if after_callback.is_a? Proc
233
- instance_exec *args, &after_callback
234
- else
235
- send after_callback
236
- end
237
- end
238
- end
239
- end
240
-
241
- private
242
-
243
- def callbacks_prepended_module_instance
244
- _callbacks_prepended_module_instance = self.singleton_class.ancestors.reverse.detect { |ancestor| ancestor.is_a? SuperCallbacks::Prepended }
245
-
246
- if _callbacks_prepended_module_instance.nil?
247
- self.singleton_class.prepend SuperCallbacks::Prepended
248
- _callbacks_prepended_module_instance = self.singleton_class.ancestors.reverse.detect { |ancestor| ancestor.is_a? SuperCallbacks::Prepended }
249
- end
250
-
251
- _callbacks_prepended_module_instance
59
+ unless overriden_class_methods.empty?
60
+ puts "WARN: SuperCallbacks will override #{base} the following already existing class methods: #{overriden_class_methods}"
252
61
  end
253
62
  end
254
63
  end
@@ -0,0 +1,146 @@
1
+ module SuperCallbacks
2
+ # The methods defined here will be available "class" and "instance" for any class where SuperCallbacks is included
3
+ module ClassAndInstanceMethods
4
+ def before!(method_name, *remaining_args, &callback_proc)
5
+ raise ArgumentError, "`#{method_name}` is not or not yet defined for #{self}" unless method_defined? method_name
6
+ before(method_name, *remaining_args, &callback_proc)
7
+ end
8
+
9
+ def after!(method_name, *remaining_args, &callback_proc)
10
+ raise ArgumentError, "`#{method_name}` is not or not yet defined for #{self}" unless method_defined? method_name
11
+ after(method_name, *remaining_args, &callback_proc)
12
+ end
13
+
14
+ def before(method_name, callback_method_name = nil, options = {}, &callback_proc)
15
+ callback_method_name_or_proc = callback_proc || callback_method_name
16
+ unless [Symbol, String, Proc].any? { |klass| callback_method_name_or_proc.is_a? klass }
17
+ raise ArgumentError, "Only `Symbol`, `String` or `Proc` allowed for `method_name`, but is #{callback_method_name_or_proc.class}"
18
+ end
19
+
20
+ invalid_option_keys = options.keys - VALID_OPTION_KEYS
21
+ unless invalid_option_keys.empty?
22
+ raise ArgumentError, "Invalid `options` keys: #{invalid_option_keys}. Valid are only: #{VALID_OPTION_KEYS}"
23
+ end
24
+ if options[:if] && !([Symbol, String, Proc].any? { |klass| callback_method_name_or_proc.is_a? klass })
25
+ raise ArgumentError, "Only `Symbol`, `String` or `Proc` allowed for `options[:if]`, but is #{options[:if].class}"
26
+ end
27
+
28
+ self.before_callbacks ||= {}
29
+ self.before_callbacks[method_name.to_sym] ||= []
30
+ self.before_callbacks[method_name.to_sym] << [callback_method_name_or_proc, options[:if]]
31
+
32
+ _callbacks_prepended_module_instance = callbacks_prepended_module_instance
33
+
34
+ # dont redefine, to save cpu cycles
35
+ unless _callbacks_prepended_module_instance.method_defined? method_name
36
+ _callbacks_prepended_module_instance.send(:define_method, method_name) do |*args|
37
+ begin
38
+ # refactored to use Thread.current for thread-safetiness
39
+ Thread.current[:super_callbacks_all_instance_variables_before_change] ||= {}
40
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id] ||= []
41
+
42
+ all_instance_variables_before_change = instance_variables.each_with_object({}) do |instance_variable, hash|
43
+ hash[instance_variable] = instance_variable_get(instance_variable)
44
+ end
45
+
46
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id] << all_instance_variables_before_change
47
+
48
+ run_before_callbacks(method_name, *args)
49
+ super_value = super(*args)
50
+ run_after_callbacks(method_name, *args)
51
+ ensure
52
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id].pop
53
+
54
+ if Thread.current[:super_callbacks_all_instance_variables_before_change][object_id].empty?
55
+ Thread.current[:super_callbacks_all_instance_variables_before_change].delete(object_id)
56
+ end
57
+
58
+ if Thread.current[:super_callbacks_all_instance_variables_before_change].empty?
59
+ Thread.current[:super_callbacks_all_instance_variables_before_change] = nil
60
+ end
61
+ end
62
+
63
+ super_value
64
+ end
65
+ end
66
+ end
67
+
68
+ def after(method_name, callback_method_name = nil, options = {}, &callback_proc)
69
+ callback_method_name_or_proc = callback_proc || callback_method_name
70
+ unless [Symbol, String, Proc].include? callback_method_name_or_proc.class
71
+ raise ArgumentError, "Only `Symbol`, `String` or `Proc` allowed for `method_name`, but is #{callback_method_name_or_proc.class}"
72
+ end
73
+
74
+ invalid_option_keys = options.keys - VALID_OPTION_KEYS
75
+ unless invalid_option_keys.empty?
76
+ raise ArgumentError, "Invalid `options` keys: #{invalid_option_keys}. Valid are only: #{VALID_OPTION_KEYS}"
77
+ end
78
+ if options[:if] && ![Symbol, String, Proc].include?(options[:if].class)
79
+ raise ArgumentError, "Only `Symbol`, `String` or `Proc` allowed for `options[:if]`, but is #{options[:if].class}"
80
+ end
81
+
82
+ self.after_callbacks ||= {}
83
+ self.after_callbacks[method_name.to_sym] ||= []
84
+ self.after_callbacks[method_name.to_sym] << [callback_method_name_or_proc, options[:if]]
85
+
86
+ _callbacks_prepended_module_instance = callbacks_prepended_module_instance
87
+
88
+ # dont redefine, to save cpu cycles
89
+ unless _callbacks_prepended_module_instance.method_defined? method_name
90
+ _callbacks_prepended_module_instance.send(:define_method, method_name) do |*args|
91
+ begin
92
+ # refactored to use Thread.current for thread-safetiness
93
+ Thread.current[:super_callbacks_all_instance_variables_before_change] ||= {}
94
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id] ||= []
95
+
96
+ all_instance_variables_before_change = instance_variables.each_with_object({}) do |instance_variable, hash|
97
+ hash[instance_variable] = instance_variable_get(instance_variable)
98
+ end
99
+
100
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id] << all_instance_variables_before_change
101
+
102
+ run_before_callbacks(method_name, *args)
103
+ super_value = super(*args)
104
+ run_after_callbacks(method_name, *args)
105
+ ensure
106
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id].pop
107
+
108
+ if Thread.current[:super_callbacks_all_instance_variables_before_change][object_id].empty?
109
+ Thread.current[:super_callbacks_all_instance_variables_before_change].delete(object_id)
110
+ end
111
+
112
+ if Thread.current[:super_callbacks_all_instance_variables_before_change].empty?
113
+ Thread.current[:super_callbacks_all_instance_variables_before_change] = nil
114
+ end
115
+ end
116
+
117
+ super_value
118
+ end
119
+ end
120
+ end
121
+
122
+ def instance_variables_before_change
123
+ raise 'You cannot call this method outside the SuperCallback cycle' if Thread.current[:super_callbacks_all_instance_variables_before_change].nil?
124
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id].last
125
+ end
126
+
127
+ def instance_variable_before_change(instance_variable)
128
+ raise 'You cannot call this method outside the SuperCallback cycle' if Thread.current[:super_callbacks_all_instance_variables_before_change].nil?
129
+ raise ArgumentError, "#{instance_variable} should be a string that starts with `@`" unless instance_variable.to_s.start_with? '@'
130
+ instance_variables_before_change[instance_variable.to_sym]
131
+ end
132
+
133
+ def instance_variable_changed?(instance_variable)
134
+ raise 'You cannot call this method outside the SuperCallback cycle' if Thread.current[:super_callbacks_all_instance_variables_before_change].nil?
135
+ raise ArgumentError, "#{instance_variable} should be a string that starts with `@`" unless instance_variable.to_s.start_with? '@'
136
+
137
+ before_change_value = instance_variable_before_change(instance_variable.to_sym)
138
+ current_value = instance_variable_get(instance_variable)
139
+ before_change_value != current_value
140
+ end
141
+
142
+ # TODO
143
+ # def around
144
+ # end
145
+ end
146
+ end
@@ -0,0 +1,10 @@
1
+ module SuperCallbacks
2
+ # The methods defined here will be available "class" methods for any class where SuperCallbacks is included
3
+ module ClassMethods
4
+ private
5
+
6
+ def callbacks_prepended_module_instance
7
+ ancestors.reverse.detect { |ancestor| ancestor.is_a? SuperCallbacks::Prepended }
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,23 @@
1
+ module SuperCallbacks
2
+ module Helpers
3
+ # (modified) File activesupport/lib/active_support/core_ext/hash/deep_merge.rb, line 18
4
+ def self.deep_merge_hashes_and_combine_arrays(this_hash, other_hash, &block)
5
+ self.deep_merge_hashes_and_combine_arrays!(this_hash.dup, other_hash, &block)
6
+ end
7
+
8
+ # (modified) File activesupport/lib/active_support/core_ext/hash/deep_merge.rb, line 23
9
+ def self.deep_merge_hashes_and_combine_arrays!(this_hash, other_hash, &block)
10
+ this_hash.merge!(other_hash) do |key, this_val, other_val|
11
+ if this_val.is_a?(Hash) && other_val.is_a?(Hash)
12
+ self.deep_merge_hashes(this_val, other_val, &block)
13
+ elsif this_val.is_a?(Array) && other_val.is_a?(Array)
14
+ this_val + other_val
15
+ elsif block_given?
16
+ block.call(key, this_val, other_val)
17
+ else
18
+ other_val
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,89 @@
1
+ module SuperCallbacks
2
+ # The methods defined here will be available "class" methods for any class where SuperCallbacks is included
3
+ module InstanceMethods
4
+ # TODO: optimize by instead of dynamically getting all_ancestral_after_callbacks on runtime
5
+ # set them immediately when `include` is called on Base class
6
+ def run_before_callbacks(method_name, *args)
7
+ all_ancestral_before_callbacks = self.class.ancestors.reverse.each_with_object({}) do |ancestor, hash|
8
+ SuperCallbacks::Helpers.deep_merge_hashes_and_combine_arrays!(
9
+ hash,
10
+ ancestor.instance_variable_get(:@before_callbacks) || {}
11
+ )
12
+ end
13
+
14
+ singleton_class_before_callbacks = instance_variable_get(:@before_callbacks) || {}
15
+
16
+ all_before_callbacks = SuperCallbacks::Helpers.deep_merge_hashes_and_combine_arrays(
17
+ all_ancestral_before_callbacks,
18
+ singleton_class_before_callbacks
19
+ )
20
+
21
+ all_before_callbacks_on_method = all_before_callbacks[method_name] || []
22
+
23
+ all_before_callbacks_on_method.each do |before_callback, options_if|
24
+ is_condition_truthy = true
25
+
26
+ if options_if
27
+ is_condition_truthy = instance_exec *args, &options_if
28
+ end
29
+
30
+ if is_condition_truthy
31
+ if before_callback.is_a? Proc
32
+ instance_exec *args, &before_callback
33
+ else
34
+ send before_callback
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ # TODO: optimize by instead of dynamically getting all_ancestral_after_callbacks on runtime
41
+ # set them immediately when `include` is called on Base class
42
+ def run_after_callbacks(method_name, *args)
43
+ all_ancestral_after_callbacks = self.class.ancestors.reverse.each_with_object({}) do |ancestor, hash|
44
+ SuperCallbacks::Helpers.deep_merge_hashes_and_combine_arrays!(
45
+ hash,
46
+ ancestor.instance_variable_get(:@after_callbacks) || {}
47
+ )
48
+ end
49
+
50
+ singleton_class_after_callbacks = instance_variable_get(:@after_callbacks) || {}
51
+
52
+ all_after_callbacks = SuperCallbacks::Helpers.deep_merge_hashes_and_combine_arrays(
53
+ all_ancestral_after_callbacks,
54
+ singleton_class_after_callbacks
55
+ )
56
+
57
+ all_after_callbacks_on_method = all_after_callbacks[method_name] || []
58
+
59
+ all_after_callbacks_on_method.each do |after_callback, options_if|
60
+ is_condition_truthy = true
61
+
62
+ if options_if
63
+ is_condition_truthy = instance_exec *args, &options_if
64
+ end
65
+
66
+ if is_condition_truthy
67
+ if after_callback.is_a? Proc
68
+ instance_exec *args, &after_callback
69
+ else
70
+ send after_callback
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ private
77
+
78
+ def callbacks_prepended_module_instance
79
+ _callbacks_prepended_module_instance = self.singleton_class.ancestors.reverse.detect { |ancestor| ancestor.is_a? SuperCallbacks::Prepended }
80
+
81
+ if _callbacks_prepended_module_instance.nil?
82
+ self.singleton_class.prepend SuperCallbacks::Prepended
83
+ _callbacks_prepended_module_instance = self.singleton_class.ancestors.reverse.detect { |ancestor| ancestor.is_a? SuperCallbacks::Prepended }
84
+ end
85
+
86
+ _callbacks_prepended_module_instance
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,9 @@
1
+ # This is the module that will be prepended to the base (target) class where `include SuperCallbacks`
2
+ # have been called. This will be always a new instance of a Module, and will not be a permanent
3
+ # Module. This is important because, this module-instance is used by SuperCallbacks to define
4
+ # the methods whenever `before` or `after` is called. These defined methods here will then have a
5
+ # `super` inside it, which then will also call the "real" method defined in the target class.
6
+ module SuperCallbacks
7
+ class Prepended < Module
8
+ end
9
+ end
@@ -1,3 +1,3 @@
1
1
  module SuperCallbacks
2
- VERSION = '1.1.2'
2
+ VERSION = '1.2.0'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: super_callbacks
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.2
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jules Roman B. Polidario
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-08-14 00:00:00.000000000 Z
11
+ date: 2019-08-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -87,6 +87,11 @@ files:
87
87
  - bin/console
88
88
  - bin/setup
89
89
  - lib/super_callbacks.rb
90
+ - lib/super_callbacks/class_and_instance_methods.rb
91
+ - lib/super_callbacks/class_methods.rb
92
+ - lib/super_callbacks/helpers.rb
93
+ - lib/super_callbacks/instance_methods.rb
94
+ - lib/super_callbacks/prepended.rb
90
95
  - lib/super_callbacks/version.rb
91
96
  - super_callbacks.gemspec
92
97
  homepage: https://github.com/jrpolidario/super_callbacks