transproc 0.4.0 → 0.4.2

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 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: