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.
Files changed (59) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +15 -0
  3. data/.gitignore +3 -0
  4. data/.travis.yml +14 -10
  5. data/CHANGELOG.md +22 -1
  6. data/Gemfile +5 -18
  7. data/README.md +3 -5
  8. data/Rakefile +1 -0
  9. data/lib/transproc.rb +3 -4
  10. data/lib/transproc/all.rb +2 -0
  11. data/lib/transproc/array.rb +5 -3
  12. data/lib/transproc/array/combine.rb +2 -0
  13. data/lib/transproc/class.rb +2 -0
  14. data/lib/transproc/coercions.rb +2 -0
  15. data/lib/transproc/compiler.rb +45 -0
  16. data/lib/transproc/composer.rb +2 -0
  17. data/lib/transproc/composite.rb +2 -0
  18. data/lib/transproc/conditional.rb +2 -0
  19. data/lib/transproc/constants.rb +5 -0
  20. data/lib/transproc/error.rb +2 -0
  21. data/lib/transproc/function.rb +2 -0
  22. data/lib/transproc/functions.rb +2 -0
  23. data/lib/transproc/hash.rb +83 -34
  24. data/lib/transproc/proc.rb +2 -0
  25. data/lib/transproc/recursion.rb +2 -0
  26. data/lib/transproc/registry.rb +1 -0
  27. data/lib/transproc/store.rb +1 -0
  28. data/lib/transproc/support/deprecations.rb +2 -0
  29. data/lib/transproc/transformer.rb +7 -1
  30. data/lib/transproc/transformer/class_interface.rb +31 -65
  31. data/lib/transproc/transformer/deprecated/class_interface.rb +80 -0
  32. data/lib/transproc/transformer/dsl.rb +51 -0
  33. data/lib/transproc/version.rb +3 -1
  34. data/spec/spec_helper.rb +15 -11
  35. data/spec/unit/array/combine_spec.rb +3 -1
  36. data/spec/unit/array_transformations_spec.rb +1 -1
  37. data/spec/unit/class_transformations_spec.rb +9 -6
  38. data/spec/unit/coercions_spec.rb +1 -1
  39. data/spec/unit/composer_spec.rb +1 -1
  40. data/spec/unit/conditional_spec.rb +1 -1
  41. data/spec/unit/function_not_found_error_spec.rb +1 -1
  42. data/spec/unit/function_spec.rb +1 -1
  43. data/spec/unit/hash_transformations_spec.rb +12 -1
  44. data/spec/unit/proc_transformations_spec.rb +3 -1
  45. data/spec/unit/recursion_spec.rb +1 -1
  46. data/spec/unit/registry_spec.rb +1 -1
  47. data/spec/unit/store_spec.rb +2 -1
  48. data/spec/unit/transformer/class_interface_spec.rb +364 -0
  49. data/spec/unit/transformer/dsl_spec.rb +15 -0
  50. data/spec/unit/transformer/instance_methods_spec.rb +25 -0
  51. data/spec/unit/transformer_spec.rb +128 -40
  52. data/spec/unit/transproc_spec.rb +1 -1
  53. data/transproc.gemspec +1 -5
  54. metadata +16 -54
  55. data/.rubocop.yml +0 -66
  56. data/.rubocop_todo.yml +0 -11
  57. data/rakelib/mutant.rake +0 -16
  58. data/rakelib/rubocop.rake +0 -18
  59. data/spec/support/mutant.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 64dbe08d203b9ac87e38de4e8f46dde2899d5cfc
4
- data.tar.gz: 6c4e2cd21783a8fea8e3f4ebfe05656ca8e1cd73
2
+ SHA256:
3
+ metadata.gz: bd2c6ee5f665083297c1fcfd89a5e37d2284e498d6bc3953b88314e9513cbf43
4
+ data.tar.gz: 6b9a5f270234b26b479095c66187f91fcca9428afb605f75aa9f51b890976eef
5
5
  SHA512:
6
- metadata.gz: e46205bc38e87eb30fa40d1466bfb0f1cff727a945d985f22bb17ae9ac2c3be4910363403c08f2092514f15c2de6d06b8d5863ec9db530fa600465ea9782c601
7
- data.tar.gz: 964e3d01aa66e74542a79ea9844977c299cab95984959d066336b3203b67364e4728938e5412a9cde5acbc7661d5f5d744e3d42d17dc89574856f4d56e8d1aff
6
+ metadata.gz: 1c79034b27d317e52be52e98eb0cce057400efc99c05203e16fd39112a911d0bb568511d58dd138bbc32568cf8951fab586dcdf90e3c9bff434b6fd93347044b
7
+ data.tar.gz: a2086dc6a92e03837412d7665869c1ad516a3a5ce097db1b31224936d767af048eadb32c86e8e0bcf815ef38445f9aed9a878b5f5ca97b5559b3194efa567aec
@@ -0,0 +1,15 @@
1
+ version: "2"
2
+
3
+ prepare:
4
+ fetch:
5
+ - url: "https://raw.githubusercontent.com/dry-rb/devtools/master/.rubocop.yml"
6
+ path: ".rubocop.yml"
7
+
8
+ exclude_patterns:
9
+ - "benchmarks/"
10
+ - "examples/"
11
+ - "spec/"
12
+
13
+ plugins:
14
+ rubocop:
15
+ enabled: true
data/.gitignore CHANGED
@@ -13,3 +13,6 @@
13
13
  *.o
14
14
  *.a
15
15
  mkmf.log
16
+ .rubocop.yml
17
+ .rubocop_todo.yml
18
+
@@ -1,18 +1,22 @@
1
- dist: trusty
2
1
  language: ruby
3
- sudo: required
4
2
  cache: bundler
5
3
  bundler_args: --without tools
4
+ before_script:
5
+ - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
6
+ - chmod +x ./cc-test-reporter
7
+ - ./cc-test-reporter before-build
8
+ after_script:
9
+ - "[ -d coverage ] && ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT"
6
10
  script: 'bundle exec rake spec'
7
- after_success:
8
- - '[ "$TRAVIS_RUBY_VERSION" = "2.3.1" ] && [ "$TRAVIS_BRANCH" = "master" ] && bundle exec codeclimate-test-reporter'
9
11
  rvm:
10
- - 2.0
11
- - 2.2
12
- - 2.1
13
- - 2.3.1
14
- - rbx-3
15
- - jruby-9.1.5.0
12
+ - 2.3.8
13
+ - 2.6.3
14
+ - 2.5.5
15
+ - 2.4.6
16
+ - jruby-9.2.7.0
17
+ env:
18
+ global:
19
+ - COVERAGE='true'
16
20
  notifications:
17
21
  webhooks:
18
22
  urls:
@@ -1,3 +1,24 @@
1
+ # v1.1.0 2019-07-18
2
+
3
+ This is the last transproc release before the project will be forked to `dry-transformer`.
4
+
5
+ ## Added
6
+
7
+ * New DSL for defining transformers using `define!` method, which now supports instance methods as transformation functions (@solnic)
8
+ * Simplified transformer class definition - registry is auto-configured and you can use `import` at the transformer class level (@solnic)
9
+ * New `HashTransformation.deep_stringify_keys` function
10
+
11
+ [Compare v1.0.3...v1.1.0](https://github.com/solnic/transproc/compare/v1.0.3...v1.1.0)
12
+
13
+ # v1.0.3 2018-12-01
14
+
15
+ ## Changed
16
+
17
+ * [BREAKING] Added minimal Ruby version to the gemspec file. Transproc now works with Ruby 2.3 and above (flash-gordon)
18
+ * Performance improvements introduced by using new built-in methods in `Hash` (v-kolesnikov + flash-gordon)
19
+
20
+ [Compare v1.0.2...v1.0.3](https://github.com/solnic/transproc/compare/v1.0.2...v1.0.3)
21
+
1
22
  # v1.0.2 2017-02-25
2
23
 
3
24
  ## Fixed
@@ -8,7 +29,7 @@
8
29
 
9
30
  # v1.0.1 2017-02-25
10
31
 
11
- * `combine` is now multiple times faster, depending on the level of nesting (Kukunin + splattael)
32
+ * `combine` is now multiple times faster, depending on the level of nesting (Kukunin + splattael)
12
33
  * `nest` (thus `wrap` too) is ~2x faster now (solnic)
13
34
 
14
35
  [Compare v1.0.0...v1.0.1](https://github.com/solnic/transproc/compare/v1.0.0...v1.0.1)
data/Gemfile CHANGED
@@ -2,28 +2,15 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- group :test do
6
- gem 'equalizer'
5
+ gem 'rake'
6
+ gem 'rspec', '~> 3.8'
7
+ gem 'dry-equalizer', '~> 0.2'
7
8
 
8
- if RUBY_VERSION >= '2.1'
9
- gem 'anima'
10
- platform :mri do
11
- gem 'mutant', github: 'mbj/mutant', branch: 'master'
12
- gem 'mutant-rspec'
13
-
14
- gem 'codeclimate-test-reporter', require: false
15
- gem 'simplecov', require: false
16
- end
17
- else
18
- gem 'anima', '~> 0.2.0'
19
- end
9
+ platform :mri do
10
+ gem 'simplecov', require: false
20
11
  end
21
12
 
22
13
  group :tools do
23
- gem 'rubocop', '~> 0.30.0'
24
14
  gem 'byebug', platform: :mri
25
15
  gem 'benchmark-ips'
26
- gem 'guard'
27
- gem 'guard-rspec'
28
- gem 'guard-rubocop'
29
16
  end
data/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
  [gem]: https://rubygems.org/gems/transproc
2
2
  [travis]: https://travis-ci.org/solnic/transproc
3
- [gemnasium]: https://gemnasium.com/solnic/transproc
4
3
  [codeclimate]: https://codeclimate.com/github/solnic/transproc
5
4
  [coveralls]: https://coveralls.io/r/solnic/transproc
6
5
  [inchpages]: http://inch-ci.org/github/solnic/transproc
@@ -9,7 +8,6 @@
9
8
 
10
9
  [![Gem Version](https://badge.fury.io/rb/transproc.svg)][gem]
11
10
  [![Build Status](https://travis-ci.org/solnic/transproc.svg?branch=master)][travis]
12
- [![Dependency Status](https://gemnasium.com/solnic/transproc.svg)][gemnasium]
13
11
  [![Code Climate](https://codeclimate.com/github/solnic/transproc/badges/gpa.svg)][codeclimate]
14
12
  [![Test Coverage](https://codeclimate.com/github/solnic/transproc/badges/coverage.svg)][codeclimate]
15
13
  [![Inline docs](http://inch-ci.org/github/solnic/transproc.svg?branch=master)][inchpages]
@@ -197,9 +195,9 @@ transformation = t(:camel_case)
197
195
  transformation.call 'i_am_a_camel'
198
196
  # => "IAmACamel"
199
197
 
200
- transformation = t(:map_array, t(:symbolize_keys)
201
- .>> t(:rename_keys, user_name: :user))
202
- .>> t(:wrap, :address, [:city, :street, :zipcode])
198
+ transformation = t(:map_array, (
199
+ t(:symbolize_keys).>> t(:rename_keys, user_name: :user)
200
+ )).>> t(:wrap, :address, [:city, :street, :zipcode])
203
201
 
204
202
  transformation.call(
205
203
  [
data/Rakefile CHANGED
@@ -1,3 +1,4 @@
1
+ require 'bundler/gem_tasks'
1
2
  require 'rspec/core/rake_task'
2
3
 
3
4
  task default: :spec
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'transproc/version'
4
+ require 'transproc/constants'
2
5
  require 'transproc/function'
3
6
  require 'transproc/functions'
4
7
  require 'transproc/composer'
@@ -8,9 +11,5 @@ require 'transproc/registry'
8
11
  require 'transproc/transformer'
9
12
  require 'transproc/support/deprecations'
10
13
 
11
- module Transproc
12
- Undefined = Object.new.freeze
13
- end
14
-
15
14
  require 'transproc/array'
16
15
  require 'transproc/hash'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'transproc'
2
4
 
3
5
  require 'transproc/class'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'transproc/coercions'
2
4
  require 'transproc/hash'
3
5
  require 'transproc/array/combine'
@@ -65,7 +67,7 @@ module Transproc
65
67
  # Group array values using provided root key and value keys
66
68
  #
67
69
  # @example
68
- # fn = Transproc(:group, :tags, [:tag_name])
70
+ # fn = Transproc(:group, :tags, [:tag])
69
71
  #
70
72
  # fn.call [
71
73
  # { task: 'Group it', tag: 'task' },
@@ -98,10 +100,10 @@ module Transproc
98
100
  # Ungroup array values using provided root key and value keys
99
101
  #
100
102
  # @example
101
- # fn = Transproc(:group, :tags, [:tag_name])
103
+ # fn = Transproc(:ungroup, :tags, [:tag])
102
104
  #
103
105
  # fn.call [
104
- # { task: 'Group it', tags: [{ tag: 'task' }, { tag: 'important' }]
106
+ # { task: 'Group it', tags: [{ tag: 'task' }, { tag: 'important' }] }
105
107
  # ]
106
108
  # # => [
107
109
  # { task: 'Group it', tag: 'task' },
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Transproc
2
4
  module ArrayTransformations
3
5
  class Combine
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Transproc
2
4
  # Transformation functions for Classes
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'date'
2
4
  require 'time'
3
5
  require 'bigdecimal'
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Transproc
4
+ # @api private
5
+ class Compiler
6
+ InvalidFunctionNameError = Class.new(StandardError)
7
+
8
+ attr_reader :registry, :transformer
9
+
10
+ def initialize(registry, transformer = nil)
11
+ @registry = registry
12
+ @transformer = transformer
13
+ end
14
+
15
+ def call(ast)
16
+ ast.map(&method(:visit)).reduce(:>>)
17
+ end
18
+
19
+ def visit(node)
20
+ id, *rest = node
21
+ public_send(:"visit_#{id}", *rest)
22
+ end
23
+
24
+ def visit_fn(node)
25
+ name, rest = node
26
+ args = rest.map { |arg| visit(arg) }
27
+
28
+ if registry.contain?(name)
29
+ registry[name, *args]
30
+ elsif transformer.respond_to?(name)
31
+ Function.new(transformer.method(name), name: name, args: args)
32
+ else
33
+ raise InvalidFunctionNameError, "function name +#{name}+ is not valid"
34
+ end
35
+ end
36
+
37
+ def visit_arg(arg)
38
+ arg
39
+ end
40
+
41
+ def visit_t(node)
42
+ call(node)
43
+ end
44
+ end
45
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'transproc/support/deprecations'
2
4
 
3
5
  module Transproc
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Transproc
2
4
  # Composition of two functions
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Transproc
2
4
  # Conditional transformation functions
3
5
  #
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Transproc
4
+ Undefined = Object.new.freeze
5
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Transproc
2
4
  Error = Class.new(StandardError)
3
5
  FunctionAlreadyRegisteredError = Class.new(Error)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'transproc/composite'
2
4
 
3
5
  module Transproc
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Transproc
2
4
  # Function container extension
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'transproc/coercions'
2
4
 
3
5
  module Transproc
@@ -17,20 +19,26 @@ module Transproc
17
19
  module HashTransformations
18
20
  extend Registry
19
21
 
20
- # Map all keys in a hash with the provided transformation function
21
- #
22
- # @example
23
- # Transproc(:map_keys, -> s { s.upcase })['name' => 'Jane']
24
- # # => {"NAME" => "Jane"}
25
- #
26
- # @param [Hash]
27
- #
28
- # @return [Hash]
29
- #
30
- # @api public
31
- def self.map_keys(source_hash, fn)
32
- Hash[source_hash].tap do |hash|
33
- hash.keys.each { |key| hash[fn[key]] = hash.delete(key) }
22
+ if RUBY_VERSION >= '2.5'
23
+ # Map all keys in a hash with the provided transformation function
24
+ #
25
+ # @example
26
+ # Transproc(:map_keys, -> s { s.upcase })['name' => 'Jane']
27
+ # # => {"NAME" => "Jane"}
28
+ #
29
+ # @param [Hash]
30
+ #
31
+ # @return [Hash]
32
+ #
33
+ # @api public
34
+ def self.map_keys(source_hash, fn)
35
+ Hash[source_hash].transform_keys!(&fn)
36
+ end
37
+ else
38
+ def self.map_keys(source_hash, fn)
39
+ Hash[source_hash].tap do |hash|
40
+ hash.keys.each { |key| hash[fn[key]] = hash.delete(key) }
41
+ end
34
42
  end
35
43
  end
36
44
 
@@ -94,20 +102,55 @@ module Transproc
94
102
  map_keys(hash, Coercions[:to_string].fn)
95
103
  end
96
104
 
97
- # Map all values in a hash using transformation function
105
+ # Stringify keys in a hash recursively
98
106
  #
99
107
  # @example
100
- # Transproc(:map_values, -> v { v.upcase })[:name => 'Jane']
101
- # # => {"name" => "JANE"}
108
+ # input = { :foo => "bar", :baz => [{ :one => 1 }] }
109
+ #
110
+ # t(:deep_stringify_keys)[input]
111
+ # # => { "foo" => "bar", "baz" => [{ "one" => 1 }] }
102
112
  #
103
113
  # @param [Hash]
104
114
  #
105
115
  # @return [Hash]
106
116
  #
107
117
  # @api public
108
- def self.map_values(source_hash, fn)
109
- Hash[source_hash].tap do |hash|
110
- hash.each { |key, value| hash[key] = fn[value] }
118
+ def self.deep_stringify_keys(hash)
119
+ hash.each_with_object({}) do |(key, value), output|
120
+ output[key.to_s] =
121
+ case value
122
+ when Hash
123
+ deep_stringify_keys(value)
124
+ when Array
125
+ value.map { |item|
126
+ item.is_a?(Hash) ? deep_stringify_keys(item) : item
127
+ }
128
+ else
129
+ value
130
+ end
131
+ end
132
+ end
133
+
134
+ if RUBY_VERSION >= '2.4'
135
+ # Map all values in a hash using transformation function
136
+ #
137
+ # @example
138
+ # Transproc(:map_values, -> v { v.upcase })[:name => 'Jane']
139
+ # # => {"name" => "JANE"}
140
+ #
141
+ # @param [Hash]
142
+ #
143
+ # @return [Hash]
144
+ #
145
+ # @api public
146
+ def self.map_values(source_hash, fn)
147
+ Hash[source_hash].transform_values!(&fn)
148
+ end
149
+ else
150
+ def self.map_values(source_hash, fn)
151
+ Hash[source_hash].tap do |hash|
152
+ hash.each { |key, value| hash[key] = fn[value] }
153
+ end
111
154
  end
112
155
  end
113
156
 
@@ -167,20 +210,26 @@ module Transproc
167
210
  Hash[hash].reject { |k, _| keys.include?(k) }
168
211
  end
169
212
 
170
- # Accepts specified keys from a hash
171
- #
172
- # @example
173
- # Transproc(:accept_keys, [:name])[name: 'Jane', email: 'jane@doe.org']
174
- # # => {:name=>"Jane"}
175
- #
176
- # @param [Hash] hash The input hash
177
- # @param [Array] keys The keys to be accepted
178
- #
179
- # @return [Hash]
180
- #
181
- # @api public
182
- def self.accept_keys(hash, keys)
183
- reject_keys(hash, hash.keys - keys)
213
+ if RUBY_VERSION >= '2.5'
214
+ # Accepts specified keys from a hash
215
+ #
216
+ # @example
217
+ # Transproc(:accept_keys, [:name])[name: 'Jane', email: 'jane@doe.org']
218
+ # # => {:name=>"Jane"}
219
+ #
220
+ # @param [Hash] hash The input hash
221
+ # @param [Array] keys The keys to be accepted
222
+ #
223
+ # @return [Hash]
224
+ #
225
+ # @api public
226
+ def self.accept_keys(hash, keys)
227
+ Hash[hash].slice(*keys)
228
+ end
229
+ else
230
+ def self.accept_keys(hash, keys)
231
+ reject_keys(hash, hash.keys - keys)
232
+ end
184
233
  end
185
234
 
186
235
  # Map a key in a hash with the provided transformation function