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 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