transproc 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,29 +1,70 @@
1
+ require 'transproc/conditional'
2
+
1
3
  module Transproc
2
- register(:array_recursion) do |value, fn|
3
- result = fn[value]
4
-
5
- result.map! do |item|
6
- if item.is_a?(::Array)
7
- Transproc(:array_recursion, fn)[item]
8
- else
9
- item
10
- end
11
- end
12
- end
4
+ # Recursive transformation functions
5
+ #
6
+ # @example
7
+ # require 'transproc/recursion'
8
+ #
9
+ # include Transproc::Helper
10
+ #
11
+ # fn = t(:hash_recursion, t(:symbolize_keys))
12
+ #
13
+ # fn["name" => "Jane", "address" => { "street" => "Street 1", "zipcode" => "123" }]
14
+ # # => {:name=>"Jane", :address=>{:street=>"Street 1", :zipcode=>"123"}}
15
+ #
16
+ # @api public
17
+ module Recursion
18
+ extend Functions
19
+
20
+ IF_ARRAY = -> fn { Transproc(:is, Array, fn) }
13
21
 
14
- register(:hash_recursion) do |value, fn|
15
- result = fn[value]
22
+ IF_HASH = -> fn { Transproc(:is, Hash, fn) }
16
23
 
17
- result.keys.each do |key|
18
- item = result.delete(key)
24
+ # Recursively apply the provided transformation function to an array
25
+ #
26
+ # @example
27
+ # Transproc(:array_recursion, -> s { s.compact })[
28
+ # [['Joe', 'Jane', nil], ['Smith', 'Doe', nil]]
29
+ # ]
30
+ # # => [["Joe", "Jane"], ["Smith", "Doe"]]
31
+ #
32
+ # @param [Array]
33
+ #
34
+ # @return [Array]
35
+ #
36
+ # @api public
37
+ def array_recursion(value, fn)
38
+ result = fn[value]
39
+ guarded = IF_ARRAY[-> v { Transproc(:array_recursion, fn)[v] }]
19
40
 
20
- if item.is_a?(::Hash)
21
- result[key] = Transproc(:hash_recursion, fn)[item]
22
- else
23
- result[key] = item
41
+ result.map! do |item|
42
+ guarded[item]
24
43
  end
25
44
  end
26
45
 
27
- result
46
+ # Recursively apply the provided transformation function to a hash
47
+ #
48
+ # @example
49
+ # Transproc(:hash_recursion, Transproc(:symbolize_keys))[
50
+ # ["name" => "Jane", "address" => { "street" => "Street 1", "zipcode" => "123" }]
51
+ # ]
52
+ # # => {:name=>"Jane", :address=>{:street=>"Street 1", :zipcode=>"123"}}
53
+ #
54
+ # @param [Hash]
55
+ #
56
+ # @return [Hash]
57
+ #
58
+ # @api public
59
+ def hash_recursion(value, fn)
60
+ result = fn[value]
61
+ guarded = IF_HASH[-> v { Transproc(:hash_recursion, fn)[v] }]
62
+
63
+ result.keys.each do |key|
64
+ result[key] = guarded[result.delete(key)]
65
+ end
66
+
67
+ result
68
+ end
28
69
  end
29
70
  end
@@ -1,3 +1,3 @@
1
1
  module Transproc
2
- VERSION = '0.1.3'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
@@ -56,8 +56,8 @@ describe 'Array transformations with Transproc' do
56
56
  wrap =
57
57
  t(
58
58
  :map_array,
59
- t(:nest, :user, [:name, :title]),
60
- t(:map_key, :user, t(:nest, :task, [:title]))
59
+ t(:nest, :user, [:name, :title]) +
60
+ t(:map_value, :user, t(:nest, :task, [:title]))
61
61
  )
62
62
 
63
63
  input = [{ name: 'Jane', title: 'One' }]
@@ -7,6 +7,12 @@ describe 'Transproc / Coercions' do
7
7
  end
8
8
  end
9
9
 
10
+ describe 'to_symbol' do
11
+ it 'turns string into a symbol' do
12
+ expect(t(:to_symbol)['test']).to eql(:test)
13
+ end
14
+ end
15
+
10
16
  describe 'to_integer' do
11
17
  it 'turns string into an integer' do
12
18
  expect(t(:to_integer)['1']).to eql(1)
@@ -61,13 +67,13 @@ describe 'Transproc / Coercions' do
61
67
  describe 'to_boolean' do
62
68
  subject(:coercer) { t(:to_boolean) }
63
69
 
64
- Transproc::TRUE_VALUES.each do |value|
70
+ Transproc::Coercions::TRUE_VALUES.each do |value|
65
71
  it "turns #{value.inspect} to true" do
66
72
  expect(coercer[value]).to be(true)
67
73
  end
68
74
  end
69
75
 
70
- Transproc::FALSE_VALUES.each do |value|
76
+ Transproc::Coercions::FALSE_VALUES.each do |value|
71
77
  it "turns #{value.inspect} to false" do
72
78
  expect(coercer[value]).to be(false)
73
79
  end
@@ -8,7 +8,7 @@ describe Transproc::Composer do
8
8
  def fn
9
9
  compose do |fns|
10
10
  fns << t(:map_array, t(:symbolize_keys)) <<
11
- t(:map_array, t(:map_key, :age, t(:to_integer)))
11
+ t(:map_array, t(:map_value, :age, t(:to_integer)))
12
12
  end
13
13
  end
14
14
  end.new
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Conditional transformations with Transproc' do
4
+ describe 'guard' do
5
+ let(:fn) { t(:guard, ->(value) { value.is_a?(::String) }, t(:to_integer)) }
6
+
7
+ context 'when predicate returns truthy value' do
8
+ it 'applies the transformation and returns the result' do
9
+ input = '2'
10
+
11
+ expect(fn[input]).to eql(2)
12
+ end
13
+ end
14
+
15
+ context 'when predicate returns falsey value' do
16
+ it 'returns the original value' do
17
+ input = { 'foo' => 'bar' }
18
+
19
+ expect(fn[input]).to eql('foo' => 'bar')
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,56 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Transproc::Function" do
4
+ describe "#>>" do
5
+ it "composes named functions" do
6
+ f1 = t(:symbolize_keys)
7
+ f2 = t(:rename_keys, user_name: :name)
8
+
9
+ f3 = f1 >> f2
10
+
11
+ expect(f3.to_ast).to eql(
12
+ [
13
+ :symbolize_keys, [],
14
+ [
15
+ :rename_keys, [ user_name: :name ]
16
+ ]
17
+ ]
18
+ )
19
+
20
+ expect(f3['user_name' => 'Jane']).to eql(name: 'Jane')
21
+
22
+ f4 = f3 >> t(:nest, :details, [:name])
23
+
24
+ expect(f4.to_ast).to eql(
25
+ [
26
+ :symbolize_keys, [],
27
+ [
28
+ :rename_keys, [ user_name: :name ]
29
+ ],
30
+ [
31
+ :nest, [:details, [:name]]
32
+ ]
33
+ ]
34
+ )
35
+
36
+ expect(f4['user_name' => 'Jane']).to eql(details: { name: 'Jane' })
37
+ end
38
+
39
+ it "composes anonymous functions" do
40
+ # TODO: Use Transproc -> (v) { v.to_s } after release of jruby-9k
41
+ f1 = Transproc proc { |v, m| v * m }, 2
42
+ f2 = Transproc proc { |v| v.to_s }
43
+
44
+ f3 = f1 >> f2
45
+
46
+ expect(f3.to_ast).to eql(
47
+ [
48
+ f1.fn, [2],
49
+ [
50
+ f2.fn, []
51
+ ]
52
+ ]
53
+ )
54
+ end
55
+ end
56
+ end
@@ -1,6 +1,30 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe 'Hash mapping with Transproc' do
4
+ describe 'map_keys' do
5
+ it 'returns a new hash with given proc applied to keys' do
6
+ map_keys = t(:map_keys, ->(key) { key.strip })
7
+
8
+ input = { ' foo ' => 'bar' }
9
+ output = { 'foo' => 'bar' }
10
+
11
+ expect(map_keys[input]).to eql(output)
12
+ expect(input).to eql(' foo ' => 'bar')
13
+ end
14
+ end
15
+
16
+ describe 'map_keys!' do
17
+ it 'returns updated hash with given proc applied to keys' do
18
+ map_keys = t(:map_keys!, ->(key) { key.strip })
19
+
20
+ input = { ' foo ' => 'bar' }
21
+ output = { 'foo' => 'bar' }
22
+
23
+ expect(map_keys[input]).to eql(output)
24
+ expect(input).to eql('foo' => 'bar')
25
+ end
26
+ end
27
+
4
28
  describe 'symbolize_keys' do
5
29
  it 'returns a new hash with symbolized keys' do
6
30
  symbolize_keys = t(:symbolize_keys)
@@ -26,6 +50,104 @@ describe 'Hash mapping with Transproc' do
26
50
  end
27
51
  end
28
52
 
53
+ describe 'stringify_keys' do
54
+ it 'returns a new hash with stringified keys' do
55
+ stringify_keys = t(:stringify_keys)
56
+
57
+ input = { foo: 'bar' }
58
+ output = { 'foo' => 'bar' }
59
+
60
+ expect(stringify_keys[input]).to eql(output)
61
+ expect(input).to eql(foo: 'bar')
62
+ end
63
+ end
64
+
65
+ describe 'stringify_keys!' do
66
+ it 'returns a new hash with stringified keys' do
67
+ stringify_keys = t(:stringify_keys!)
68
+
69
+ input = { foo: 'bar' }
70
+ output = { 'foo' => 'bar' }
71
+
72
+ expect(stringify_keys[input]).to eql(output)
73
+ expect(input).to eql('foo' => 'bar')
74
+ end
75
+ end
76
+
77
+ describe 'map_values' do
78
+ it 'returns a new hash with given proc applied to values' do
79
+ map_values = t(:map_values, ->(value) { value.strip })
80
+
81
+ input = { 'foo' => ' bar ' }
82
+ output = { 'foo' => 'bar' }
83
+
84
+ expect(map_values[input]).to eql(output)
85
+ expect(input).to eql('foo' => ' bar ')
86
+ end
87
+ end
88
+
89
+ describe 'map_values!' do
90
+ it 'returns updated hash with given proc applied to values' do
91
+ map_values = t(:map_values!, ->(value) { value.strip })
92
+
93
+ input = { 'foo' => ' bar ' }
94
+ output = { 'foo' => 'bar' }
95
+
96
+ expect(map_values[input]).to eql(output)
97
+ expect(input).to eql('foo' => 'bar')
98
+ end
99
+ end
100
+
101
+ describe 'rename_keys' do
102
+ it 'returns a new hash with applied functions' do
103
+ map = t(:rename_keys, 'foo' => :foo)
104
+
105
+ input = { 'foo' => 'bar', :bar => 'baz' }
106
+ output = { foo: 'bar', bar: 'baz' }
107
+
108
+ expect(map[input]).to eql(output)
109
+ expect(input).to eql('foo' => 'bar', :bar => 'baz')
110
+ end
111
+ end
112
+
113
+ describe 'rename_keys!' do
114
+ it 'returns updated hash with applied functions' do
115
+ map = t(:rename_keys!, 'foo' => :foo)
116
+
117
+ input = { 'foo' => 'bar', :bar => 'baz' }
118
+ output = { foo: 'bar', bar: 'baz' }
119
+
120
+ map[input]
121
+
122
+ expect(input).to eql(output)
123
+ end
124
+ end
125
+
126
+ describe 'map_value' do
127
+ it 'applies function to value under specified key' do
128
+ transformation = t(:map_value, :user, t(:symbolize_keys))
129
+
130
+ input = { user: { 'name' => 'Jane' } }
131
+ output = { user: { name: 'Jane' } }
132
+
133
+ expect(transformation[input]).to eql(output)
134
+ expect(input).to eql(user: { 'name' => 'Jane' })
135
+ end
136
+ end
137
+
138
+ describe 'map_value!' do
139
+ it 'applies function to value under specified key' do
140
+ transformation = t(:map_value!, :user, t(:symbolize_keys))
141
+
142
+ input = { user: { 'name' => 'Jane' } }
143
+ output = { user: { name: 'Jane' } }
144
+
145
+ transformation[input]
146
+
147
+ expect(input).to eql(output)
148
+ end
149
+ end
150
+
29
151
  describe 'nest' do
