transproc 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/Gemfile +5 -2
- data/lib/transproc.rb +3 -2
- data/lib/transproc/array.rb +9 -5
- data/lib/transproc/coercions.rb +20 -0
- data/lib/transproc/error.rb +4 -3
- data/lib/transproc/function.rb +5 -5
- data/lib/transproc/hash.rb +7 -5
- data/lib/transproc/recursion.rb +42 -2
- data/lib/transproc/registry.rb +126 -0
- data/lib/transproc/version.rb +1 -1
- data/spec/spec_helper.rb +3 -0
- data/spec/support/mutant.rb +10 -0
- data/spec/unit/array_transformations_spec.rb +83 -3
- data/spec/unit/coercions_spec.rb +34 -0
- data/spec/unit/hash_transformations_spec.rb +22 -0
- data/spec/unit/recursion_spec.rb +52 -0
- data/spec/unit/registry_spec.rb +95 -0
- data/spec/unit/transproc_spec.rb +9 -5
- data/transproc.gemspec +1 -1
- metadata +9 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 18747dd5f157b683c7f3fc9200c7a7eb084cb270
|
4
|
+
data.tar.gz: 6961af7f27c18df6f9da0e3b0cbf839a003c839f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51f5533988a1658e74988a7176936aac2d97925624720ac74dc5ca3ffc51651f61732947e645a3b65ed430992d442ae5aff4d8d35a81acc2bcaae45a7bdb9d77
|
7
|
+
data.tar.gz: 0b665c3972a08d45fed440025c3c986dfb00726615c16ed4e52c29ac9a86ca7e6e91d9cd8275f32829b638b21edea9a4ba8813b8e673f0839e9576d54d4cefa0
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## v0.2.4 2015-06-20
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* `Transproc::Registry` extension for registering reusable functions within modules (nepalez)
|
6
|
+
* `:recursion` recurse over an enumerable (AMHOL)
|
7
|
+
|
8
|
+
### Changed
|
9
|
+
|
10
|
+
* `:group`, `:nest`, and `:wrap` support adding new data to existing groups/wraps (nepalez)
|
11
|
+
* `Transproc::MalformedInputError` includes original backtrace now (solnic)
|
12
|
+
|
13
|
+
[Compare v0.2.3...v0.2.4](https://github.com/solnic/transproc/compare/v0.2.3...v0.2.4)
|
14
|
+
|
1
15
|
## v0.2.3 2015-06-02
|
2
16
|
|
3
17
|
### Added
|
data/Gemfile
CHANGED
@@ -5,9 +5,12 @@ gemspec
|
|
5
5
|
group :test do
|
6
6
|
gem 'equalizer'
|
7
7
|
gem 'anima'
|
8
|
-
gem 'mutant'
|
9
|
-
gem 'mutant-rspec'
|
10
8
|
gem 'codeclimate-test-reporter', require: nil
|
9
|
+
|
10
|
+
platform :mri do
|
11
|
+
gem 'mutant', github: 'mbj/mutant', branch: 'master'
|
12
|
+
gem 'mutant-rspec'
|
13
|
+
end
|
11
14
|
end
|
12
15
|
|
13
16
|
group :tools do
|
data/lib/transproc.rb
CHANGED
@@ -3,6 +3,7 @@ require 'transproc/function'
|
|
3
3
|
require 'transproc/functions'
|
4
4
|
require 'transproc/composer'
|
5
5
|
require 'transproc/error'
|
6
|
+
require 'transproc/registry'
|
6
7
|
|
7
8
|
module Transproc
|
8
9
|
module_function
|
@@ -21,7 +22,7 @@ module Transproc
|
|
21
22
|
def register(*args, &block)
|
22
23
|
name, fn = *args
|
23
24
|
if functions.include?(name)
|
24
|
-
raise FunctionAlreadyRegisteredError, "
|
25
|
+
raise FunctionAlreadyRegisteredError, "Function #{name} is already defined"
|
25
26
|
end
|
26
27
|
functions[name] = fn || block
|
27
28
|
end
|
@@ -33,7 +34,7 @@ module Transproc
|
|
33
34
|
# @api private
|
34
35
|
def [](name)
|
35
36
|
functions.fetch(name) {
|
36
|
-
raise FunctionNotFoundError, "
|
37
|
+
raise FunctionNotFoundError, "No registered function for #{name}"
|
37
38
|
}
|
38
39
|
end
|
39
40
|
|
data/lib/transproc/array.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'transproc/coercions'
|
2
|
+
|
1
3
|
module Transproc
|
2
4
|
# Transformation functions for Array objects
|
3
5
|
#
|
@@ -87,13 +89,15 @@ module Transproc
|
|
87
89
|
def group(array, key, keys)
|
88
90
|
grouped = Hash.new { |h, k| h[k] = [] }
|
89
91
|
array.each do |hash|
|
90
|
-
hash = hash
|
91
|
-
|
92
|
-
|
93
|
-
|
92
|
+
hash = Hash[hash]
|
93
|
+
|
94
|
+
old_group = Transproc::Coercions.to_tuples(hash.delete(key))
|
95
|
+
new_group = keys.inject({}) { |a, e| a.merge(e => hash.delete(e)) }
|
96
|
+
|
97
|
+
grouped[hash] << old_group.map { |item| item.merge(new_group) }
|
94
98
|
end
|
95
99
|
grouped.map do |root, children|
|
96
|
-
root.merge(key => children)
|
100
|
+
root.merge(key => children.flatten)
|
97
101
|
end
|
98
102
|
end
|
99
103
|
|
data/lib/transproc/coercions.rb
CHANGED
@@ -153,5 +153,25 @@ module Transproc
|
|
153
153
|
def to_datetime(value)
|
154
154
|
DateTime.parse(value)
|
155
155
|
end
|
156
|
+
|
157
|
+
# Coerce value into an array containing tuples only
|
158
|
+
#
|
159
|
+
# If the source is not an array, or doesn't contain a tuple, returns
|
160
|
+
# an array with one empty tuple
|
161
|
+
#
|
162
|
+
# @example
|
163
|
+
# Transproc(:to_tuples)[:foo] # => [{}]
|
164
|
+
# Transproc(:to_tuples)[[]] # => [{}]
|
165
|
+
# Transproc(:to_tuples)[[{ foo: :FOO, :bar }]] # => [{ foo: :FOO }]
|
166
|
+
#
|
167
|
+
# @param [Object] value
|
168
|
+
#
|
169
|
+
# @return [Array<Hash>]
|
170
|
+
#
|
171
|
+
def to_tuples(value)
|
172
|
+
array = value.is_a?(Array) ? Array[*value] : [{}]
|
173
|
+
array.select! { |item| item.is_a?(Hash) }
|
174
|
+
array.any? ? array : [{}]
|
175
|
+
end
|
156
176
|
end
|
157
177
|
end
|
data/lib/transproc/error.rb
CHANGED
@@ -4,13 +4,14 @@ module Transproc
|
|
4
4
|
FunctionAlreadyRegisteredError = Class.new(Error)
|
5
5
|
|
6
6
|
class MalformedInputError < Error
|
7
|
+
attr_reader :function, :value, :original_error
|
8
|
+
|
7
9
|
def initialize(function, value, error)
|
8
10
|
@function = function
|
9
11
|
@value = value
|
10
12
|
@original_error = error
|
11
|
-
super
|
13
|
+
super "Failed to call_function #{function} with #{value.inspect} - #{error}"
|
14
|
+
set_backtrace(error.backtrace)
|
12
15
|
end
|
13
|
-
|
14
|
-
attr_reader :function, :value, :original_error
|
15
16
|
end
|
16
17
|
end
|
data/lib/transproc/function.rb
CHANGED
@@ -23,9 +23,9 @@ module Transproc
|
|
23
23
|
attr_reader :args
|
24
24
|
|
25
25
|
# @api private
|
26
|
-
def initialize(fn, options
|
26
|
+
def initialize(fn, options)
|
27
27
|
@fn = fn
|
28
|
-
@args = options
|
28
|
+
@args = options[:args]
|
29
29
|
end
|
30
30
|
|
31
31
|
# Call the wrapped proc
|
@@ -37,8 +37,8 @@ module Transproc
|
|
37
37
|
# @api public
|
38
38
|
def call(*value)
|
39
39
|
fn[*value, *args]
|
40
|
-
rescue =>
|
41
|
-
raise MalformedInputError.new(@fn, value,
|
40
|
+
rescue => e
|
41
|
+
raise MalformedInputError.new(@fn, value, e)
|
42
42
|
end
|
43
43
|
alias_method :[], :call
|
44
44
|
|
@@ -63,7 +63,7 @@ module Transproc
|
|
63
63
|
#
|
64
64
|
# @api public
|
65
65
|
def to_ast
|
66
|
-
identifier = fn.
|
66
|
+
identifier = fn.instance_of?(Proc) ? fn : fn.name
|
67
67
|
[identifier, args]
|
68
68
|
end
|
69
69
|
end
|
data/lib/transproc/hash.rb
CHANGED
@@ -174,7 +174,7 @@ module Transproc
|
|
174
174
|
#
|
175
175
|
# @example
|
176
176
|
# Transproc(:accept_keys, [:name])[name: 'Jane', email: 'jane@doe.org']
|
177
|
-
# # => {:
|
177
|
+
# # => {:name=>"Jane"}
|
178
178
|
#
|
179
179
|
# @param [Hash] hash The input hash
|
180
180
|
# @param [Array] keys The keys to be accepted
|
@@ -244,7 +244,9 @@ module Transproc
|
|
244
244
|
|
245
245
|
if nest_keys.size > 0
|
246
246
|
child = Hash[nest_keys.zip(nest_keys.map { |key| hash.delete(key) })]
|
247
|
-
hash
|
247
|
+
old_nest = hash[root]
|
248
|
+
new_nest = old_nest.is_a?(Hash) ? old_nest.merge(child) : child
|
249
|
+
hash.update(root => new_nest)
|
248
250
|
else
|
249
251
|
hash.update(root => {})
|
250
252
|
end
|
@@ -302,7 +304,7 @@ module Transproc
|
|
302
304
|
#
|
303
305
|
# @api public
|
304
306
|
def fold(hash, key, tuple_key)
|
305
|
-
fold!(hash
|
307
|
+
fold!(Hash[hash], key, tuple_key)
|
306
308
|
end
|
307
309
|
|
308
310
|
# Same as `:fold` but mutates the hash
|
@@ -311,7 +313,7 @@ module Transproc
|
|
311
313
|
#
|
312
314
|
# @api public
|
313
315
|
def fold!(hash, key, tuple_key)
|
314
|
-
hash.
|
316
|
+
hash.update(key => ArrayTransformations.extract_key(hash[key], tuple_key))
|
315
317
|
end
|
316
318
|
|
317
319
|
# Splits hash to array by all values from a specified key
|
@@ -328,7 +330,7 @@ module Transproc
|
|
328
330
|
# { title: 'be cool' }
|
329
331
|
# ]
|
330
332
|
# }
|
331
|
-
# Transproc(:split, :tasks, [:priority])
|
333
|
+
# Transproc(:split, :tasks, [:priority])[input]
|
332
334
|
# => [
|
333
335
|
# { name: 'Joe', priority: 1, tasks: [{ title: 'sleep well' }] },
|
334
336
|
# { name: 'Joe', priority: 2, tasks: [{ title: 'be nice' }, { title: nil }] },
|
data/lib/transproc/recursion.rb
CHANGED
@@ -17,10 +17,50 @@ module Transproc
|
|
17
17
|
module Recursion
|
18
18
|
extend Functions
|
19
19
|
|
20
|
+
IF_ENUMERABLE = -> fn { Transproc(:is, Enumerable, fn) }
|
21
|
+
|
20
22
|
IF_ARRAY = -> fn { Transproc(:is, Array, fn) }
|
21
23
|
|
22
24
|
IF_HASH = -> fn { Transproc(:is, Hash, fn) }
|
23
25
|
|
26
|
+
# Recursively apply the provided transformation function to an enumerable
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# Transproc(:recursion, Transproc(:is, ::Hash, Transproc(:symbolize_keys)))[
|
30
|
+
# {
|
31
|
+
# 'id' => 1,
|
32
|
+
# 'name' => 'Jane',
|
33
|
+
# 'tasks' => [
|
34
|
+
# { 'id' => 1, 'description' => 'Write some code' },
|
35
|
+
# { 'id' => 2, 'description' => 'Write some more code' }
|
36
|
+
# ]
|
37
|
+
# }
|
38
|
+
# ]
|
39
|
+
# => {:id=>1, :name=>"Jane", :tasks=>[{:id=>1, :description=>"Write some code"}, {:id=>2, :description=>"Write some more code"}]}
|
40
|
+
#
|
41
|
+
# @param [Enumerable]
|
42
|
+
#
|
43
|
+
# @return [Enumerable]
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
def recursion(value, fn)
|
47
|
+
result = fn[value]
|
48
|
+
guarded = IF_ENUMERABLE[-> v { recursion(v, fn) }]
|
49
|
+
|
50
|
+
case result
|
51
|
+
when ::Hash
|
52
|
+
result.keys.each do |key|
|
53
|
+
result[key] = guarded[result.delete(key)]
|
54
|
+
end
|
55
|
+
when ::Array
|
56
|
+
result.map! do |item|
|
57
|
+
guarded[item]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
result
|
62
|
+
end
|
63
|
+
|
24
64
|
# Recursively apply the provided transformation function to an array
|
25
65
|
#
|
26
66
|
# @example
|
@@ -36,7 +76,7 @@ module Transproc
|
|
36
76
|
# @api public
|
37
77
|
def array_recursion(value, fn)
|
38
78
|
result = fn[value]
|
39
|
-
guarded = IF_ARRAY[-> v {
|
79
|
+
guarded = IF_ARRAY[-> v { array_recursion(v, fn) }]
|
40
80
|
|
41
81
|
result.map! do |item|
|
42
82
|
guarded[item]
|
@@ -58,7 +98,7 @@ module Transproc
|
|
58
98
|
# @api public
|
59
99
|
def hash_recursion(value, fn)
|
60
100
|
result = fn[value]
|
61
|
-
guarded = IF_HASH[-> v {
|
101
|
+
guarded = IF_HASH[-> v { hash_recursion(v, fn) }]
|
62
102
|
|
63
103
|
result.keys.each do |key|
|
64
104
|
result[key] = guarded[result.delete(key)]
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Transproc
|
2
|
+
# Container to define transproc functions in, and access them via `[]` method
|
3
|
+
# from the outside of the module
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# module FooMethods
|
7
|
+
# extend Transproc::Registry
|
8
|
+
#
|
9
|
+
# def foo(name, prefix)
|
10
|
+
# [prefix, '_', name].join
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# fn = FooMethods[:foo, 'baz']
|
15
|
+
# fn['qux'] # => 'qux_baz'
|
16
|
+
#
|
17
|
+
# module BarMethods
|
18
|
+
# # extend Transproc::Registry
|
19
|
+
# include FooMethods
|
20
|
+
#
|
21
|
+
# def bar(*args)
|
22
|
+
# foo(*args).upcase
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# fn = BarMethods[:foo, 'baz']
|
27
|
+
# fn['qux'] # => 'qux_baz'
|
28
|
+
#
|
29
|
+
# fn = BarMethods[:bar, 'baz']
|
30
|
+
# fn['qux'] # => 'QUX_BAZ'
|
31
|
+
#
|
32
|
+
# @api public
|
33
|
+
module Registry
|
34
|
+
# Builds the transproc function either from a Proc, or from the module method
|
35
|
+
#
|
36
|
+
# @param [Proc, Symbol] fn
|
37
|
+
# Either a proc, or a name of the module's function to be wrapped to transproc
|
38
|
+
# @param [Object, Array] args
|
39
|
+
# Args to be carried by the transproc
|
40
|
+
#
|
41
|
+
# @return [Transproc::Function]
|
42
|
+
#
|
43
|
+
# @alias :t
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
def [](fn, *args)
|
47
|
+
fun = fn.is_a?(Proc) ? fn : method(fn).to_proc
|
48
|
+
Transproc::Function.new(fun, args: args)
|
49
|
+
end
|
50
|
+
alias_method :t, :[]
|
51
|
+
|
52
|
+
# Forwards the named method (transproc) to another module
|
53
|
+
#
|
54
|
+
# Allows using transprocs from other modules without including those
|
55
|
+
# modules as a whole
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# module Foo
|
59
|
+
# extend Transproc::Registry
|
60
|
+
#
|
61
|
+
# def foo(value)
|
62
|
+
# value.upcase
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# def bar(value)
|
66
|
+
# value.downcase
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# module Bar
|
71
|
+
# extend Transproc::Registry
|
72
|
+
#
|
73
|
+
# uses :foo, from: Foo, as: :baz
|
74
|
+
# uses :bar, from: Foo
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# Bar[:baz]['Qux'] # => 'QUX'
|
78
|
+
# Bar[:bar]['Qux'] # => 'qux'
|
79
|
+
#
|
80
|
+
# @param [String, Symbol] name
|
81
|
+
# @option [Class] :from The module to take the method from
|
82
|
+
# @option [String, Symbol] :as
|
83
|
+
# The name of imported transproc inside the current module
|
84
|
+
#
|
85
|
+
# @return [undefined]
|
86
|
+
#
|
87
|
+
# @api public
|
88
|
+
def uses(name, options = {})
|
89
|
+
source = options.fetch(:from)
|
90
|
+
new_name = options.fetch(:as, name)
|
91
|
+
define_method(new_name) { |*args| source.__send__(name, *args) }
|
92
|
+
end
|
93
|
+
|
94
|
+
# @api private
|
95
|
+
def self.extended(target)
|
96
|
+
target.extend(ClassMethods)
|
97
|
+
end
|
98
|
+
|
99
|
+
# @api private
|
100
|
+
module ClassMethods
|
101
|
+
# Makes `[]` and all functions defined in the included modules
|
102
|
+
# accessible in their receiver
|
103
|
+
#
|
104
|
+
# @api private
|
105
|
+
def included(other)
|
106
|
+
other.extend(Transproc::Registry, self)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Makes newly module-defined functions accessible via `[]` method
|
110
|
+
# by adding it to the module's eigenclass
|
111
|
+
#
|
112
|
+
# @api private
|
113
|
+
def method_added(name)
|
114
|
+
module_function(name)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Makes undefined methods inaccessible via `[]` method by
|
118
|
+
# undefining it from the module's eigenclass
|
119
|
+
#
|
120
|
+
# @api private
|
121
|
+
def method_undefined(name)
|
122
|
+
singleton_class.__send__(:undef_method, name)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/lib/transproc/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -175,17 +175,69 @@ describe Transproc::ArrayTransformations do
|
|
175
175
|
|
176
176
|
expect(wrap[input]).to eql(output)
|
177
177
|
end
|
178
|
+
|
179
|
+
it 'adds data to the existing tuples' do
|
180
|
+
wrap = t(:wrap, :task, [:title])
|
181
|
+
|
182
|
+
input = [{ name: 'Jane', task: { priority: 1 }, title: 'One' }]
|
183
|
+
output = [{ name: 'Jane', task: { priority: 1, title: 'One' } }]
|
184
|
+
|
185
|
+
expect(wrap[input]).to eql(output)
|
186
|
+
end
|
178
187
|
end
|
179
188
|
|
180
189
|
describe '.group' do
|
181
|
-
|
182
|
-
group = t(:group, :tasks, [:title])
|
190
|
+
subject(:group) { t(:group, :tasks, [:title]) }
|
183
191
|
|
184
|
-
|
192
|
+
it 'returns a new array with grouped hashes' do
|
193
|
+
input = [{ name: 'Jane', title: 'One' }, { name: 'Jane', title: 'Two' }]
|
185
194
|
output = [{ name: 'Jane', tasks: [{ title: 'One' }, { title: 'Two' }] }]
|
186
195
|
|
187
196
|
expect(group[input]).to eql(output)
|
188
197
|
end
|
198
|
+
|
199
|
+
it 'updates the existing group' do
|
200
|
+
input = [
|
201
|
+
{
|
202
|
+
name: 'Jane',
|
203
|
+
title: 'One',
|
204
|
+
tasks: [{ type: 'one' }, { type: 'two' }]
|
205
|
+
},
|
206
|
+
{
|
207
|
+
name: 'Jane',
|
208
|
+
title: 'Two',
|
209
|
+
tasks: [{ type: 'one' }, { type: 'two' }]
|
210
|
+
}
|
211
|
+
]
|
212
|
+
output = [
|
213
|
+
{
|
214
|
+
name: 'Jane',
|
215
|
+
tasks: [
|
216
|
+
{ title: 'One', type: 'one' },
|
217
|
+
{ title: 'One', type: 'two' },
|
218
|
+
{ title: 'Two', type: 'one' },
|
219
|
+
{ title: 'Two', type: 'two' }
|
220
|
+
]
|
221
|
+
}
|
222
|
+
]
|
223
|
+
|
224
|
+
expect(group[input]).to eql(output)
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'ingnores old values except for array of tuples' do
|
228
|
+
input = [
|
229
|
+
{ name: 'Jane', title: 'One', tasks: [{ priority: 1 }, :wrong] },
|
230
|
+
{ name: 'Jane', title: 'Two', tasks: :wrong }
|
231
|
+
]
|
232
|
+
output = [
|
233
|
+
{
|
234
|
+
name: 'Jane',
|
235
|
+
tasks: [{ title: 'One', priority: 1 }, { title: 'Two' }]
|
236
|
+
}
|
237
|
+
]
|
238
|
+
|
239
|
+
expect(group[input]).to eql(output)
|
240
|
+
end
|
189
241
|
end
|
190
242
|
|
191
243
|
describe '.ungroup' do
|
@@ -211,6 +263,34 @@ describe Transproc::ArrayTransformations do
|
|
211
263
|
|
212
264
|
expect(ungroup[input]).to eql(output)
|
213
265
|
end
|
266
|
+
|
267
|
+
it 'ungroups array partially' do
|
268
|
+
input = [
|
269
|
+
{
|
270
|
+
name: 'Jane',
|
271
|
+
tasks: [
|
272
|
+
{ title: 'One', type: 'one' },
|
273
|
+
{ title: 'One', type: 'two' },
|
274
|
+
{ title: 'Two', type: 'one' },
|
275
|
+
{ title: 'Two', type: 'two' }
|
276
|
+
]
|
277
|
+
}
|
278
|
+
]
|
279
|
+
output = [
|
280
|
+
{
|
281
|
+
name: 'Jane',
|
282
|
+
title: 'One',
|
283
|
+
tasks: [{ type: 'one' }, { type: 'two' }]
|
284
|
+
},
|
285
|
+
{
|
286
|
+
name: 'Jane',
|
287
|
+
title: 'Two',
|
288
|
+
tasks: [{ type: 'one' }, { type: 'two' }]
|
289
|
+
}
|
290
|
+
]
|
291
|
+
|
292
|
+
expect(ungroup[input]).to eql(output)
|
293
|
+
end
|
214
294
|
end
|
215
295
|
|
216
296
|
describe '.combine' do
|
data/spec/unit/coercions_spec.rb
CHANGED
@@ -79,4 +79,38 @@ describe Transproc::Coercions do
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
end
|
82
|
+
|
83
|
+
describe '.to_tuples' do
|
84
|
+
subject(:to_tuples) { t(:to_tuples) }
|
85
|
+
|
86
|
+
context 'non-array' do
|
87
|
+
let(:input) { :foo }
|
88
|
+
|
89
|
+
it 'returns an array with one blank tuple' do
|
90
|
+
output = [{}]
|
91
|
+
|
92
|
+
expect(to_tuples[input]).to eql(output)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context 'empty array' do
|
97
|
+
let(:input) { [] }
|
98
|
+
|
99
|
+
it 'returns an array with one blank tuple' do
|
100
|
+
output = [{}]
|
101
|
+
|
102
|
+
expect(to_tuples[input]).to eql(output)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'array of tuples' do
|
107
|
+
let(:input) { [:foo, { bar: :BAZ }, :qux] }
|
108
|
+
|
109
|
+
it 'returns an array with tuples only' do
|
110
|
+
output = [{ bar: :BAZ }]
|
111
|
+
|
112
|
+
expect(to_tuples[input]).to eql(output)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
82
116
|
end
|
@@ -172,6 +172,28 @@ describe Transproc::HashTransformations do
|
|
172
172
|
expect(input).to eql(output)
|
173
173
|
end
|
174
174
|
|
175
|
+
it 'returns new hash with keys nested under the existing key' do
|
176
|
+
nest = t(:nest!, :baz, ['two'])
|
177
|
+
|
178
|
+
input = { 'foo' => 'bar', baz: { 'one' => nil }, 'two' => false }
|
179
|
+
output = { 'foo' => 'bar', baz: { 'one' => nil, 'two' => false } }
|
180
|
+
|
181
|
+
nest[input]
|
182
|
+
|
183
|
+
expect(input).to eql(output)
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'rewrites the existing key if its value is not a hash' do
|
187
|
+
nest = t(:nest!, :baz, ['two'])
|
188
|
+
|
189
|
+
input = { 'foo' => 'bar', baz: 'one', 'two' => false }
|
190
|
+
output = { 'foo' => 'bar', baz: { 'two' => false } }
|
191
|
+
|
192
|
+
nest[input]
|
193
|
+
|
194
|
+
expect(input).to eql(output)
|
195
|
+
end
|
196
|
+
|
175
197
|
it 'returns new hash with an empty hash under a new key when nest-keys are missing' do
|
176
198
|
nest = t(:nest!, :baz, ['foo'])
|
177
199
|
|
data/spec/unit/recursion_spec.rb
CHANGED
@@ -1,6 +1,58 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Transproc::Recursion do
|
4
|
+
describe '.recursion' do
|
5
|
+
let(:original) do
|
6
|
+
{
|
7
|
+
'foo' => 'bar',
|
8
|
+
'bar' => {
|
9
|
+
'foo' => 'bar',
|
10
|
+
'bar' => ['foo', 'bar', 'baz'],
|
11
|
+
'baz' => 'foo'
|
12
|
+
},
|
13
|
+
'baz' => 'bar'
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:input) { original.dup }
|
18
|
+
|
19
|
+
let(:output) do
|
20
|
+
{
|
21
|
+
'foo' => 'bar',
|
22
|
+
'bar' => {
|
23
|
+
'foo' => 'bar',
|
24
|
+
'bar' => ['foo', 'bar'],
|
25
|
+
}
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'when function is non-destructive' do
|
30
|
+
let(:map) do
|
31
|
+
t(:recursion, -> enum {
|
32
|
+
enum.reject { |v| v == 'baz' }
|
33
|
+
})
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'applies funtions to all items recursively' do
|
37
|
+
expect(map[input]).to eql(output)
|
38
|
+
expect(input).to eql(original)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when function is destructive' do
|
43
|
+
let(:map) do
|
44
|
+
t(:recursion, -> enum {
|
45
|
+
enum.reject! { |v| v == 'baz' }
|
46
|
+
})
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'applies funtions to all items recursively and destructively' do
|
50
|
+
expect(map[input]).to eql(output)
|
51
|
+
expect(input).to eql(output)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
4
56
|
describe '.array_recursion' do
|
5
57
|
let(:original) do
|
6
58
|
[
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Transproc::Registry do
|
4
|
+
before do
|
5
|
+
module FooModule
|
6
|
+
extend Transproc::Registry
|
7
|
+
|
8
|
+
def foo(value, prefix)
|
9
|
+
[prefix, '_', value].join
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
module BarModule
|
14
|
+
include FooModule
|
15
|
+
|
16
|
+
def bar(*args)
|
17
|
+
foo(*args).upcase
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module BazModule
|
22
|
+
extend Transproc::Registry
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '.[]' do
|
27
|
+
it 'builds function from the method' do
|
28
|
+
fn = ::FooModule[:foo, 'baz']
|
29
|
+
|
30
|
+
expect(fn['qux']).to eql 'baz_qux'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'builds function from the proc' do
|
34
|
+
fun = -> value, prefix { [prefix, '_', value].join }
|
35
|
+
fn = ::FooModule[fun, 'baz']
|
36
|
+
|
37
|
+
expect(fn['qux']).to eql 'baz_qux'
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'builds function using methods from included modules' do
|
41
|
+
fn = ::BarModule[:bar, 'baz']
|
42
|
+
|
43
|
+
expect(fn['qux']).to eql 'BAZ_QUX'
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'can access methods from included modules directly' do
|
47
|
+
fn = ::BarModule[:foo, 'baz']
|
48
|
+
|
49
|
+
expect(fn['qux']).to eql 'baz_qux'
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'cannot access undefined methods' do
|
53
|
+
module ::BarModule
|
54
|
+
undef_method :foo
|
55
|
+
end
|
56
|
+
|
57
|
+
expect { ::BarModule[:foo, 'baz'] }.to raise_error(NameError)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe '.uses' do
|
62
|
+
it 'forwards methods to another module directly' do
|
63
|
+
expect { ::BazModule[:baz, 'baz'] }.to raise_error(NameError)
|
64
|
+
|
65
|
+
module BazModule
|
66
|
+
uses :foo, as: :ffoo, from: FooModule
|
67
|
+
uses :bar, from: BarModule
|
68
|
+
end
|
69
|
+
|
70
|
+
ffoo = ::BazModule[:ffoo, 'baz']
|
71
|
+
bar = ::BazModule[:bar, 'baz']
|
72
|
+
|
73
|
+
expect(ffoo['qux']).to eql 'baz_qux'
|
74
|
+
expect(bar['qux']).to eql 'BAZ_QUX'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe '#t' do
|
79
|
+
it 'is an alias for .[]' do
|
80
|
+
module FooModule
|
81
|
+
def qux(value, *args)
|
82
|
+
t(:foo, *args)[value]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
fn = ::FooModule[:foo, 'baz']
|
87
|
+
|
88
|
+
expect(fn['qux']).to eql 'baz_qux'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
after { Object.send :remove_const, :BazModule }
|
93
|
+
after { Object.send :remove_const, :BarModule }
|
94
|
+
after { Object.send :remove_const, :FooModule }
|
95
|
+
end
|
data/spec/unit/transproc_spec.rb
CHANGED
@@ -52,13 +52,17 @@ describe Transproc do
|
|
52
52
|
|
53
53
|
describe 'handling malformed input' do
|
54
54
|
it 'raises a Transproc::MalformedInputError' do
|
55
|
-
Transproc.register(:im_dangerous, ->() {
|
56
|
-
raise ArgumentError, 'sorry, you got some bad apples in your input'
|
57
|
-
})
|
58
|
-
|
59
55
|
expect {
|
60
|
-
Transproc(:
|
56
|
+
Transproc(:to_integer)[{}]
|
61
57
|
}.to raise_error(Transproc::MalformedInputError)
|
58
|
+
|
59
|
+
begin
|
60
|
+
Transproc(:to_integer)[{}]
|
61
|
+
rescue Transproc::MalformedInputError => e
|
62
|
+
expect(e.message).to include('to_integer')
|
63
|
+
expect(e.message).to include("undefined method `to_i'")
|
64
|
+
expect(e.backtrace).to eql(e.original_error.backtrace)
|
65
|
+
end
|
62
66
|
end
|
63
67
|
end
|
64
68
|
end
|
data/transproc.gemspec
CHANGED
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.2.
|
4
|
+
version: 0.2.4
|
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-06-
|
11
|
+
date: 2015-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '3.
|
47
|
+
version: '3.3'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '3.
|
54
|
+
version: '3.3'
|
55
55
|
description: Transform Ruby objects in functional style
|
56
56
|
email:
|
57
57
|
- piotr.solnica@gmail.com
|
@@ -83,10 +83,12 @@ files:
|
|
83
83
|
- lib/transproc/functions.rb
|
84
84
|
- lib/transproc/hash.rb
|
85
85
|
- lib/transproc/recursion.rb
|
86
|
+
- lib/transproc/registry.rb
|
86
87
|
- lib/transproc/version.rb
|
87
88
|
- rakelib/mutant.rake
|
88
89
|
- rakelib/rubocop.rake
|
89
90
|
- spec/spec_helper.rb
|
91
|
+
- spec/support/mutant.rb
|
90
92
|
- spec/unit/array_transformations_spec.rb
|
91
93
|
- spec/unit/class_transformations_spec.rb
|
92
94
|
- spec/unit/coercions_spec.rb
|
@@ -95,6 +97,7 @@ files:
|
|
95
97
|
- spec/unit/function_spec.rb
|
96
98
|
- spec/unit/hash_transformations_spec.rb
|
97
99
|
- spec/unit/recursion_spec.rb
|
100
|
+
- spec/unit/registry_spec.rb
|
98
101
|
- spec/unit/transproc_spec.rb
|
99
102
|
- transproc.gemspec
|
100
103
|
homepage: http://solnic.github.io/transproc/
|
@@ -123,6 +126,7 @@ specification_version: 4
|
|
123
126
|
summary: Transform Ruby objects in functional style
|
124
127
|
test_files:
|
125
128
|
- spec/spec_helper.rb
|
129
|
+
- spec/support/mutant.rb
|
126
130
|
- spec/unit/array_transformations_spec.rb
|
127
131
|
- spec/unit/class_transformations_spec.rb
|
128
132
|
- spec/unit/coercions_spec.rb
|
@@ -131,4 +135,5 @@ test_files:
|
|
131
135
|
- spec/unit/function_spec.rb
|
132
136
|
- spec/unit/hash_transformations_spec.rb
|
133
137
|
- spec/unit/recursion_spec.rb
|
138
|
+
- spec/unit/registry_spec.rb
|
134
139
|
- spec/unit/transproc_spec.rb
|