verifly 0.2.0.1 → 0.3.1.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 +5 -5
- data/README.md +1 -1
- data/lib/verifly.rb +7 -5
- data/lib/verifly/applicator.rb +95 -24
- data/lib/verifly/applicator_with_options.rb +15 -7
- data/lib/verifly/class_builder.rb +4 -4
- data/lib/verifly/dependent_callbacks.rb +112 -0
- data/lib/verifly/dependent_callbacks/callback.rb +59 -0
- data/lib/verifly/dependent_callbacks/callback_group.rb +115 -0
- data/lib/verifly/dependent_callbacks/invoker.rb +156 -0
- data/lib/verifly/dependent_callbacks/service.rb +71 -0
- data/lib/verifly/dependent_callbacks/storage.rb +36 -0
- data/lib/verifly/has_logger.rb +28 -0
- data/lib/verifly/verifier.rb +2 -2
- data/lib/verifly/version.rb +1 -1
- metadata +81 -61
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ef4ba187f3fbc61190750de9fe19bf780c8a17e980b5efe4658c748ab1578fc7
|
4
|
+
data.tar.gz: a879d55708639831374f646889db8b8385d769737b378906183776f49cafdd53
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 12191c302d7adf9081af6ccb818a20439ae387a5509b85f7e80d7457689669975ca78fc3ac4e4971a21c85664dede77c9a9744bd783bd73d124fcf675a1e1ffb
|
7
|
+
data.tar.gz: 7b28d9b3c44a3a8453c0eaeecea85eee02e9275a1d4db308afb0ec977cd2c30e039ab2ec959003509f5f41cac4b76eafb5d97b3404f7744eed6f429908136952
|
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Verifly v0.2
|
2
|
-
[](https://travis-ci.org/umbrellio/verifly)
|
2
|
+
[](https://travis-ci.org/umbrellio/verifly)[](https://coveralls.io/github/umbrellio/verifly)
|
3
3
|
|
4
4
|
This gem provides an api to run sequential checks like
|
5
5
|
'ActiveModel::Validations' do, but with generic messages instead of errors.
|
data/lib/verifly.rb
CHANGED
@@ -4,10 +4,12 @@
|
|
4
4
|
# important one, while others depend on it.
|
5
5
|
# See README.md or in-code documentation for more info.
|
6
6
|
module Verifly
|
7
|
-
autoload :VERSION,
|
7
|
+
autoload :VERSION, "verifly/version"
|
8
8
|
|
9
|
-
autoload :Applicator,
|
10
|
-
autoload :ApplicatorWithOptions,
|
11
|
-
autoload :ClassBuilder,
|
12
|
-
autoload :
|
9
|
+
autoload :Applicator, "verifly/applicator"
|
10
|
+
autoload :ApplicatorWithOptions, "verifly/applicator_with_options"
|
11
|
+
autoload :ClassBuilder, "verifly/class_builder"
|
12
|
+
autoload :DependentCallbacks, "verifly/dependent_callbacks"
|
13
|
+
autoload :HasLogger, "verifly/has_logger"
|
14
|
+
autoload :Verifier, "verifly/verifier"
|
13
15
|
end
|
data/lib/verifly/applicator.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Verifly
|
4
|
-
# @abstract implement `#call`
|
4
|
+
# @abstract implement `#call`, `#source` and `#source_location`
|
5
5
|
# Applies "applicable" objects to given bindings
|
6
6
|
# (applicable objects are named based on their use,
|
7
7
|
# currently any object is applicable).
|
@@ -14,8 +14,8 @@ module Verifly
|
|
14
14
|
# Proxy is used when applicable itself is an instance of Applicator.
|
15
15
|
# It just delegates #call method to applicable
|
16
16
|
# @example
|
17
|
-
# Applicator.call(Applicator.build(:foo), binding_, context)
|
18
|
-
# # => Applicator.call(:foo, binding_, context)
|
17
|
+
# Applicator.call(Applicator.build(:foo), binding_, *context)
|
18
|
+
# # => Applicator.call(:foo, binding_, *context)
|
19
19
|
class Proxy < self
|
20
20
|
# @param applicable [Applicator]
|
21
21
|
# @return Proxy if applicable is an instance of Applicator
|
@@ -27,8 +27,20 @@ module Verifly
|
|
27
27
|
# @param binding_ [#instance_exec] target to apply applicable
|
28
28
|
# @param context additional info to send it to applicable
|
29
29
|
# @return application result
|
30
|
-
def call(binding_, context)
|
31
|
-
applicable.call(binding_, context)
|
30
|
+
def call(binding_, *context)
|
31
|
+
applicable.call(binding_, *context)
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param binding_ [#instance_exec] binding to find relative source
|
35
|
+
# @return [[String, Integer]?] (file, line) location of calblack source (if exists)
|
36
|
+
def source_location(binding_)
|
37
|
+
applicable.source_location(binding_)
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param binding_ [#instance_exec] binding to find relative source
|
41
|
+
# @return [String?] callback (not it's defenition) source code
|
42
|
+
def source(binding_)
|
43
|
+
applicable.source(binding_)
|
32
44
|
end
|
33
45
|
end
|
34
46
|
|
@@ -51,25 +63,45 @@ module Verifly
|
|
51
63
|
# @param binding_ [#instance_exec] target to apply applicable to
|
52
64
|
# @param context additional info to send to applicable
|
53
65
|
# @return application result
|
54
|
-
def call(binding_, context)
|
66
|
+
def call(binding_, *context)
|
55
67
|
if binding_.is_a?(Binding)
|
56
|
-
call_on_binding(binding_, context)
|
68
|
+
call_on_binding(binding_, *context)
|
57
69
|
else
|
58
|
-
invoke_lambda(binding_.method(applicable), binding_, context)
|
70
|
+
invoke_lambda(binding_.method(applicable), binding_, *context)
|
59
71
|
end
|
60
72
|
end
|
61
73
|
|
74
|
+
# @param binding_ [#instance_exec] binding to find relative source
|
75
|
+
# @return [[String, Integer]] (file, line) location of calblack source (if exists)
|
76
|
+
# @raise [NameError] if method does not exist on binding_
|
77
|
+
def source_location(binding_)
|
78
|
+
binding_method(binding_).method(applicable).source_location
|
79
|
+
end
|
80
|
+
|
81
|
+
# @param binding_ [#instance_exec] binding to find relative source
|
82
|
+
# @return [String] relative method source defenition
|
83
|
+
# @raise [NameError] if method does not exist on binding_
|
84
|
+
def source(binding_)
|
85
|
+
binding_method(binding_).method(applicable).source
|
86
|
+
end
|
87
|
+
|
88
|
+
# @param binding_ [#instance_exec] binding to find relative source
|
89
|
+
# @return [Method] method, extracted from binding
|
90
|
+
def binding_method(binding_)
|
91
|
+
binding_.is_a?(Binding) ? binding_.receiver : binding_
|
92
|
+
end
|
93
|
+
|
62
94
|
private
|
63
95
|
|
64
96
|
# When Binding is a target, we have to respect both methods and variables
|
65
97
|
# @param binding_ [Binding] target to apply applicable to
|
66
98
|
# @param context additional info to send to applicable
|
67
99
|
# @return application result
|
68
|
-
def call_on_binding(binding_, context)
|
69
|
-
if binding_.
|
70
|
-
invoke_lambda(binding_.receiver.method(applicable), binding_, context)
|
71
|
-
else
|
100
|
+
def call_on_binding(binding_, *context)
|
101
|
+
if binding_.local_variable_defined?(applicable)
|
72
102
|
binding_.local_variable_get(applicable)
|
103
|
+
else
|
104
|
+
invoke_lambda(binding_.receiver.method(applicable), binding_, *context)
|
73
105
|
end
|
74
106
|
end
|
75
107
|
end
|
@@ -77,7 +109,7 @@ module Verifly
|
|
77
109
|
# InstanceEvaluator is used for strings. It works like instance_eval or
|
78
110
|
# Binding#eval depending on binding_ class
|
79
111
|
# @example
|
80
|
-
# Applicator.call('foo if context[:foo]', binding_, context)
|
112
|
+
# Applicator.call('foo if context[:foo]', binding_, *context)
|
81
113
|
# # => foo if context[:foo]
|
82
114
|
class InstanceEvaluator < self
|
83
115
|
# @param applicable [String]
|
@@ -90,7 +122,7 @@ module Verifly
|
|
90
122
|
# @param binding_ [#instance_exec] target to apply applicable to
|
91
123
|
# @param context additional info to send to applicable
|
92
124
|
# @return application result
|
93
|
-
def call(binding_, context)
|
125
|
+
def call(binding_, *context)
|
94
126
|
if binding_.is_a?(Binding)
|
95
127
|
binding_ = binding_.dup
|
96
128
|
binding_.local_variable_set(:context, context)
|
@@ -100,6 +132,17 @@ module Verifly
|
|
100
132
|
end
|
101
133
|
end
|
102
134
|
|
135
|
+
# Source location is not available
|
136
|
+
# @return [nil]
|
137
|
+
def source_location(*); end
|
138
|
+
|
139
|
+
# @return [String] exactly it's defenition
|
140
|
+
def source(*)
|
141
|
+
applicable
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
103
146
|
# @return [String, Integer]
|
104
147
|
# file and line where `Applicator.call` was called
|
105
148
|
def caller_line
|
@@ -113,7 +156,7 @@ module Verifly
|
|
113
156
|
# ProcApplicatior is used when #to_proc is available.
|
114
157
|
# It works not only with procs, but also with hashes etc
|
115
158
|
# @example with a proc
|
116
|
-
# Applicator.call(-> { foo }, binding_, context) # => foo
|
159
|
+
# Applicator.call(-> { foo }, binding_, *context) # => foo
|
117
160
|
# @example with a hash
|
118
161
|
# Applicator.call(Hash[foo: true], binding_, :foo) # => true
|
119
162
|
# Applicator.call(Hash[foo: true], binding_, :bar) # => nil
|
@@ -128,19 +171,38 @@ module Verifly
|
|
128
171
|
# @param binding_ [#instance_exec] target to apply applicable to
|
129
172
|
# @param context additional info to send to applicable
|
130
173
|
# @return application result
|
131
|
-
def call(binding_, context)
|
132
|
-
invoke_lambda(applicable.to_proc, binding_, context)
|
174
|
+
def call(binding_, *context)
|
175
|
+
invoke_lambda(applicable.to_proc, binding_, *context)
|
176
|
+
end
|
177
|
+
|
178
|
+
# @return [String, Integer] Proc#source_location
|
179
|
+
def source_location(*)
|
180
|
+
applicable.to_proc.source_location
|
181
|
+
end
|
182
|
+
|
183
|
+
# @return [String] Proc#source
|
184
|
+
def source(*)
|
185
|
+
applicable.to_proc.source
|
133
186
|
end
|
134
187
|
end
|
135
188
|
|
136
189
|
# Quoter is used when there is no other way to apply applicatable.
|
137
190
|
# @example
|
138
|
-
# Applicator.call(true, binding_, context) # => true
|
191
|
+
# Applicator.call(true, binding_, *context) # => true
|
139
192
|
class Quoter < self
|
140
193
|
# @return applicable without changes
|
141
194
|
def call(*)
|
142
195
|
applicable
|
143
196
|
end
|
197
|
+
|
198
|
+
# No source location available
|
199
|
+
# @return [nil]
|
200
|
+
def source_location(*); end
|
201
|
+
|
202
|
+
# @return [String] quoted value's inspect
|
203
|
+
def source
|
204
|
+
applicable.inpsect
|
205
|
+
end
|
144
206
|
end
|
145
207
|
|
146
208
|
extend ClassBuilder::Mixin
|
@@ -151,7 +213,6 @@ module Verifly
|
|
151
213
|
attr_accessor :applicable
|
152
214
|
|
153
215
|
# Applies applicable on binding_ with context
|
154
|
-
# @todo add @see #initialize when its todo is done
|
155
216
|
# @param applicable [applicable]
|
156
217
|
# see examples in definitions of subclasses
|
157
218
|
# @param binding_ [#instance_exec]
|
@@ -161,8 +222,8 @@ module Verifly
|
|
161
222
|
# geneneric data you want to pass to applicable function.
|
162
223
|
# If applicable cannot accept params, context will not be sent
|
163
224
|
# @return application result
|
164
|
-
def self.call(applicable, binding_, context)
|
165
|
-
build(applicable).call(binding_, context)
|
225
|
+
def self.call(applicable, binding_, *context)
|
226
|
+
build(applicable).call(binding_, *context)
|
166
227
|
end
|
167
228
|
|
168
229
|
# Always use build instead of new
|
@@ -180,13 +241,23 @@ module Verifly
|
|
180
241
|
applicable == other.applicable
|
181
242
|
end
|
182
243
|
|
183
|
-
# @!method call(binding_, context)
|
244
|
+
# @!method call(binding_, *context)
|
184
245
|
# @abstract
|
185
246
|
# Applies applicable on binding_ with context
|
186
247
|
# @param binding_ [#instance_exec] binding to be used for applying
|
187
248
|
# @param context param that will be passed if requested
|
188
249
|
# @return application result
|
189
250
|
|
251
|
+
# @!method source_location(binding_)
|
252
|
+
# @abstract
|
253
|
+
# @param binding_ [#instance_exec] binding to find relative source
|
254
|
+
# @return [[String, Integer]?] (file, line) location of calblack source (if exists)
|
255
|
+
|
256
|
+
# @!method source(binding_)
|
257
|
+
# @abstract
|
258
|
+
# @param binding_ [#instance_exec] binding to find relative source
|
259
|
+
# @return [String?] callback (not it's defenition) source code
|
260
|
+
|
190
261
|
private
|
191
262
|
|
192
263
|
# invokes lambda respecting its arity
|
@@ -194,11 +265,11 @@ module Verifly
|
|
194
265
|
# @param binding_ [#instance_exec] binding_ would be used in application
|
195
266
|
# @param context param would be passed if lambda arity > 0
|
196
267
|
# @return invocation result
|
197
|
-
def invoke_lambda(lambda, binding_, context)
|
268
|
+
def invoke_lambda(lambda, binding_, *context)
|
198
269
|
if lambda.arity.zero?
|
199
270
|
binding_.instance_exec(&lambda)
|
200
271
|
else
|
201
|
-
binding_.instance_exec(context, &lambda)
|
272
|
+
binding_.instance_exec(*context, &lambda)
|
202
273
|
end
|
203
274
|
end
|
204
275
|
end
|
@@ -24,9 +24,7 @@ module Verifly
|
|
24
24
|
# @raise [ArgumentError] if there is more than two arguments and block
|
25
25
|
# @raise [ArgumentError] if there is zero arguments and no block
|
26
26
|
def initialize(*args, &block)
|
27
|
-
action, options
|
28
|
-
options ||= {}
|
29
|
-
raise ArgumentError unless action && rest.empty?
|
27
|
+
action, options = normalize_options(*args, &block)
|
30
28
|
|
31
29
|
self.action = Applicator.build(action)
|
32
30
|
self.if_condition = Applicator.build(options.fetch(:if, true))
|
@@ -41,10 +39,20 @@ module Verifly
|
|
41
39
|
# generic context to apply (see Applicator)
|
42
40
|
# @return main action application result
|
43
41
|
# @return [nil] if condition checks failed
|
44
|
-
def call(binding_, context)
|
45
|
-
return unless if_condition.call(binding_, context)
|
46
|
-
return if unless_condition.call(binding_, context)
|
47
|
-
action.call(binding_, context)
|
42
|
+
def call(binding_, *context)
|
43
|
+
return unless if_condition.call(binding_, *context)
|
44
|
+
return if unless_condition.call(binding_, *context)
|
45
|
+
action.call(binding_, *context)
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def normalize_options(*args, &block)
|
51
|
+
action, options, *rest = block ? [block, *args] : args
|
52
|
+
options ||= {}
|
53
|
+
raise ArgumentError unless action && rest.empty?
|
54
|
+
|
55
|
+
[action, options]
|
48
56
|
end
|
49
57
|
end
|
50
58
|
end
|
@@ -4,18 +4,18 @@ module Verifly
|
|
4
4
|
# ClassBuilder is similar to Uber::Builder, but it
|
5
5
|
# allows child classes to decide whether they will be used.
|
6
6
|
# I find it much more object-oriented
|
7
|
-
# @attr klasses [
|
7
|
+
# @attr klasses [[Class]]
|
8
8
|
# classes to iterate during search of most suitable
|
9
9
|
class ClassBuilder
|
10
10
|
# Mixin provides useful methods to integrate into builder subsystem.
|
11
11
|
# Feel free to override or just never include it.
|
12
|
-
# @attr_writer [
|
12
|
+
# @attr_writer [[Class]] buildable_classes
|
13
13
|
# Array of classes which will be checked if they
|
14
14
|
# suite constructor arguments. Order matters
|
15
15
|
module Mixin
|
16
16
|
# Array of classes which will be checked if they
|
17
17
|
# suite constructor arguments. Order matters
|
18
|
-
# @param klasses [
|
18
|
+
# @param klasses [[Class]]
|
19
19
|
def buildable_classes=(klasses)
|
20
20
|
@class_builder = ClassBuilder.new(klasses).freeze
|
21
21
|
end
|
@@ -40,7 +40,7 @@ module Verifly
|
|
40
40
|
|
41
41
|
attr_accessor :klasses
|
42
42
|
|
43
|
-
# @param klasses [
|
43
|
+
# @param klasses [[Class]]
|
44
44
|
def initialize(klasses)
|
45
45
|
self.klasses = klasses
|
46
46
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Verifly
|
4
|
+
# DependentCallbacks interface is similar to ActiveSupport::Callbacks, but it has few differences
|
5
|
+
#
|
6
|
+
# 1) `extend` it, not `include`
|
7
|
+
#
|
8
|
+
# 2) Use `.callback_groups` do tefine callbacks instead of define_callbacks
|
9
|
+
#
|
10
|
+
# 3) Better define callbacks in separate module, wich should extend DependentCallbacks::Storage
|
11
|
+
#
|
12
|
+
# 4) Use merge_callbacks_from(Module) instead of including it
|
13
|
+
#
|
14
|
+
# 5) There is no run_callbacks method.
|
15
|
+
#
|
16
|
+
# You can either use self.class.dependent_callbacks.invoke(group, *context) {}
|
17
|
+
# or use .export_callbacks_to(:active_support) / .export_callbacks_to(:wrap_method)
|
18
|
+
module DependentCallbacks
|
19
|
+
autoload :Callback, "verifly/dependent_callbacks/callback"
|
20
|
+
autoload :CallbackGroup, "verifly/dependent_callbacks/callback_group"
|
21
|
+
autoload :Invoker, "verifly/dependent_callbacks/invoker"
|
22
|
+
autoload :Service, "verifly/dependent_callbacks/service"
|
23
|
+
autoload :Storage, "verifly/dependent_callbacks/storage"
|
24
|
+
|
25
|
+
extend HasLogger
|
26
|
+
include Storage
|
27
|
+
|
28
|
+
# @api stdlib
|
29
|
+
# Allows children to inherit callbacks from parent
|
30
|
+
# @param [Class] child
|
31
|
+
def inherited(child)
|
32
|
+
super
|
33
|
+
|
34
|
+
child.instance_exec(dependent_callbacks_service) do |service|
|
35
|
+
@dependent_callbacks_service = Service.new(service)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Exports callbacks to another callback system / something like that
|
40
|
+
# @param [:active_support, :wrap_method] target
|
41
|
+
# Target selection.
|
42
|
+
# * :active_support exports each group to correspoding
|
43
|
+
# ActiveSupport callback (via set_callback)
|
44
|
+
# * :wrap_method defines / redefines methods, named same as each group.
|
45
|
+
# If method was defined, on it's call callbacks would run around its previous defenition.
|
46
|
+
# If not, callbacks would run around nothing
|
47
|
+
# @param groups [[Symbol]] arra of groups to export. Defaults to all groups
|
48
|
+
def export_callbacks_to(target, groups: nil)
|
49
|
+
(groups || dependent_callbacks_service.group_names).each do |group|
|
50
|
+
case target
|
51
|
+
when :active_support then _export_callback_group_to_active_support(group)
|
52
|
+
when :action_controller then _export_callback_group_to_action_controller(group)
|
53
|
+
when :wrap_method then _export_callback_group_to_method_wapper(group)
|
54
|
+
else
|
55
|
+
raise "#{target.inspect} export target unavailable. " \
|
56
|
+
"available targets are :active_support, :action_controller, :wrap_method"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Exports callbacks to ActiveSupport
|
64
|
+
# @see export_callbacks_to
|
65
|
+
# @param group [Symbol] name of group
|
66
|
+
def _export_callback_group_to_active_support(group)
|
67
|
+
exports_name = :"__verifly_dependent_callbacks_exports_#{group}"
|
68
|
+
|
69
|
+
define_method(exports_name) do |*context, &block|
|
70
|
+
self.class.dependent_callbacks_service.invoke(group) do |invoker|
|
71
|
+
invoker.context = context
|
72
|
+
invoker.inner_block = block
|
73
|
+
invoker.run(self)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
private(exports_name)
|
78
|
+
set_callback(group, :around, exports_name)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Exports callbacks to ActionController::Base
|
82
|
+
# @see export_callbacks_to
|
83
|
+
# @param group [Symbol] name of group
|
84
|
+
def _export_callback_group_to_action_controller(group)
|
85
|
+
raise unless group == :action
|
86
|
+
|
87
|
+
around_action do |request, sequence|
|
88
|
+
self.class.dependent_callbacks_service.invoke(group) do |invoker|
|
89
|
+
invoker.context << request
|
90
|
+
invoker.inner_block = sequence
|
91
|
+
invoker.break_if { response_body }
|
92
|
+
invoker.run(self)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Exports callbacks to methods
|
98
|
+
# @see export_callbacks_to
|
99
|
+
# @param group [Symbol] name of group
|
100
|
+
def _export_callback_group_to_method_wapper(group)
|
101
|
+
instance_method = instance_method(group) rescue nil
|
102
|
+
|
103
|
+
define_method(group) do |*args, &block|
|
104
|
+
self.class.dependent_callbacks_service.invoke(group) do |invoker|
|
105
|
+
invoker.around { instance_method&.bind(self)&.call(*args, &block) }
|
106
|
+
invoker.context = args
|
107
|
+
invoker.run(self)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Verifly
|
4
|
+
module DependentCallbacks
|
5
|
+
# ApplicatorWithOptions improved to handle everything needed in DependentCallbacks
|
6
|
+
# @attr name [Symbol?] callback name
|
7
|
+
# @attr position [:before, :after, :around] callback position
|
8
|
+
# @attr before [[Symbol]] names of calblacks before which this is
|
9
|
+
# @attr after [[Symbol]] names of calblacks after which this is
|
10
|
+
class Callback < ApplicatorWithOptions
|
11
|
+
# Available positions of calblack: before, after or around action
|
12
|
+
POSITIONS = %i[before after around].freeze
|
13
|
+
|
14
|
+
attr_accessor :name, :position, :before, :after
|
15
|
+
|
16
|
+
# @!method initialize(position, action = block, options = {}, &block)
|
17
|
+
# @see ApplicatorWithOptions#initialize
|
18
|
+
# @param position [:before, :after, :around] position
|
19
|
+
# @param action [applicable] main action
|
20
|
+
# @option options [applicable] :if
|
21
|
+
# main action is only applied if this evaluates to truthy value
|
22
|
+
# @option options [applicable] :unless
|
23
|
+
# main action is only applied if this evaluates to falsey value
|
24
|
+
# @option options [Symbol] :name
|
25
|
+
# name override for callback. By default, name is taken from applicable if it is a symbol
|
26
|
+
# or set to nil. This option allows to use named applicables like proc
|
27
|
+
# @option options [[Symbol]] :insert_before
|
28
|
+
# array of callback names which should be sequenced after current.
|
29
|
+
# Note, that if position == :after, sequence would go backwards
|
30
|
+
# @option options [[Symbol]] :require
|
31
|
+
# array of callback names which should be sequenced before current.
|
32
|
+
# @raise [ArgumentError] if there is more than three arguments and block
|
33
|
+
# @raise [ArgumentError] if there is one argument and no block
|
34
|
+
def initialize(position, *args, &block)
|
35
|
+
super(*args, &block)
|
36
|
+
|
37
|
+
action, options = normalize_options(*args, &block)
|
38
|
+
|
39
|
+
self.name = options.fetch(:name) { action if action.is_a?(Symbol) }
|
40
|
+
|
41
|
+
self.position = position
|
42
|
+
raise "#{position} should be one of #{POSITIONS}" unless POSITIONS.include?(position)
|
43
|
+
|
44
|
+
self.before = Array(options.fetch(:insert_before, []))
|
45
|
+
self.after = Array(options.fetch(:require, []))
|
46
|
+
end
|
47
|
+
|
48
|
+
# Converts callback to nice table in dot label format
|
49
|
+
# @param [#instance_exec] binding_
|
50
|
+
# @return [String] graphviz LabelHTML
|
51
|
+
def to_dot_label(binding_)
|
52
|
+
template_path = File.expand_path("callback.dothtml.erb", __dir__)
|
53
|
+
erb = ERB.new(File.read(template_path))
|
54
|
+
erb.filename = template_path
|
55
|
+
erb.result(binding)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "tsort"
|
4
|
+
|
5
|
+
module Verifly
|
6
|
+
module DependentCallbacks
|
7
|
+
# Handles callbacks with same 'group' option, allowing to do sequential invokation of them
|
8
|
+
# @attr name [Symbol] name of callback group
|
9
|
+
# @attr index [{Symbol => Callback}] index for named callback lookup
|
10
|
+
# @attr list [[Callback]] all callbacks
|
11
|
+
class CallbackGroup
|
12
|
+
# Implements topoplogy sorting of callbacks.
|
13
|
+
# As far as CallbackGroup is designed to store Callbacks, it is unable to link them into
|
14
|
+
# graph immediately . Service can do it, because if some callbacks are missing on
|
15
|
+
# compilation stage, there should be an error
|
16
|
+
# @see http://ruby-doc.org/stdlib-2.3.4/libdoc/tsort/rdoc/TSort.html
|
17
|
+
# @attr dependencies [{ Callback => Callback }] dependency graph
|
18
|
+
class TSortService
|
19
|
+
include TSort
|
20
|
+
|
21
|
+
attr_accessor :dependencies
|
22
|
+
|
23
|
+
# @param callback_group [CallbackGroup] group to be tsorted
|
24
|
+
# @return [[Callback]] tsorted callbacks array (aka sequence)
|
25
|
+
def self.call(callback_group)
|
26
|
+
new(callback_group).tsort
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param callback_group [CallbackGroup] group to be tsorted
|
30
|
+
def initialize(callback_group)
|
31
|
+
self.dependencies = Hash.new { |h, k| h[k] = Set[] }
|
32
|
+
|
33
|
+
callback_group.list.each do |callback|
|
34
|
+
dependencies[callback] ||= []
|
35
|
+
|
36
|
+
callback.before.each do |key|
|
37
|
+
dependencies[callback_group.index.fetch(key)] << callback
|
38
|
+
end
|
39
|
+
|
40
|
+
callback.after.each do |key|
|
41
|
+
dependencies[callback] << callback_group.index.fetch(key)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# @api stdlib
|
49
|
+
# @see TSort
|
50
|
+
def tsort_each_node(&block)
|
51
|
+
dependencies.keys.each(&block)
|
52
|
+
end
|
53
|
+
|
54
|
+
# @api stdlib
|
55
|
+
# @see TSort
|
56
|
+
def tsort_each_child(node, &block)
|
57
|
+
dependencies[node].each(&block)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
attr_accessor :index, :list, :name
|
62
|
+
|
63
|
+
# @param name [Symbol] name of callback group
|
64
|
+
# @yield self if block given
|
65
|
+
def initialize(name)
|
66
|
+
self.name = name
|
67
|
+
self.index = {}
|
68
|
+
self.list = []
|
69
|
+
|
70
|
+
yield(self) if block_given?
|
71
|
+
end
|
72
|
+
|
73
|
+
# Adds callback to list and index, reset sequence
|
74
|
+
# @param callback [Callback] new callback
|
75
|
+
def add_callback(callback)
|
76
|
+
list << callback
|
77
|
+
index[callback.name] = callback if callback.name
|
78
|
+
|
79
|
+
@sequence = nil
|
80
|
+
end
|
81
|
+
|
82
|
+
# Merges with another group
|
83
|
+
# @param other [CallbackGroup]
|
84
|
+
# @raise if group names differ
|
85
|
+
def merge(other)
|
86
|
+
raise "Only groups with one name could be merged" unless name == other.name
|
87
|
+
|
88
|
+
[*list, *other.list].each_with_object(CallbackGroup.new(name)) do |callback, group|
|
89
|
+
group.add_callback(callback)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Memoizes tsorted graph
|
94
|
+
# @return [[Callback]]
|
95
|
+
def sequence
|
96
|
+
@sequence ||= TSortService.call(self)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Digest change forces recompilation of callback group in service
|
100
|
+
# @return [Numeric]
|
101
|
+
def digest
|
102
|
+
[name, list].hash
|
103
|
+
end
|
104
|
+
|
105
|
+
# Renders graphviz dot-representation of callback group
|
106
|
+
# @return graphviz dot
|
107
|
+
def to_dot(binding_)
|
108
|
+
template_path = File.expand_path("callback_group.dot.erb", __dir__)
|
109
|
+
erb = ERB.new(File.read(template_path))
|
110
|
+
erb.filename = template_path
|
111
|
+
erb.result(binding)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "benchmark"
|
4
|
+
|
5
|
+
module Verifly
|
6
|
+
module DependentCallbacks
|
7
|
+
# Simple service to invoke callback groups
|
8
|
+
# @example simple invokation
|
9
|
+
# def invoke_callbacks
|
10
|
+
# Invoker.new(group) do |invoker|
|
11
|
+
# invoker.around {}
|
12
|
+
# invoker.run(self)
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
# @see DependentCallbacks#export_callbacks_to
|
16
|
+
# @!attribute flat_sequence
|
17
|
+
# @return [[Callback]] tsorted callbacks
|
18
|
+
# @!attribute context
|
19
|
+
# @return [[Object]] invokation context. It would be passed to all applicators
|
20
|
+
# @!attribute inner_block
|
21
|
+
# @return [Proc] block in the middle of middleware
|
22
|
+
# @!visibility private
|
23
|
+
# @!attribute break_if_proc
|
24
|
+
# @note does not affect 'after' callbacks
|
25
|
+
# @return [Proc] if this block evaluate to truthy, sequence would be halted.
|
26
|
+
# @!attribute binding_
|
27
|
+
# @return [#instance_exec] binding_ to evaluate on
|
28
|
+
class Invoker
|
29
|
+
attr_accessor :flat_sequence, :context, :inner_block
|
30
|
+
|
31
|
+
# @param callback_group [CallbackGroup]
|
32
|
+
# @yield self if block given
|
33
|
+
def initialize(callback_group)
|
34
|
+
self.flat_sequence = callback_group.sequence
|
35
|
+
self.context = []
|
36
|
+
self.break_if_proc = proc { false }
|
37
|
+
yield(self) if block_given?
|
38
|
+
end
|
39
|
+
|
40
|
+
# @yield in the middle of middleware, setting inner_block attribute
|
41
|
+
# @see inner_block
|
42
|
+
def around(&block)
|
43
|
+
self.inner_block = block
|
44
|
+
end
|
45
|
+
|
46
|
+
# @yield between callbacks halting chain if evaluated to true
|
47
|
+
# @see break_if_proc
|
48
|
+
def break_if(&block)
|
49
|
+
self.break_if_proc = block
|
50
|
+
end
|
51
|
+
|
52
|
+
# Sets binding_, reduces callbacks into big proc and evaluates it
|
53
|
+
# @param binding_ [#instance_exec] binding_ to be evaluated on
|
54
|
+
# @return inner_block call result
|
55
|
+
def run(binding_)
|
56
|
+
self.binding_ = binding_
|
57
|
+
result = nil
|
58
|
+
block_with_result_extractor = -> { result = inner_block&.call }
|
59
|
+
|
60
|
+
log!(:info, "Started chain processing")
|
61
|
+
|
62
|
+
sequence =
|
63
|
+
flat_sequence.reverse_each.reduce(block_with_result_extractor) do |sequence, callback|
|
64
|
+
-> { call_callback(callback, sequence) }
|
65
|
+
end
|
66
|
+
|
67
|
+
sequence.call
|
68
|
+
result
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
attr_accessor :break_if_proc, :binding_
|
74
|
+
|
75
|
+
# Invokes callback in context of invoker
|
76
|
+
# @param callback [Callback] current callbacks
|
77
|
+
# @param sequence [Proc] already built sequence of callbacks
|
78
|
+
def call_callback(callback, sequence)
|
79
|
+
log!(:debug, "Invokation", callback: callback)
|
80
|
+
|
81
|
+
case callback.position
|
82
|
+
when :before then call_callback_before(callback, sequence)
|
83
|
+
when :after then call_callback_after(callback, sequence)
|
84
|
+
when :around then call_callback_around(callback, sequence)
|
85
|
+
end
|
86
|
+
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
|
90
|
+
# Invokes before_<name> callbacks
|
91
|
+
# @param callback [Callback] current callbacks
|
92
|
+
# @param sequence [Proc] already built sequence of callbacks
|
93
|
+
def call_callback_before(callback, sequence)
|
94
|
+
call_with_time_report!(callback, binding_, *context)
|
95
|
+
|
96
|
+
if break_if_proc.call(*context)
|
97
|
+
log!(:warn, "Chain halted", callback: callback)
|
98
|
+
else
|
99
|
+
sequence.call
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Invokes after_<name> callbacks
|
104
|
+
# @param callback [Callback] current callbacks
|
105
|
+
# @param sequence [Proc] already built sequence of callbacks
|
106
|
+
def call_callback_after(callback, sequence)
|
107
|
+
sequence.call
|
108
|
+
call_with_time_report!(callback, binding_, *context)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Invokes around_<name> callbacks
|
112
|
+
# @param callback [Callback] current callbacks
|
113
|
+
# @param sequence [Proc] already built sequence of callbacks
|
114
|
+
def call_callback_around(callback, sequence)
|
115
|
+
inner_executed = false
|
116
|
+
inner = lambda do
|
117
|
+
inner_executed = true
|
118
|
+
if break_if_proc.call(*context)
|
119
|
+
log!(:warn, "Chain halted", callback: callback)
|
120
|
+
else
|
121
|
+
sequence.call
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
call_with_time_report!(callback, binding_, inner, *context)
|
126
|
+
|
127
|
+
unless inner_executed
|
128
|
+
log!(:warn, "Chain halted (sequential block not called)", callback: callback)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Logger interface to decorate messages. Uses DependentCallbacks.logger
|
133
|
+
# @param severity [:debug, :info, :warn, :error, :fatal] severity level
|
134
|
+
# @param message [String] message
|
135
|
+
# @param callback [Callback?] callback to get extra context
|
136
|
+
def log!(severity, message, callback: nil)
|
137
|
+
DependentCallbacks.logger.public_send(severity, "Verifly::DependentCallbacks::Invoker") do
|
138
|
+
if callback
|
139
|
+
<<~TXT.squish if callback
|
140
|
+
#{message} callback #{callback.name || "(anonymous)"}
|
141
|
+
in #{callback.action.source_location(binding_)&.join(':')}
|
142
|
+
TXT
|
143
|
+
else
|
144
|
+
message
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def call_with_time_report!(callback, *args) # :nodoc:
|
150
|
+
return callback.call(*args) unless DependentCallbacks.logger.info?
|
151
|
+
time_in_ms = Benchmark.realtime { callback.call(*args) } * 1000
|
152
|
+
log!(:info, "Run in #{time_in_ms.round(1)}ms", callback: callback)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Verifly
|
4
|
+
module DependentCallbacks
|
5
|
+
# A service to store all callbacks info and delegate methods fom DSLs
|
6
|
+
# @attr groups [Symbol => CallbackGroup] groups index
|
7
|
+
# @attr parents [[Service]] parents in service inheritance system
|
8
|
+
class Service
|
9
|
+
attr_accessor :groups, :parents
|
10
|
+
|
11
|
+
# @param parents [[Service]] is filled in by the parent
|
12
|
+
def initialize(*parents)
|
13
|
+
self.parents = parents
|
14
|
+
self.groups = Hash.new { |h, k| h[k] = CallbackGroup.new(k) }
|
15
|
+
end
|
16
|
+
|
17
|
+
# Merges another service into this
|
18
|
+
# @param other [Service]
|
19
|
+
def merge!(other)
|
20
|
+
parents << other
|
21
|
+
end
|
22
|
+
|
23
|
+
# Adds callback into matching group
|
24
|
+
# @see Callback#initialize
|
25
|
+
# @param position [:before, :after, :around]
|
26
|
+
# @param group [Symbol] group name
|
27
|
+
# @param args callback args
|
28
|
+
def add_callback(position, group, *args, &block)
|
29
|
+
groups[group].add_callback(Callback.new(position, *args, &block))
|
30
|
+
end
|
31
|
+
|
32
|
+
def invoke(group_name)
|
33
|
+
invoker = Invoker.new(compiled_group(group_name))
|
34
|
+
yield(invoker)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [[Symbol]] names of all groups stored inside itself or parents
|
38
|
+
def group_names
|
39
|
+
[groups.keys, *parents.map(&:group_names)].flatten.uniq
|
40
|
+
end
|
41
|
+
|
42
|
+
# Compiles callback group from itself and parents callback groups.
|
43
|
+
# If nothing changed, cached value taken
|
44
|
+
# @param group_name [Symbol] group name
|
45
|
+
# @return [CallbackGroup] callback group joined from all relative callback groups
|
46
|
+
def compiled_group(group_name)
|
47
|
+
@compiled_groups_cache ||= Hash.new { |h, k| h[k] = {} }
|
48
|
+
cache_entry = @compiled_groups_cache[group_name]
|
49
|
+
return cache_entry[:group] if cache_entry[:digest] == digest
|
50
|
+
|
51
|
+
cache_entry[:digest] = digest
|
52
|
+
cache_entry[:group] = parents.map { |parent| parent.compiled_group(group_name) }
|
53
|
+
.reduce(groups[group_name], &:merge)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Digest change forces recompilation of compiled_group
|
57
|
+
# @return [Numeric]
|
58
|
+
def digest
|
59
|
+
[
|
60
|
+
*parents.map(&:digest),
|
61
|
+
*groups.map { |k, v| [k, v.digest].join },
|
62
|
+
].hash
|
63
|
+
end
|
64
|
+
|
65
|
+
# Exprorts selected group to graphiz .dot format
|
66
|
+
def to_dot(group, binding_)
|
67
|
+
compiled_group(group).to_dot(binding_)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Verifly
|
4
|
+
module DependentCallbacks
|
5
|
+
# Subset of DependentCallbacks dsl methods, which could be used in callbacks storage
|
6
|
+
module Storage
|
7
|
+
# Declares callback groups with given names. This creates before_ after_ and around_
|
8
|
+
# signleton methods for each group given
|
9
|
+
# @see Service#add_callback
|
10
|
+
# @param groups [[Symbol]]
|
11
|
+
def callback_groups(*groups)
|
12
|
+
groups.each do |group|
|
13
|
+
dependent_callbacks_service.groups[group] # Creates an empty group
|
14
|
+
|
15
|
+
%i[before after around].each do |position|
|
16
|
+
define_singleton_method("#{position}_#{group}") do |*args, &block|
|
17
|
+
dependent_callbacks_service.add_callback(position, group, *args, &block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Merges all callbacks from given storage
|
24
|
+
# @param storage [Module { extend Storage }]
|
25
|
+
def merge_callbacks_from(storage)
|
26
|
+
include(storage)
|
27
|
+
dependent_callbacks_service.merge!(storage.dependent_callbacks_service)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Service] associated with current Class / Module
|
31
|
+
def dependent_callbacks_service
|
32
|
+
@dependent_callbacks_service ||= Service.new
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
|
5
|
+
module Verifly
|
6
|
+
# Mixin with logger attr_accessor and default value for it
|
7
|
+
# @!attribute logger
|
8
|
+
# @return [::Logger] logger to be used within target module
|
9
|
+
module HasLogger
|
10
|
+
# Does nothing, provides ::Logger api
|
11
|
+
class NullLogger < ::Logger
|
12
|
+
# @api stdlib
|
13
|
+
def initialize
|
14
|
+
super(nil)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @api stdlib
|
18
|
+
# Logs nothing
|
19
|
+
def add(*); end
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_writer :logger
|
23
|
+
|
24
|
+
def logger
|
25
|
+
@logger ||= NullLogger.new
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/verifly/verifier.rb
CHANGED
@@ -11,7 +11,7 @@ module Verifly
|
|
11
11
|
# Array with all messages yielded by the verifier
|
12
12
|
class Verifier
|
13
13
|
autoload :ApplicatorWithOptionsBuilder,
|
14
|
-
|
14
|
+
"verifly/verifier/applicator_with_options_builder"
|
15
15
|
|
16
16
|
attr_accessor :model, :messages
|
17
17
|
|
@@ -65,7 +65,7 @@ module Verifly
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
# @return [
|
68
|
+
# @return [[ApplicatorWithOptions]]
|
69
69
|
# List of applicators, bound by .verify
|
70
70
|
def self.bound_applicators
|
71
71
|
@bound_applicators ||= []
|
data/lib/verifly/version.rb
CHANGED
metadata
CHANGED
@@ -1,157 +1,171 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: verifly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alexander Smirnov
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-10-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '0
|
19
|
+
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '0
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: coveralls
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: launchy
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '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'
|
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'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: rake
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '0
|
75
|
+
version: '0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
|
-
- - "
|
80
|
+
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '0
|
82
|
+
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: rspec
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0
|
89
|
+
version: '0'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0
|
96
|
+
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: rspec-its
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - ">="
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
103
|
+
version: '0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
110
|
+
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: yard
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - "
|
115
|
+
- - ">="
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 0
|
117
|
+
version: '0'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - "
|
122
|
+
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: 0
|
124
|
+
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: actionpack
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- - "
|
129
|
+
- - ">="
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: '
|
131
|
+
version: '0'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- - "
|
136
|
+
- - ">="
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: '
|
138
|
+
version: '0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
140
|
+
name: activesupport
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
|
-
- - "
|
143
|
+
- - ">="
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version: '
|
145
|
+
version: '0'
|
146
146
|
type: :development
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
|
-
- - "
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rubocop-config-umbrellio
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
151
158
|
- !ruby/object:Gem::Version
|
152
|
-
version: '
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
153
167
|
description: 'An api to run sequential checks like ''ActiveModel::Validations'' do,
|
154
|
-
but with generic messages instead of errors. See more info at
|
168
|
+
but with generic messages instead of errors. See more info at https://www.rubydoc.info/gems/verifly/0.3.1.0 '
|
155
169
|
email:
|
156
170
|
- begdory4@gmail.com
|
157
171
|
executables: []
|
@@ -163,6 +177,13 @@ files:
|
|
163
177
|
- lib/verifly/applicator.rb
|
164
178
|
- lib/verifly/applicator_with_options.rb
|
165
179
|
- lib/verifly/class_builder.rb
|
180
|
+
- lib/verifly/dependent_callbacks.rb
|
181
|
+
- lib/verifly/dependent_callbacks/callback.rb
|
182
|
+
- lib/verifly/dependent_callbacks/callback_group.rb
|
183
|
+
- lib/verifly/dependent_callbacks/invoker.rb
|
184
|
+
- lib/verifly/dependent_callbacks/service.rb
|
185
|
+
- lib/verifly/dependent_callbacks/storage.rb
|
186
|
+
- lib/verifly/has_logger.rb
|
166
187
|
- lib/verifly/verifier.rb
|
167
188
|
- lib/verifly/version.rb
|
168
189
|
homepage: https://github.com/umbrellio/verifly
|
@@ -170,24 +191,23 @@ licenses:
|
|
170
191
|
- MIT
|
171
192
|
metadata:
|
172
193
|
yard.run: yri
|
173
|
-
post_install_message:
|
194
|
+
post_install_message:
|
174
195
|
rdoc_options: []
|
175
196
|
require_paths:
|
176
197
|
- lib
|
177
198
|
required_ruby_version: !ruby/object:Gem::Requirement
|
178
199
|
requirements:
|
179
|
-
- - "
|
200
|
+
- - ">="
|
180
201
|
- !ruby/object:Gem::Version
|
181
|
-
version: '2.
|
202
|
+
version: '2.5'
|
182
203
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
204
|
requirements:
|
184
205
|
- - ">="
|
185
206
|
- !ruby/object:Gem::Version
|
186
207
|
version: '0'
|
187
208
|
requirements: []
|
188
|
-
|
189
|
-
|
190
|
-
signing_key:
|
209
|
+
rubygems_version: 3.1.2
|
210
|
+
signing_key:
|
191
211
|
specification_version: 4
|
192
|
-
summary: See more info at
|
212
|
+
summary: See more info at https://www.rubydoc.info/gems/verifly/0.3.1.0
|
193
213
|
test_files: []
|