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 +4 -4
- data/README.md +4 -1
- data/lib/super_callbacks.rb +74 -1
- data/lib/super_callbacks/class_and_instance_methods.rb +150 -146
- data/lib/super_callbacks/class_methods.rb +10 -10
- data/lib/super_callbacks/helpers.rb +43 -23
- data/lib/super_callbacks/instance_methods.rb +71 -89
- data/lib/super_callbacks/prepended.rb +14 -9
- data/lib/super_callbacks/version.rb +3 -3
- data/super_callbacks.gemspec +30 -29
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a9de4f62f5259330dd9c9043dcd6e9e5faa50bfb
|
4
|
+
data.tar.gz: 5428ec46f89d68264b01e11d2ab32a9ac076834d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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.
|
data/lib/super_callbacks.rb
CHANGED
@@ -20,7 +20,80 @@ module SuperCallbacks
|
|
20
20
|
base.send :include, InstanceMethods
|
21
21
|
base.extend ClassAndInstanceMethods
|
22
22
|
base.send :include, ClassAndInstanceMethods
|
23
|
-
|
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
|
-
|
33
|
-
|
34
|
-
# dont redefine, to save cpu cycles
|
35
|
-
unless
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
Thread.current
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
self.after_callbacks
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
if Thread.current[:super_callbacks_all_instance_variables_before_change].empty?
|
113
|
-
Thread.current[:super_callbacks_all_instance_variables_before_change]
|
114
|
-
end
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
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
|
+
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
|
-
|
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
|
-
|
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
|
+
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
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
|
+
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.
|
3
|
-
end
|
1
|
+
module SuperCallbacks
|
2
|
+
VERSION = '1.3.0'
|
3
|
+
end
|
data/super_callbacks.gemspec
CHANGED
@@ -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 '
|
29
|
-
|
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.
|
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-
|
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:
|
56
|
+
name: pry
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
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:
|
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
|