transproc 0.1.3 → 0.2.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.
- 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 [](https://gitter.im/solnic/transproc?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
9
9
|
|
10
10
|
[][gem]
|
11
11
|
[][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
|