30
152
  it 'returns new hash with keys nested under a new key' do
31
153
  nest = t(:nest, :baz, ['foo'])
@@ -101,60 +223,10 @@ describe 'Hash mapping with Transproc' do
101
223
  end
102
224
  end
103
225
 
104
- describe 'map_hash' do
105
- it 'returns a new hash with applied functions' do
106
- map = t(:map_hash, 'foo' => :foo)
107
-
108
- input = { 'foo' => 'bar', :bar => 'baz' }
109
- output = { foo: 'bar', bar: 'baz' }
110
-
111
- expect(map[input]).to eql(output)
112
- expect(input).to eql('foo' => 'bar', :bar => 'baz')
113
- end
114
- end
115
-
116
- describe 'map_hash!' do
117
- it 'returns updated hash with applied functions' do
118
- map = t(:map_hash!, 'foo' => :foo)
119
-
120
- input = { 'foo' => 'bar', :bar => 'baz' }
121
- output = { foo: 'bar', bar: 'baz' }
122
-
123
- map[input]
124
-
125
- expect(input).to eql(output)
126
- end
127
- end
128
-
129
- describe 'map_key' do
130
- it 'applies function to value under specified key' do
131
- transformation = t(:map_key, :user, t(:symbolize_keys))
132
-
133
- input = { user: { 'name' => 'Jane' } }
134
- output = { user: { name: 'Jane' } }
135
-
136
- expect(transformation[input]).to eql(output)
137
- expect(input).to eql(user: { 'name' => 'Jane' })
138
- end
139
- end
140
-
141
- describe 'map_key!' do
142
- it 'applies function to value under specified key' do
143
- transformation = t(:map_key!, :user, t(:symbolize_keys))
144
-
145
- input = { user: { 'name' => 'Jane' } }
146
- output = { user: { name: 'Jane' } }
147
-
148
- transformation[input]
149
-
150
- expect(input).to eql(output)
151
- end
152
- end
153
-
154
226
  describe 'nested transform' do
155
227
  it 'applies functions to nested hashes' do
156
228
  symbolize_keys = t(:symbolize_keys)
157
- map_user_key = t(:map_key, :user, symbolize_keys)
229
+ map_user_key = t(:map_value, :user, symbolize_keys)
158
230
 
159
231
  transformation = symbolize_keys >> map_user_key
160
232
 
@@ -168,7 +240,7 @@ describe 'Hash mapping with Transproc' do
168
240
  describe 'combining transformations' do
169
241
  it 'applies functions to the hash' do
170
242
  symbolize_keys = t(:symbolize_keys)
171
- map = t(:map_hash, user_name: :name, user_email: :email)
243
+ map = t(:rename_keys, user_name: :name, user_email: :email)
172
244
 
173
245
  transformation = symbolize_keys >> map
174
246
 
data/transproc.gemspec CHANGED
@@ -8,9 +8,9 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Transproc::VERSION.dup
9
9
  spec.authors = ["Piotr Solnica"]
10
10
  spec.email = ["piotr.solnica@gmail.com"]
11
- spec.summary = %q{Experimental functional transformations for Ruby}
11
+ spec.summary = %q{Transform Ruby objects in functional style}
12
12
  spec.description = spec.summary
13
- spec.homepage = ""
13
+ spec.homepage = "http://solnic.github.io/transproc/"
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")