transproc 0.2.0 → 0.2.1
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/.rubocop.yml +66 -0
- data/.rubocop_todo.yml +11 -0
- data/.travis.yml +2 -1
- data/CHANGELOG.md +31 -2
- data/Gemfile +5 -0
- data/Guardfile +2 -2
- data/README.md +5 -4
- data/Rakefile +1 -1
- data/lib/transproc.rb +13 -2
- data/lib/transproc/all.rb +2 -0
- data/lib/transproc/array.rb +78 -1
- data/lib/transproc/class.rb +52 -0
- data/lib/transproc/composite.rb +50 -0
- data/lib/transproc/conditional.rb +0 -1
- data/lib/transproc/error.rb +16 -0
- data/lib/transproc/function.rb +8 -50
- data/lib/transproc/hash.rb +52 -2
- data/lib/transproc/object.rb +19 -0
- data/lib/transproc/version.rb +1 -1
- data/rakelib/mutant.rake +16 -0
- data/rakelib/rubocop.rake +18 -0
- data/spec/spec_helper.rb +4 -2
- data/spec/unit/array_transformations_spec.rb +166 -0
- data/spec/unit/class_transformations_spec.rb +47 -0
- data/spec/{integration → unit}/coercions_spec.rb +10 -10
- data/spec/{integration → unit}/composer_spec.rb +0 -0
- data/spec/{integration → unit}/conditional_spec.rb +2 -2
- data/spec/{integration → unit}/function_spec.rb +30 -7
- data/spec/{integration/hash_spec.rb → unit/hash_transformations_spec.rb} +71 -23
- data/spec/{integration → unit}/recursion_spec.rb +6 -9
- data/spec/unit/transproc_spec.rb +64 -0
- data/transproc.gemspec +10 -10
- metadata +28 -19
- data/spec/integration/array_spec.rb +0 -98
- data/spec/integration/transproc_spec.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c88956f58489c894cec8a4dd8a8714e47b3b1af0
|
4
|
+
data.tar.gz: 570b8a17eb185d2129fda432dce71160d13fdcc0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c81a55daec46a1cf7f46b0ce4aa99897f2efa598899f335891036fd9531b1e991fb969d4f17a31ef89743eea7cff21c8b72573bac9eb98e02fc45b4212a1a620
|
7
|
+
data.tar.gz: b6f9740cab83acad2875220322f1a93814ce6e1a861893cceb08903730f2f2208b11f98b229b6a54cf6da63f3b8d1925d52b00e009fc0567c6a953796ffd8032
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
inherit_from: .rubocop_todo.yml
|
2
|
+
|
3
|
+
# Exclude temporary files
|
4
|
+
AllCops:
|
5
|
+
Exclude:
|
6
|
+
- tmp/**/*
|
7
|
+
|
8
|
+
# Allow global Transproc method definition
|
9
|
+
Style/MethodName:
|
10
|
+
Exclude:
|
11
|
+
- lib/transproc.rb
|
12
|
+
|
13
|
+
# No need to handle LoadError in Rakefile
|
14
|
+
Lint/HandleExceptions:
|
15
|
+
Exclude:
|
16
|
+
- rakelib/*.rake
|
17
|
+
- spec/spec_helper.rb
|
18
|
+
|
19
|
+
# It’s quite readable when we know what we are doing
|
20
|
+
Lint/AssignmentInCondition:
|
21
|
+
Enabled: false
|
22
|
+
|
23
|
+
# The enforced style doesn’t match Vim’s defaults
|
24
|
+
Style/AlignParameters:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
# UTF-8 is perfectly fine in comments
|
28
|
+
Style/AsciiComments:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
# Allow using braces for value-returning blocks
|
32
|
+
Style/BlockDelimiters:
|
33
|
+
Enabled: false
|
34
|
+
|
35
|
+
# Documentation checked by Inch CI
|
36
|
+
Style/Documentation:
|
37
|
+
Enabled: false
|
38
|
+
|
39
|
+
# Early returns have their vices
|
40
|
+
Style/GuardClause:
|
41
|
+
Enabled: false
|
42
|
+
|
43
|
+
# Need to be skipped for >-> usage
|
44
|
+
Style/Lambda:
|
45
|
+
Enabled: false
|
46
|
+
|
47
|
+
# Multiline block chains are ok
|
48
|
+
Style/MultilineBlockChain:
|
49
|
+
Enabled: false
|
50
|
+
|
51
|
+
# Even a single escaped slash can be confusing
|
52
|
+
Style/RegexpLiteral:
|
53
|
+
MaxSlashes: 0
|
54
|
+
|
55
|
+
# Don’t introduce semantic fail/raise distinction
|
56
|
+
Style/SignalException:
|
57
|
+
EnforcedStyle: only_raise
|
58
|
+
|
59
|
+
# Allow compact style for specs
|
60
|
+
Style/ClassAndModuleChildren:
|
61
|
+
Exclude:
|
62
|
+
- spec/**/*_spec.rb
|
63
|
+
|
64
|
+
# Allow logical `or`/`and` in conditionals
|
65
|
+
Style/AndOr:
|
66
|
+
EnforcedStyle: conditionals
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# This configuration was generated by `rubocop --auto-gen-config`
|
2
|
+
# on 2015-04-16 18:31:27 +0100 using RuboCop version 0.26.0.
|
3
|
+
# The point is for the user to remove these configuration records
|
4
|
+
# one by one as the offenses are removed from the code base.
|
5
|
+
# Note that changes in the inspected code, or installation of new
|
6
|
+
# versions of RuboCop, may require this file to be generated again.
|
7
|
+
|
8
|
+
# Offense count: 11
|
9
|
+
# Configuration parameters: AllowURI.
|
10
|
+
Metrics/LineLength:
|
11
|
+
Max: 110
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,10 +1,39 @@
|
|
1
|
+
## v0.2.1 2015-05-17
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* `:constructor_inject` injects arguments into the constructor of a class (AMHOL)
|
6
|
+
* `:set_ivars` which allocates an object and sets instance variables from a hash (key/value pairs) on an object (AMHOL)
|
7
|
+
* `:combine` which merges multiple arrays into one using "join keys" (solnic)
|
8
|
+
* `:reject_keys` which rejects specified keys in a hash (solnic)
|
9
|
+
* `:accept_keys` which accepts specified keys in a hash (solnic)
|
10
|
+
* `:extract_key` converts the array of hashes to array of values (nepalez)
|
11
|
+
|
12
|
+
### Changed
|
13
|
+
|
14
|
+
* `:unwrap` can be called without keys (solnic)
|
15
|
+
* `Transproc.register` raises a meaningful error when a given function is already registered (kwando)
|
16
|
+
* `Transproc[]` raises a meaningful error when a given function doesn't exist (kwando)
|
17
|
+
* `Transproc[]` raises a meaningful error when a transformation crashes (kwando)
|
18
|
+
|
19
|
+
### Fixed
|
20
|
+
|
21
|
+
* `Transproc()` no longer creates a function if it's already a function (splattael)
|
22
|
+
* A couple of mistakes in the API docs (AMHOL)
|
23
|
+
|
24
|
+
### Internal
|
25
|
+
|
26
|
+
* Rubocop integration \o/ (AMHOL)
|
27
|
+
|
28
|
+
[Compare v0.2.0...v0.2.1](https://github.com/solnic/transproc/compare/v0.2.0...v0.2.1)
|
29
|
+
|
1
30
|
## v0.2.0 2015-04-14
|
2
31
|
|
3
32
|
### Added
|
4
33
|
|
34
|
+
* `:map_keys` hash transformation (AMHOL)
|
35
|
+
* `:stringify_keys` hash transformation (AMHOL)
|
5
36
|
* `:map_values` hash transformation (AMHOL)
|
6
|
-
* `:map_value` hash transformation (AMHOL)
|
7
|
-
* `:map_value` hash transformation (AMHOL)
|
8
37
|
* `:guard` function (AMHOL)
|
9
38
|
* `:is` type-check function (solnic)
|
10
39
|
* `Function#to_ast` for easy inspection (solnic)
|
data/Gemfile
CHANGED
@@ -3,13 +3,18 @@ source 'https://rubygems.org'
|
|
3
3
|
gemspec
|
4
4
|
|
5
5
|
group :test do
|
6
|
+
gem 'equalizer'
|
7
|
+
gem 'anima'
|
6
8
|
gem 'mutant'
|
7
9
|
gem 'mutant-rspec'
|
8
10
|
gem 'codeclimate-test-reporter', require: nil
|
9
11
|
end
|
10
12
|
|
11
13
|
group :tools do
|
14
|
+
gem 'rubocop', '~> 0.30.0'
|
12
15
|
gem 'byebug', platform: :mri
|
13
16
|
gem 'benchmark-ips'
|
17
|
+
gem 'guard'
|
14
18
|
gem 'guard-rspec'
|
19
|
+
gem 'guard-rubocop'
|
15
20
|
end
|
data/Guardfile
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
guard :rspec, cmd:
|
1
|
+
guard :rspec, cmd: 'rspec' do
|
2
2
|
# run all specs if configuration is modified
|
3
3
|
watch('Guardfile') { 'spec' }
|
4
4
|
watch('Gemfile.lock') { 'spec' }
|
@@ -12,5 +12,5 @@ guard :rspec, cmd: "rspec" do
|
|
12
12
|
# run a spec if it is modified
|
13
13
|
watch(%r{\Aspec/(?:unit|integration)/.+_spec\.rb\z})
|
14
14
|
|
15
|
-
notification :tmux, display_message: true
|
15
|
+
notification :tmux, display_message: true if ENV.key?('TMUX')
|
16
16
|
end
|
data/README.md
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
[coveralls]: https://coveralls.io/r/solnic/transproc
|
6
6
|
[inchpages]: http://inch-ci.org/github/solnic/transproc
|
7
7
|
|
8
|
-
# Transproc [](https://gitter.im/solnic/transproc
|
8
|
+
# Transproc [](https://gitter.im/solnic/transproc)
|
9
9
|
|
10
10
|
[][gem]
|
11
11
|
[][travis]
|
12
|
-
[][gemnasium]
|
13
13
|
[][codeclimate]
|
14
14
|
[][codeclimate]
|
15
15
|
[][inchpages]
|
@@ -36,6 +36,7 @@ Or install it yourself as:
|
|
36
36
|
## Usage
|
37
37
|
|
38
38
|
``` ruby
|
39
|
+
require 'json'
|
39
40
|
require 'transproc/all'
|
40
41
|
|
41
42
|
# compose transformation functions
|
@@ -56,7 +57,7 @@ transformation.call(
|
|
56
57
|
# => [{:user=>"Jane", :address=>{:city=>"NYC", :street=>"Street 1", :zipcode=>"123"}}]
|
57
58
|
|
58
59
|
# Define your own transformations easily
|
59
|
-
Transproc(:to_json, -> v { JSON.dump(v) })
|
60
|
+
Transproc.register(:to_json, -> v { JSON.dump(v) })
|
60
61
|
|
61
62
|
Transproc(:to_json).call([{ name: 'Jane' }])
|
62
63
|
# => "[{\"name\":\"Jane\"}]"
|
@@ -70,7 +71,7 @@ module MyTransformations
|
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
73
|
-
(Transproc(:load_json) >> Transproc(:symbolize_keys)).call('[{"name":"Jane"}]')
|
74
|
+
(Transproc(:load_json) >> Transproc(:map_array, Transproc(:symbolize_keys))).call('[{"name":"Jane"}]')
|
74
75
|
# => [{ :name => "Jane" }]
|
75
76
|
```
|
76
77
|
|
data/Rakefile
CHANGED
data/lib/transproc.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'transproc/version'
|
2
2
|
require 'transproc/function'
|
3
3
|
require 'transproc/composer'
|
4
|
+
require 'transproc/error'
|
4
5
|
|
5
6
|
module Transproc
|
6
7
|
module_function
|
@@ -18,6 +19,9 @@ module Transproc
|
|
18
19
|
# @api public
|
19
20
|
def register(*args, &block)
|
20
21
|
name, fn = *args
|
22
|
+
if functions.include?(name)
|
23
|
+
raise FunctionAlreadyRegisteredError, "function #{name} is already defined"
|
24
|
+
end
|
21
25
|
functions[name] = fn || block
|
22
26
|
end
|
23
27
|
|
@@ -27,7 +31,9 @@ module Transproc
|
|
27
31
|
#
|
28
32
|
# @api private
|
29
33
|
def [](name)
|
30
|
-
functions.fetch(name)
|
34
|
+
functions.fetch(name) {
|
35
|
+
raise FunctionNotFoundError, "no registered function for #{name}"
|
36
|
+
}
|
31
37
|
end
|
32
38
|
|
33
39
|
# Function registry
|
@@ -75,6 +81,11 @@ end
|
|
75
81
|
def Transproc(fn, *args)
|
76
82
|
case fn
|
77
83
|
when Proc then Transproc::Function.new(fn, args: args)
|
78
|
-
when Symbol
|
84
|
+
when Symbol
|
85
|
+
fun = Transproc[fn]
|
86
|
+
case fun
|
87
|
+
when Transproc::Function, Transproc::Composite then fun
|
88
|
+
else Transproc::Function.new(fun, args: args)
|
89
|
+
end
|
79
90
|
end
|
80
91
|
end
|
data/lib/transproc/all.rb
CHANGED
data/lib/transproc/array.rb
CHANGED
@@ -16,7 +16,7 @@ module Transproc
|
|
16
16
|
# { 'city' => 'NYC', 'zipcode' => '312' }
|
17
17
|
# ]
|
18
18
|
# )
|
19
|
-
# # => [{
|
19
|
+
# # => [{:address=>{:city=>"Boston", :zipcode=>"123"}}, {:address=>{:city=>"NYC", :zipcode=>"312"}}]
|
20
20
|
#
|
21
21
|
# @api public
|
22
22
|
module ArrayTransformations
|
@@ -98,5 +98,82 @@ module Transproc
|
|
98
98
|
root.merge(key => children)
|
99
99
|
end
|
100
100
|
end
|
101
|
+
|
102
|
+
# Combines two arrays by merging child items from right array using join keys
|
103
|
+
#
|
104
|
+
# @example
|
105
|
+
# fn = t(:combine, [[:tasks, name: :user]])
|
106
|
+
#
|
107
|
+
# fn.call([[{ name: 'Jane' }], [{ user: 'Jane', title: 'One' }]])
|
108
|
+
# # => [{:name=>"Jane", :tasks=>[{:user=>"Jane", :title=>"One"}]}]
|
109
|
+
#
|
110
|
+
# @param [Array<Array>] array The input array
|
111
|
+
# @param [Array<Hash>] mappings The mapping definitions array
|
112
|
+
#
|
113
|
+
# @return [Array<Hash>]
|
114
|
+
#
|
115
|
+
# @api public
|
116
|
+
def combine(array, mappings)
|
117
|
+
root, groups = array
|
118
|
+
|
119
|
+
cache = Hash.new { |h, k| h[k] = Hash.new }
|
120
|
+
|
121
|
+
root.map { |parent|
|
122
|
+
child_hash = {}
|
123
|
+
|
124
|
+
groups.each_with_index do |candidates, index|
|
125
|
+
key, keys, group_mappings = mappings[index]
|
126
|
+
|
127
|
+
children =
|
128
|
+
if group_mappings
|
129
|
+
combine(candidates, group_mappings)
|
130
|
+
else
|
131
|
+
candidates
|
132
|
+
end
|
133
|
+
|
134
|
+
child_keys = keys.values
|
135
|
+
pkey_value = parent.values_at(*keys.keys) # ugh
|
136
|
+
|
137
|
+
cache[key][child_keys] ||= children.group_by { |child|
|
138
|
+
child.values_at(*child_keys)
|
139
|
+
}
|
140
|
+
child_arr = cache[key][child_keys][pkey_value] || []
|
141
|
+
|
142
|
+
child_hash.update(key => child_arr)
|
143
|
+
end
|
144
|
+
|
145
|
+
parent.merge(child_hash)
|
146
|
+
}
|
147
|
+
end
|
148
|
+
|
149
|
+
# Converts the array of hashes to array of values, extracted by given key
|
150
|
+
#
|
151
|
+
# @example
|
152
|
+
# fn = t(:extract_key, :name)
|
153
|
+
# fn.call [
|
154
|
+
# { name: 'Alice', role: 'sender' },
|
155
|
+
# { name: 'Bob', role: 'receiver' },
|
156
|
+
# { role: 'listener' }
|
157
|
+
# ]
|
158
|
+
# # => ['Alice', 'Bob', nil]
|
159
|
+
#
|
160
|
+
# @param [Array<Hash>] array The input array of hashes
|
161
|
+
# @param [Object] key The key to extract values by
|
162
|
+
#
|
163
|
+
# @return [Array]
|
164
|
+
#
|
165
|
+
# @api public
|
166
|
+
def extract_key(array, key)
|
167
|
+
extract_key!(Array[*array], key)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Same as `extract_key` but mutates the array
|
171
|
+
#
|
172
|
+
# @see ArrayTransformations.extract_key
|
173
|
+
#
|
174
|
+
# @api public
|
175
|
+
def extract_key!(array, key)
|
176
|
+
map_array!(array, -> v { v[key] })
|
177
|
+
end
|
101
178
|
end
|
102
179
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Transproc
|
2
|
+
# Transformation functions for Classes
|
3
|
+
#
|
4
|
+
# @example
|
5
|
+
# require 'transproc/class'
|
6
|
+
#
|
7
|
+
# include Transproc::Helper
|
8
|
+
#
|
9
|
+
# fn = t(:constructor_inject, 'User', :name, :age)
|
10
|
+
#
|
11
|
+
# fn[Struct]
|
12
|
+
# # => Struct::User
|
13
|
+
#
|
14
|
+
# @api public
|
15
|
+
module ClassTransformations
|
16
|
+
extend Functions
|
17
|
+
|
18
|
+
# Inject given arguments into the constructor of the class
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
# Transproct(:constructor_inject, 'User', :name, :age)[Struct]
|
22
|
+
# # => Struct::User
|
23
|
+
#
|
24
|
+
# @param [Class]
|
25
|
+
#
|
26
|
+
# @return [Mixed]
|
27
|
+
#
|
28
|
+
# @api public
|
29
|
+
def constructor_inject(*args, klass)
|
30
|
+
klass.new(*args)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Set instance variables from the hash argument (key/value pairs) on the object
|
34
|
+
#
|
35
|
+
# @example
|
36
|
+
# Transproc(:set_ivars, Object)[name: 'Jane', age: 25]
|
37
|
+
# # => #<Object:0x007f411d06a210 @name="Jane", @age=25>
|
38
|
+
#
|
39
|
+
# @param [Object]
|
40
|
+
#
|
41
|
+
# @return [Object]
|
42
|
+
#
|
43
|
+
# @api public
|
44
|
+
def set_ivars(ivar_hash, klass)
|
45
|
+
object = klass.allocate
|
46
|
+
ivar_hash.each do |ivar_name, ivar_value|
|
47
|
+
object.instance_variable_set("@#{ivar_name}", ivar_value)
|
48
|
+
end
|
49
|
+
object
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Transproc
|
2
|
+
# Composition of two functions
|
3
|
+
#
|
4
|
+
# @api private
|
5
|
+
class Composite
|
6
|
+
# @return [Proc]
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
attr_reader :left
|
10
|
+
|
11
|
+
# @return [Proc]
|
12
|
+
#
|
13
|
+
# @api private
|
14
|
+
attr_reader :right
|
15
|
+
|
16
|
+
# @api private
|
17
|
+
def initialize(left, right)
|
18
|
+
@left = left
|
19
|
+
@right = right
|
20
|
+
end
|
21
|
+
|
22
|
+
# Call right side with the result from the left side
|
23
|
+
#
|
24
|
+
# @param [Object] value The input value
|
25
|
+
#
|
26
|
+
# @return [Object]
|
27
|
+
#
|
28
|
+
# @api public
|
29
|
+
def call(value)
|
30
|
+
right[left[value]]
|
31
|
+
end
|
32
|
+
alias_method :[], :call
|
33
|
+
|
34
|
+
# @see Function#compose
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
def compose(other)
|
38
|
+
self.class.new(self, other)
|
39
|
+
end
|
40
|
+
alias_method :+, :compose
|
41
|
+
alias_method :>>, :compose
|
42
|
+
|
43
|
+
# @see Function#to_ast
|
44
|
+
#
|
45
|
+
# @api public
|
46
|
+
def to_ast
|
47
|
+
left.to_ast << right.to_ast
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|