super_callbacks 1.2.0 → 1.3.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: 1421ece86724a74cf1d418b8d78149445cf42147
4
- data.tar.gz: 3fbe860e516f3b0f68a4025b2e8200218a2c7a56
3
+ metadata.gz: a9de4f62f5259330dd9c9043dcd6e9e5faa50bfb
4
+ data.tar.gz: 5428ec46f89d68264b01e11d2ab32a9ac076834d
5
5
  SHA512:
6
- metadata.gz: 336f098ce35d7485319a625558df3e224eaac0e4fe0afc1b5eec0c08c7c1d290ff8ed87e0e7d518609ca0e6ef17bce86196011ca32b2d773d66eb3c0096c50b9
7
- data.tar.gz: bdc621d66c9712b031b63a313e879fbe112ef448407ff8790f77137b03dc8d43432f8d5040d8f4617f895678eb6339ed266498c95fbf612c252df01317be6928
6
+ metadata.gz: 7416ac8af83da5407af6113f103b28dee8729086b0e9f2cc53b689252abe4883c23cdaeff553102761a8b5dbf16222d40ca009e0fff5ee273a03eb2633a0e308
7
+ data.tar.gz: 3c984287755e49a18b8b5b3e46ee6a95911ca9e7bf5b9e86e922638c4329b73a3d5166264aa62647930d021bdf00e711a8537af78e18838a1bfedbb82148d042
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.2'
26
+ gem 'super_callbacks', '~> 1.3'
27
27
  ```
28
28
 
29
29
  And then execute:
@@ -425,6 +425,9 @@ The gem is available as open source under the terms of the [MIT License](https:/
425
425
 
426
426
  ## Changelog
427
427
 
428
+ * [1.3.0 (2019-08-15)](https://github.com/jrpolidario/super_callbacks/commit/dcc71648d9be06074280c936add04b6b3f921e10)
429
+ * [Fixed difficult bug: callbacks not in order when method defined in subclass and not in superclass when using inherited callbacks](https://github.com/jrpolidario/super_callbacks/issues/1
430
+ * Refactored a lot of code, and added slight overhead. I'm satisfied at the moment until I could find the best solution.
428
431
  * 1.2.0 (2019-08-15)
429
432
  * [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
433
  * Housecleaning: segregated super_callbacks.rb into multiple files.
@@ -20,7 +20,80 @@ module SuperCallbacks
20
20
  base.send :include, InstanceMethods
21
21
  base.extend ClassAndInstanceMethods
22
22
  base.send :include, ClassAndInstanceMethods
23
- base.send :prepend, Prepended.new
23
+
24
+ base.singleton_class.send :attr_accessor, :super_callbacks_prepended
25
+ base.super_callbacks_prepended = Prepended.new(base)
26
+ base.send :prepend, base.super_callbacks_prepended
27
+
28
+ # needed to fix bug while still support nested callbacks of which methods
29
+ # are defined in the subclasses
30
+ base.singleton_class.send :prepend, InheritancePrepender
31
+ end
32
+
33
+ module InheritancePrepender
34
+ def inherited(subclass)
35
+ # need to make a copy of the last SuperCallbacks::Prepended module in the ancestor chain
36
+ # and then `prepend` that module into the newly defined subclass
37
+ first_callbacks_prepended_module_instance = self.ancestors.detect { |ancestor| ancestor.is_a? SuperCallbacks::Prepended }
38
+
39
+ new_super_callbacks_prepended = Prepended.new(subclass)
40
+
41
+ subclass.singleton_class.send :attr_accessor, :super_callbacks_prepended
42
+ subclass.super_callbacks_prepended = new_super_callbacks_prepended
43
+
44
+ # ... which could be done via redefining the methods first...
45
+ (
46
+ first_callbacks_prepended_module_instance.instance_methods(false) +
47
+ first_callbacks_prepended_module_instance.private_instance_methods(false)
48
+ ).each do |method_name|
49
+ new_super_callbacks_prepended.send(:define_method, method_name) do |*args|
50
+ # this is the reason why this method needs to be redefined, and not just simply
51
+ # copied from the last SuperCallbacks::Prepended module;
52
+ # because the callback must only run once on the outermost prepended module
53
+ # for details, see spec 'runs the callbacks in correct order when the method is defined in the subclass'
54
+ return super(*args) if self.class.super_callbacks_prepended != subclass.super_callbacks_prepended
55
+
56
+ begin
57
+ # refactored to use Thread.current for thread-safetiness
58
+ Thread.current[:super_callbacks_all_instance_variables_before_change] ||= {}
59
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id] ||= []
60
+
61
+ all_instance_variables_before_change = instance_variables.each_with_object({}) do |instance_variable, hash|
62
+ hash[instance_variable] = instance_variable_get(instance_variable)
63
+ end
64
+
65
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id] << all_instance_variables_before_change
66
+
67
+ run_before_callbacks(method_name, *args)
68
+ super_value = super(*args)
69
+ run_after_callbacks(method_name, *args)
70
+ ensure
71
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id].pop
72
+
73
+ if Thread.current[:super_callbacks_all_instance_variables_before_change][object_id].empty?
74
+ Thread.current[:super_callbacks_all_instance_variables_before_change].delete(object_id)
75
+ end
76
+
77
+ if Thread.current[:super_callbacks_all_instance_variables_before_change].empty?
78
+ Thread.current[:super_callbacks_all_instance_variables_before_change] = nil
79
+ end
80
+ end
81
+
82
+ super_value
83
+ end
84
+ end
85
+
86
+ subclass.send :prepend, new_super_callbacks_prepended
87
+
88
+ copied_before_callbacks = Helpers.deep_array_and_hash_dup(first_callbacks_prepended_module_instance.base.before_callbacks)
89
+ copied_after_callbacks = Helpers.deep_array_and_hash_dup(first_callbacks_prepended_module_instance.base.after_callbacks)
90
+
91
+ subclass.instance_variable_set(:@before_callbacks, copied_before_callbacks)
92
+ subclass.instance_variable_set(:@after_callbacks, copied_after_callbacks)
93
+
94
+ # subclass.singleton_class.send :prepend, InheritancePrepender
95
+ super
96
+ end
24
97
  end
25
98
 
26
99
  private
@@ -1,146 +1,150 @@
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
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
+ super_callbacks_prepended = self.super_callbacks_prepended
33
+
34
+ # dont redefine, to save cpu cycles
35
+ unless super_callbacks_prepended.instance_methods(false).include? method_name
36
+ super_callbacks_prepended.send(:define_method, method_name) do |*args|
37
+ return super(*args) if self.class.super_callbacks_prepended != super_callbacks_prepended
38
+
39
+ begin
40
+ # refactored to use Thread.current for thread-safetiness
41
+ Thread.current[:super_callbacks_all_instance_variables_before_change] ||= {}
42
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id] ||= []
43
+
44
+ all_instance_variables_before_change = instance_variables.each_with_object({}) do |instance_variable, hash|
45
+ hash[instance_variable] = instance_variable_get(instance_variable)
46
+ end
47
+
48
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id] << all_instance_variables_before_change
49
+
50
+ run_before_callbacks(method_name, *args)
51
+ super_value = super(*args)
52
+ run_after_callbacks(method_name, *args)
53
+ ensure
54
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id].pop
55
+
56
+ if Thread.current[:super_callbacks_all_instance_variables_before_change][object_id].empty?
57
+ Thread.current[:super_callbacks_all_instance_variables_before_change].delete(object_id)
58
+ end
59
+
60
+ if Thread.current[:super_callbacks_all_instance_variables_before_change].empty?
61
+ Thread.current[:super_callbacks_all_instance_variables_before_change] = nil
62
+ end
63
+ end
64
+
65
+ super_value
66
+ end
67
+ end
68
+ end
69
+
70
+ def after(method_name, callback_method_name = nil, options = {}, &callback_proc)
71
+ callback_method_name_or_proc = callback_proc || callback_method_name
72
+ unless [Symbol, String, Proc].include? callback_method_name_or_proc.class
73
+ raise ArgumentError, "Only `Symbol`, `String` or `Proc` allowed for `method_name`, but is #{callback_method_name_or_proc.class}"
74
+ end
75
+
76
+ invalid_option_keys = options.keys - VALID_OPTION_KEYS
77
+ unless invalid_option_keys.empty?
78
+ raise ArgumentError, "Invalid `options` keys: #{invalid_option_keys}. Valid are only: #{VALID_OPTION_KEYS}"
79
+ end
80
+ if options[:if] && ![Symbol, String, Proc].include?(options[:if].class)
81
+ raise ArgumentError, "Only `Symbol`, `String` or `Proc` allowed for `options[:if]`, but is #{options[:if].class}"
82
+ end
83
+
84
+ self.after_callbacks ||= {}
85
+ self.after_callbacks[method_name.to_sym] ||= []
86
+ self.after_callbacks[method_name.to_sym] << [callback_method_name_or_proc, options[:if]]
87
+
88
+ super_callbacks_prepended = self.super_callbacks_prepended
89
+
90
+ # dont redefine, to save cpu cycles
91
+ unless super_callbacks_prepended.instance_methods(false).include? method_name
92
+ super_callbacks_prepended.send(:define_method, method_name) do |*args|
93
+ return super(*args) if self.class.super_callbacks_prepended != super_callbacks_prepended
94
+
95
+ begin
96
+ # refactored to use Thread.current for thread-safetiness
97
+ Thread.current[:super_callbacks_all_instance_variables_before_change] ||= {}
98
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id] ||= []
99
+
100
+ all_instance_variables_before_change = instance_variables.each_with_object({}) do |instance_variable, hash|
101
+ hash[instance_variable] = instance_variable_get(instance_variable)
102
+ end
103
+
104
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id] << all_instance_variables_before_change
105
+
106
+ run_before_callbacks(method_name, *args)
107
+ super_value = super(*args)
108
+ run_after_callbacks(method_name, *args)
109
+ ensure
110
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id].pop
111
+
112
+ if Thread.current[:super_callbacks_all_instance_variables_before_change][object_id].empty?
113
+ Thread.current[:super_callbacks_all_instance_variables_before_change].delete(object_id)
114
+ end
115
+
116
+ if Thread.current[:super_callbacks_all_instance_variables_before_change].empty?
117
+ Thread.current[:super_callbacks_all_instance_variables_before_change] = nil
118
+ end
119
+ end
120
+
121
+ super_value
122
+ end
123
+ end
124
+ end
125
+
126
+ def instance_variables_before_change
127
+ raise 'You cannot call this method outside the SuperCallbacks cycle' if Thread.current[:super_callbacks_all_instance_variables_before_change].nil?
128
+ Thread.current[:super_callbacks_all_instance_variables_before_change][object_id].last
129
+ end
130
+
131
+ def instance_variable_before_change(instance_variable)
132
+ raise 'You cannot call this method outside the SuperCallbacks cycle' if Thread.current[:super_callbacks_all_instance_variables_before_change].nil?
133
+ raise ArgumentError, "#{instance_variable} should be a string that starts with `@`" unless instance_variable.to_s.start_with? '@'
134
+ instance_variables_before_change[instance_variable.to_sym]
135
+ end
136
+
137
+ def instance_variable_changed?(instance_variable)
138
+ raise 'You cannot call this method outside the SuperCallbacks cycle' if Thread.current[:super_callbacks_all_instance_variables_before_change].nil?
139
+ raise ArgumentError, "#{instance_variable} should be a string that starts with `@`" unless instance_variable.to_s.start_with? '@'
140
+
141
+ before_change_value = instance_variable_before_change(instance_variable.to_sym)
142
+ current_value = instance_variable_get(instance_variable)
143
+ before_change_value != current_value
144
+ end
145
+
146
+ # TODO
147
+ # def around
148
+ # end
149
+ end
150
+ end
@@ -1,10 +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
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
@@ -1,23 +1,43 @@
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
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
+
23
+ def self.deep_array_and_hash_dup(enumerables, levels_left = nil)
24
+ return enumerables if levels_left && levels_left == 0
25
+
26
+ if enumerables.is_a?(Array)
27
+ enumerables = enumerables.dup
28
+ enumerables.each_with_index do |enumerable, index|
29
+ enumerables[index] = deep_array_and_hash_dup(enumerable, levels_left.nil? ? nil : levels_left - 1)
30
+ end
31
+ enumerables
32
+ elsif enumerables.is_a?(Hash)
33
+ enumerables = enumerables.dup
34
+ enumerables.each do |key, value|
35
+ enumerables[key] = deep_array_and_hash_dup(value, levels_left.nil? ? nil : levels_left - 1)
36
+ end
37
+ enumerables
38
+ else
39
+ enumerables
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,89 +1,71 @@
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
1
+ module SuperCallbacks
2
+ # The methods defined here will be available "class" methods for any class where SuperCallbacks is included
3
+ module InstanceMethods
4
+ def run_before_callbacks(method_name, *args)
5
+ class_before_callbacks = self.class.instance_variable_get(:@before_callbacks) || {}
6
+ instance_before_callbacks = instance_variable_get(:@before_callbacks) || {}
7
+
8
+ all_before_callbacks = SuperCallbacks::Helpers.deep_merge_hashes_and_combine_arrays(
9
+ class_before_callbacks,
10
+ instance_before_callbacks
11
+ )
12
+
13
+ all_before_callbacks_on_method = all_before_callbacks[method_name] || []
14
+
15
+ all_before_callbacks_on_method.each do |before_callback, options_if|
16
+ is_condition_truthy = true
17
+
18
+ if options_if
19
+ is_condition_truthy = instance_exec *args, &options_if
20
+ end
21
+
22
+ if is_condition_truthy
23
+ if before_callback.is_a? Proc
24
+ instance_exec *args, &before_callback
25
+ else
26
+ send before_callback
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ def run_after_callbacks(method_name, *args)
33
+ class_after_callbacks = self.class.instance_variable_get(:@after_callbacks) || {}
34
+ instance_after_callbacks = instance_variable_get(:@after_callbacks) || {}
35
+
36
+ all_after_callbacks = SuperCallbacks::Helpers.deep_merge_hashes_and_combine_arrays(
37
+ class_after_callbacks,
38
+ instance_after_callbacks
39
+ )
40
+
41
+ all_after_callbacks_on_method = all_after_callbacks[method_name] || []
42
+
43
+ all_after_callbacks_on_method.each do |after_callback, options_if|
44
+ is_condition_truthy = true
45
+
46
+ if options_if
47
+ is_condition_truthy = instance_exec *args, &options_if
48
+ end
49
+
50
+ if is_condition_truthy
51
+ if after_callback.is_a? Proc
52
+ instance_exec *args, &after_callback
53
+ else
54
+ send after_callback
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ def super_callbacks_prepended
61
+ _super_callbacks_prepended = self.singleton_class.ancestors.reverse.detect { |ancestor| ancestor.is_a? SuperCallbacks::Prepended }
62
+
63
+ if _super_callbacks_prepended.nil?
64
+ self.singleton_class.prepend SuperCallbacks::Prepended
65
+ _super_callbacks_prepended = self.singleton_class.ancestors.reverse.detect { |ancestor| ancestor.is_a? SuperCallbacks::Prepended }
66
+ end
67
+
68
+ _super_callbacks_prepended
69
+ end
70
+ end
71
+ end
@@ -1,9 +1,14 @@
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
+ # 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
+ attr_accessor :base
9
+
10
+ def initialize(base)
11
+ @base = base
12
+ end
13
+ end
14
+ end
@@ -1,3 +1,3 @@
1
- module SuperCallbacks
2
- VERSION = '1.2.0'
3
- end
1
+ module SuperCallbacks
2
+ VERSION = '1.3.0'
3
+ end
@@ -1,29 +1,30 @@
1
- lib = File.expand_path('lib', __dir__)
2
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require 'super_callbacks/version'
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = 'super_callbacks'
7
- spec.version = SuperCallbacks::VERSION
8
- spec.authors = ['Jules Roman B. Polidario']
9
- spec.email = ['jules@topfloor.ie']
10
-
11
- spec.summary = 'Allows `before` and `after` callbacks to any Class. Supports dirty checking of instance variables changes, class and instance level callbacks, conditional callbacks, and inherited callbacks.'
12
- spec.description = 'Allows `before` and `after` callbacks to any Class. Supports dirty checking of instance variables changes, class and instance level callbacks, conditional callbacks, and inherited callbacks. Focuses on performance and flexibility as intended primarily for game development, and event-driven apps.'
13
- spec.homepage = 'https://github.com/jrpolidario/super_callbacks'
14
- spec.license = 'MIT'
15
-
16
- spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
- f.match(%r{^(test|spec|features)/})
18
- end
19
- spec.bindir = 'exe'
20
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
- spec.require_paths = ['lib']
22
-
23
- spec.required_ruby_version = '~> 2.0'
24
-
25
- spec.add_development_dependency 'bundler', '~> 1.16'
26
- spec.add_development_dependency 'rake', '~> 10.0'
27
- spec.add_development_dependency 'rspec', '~> 3.0'
28
- spec.add_development_dependency 'byebug', '~> 9.0'
29
- end
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'super_callbacks/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'super_callbacks'
7
+ spec.version = SuperCallbacks::VERSION
8
+ spec.authors = ['Jules Roman B. Polidario']
9
+ spec.email = ['jules@topfloor.ie']
10
+
11
+ spec.summary = 'Allows `before` and `after` callbacks to any Class. Supports dirty checking of instance variables changes, class and instance level callbacks, conditional callbacks, and inherited callbacks.'
12
+ spec.description = 'Allows `before` and `after` callbacks to any Class. Supports dirty checking of instance variables changes, class and instance level callbacks, conditional callbacks, and inherited callbacks. Focuses on performance and flexibility as intended primarily for game development, and event-driven apps.'
13
+ spec.homepage = 'https://github.com/jrpolidario/super_callbacks'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.required_ruby_version = '~> 2.0'
24
+
25
+ spec.add_development_dependency 'bundler', '~> 1.16'
26
+ spec.add_development_dependency 'rake', '~> 10.0'
27
+ spec.add_development_dependency 'rspec', '~> 3.0'
28
+ spec.add_development_dependency 'pry', '~> 0.12.2'
29
+ spec.add_development_dependency 'pry-nav', '~> 0.3.0'
30
+ 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.2.0
4
+ version: 1.3.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-15 00:00:00.000000000 Z
11
+ date: 2019-08-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -53,19 +53,33 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: byebug
56
+ name: pry
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '9.0'
61
+ version: 0.12.2
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '9.0'
68
+ version: 0.12.2
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-nav
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.3.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.3.0
69
83
  description: Allows `before` and `after` callbacks to any Class. Supports dirty checking
70
84
  of instance variables changes, class and instance level callbacks, conditional callbacks,
71
85
  and inherited callbacks. Focuses on performance and flexibility as intended primarily