dry-transformer 0.1.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.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/.codeclimate.yml +12 -0
  3. data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +10 -0
  4. data/.github/ISSUE_TEMPLATE/---bug-report.md +30 -0
  5. data/.github/ISSUE_TEMPLATE/---feature-request.md +18 -0
  6. data/.github/workflows/custom_ci.yml +66 -0
  7. data/.github/workflows/docsite.yml +34 -0
  8. data/.github/workflows/sync_configs.yml +34 -0
  9. data/.gitignore +16 -0
  10. data/.rspec +4 -0
  11. data/.rubocop.yml +95 -0
  12. data/CHANGELOG.md +3 -0
  13. data/CODE_OF_CONDUCT.md +13 -0
  14. data/CONTRIBUTING.md +29 -0
  15. data/Gemfile +19 -0
  16. data/LICENSE +20 -0
  17. data/README.md +29 -0
  18. data/Rakefile +6 -0
  19. data/docsite/source/built-in-transformations.html.md +47 -0
  20. data/docsite/source/index.html.md +15 -0
  21. data/docsite/source/transformation-objects.html.md +32 -0
  22. data/docsite/source/using-standalone-functions.html.md +82 -0
  23. data/dry-transformer.gemspec +22 -0
  24. data/lib/dry-transformer.rb +3 -0
  25. data/lib/dry/transformer.rb +23 -0
  26. data/lib/dry/transformer/all.rb +11 -0
  27. data/lib/dry/transformer/array.rb +183 -0
  28. data/lib/dry/transformer/array/combine.rb +65 -0
  29. data/lib/dry/transformer/class.rb +56 -0
  30. data/lib/dry/transformer/coercions.rb +196 -0
  31. data/lib/dry/transformer/compiler.rb +47 -0
  32. data/lib/dry/transformer/composite.rb +54 -0
  33. data/lib/dry/transformer/conditional.rb +76 -0
  34. data/lib/dry/transformer/constants.rb +7 -0
  35. data/lib/dry/transformer/error.rb +16 -0
  36. data/lib/dry/transformer/function.rb +109 -0
  37. data/lib/dry/transformer/hash.rb +453 -0
  38. data/lib/dry/transformer/pipe.rb +75 -0
  39. data/lib/dry/transformer/pipe/class_interface.rb +115 -0
  40. data/lib/dry/transformer/pipe/dsl.rb +58 -0
  41. data/lib/dry/transformer/proc.rb +46 -0
  42. data/lib/dry/transformer/recursion.rb +121 -0
  43. data/lib/dry/transformer/registry.rb +150 -0
  44. data/lib/dry/transformer/store.rb +128 -0
  45. data/lib/dry/transformer/version.rb +7 -0
  46. data/spec/spec_helper.rb +31 -0
  47. data/spec/unit/array/combine_spec.rb +224 -0
  48. data/spec/unit/array_transformations_spec.rb +233 -0
  49. data/spec/unit/class_transformations_spec.rb +50 -0
  50. data/spec/unit/coercions_spec.rb +132 -0
  51. data/spec/unit/conditional_spec.rb +48 -0
  52. data/spec/unit/function_not_found_error_spec.rb +12 -0
  53. data/spec/unit/function_spec.rb +193 -0
  54. data/spec/unit/hash_transformations_spec.rb +490 -0
  55. data/spec/unit/proc_transformations_spec.rb +20 -0
  56. data/spec/unit/recursion_spec.rb +145 -0
  57. data/spec/unit/registry_spec.rb +202 -0
  58. data/spec/unit/store_spec.rb +198 -0
  59. data/spec/unit/transformer/class_interface_spec.rb +350 -0
  60. data/spec/unit/transformer/dsl_spec.rb +15 -0
  61. data/spec/unit/transformer/instance_methods_spec.rb +25 -0
  62. metadata +119 -0
