transproc 0.4.0 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 01eecdd5844cf6c0c3b491536ceb189ad6790517
4
- data.tar.gz: c90ca55ae62ce7c30e182827695a299719725205
3
+ metadata.gz: 5fb2ca80c956f246b5277462bb1dcbf5c3dbd09d
4
+ data.tar.gz: 7ae7b71a375e026ca281fbb6906850e418ba2b21
5
5
  SHA512:
6
- metadata.gz: bc6de084dda67ce23903aa7f9fc55c070508828759f4a55b324089738a51e9919d2e69598664ee637185293ce8254a93b593b38b17550b57fe5644f2e3c625cd
7
- data.tar.gz: 4acba367f90300be3aee4dedb910db44ac0eed89efa998094e50d4b716f51c584c9e8db8f362670aad752febd70f1d95decefa107fefc2d5c7a9b0b72020d060
6
+ metadata.gz: d5fcb3e8a8229938d4f2cf16dab007eea0d3b7fbcb09c901c3a7aec87d5ceaa99ebd8e7254400409b4d540fb6426d5ba9d0b6182d36450d27a480430473c68c7
7
+ data.tar.gz: 708adcb9250af48b08950fdd85e82b6168d468c02b089544b97db283854be4852c37c347c4606779cee336bbf10ed6cc18bc4d595fea44db2fe5be7fd3dbb4b1
data/.travis.yml CHANGED
@@ -1,19 +1,18 @@
1
+ dist: trusty
1
2
  language: ruby
2
- sudo: false
3
+ sudo: required
3
4
  cache: bundler
4
- env:
5
- - CODECLIMATE_REPO_TOKEN=aead71de2239048f830499462c54b57dfc646f1d56ad5dcbbc3469a6ebaf97ca
6
5
  bundler_args: --without tools
7
6
  script: 'bundle exec rake spec'
7
+ after_success:
8
+ - '[ "$TRAVIS_RUBY_VERSION" = "2.3.1" ] && [ "$TRAVIS_BRANCH" = "master" ] && bundle exec codeclimate-test-reporter'
8
9
  rvm:
9
10
  - 2.0
11
+ - 2.2
10
12
  - 2.1
11
- - rbx-2
12
- - jruby
13
- - ruby-head
14
- matrix:
15
- allow_failures:
16
- - rvm: ruby-head
13
+ - 2.3.1
14
+ - rbx-3
15
+ - jruby-9.1.5.0
17
16
  notifications:
18
17
  webhooks:
19
18
  urls:
data/CHANGELOG.md CHANGED
@@ -1,4 +1,28 @@
1
- ## v0.3.3 to-be-released
1
+ ## v0.4.2 2017-01-12
2
+
3
+ ## Added
4
+
5
+ * prefix option to HashTransformations#unwrap and HashTransformations#unwrap! (AMHOL)
6
+
7
+ ## Fixed
8
+
9
+ * `map_array` won't cause a SystemStackError in case of gigantic arrays (solnic)
10
+
11
+ [Compare v0.4.1...v0.4.2](https://github.com/solnic/transproc/compare/v0.4.1...v0.4.2)
12
+
13
+ ## v0.4.1 2016-11-08
14
+
15
+ ## Added
16
+
17
+ * Class level transproc DSL (AMHOL)
18
+
19
+ ## Fixed
20
+
21
+ * Works on latest rubinius again (katafrakt)
22
+
23
+ [Compare v0.4.0...v0.4.1](https://github.com/solnic/transproc/compare/v0.4.0...v0.4.1)
24
+
25
+ ## v0.4.0 2015-11-23
2
26
 
3
27
  ## Fixed
4
28
 
@@ -8,7 +32,7 @@
8
32
 
9
33
  * `MalformedInputError` exception. Transproc doesn't catch and re-raise exceptions any longer (nepalez)
10
34
 
11
- [Compare v0.3.2...HEAD](https://github.com/solnic/transproc/compare/v0.3.2...HEAD)
35
+ [Compare v0.3.2...v0.4.0](https://github.com/solnic/transproc/compare/v0.3.2...v0.4.0)
12
36
 
13
37
  ## v0.3.2 2015-08-17
14
38
 
data/Gemfile CHANGED
@@ -4,13 +4,15 @@ gemspec
4
4
 
5
5
  group :test do
6
6
  gem 'equalizer'
7
- gem 'codeclimate-test-reporter', require: nil
8
7
 
9
8
  if RUBY_VERSION >= '2.1'
10
9
  gem 'anima'
11
10
  platform :mri do
12
11
  gem 'mutant', github: 'mbj/mutant', branch: 'master'
13
12
  gem 'mutant-rspec'
13
+
14
+ gem 'codeclimate-test-reporter', require: false
15
+ gem 'simplecov', require: false
14
16
  end
15
17
  else
16
18
  gem 'anima', '~> 0.2.0'
data/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [coveralls]: https://coveralls.io/r/solnic/transproc
6
6
  [inchpages]: http://inch-ci.org/github/solnic/transproc
7
7
 
8
- # Transproc [![Join the chat at https://gitter.im/solnic/transproc](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/solnic/transproc)
8
+ # Transproc
9
9
 
10
10
  [![Gem Version](https://badge.fury.io/rb/transproc.svg)][gem]
11
11
  [![Build Status](https://travis-ci.org/solnic/transproc.svg?branch=master)][travis]
@@ -38,7 +38,7 @@ module Transproc
38
38
  #
39
39
  # @api public
40
40
  def self.map_array(array, fn)
41
- map_array!(Array[*array], fn)
41
+ map_array!(array.dup, fn)
42
42
  end
43
43
 
44
44
  # Same as `map_array` but mutates the array
@@ -318,14 +318,19 @@ module Transproc
318
318
  # Transproc(:unwrap, :address, [:street, :zipcode])[address: { street: 'Street', zipcode: '123' }]
319
319
  # # => {street: "Street", zipcode: "123"}
320
320
  #
321
- # @param [Hash]
321
+ # @param [Hash] hash
322
+ # @param [Mixed] root The root key to unwrap values from
323
+ # @param [Array] keys The keys that should be unwrapped (optional)
324
+ # @param [Hash] options hash of options (optional)
325
+ # @option options [Boolean] :prefix if true, unwrapped keys will be prefixed
326
+ # with the root key followed by an underscore (_)
322
327
  #
323
328
  # @return [Hash]
324
329
  #
325
330
  # @api public
326
- def self.unwrap(hash, root, keys = nil)
331
+ def self.unwrap(hash, root, keys = nil, prefix: false)
327
332
  copy = Hash[hash].merge(root => Hash[hash[root]])
328
- unwrap!(copy, root, keys)
333
+ unwrap!(copy, root, keys, prefix: prefix)
329
334
  end
330
335
 
331
336
  # Same as `:unwrap` but mutates the hash
@@ -333,11 +338,23 @@ module Transproc
333
338
  # @see HashTransformations.unwrap
334
339
  #
335
340
  # @api public
336
- def self.unwrap!(hash, root, selected = nil)
341
+ def self.unwrap!(hash, root, selected = nil, prefix: false)
337
342
  if nested_hash = hash[root]
338
343
  keys = nested_hash.keys
339
344
  keys &= selected if selected
340
- hash.update(Hash[keys.zip(keys.map { |key| nested_hash.delete(key) })])
345
+ new_keys = if prefix
346
+ keys.map do |key|
347
+ if root.is_a?(::Symbol)
348
+ [root, key].join('_').to_sym
349
+ else
350
+ [root, key].join('_')
351
+ end
352
+ end
353
+ else
354
+ keys
355
+ end
356
+
357
+ hash.update(Hash[new_keys.zip(keys.map { |key| nested_hash.delete(key) })])
341
358
  hash.delete(root) if nested_hash.empty?
342
359
  end
343
360
 
@@ -0,0 +1,102 @@
1
+ module Transproc
2
+ class Transformer
3
+ # @api private
4
+ module ClassInterface
5
+ # Return a base Transproc::Transformer class with the
6
+ # container configured to the passed argument.
7
+ #
8
+ # @example
9
+ #
10
+ # class MyTransformer < Transproc::Transformer[Transproc]
11
+ # end
12
+ #
13
+ # @param [Transproc::Registry] container
14
+ # The container to resolve transprocs from
15
+ #
16
+ # @return [subclass of Transproc::Transformer]
17
+ #
18
+ # @api public
19
+ def [](container)
20
+ klass = Class.new(Transformer)
21
+ klass.container(container)
22
+ klass
23
+ end
24
+
25
+ # @api private
26
+ def inherited(subclass)
27
+ subclass.container(container)
28
+ end
29
+
30
+ # Get or set the container to resolve transprocs from.
31
+ #
32
+ # @example
33
+ #
34
+ # # Setter
35
+ # Transproc::Transformer.container(Transproc)
36
+ # # => Transproc
37
+ #
38
+ # # Getter
39
+ # Transproc::Transformer.container
40
+ # # => Transproc
41
+ #
42
+ # @param [Transproc::Registry] container
43
+ # The container to resolve transprocs from
44
+ #
45
+ # @return [Transproc::Registry]
46
+ #
47
+ # @api private
48
+ def container(container = ::Transproc::Undefined)
49
+ if container == ::Transproc::Undefined
50
+ @container
51
+ else
52
+ @container = container
53
+ end
54
+ end
55
+
56
+ # @api private
57
+ def method_missing(method, *args, &block)
58
+ if container.functions.has_key?(method)
59
+ if block_given?
60
+ transformations << container[
61
+ method,
62
+ *args,
63
+ create(container, &block).transproc
64
+ ]
65
+ else
66
+ transformations << container[method, *args]
67
+ end
68
+ else
69
+ super
70
+ end
71
+ end
72
+
73
+ # @api private
74
+ def respond_to_missing?(method, _include_private = false)
75
+ container.functions.has_key?(method) || super
76
+ end
77
+
78
+ # @api private
79
+ def transproc
80
+ transformations.reduce(:>>)
81
+ end
82
+
83
+ private
84
+ # An array containing the transformation pipeline
85
+ #
86
+ # @api private
87
+ def transformations
88
+ @transformations ||= []
89
+ end
90
+
91
+ # Create and return a new instance of Transproc::Transformer
92
+ # evaluating the block argument as the class body
93
+ #
94
+ # @api private
95
+ def create(container, &block)
96
+ klass = self[container]
97
+ klass.instance_eval(&block)
98
+ klass
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,69 @@
1
+ require 'transproc/transformer/class_interface'
2
+
3
+ module Transproc
4
+ # Transfomer class for defining transprocs with a class DSL.
5
+ #
6
+ # @example
7
+ # require 'anima'
8
+ # require 'transproc/all'
9
+ #
10
+ # class User
11
+ # include Anima.new(:name, :address)
12
+ # end
13
+ #
14
+ # class Address
15
+ # include Anima.new(:city, :street, :zipcode)
16
+ # end
17
+ #
18
+ # class UsersMapper < Transproc::Transformer
19
+ # map_array do
20
+ # symbolize_keys
21
+ # rename_keys user_name: :name
22
+ # nest :address, %i(city street zipcode)
23
+ # map_value :address do
24
+ # constructor_inject Address
25
+ # end
26
+ # constructor_inject User
27
+ # end
28
+ # end
29
+ #
30
+ # UsersMapper.new.call(
31
+ # [
32
+ # { 'user_name' => 'Jane',
33
+ # 'city' => 'NYC',
34
+ # 'street' => 'Street 1',
35
+ # 'zipcode' => '123'
36
+ # }
37
+ # ]
38
+ # )
39
+ # # => [
40
+ # #<User
41
+ # name="Jane"
42
+ # address=#<Address city="NYC" street="Street 1" zipcode="123">>
43
+ # ]
44
+ #
45
+ # @api public
46
+ class Transformer
47
+ extend ClassInterface
48
+
49
+ # Execute the transformation pipeline with the given input.
50
+ #
51
+ # @example
52
+ #
53
+ # class SymbolizeKeys < Transproc::Transformer
54
+ # symbolize_keys
55
+ # end
56
+ #
57
+ # SymbolizeKeys.new.call('name' => 'Jane')
58
+ # # => {:name=>"Jane"}
59
+ #
60
+ # @param [mixed] input The input to pass to the pipeline
61
+ #
62
+ # @return [mixed] output The output returned from the pipeline
63
+ #
64
+ # @api public
65
+ def call(input)
66
+ self.class.transproc.call(input)
67
+ end
68
+ end
69
+ end
@@ -1,3 +1,3 @@
1
1
  module Transproc
2
- VERSION = '0.4.0'.freeze
2
+ VERSION = '0.4.2'.freeze
3
3
  end
data/lib/transproc.rb CHANGED
@@ -5,9 +5,12 @@ require 'transproc/composer'
5
5
  require 'transproc/error'
6
6
  require 'transproc/store'
7
7
  require 'transproc/registry'
8
+ require 'transproc/transformer'
8
9
  require 'transproc/support/deprecations'
9
10
 
10
11
  module Transproc
12
+ Undefined = Object.new.freeze
13
+ Transformer.container(self)
11
14
  # Function registry
12
15
  #
13
16
  # @api private
@@ -75,10 +78,10 @@ def Transproc(fn, *args)
75
78
  case fn
76
79
  when Proc then Transproc::Function.new(fn, args: args)
77
80
  when Symbol
78
- fun = Transproc[fn, *args]
79
- case fun
80
- when Transproc::Function, Transproc::Composite then fun
81
- else Transproc::Function.new(fun, args: args)
81
+ func = Transproc[fn, *args]
82
+ case func
83
+ when Transproc::Function, Transproc::Composite then func
84
+ else Transproc::Function.new(func, args: args)
82
85
  end
83
86
  end
84
87
  end
data/spec/spec_helper.rb CHANGED
@@ -1,6 +1,8 @@
1
- if RUBY_ENGINE == 'rbx'
2
- require 'codeclimate-test-reporter'
3
- CodeClimate::TestReporter.start
1
+ if RUBY_ENGINE == 'ruby' && RUBY_VERSION == '2.3.1'
2
+ require 'simplecov'
3
+ SimpleCov.start do
4
+ add_filter '/spec/'
5
+ end
4
6
  end
5
7
 
6
8
  require 'equalizer'
@@ -132,6 +132,14 @@ describe Transproc::ArrayTransformations do
132
132
  expect(map[input]).to eql(output)
133
133
  expect(input).to eql(original)
134
134
  end
135
+
136
+ it 'handles huge arrays' do
137
+ map = described_class.t(:map_array, hashes[:symbolize_keys])
138
+
139
+ input = 138706.times.map { |i| { 'key' => i } }
140
+
141
+ expect { map[input] }.to_not raise_error(SystemStackError, /stack level too deep/)
142
+ end
135
143
  end
136
144
 
137
145
  describe '.map_array!' do
@@ -318,6 +318,28 @@ describe Transproc::HashTransformations do
318
318
 
319
319
  expect(input).to eql(output)
320
320
  end
321
+
322
+ it 'prefixes unwrapped keys and retains root string type if prefix option is truthy' do
323
+ unwrap = described_class.t(:unwrap!, 'wrapped', prefix: true)
324
+
325
+ input = { 'wrapped' => { one: nil, two: false } }
326
+ output = { 'wrapped_one' => nil, 'wrapped_two' => false }
327
+
328
+ unwrap[input]
329
+
330
+ expect(input).to eql(output)
331
+ end
332
+
333
+ it 'prefixes unwrapped keys and retains root type if prefix option is truthy' do
334
+ unwrap = described_class.t(:unwrap!, :wrapped, prefix: true)
335
+
336
+ input = { wrapped: { 'one' => nil, 'two' => false } }
337
+ output = { wrapped_one: nil, wrapped_two: false }
338
+
339
+ unwrap[input]
340
+
341
+ expect(input).to eql(output)
342
+ end
321
343
  end
322
344
 
323
345
  describe '.unwrap' do
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ describe Transproc::Transformer do
4
+ let(:klass) { Class.new(Transproc::Transformer) }
5
+ let(:transformer) { klass.new }
6
+
7
+ describe '.container' do
8
+ context 'without setter argument' do
9
+ subject! { klass.container }
10
+
11
+ it 'defaults to Transproc' do
12
+ is_expected.to eq(Transproc)
13
+ end
14
+ end
15
+
16
+ context 'with setter argument' do
17
+ subject! { klass.container({}) }
18
+
19
+ it 'sets and returns the container' do
20
+ expect(klass.container).to eq({})
21
+ end
22
+ end
23
+ end
24
+
25
+ describe '.[]' do
26
+ let(:container) { double('Transproc') }
27
+
28
+ subject!(:klass) { Transproc::Transformer[container] }
29
+
30
+ it { expect(klass.container).to eq(container) }
31
+ it { is_expected.to be_a(::Class) }
32
+ end
33
+
34
+ describe '#call' do
35
+ let(:klass) do
36
+ Class.new(Transproc::Transformer) do
37
+ map_array do
38
+ symbolize_keys
39
+ rename_keys user_name: :name
40
+ nest :address, [:city, :street, :zipcode]
41
+ map_value :address do
42
+ constructor_inject Test::Address
43
+ end
44
+ constructor_inject Test::User
45
+ end
46
+ end
47
+ end
48
+ let(:input) do
49
+ [
50
+ { 'user_name' => 'Jane',
51
+ 'city' => 'NYC',
52
+ 'street' => 'Street 1',
53
+ 'zipcode' => '123'
54
+ }
55
+ ]
56
+ end
57
+ let(:output) do
58
+ [
59
+ Test::User.new(
60
+ name: 'Jane',
61
+ address: Test::Address.new(
62
+ city: 'NYC',
63
+ street: 'Street 1',
64
+ zipcode: '123'
65
+ )
66
+ )
67
+ ]
68
+ end
69
+
70
+ before do
71
+ module Test
72
+ class User
73
+ include Anima.new(:name, :address)
74
+ end
75
+
76
+ class Address
77
+ include Anima.new(:city, :street, :zipcode)
78
+ end
79
+ end
80
+ end
81
+
82
+ subject! { transformer.call(input) }
83
+
84
+ it { is_expected.to eq(output) }
85
+ end
86
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: transproc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-23 00:00:00.000000000 Z
11
+ date: 2017-01-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -88,6 +88,8 @@ files:
88
88
  - lib/transproc/rspec.rb
89
89
  - lib/transproc/store.rb
90
90
  - lib/transproc/support/deprecations.rb
91
+ - lib/transproc/transformer.rb
92
+ - lib/transproc/transformer/class_interface.rb
91
93
  - lib/transproc/version.rb
92
94
  - rakelib/mutant.rake
93
95
  - rakelib/rubocop.rake
@@ -105,6 +107,7 @@ files:
105
107
  - spec/unit/recursion_spec.rb
106
108
  - spec/unit/registry_spec.rb
107
109
  - spec/unit/store_spec.rb
110
+ - spec/unit/transformer_spec.rb
108
111
  - spec/unit/transproc_spec.rb
109
112
  - transproc.gemspec
110
113
  homepage: http://solnic.github.io/transproc/
@@ -127,7 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
127
130
  version: '0'
128
131
  requirements: []
129
132
  rubyforge_project:
130
- rubygems_version: 2.4.5
133
+ rubygems_version: 2.5.1
131
134
  signing_key:
132
135
  specification_version: 4
133
136
  summary: Transform Ruby objects in functional style
@@ -146,5 +149,5 @@ test_files:
146
149
  - spec/unit/recursion_spec.rb
147
150
  - spec/unit/registry_spec.rb
148
151
  - spec/unit/store_spec.rb
152
+ - spec/unit/transformer_spec.rb
149
153
  - spec/unit/transproc_spec.rb
150
- has_rdoc: