super_callbacks 1.1.2 → 1.2.0

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 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