decanter 3.4.2 → 4.0.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 +10 -2
- data/lib/decanter/base.rb +2 -0
- data/lib/decanter/collection_detection.rb +26 -0
- data/lib/decanter/configuration.rb +2 -1
- data/lib/decanter/core.rb +24 -6
- data/lib/decanter/extensions.rb +2 -19
- data/lib/decanter/parser/float_parser.rb +1 -1
- data/lib/decanter/parser/integer_parser.rb +1 -1
- data/lib/decanter/version.rb +1 -1
- data/lib/decanter.rb +0 -1
- data/migration-guides/v4.0.0.md +35 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fa10b029acfb863227bd9b42b915f32ce94d65dd0a82955bc13b530d3278fe87
|
4
|
+
data.tar.gz: c22c844568de79508809ceb5609e8166cc547bfc26fa4a293085baae56ce6ade
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1df6bc91a47bc1dbbf9a528b895407fed1d84cdf733121aff44440d03b4fe82a8871143d1b2d0bb87b6c9cfb1467c0a839cb394ac0bd813e5df7230faa00b9a4
|
7
|
+
data.tar.gz: 9e26fe9068e2f58d4ae8b1987a05fde413e9edac2bd0f123add82c7dfcb18f50a9d08629b71fb2c1d2d0387668f32ecfc53bda6a7e5a93d07289d96f8bbc5eaf
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
Decanter is a Ruby gem that makes it easy to transform incoming data before it hits the model. You can think of Decanter as the opposite of Active Model Serializers (AMS). While AMS transforms your outbound data into a format that your frontend consumes, Decanter transforms your incoming data into a format that your backend consumes.
|
4
4
|
|
5
5
|
```ruby
|
6
|
-
gem 'decanter', '~>
|
6
|
+
gem 'decanter', '~> 4.0'
|
7
7
|
```
|
8
8
|
|
9
9
|
## Migration Guides
|
@@ -101,6 +101,7 @@ If this option is not provided, autodetect logic is used to determine if the pro
|
|
101
101
|
- `nil` or not provided: will try to autodetect single vs collection
|
102
102
|
- `true` will always treat the incoming params args as *collection*
|
103
103
|
- `false` will always treat incoming params args as *single object*
|
104
|
+
- `truthy` will raise an error
|
104
105
|
|
105
106
|
### Nested resources
|
106
107
|
|
@@ -144,7 +145,11 @@ input :start_date, :date, parse_format: '%Y-%m-%d'
|
|
144
145
|
|
145
146
|
### Exceptions
|
146
147
|
|
147
|
-
By default, `Decanter#decant` will raise an exception when unexpected parameters are passed. To override this behavior, you can
|
148
|
+
By default, `Decanter#decant` will raise an exception when unexpected parameters are passed. To override this behavior, you can change the strict mode option to one of:
|
149
|
+
|
150
|
+
- `true` (default): unhandled keys will raise an unexpected parameters exception
|
151
|
+
- `false`: all parameter key-value pairs will be included in the result
|
152
|
+
- `:ignore`: unhandled keys will be excluded from the decanted result
|
148
153
|
|
149
154
|
```ruby
|
150
155
|
class TripDecanter < Decanter::Base
|
@@ -275,11 +280,14 @@ TripDecanter.decant({ name: 'Vacation 2020' })
|
|
275
280
|
|
276
281
|
You can generate a local copy of the default configuration with `rails generate decanter:install`. This will create an initializer where you can do global configuration:
|
277
282
|
|
283
|
+
Setting strict mode to :ignore will log out any unhandled keys. To avoid excessive logging, the global configuration can be set to `log_unhandled_keys = false`
|
284
|
+
|
278
285
|
```ruby
|
279
286
|
# ./config/initializers/decanter.rb
|
280
287
|
|
281
288
|
Decanter.config do |config|
|
282
289
|
config.strict = false
|
290
|
+
config.log_unhandled_keys = false
|
283
291
|
end
|
284
292
|
```
|
285
293
|
|
data/lib/decanter/base.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Decanter
|
2
|
+
module CollectionDetection
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def decant(args, **options)
|
9
|
+
return super(args) unless collection?(args, options[:is_collection])
|
10
|
+
|
11
|
+
args.map { |resource| super(resource) }
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# leveraging the approach used in the [fast JSON API gem](https://github.com/Netflix/fast_jsonapi#collection-serialization)
|
17
|
+
def collection?(args, collection_option = nil)
|
18
|
+
raise(ArgumentError, "#{name}: Unknown collection option value: #{collection_option}") unless [true, false, nil].include? collection_option
|
19
|
+
|
20
|
+
return collection_option unless collection_option.nil?
|
21
|
+
|
22
|
+
args.respond_to?(:size) && !args.respond_to?(:each_pair)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/decanter/core.rb
CHANGED
@@ -50,10 +50,15 @@ module Decanter
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def strict(mode)
|
53
|
-
raise(ArgumentError, "#{self.name}: Unknown strict value #{mode}") unless [true, false].include? mode
|
53
|
+
raise(ArgumentError, "#{self.name}: Unknown strict value #{mode}") unless [:ignore, true, false].include? mode
|
54
54
|
@strict_mode = mode
|
55
55
|
end
|
56
56
|
|
57
|
+
def log_unhandled_keys(mode)
|
58
|
+
raise(ArgumentError, "#{self.name}: Unknown log_unhandled_keys value #{mode}") unless [true, false].include? mode
|
59
|
+
@log_unhandled_keys_mode = mode
|
60
|
+
end
|
61
|
+
|
57
62
|
def decant(args)
|
58
63
|
return handle_empty_args if args.blank?
|
59
64
|
return empty_required_input_error unless required_input_keys_present?(args)
|
@@ -71,8 +76,9 @@ module Decanter
|
|
71
76
|
.map { |input| [input[:key], input[:options][DEFAULT_VALUE_KEY]] }
|
72
77
|
.to_h
|
73
78
|
|
74
|
-
# parse default values
|
75
|
-
handled_keys
|
79
|
+
# parse handled default values, including keys
|
80
|
+
# with defaults not already managed by handled_keys
|
81
|
+
default_result.merge(handled_keys(default_result))
|
76
82
|
end
|
77
83
|
|
78
84
|
def default_value_inputs
|
@@ -121,14 +127,21 @@ module Decanter
|
|
121
127
|
.map { |handler| "#{handler[:name]}_attributes".to_sym }
|
122
128
|
|
123
129
|
return {} unless unhandled_keys.any?
|
124
|
-
|
125
|
-
|
130
|
+
|
131
|
+
case strict_mode
|
132
|
+
when :ignore
|
133
|
+
p "#{self.name} ignoring unhandled keys: #{unhandled_keys.join(', ')}." if log_unhandled_keys_mode
|
134
|
+
{}
|
135
|
+
when true
|
136
|
+
raise(UnhandledKeysError, "#{self.name} received unhandled keys: #{unhandled_keys.join(', ')}.")
|
137
|
+
else
|
138
|
+
args.select { |key| unhandled_keys.include? key.to_sym }
|
139
|
+
end
|
126
140
|
end
|
127
141
|
|
128
142
|
def handled_keys(args)
|
129
143
|
arg_keys = args.keys.map(&:to_sym)
|
130
144
|
inputs, assocs = handlers.values.partition { |handler| handler[:type] == :input }
|
131
|
-
|
132
145
|
{}.merge(
|
133
146
|
# Inputs
|
134
147
|
inputs.select { |handler| (arg_keys & handler[:name]).any? }
|
@@ -221,6 +234,11 @@ module Decanter
|
|
221
234
|
@strict_mode.nil? ? Decanter.configuration.strict : @strict_mode
|
222
235
|
end
|
223
236
|
|
237
|
+
def log_unhandled_keys_mode
|
238
|
+
return !!(Decanter.configuration.log_unhandled_keys) if @log_unhandled_keys_mode.nil?
|
239
|
+
!!@log_unhandled_keys_mode
|
240
|
+
end
|
241
|
+
|
224
242
|
# Helpers
|
225
243
|
|
226
244
|
private
|
data/lib/decanter/extensions.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module Decanter
|
2
2
|
module Extensions
|
3
|
-
|
4
3
|
def self.included(base)
|
5
4
|
base.extend(ClassMethods)
|
6
5
|
end
|
@@ -34,29 +33,13 @@ module Decanter
|
|
34
33
|
.save!(context: options[:context])
|
35
34
|
end
|
36
35
|
|
37
|
-
def decant(args, options={})
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
def decant_collection(args, options)
|
42
|
-
args.map { |resource| decant_args(resource, options) }
|
43
|
-
end
|
44
|
-
|
45
|
-
def decant_args(args, options)
|
46
|
-
if specified_decanter = options[:decanter]
|
36
|
+
def decant(args, options = {})
|
37
|
+
if (specified_decanter = options[:decanter])
|
47
38
|
Decanter.decanter_from(specified_decanter)
|
48
39
|
else
|
49
40
|
Decanter.decanter_for(self)
|
50
41
|
end.decant(args)
|
51
42
|
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
# leveraging the approach used in the [fast JSON API gem](https://github.com/Netflix/fast_jsonapi#collection-serialization)
|
56
|
-
def is_collection?(args, collection_option=nil)
|
57
|
-
return collection_option[:is_collection] unless collection_option.nil?
|
58
|
-
args.respond_to?(:size) && !args.respond_to?(:each_pair)
|
59
|
-
end
|
60
43
|
end
|
61
44
|
|
62
45
|
module ActiveRecordExtensions
|
data/lib/decanter/version.rb
CHANGED
data/lib/decanter.rb
CHANGED
@@ -0,0 +1,35 @@
|
|
1
|
+
# v4.0.0 Migration Guide
|
2
|
+
|
3
|
+
_Note: this guide assumes you are upgrading from decanter v3 to v4._
|
4
|
+
|
5
|
+
This version contains the following breaking changes:
|
6
|
+
|
7
|
+
1. `FloatParser` and `IntegerParser` have been updated to address a bug where negative numbers were being parsed as positive. In the (unlikely) event that your project was relying on the previous behavior, you can pin the gem version to `v3.6.0` or include the legacy version(s) of the parsers as custom parsers in your project.
|
8
|
+
|
9
|
+
To add a custom parser, add the new parser class to your project:
|
10
|
+
|
11
|
+
```rb
|
12
|
+
# app/parsers/postive_float_parser.rb
|
13
|
+
|
14
|
+
class PositiveFloatParser < Decanter::Parser::ValueParser
|
15
|
+
REGEX = /(\d|[.])/
|
16
|
+
|
17
|
+
allow Float, Integer
|
18
|
+
|
19
|
+
parser do |val, options|
|
20
|
+
raise Decanter::ParseError.new 'Expects a single value' if val.is_a? Array
|
21
|
+
next if (val.nil? || val === '')
|
22
|
+
val.scan(REGEX).join.try(:to_f)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
Then, use the appropriate key to look up the parser in your decanter:
|
28
|
+
|
29
|
+
```rb
|
30
|
+
# app/decanters/product_decanter.rb
|
31
|
+
|
32
|
+
class ProductDecanter < Decanter::Base
|
33
|
+
input :price, :positive_float #=> PositiveFloatParser
|
34
|
+
end
|
35
|
+
```
|
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:
|
4
|
+
version: 4.0.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: 2021-
|
12
|
+
date: 2021-11-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: actionpack
|
@@ -152,6 +152,7 @@ files:
|
|
152
152
|
- decanter.gemspec
|
153
153
|
- lib/decanter.rb
|
154
154
|
- lib/decanter/base.rb
|
155
|
+
- lib/decanter/collection_detection.rb
|
155
156
|
- lib/decanter/configuration.rb
|
156
157
|
- lib/decanter/core.rb
|
157
158
|
- lib/decanter/exceptions.rb
|
@@ -182,6 +183,7 @@ files:
|
|
182
183
|
- lib/generators/rails/templates/decanter.rb.erb
|
183
184
|
- lib/generators/rails/templates/parser.rb.erb
|
184
185
|
- migration-guides/v3.0.0.md
|
186
|
+
- migration-guides/v4.0.0.md
|
185
187
|
homepage: https://github.com/launchpadlab/decanter
|
186
188
|
licenses:
|
187
189
|
- MIT
|