param_param 0.1.0 → 1.0.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 +4 -4
- data/README.md +32 -37
- data/lib/param_param/actions.rb +57 -0
- data/lib/param_param/std.rb +113 -227
- data/lib/param_param.rb +19 -61
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8ef6579d2ab3739b51f2987d16d8832e5447a669c1b0fdaebefc052a86ce502d
|
4
|
+
data.tar.gz: 331d8721dbdb989c3fa7f01da832d860a34f6207be0a68601651e6ef534685cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f21bfc4dfcd7fb9e3079aa9776665b408e413af23ceef696e9f06891957b0d7f7a37e8ed21449896db5a8a44ef82c9535c0ceb41a5a7ec00c3f88d8c84f6c6fe
|
7
|
+
data.tar.gz: b32827903bd128ea3477162864e39cd83eefebb57d5d08af8d3458b4880e9aab8b2560413a53b6c2d0ea231d40ba40ae0e79b7e03ae90874e59eda5393402bc7
|
data/README.md
CHANGED
@@ -11,72 +11,67 @@ Inspired by Martin Chabot's [Simple Functional Strong Parameters In Ruby](https:
|
|
11
11
|
require 'param_param'
|
12
12
|
require 'param_param/std'
|
13
13
|
|
14
|
-
class
|
14
|
+
class MyParams
|
15
15
|
include ParamParam
|
16
16
|
include ParamParam::Std
|
17
17
|
|
18
18
|
# You can add your own actions
|
19
|
-
|
20
|
-
|
21
|
-
RULES = define.(
|
22
|
-
name: required.(string.(all_of.([not_blank, max_size.(50), capitalized]))),
|
23
|
-
admin: required.(bool.(any)),
|
24
|
-
age: optional.(integer.(gt.(0))),
|
25
|
-
)
|
19
|
+
CAPITALIZED = ->(option) { Success.new(Optiomist.some(option.value.capitalize)) }
|
20
|
+
end
|
26
21
|
|
27
|
-
|
28
|
-
|
29
|
-
|
22
|
+
user_params = MyParams.define do |p|
|
23
|
+
{
|
24
|
+
name: p::REQUIRED.(p::ALL_OF.([p::STRING, p::MIN_SIZE.(1), p::MAX_SIZE.(50), p::CAPITALIZED])),
|
25
|
+
admin: p::REQUIRED.(p::BOOL),
|
26
|
+
age: p::OPTIONAL.(p::ALL_OF.([p::INTEGER, p::GT.(0)])),
|
27
|
+
}
|
30
28
|
end
|
31
29
|
|
32
|
-
params, errors =
|
30
|
+
params, errors = user_params.(
|
33
31
|
name: 'JOHN',
|
34
32
|
admin: '0',
|
35
33
|
age: '30',
|
36
34
|
race: 'It is not important',
|
37
35
|
)
|
36
|
+
|
38
37
|
params # {:name=>"John", :admin=>false, :age=>30}
|
39
38
|
errors # {}
|
40
39
|
|
41
40
|
params, errors = UserParams.new.process(admin: 'no', age: 'very old')
|
42
41
|
params # {:admin=>false}
|
43
|
-
errors # {:name=>:missing, :age=>:
|
42
|
+
errors # {:name=>:missing, :age=>:not_integer}
|
44
43
|
```
|
45
44
|
|
46
45
|
## Perform some chain of operations on provided data.
|
47
46
|
```
|
48
47
|
require 'param_param'
|
49
48
|
|
49
|
+
require 'param_param'
|
50
|
+
|
50
51
|
module Mather
|
51
52
|
include ParamParam
|
52
53
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
def self.mul
|
58
|
-
->(value, option) { Success.new(Optiomist.some(option.value * value)) }.curry
|
59
|
-
end
|
60
|
-
|
61
|
-
def self.sub
|
62
|
-
->(value, option) { Success.new(Optiomist.some(option.value - value)) }.curry
|
63
|
-
end
|
54
|
+
ADD = ->(value, option) { Success.new(Optiomist.some(option.value + value)) }.curry
|
55
|
+
MUL = ->(value, option) { Success.new(Optiomist.some(option.value * value)) }.curry
|
56
|
+
SUB = ->(value, option) { Success.new(Optiomist.some(option.value - value)) }.curry
|
64
57
|
end
|
65
58
|
|
66
|
-
rules = Mather.define
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
)
|
59
|
+
rules = Mather.define do |m|
|
60
|
+
{
|
61
|
+
a: m::ADD.(2),
|
62
|
+
b: m::MUL.(2),
|
63
|
+
c: m::SUB.(2),
|
64
|
+
d: m::ALL_OF.([m::ADD.(2), m::MUL.(2), m::SUB.(2)]),
|
65
|
+
}
|
66
|
+
end
|
72
67
|
|
73
|
-
params,
|
74
|
-
a:
|
75
|
-
b:
|
76
|
-
c:
|
77
|
-
d:
|
68
|
+
params, errors = rules.(
|
69
|
+
a: 10,
|
70
|
+
b: 10,
|
71
|
+
c: 10,
|
72
|
+
d: 10,
|
78
73
|
)
|
79
74
|
|
80
|
-
params # {:a=>
|
81
|
-
|
75
|
+
params # {:a=>12, :b=>20, :c=>8, :d=>22}
|
76
|
+
errors # {}
|
82
77
|
```
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ParamParam
|
4
|
+
# Defines actions to process hash values.
|
5
|
+
#
|
6
|
+
# An action is a lambda function assigned to a key.
|
7
|
+
# When a hash with values is provied, a value related to a key of the same name as the one the action is assigned to, processes the value.
|
8
|
+
#
|
9
|
+
# An action needs to be a lambda taking +Optiomist+ option as parameter and returning either:
|
10
|
+
# - +ParamParam::Success+ with processed option
|
11
|
+
# - +ParamParam::Failure+ with an error
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
# actions = Actions.new(
|
15
|
+
# key: lambda { |option| option.some? ? Success.new(process(option.value)) : Failure.new(:missing) },
|
16
|
+
# ...
|
17
|
+
# )
|
18
|
+
class Actions
|
19
|
+
def initialize(actions)
|
20
|
+
@actions = actions
|
21
|
+
end
|
22
|
+
|
23
|
+
# It takes a hash and processes values related to a key by an action assigned to the same key.
|
24
|
+
#
|
25
|
+
# It returns two hashes:
|
26
|
+
# - if a value related to a key can be procesed by an action,
|
27
|
+
# the result is bound to the key and added to the first params hash
|
28
|
+
# - if a value related to a key can't be processed by an action,
|
29
|
+
# the error is bound to the key and added to the last errors hash
|
30
|
+
def call(params)
|
31
|
+
results = actions.to_h do |key, fn|
|
32
|
+
option = params.key?(key) ? optionize(params[key]) : Optiomist.none
|
33
|
+
[key, fn.call(option)]
|
34
|
+
end
|
35
|
+
|
36
|
+
errors = results.select { |_, result| result.failure? }
|
37
|
+
.transform_values(&:error)
|
38
|
+
params = results.select { |_, result| result.success? && result.value.some? }
|
39
|
+
.transform_values { |result| result.value.value }
|
40
|
+
[params, errors]
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
attr_reader :actions
|
46
|
+
|
47
|
+
# Converts provided value to +Optiomist::Some+.
|
48
|
+
# If the value is already +Optiomist::Some+ or +Optiomist::None+ returns it untouched.
|
49
|
+
def optionize(value)
|
50
|
+
if value.is_a?(Optiomist::Some) || value.is_a?(Optiomist::None)
|
51
|
+
value
|
52
|
+
else
|
53
|
+
Optiomist.some(value)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/param_param/std.rb
CHANGED
@@ -3,244 +3,130 @@
|
|
3
3
|
module ParamParam
|
4
4
|
# It defines some actions that can be useful in an everyday life.
|
5
5
|
module Std
|
6
|
-
def self.included(base)
|
7
|
-
base.include(Messages)
|
8
|
-
base.extend(Actions)
|
9
|
-
end
|
10
|
-
|
11
6
|
# Some string values that can be considered as +true+ (thank you dry-rb for inspiration).
|
12
7
|
TRUE_VALUES = %w[1 on On ON t true True TRUE T y yes Yes YES Y].freeze
|
13
8
|
# Some string values that can be considered as +false+ (thank you dry-rb for inspiration).
|
14
9
|
FALSE_VALUES = %w[0 off Off OFF f false False FALSE F n no No NO N].freeze
|
15
10
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
11
|
+
MISSING_ERR = :missing
|
12
|
+
|
13
|
+
NOT_GTE_ERR = :not_gte
|
14
|
+
NOT_GT_ERR = :not_gt
|
15
|
+
NOT_LTE_ERR = :not_lte
|
16
|
+
NOT_LT_ERR = :not_lt
|
17
|
+
NOT_INCLUDED_ERR = :not_included
|
18
|
+
TOO_LONG_ERR = :too_long
|
19
|
+
TOO_SHORT_ERR = :too_short
|
20
|
+
|
21
|
+
NOT_BOOL_ERR = :not_bool
|
22
|
+
NOT_DECIMAL_ERR = :not_decimal
|
23
|
+
NOT_INTEGER_ERR = :not_integer
|
24
|
+
NOT_STRING_ERR = :not_string
|
25
|
+
|
26
|
+
# Verifies inclusion of a value in a collection.
|
27
|
+
#
|
28
|
+
# Verifies if value of the +option+ is included in the provided +collection+.
|
29
|
+
INCLUDED_IN = lambda do |collection, option|
|
30
|
+
collection.include?(option.value) ? Success.new(option) : Failure.new(NOT_INCLUDED_ERR)
|
31
|
+
end.curry
|
32
|
+
|
33
|
+
# Describes an optional value.
|
34
|
+
#
|
35
|
+
# If +option+ is the +Optiomist::None+ it succeeds causing the parameter not to be included in the final result.
|
36
|
+
# Otherwise executes the funciton +fn+ for the option.
|
37
|
+
OPTIONAL = lambda do |fn, option|
|
38
|
+
case option
|
39
|
+
in Optiomist::None
|
40
|
+
Success.new(option)
|
41
|
+
in Optiomist::Some
|
42
|
+
fn.call(option)
|
43
|
+
end
|
44
|
+
end.curry
|
45
|
+
|
46
|
+
# Describes a required value.
|
47
|
+
#
|
48
|
+
# It fails if +option+ is a +Optiomist::None+. Otherwise executes the funciton +fn+ for the option.
|
49
|
+
REQUIRED = lambda do |fn, option|
|
50
|
+
case option
|
51
|
+
in Optiomist::None
|
52
|
+
Failure.new(MISSING_ERR)
|
53
|
+
in Optiomist::Some
|
54
|
+
fn.call(option)
|
55
|
+
end
|
56
|
+
end.curry
|
57
|
+
|
58
|
+
# Checks if the +option+'s value is greater than or equal to the provided +limit+.
|
59
|
+
GTE = ->(limit, option) { option.value >= limit ? Success.new(option) : Failure.new(NOT_GTE_ERR) }.curry
|
60
|
+
|
61
|
+
# Checks if the +option+'s value is greater than the provided +limit+.
|
62
|
+
GT = ->(limit, option) { option.value > limit ? Success.new(option) : Failure.new(NOT_GT_ERR) }.curry
|
63
|
+
|
64
|
+
# Checks if the +option+'s value is less than or equal to the provided +limit+.
|
65
|
+
LTE = ->(limit, option) { option.value <= limit ? Success.new(option) : Failure.new(NOT_LTE_ERR) }.curry
|
66
|
+
|
67
|
+
# Checks if the +option+'s value is less than the provided +limit+.
|
68
|
+
LT = ->(limit, option) { option.value < limit ? Success.new(option) : Failure.new(NOT_LT_ERR) }.curry
|
69
|
+
|
70
|
+
# Checks if the size of the value in +option+ does not exceed provided +limit+.
|
71
|
+
MAX_SIZE = lambda do |limit, option|
|
72
|
+
option.value.size <= limit ? Success.new(option) : Failure.new(TOO_LONG_ERR)
|
73
|
+
end.curry
|
74
|
+
|
75
|
+
# Checks if the size of the value in +option+ is not lower than the provided +limit+.
|
76
|
+
MIN_SIZE = lambda do |limit, option|
|
77
|
+
option.value.size >= limit ? Success.new(option) : Failure.new(TOO_SHORT_ERR)
|
78
|
+
end.curry
|
79
|
+
|
80
|
+
# Removes leading and trailing spaces from string provided in +option+'s value.
|
81
|
+
STRIPPED = ->(option) { Success.new(Optiomist.some(option.value.strip)) }
|
82
|
+
|
83
|
+
# Converts provided +option+'s value to integer.
|
84
|
+
# If the conversion is not possible it fails.
|
85
|
+
INTEGER = lambda do |option|
|
86
|
+
begin
|
87
|
+
integer_value = Integer(option.value)
|
88
|
+
rescue StandardError
|
89
|
+
return Failure.new(NOT_INTEGER_ERR)
|
90
|
+
end
|
91
|
+
Success.new(Optiomist.some(integer_value))
|
33
92
|
end
|
34
93
|
|
35
|
-
#
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
# Verifies if value of the +option+ is included in the provided +collection+.
|
43
|
-
def included_in
|
44
|
-
lambda { |collection, option|
|
45
|
-
collection.include?(option.value) ? Success.new(option) : Failure.new(Messages::NOT_INCLUDED)
|
46
|
-
}.curry
|
47
|
-
end
|
48
|
-
|
49
|
-
# Describes an optional value.
|
50
|
-
#
|
51
|
-
# Returns
|
52
|
-
# lambda { |fn, option| ... }.
|
53
|
-
#
|
54
|
-
# If +option+ is the +Optiomist::None+ it succeeds causing the parameter not to be included in the final result.
|
55
|
-
# Otherwise executes the funciton +fn+ for the option.
|
56
|
-
def optional
|
57
|
-
lambda { |fn, option|
|
58
|
-
case option
|
59
|
-
in Optiomist::None
|
60
|
-
Success.new(option)
|
61
|
-
in Optiomist::Some
|
62
|
-
fn.call(option)
|
63
|
-
end
|
64
|
-
}.curry
|
65
|
-
end
|
66
|
-
|
67
|
-
# Describes a required value.
|
68
|
-
#
|
69
|
-
# Returns
|
70
|
-
# lambda { |fn, option| ... }.
|
71
|
-
#
|
72
|
-
# If +option+ is a +Optiomist::None+ it fails otherwise executes the funciton +fn+ for the option.
|
73
|
-
def required
|
74
|
-
lambda { |fn, option|
|
75
|
-
case option
|
76
|
-
in Optiomist::None
|
77
|
-
Failure.new(Messages::MISSING)
|
78
|
-
in Optiomist::Some
|
79
|
-
fn.call(option)
|
80
|
-
end
|
81
|
-
}.curry
|
82
|
-
end
|
83
|
-
|
84
|
-
# Converts blank value to nil or passes non blank value to next rule.
|
85
|
-
#
|
86
|
-
# Returns
|
87
|
-
# lambda { |fn, option| ... }.
|
88
|
-
#
|
89
|
-
# If provided +option+'s value is blank it succeeds with +nil+
|
90
|
-
# otherwise executes provided function for the +option+.
|
91
|
-
def blank_to_nil_or
|
92
|
-
lambda { |fn, option|
|
93
|
-
blank?(option.value) ? Success.new(Optiomist.some(nil)) : fn.call(option)
|
94
|
-
}.curry
|
95
|
-
end
|
96
|
-
|
97
|
-
# Verifies if value is not blank.
|
98
|
-
#
|
99
|
-
# Returns
|
100
|
-
# lambda { |option| ... }.
|
101
|
-
#
|
102
|
-
# It fails if provided +option+ is blank, otherwise succeeds with the +option+.
|
103
|
-
def not_blank
|
104
|
-
->(option) { blank?(option.value) ? Failure.new(Messages::BLANK) : Success.new(option) }
|
105
|
-
end
|
106
|
-
|
107
|
-
# Verifies if provided value is nil, empty string or string consisting only from spaces.
|
108
|
-
def blank?(value)
|
109
|
-
value.nil? || (value.is_a?(String) && value.strip.empty?)
|
110
|
-
end
|
111
|
-
|
112
|
-
# Returns
|
113
|
-
# lambda { |limit, option| ... }.
|
114
|
-
#
|
115
|
-
# Checks if the +option+'s value is greater than or equal to the provided +limit+.
|
116
|
-
def gte
|
117
|
-
->(limit, option) { option.value >= limit ? Success.new(option) : Failure.new(Messages::NOT_GTE) }.curry
|
118
|
-
end
|
119
|
-
|
120
|
-
# Returns
|
121
|
-
# lambda { |limit, option| ... }.
|
122
|
-
#
|
123
|
-
# Checks if the +option+'s value is greater than the provided +limit+.
|
124
|
-
def gt
|
125
|
-
->(limit, option) { option.value > limit ? Success.new(option) : Failure.new(Messages::NOT_GT) }.curry
|
126
|
-
end
|
127
|
-
|
128
|
-
# Returns
|
129
|
-
# lambda { |limit, option| ... }.
|
130
|
-
#
|
131
|
-
# Checks if the +option+'s value is less than or equal to the provided +limit+.
|
132
|
-
def lte
|
133
|
-
->(limit, option) { option.value <= limit ? Success.new(option) : Failure.new(Messages::NOT_LTE) }.curry
|
134
|
-
end
|
135
|
-
|
136
|
-
# Returns
|
137
|
-
# lambda { |limit, option| ... }.
|
138
|
-
#
|
139
|
-
# Checks if the +option+'s value is less than the provided +limit+.
|
140
|
-
def lt
|
141
|
-
->(limit, option) { option.value < limit ? Success.new(option) : Failure.new(Messages::NOT_LT) }.curry
|
142
|
-
end
|
143
|
-
|
144
|
-
# Returns
|
145
|
-
# lambda { |limit, option| ... }.
|
146
|
-
#
|
147
|
-
# Checks if the size of the value in +option+ does not exceed provided +limit+.
|
148
|
-
def max_size
|
149
|
-
lambda { |limit, option|
|
150
|
-
option.value.size <= limit ? Success.new(option) : Failure.new(Messages::TOO_LONG)
|
151
|
-
}.curry
|
152
|
-
end
|
153
|
-
|
154
|
-
# Returns
|
155
|
-
# lambda { |limit, option| ... }.
|
156
|
-
#
|
157
|
-
# Checks if the size of the value in +option+ is not lower than the provided +limit+.
|
158
|
-
def min_size
|
159
|
-
lambda { |limit, option|
|
160
|
-
option.value.size >= limit ? Success.new(option) : Failure.new(Messages::TOO_SHORT)
|
161
|
-
}.curry
|
162
|
-
end
|
163
|
-
|
164
|
-
# Returns
|
165
|
-
# lambda { |option| ... }.
|
166
|
-
#
|
167
|
-
# Removes leading and trailing spaces from string provided in +option+'s value.
|
168
|
-
def stripped
|
169
|
-
->(option) { Success.new(Optiomist.some(option.value.strip)) }
|
170
|
-
end
|
171
|
-
|
172
|
-
# Returns
|
173
|
-
# lambda { |fn, option| ... }.
|
174
|
-
#
|
175
|
-
# Converts provided +option+'s value to integer.
|
176
|
-
# If the conversion is not possible it fails, otherwise executes the provider function +fn+
|
177
|
-
# for the converted integer value.
|
178
|
-
def integer
|
179
|
-
lambda { |fn, option|
|
180
|
-
begin
|
181
|
-
integer_value = Integer(option.value)
|
182
|
-
rescue StandardError
|
183
|
-
return Failure.new(Messages::NON_INTEGER)
|
184
|
-
end
|
185
|
-
fn.call(Optiomist.some(integer_value))
|
186
|
-
}.curry
|
187
|
-
end
|
188
|
-
|
189
|
-
# Returns
|
190
|
-
# lambda { |fn, option| ... }.
|
191
|
-
#
|
192
|
-
# Converts provided +option+'s value to float.
|
193
|
-
# If the conversion is not possible it fails, otherwise executes the provider function +fn+
|
194
|
-
# for the converted float value.
|
195
|
-
def decimal
|
196
|
-
lambda { |fn, option|
|
197
|
-
begin
|
198
|
-
float_value = Float(option.value)
|
199
|
-
rescue StandardError
|
200
|
-
return Failure.new(Messages::NON_DECIMAL)
|
201
|
-
end
|
202
|
-
fn.call(Optiomist.some(float_value))
|
203
|
-
}.curry
|
94
|
+
# Converts provided +option+'s value to float.
|
95
|
+
# If the conversion is not possible it fails.
|
96
|
+
DECIMAL = lambda do |option|
|
97
|
+
begin
|
98
|
+
float_value = Float(option.value)
|
99
|
+
rescue StandardError
|
100
|
+
return Failure.new(NOT_DECIMAL_ERR)
|
204
101
|
end
|
102
|
+
Success.new(Optiomist.some(float_value))
|
103
|
+
end
|
205
104
|
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
else
|
221
|
-
Failure.new(Messages::NON_BOOL)
|
222
|
-
end
|
223
|
-
in Optiomist::None
|
224
|
-
Failure.new(Messages::NON_BOOL)
|
225
|
-
end
|
226
|
-
}.curry
|
105
|
+
# Converts provided +option+'s value to boolean.
|
106
|
+
# If the conversion is not possible it fails.
|
107
|
+
BOOL = lambda do |option|
|
108
|
+
case option
|
109
|
+
in Optiomist::Some
|
110
|
+
if [true, *TRUE_VALUES].include?(option.value)
|
111
|
+
Success.new(Optiomist.some(true))
|
112
|
+
elsif [false, *FALSE_VALUES].include?(option.value)
|
113
|
+
Success.new(Optiomist.some(false))
|
114
|
+
else
|
115
|
+
Failure.new(NOT_BOOL_ERR)
|
116
|
+
end
|
117
|
+
in Optiomist::None
|
118
|
+
Failure.new(NOT_BOOL_ERR)
|
227
119
|
end
|
120
|
+
end
|
228
121
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
case option
|
238
|
-
in Optiomist::Some
|
239
|
-
fn.call(Optiomist.some(option.value.to_s))
|
240
|
-
in Optiomist::None
|
241
|
-
Failure.new(Messages::NON_STRING)
|
242
|
-
end
|
243
|
-
}.curry
|
122
|
+
# Converts provided +option+'s value to string.
|
123
|
+
# If the conversion is not possible it fails.
|
124
|
+
STRING = lambda do |option|
|
125
|
+
case option
|
126
|
+
in Optiomist::Some
|
127
|
+
Success.new(Optiomist.some(option.value.to_s))
|
128
|
+
in Optiomist::None
|
129
|
+
Failure.new(NOT_STRING_ERR)
|
244
130
|
end
|
245
131
|
end
|
246
132
|
end
|
data/lib/param_param.rb
CHANGED
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
require 'optiomist'
|
4
4
|
require 'param_param/result'
|
5
|
+
require 'param_param/actions'
|
5
6
|
|
6
|
-
# It allows to define
|
7
|
+
# It allows to define actions that transform hash values.
|
8
|
+
# Each actions is bound to a particular key and processes a value related to that key.
|
7
9
|
#
|
8
|
-
#
|
9
|
-
# A pipeline consists of a chain of actions that are executed one by one.
|
10
|
+
# Actions could be chained and executed one by one.
|
10
11
|
# An actions receives a value from a previous action and is expected to return successful or failed result.
|
11
12
|
# A successful result contains a new value being the result of the processing.
|
12
13
|
# The new value is passed further in the chain to the next action.
|
@@ -14,68 +15,25 @@ require 'param_param/result'
|
|
14
15
|
# Following actions in the chain are not executed.
|
15
16
|
module ParamParam
|
16
17
|
def self.included(base)
|
17
|
-
base.extend(
|
18
|
+
base.extend(ClassMethods)
|
18
19
|
end
|
19
20
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
# If the value is already +Optiomist::Some+ or +Optiomist::None+ returns it untouched.
|
24
|
-
def optionize(value)
|
25
|
-
if value.is_a?(Optiomist::Some) || value.is_a?(Optiomist::None)
|
26
|
-
value
|
27
|
-
else
|
28
|
-
Optiomist.some(value)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Defines pipelines and binds them to symbols.
|
33
|
-
# Pipelines are used to process parameters provided in a form of a hash.
|
34
|
-
# Each pipeline is defined for a key and processes a value related to that key in provided parameters.
|
35
|
-
# lambda { |rules, params| ... }
|
36
|
-
#
|
37
|
-
# The lambda returns two hashes:
|
38
|
-
# - if a value related to a key can be procesed by an action,
|
39
|
-
# the result is bound to the key and added to the first hash
|
40
|
-
# - if a value related to a key can't be processed by an action,
|
41
|
-
# the error is bound to the key and added to the second hash
|
42
|
-
#
|
43
|
-
# Each action needs to be a lambda taking +Optiomist+ as the only or the last parameter and returning either:
|
44
|
-
# - +ParamParam::Success+ with processed option
|
45
|
-
# - +ParamParam::Failure+ with an error
|
46
|
-
def define
|
47
|
-
lambda { |rules, params|
|
48
|
-
results = rules.to_h do |key, fn|
|
49
|
-
option = params.key?(key) ? optionize(params[key]) : Optiomist.none
|
50
|
-
[key, fn.call(option)]
|
51
|
-
end
|
52
|
-
|
53
|
-
errors = results.select { |_, result| result.failure? }
|
54
|
-
.transform_values(&:error)
|
55
|
-
params = results.select { |_, result| result.success? && result.value.some? }
|
56
|
-
.transform_values { |result| result.value.value }
|
57
|
-
[params, errors]
|
58
|
-
}.curry
|
21
|
+
module ClassMethods
|
22
|
+
def define(&)
|
23
|
+
Actions.new(yield(self))
|
59
24
|
end
|
25
|
+
end
|
60
26
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
lambda { |fns, option|
|
69
|
-
fns.reduce(Success.new(option)) { |result, fn| result.failure? ? result : fn.call(result.value) }
|
70
|
-
}.curry
|
71
|
-
end
|
27
|
+
# A chain of actions that are executed consecutively
|
28
|
+
# and pass a value from a previous action to the following one.
|
29
|
+
#
|
30
|
+
# If some action fails the chain is broken and value stops being processed.
|
31
|
+
ALL_OF = lambda do |fns, option|
|
32
|
+
fns.reduce(Success.new(option)) { |result, fn| result.failure? ? result : fn.call(result.value) }
|
33
|
+
end.curry
|
72
34
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
# Always succeeds with the provided +option+.
|
77
|
-
def any
|
78
|
-
->(option) { Success.new(option) }
|
79
|
-
end
|
35
|
+
# Always succeeds with the provided +option+.
|
36
|
+
ANY = lambda do |option|
|
37
|
+
Success.new(option)
|
80
38
|
end
|
81
39
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: param_param
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michał Radmacher
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-12-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: optiomist
|
@@ -35,6 +35,7 @@ files:
|
|
35
35
|
- LICENSE
|
36
36
|
- README.md
|
37
37
|
- lib/param_param.rb
|
38
|
+
- lib/param_param/actions.rb
|
38
39
|
- lib/param_param/result.rb
|
39
40
|
- lib/param_param/std.rb
|
40
41
|
homepage: https://github.com/mradmacher/param_param
|
@@ -57,8 +58,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
57
58
|
- !ruby/object:Gem::Version
|
58
59
|
version: '0'
|
59
60
|
requirements: []
|
60
|
-
rubygems_version: 3.
|
61
|
+
rubygems_version: 3.4.10
|
61
62
|
signing_key:
|
62
63
|
specification_version: 4
|
63
|
-
summary: Lambda powered
|
64
|
+
summary: Lambda powered processing of hash values
|
64
65
|
test_files: []
|