transproc 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +7 -0
- data/CHANGELOG.md +23 -1
- data/Gemfile +3 -0
- data/README.md +30 -12
- data/lib/transproc.rb +61 -6
- data/lib/transproc/all.rb +1 -0
- data/lib/transproc/array.rb +94 -19
- data/lib/transproc/coercions.rb +140 -28
- data/lib/transproc/composer.rb +43 -0
- data/lib/transproc/conditional.rb +56 -0
- data/lib/transproc/function.rb +95 -4
- data/lib/transproc/hash.rb +218 -41
- data/lib/transproc/recursion.rb +61 -20
- data/lib/transproc/version.rb +1 -1
- data/spec/integration/array_spec.rb +2 -2
- data/spec/integration/coercions_spec.rb +8 -2
- data/spec/integration/composer_spec.rb +1 -1
- data/spec/integration/conditional_spec.rb +23 -0
- data/spec/integration/function_spec.rb +56 -0
- data/spec/integration/hash_spec.rb +124 -52
- data/transproc.gemspec +2 -2
- metadata +10 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2cc7103ff3c33f4d49ebf8bd85f049500f5b0a27
|
4
|
+
data.tar.gz: 08c4676baf065be02235c2a9885d62fadb514311
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a532a67708497aa9ddc6e53c10bddbcc1b0c9c71ac648b05f59b9d4c24ec61e08d2f713b535134824a67aec9f099be647e5593bbe3033a32dca96c164b1b0b85
|
7
|
+
data.tar.gz: b21cc5f7674752c33e4d55c3067f9f71ba372444d4ee497cc054a14293f89087232b55489e670aa7fafe891ce147a15924c1418009268c4875771d7a657c3431
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,4 +1,26 @@
|
|
1
|
-
## v0.
|
1
|
+
## v0.2.0 2015-04-14
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* `:map_values` hash transformation (AMHOL)
|
6
|
+
* `:map_value` hash transformation (AMHOL)
|
7
|
+
* `:map_value` hash transformation (AMHOL)
|
8
|
+
* `:guard` function (AMHOL)
|
9
|
+
* `:is` type-check function (solnic)
|
10
|
+
* `Function#to_ast` for easy inspection (solnic)
|
11
|
+
* Ability to define module with custom functions that will be auto-registered (solnic + splattael)
|
12
|
+
|
13
|
+
### Changed
|
14
|
+
|
15
|
+
* [BREAKING] `map_hash` renamed to `rename_keys`
|
16
|
+
* [BREAKING] `map_key` renamed to `map_value`
|
17
|
+
* [BREAKING] `map_array` no longer accepts multiple functions (AMHOL)
|
18
|
+
* All functions are now defined as module functions (solnic + splattael)
|
19
|
+
* Functions no longer create anonymous procs (solnic)
|
20
|
+
|
21
|
+
[Compare v0.1.3...v0.2.0](https://github.com/solnic/transproc/compare/v0.1.3...v0.2.0)
|
22
|
+
|
23
|
+
## v0.1.3 2015-04-07
|
2
24
|
|
3
25
|
### Added
|
4
26
|
|
data/Gemfile
CHANGED
@@ -3,10 +3,13 @@ source 'https://rubygems.org'
|
|
3
3
|
gemspec
|
4
4
|
|
5
5
|
group :test do
|
6
|
+
gem 'mutant'
|
7
|
+
gem 'mutant-rspec'
|
6
8
|
gem 'codeclimate-test-reporter', require: nil
|
7
9
|
end
|
8
10
|
|
9
11
|
group :tools do
|
10
12
|
gem 'byebug', platform: :mri
|
11
13
|
gem 'benchmark-ips'
|
14
|
+
gem 'guard-rspec'
|
12
15
|
end
|
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
|
8
|
+
# Transproc [![Join the chat at https://gitter.im/solnic/transproc](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/solnic/transproc?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
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]
|
@@ -39,21 +39,39 @@ Or install it yourself as:
|
|
39
39
|
require 'transproc/all'
|
40
40
|
|
41
41
|
# compose transformation functions
|
42
|
-
|
42
|
+
include Transproc::Helper
|
43
43
|
|
44
|
-
|
45
|
-
transformation[
|
46
|
-
# => {:name=>"Jane"}
|
47
|
-
|
48
|
-
# or using a helper (no, it's not a good idea to include it here :))
|
49
|
-
include Transproc::Composer
|
44
|
+
transformation = t(:map_array, t(:symbolize_keys) >> t(:rename_keys, user_name: :user))
|
45
|
+
transformation >>= t(:wrap, :address, [:city, :street, :zipcode])
|
50
46
|
|
51
|
-
|
52
|
-
|
47
|
+
# call the function
|
48
|
+
transformation.call(
|
49
|
+
[
|
50
|
+
{ 'user_name' => 'Jane',
|
51
|
+
'city' => 'NYC',
|
52
|
+
'street' => 'Street 1',
|
53
|
+
'zipcode' => '123' }
|
54
|
+
]
|
55
|
+
)
|
56
|
+
# => [{:user=>"Jane", :address=>{:city=>"NYC", :street=>"Street 1", :zipcode=>"123"}}]
|
57
|
+
|
58
|
+
# Define your own transformations easily
|
59
|
+
Transproc(:to_json, -> v { JSON.dump(v) })
|
60
|
+
|
61
|
+
Transproc(:to_json).call([{ name: 'Jane' }])
|
62
|
+
# => "[{\"name\":\"Jane\"}]"
|
63
|
+
|
64
|
+
# ...or create a module with custom transformations
|
65
|
+
module MyTransformations
|
66
|
+
extend Transproc::Functions
|
67
|
+
|
68
|
+
def load_json(v)
|
69
|
+
JSON.load(v)
|
70
|
+
end
|
53
71
|
end
|
54
72
|
|
55
|
-
|
56
|
-
# => {:name=>"Jane"}
|
73
|
+
(Transproc(:load_json) >> Transproc(:symbolize_keys)).call('[{"name":"Jane"}]')
|
74
|
+
# => [{ :name => "Jane" }]
|
57
75
|
```
|
58
76
|
|
59
77
|
## Credits
|
data/lib/transproc.rb
CHANGED
@@ -3,23 +3,78 @@ require 'transproc/function'
|
|
3
3
|
require 'transproc/composer'
|
4
4
|
|
5
5
|
module Transproc
|
6
|
-
|
6
|
+
module_function
|
7
|
+
|
8
|
+
# Register a new function
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# Transproc.register(:to_json, -> v { v.to_json })
|
12
|
+
#
|
13
|
+
# Transproc(:map_array, Transproc(:to_json))
|
14
|
+
#
|
15
|
+
#
|
16
|
+
# @return [Function]
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
def register(*args, &block)
|
7
20
|
name, fn = *args
|
8
21
|
functions[name] = fn || block
|
9
22
|
end
|
10
23
|
|
11
|
-
|
24
|
+
# Get registered function with provided name
|
25
|
+
#
|
26
|
+
# @param [Symbol] name The name of the registered function
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
def [](name)
|
30
|
+
functions.fetch(name)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Function registry
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
def functions
|
12
37
|
@_functions ||= {}
|
13
38
|
end
|
14
39
|
|
15
|
-
|
16
|
-
|
40
|
+
# Function container extension
|
41
|
+
#
|
42
|
+
# @example
|
43
|
+
# module MyTransformations
|
44
|
+
# extend Transproc::Functions
|
45
|
+
#
|
46
|
+
# def boom!(value)
|
47
|
+
# "#{value} BOOM!"
|
48
|
+
# end
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# Transproc(:boom!)['w00t!'] # => "w00t! BOOM!"
|
52
|
+
#
|
53
|
+
# @api public
|
54
|
+
module Functions
|
55
|
+
def method_added(meth)
|
56
|
+
module_function meth
|
57
|
+
Transproc.register(meth, method(meth))
|
58
|
+
end
|
17
59
|
end
|
18
60
|
end
|
19
61
|
|
62
|
+
# Access registered functions
|
63
|
+
#
|
64
|
+
# @example
|
65
|
+
# Transproc(:map_array, Transproc(:to_string))
|
66
|
+
#
|
67
|
+
# Transproc(:to_string) >> Transproc(-> v { v.upcase })
|
68
|
+
#
|
69
|
+
# @param [Symbol,Proc] fn The name of the registered function or an anonymous proc
|
70
|
+
# @param [Array] args Optional addition args that a given function may need
|
71
|
+
#
|
72
|
+
# @return [Function]
|
73
|
+
#
|
74
|
+
# @api public
|
20
75
|
def Transproc(fn, *args)
|
21
76
|
case fn
|
22
|
-
when Proc then Transproc::Function.new(fn, args)
|
23
|
-
when Symbol then Transproc::Function.new(Transproc[fn], args)
|
77
|
+
when Proc then Transproc::Function.new(fn, args: args)
|
78
|
+
when Symbol then Transproc::Function.new(Transproc[fn], args: args)
|
24
79
|
end
|
25
80
|
end
|
data/lib/transproc/all.rb
CHANGED
data/lib/transproc/array.rb
CHANGED
@@ -1,27 +1,102 @@
|
|
1
|
+
require 'transproc/hash'
|
2
|
+
|
1
3
|
module Transproc
|
2
|
-
|
3
|
-
|
4
|
-
|
4
|
+
# Transformation functions for Array objects
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# require 'transproc/array'
|
8
|
+
#
|
9
|
+
# include Transproc::Helper
|
10
|
+
#
|
11
|
+
# fn = t(:map_array, t(:symbolize_keys)) >> t(:wrap, :address, [:city, :zipcode])
|
12
|
+
#
|
13
|
+
# fn.call(
|
14
|
+
# [
|
15
|
+
# { 'city' => 'Boston', 'zipcode' => '123' },
|
16
|
+
# { 'city' => 'NYC', 'zipcode' => '312' }
|
17
|
+
# ]
|
18
|
+
# )
|
19
|
+
# # => [{ city: 'Boston', zipcode: '123' }, { city: 'NYC', zipcode: '312' }]
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
module ArrayTransformations
|
23
|
+
extend Functions
|
5
24
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
25
|
+
# Map array values using transformation function
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
#
|
29
|
+
# fn = Transproc(:map_array, -> v { v.upcase })
|
30
|
+
#
|
31
|
+
# fn.call ['foo', 'bar'] # => ["FOO", "BAR"]
|
32
|
+
#
|
33
|
+
# @param [Array] array The input array
|
34
|
+
# @param [Proc] fn The transformation function
|
35
|
+
#
|
36
|
+
# @return [Array]
|
37
|
+
#
|
38
|
+
# @api public
|
39
|
+
def map_array(array, fn)
|
40
|
+
map_array!(Array[*array], fn)
|
41
|
+
end
|
10
42
|
|
11
|
-
|
12
|
-
|
13
|
-
|
43
|
+
# Same as `map_array` but mutates the array
|
44
|
+
#
|
45
|
+
# @see ArrayTransformations.map_array
|
46
|
+
#
|
47
|
+
# @api public
|
48
|
+
def map_array!(array, fn)
|
49
|
+
array.map! { |value| fn[value] }
|
50
|
+
end
|
14
51
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
52
|
+
# Wrap array values using HashTransformations.nest function
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# fn = Transproc(:wrap, :address, [:city, :zipcode])
|
56
|
+
#
|
57
|
+
# fn.call [{ city: 'NYC', zipcode: '123' }]
|
58
|
+
# # => [{ address: { city: 'NYC', zipcode: '123' } }]
|
59
|
+
#
|
60
|
+
# @param [Array] array The input array
|
61
|
+
# @param [Object] key The nesting root key
|
62
|
+
# @param [Object] keys The nesting value keys
|
63
|
+
#
|
64
|
+
# @return [Array]
|
65
|
+
#
|
66
|
+
# @api public
|
67
|
+
def wrap(array, key, keys)
|
68
|
+
map_array(array, Transproc(:nest, key, keys))
|
22
69
|
end
|
23
|
-
|
24
|
-
|
70
|
+
|
71
|
+
# Group array values using provided root key and value keys
|
72
|
+
#
|
73
|
+
# @example
|
74
|
+
# fn = Transproc(:group, :tags, [:tag_name])
|
75
|
+
#
|
76
|
+
# fn.call [
|
77
|
+
# { task: 'Group it', tag: 'task' },
|
78
|
+
# { task: 'Group it', tag: 'important' }
|
79
|
+
# ]
|
80
|
+
# # => [{ task: 'Group it', tags: [{ tag: 'task' }, { tag: 'important' }]]
|
81
|
+
#
|
82
|
+
# @param [Array] array The input array
|
83
|
+
# @param [Object] key The nesting root key
|
84
|
+
# @param [Object] keys The nesting value keys
|
85
|
+
#
|
86
|
+
# @return [Array]
|
87
|
+
#
|
88
|
+
# @api public
|
89
|
+
def group(array, key, keys)
|
90
|
+
grouped = Hash.new { |h, k| h[k] = [] }
|
91
|
+
array.each do |hash|
|
92
|
+
hash = hash.dup
|
93
|
+
child = {}
|
94
|
+
keys.each { |k| child[k] = hash.delete(k) }
|
95
|
+
grouped[hash] << child
|
96
|
+
end
|
97
|
+
grouped.map do |root, children|
|
98
|
+
root.merge(key => children)
|
99
|
+
end
|
25
100
|
end
|
26
101
|
end
|
27
102
|
end
|
data/lib/transproc/coercions.rb
CHANGED
@@ -4,42 +4,154 @@ require 'bigdecimal'
|
|
4
4
|
require 'bigdecimal/util'
|
5
5
|
|
6
6
|
module Transproc
|
7
|
-
|
8
|
-
|
7
|
+
# Coercion functions for common types
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
module Coercions
|
11
|
+
extend Functions
|
9
12
|
|
10
|
-
|
11
|
-
|
12
|
-
].freeze
|
13
|
+
TRUE_VALUES = [true, 1, '1', 'on', 't', 'true', 'y', 'yes'].freeze
|
14
|
+
FALSE_VALUES = [false, 0, '0', 'off', 'f', 'false', 'n', 'no'].freeze
|
13
15
|
|
14
|
-
|
15
|
-
|
16
|
-
|
16
|
+
BOOLEAN_MAP = Hash[
|
17
|
+
TRUE_VALUES.product([true]) + FALSE_VALUES.product([false])
|
18
|
+
].freeze
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
-
|
20
|
+
# Coerce value into a string
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
# Transproc(:to_string)[1]
|
24
|
+
# # => "1"
|
25
|
+
#
|
26
|
+
# @param [Object] value The input value
|
27
|
+
#
|
28
|
+
# @return [String]
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def to_string(value)
|
32
|
+
value.to_s
|
33
|
+
end
|
21
34
|
|
22
|
-
|
23
|
-
|
24
|
-
|
35
|
+
# Coerce value into a symbol
|
36
|
+
#
|
37
|
+
# @example
|
38
|
+
# Transproc(:to_symbol)['foo']
|
39
|
+
# # => :foo
|
40
|
+
#
|
41
|
+
# @param [Object] value The input value
|
42
|
+
#
|
43
|
+
# @return [Symbol]
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
def to_symbol(value)
|
47
|
+
value.to_sym
|
48
|
+
end
|
25
49
|
|
26
|
-
|
27
|
-
|
28
|
-
|
50
|
+
# Coerce value into a integer
|
51
|
+
#
|
52
|
+
# @example
|
53
|
+
# Transproc(:to_integer)['1']
|
54
|
+
# # => 1
|
55
|
+
#
|
56
|
+
# @param [Object] value The input value
|
57
|
+
#
|
58
|
+
# @return [Integer]
|
59
|
+
#
|
60
|
+
# @api public
|
61
|
+
def to_integer(value)
|
62
|
+
value.to_i
|
63
|
+
end
|
29
64
|
|
30
|
-
|
31
|
-
|
32
|
-
|
65
|
+
# Coerce value into a float
|
66
|
+
#
|
67
|
+
# @example
|
68
|
+
# Transproc(:to_float)['1.2']
|
69
|
+
# # => 1.2
|
70
|
+
#
|
71
|
+
# @param [Object] value The input value
|
72
|
+
#
|
73
|
+
# @return [Float]
|
74
|
+
#
|
75
|
+
# @api public
|
76
|
+
def to_float(value)
|
77
|
+
value.to_f
|
78
|
+
end
|
33
79
|
|
34
|
-
|
35
|
-
|
36
|
-
|
80
|
+
# Coerce value into a decimal
|
81
|
+
#
|
82
|
+
# @example
|
83
|
+
# Transproc(:to_decimal)[1.2]
|
84
|
+
# # => #<BigDecimal:7fca32acea50,'0.12E1',18(36)>
|
85
|
+
#
|
86
|
+
# @param [Object] value The input value
|
87
|
+
#
|
88
|
+
# @return [Decimal]
|
89
|
+
#
|
90
|
+
# @api public
|
91
|
+
def to_decimal(value)
|
92
|
+
value.to_d
|
93
|
+
end
|
37
94
|
|
38
|
-
|
39
|
-
|
40
|
-
|
95
|
+
# Coerce value into a boolean
|
96
|
+
#
|
97
|
+
# @example
|
98
|
+
# Transproc(:to_boolean)['true']
|
99
|
+
# # => true
|
100
|
+
# Transproc(:to_boolean)['f']
|
101
|
+
# # => false
|
102
|
+
#
|
103
|
+
# @param [Object] value The input value
|
104
|
+
#
|
105
|
+
# @return [TrueClass,FalseClass]
|
106
|
+
#
|
107
|
+
# @api public
|
108
|
+
def to_boolean(value)
|
109
|
+
BOOLEAN_MAP.fetch(value)
|
110
|
+
end
|
111
|
+
|
112
|
+
# Coerce value into a date
|
113
|
+
#
|
114
|
+
# @example
|
115
|
+
# Transproc(:to_date)['2015-04-14']
|
116
|
+
# # => #<Date: 2015-04-14 ((2457127j,0s,0n),+0s,2299161j)>
|
117
|
+
#
|
118
|
+
# @param [Object] value The input value
|
119
|
+
#
|
120
|
+
# @return [Date]
|
121
|
+
#
|
122
|
+
# @api public
|
123
|
+
def to_date(value)
|
124
|
+
Date.parse(value)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Coerce value into a time
|
128
|
+
#
|
129
|
+
# @example
|
130
|
+
# Transproc(:to_time)['2015-04-14 12:01:45']
|
131
|
+
# # => 2015-04-14 12:01:45 +0200
|
132
|
+
#
|
133
|
+
# @param [Object] value The input value
|
134
|
+
#
|
135
|
+
# @return [Time]
|
136
|
+
#
|
137
|
+
# @api public
|
138
|
+
def to_time(value)
|
139
|
+
Time.parse(value)
|
140
|
+
end
|
41
141
|
|
42
|
-
|
43
|
-
|
142
|
+
# Coerce value into a datetime
|
143
|
+
#
|
144
|
+
# @example
|
145
|
+
# Transproc(:to_datetime)['2015-04-14 12:01:45']
|
146
|
+
# # => #<DateTime: 2015-04-14T12:01:45+00:00 ((2457127j,43305s,0n),+0s,2299161j)>
|
147
|
+
#
|
148
|
+
# @param [Object] value The input value
|
149
|
+
#
|
150
|
+
# @return [DateTime]
|
151
|
+
#
|
152
|
+
# @api public
|
153
|
+
def to_datetime(value)
|
154
|
+
DateTime.parse(value)
|
155
|
+
end
|
44
156
|
end
|
45
157
|
end
|