decanter 0.7.2 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +41 -0
- data/lib/decanter/core.rb +15 -9
- data/lib/decanter/parser.rb +22 -5
- data/lib/decanter/parser/core.rb +25 -21
- data/lib/decanter/version.rb +1 -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: 284c077381d45c71768a4e7523843a6970ad386c
|
4
|
+
data.tar.gz: 09fc5570e94021864b353ae4f928aea669e1507c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a058f4fc3a206341742af0e39d62abc663d7a8665b244f2ab29bc8f9df73cd810955fa19502bc8943b32f8662e1dcf254c4ceeac6ce20eed997a9b51a48e9317
|
7
|
+
data.tar.gz: 19442b1a6b19562768fa4a87a125f661199660bac14eebf4c19ef78bfbc64d4fbab11d6f181d4e97ddb8e9db16fb08bcb3de3f28cd95db9ca3386aad99f247ea
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -378,6 +378,45 @@ class SquashDateParser < Decanter::Parser::ValueParser
|
|
378
378
|
end
|
379
379
|
```
|
380
380
|
|
381
|
+
Chaining Parsers
|
382
|
+
---
|
383
|
+
|
384
|
+
Parsers are composable! Suppose you want a parser that takes an incoming percentage like "50.3%" and converts it into a float for your database like .503. You could implement this with:
|
385
|
+
|
386
|
+
```ruby
|
387
|
+
class PercentParser < ValueParser
|
388
|
+
REGEX = /(\d|[.])/
|
389
|
+
|
390
|
+
parser do |val, options|
|
391
|
+
my_float = val.scan(REGEX).join.try(:to_f)
|
392
|
+
my_float / 100 if my_float
|
393
|
+
end
|
394
|
+
end
|
395
|
+
```
|
396
|
+
|
397
|
+
This works, but it duplicates logic that already exists in `FloatParser`. Instead, you can specify a parser that should always run before your parsing logic, then you can assume that your parser receives a float:
|
398
|
+
|
399
|
+
```ruby
|
400
|
+
class SmartPercentParser < ValueParser
|
401
|
+
|
402
|
+
pre :float
|
403
|
+
|
404
|
+
parser do |val, options|
|
405
|
+
val / 100
|
406
|
+
end
|
407
|
+
end
|
408
|
+
```
|
409
|
+
|
410
|
+
If a preparser returns nil or an empty string, subsequent parsers will not be called, just like normal!
|
411
|
+
|
412
|
+
This can also be achieved by providing multiple parsers in your decanter:
|
413
|
+
|
414
|
+
```ruby
|
415
|
+
class SomeDecanter < Decanter::Base
|
416
|
+
input :some_percent, [:float, :percent]
|
417
|
+
end
|
418
|
+
```
|
419
|
+
|
381
420
|
No Need for Strong Params
|
382
421
|
---
|
383
422
|
|
@@ -403,6 +442,8 @@ class TripDecanter < Decanter::Base
|
|
403
442
|
end
|
404
443
|
```
|
405
444
|
|
445
|
+
In addition, if you provide the option `:required` for an input in your decanter, an exception will be thrown if the parameters is nil or an empty string.
|
446
|
+
|
406
447
|
Configuration
|
407
448
|
---
|
408
449
|
|
data/lib/decanter/core.rb
CHANGED
@@ -7,11 +7,11 @@ module Decanter
|
|
7
7
|
|
8
8
|
module ClassMethods
|
9
9
|
|
10
|
-
def input(name,
|
10
|
+
def input(name, parsers=nil, **options)
|
11
11
|
|
12
12
|
_name = [name].flatten
|
13
13
|
|
14
|
-
if _name.length > 1 &&
|
14
|
+
if _name.length > 1 && parsers.blank?
|
15
15
|
raise ArgumentError.new("#{self.name} no parser specified for input with multiple values.")
|
16
16
|
end
|
17
17
|
|
@@ -19,7 +19,7 @@ module Decanter
|
|
19
19
|
key: options.fetch(:key, _name.first),
|
20
20
|
name: _name,
|
21
21
|
options: options,
|
22
|
-
|
22
|
+
parsers: parsers,
|
23
23
|
type: :input
|
24
24
|
}
|
25
25
|
end
|
@@ -102,7 +102,7 @@ module Decanter
|
|
102
102
|
def handle_input(handler, args)
|
103
103
|
values = args.values_at(*handler[:name])
|
104
104
|
values = values.length == 1 ? values.first : values
|
105
|
-
parse(handler[:key], handler[:
|
105
|
+
parse(handler[:key], handler[:parsers], values, handler[:options])
|
106
106
|
end
|
107
107
|
|
108
108
|
def handle_association(handler, args)
|
@@ -154,12 +154,18 @@ module Decanter
|
|
154
154
|
end
|
155
155
|
end
|
156
156
|
|
157
|
-
def parse(key,
|
158
|
-
|
159
|
-
|
160
|
-
.parse(key, values, options)
|
161
|
-
:
|
157
|
+
def parse(key, parsers, values, options)
|
158
|
+
case
|
159
|
+
when !parsers
|
162
160
|
{ key => values }
|
161
|
+
when options[:required] == true && Array.wrap(values).all? { |value| value.nil? || value == "" }
|
162
|
+
raise ArgumentError.new("No value for required argument: #{key}")
|
163
|
+
else
|
164
|
+
Parser.parsers_for(parsers)
|
165
|
+
.reduce({key => values}) do |vals_hash, parser|
|
166
|
+
vals_hash.keys.reduce({}) { |acc, k| acc.merge(parser.parse(k, vals_hash[k], options)) }
|
167
|
+
end
|
168
|
+
end
|
163
169
|
end
|
164
170
|
|
165
171
|
def handlers
|
data/lib/decanter/parser.rb
CHANGED
@@ -2,8 +2,17 @@ module Decanter
|
|
2
2
|
module Parser
|
3
3
|
|
4
4
|
class << self
|
5
|
-
def
|
6
|
-
|
5
|
+
def parsers_for(klass_or_syms)
|
6
|
+
Array.wrap(klass_or_syms)
|
7
|
+
.map { |klass_or_sym| klass_or_sym_to_str(klass_or_sym) }
|
8
|
+
.map { |parser_str| parser_constantize(parser_str) }
|
9
|
+
.map { |parser| expand(parser) }
|
10
|
+
.flatten
|
11
|
+
end
|
12
|
+
|
13
|
+
# convert from a class or symbol to a string and concat 'Parser'
|
14
|
+
def klass_or_sym_to_str(klass_or_sym)
|
15
|
+
case klass_or_sym
|
7
16
|
when Class
|
8
17
|
klass_or_sym.name
|
9
18
|
when Symbol
|
@@ -11,10 +20,18 @@ module Decanter
|
|
11
20
|
else
|
12
21
|
raise ArgumentError.new("cannot lookup parser for #{klass_or_sym} with class #{klass_or_sym.class}")
|
13
22
|
end.concat('Parser')
|
23
|
+
end
|
24
|
+
|
25
|
+
# convert from a string to a constant
|
26
|
+
def parser_constantize(parser_str)
|
27
|
+
parser_str.safe_constantize ||
|
28
|
+
"Decanter::Parser::".concat(parser_str).safe_constantize ||
|
29
|
+
raise(NameError.new("cannot find parser #{parser_str}"))
|
30
|
+
end
|
14
31
|
|
15
|
-
|
16
|
-
|
17
|
-
parser
|
32
|
+
# expand to include preparsers
|
33
|
+
def expand(parser)
|
34
|
+
Parser.parsers_for(parser.preparsers).push(parser)
|
18
35
|
end
|
19
36
|
end
|
20
37
|
end
|
data/lib/decanter/parser/core.rb
CHANGED
@@ -8,38 +8,42 @@ module Decanter
|
|
8
8
|
|
9
9
|
module ClassMethods
|
10
10
|
|
11
|
-
#
|
11
|
+
# Check if allowed, parse if not
|
12
12
|
def parse(name, values, options={})
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
if options[:required]
|
19
|
-
raise ArgumentError.new("No value for required argument: #{name}")
|
20
|
-
else
|
21
|
-
return { name => nil }
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
if @allowed && value_ary.all? { |value| @allowed.any? { |allowed| value.is_a? allowed } }
|
26
|
-
return { name => values }
|
13
|
+
case
|
14
|
+
when allowed?(values)
|
15
|
+
{ name => values }
|
16
|
+
else
|
17
|
+
_parse(name, values, options)
|
27
18
|
end
|
28
|
-
|
29
|
-
unless @parser
|
30
|
-
raise ArgumentError.new("No parser for argument: #{name} with types: #{value_ary.map(&:class).join(', ')}")
|
31
|
-
end
|
32
|
-
|
33
|
-
_parse(name, values, options)
|
34
19
|
end
|
35
20
|
|
21
|
+
# Define parser
|
36
22
|
def parser(&block)
|
37
23
|
@parser = block
|
38
24
|
end
|
39
25
|
|
26
|
+
# Set allowed classes
|
40
27
|
def allow(*args)
|
41
28
|
@allowed = args
|
42
29
|
end
|
30
|
+
|
31
|
+
# Set preparsers
|
32
|
+
def pre(*parsers)
|
33
|
+
@pre = parsers
|
34
|
+
end
|
35
|
+
|
36
|
+
# Get prepareer
|
37
|
+
def preparsers
|
38
|
+
@pre || []
|
39
|
+
end
|
40
|
+
|
41
|
+
# Check for allowed classes
|
42
|
+
def allowed?(values)
|
43
|
+
@allowed && Array.wrap(values).all? do |value|
|
44
|
+
@allowed.any? { |allowed| value.is_a? allowed }
|
45
|
+
end
|
46
|
+
end
|
43
47
|
end
|
44
48
|
end
|
45
49
|
end
|
data/lib/decanter/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: decanter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Francis
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-05-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|