@@ -0,0 +1,350 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ostruct'
4
+ require 'dry/equalizer'
5
+
6
+ RSpec.describe Dry::Transformer do
7
+ let(:container) { Module.new { extend Dry::Transformer::Registry } }
8
+ let(:klass) { Dry::Transformer[container] }
9
+ let(:transformer) { klass.new }
10
+
11
+ describe '.import' do
12
+ it 'allows importing functions into an auto-configured registry' do
13
+ klass = Class.new(Dry::Transformer::Pipe) do
14
+ import Dry::Transformer::ArrayTransformations
15
+ import Dry::Transformer::Coercions
16
+
17
+ define! do
18
+ map_array(&:to_symbol)
19
+ end
20
+ end
21
+
22
+ transformer = klass.new
23
+
24
+ expect(transformer.(['foo', 'bar'])).to eql([:foo, :bar])
25
+ end
26
+ end
27
+
28
+ describe '.new' do
29
+ it 'supports arguments' do
30
+ klass = Class.new(Dry::Transformer::Pipe) do
31
+ import Dry::Transformer::ArrayTransformations
32
+ import Dry::Transformer::Coercions
33
+
34
+ define! do
35
+ map_array(&:to_symbol)
36
+ end
37
+
38
+ def initialize(good)
39
+ @good = good
40
+ end
41
+
42
+ def good?
43
+ @good
44
+ end
45
+ end
46
+
47
+ transformer = klass.new(true)
48
+
49
+ expect(transformer).to be_good
50
+ end
51
+ end
52
+
53
+ describe '.container' do
54
+ it 'returns the configured container' do
55
+ expect(klass.container).to be(container)
56
+ end
57
+
58
+ context 'with setter argument' do
59
+ let(:container) { double(:custom_container) }
60
+
61
+ it 'sets and returns the container' do
62
+ klass.container(container)
63
+
64
+ expect(klass.container).to be(container)
65
+ end
66
+ end
67
+ end
68
+
69
+ describe 'inheritance' do
70
+ let(:container) do
71
+ Module.new do
72
+ extend Dry::Transformer::Registry
73
+
74
+ def self.arbitrary(value, fn)
75
+ fn[value]
76
+ end
77
+ end
78
+ end
79
+
80
+ let(:superclass) do
81
+ Class.new(Dry::Transformer[container]) do
82
+ define! do
83
+ arbitrary ->(v) { v + 1 }
84
+ end
85
+ end
86
+ end
87
+
88
+ let(:subclass) do
89
+ Class.new(superclass) do
90
+ define! do
91
+ arbitrary ->(v) { v * 2 }
92
+ end
93
+ end
94
+ end
95
+
96
+ it 'inherits container from superclass' do
97
+ expect(subclass.container).to be(superclass.container)
98
+ end
99
+
100
+ it 'inherits transproc from superclass' do
101
+ expect(superclass.new.call(2)).to be(3)
102
+ expect(subclass.new.call(2)).to be(6)
103
+ end
104
+ end
105
+
106
+ describe '.[]' do
107
+ subject(:subclass) { klass[another_container] }
108
+
109
+ let(:another_container) { double('Dry::Transformer') }
110
+
111
+ it 'sets a container' do
112
+ expect(subclass.container).to be(another_container)
113
+ end
114
+
115
+ it 'returns a class' do
116
+ expect(subclass).to be_a(Class)
117
+ end
118
+
119
+ it 'creates a subclass of Transformer' do
120
+ expect(subclass).to be < Dry::Transformer::Pipe
121
+ end
122
+
123
+ it 'does not change super class' do
124
+ expect(klass.container).to be(container)
125
+ end
126
+
127
+ it 'does not inherit transproc' do
128
+ expect(klass[container].new.transproc).to be_nil
129
+ end
130
+
131
+ context 'with predefined transformer' do
132
+ let(:klass) do
133
+ Class.new(Dry::Transformer[container]) do
134
+ container.import Dry::Transformer::Coercions
135
+ container.import Dry::Transformer::HashTransformations
136
+
137
+ define! do
138
+ map_value :attr, t(:to_symbol)
139
+ end
140
+ end
141
+ end
142
+
143
+ it "inherits parent's transproc" do
144
+ expect(klass[container].new.transproc).to eql(klass.new.transproc)
145
+ end
146
+ end
147
+ end
148
+
149
+ describe '.define!' do
150
+ let(:container) do
151
+ Module.new do
152
+ extend Dry::Transformer::Registry
153
+
154
+ import Dry::Transformer::HashTransformations
155
+
156
+ def self.to_symbol(v)
157
+ v.to_sym
158
+ end
159
+ end
160
+ end
161
+
162
+ let(:klass) { Dry::Transformer[container] }
163
+
164
+ it 'defines anonymous transproc' do
165
+ transproc = klass.define! do
166
+ map_value(:attr, t(:to_symbol))
167
+ end
168
+
169
+ expect(transproc.new.transproc[attr: 'abc']).to eq(attr: :abc)
170
+ end
171
+
172
+ it 'does not affect original transformer' do
173
+ Class.new(klass).define! do
174
+ map_value(:attr, :to_sym.to_proc)
175
+ end
176
+
177
+ expect(klass.new.transproc).to be_nil
178
+ end
179
+
180
+ context 'with custom container' do
181
+ let(:container) do
182
+ Module.new do
183
+ extend Dry::Transformer::Registry
184
+
185
+ def self.arbitrary(value, fn)
186
+ fn[value]
187
+ end
188
+ end
189
+ end
190
+
191
+ let(:klass) { described_class[container] }
192
+
193
+ it 'uses a container from the transformer' do
194
+ transproc = klass.define! do
195
+ arbitrary ->(v) { v + 1 }
196
+ end.new
197
+
198
+ expect(transproc.call(2)).to eq 3
199
+ end
200
+ end
201
+
202
+ context 'with predefined transformer' do
203
+ let(:klass) do
204
+ Class.new(described_class[container]) do
205
+ define! do
206
+ map_value :attr, ->(v) { v + 1 }
207
+ end
208
+ end
209
+ end
210
+
211
+ it 'builds transformation from the DSL definition' do
212
+ transproc = klass.new
213
+
214
+ expect(transproc.call(attr: 2)).to eql(attr: 3)
215
+ end
216
+ end
217
+ end
218
+
219
+ describe '.t' do
220
+ subject(:klass) { Dry::Transformer[container] }
221
+
222
+ let(:container) do
223
+ Module.new do
224
+ extend Dry::Transformer::Registry
225
+
226
+ import Dry::Transformer::HashTransformations
227
+ import Dry::Transformer::Conditional
228
+
229
+ def self.custom(value, suffix)
230
+ value + suffix
231
+ end
232
+ end
233
+ end
234
+
235
+ it 'returns a registed function' do
236
+ expect(klass.t(:custom, '_bar')).to eql(container[:custom, '_bar'])
237
+ end
238
+
239
+ it 'is useful in DSL' do
240
+ transproc = Class.new(klass).define! do
241
+ map_value :a, t(:custom, '_bar')
242
+ end.new
243
+
244
+ expect(transproc.call(a: 'foo')).to eq(a: 'foo_bar')
245
+ end
246
+
247
+ it 'works in nested block' do
248
+ transproc = Class.new(klass).define! do
249
+ map_values do
250
+ is String, t(:custom, '_bar')
251
+ end
252
+ end.new
253
+
254
+ expect(transproc.call(a: 'foo', b: :symbol)).to eq(a: 'foo_bar', b: :symbol)
255
+ end
256
+ end
257
+
258
+ describe '#call' do
259
+ let(:container) do
260
+ Module.new do
261
+ extend Dry::Transformer::Registry
262
+
263
+ import Dry::Transformer::HashTransformations
264
+ import Dry::Transformer::ArrayTransformations
265
+ import Dry::Transformer::ClassTransformations
266
+ end
267
+ end
268
+
269
+ let(:klass) do
270
+ Class.new(Dry::Transformer[container]) do
271
+ define! do
272
+ map_array do
273
+ symbolize_keys
274
+ rename_keys user_name: :name
275
+ nest :address, [:city, :street, :zipcode]
276
+
277
+ map_value :address do
278
+ constructor_inject Test::Address
279
+ end
280
+
281
+ constructor_inject Test::User
282
+ end
283
+ end
284
+ end
285
+ end
286
+
287
+ let(:input) do
288
+ [
289
+ { 'user_name' => 'Jane',
290
+ 'city' => 'NYC',
291
+ 'street' => 'Street 1',
292
+ 'zipcode' => '123'
293
+ }
294
+ ]
295
+ end
296
+
297
+ let(:expected_output) do
298
+ [
299
+ Test::User.new(
300
+ name: 'Jane',
301
+ address: Test::Address.new(
302
+ city: 'NYC',
303
+ street: 'Street 1',
304
+ zipcode: '123'
305
+ )
306
+ )
307
+ ]
308
+ end
309
+
310
+ before do
311
+ module Test
312
+ class User < OpenStruct
313
+ include Dry::Equalizer(:name, :address)
314
+ end
315
+
316
+ class Address < OpenStruct
317
+ include Dry::Equalizer(:city, :street, :zipcode)
318
+ end
319
+ end
320
+ end
321
+
322
+ it "transforms input" do
323
+ expect(transformer.(input)).to eql(expected_output)
324
+ end
325
+
326
+ context 'with custom registry' do
327
+ let(:klass) do
328
+ Class.new(Dry::Transformer[registry]) do
329
+ define! do
330
+ append ' is awesome'
331
+ end
332
+ end
333
+ end
334
+
335
+ let(:registry) do
336
+ Module.new do
337
+ extend Dry::Transformer::Registry
338
+
339
+ def self.append(value, suffix)
340
+ value + suffix
341
+ end
342
+ end
343
+ end
344
+
345
+ it 'uses custom functions' do
346
+ expect(transformer.('transproc')).to eql('transproc is awesome')
347
+ end
348
+ end
349
+ end
350
+ end
@@ -0,0 +1,15 @@
1
+ RSpec.describe Dry::Transformer do
2
+ let(:container) { Module.new { extend Dry::Transformer::Registry } }
3
+ let(:klass) { Dry::Transformer[container] }
4
+ let(:transformer) { klass.new }
5
+
6
+ context 'when invalid method is used' do
7
+ it 'raises an error on initialization' do
8
+ klass.define! do
9
+ not_valid
10
+ end
11
+
12
+ expect { klass.new }.to raise_error(Dry::Transformer::Compiler::InvalidFunctionNameError, /not_valid/)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,25 @@
1
+ RSpec.describe Dry::Transformer, 'instance methods' do
2
+ subject(:transformer) do
3
+ Class.new(Dry::Transformer[registry]) do
4
+ define! do
5
+ map_array(&:capitalize)
6
+ end
7
+
8
+ def capitalize(input)
9
+ input.upcase
10
+ end
11
+ end.new
12
+ end
13
+
14
+ let(:registry) do
15
+ Module.new do
16
+ extend Dry::Transformer::Registry
17
+
18
+ import Dry::Transformer::ArrayTransformations
19
+ end
20
+ end
21
+
22
+ it 'registers a new transformation function' do
23
+ expect(transformer.call(%w[foo bar])).to eql(%w[FOO BAR])
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dry-transformer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Piotr Solnica
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-12-28 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Data transformation toolkit
14
+ email:
15
+ - piotr.solnica@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".codeclimate.yml"
21
+ - ".github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md"
22
+ - ".github/ISSUE_TEMPLATE/---bug-report.md"
23
+ - ".github/ISSUE_TEMPLATE/---feature-request.md"
24
+ - ".github/workflows/custom_ci.yml"
25
+ - ".github/workflows/docsite.yml"
26
+ - ".github/workflows/sync_configs.yml"
27
+ - ".gitignore"
28
+ - ".rspec"
29
+ - ".rubocop.yml"
30
+ - CHANGELOG.md
31
+ - CODE_OF_CONDUCT.md
32
+ - CONTRIBUTING.md
33
+ - Gemfile
34
+ - LICENSE
35
+ - README.md
36
+ - Rakefile
37
+ - docsite/source/built-in-transformations.html.md
38
+ - docsite/source/index.html.md
39
+ - docsite/source/transformation-objects.html.md
40
+ - docsite/source/using-standalone-functions.html.md
41
+ - dry-transformer.gemspec
42
+ - lib/dry-transformer.rb
43
+ - lib/dry/transformer.rb
44
+ - lib/dry/transformer/all.rb
45
+ - lib/dry/transformer/array.rb
46
+ - lib/dry/transformer/array/combine.rb
47
+ - lib/dry/transformer/class.rb
48
+ - lib/dry/transformer/coercions.rb
49
+ - lib/dry/transformer/compiler.rb
50
+ - lib/dry/transformer/composite.rb
51
+ - lib/dry/transformer/conditional.rb
52
+ - lib/dry/transformer/constants.rb
53
+ - lib/dry/transformer/error.rb
54
+ - lib/dry/transformer/function.rb
55
+ - lib/dry/transformer/hash.rb
56
+ - lib/dry/transformer/pipe.rb
57
+ - lib/dry/transformer/pipe/class_interface.rb
58
+ - lib/dry/transformer/pipe/dsl.rb
59
+ - lib/dry/transformer/proc.rb
60
+ - lib/dry/transformer/recursion.rb
61
+ - lib/dry/transformer/registry.rb
62
+ - lib/dry/transformer/store.rb
63
+ - lib/dry/transformer/version.rb
64
+ - spec/spec_helper.rb
65
+ - spec/unit/array/combine_spec.rb
66
+ - spec/unit/array_transformations_spec.rb
67
+ - spec/unit/class_transformations_spec.rb
68
+ - spec/unit/coercions_spec.rb
69
+ - spec/unit/conditional_spec.rb
70
+ - spec/unit/function_not_found_error_spec.rb
71
+ - spec/unit/function_spec.rb
72
+ - spec/unit/hash_transformations_spec.rb
73
+ - spec/unit/proc_transformations_spec.rb
74
+ - spec/unit/recursion_spec.rb
75
+ - spec/unit/registry_spec.rb
76
+ - spec/unit/store_spec.rb
77
+ - spec/unit/transformer/class_interface_spec.rb
78
+ - spec/unit/transformer/dsl_spec.rb
79
+ - spec/unit/transformer/instance_methods_spec.rb
80
+ homepage: https://dry-rb.org/gems/dry-transformer/
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ version: 2.3.0
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubygems_version: 3.0.3
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: Data transformation toolkit
103
+ test_files:
104
+ - spec/spec_helper.rb
105
+ - spec/unit/array/combine_spec.rb
106
+ - spec/unit/array_transformations_spec.rb
107
+ - spec/unit/class_transformations_spec.rb
108
+ - spec/unit/coercions_spec.rb
109
+ - spec/unit/conditional_spec.rb
110
+ - spec/unit/function_not_found_error_spec.rb
111
+ - spec/unit/function_spec.rb
112
+ - spec/unit/hash_transformations_spec.rb
113
+ - spec/unit/proc_transformations_spec.rb
114
+ - spec/unit/recursion_spec.rb
115
+ - spec/unit/registry_spec.rb
116
+ - spec/unit/store_spec.rb
117
+ - spec/unit/transformer/class_interface_spec.rb
118
+ - spec/unit/transformer/dsl_spec.rb
119
+ - spec/unit/transformer/instance_methods_spec.rb