subroutine 0.5.3 → 0.6.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/lib/subroutine/op.rb +35 -61
- data/lib/subroutine/type_caster.rb +74 -105
- data/lib/subroutine/version.rb +2 -2
- data/test/support/ops.rb +0 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 388f424f1515f93ce602dd4b6578288733a0c7e8
|
4
|
+
data.tar.gz: 8514ac581bf6afa707d12801a45f017833cd3e2b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e008b4415a368b7df088f4049cf08714951fdce842c53c9ddd11c31401ded7e2cc42997325a62012f753adeca942765d86c0f33df6fa1fc4534471a9512b432a
|
7
|
+
data.tar.gz: cf77b24680e08978e7b927c7b4999da80b72bbcd9247811052ed4d7eca49ec32f79c7db3750159dcb3718d7cab9b8ac37b9ab7647f6e04e85d306ab98016a91b
|
data/lib/subroutine/op.rb
CHANGED
@@ -3,16 +3,14 @@ require 'active_support/core_ext/object/duplicable'
|
|
3
3
|
require 'active_support/core_ext/object/deep_dup'
|
4
4
|
require 'active_model'
|
5
5
|
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
6
|
+
require 'subroutine/failure'
|
7
|
+
require 'subroutine/type_caster'
|
8
|
+
require 'subroutine/filtered_errors'
|
9
|
+
require 'subroutine/output_not_set_error'
|
10
|
+
require 'subroutine/unknown_output_error'
|
11
11
|
|
12
12
|
module Subroutine
|
13
|
-
|
14
13
|
class Op
|
15
|
-
|
16
14
|
include ::ActiveModel::Model
|
17
15
|
include ::ActiveModel::Validations::Callbacks
|
18
16
|
|
@@ -21,9 +19,7 @@ module Subroutine
|
|
21
19
|
}.freeze
|
22
20
|
|
23
21
|
class << self
|
24
|
-
|
25
|
-
::Subroutine::TypeCaster::TYPES.values.flatten.each do |caster|
|
26
|
-
|
22
|
+
::Subroutine::TypeCaster.casters.each_key do |caster|
|
27
23
|
next if method_defined?(caster)
|
28
24
|
|
29
25
|
class_eval <<-EV, __FILE__, __LINE__ + 1
|
@@ -46,15 +42,12 @@ module Subroutine
|
|
46
42
|
_field(f, options)
|
47
43
|
end
|
48
44
|
end
|
49
|
-
|
50
|
-
alias_method :fields, :field
|
45
|
+
alias fields field
|
51
46
|
|
52
47
|
def outputs(*names)
|
53
48
|
options = names.extract_options!
|
54
49
|
names.each do |name|
|
55
|
-
self._outputs =
|
56
|
-
name.to_sym => DEFAULT_OUTPUT_OPTIONS.merge(options)
|
57
|
-
})
|
50
|
+
self._outputs = _outputs.merge(name.to_sym => DEFAULT_OUTPUT_OPTIONS.merge(options))
|
58
51
|
|
59
52
|
class_eval <<-EV, __FILE__, __LINE__ + 1
|
60
53
|
def #{name}
|
@@ -69,7 +62,7 @@ module Subroutine
|
|
69
62
|
_ignore_errors(f)
|
70
63
|
end
|
71
64
|
end
|
72
|
-
|
65
|
+
alias ignore_errors ignore_error
|
73
66
|
|
74
67
|
def inputs_from(*ops)
|
75
68
|
options = ops.extract_options!
|
@@ -98,7 +91,6 @@ module Subroutine
|
|
98
91
|
child._error_ignores = self._error_ignores.dup
|
99
92
|
end
|
100
93
|
|
101
|
-
|
102
94
|
def submit!(*args)
|
103
95
|
op = new(*args)
|
104
96
|
op.submit!
|
@@ -115,23 +107,21 @@ module Subroutine
|
|
115
107
|
protected
|
116
108
|
|
117
109
|
def _field(field_name, options = {})
|
118
|
-
|
110
|
+
_fields[field_name.to_sym] = options
|
119
111
|
|
120
112
|
if options[:aka]
|
121
113
|
Array(options[:aka]).each do |as|
|
122
|
-
|
114
|
+
_error_map[as.to_sym] = field_name.to_sym
|
123
115
|
end
|
124
116
|
end
|
125
117
|
|
126
|
-
if options[:ignore_errors]
|
127
|
-
_ignore_errors(field_name)
|
128
|
-
end
|
118
|
+
_ignore_errors(field_name) if options[:ignore_errors]
|
129
119
|
|
130
120
|
class_eval <<-EV, __FILE__, __LINE__ + 1
|
131
121
|
|
132
122
|
def #{field_name}=(v)
|
133
123
|
config = #{field_name}_config
|
134
|
-
v =
|
124
|
+
v = ::Subroutine::TypeCaster.cast(v, config[:type])
|
135
125
|
@params["#{field_name}"] = v
|
136
126
|
end
|
137
127
|
|
@@ -140,7 +130,7 @@ module Subroutine
|
|
140
130
|
end
|
141
131
|
|
142
132
|
def #{field_name}_config
|
143
|
-
|
133
|
+
_fields[:#{field_name}]
|
144
134
|
end
|
145
135
|
|
146
136
|
EV
|
@@ -148,7 +138,7 @@ module Subroutine
|
|
148
138
|
end
|
149
139
|
|
150
140
|
def _ignore_errors(field_name)
|
151
|
-
|
141
|
+
_error_ignores[field_name.to_sym] = true
|
152
142
|
end
|
153
143
|
|
154
144
|
end
|
@@ -234,10 +224,6 @@ module Subroutine
|
|
234
224
|
|
235
225
|
protected
|
236
226
|
|
237
|
-
def type_caster
|
238
|
-
@type_caster ||= ::Subroutine::TypeCaster.new
|
239
|
-
end
|
240
|
-
|
241
227
|
# these enable you to 1) add log output or 2) add performance monitoring such as skylight.
|
242
228
|
def observe_submission
|
243
229
|
yield
|
@@ -251,17 +237,11 @@ module Subroutine
|
|
251
237
|
yield
|
252
238
|
end
|
253
239
|
|
254
|
-
|
255
240
|
def validate_and_perform
|
256
|
-
bool = observe_validation
|
257
|
-
valid?
|
258
|
-
end
|
259
|
-
|
241
|
+
bool = observe_validation{ valid? }
|
260
242
|
return false unless bool
|
261
243
|
|
262
|
-
observe_perform
|
263
|
-
perform
|
264
|
-
end
|
244
|
+
observe_perform{ perform }
|
265
245
|
end
|
266
246
|
|
267
247
|
# implement this in your concrete class.
|
@@ -271,7 +251,7 @@ module Subroutine
|
|
271
251
|
|
272
252
|
# check if a specific field was provided
|
273
253
|
def field_provided?(key)
|
274
|
-
@params.
|
254
|
+
@params.key?(key)
|
275
255
|
end
|
276
256
|
|
277
257
|
# applies the errors in error_object to self
|
@@ -281,30 +261,27 @@ module Subroutine
|
|
281
261
|
|
282
262
|
error_object.each do |k,v|
|
283
263
|
|
284
|
-
next if
|
264
|
+
next if _error_ignores[k.to_sym]
|
285
265
|
|
286
|
-
if respond_to?(
|
266
|
+
if respond_to?(k)
|
287
267
|
errors.add(k, v)
|
288
|
-
elsif
|
289
|
-
errors.add(
|
268
|
+
elsif _error_map[k.to_sym]
|
269
|
+
errors.add(_error_map[k.to_sym], v)
|
290
270
|
else
|
291
271
|
errors.add(:base, error_object.full_message(k,v))
|
292
272
|
end
|
293
|
-
|
294
273
|
end
|
295
274
|
|
296
275
|
false
|
297
276
|
end
|
298
277
|
|
299
|
-
|
300
278
|
# if you want to use strong parameters or something in your form object you can do so here.
|
301
279
|
# by default we just slice the inputs to the defined fields
|
302
280
|
def sanitize_params(inputs)
|
303
281
|
out = {}.with_indifferent_access
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
end
|
282
|
+
_fields.each_pair do |field, config|
|
283
|
+
next unless inputs.key?(field)
|
284
|
+
out[field] = ::Subroutine::TypeCaster.cast(inputs[field], config[:type])
|
308
285
|
end
|
309
286
|
|
310
287
|
out
|
@@ -313,23 +290,20 @@ module Subroutine
|
|
313
290
|
def sanitize_defaults
|
314
291
|
defaults = {}.with_indifferent_access
|
315
292
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
end
|
326
|
-
defaults[field] = type_caster.cast(deflt, config[:type])
|
293
|
+
_fields.each_pair do |field, config|
|
294
|
+
next if config[:default].nil?
|
295
|
+
deflt = config[:default]
|
296
|
+
if deflt.respond_to?(:call)
|
297
|
+
deflt = deflt.call
|
298
|
+
elsif deflt.duplicable? # from active_support
|
299
|
+
# Some classes of default values need to be duplicated, or the instance field value will end up referencing
|
300
|
+
# the class global default value, and potentially modify it.
|
301
|
+
deflt = deflt.deep_dup # from active_support
|
327
302
|
end
|
303
|
+
defaults[field] = ::Subroutine::TypeCaster.cast(deflt, config[:type])
|
328
304
|
end
|
329
305
|
|
330
306
|
defaults
|
331
307
|
end
|
332
|
-
|
333
308
|
end
|
334
|
-
|
335
309
|
end
|
@@ -6,128 +6,97 @@ require 'active_support/core_ext/object/try'
|
|
6
6
|
require 'active_support/core_ext/array/wrap'
|
7
7
|
|
8
8
|
module Subroutine
|
9
|
-
|
9
|
+
module TypeCaster
|
10
10
|
|
11
|
+
def casters
|
12
|
+
@casters ||= {}
|
13
|
+
end
|
14
|
+
module_function :casters
|
11
15
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
:decimal => [:decimal, :big_decimal],
|
16
|
-
:string => [:string, :text],
|
17
|
-
:boolean => [:bool, :boolean],
|
18
|
-
:iso_date => [:iso_date],
|
19
|
-
:iso_time => [:iso_time],
|
20
|
-
:date => [:date],
|
21
|
-
:time => [:time, :timestamp],
|
22
|
-
:hash => [:object, :hashmap, :dict],
|
23
|
-
:array => [:array]
|
24
|
-
}
|
25
|
-
|
26
|
-
|
27
|
-
def cast(value, type)
|
28
|
-
return value if value.nil? || type.nil?
|
29
|
-
|
30
|
-
case type.to_sym
|
31
|
-
when *TYPES[:integer]
|
32
|
-
cast_number(value, :to_i)
|
33
|
-
when *TYPES[:number]
|
34
|
-
cast_number(value, :to_f)
|
35
|
-
when *TYPES[:decimal]
|
36
|
-
cast_number(value, :to_d, :to_f)
|
37
|
-
when *TYPES[:string]
|
38
|
-
cast_string(value)
|
39
|
-
when *TYPES[:boolean]
|
40
|
-
cast_boolean(value)
|
41
|
-
when *TYPES[:iso_date]
|
42
|
-
t = cast_iso_time(value)
|
43
|
-
t ? t.split('T')[0] : t
|
44
|
-
when *TYPES[:date]
|
45
|
-
cast_date(value).try(:to_date)
|
46
|
-
when *TYPES[:iso_time]
|
47
|
-
cast_iso_time(value)
|
48
|
-
when *TYPES[:time]
|
49
|
-
cast_time(value)
|
50
|
-
when *TYPES[:hash]
|
51
|
-
cast_hash(value)
|
52
|
-
when *TYPES[:array]
|
53
|
-
cast_array(value)
|
54
|
-
else
|
55
|
-
value
|
16
|
+
def register(*names, &block)
|
17
|
+
names.each do |n|
|
18
|
+
casters[n] = block
|
56
19
|
end
|
57
20
|
end
|
21
|
+
module_function :register
|
58
22
|
|
59
|
-
|
60
|
-
|
61
|
-
def cast_number(value, *meths)
|
62
|
-
return nil if value.blank?
|
63
|
-
meth = meths.detect{|m| value.respond_to?(m) }
|
64
|
-
meth ? value.send(meth) : value
|
65
|
-
end
|
23
|
+
def cast(value, type, *args)
|
24
|
+
return value if value.nil? || type.nil?
|
66
25
|
|
67
|
-
|
68
|
-
|
26
|
+
caster = casters[type]
|
27
|
+
caster ? caster.call(value, *args) : value
|
69
28
|
end
|
29
|
+
module_function :cast
|
70
30
|
|
71
|
-
|
72
|
-
|
73
|
-
end
|
31
|
+
end
|
32
|
+
end
|
74
33
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
34
|
+
Subroutine::TypeCaster.register :number do |value, *meths|
|
35
|
+
next nil if value.blank?
|
36
|
+
meth = meths.detect{|m| value.respond_to?(m) }
|
37
|
+
meth ? value.send(meth) : value.to_f
|
38
|
+
end
|
79
39
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
end
|
40
|
+
Subroutine::TypeCaster.register :integer, :int, :epoch do |value|
|
41
|
+
Subroutine::TypeCaster.cast(value, :number, :to_i)
|
42
|
+
end
|
84
43
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
t ||= value if value.is_a?(::Time)
|
89
|
-
t ||= value if value.try(:acts_like?, :time)
|
90
|
-
t ||= ::Time.parse(cast_string(value))
|
91
|
-
t.utc.iso8601
|
92
|
-
end
|
44
|
+
Subroutine::TypeCaster.register :decimal, :big_decimal do |value|
|
45
|
+
Subroutine::TypeCaster.cast(value, :number, :to_d, :to_f)
|
46
|
+
end
|
93
47
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
d ||= value if value.is_a?(::Date)
|
98
|
-
d ||= value if value.try(:acts_like?, :date)
|
99
|
-
d ||= ::Date.parse(cast_string(value))
|
100
|
-
d.iso8601
|
101
|
-
end
|
48
|
+
Subroutine::TypeCaster.register :string, :text do |value|
|
49
|
+
String(value)
|
50
|
+
end
|
102
51
|
|
103
|
-
|
104
|
-
|
105
|
-
|
52
|
+
Subroutine::TypeCaster.register :boolean, :bool do |value|
|
53
|
+
!!(String(value) =~ /^(yes|true|1|ok)$/)
|
54
|
+
end
|
106
55
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
56
|
+
Subroutine::TypeCaster.register :iso_date do |value|
|
57
|
+
next nil unless value.present?
|
58
|
+
d = nil
|
59
|
+
d ||= value if value.is_a?(::Date)
|
60
|
+
d ||= value if value.try(:acts_like?, :date)
|
61
|
+
d ||= ::Date.parse(String(value))
|
62
|
+
d.iso8601
|
63
|
+
end
|
116
64
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
65
|
+
Subroutine::TypeCaster.register :iso_time do |value|
|
66
|
+
next nil unless value.present?
|
67
|
+
t = nil
|
68
|
+
t ||= value if value.is_a?(::Time)
|
69
|
+
t ||= value if value.try(:acts_like?, :time)
|
70
|
+
t ||= ::Time.parse(String(value))
|
71
|
+
t.utc.iso8601
|
72
|
+
end
|
122
73
|
|
123
|
-
|
124
|
-
|
125
|
-
|
74
|
+
Subroutine::TypeCaster.register :date do |value|
|
75
|
+
next nil unless value.present?
|
76
|
+
::Date.parse(String(value))
|
77
|
+
end
|
126
78
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
79
|
+
Subroutine::TypeCaster.register :time, :timestamp, :datetime do |value|
|
80
|
+
next nil unless value.present?
|
81
|
+
::Time.parse(String(value))
|
82
|
+
end
|
131
83
|
|
84
|
+
Subroutine::TypeCaster.register :hash, :object, :hashmap, :dict do |value|
|
85
|
+
if value.class.name == 'ActionController::Parameters'
|
86
|
+
value = value.to_hash
|
87
|
+
value.each_pair { |k, v| value[k] = Subroutine::TypeCaster.cast(v, :hash) if v.class.name == 'ActionController::Parameters' }
|
88
|
+
next value
|
132
89
|
end
|
90
|
+
|
91
|
+
next value if value.is_a?(Hash)
|
92
|
+
next {} if value.blank?
|
93
|
+
next value.to_hash if value.respond_to?(:to_hash)
|
94
|
+
next value.to_h if value.respond_to?(:to_h)
|
95
|
+
next ::Hash[value.to_a] if value.respond_to?(:to_a)
|
96
|
+
{}
|
97
|
+
end
|
98
|
+
|
99
|
+
Subroutine::TypeCaster.register :array do |value|
|
100
|
+
next [] if value.blank?
|
101
|
+
::Array.wrap(value)
|
133
102
|
end
|
data/lib/subroutine/version.rb
CHANGED
data/test/support/ops.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: subroutine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Nelson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-06-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|