transproc 1.0.3 → 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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +15 -0
  3. data/.gitignore +3 -0
  4. data/.travis.yml +10 -10
  5. data/CHANGELOG.md +14 -2
  6. data/Gemfile +5 -17
  7. data/lib/transproc.rb +3 -4
  8. data/lib/transproc/all.rb +2 -0
  9. data/lib/transproc/array.rb +2 -0
  10. data/lib/transproc/array/combine.rb +2 -0
  11. data/lib/transproc/class.rb +2 -0
  12. data/lib/transproc/coercions.rb +2 -0
  13. data/lib/transproc/compiler.rb +45 -0
  14. data/lib/transproc/composer.rb +2 -0
  15. data/lib/transproc/composite.rb +2 -0
  16. data/lib/transproc/conditional.rb +2 -0
  17. data/lib/transproc/constants.rb +5 -0
  18. data/lib/transproc/error.rb +2 -0
  19. data/lib/transproc/function.rb +2 -0
  20. data/lib/transproc/functions.rb +2 -0
  21. data/lib/transproc/hash.rb +31 -0
  22. data/lib/transproc/proc.rb +2 -0
  23. data/lib/transproc/recursion.rb +2 -0
  24. data/lib/transproc/registry.rb +1 -0
  25. data/lib/transproc/store.rb +1 -0
  26. data/lib/transproc/support/deprecations.rb +2 -0
  27. data/lib/transproc/transformer.rb +7 -1
  28. data/lib/transproc/transformer/class_interface.rb +31 -65
  29. data/lib/transproc/transformer/deprecated/class_interface.rb +80 -0
  30. data/lib/transproc/transformer/dsl.rb +51 -0
  31. data/lib/transproc/version.rb +3 -1
  32. data/spec/spec_helper.rb +5 -7
  33. data/spec/unit/array/combine_spec.rb +3 -1
  34. data/spec/unit/array_transformations_spec.rb +1 -1
  35. data/spec/unit/class_transformations_spec.rb +9 -6
  36. data/spec/unit/coercions_spec.rb +1 -1
  37. data/spec/unit/composer_spec.rb +1 -1
  38. data/spec/unit/conditional_spec.rb +1 -1
  39. data/spec/unit/function_not_found_error_spec.rb +1 -1
  40. data/spec/unit/function_spec.rb +1 -1
  41. data/spec/unit/hash_transformations_spec.rb +12 -1
  42. data/spec/unit/proc_transformations_spec.rb +3 -1
  43. data/spec/unit/recursion_spec.rb +1 -1
  44. data/spec/unit/registry_spec.rb +1 -1
  45. data/spec/unit/store_spec.rb +2 -1
  46. data/spec/unit/transformer/class_interface_spec.rb +364 -0
  47. data/spec/unit/transformer/dsl_spec.rb +15 -0
  48. data/spec/unit/transformer/instance_methods_spec.rb +25 -0
  49. data/spec/unit/transformer_spec.rb +128 -40
  50. data/spec/unit/transproc_spec.rb +1 -1
  51. data/transproc.gemspec +0 -4
  52. metadata +15 -53
  53. data/.rubocop.yml +0 -66
  54. data/.rubocop_todo.yml +0 -11
  55. data/rakelib/mutant.rake +0 -16
  56. data/rakelib/rubocop.rake +0 -18
  57. data/spec/support/mutant.rb +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0d88691992987b997ac6deeec1affc3a04a1657fc4ed3521f84c6b26adcce5a1
4
- data.tar.gz: dc1f06315b9c3a039c6694a14e4cd379b6836b1710fafe0123a05e985441eca8
3
+ metadata.gz: bd2c6ee5f665083297c1fcfd89a5e37d2284e498d6bc3953b88314e9513cbf43
4
+ data.tar.gz: 6b9a5f270234b26b479095c66187f91fcca9428afb605f75aa9f51b890976eef
5
5
  SHA512:
6
- metadata.gz: a2288e68392270823d3dcc4543534553c0d1ac504d95f84f4f9ccc54f52f682dfb64118aecfa913226247c24cc1d77d367193df29ef89a14255f9870c6ae3425
7
- data.tar.gz: 65b3916bc564ad16fab85fd84bb66a75544ee7924b4df50673727f212cbc551dc2106a594a052b47093dff1653c7e92e1e464e74facc9cd1aa51ae7c8084e3d0
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,21 +1,21 @@
1
- dist: trusty
2
1
  language: ruby
