transproc 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 2cc7103ff3c33f4d49ebf8bd85f049500f5b0a27
4
- data.tar.gz: 08c4676baf065be02235c2a9885d62fadb514311
3
+ metadata.gz: c88956f58489c894cec8a4dd8a8714e47b3b1af0
4
+ data.tar.gz: 570b8a17eb185d2129fda432dce71160d13fdcc0
5
5
  SHA512:
6
- metadata.gz: a532a67708497aa9ddc6e53c10bddbcc1b0c9c71ac648b05f59b9d4c24ec61e08d2f713b535134824a67aec9f099be647e5593bbe3033a32dca96c164b1b0b85
7
- data.tar.gz: b21cc5f7674752c33e4d55c3067f9f71ba372444d4ee497cc054a14293f89087232b55489e670aa7fafe891ce147a15924c1418009268c4875771d7a657c3431
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
@@ -3,7 +3,8 @@ sudo: false
3
3
  cache: bundler
4
4
  env:
5
5
  - CODECLIMATE_REPO_TOKEN=aead71de2239048f830499462c54b57dfc646f1d56ad5dcbbc3469a6ebaf97ca
6
- script: "bundle exec rake spec"
6
+ bundler_args: --without tools
7
+ script: 'bundle exec rake spec'
7
8
  rvm:
8
9
  - 2.0
9
10
  - 2.1
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: "rspec" do
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 [![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)
8
+ # Transproc [![Join the chat at https://gitter.im/solnic/transproc](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/solnic/transproc)
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]
12
- [![Dependency Status](https://gemnasium.com/solnic/transproc.png)][gemnasium]
12
+ [![Dependency Status](https://gemnasium.com/solnic/transproc.svg)][gemnasium]
13
13
  [![Code Climate](https://codeclimate.com/github/solnic/transproc/badges/gpa.svg)][codeclimate]
14
14
  [![Test Coverage](https://codeclimate.com/github/solnic/transproc/badges/coverage.svg)][codeclimate]
15
15
  [![Inline docs](http://inch-ci.org/github/solnic/transproc.svg?branch=master)][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
@@ -1,4 +1,4 @@
1
- require "rspec/core/rake_task"
1
+ require 'rspec/core/rake_task'
2
2
 
3
3
  task default: :spec
4
4
 
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 then Transproc::Function.new(Transproc[fn], args: args)
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
@@ -1,7 +1,9 @@
1
1
  require 'transproc'
2
2
 
3
+ require 'transproc/class'
3
4
  require 'transproc/coercions'
4
5
  require 'transproc/conditional'
5
6
  require 'transproc/array'
6
7
  require 'transproc/hash'
8
+ require 'transproc/object'
7
9
  require 'transproc/recursion'
@@ -16,7 +16,7 @@ module Transproc
16
16
  # { 'city' => 'NYC', 'zipcode' => '312' }
17
17
  # ]
18
18
  # )
19
- # # => [{ city: 'Boston', zipcode: '123' }, { city: 'NYC', zipcode: '312' }]
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