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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 51dd4d01f29eee0ec20fedb8cd5fc0de95ce73db
4
- data.tar.gz: 8dad3a9f2520e5512c2132a56ed75867f9c4b4fa
3
+ metadata.gz: 388f424f1515f93ce602dd4b6578288733a0c7e8
4
+ data.tar.gz: 8514ac581bf6afa707d12801a45f017833cd3e2b
5
5
  SHA512:
6
- metadata.gz: bedcc9b19e8e29542920bfb858a2022440d99486b85da42f9d7aed895487e24fc249bfc379301f5e07f7348af769a0e3dced6873746c030bd51b117d3c2b4e23
7
- data.tar.gz: 49c40b9764b62942b64623b1891fb1626e6c6756d627b99c3fa10f62f8d1f7c792a55286fbcedf4ee5ad3d969387e67303fcfb982fe0519f077bdf6d0c76026d
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 "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"
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 = self._outputs.merge({
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
- alias_method :ignore_errors, :ignore_error
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
- self._fields[field_name.to_sym] = options
110
+ _fields[field_name.to_sym] = options
119
111
 
120
112
  if options[:aka]
121
113
  Array(options[:aka]).each do |as|
122
- self._error_map[as.to_sym] = field_name.to_sym
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 = type_caster.cast(v, config[:type])
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
- self._fields[:#{field_name}]
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
- self._error_ignores[field_name.to_sym] = true
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 do
257
- valid?
258
- end
259
-
241
+ bool = observe_validation{ valid? }
260
242
  return false unless bool
261
243
 
262
- observe_perform do
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.has_key?(key)
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 self._error_ignores[k.to_sym]
264
+ next if _error_ignores[k.to_sym]
285
265
 
286
- if respond_to?("#{k}")
266
+ if respond_to?(k)
287
267
  errors.add(k, v)
288
- elsif self._error_map[k.to_sym]
289
- errors.add(self._error_map[k.to_sym], v)
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
- self._fields.each_pair do |field, config|
305
- if inputs.has_key?(field)
306
- out[field] = type_caster.cast(inputs[field], config[:type])
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
- self._fields.each_pair do |field, config|
317
- unless config[:default].nil?
318
- deflt = config[:default]
319
- if deflt.respond_to?(:call)
320
- deflt = deflt.call
321
- elsif deflt.duplicable? # from active_support
322
- # Some classes of default values need to be duplicated, or the instance field value will end up referencing
323
- # the class global default value, and potentially modify it.
324
- deflt = deflt.deep_dup # from active_support
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
- class TypeCaster
9
+ module TypeCaster
10
10
 
11
+ def casters
12
+ @casters ||= {}
13
+ end
14
+ module_function :casters
11
15
 
12
- TYPES = {
13
- :integer => [:int, :integer, :epoch],
14
- :number => [:number, :float],
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
- protected
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
- def cast_string(value)
68
- String(value)
26
+ caster = casters[type]
27
+ caster ? caster.call(value, *args) : value
69
28
  end
29
+ module_function :cast
70
30
 
71
- def cast_boolean(value)
72
- !!(cast_string(value) =~ /^(yes|true|1|ok)$/)
73
- end
31
+ end
32
+ end
74
33
 
75
- def cast_time(value)
76
- return nil unless value.present?
77
- ::Time.parse(cast_string(value))
78
- end
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
- def cast_date(value)
81
- return nil unless value.present?
82
- ::Date.parse(cast_string(value))
83
- end
40
+ Subroutine::TypeCaster.register :integer, :int, :epoch do |value|
41
+ Subroutine::TypeCaster.cast(value, :number, :to_i)
42
+ end
84
43
 
85
- def cast_iso_time(value)
86
- return nil unless value.present?
87
- t = nil
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
- def cast_iso_date(value)
95
- return nil unless value.present?
96
- d = nil
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
- def cast_hash(value)
104
- _cast_hash(value).try(:stringify_keys)
105
- end
52
+ Subroutine::TypeCaster.register :boolean, :bool do |value|
53
+ !!(String(value) =~ /^(yes|true|1|ok)$/)
54
+ end
106
55
 
107
- def _cast_hash(value)
108
- return _cast_action_controller_query_params(value) if is_action_controller_query_params?(value)
109
- return value if value.is_a?(Hash)
110
- return {} if value.blank?
111
- return value.to_hash if value.respond_to?(:to_hash)
112
- return value.to_h if value.respond_to?(:to_h)
113
- return ::Hash[value.to_a] if value.respond_to?(:to_a)
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
- def _cast_action_controller_query_params(value)
118
- value = value.to_hash
119
- value.each_pair{|k,v| value[k] = _cast_action_controller_query_params(v) if is_action_controller_query_params?(v) }
120
- value
121
- end
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
- def is_action_controller_query_params?(value)
124
- value.class.name == "ActionController::Parameters"
125
- end
74
+ Subroutine::TypeCaster.register :date do |value|
75
+ next nil unless value.present?
76
+ ::Date.parse(String(value))
77
+ end
126
78
 
127
- def cast_array(value)
128
- return [] if value.blank?
129
- ::Array.wrap(value)
130
- end
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
@@ -1,8 +1,8 @@
1
1
  module Subroutine
2
2
 
3
3
  MAJOR = 0
4
- MINOR = 5
5
- PATCH = 3
4
+ MINOR = 6
5
+ PATCH = 0
6
6
  PRE = nil
7
7
 
8
8
  VERSION = [MAJOR, MINOR, PATCH, PRE].compact.join('.')
data/test/support/ops.rb CHANGED
@@ -119,7 +119,6 @@ class TypeCastOp < ::Subroutine::Op
119
119
  iso_time :iso_time_input
120
120
  object :object_input
121
121
  array :array_input, :default => 'foo'
122
-
123
122
  end
124
123
 
125
124
  class OpWithAuth < ::Subroutine::Op
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.5.3
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-01-23 00:00:00.000000000 Z
11
+ date: 2018-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel