transproc 1.0.2 → 1.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.
- checksums.yaml +5 -5
- data/.codeclimate.yml +15 -0
- data/.gitignore +3 -0
- data/.travis.yml +14 -10
- data/CHANGELOG.md +22 -1
- data/Gemfile +5 -18
- data/README.md +3 -5
- data/Rakefile +1 -0
- data/lib/transproc.rb +3 -4
- data/lib/transproc/all.rb +2 -0
- data/lib/transproc/array.rb +5 -3
- data/lib/transproc/array/combine.rb +2 -0
- data/lib/transproc/class.rb +2 -0
- data/lib/transproc/coercions.rb +2 -0
- data/lib/transproc/compiler.rb +45 -0
- data/lib/transproc/composer.rb +2 -0
- data/lib/transproc/composite.rb +2 -0
- data/lib/transproc/conditional.rb +2 -0
- data/lib/transproc/constants.rb +5 -0
- data/lib/transproc/error.rb +2 -0
- data/lib/transproc/function.rb +2 -0
- data/lib/transproc/functions.rb +2 -0
- data/lib/transproc/hash.rb +83 -34
- data/lib/transproc/proc.rb +2 -0
- data/lib/transproc/recursion.rb +2 -0
- data/lib/transproc/registry.rb +1 -0
- data/lib/transproc/store.rb +1 -0
- data/lib/transproc/support/deprecations.rb +2 -0
- data/lib/transproc/transformer.rb +7 -1
- data/lib/transproc/transformer/class_interface.rb +31 -65
- data/lib/transproc/transformer/deprecated/class_interface.rb +80 -0
- data/lib/transproc/transformer/dsl.rb +51 -0
- data/lib/transproc/version.rb +3 -1
- data/spec/spec_helper.rb +15 -11
- data/spec/unit/array/combine_spec.rb +3 -1
- data/spec/unit/array_transformations_spec.rb +1 -1
- data/spec/unit/class_transformations_spec.rb +9 -6
- data/spec/unit/coercions_spec.rb +1 -1
- data/spec/unit/composer_spec.rb +1 -1
- data/spec/unit/conditional_spec.rb +1 -1
- data/spec/unit/function_not_found_error_spec.rb +1 -1
- data/spec/unit/function_spec.rb +1 -1
- data/spec/unit/hash_transformations_spec.rb +12 -1
- data/spec/unit/proc_transformations_spec.rb +3 -1
- data/spec/unit/recursion_spec.rb +1 -1
- data/spec/unit/registry_spec.rb +1 -1
- data/spec/unit/store_spec.rb +2 -1
- data/spec/unit/transformer/class_interface_spec.rb +364 -0
- data/spec/unit/transformer/dsl_spec.rb +15 -0
- data/spec/unit/transformer/instance_methods_spec.rb +25 -0
- data/spec/unit/transformer_spec.rb +128 -40
- data/spec/unit/transproc_spec.rb +1 -1
- data/transproc.gemspec +1 -5
- metadata +16 -54
- data/.rubocop.yml +0 -66
- data/.rubocop_todo.yml +0 -11
- data/rakelib/mutant.rake +0 -16
- data/rakelib/rubocop.rake +0 -18
- data/spec/support/mutant.rb +0 -10
data/spec/unit/function_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
describe Transproc::HashTransformations do
|
4
4
|
describe '.map_keys' do
|
@@ -50,6 +50,17 @@ describe Transproc::HashTransformations do
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
describe '.deep_stringify_keys' do
|
54
|
+
it 'returns a new hash with symbolized keys' do
|
55
|
+
stringify_keys = described_class.t(:deep_stringify_keys)
|
56
|
+
|
57
|
+
input = { foo: 'bar', baz: [{ one: 1 }, 'two'] }
|
58
|
+
output = { 'foo' => 'bar', 'baz' => [{ 'one' => 1 }, 'two'] }
|
59
|
+
|
60
|
+
expect(stringify_keys[input]).to eql(output)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
53
64
|
it { expect(described_class).not_to be_contain(:stringify_keys!) }
|
54
65
|
|
55
66
|
describe '.map_values' do
|
data/spec/unit/recursion_spec.rb
CHANGED
data/spec/unit/registry_spec.rb
CHANGED
data/spec/unit/store_spec.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
describe Transproc::Store do
|
4
4
|
let(:store) { described_class.new methods }
|
@@ -186,6 +186,7 @@ describe Transproc::Store do
|
|
186
186
|
end
|
187
187
|
|
188
188
|
it 'skips Transproc::Registry singleton methods' do
|
189
|
+
pending "this fails for some reason" if RUBY_ENGINE == "jruby"
|
189
190
|
expect(subject.methods.keys).to contain_exactly(:foo, :bar, :baz, :qux)
|
190
191
|
end
|
191
192
|
end
|
@@ -0,0 +1,364 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ostruct'
|
4
|
+
require 'dry/equalizer'
|
5
|
+
|
6
|
+
describe Transproc::Transformer do
|
7
|
+
let(:container) { Module.new { extend Transproc::Registry } }
|
8
|
+
let(:klass) { Transproc::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(Transproc::Transformer) do
|
14
|
+
import Transproc::ArrayTransformations
|
15
|
+
import Transproc::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(Transproc::Transformer) do
|
31
|
+
import Transproc::ArrayTransformations
|
32
|
+
import Transproc::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 Transproc::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(Transproc::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('Transproc') }
|
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 < Transproc::Transformer
|
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(Transproc::Transformer[container]) do
|
134
|
+
container.import Transproc::Coercions
|
135
|
+
container.import Transproc::HashTransformations
|
136
|
+
|
137
|
+
map_value :attr, t(:to_symbol)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
it "inherits parent's transproc" do
|
142
|
+
expect(klass[container].new.transproc).to eql(klass.new.transproc)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
describe '.define' do
|
148
|
+
let(:container) do
|
149
|
+
Module.new do
|
150
|
+
extend Transproc::Registry
|
151
|
+
|
152
|
+
import Transproc::HashTransformations
|
153
|
+
|
154
|
+
def self.to_symbol(v)
|
155
|
+
v.to_sym
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
let(:klass) { Transproc::Transformer[container] }
|
161
|
+
|
162
|
+
it 'defines anonymous transproc' do
|
163
|
+
transproc = klass.define do
|
164
|
+
map_value(:attr, t(:to_symbol))
|
165
|
+
end
|
166
|
+
|
167
|
+
expect(transproc[attr: 'abc']).to eq(attr: :abc)
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'has .build alias' do
|
171
|
+
transproc = klass.build do
|
172
|
+
map_value(:attr, t(:to_symbol))
|
173
|
+
end
|
174
|
+
|
175
|
+
expect(transproc[attr: 'abc']).to eq(attr: :abc)
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'does not affect original transformer' do
|
179
|
+
klass.define do
|
180
|
+
map_value(:attr, :to_sym.to_proc)
|
181
|
+
end
|
182
|
+
|
183
|
+
expect(klass.new.transproc).to be_nil
|
184
|
+
end
|
185
|
+
|
186
|
+
context 'with custom container' do
|
187
|
+
let(:container) do
|
188
|
+
Module.new do
|
189
|
+
extend Transproc::Registry
|
190
|
+
|
191
|
+
def self.arbitrary(value, fn)
|
192
|
+
fn[value]
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
let(:klass) { described_class[container] }
|
198
|
+
|
199
|
+
it 'uses a container from the transformer' do
|
200
|
+
transproc = klass.define! do
|
201
|
+
arbitrary ->(v) { v + 1 }
|
202
|
+
end.new
|
203
|
+
|
204
|
+
expect(transproc.call(2)).to eq 3
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context 'with predefined transformer' do
|
209
|
+
let(:klass) do
|
210
|
+
Class.new(described_class[container]) do
|
211
|
+
define! do
|
212
|
+
map_value :attr, ->(v) { v + 1 }
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'builds transformation from the DSL definition' do
|
218
|
+
transproc = klass.new
|
219
|
+
|
220
|
+
expect(transproc.call(attr: 2)).to eql(attr: 3)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
describe '.t' do
|
226
|
+
subject(:klass) { Transproc::Transformer[container] }
|
227
|
+
|
228
|
+
let(:container) do
|
229
|
+
Module.new do
|
230
|
+
extend Transproc::Registry
|
231
|
+
|
232
|
+
import Transproc::HashTransformations
|
233
|
+
import Transproc::Conditional
|
234
|
+
|
235
|
+
def self.custom(value, suffix)
|
236
|
+
value + suffix
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'returns a registed function' do
|
242
|
+
expect(klass.t(:custom, '_bar')).to eql(container[:custom, '_bar'])
|
243
|
+
end
|
244
|
+
|
245
|
+
it 'is useful in DSL' do
|
246
|
+
transproc = Class.new(klass) do
|
247
|
+
map_value :a, t(:custom, '_bar')
|
248
|
+
end.new
|
249
|
+
|
250
|
+
expect(transproc.call(a: 'foo')).to eq(a: 'foo_bar')
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'works in nested block' do
|
254
|
+
transproc = Class.new(klass) do
|
255
|
+
map_values do
|
256
|
+
is String, t(:custom, '_bar')
|
257
|
+
end
|
258
|
+
end.new
|
259
|
+
|
260
|
+
expect(transproc.call(a: 'foo', b: :symbol)).to eq(a: 'foo_bar', b: :symbol)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
describe '.method_missing' do
|
265
|
+
it 'responds to missing when there is a corresponding function' do
|
266
|
+
container.import Transproc::HashTransformations
|
267
|
+
|
268
|
+
expect(klass.method(:map_values)).to be_a(Method)
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'raises when there is no corresponding function or instance method' do
|
272
|
+
expect { klass.not_here }.to raise_error(NoMethodError, /not_here/)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
describe '#call' do
|
277
|
+
let(:container) do
|
278
|
+
Module.new do
|
279
|
+
extend Transproc::Registry
|
280
|
+
|
281
|
+
import Transproc::HashTransformations
|
282
|
+
import Transproc::ArrayTransformations
|
283
|
+
import Transproc::ClassTransformations
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
let(:klass) do
|
288
|
+
Class.new(Transproc::Transformer[container]) do
|
289
|
+
map_array do
|
290
|
+
symbolize_keys
|
291
|
+
rename_keys user_name: :name
|
292
|
+
nest :address, [:city, :street, :zipcode]
|
293
|
+
|
294
|
+
map_value :address do
|
295
|
+
constructor_inject Test::Address
|
296
|
+
end
|
297
|
+
|
298
|
+
constructor_inject Test::User
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
let(:input) do
|
304
|
+
[
|
305
|
+
{ 'user_name' => 'Jane',
|
306
|
+
'city' => 'NYC',
|
307
|
+
'street' => 'Street 1',
|
308
|
+
'zipcode' => '123'
|
309
|
+
}
|
310
|
+
]
|
311
|
+
end
|
312
|
+
|
313
|
+
let(:expected_output) do
|
314
|
+
[
|
315
|
+
Test::User.new(
|
316
|
+
name: 'Jane',
|
317
|
+
address: Test::Address.new(
|
318
|
+
city: 'NYC',
|
319
|
+
street: 'Street 1',
|
320
|
+
zipcode: '123'
|
321
|
+
)
|
322
|
+
)
|
323
|
+
]
|
324
|
+
end
|
325
|
+
|
326
|
+
before do
|
327
|
+
module Test
|
328
|
+
class User < OpenStruct
|
329
|
+
include Dry::Equalizer(:name, :address)
|
330
|
+
end
|
331
|
+
|
332
|
+
class Address < OpenStruct
|
333
|
+
include Dry::Equalizer(:city, :street, :zipcode)
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
it "transforms input" do
|
339
|
+
expect(transformer.(input)).to eql(expected_output)
|
340
|
+
end
|
341
|
+
|
342
|
+
context 'with custom registry' do
|
343
|
+
let(:klass) do
|
344
|
+
Class.new(Transproc::Transformer[registry]) do
|
345
|
+
append ' is awesome'
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
let(:registry) do
|
350
|
+
Module.new do
|
351
|
+
extend Transproc::Registry
|
352
|
+
|
353
|
+
def self.append(value, suffix)
|
354
|
+
value + suffix
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
it 'uses custom functions' do
|
360
|
+
expect(transformer.('transproc')).to eql('transproc is awesome')
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
RSpec.describe Transproc::Transformer do
|
2
|
+
let(:container) { Module.new { extend Transproc::Registry } }
|
3
|
+
let(:klass) { Transproc::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(Transproc::Compiler::InvalidFunctionNameError, /not_valid/)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
RSpec.describe Transproc::Transformer, 'instance methods' do
|
2
|
+
subject(:transformer) do
|
3
|
+
Class.new(Transproc::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 Transproc::Registry
|
17
|
+
|
18
|
+
import Transproc::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
|