3
- sudo: required
4
2
  cache: bundler
5
3
  bundler_args: --without tools
6
- before_script: gem update --system
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"
7
10
  script: 'bundle exec rake spec'
8
- after_success:
9
- - '[ -d coverage ] && bundle exec codeclimate-test-reporter'
10
11
  rvm:
11
12
  - 2.3.8
12
- - 2.4.5
13
- - 2.5.3
14
- - 2.6
15
- - jruby-9.2.4.1
13
+ - 2.6.3
14
+ - 2.5.5
15
+ - 2.4.6
16
+ - jruby-9.2.7.0
16
17
  env:
17
18
  global:
18
- - JRUBY_OPTS='--dev -J-Xmx1024M'
19
19
  - COVERAGE='true'
20
20
  notifications:
21
21
  webhooks:
@@ -1,11 +1,23 @@
1
- # v1.0.3 to-be-released
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
2
14
 
3
15
  ## Changed
4
16
 
5
17
  * [BREAKING] Added minimal Ruby version to the gemspec file. Transproc now works with Ruby 2.3 and above (flash-gordon)
6
18
  * Performance improvements introduced by using new built-in methods in `Hash` (v-kolesnikov + flash-gordon)
7
19
 
8
- [Compare v1.0.2...master](https://github.com/solnic/transproc/compare/v1.0.2...master)
20
+ [Compare v1.0.2...v1.0.3](https://github.com/solnic/transproc/compare/v1.0.2...v1.0.3)
9
21
 
10
22
  # v1.0.2 2017-02-25
11
23
 
data/Gemfile CHANGED
@@ -2,27 +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
- gem 'anima'
9
-
10
- platform :mri do
11
- if RUBY_VERSION >= '2.5'
12
- gem 'mutant', github: 'mbj/mutant', branch: 'master'
13
- gem 'mutant-rspec'
14
- end
15
-
16
- gem 'codeclimate-test-reporter', require: false
17
- gem 'simplecov', require: false
18
- end
9
+ platform :mri do
10
+ gem 'simplecov', require: false
19
11
  end
20
12
 
21
13
  group :tools do
22
- gem 'rubocop', '~> 0.30.0'
23
14
  gem 'byebug', platform: :mri
24
15
  gem 'benchmark-ips'
25
- gem 'guard'
26
- gem 'guard-rspec'
27
- gem 'guard-rubocop'
28
16
  end
@@ -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'
@@ -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
@@ -100,6 +102,35 @@ module Transproc
100
102
  map_keys(hash, Coercions[:to_string].fn)
101
103
  end
102
104
 
105
+ # Stringify keys in a hash recursively
106
+ #
107
+ # @example
108
+ # input = { :foo => "bar", :baz => [{ :one => 1 }] }
109
+ #
110
+ # t(:deep_stringify_keys)[input]
111
+ # # => { "foo" => "bar", "baz" => [{ "one" => 1 }] }
112
+ #
113
+ # @param [Hash]
114
+ #
115
+ # @return [Hash]
116
+ #
117
+ # @api public
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
+
103
134
  if RUBY_VERSION >= '2.4'
104
135
  # Map all values in a hash using transformation function
105
136
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Transproc
2
4
  # Transformation functions for Procs
3
5
  #
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'transproc/conditional'
2
4
 
3
5
  module Transproc
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Transproc
4
5
  # Container to define transproc functions in, and access them via `[]` method
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module Transproc
4
5
  # Immutable collection of named procedures from external modules
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Transproc
2
4
  module Deprecations
3
5
  def self.announce(name, msg)
@@ -1,4 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'transproc/transformer/class_interface'
4
+ require 'transproc/transformer/deprecated/class_interface'
2
5
 
3
6
  module Transproc
4
7
  # Transfomer class for defining transprocs with a class DSL.
@@ -45,6 +48,9 @@ module Transproc
45
48
  # @api public
46
49
  class Transformer
47
50
  extend ClassInterface
51
+ extend Deprecated::ClassInterface
52
+
53
+ attr_reader :transproc
48
54
 
49
55
  # Execute the transformation pipeline with the given input.
50
56
  #
@@ -63,7 +69,7 @@ module Transproc
63
69
  #
64
70
  # @api public
65
71
  def call(input)
66
- self.class.transproc.call(input)
72
+ transproc.call(input)
67
73
  end
68
74
  end
69
75
  end
@@ -1,7 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'transproc/transformer/dsl'
4
+
1
5
  module Transproc
2
6
  class Transformer
3
- # @api private
7
+ # @api public
4
8
  module ClassInterface
9
+ # @api private
10
+ attr_reader :dsl
11
+
5
12
  # Return a base Transproc::Transformer class with the
6
13
  # container configured to the passed argument.
7
14
  #
@@ -17,14 +24,18 @@ module Transproc
17
24
  #
18
25
  # @api public
19
26
  def [](container)
20
- klass = Class.new(Transformer)
27
+ klass = Class.new(self)
21
28
  klass.container(container)
22
29
  klass
23
30
  end
24
31
 
25
32
  # @api private
26
33
  def inherited(subclass)
34
+ super
35
+
27
36
  subclass.container(@container) if defined?(@container)
37
+
38
+ subclass.instance_variable_set('@dsl', dsl.dup) if dsl
28
39
  end
29
40
 
30
41
  # Get or set the container to resolve transprocs from.
@@ -45,41 +56,32 @@ module Transproc
45
56
  # @return [Transproc::Registry]
46
57
  #
47
58
  # @api private
48
- def container(container = ::Transproc::Undefined)
49
- if container == ::Transproc::Undefined
50
- ensure_container_presence!
51
- @container
59
+ def container(container = Undefined)
60
+ if container.equal?(Undefined)
61
+ @container ||= Module.new.extend(Transproc::Registry)
52
62
  else
53
63
  @container = container
54
64
  end
55
65
  end
56
66
 
57
- # Define an anonymous transproc derived from given Transformer
58
- # Evaluates block with transformations and returns initialized transproc.
59
- # Does not mutate original Transformer
60
- #
61
- # @example
62
- #
63
- # class MyTransformer < Transproc::Transformer[MyContainer]
64
- # end
65
- #
66
- # transproc = MyTransformer.define do
67
- # map_values t(:to_string)
68
- # end
69
- # transproc.call(a: 1, b: 2)
70
- # # => {a: '1', b: '2'}
71
- #
72
- # @yield Block allowing to define transformations. The same as class level DSL
73
- #
74
- # @return [Function] Composed transproc
75
- #
76
67
  # @api public
77
- def define(&block)
78
- return transproc unless block_given?
68
+ def import(*args)
69
+ container.import(*args)
70
+ end
71
+
72
+ # @api public
73
+ def define!(&block)
74
+ @dsl ||= DSL.new(container)
75
+ @dsl.instance_eval(&block)
76
+ self
77
+ end
79
78
 
80
- Class.new(self).tap { |klass| klass.instance_eval(&block) }.transproc
79
+ # @api public
80
+ def new(*args)
81
+ super(*args).tap do |transformer|
82
+ transformer.instance_variable_set('@transproc', dsl.(transformer)) if dsl
83
+ end
81
84
  end
82
- alias build define
83
85
 
84
86
  # Get a transformation from the container,
85
87
  # without adding it to the transformation pipeline
@@ -105,42 +107,6 @@ module Transproc
105
107
  def t(fn, *args)
106
108
  container[fn, *args]
107
109
  end
108
-
109
- # @api private
110
- def method_missing(method, *args, &block)
111
- if container.contain?(method)
112
- args.push(define(&block)) if block_given?
113
- transformations << t(method, *args)
114
- else
115
- super
116
- end
117
- end
118
-
119
- # @api private
120
- def respond_to_missing?(method, _include_private = false)
121
- container.contain?(method) || super
122
- end
123
-
124
- # @api private
125
- def transproc
126
- transformations.reduce(:>>)
127
- end
128
-
129
- private
130
-
131
- # An array containing the transformation pipeline
132
- #
133
- # @api private
134
- def transformations
135
- @transformations ||= []
136
- end
137
-
138
- # @api private
139
- def ensure_container_presence!
140
- return if defined?(@container)
141
- raise ArgumentError, 'Transformer function registry is empty. '\
142
- 'Provide your registry via Transproc::Transformer[YourRegistry]'
143
- end
144
110
  end
145
111
  end
146
112
  end