augmented 0.0.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 +7 -0
- data/.gitignore +15 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +170 -0
- data/Rakefile +6 -0
- data/augmented.gemspec +23 -0
- data/lib/augmented/enumerators/indexing.rb +13 -0
- data/lib/augmented/enumerators.rb +7 -0
- data/lib/augmented/hashes/polymorphable.rb +20 -0
- data/lib/augmented/hashes/transformable.rb +67 -0
- data/lib/augmented/hashes.rb +9 -0
- data/lib/augmented/objects/pickable.rb +33 -0
- data/lib/augmented/objects/tackable.rb +21 -0
- data/lib/augmented/objects/thru.rb +14 -0
- data/lib/augmented/objects.rb +11 -0
- data/lib/augmented/procs/chainable.rb +13 -0
- data/lib/augmented/procs.rb +7 -0
- data/lib/augmented/symbols/arguable.rb +14 -0
- data/lib/augmented/symbols/comparing.rb +50 -0
- data/lib/augmented/symbols.rb +9 -0
- data/lib/augmented/version.rb +3 -0
- data/lib/augmented.rb +15 -0
- data/test/augmented/enumerators/indexing_test.rb +15 -0
- data/test/augmented/hashes/polymorphable_test.rb +45 -0
- data/test/augmented/hashes/transformable_test.rb +87 -0
- data/test/augmented/objects/pickable_test.rb +39 -0
- data/test/augmented/objects/tackable_test.rb +25 -0
- data/test/augmented/objects/thru_test.rb +22 -0
- data/test/augmented/procs/chainable_test.rb +22 -0
- data/test/augmented/symbols/arguable_test.rb +51 -0
- data/test/augmented/symbols/comparing_test.rb +131 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ab72b071901020afba04d0c492f1bc6363545a2b
|
4
|
+
data.tar.gz: 529677d1369c9d9125212faa80a6f71cef648e81
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3376e35d3240db0dd12e8dca2ae0798200d8b75fda9b1a6b90d6c6638de1f5d2df8dda2d26ca0763dd8b9e20f24183c9930005f1bf5cdb168a33c2985fc2a65c
|
7
|
+
data.tar.gz: 09f783c3f13bd508fe7d012955ade5c12d7c9dce2e215fa7d065a72669567a06cad27c3478d3a391d55386a8a5a01f334c0ec696d667dd67593a6cf37b6aff0f
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2016 brunze
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
# Augmented
|
2
|
+
|
3
|
+
`Augmented` is a library with some core-type utility methods that I frequently find myself copying across projects. It uses refinements instead of class modification for maximum control and an easy sleep at night.
|
4
|
+
|
5
|
+
Many of the methods in `Augmented` facilitate a more functional style of programming and cover a few tiny gaps in Ruby's solid functional support.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
In your Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'augmented'
|
13
|
+
```
|
14
|
+
|
15
|
+
Or:
|
16
|
+
|
17
|
+
$ gem install augmented
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
You have 3 ways of loading the refinements. You can load all of them at once:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
using Augmented
|
25
|
+
```
|
26
|
+
|
27
|
+
You can load all refinements for just one type:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
using Augmented::Objects
|
31
|
+
using Augmented::Hashes
|
32
|
+
using Augmented::Symbols
|
33
|
+
# etc.
|
34
|
+
```
|
35
|
+
|
36
|
+
Or you can load just the methods you need:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
using Augmented::Objects::Pickable
|
40
|
+
using Augmented::Symbols::Arguable
|
41
|
+
using Augmented::Procs::Chainable
|
42
|
+
# etc.
|
43
|
+
```
|
44
|
+
|
45
|
+
## Quick Examples
|
46
|
+
|
47
|
+
##### `Enumerator#index_by`
|
48
|
+
|
49
|
+
Builds an index of all elements of an enumerator according to the given criterion.
|
50
|
+
|
51
|
+
```ruby
|
52
|
+
['a', 'bb', 'ccccc'].to_enum.index_by(&:length)
|
53
|
+
# {1=>"a", 2=>"bb", 5=>"ccccc"}
|
54
|
+
```
|
55
|
+
|
56
|
+
##### `Hash#polymorph`
|
57
|
+
|
58
|
+
Creates an object from an Hash.
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
class Sheep
|
62
|
+
def initialize attributes
|
63
|
+
@sound = attributes[:sound]
|
64
|
+
end
|
65
|
+
|
66
|
+
def speak
|
67
|
+
puts @sound
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
{ type: 'Sheep', sound: 'meeehh' }.polymorph.speak
|
72
|
+
# meeehh
|
73
|
+
```
|
74
|
+
|
75
|
+
##### `Hash#transform`, `Hash#transform!`
|
76
|
+
|
77
|
+
Recursively applies functions to a tree of hashes.
|
78
|
+
|
79
|
+
```ruby
|
80
|
+
tree = { lorem: 'ipsum', dolor: [ { sit: 10}, { sit: 20 } ] }
|
81
|
+
triple = -> i { i * 3 }
|
82
|
+
|
83
|
+
tree.transform({ lorem: :upcase, dolor: { sit: triple } })
|
84
|
+
# {:lorem=>"IPSUM", :dolor=>[{:sit=>30}, {:sit=>60}]}
|
85
|
+
```
|
86
|
+
|
87
|
+
##### `Object#pick`
|
88
|
+
|
89
|
+
Calls a bunch of methods on an object and collects the results.
|
90
|
+
|
91
|
+
```ruby
|
92
|
+
class MyThing
|
93
|
+
def lorem; 'hello'; end
|
94
|
+
def ipsum; 'cruel'; end
|
95
|
+
def dolor; 'world'; end
|
96
|
+
end
|
97
|
+
|
98
|
+
MyThing.new.pick :lorem, :dolor
|
99
|
+
# {:lorem=>"hello", :dolor=>"world"}
|
100
|
+
```
|
101
|
+
|
102
|
+
##### `Object#tack`
|
103
|
+
|
104
|
+
Appends a bunch of singleton methods to an object.
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
Object.new.tack(id: 11, greet: -> { puts "hello I'm #{id}" }).greet
|
108
|
+
# hello I'm 11
|
109
|
+
```
|
110
|
+
|
111
|
+
##### `Object#thru`
|
112
|
+
|
113
|
+
Applies a function to an object and returns the result.
|
114
|
+
|
115
|
+
```ruby
|
116
|
+
filter_words = -> s { s.gsub(/bad/, '').squeeze(' ').strip }
|
117
|
+
|
118
|
+
'BAD WORDS, BAD WORDS'.downcase.thru(&filter_words).capitalize
|
119
|
+
# Words, words
|
120
|
+
```
|
121
|
+
|
122
|
+
##### `Proc#|`
|
123
|
+
|
124
|
+
Chains several procs together so they execute from left to right.
|
125
|
+
|
126
|
+
```ruby
|
127
|
+
sub_two = -> i { i - 2 }
|
128
|
+
triple = -> i { i * 3 }
|
129
|
+
add_twenty = -> i { i + 20 }
|
130
|
+
|
131
|
+
(sub_two | triple | add_twenty)[5]
|
132
|
+
# 29
|
133
|
+
```
|
134
|
+
|
135
|
+
##### `Symbol#with`
|
136
|
+
|
137
|
+
Like [`Symbol#to_proc`](http://ruby-doc.org/core-2.3.0/Symbol.html#method-i-to_proc) but allows you to pass some arguments along.
|
138
|
+
|
139
|
+
```ruby
|
140
|
+
class Eleven
|
141
|
+
def add_many *others
|
142
|
+
11 + others.reduce(0, :+)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
:add_many.with(1, 2, 3).call(Eleven.new)
|
147
|
+
# 17
|
148
|
+
```
|
149
|
+
|
150
|
+
##### `Symbol#eq`, `Symbol#neq`, `Symbol#lt`, `Symbol#lte`, `Symbol#gt`, `Symbol#gte`
|
151
|
+
|
152
|
+
Creates functions that compare an object's attribute.
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
class User
|
156
|
+
def initialize name
|
157
|
+
@name = name
|
158
|
+
end
|
159
|
+
attr_reader :name
|
160
|
+
end
|
161
|
+
|
162
|
+
users = [ User.new('Marianne'), User.new('Jeremy') ]
|
163
|
+
|
164
|
+
users.find &(:name.eq 'Marianne')
|
165
|
+
# <User:0x... name='Marianne'>
|
166
|
+
```
|
167
|
+
|
168
|
+
## Contributing
|
169
|
+
|
170
|
+
Do you have a method you would like to see added to this library? Perhaps something you keep copying from project to project but always found too small to bother with a gem? Feel free to submit a ticket/pull request with your idea.
|
data/Rakefile
ADDED
data/augmented.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'augmented/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "augmented"
|
8
|
+
spec.version = Augmented::VERSION
|
9
|
+
spec.authors = ["bruno"]
|
10
|
+
spec.email = ["bruno@brunze.com"]
|
11
|
+
spec.summary = %q{Useful extra methods for some Ruby core types.}
|
12
|
+
spec.description = %q{Adds a few useful extra methods to some of Ruby's core types, available as refinements.}
|
13
|
+
spec.homepage = "https://github.com/brunze/augmented"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Augmented
|
2
|
+
module Hashes
|
3
|
+
module Polymorphable
|
4
|
+
refine Hash do
|
5
|
+
|
6
|
+
def polymorph type_attribute_name = :type
|
7
|
+
type = (type_attribute_name if Class === type_attribute_name) ||
|
8
|
+
self[type_attribute_name] ||
|
9
|
+
self[type_attribute_name.to_s]
|
10
|
+
|
11
|
+
raise ArgumentError, 'missing the type required for polymorph' if type.to_s.empty?
|
12
|
+
|
13
|
+
klass = Class === type ? type : Object.const_get(type.to_s)
|
14
|
+
klass.new self
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'augmented/procs/chainable'
|
2
|
+
|
3
|
+
module Augmented
|
4
|
+
module Hashes
|
5
|
+
module Transformable
|
6
|
+
refine Hash do
|
7
|
+
|
8
|
+
def transform transforms
|
9
|
+
Helpers.validate! transforms
|
10
|
+
|
11
|
+
transforms.each_pair.with_object({}) do |(key, procable), new_hash|
|
12
|
+
value = self[key]
|
13
|
+
|
14
|
+
if procable.kind_of?(Hash) && value.kind_of?(Hash)
|
15
|
+
new_hash[key] = value.transform procable
|
16
|
+
elsif value.respond_to? :each
|
17
|
+
new_hash[key] = value.map{ |thing| thing.transform procable }
|
18
|
+
else
|
19
|
+
new_hash[key] = Helpers.make_one_proc(procable).call value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def transform! transforms
|
25
|
+
Helpers.validate! transforms
|
26
|
+
|
27
|
+
transforms.each_pair do |key, procable|
|
28
|
+
value = self[key]
|
29
|
+
|
30
|
+
if procable.kind_of?(Hash) && value.kind_of?(Hash)
|
31
|
+
self[key] = value.transform! procable
|
32
|
+
elsif value.respond_to? :each
|
33
|
+
self[key] = value.each{ |thing| thing.transform! procable }
|
34
|
+
else
|
35
|
+
self[key] = Helpers.make_one_proc(procable).call value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
module Helpers
|
46
|
+
using ::Augmented::Procs::Chainable
|
47
|
+
|
48
|
+
def self.make_one_proc thing
|
49
|
+
if thing.kind_of?(Array)
|
50
|
+
thing.map(&:to_proc).reduce{ |chain, proc| chain | proc }
|
51
|
+
else
|
52
|
+
thing.to_proc
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.validate! transforms
|
57
|
+
unless transforms.respond_to? :each_pair
|
58
|
+
raise ArgumentError, 'transformations must be specified in a map-like collection (must have `each_pair` enumerator)'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
private_constant :Helpers
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Augmented
|
2
|
+
module Objects
|
3
|
+
module Pickable
|
4
|
+
refine Object do
|
5
|
+
|
6
|
+
def pick *picks
|
7
|
+
ensure_array = -> thing { thing.kind_of?(Array) ? thing : Array[thing] }
|
8
|
+
|
9
|
+
if self.respond_to? :each
|
10
|
+
self.map{ |thing| thing.pick *picks }
|
11
|
+
else
|
12
|
+
picks.each_with_object({}) do |pick, result|
|
13
|
+
|
14
|
+
if pick.kind_of? Hash
|
15
|
+
|
16
|
+
pick.each do |attribute, nested_picks|
|
17
|
+
result[attribute] = self.__send__(attribute.to_sym).pick *ensure_array[nested_picks]
|
18
|
+
end
|
19
|
+
|
20
|
+
else
|
21
|
+
attribute = pick
|
22
|
+
|
23
|
+
result[attribute] = self.__send__(attribute.to_sym)
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Augmented
|
2
|
+
module Objects
|
3
|
+
module Tackable
|
4
|
+
refine Object do
|
5
|
+
|
6
|
+
def tack **functions
|
7
|
+
functions.each_pair do |name, thing|
|
8
|
+
function = case thing
|
9
|
+
when Proc, Method, UnboundMethod then thing
|
10
|
+
else proc{ thing } end
|
11
|
+
|
12
|
+
self.define_singleton_method name, function
|
13
|
+
end
|
14
|
+
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Augmented
|
2
|
+
module Symbols
|
3
|
+
module Arguable
|
4
|
+
refine Symbol do
|
5
|
+
|
6
|
+
# credit to Uri Agassi: http://stackoverflow.com/a/23711606/2792897
|
7
|
+
def with *args, &block
|
8
|
+
-> caller, *rest { caller.__send__ self, *rest, *args, &block }
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Augmented
|
2
|
+
module Symbols
|
3
|
+
module Comparing
|
4
|
+
refine Symbol do
|
5
|
+
|
6
|
+
def eq value = NO_VALUE
|
7
|
+
Helpers.make_compare_function self, :==, value
|
8
|
+
end
|
9
|
+
|
10
|
+
def neq value = NO_VALUE
|
11
|
+
Helpers.make_compare_function self, :!=, value
|
12
|
+
end
|
13
|
+
|
14
|
+
def lt value = NO_VALUE
|
15
|
+
Helpers.make_compare_function self, :<, value
|
16
|
+
end
|
17
|
+
|
18
|
+
def lte value = NO_VALUE
|
19
|
+
Helpers.make_compare_function self, :<=, value
|
20
|
+
end
|
21
|
+
|
22
|
+
def gt value = NO_VALUE
|
23
|
+
Helpers.make_compare_function self, :>, value
|
24
|
+
end
|
25
|
+
|
26
|
+
def gte value = NO_VALUE
|
27
|
+
Helpers.make_compare_function self, :>=, value
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
module Helpers
|
34
|
+
|
35
|
+
def self.make_compare_function method_name, operator, value
|
36
|
+
if value.equal? NO_VALUE
|
37
|
+
-> thing, other { thing.__send__(method_name).__send__(operator, other.__send__(method_name)) }
|
38
|
+
else
|
39
|
+
-> thing { thing.__send__(method_name).__send__(operator, value) }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
NO_VALUE = BasicObject.new
|
46
|
+
|
47
|
+
private_constant :NO_VALUE, :Helpers
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/augmented.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'augmented/version'
|
2
|
+
|
3
|
+
require 'augmented/enumerators'
|
4
|
+
require 'augmented/hashes'
|
5
|
+
require 'augmented/objects'
|
6
|
+
require 'augmented/procs'
|
7
|
+
require 'augmented/symbols'
|
8
|
+
|
9
|
+
module Augmented
|
10
|
+
include Enumerators
|
11
|
+
include Hashes
|
12
|
+
include Objects
|
13
|
+
include Procs
|
14
|
+
include Symbols
|
15
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'augmented/enumerators/indexing'
|
3
|
+
|
4
|
+
describe Augmented::Enumerators::Indexing do
|
5
|
+
using Augmented::Enumerators::Indexing
|
6
|
+
|
7
|
+
describe '#index_by' do
|
8
|
+
|
9
|
+
it 'returns a hash keyed by the results of invoking the criterion on every collection element and the values are the last element matching the criterion' do
|
10
|
+
['a', 'bbb', 'c', 'dd'].to_enum.index_by(&:length).must_equal({ 1 => 'c', 2 => 'dd', 3 => 'bbb' })
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'augmented/hashes/polymorphable'
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
describe Augmented::Hashes::Polymorphable do
|
6
|
+
using Augmented::Hashes::Polymorphable
|
7
|
+
|
8
|
+
describe '#polymorph' do
|
9
|
+
|
10
|
+
it 'returns an object of the class specified by the `:type` attribute, initialized with the hash itself' do
|
11
|
+
object = { type: 'OpenStruct', speak: 'meeehh' }.polymorph
|
12
|
+
|
13
|
+
object.must_be_instance_of OpenStruct
|
14
|
+
object.speak.must_equal 'meeehh'
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'type attribute' do
|
18
|
+
|
19
|
+
it 'can also be a string key in the hash' do
|
20
|
+
{ 'type' => 'OpenStruct' }.polymorph.must_be_instance_of OpenStruct
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'can be an arbitrary attribute in the hash' do
|
24
|
+
{ lorem_ipsum: 'OpenStruct' }.polymorph(:lorem_ipsum).must_be_instance_of OpenStruct
|
25
|
+
{ 'lorem_ipsum' => 'OpenStruct' }.polymorph(:lorem_ipsum).must_be_instance_of OpenStruct
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'can be a class' do
|
29
|
+
{ type: OpenStruct }.polymorph().must_be_instance_of OpenStruct
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'can be a class passed directly to the method, ignoring the type attribute in the hash' do
|
33
|
+
{ type: 'TotallyIgnoredClass' }.polymorph(OpenStruct).must_be_instance_of OpenStruct
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'raises an error if it cannot find a type class' do
|
39
|
+
proc{ {}.polymorph }.must_raise ArgumentError
|
40
|
+
proc{ { type: nil }.polymorph }.must_raise ArgumentError
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'augmented/hashes/transformable'
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
describe Augmented::Hashes::Transformable do
|
6
|
+
using Augmented::Hashes::Transformable
|
7
|
+
|
8
|
+
describe '#transform(!)' do
|
9
|
+
|
10
|
+
it 'mutates the values of the given keys by applying their respective procables' do
|
11
|
+
hash = { thing1: 100, thing2: OpenStruct.new(random_method_name: 900) }.freeze
|
12
|
+
|
13
|
+
new_hash = hash.transform thing1: -> i { i * 3 }, thing2: :random_method_name
|
14
|
+
|
15
|
+
new_hash[:thing1].must_equal 300
|
16
|
+
new_hash[:thing2].must_equal 900
|
17
|
+
|
18
|
+
|
19
|
+
# mutable version test:
|
20
|
+
hash = { thing1: 100, thing2: OpenStruct.new(random_method_name: 900) }
|
21
|
+
|
22
|
+
hash.transform! thing1: -> i { i * 3 }, thing2: :random_method_name
|
23
|
+
|
24
|
+
hash[:thing1].must_equal 300
|
25
|
+
hash[:thing2].must_equal 900
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'applies procables recursively when given a hash' do
|
29
|
+
hash = { a: { b: { c: 100 } } }.freeze
|
30
|
+
|
31
|
+
new_hash = hash.transform({ a: { b: { c: -> i { i * 3 } } } })
|
32
|
+
|
33
|
+
new_hash[:a][:b][:c].must_equal 300
|
34
|
+
|
35
|
+
|
36
|
+
# mutable version test:
|
37
|
+
hash = { a: { b: { c: 100 } } }
|
38
|
+
|
39
|
+
hash.transform!({ a: { b: { c: -> i { i * 3 } } } })
|
40
|
+
|
41
|
+
hash[:a][:b][:c].must_equal 300
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'applies procables to all elements of a collection if a value is iterable (iterable MUST be a collection of hashes)' do
|
45
|
+
hash = { a: [ { my_value: 10 }, { my_value: 20 } ] }.freeze
|
46
|
+
|
47
|
+
new_hash = hash.transform(a: { my_value: -> i { i * 3 } })
|
48
|
+
|
49
|
+
new_hash[:a].must_equal [ { my_value: 30 }, { my_value: 60 } ]
|
50
|
+
|
51
|
+
|
52
|
+
# mutable version test:
|
53
|
+
hash = { a: [ { my_value: 10 }, { my_value: 20 } ] }
|
54
|
+
|
55
|
+
hash.transform!(a: { my_value: -> i { i * 3 } })
|
56
|
+
|
57
|
+
hash[:a].must_equal [ { my_value: 30 }, { my_value: 60 } ]
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'can apply several procables to a value, supplied in an array, executed from left to right' do
|
61
|
+
add_ten = -> i { i + 10 }
|
62
|
+
double = -> i { i * 2 }
|
63
|
+
|
64
|
+
hash = { a: 2.5 }.freeze
|
65
|
+
|
66
|
+
new_hash = hash.transform a: [ :to_i, add_ten, double ]
|
67
|
+
|
68
|
+
new_hash[:a].must_equal 24
|
69
|
+
|
70
|
+
|
71
|
+
# mutable version test:
|
72
|
+
hash = { a: 2.5 }
|
73
|
+
|
74
|
+
hash.transform! a: [ :to_i, add_ten, double ]
|
75
|
+
|
76
|
+
hash[:a].must_equal 24
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'returns itself (mutable version only)' do
|
80
|
+
hash = {}
|
81
|
+
same_hash = hash.transform! Hash.new
|
82
|
+
|
83
|
+
same_hash.object_id.must_equal hash.object_id
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'augmented/objects/pickable'
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
describe Augmented::Objects::Pickable do
|
6
|
+
using Augmented::Objects::Pickable
|
7
|
+
|
8
|
+
describe '#pick' do
|
9
|
+
|
10
|
+
it 'returns an hash with the results of invoking the list of picks in the target' do
|
11
|
+
target = OpenStruct.new aaa: 111, bbb: 222, ccc: 333
|
12
|
+
|
13
|
+
target.pick(:aaa, :ccc).must_equal({ aaa: 111, ccc: 333 })
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'returns the result of invoking `pick` on every element of an enumerable target' do
|
17
|
+
target = [ OpenStruct.new(val: 11), OpenStruct.new(val: 22), OpenStruct.new(val: 33) ]
|
18
|
+
|
19
|
+
target.pick(:val).must_equal [{val: 11}, {val: 22}, {val: 33}]
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'applies picks recursively when provided with hashes' do
|
23
|
+
target = OpenStruct.new(aa: (OpenStruct.new bb: (OpenStruct.new cc: 33)))
|
24
|
+
|
25
|
+
target.pick(aa: { bb: :cc }).must_equal({ aa: { bb: { cc: 33 } } })
|
26
|
+
|
27
|
+
target = OpenStruct.new(dd: OpenStruct.new(ee: 55), ff: OpenStruct.new(gg: 77))
|
28
|
+
|
29
|
+
target.pick(dd: :ee, ff: :gg).must_equal({ dd: { ee: 55 }, ff: { gg: 77 } })
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'allows you to specify pick lists with arrays when picking recursively' do
|
33
|
+
target = OpenStruct.new aa: (OpenStruct.new bb: 22, cc: (OpenStruct.new dd: 44, ee: 55))
|
34
|
+
|
35
|
+
target.pick(aa: [:bb, cc: [:dd, :ee]]).must_equal({ aa: { bb: 22, cc: { dd: 44, ee: 55 } } })
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'augmented/objects/tackable'
|
3
|
+
|
4
|
+
describe Augmented::Objects::Tackable do
|
5
|
+
using Augmented::Objects::Tackable
|
6
|
+
|
7
|
+
describe '#tack' do
|
8
|
+
|
9
|
+
it 'attaches new methods to an object' do
|
10
|
+
obj = Object.new
|
11
|
+
|
12
|
+
obj.tack lorem: 123, ipsum: -> { self }
|
13
|
+
|
14
|
+
obj.lorem.must_equal 123
|
15
|
+
obj.ipsum.object_id.must_equal obj.object_id
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'returns self' do
|
19
|
+
obj = Object.new
|
20
|
+
obj.tack.object_id.must_equal obj.object_id
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'augmented/objects/thru'
|
3
|
+
|
4
|
+
describe Augmented::Objects::Thru do
|
5
|
+
using Augmented::Objects::Thru
|
6
|
+
|
7
|
+
describe '#thru' do
|
8
|
+
|
9
|
+
it 'returns the result of applying the given function to the object' do
|
10
|
+
plus_10 = -> i { i + 10 }
|
11
|
+
|
12
|
+
5.thru(&plus_10).must_equal 15
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'returns the object untouched if called without arguments' do
|
16
|
+
obj = Object.new
|
17
|
+
obj.thru.object_id.must_equal obj.object_id
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'augmented/procs/chainable'
|
3
|
+
|
4
|
+
describe Augmented::Procs::Chainable do
|
5
|
+
using Augmented::Procs::Chainable
|
6
|
+
|
7
|
+
describe '#|' do
|
8
|
+
|
9
|
+
it 'returns a function that invokes from left to right' do
|
10
|
+
|
11
|
+
add_one = -> (i) { i + 1 }
|
12
|
+
triple = -> (i) { i * 3 }
|
13
|
+
sub_two = -> (i) { i - 2 }
|
14
|
+
add_twenty = -> (i) { i + 20 }
|
15
|
+
|
16
|
+
chain = add_one | triple | sub_two | add_twenty
|
17
|
+
|
18
|
+
chain.call(1).must_equal 24
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'augmented/symbols/arguable'
|
3
|
+
|
4
|
+
describe Augmented::Symbols::Arguable do
|
5
|
+
using Augmented::Symbols::Arguable
|
6
|
+
|
7
|
+
describe '#with' do
|
8
|
+
|
9
|
+
before do
|
10
|
+
@eleven = Class.new do
|
11
|
+
def add other_number
|
12
|
+
11 + other_number
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_many one_number, another_number, *many_numbers
|
16
|
+
11 + one_number + another_number + many_numbers.reduce(0, :+)
|
17
|
+
end
|
18
|
+
|
19
|
+
def do_whatever
|
20
|
+
yield 11
|
21
|
+
end
|
22
|
+
end.new
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'returns a function that calls the method named <symbol> while also passing the arguments supplied' do
|
26
|
+
:add.with(9).call(@eleven).must_equal 20
|
27
|
+
:add_many.with(3, 6, 10, 20, 50).call(@eleven).must_equal 100
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'the returned function' do
|
31
|
+
|
32
|
+
it "it preserves Symbol's `to_proc` behavior of passing extra arguments, if supplied" do
|
33
|
+
:add.to_proc.call(@eleven, 4).must_equal 15
|
34
|
+
:add.with().call(@eleven, 4).must_equal 15
|
35
|
+
|
36
|
+
:add_many.with(10, 20).call(@eleven, 4, 5).must_equal 50
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'passes along the block supplied to `with`, if any' do
|
40
|
+
result = nil
|
41
|
+
set_result = -> i { result = i }
|
42
|
+
|
43
|
+
:do_whatever.with(&set_result).call(@eleven)
|
44
|
+
|
45
|
+
result.must_equal 11
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'augmented/symbols/comparing'
|
3
|
+
|
4
|
+
describe Augmented::Symbols::Comparing do
|
5
|
+
using Augmented::Symbols::Comparing
|
6
|
+
|
7
|
+
let(:dummy) { Struct.new :lorem_ipsum }
|
8
|
+
|
9
|
+
let(:thing_A) { dummy.new 123 }
|
10
|
+
let(:thing_B) { dummy.new 123 }
|
11
|
+
let(:thing_C) { dummy.new 987 }
|
12
|
+
let(:thing_D) { dummy.new 0 }
|
13
|
+
|
14
|
+
describe '#eq' do
|
15
|
+
|
16
|
+
it 'returns a function that sends <symbol> to two objects and compares the results with `==`' do
|
17
|
+
comparator = :lorem_ipsum.eq
|
18
|
+
|
19
|
+
comparator.call(thing_A, thing_B).must_equal true
|
20
|
+
comparator.call(thing_A, thing_C).must_equal false
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'if you give it a value, it returns a function that sends <symbol> to an object and compares the result to a given value using `==`' do
|
24
|
+
comparator = :lorem_ipsum.eq(123)
|
25
|
+
|
26
|
+
comparator.call(thing_A).must_equal true
|
27
|
+
comparator.call(thing_C).must_equal false
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#neq' do
|
33
|
+
|
34
|
+
it 'returns a function that sends <symbol> to two objects and compares the results with `!=`' do
|
35
|
+
comparator = :lorem_ipsum.neq
|
36
|
+
|
37
|
+
comparator.call(thing_A, thing_B).must_equal false
|
38
|
+
comparator.call(thing_A, thing_C).must_equal true
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'if you give it a value, it returns a function that sends <symbol> to an object and compares the result to a given value using `!=`' do
|
42
|
+
comparator = :lorem_ipsum.neq(123)
|
43
|
+
|
44
|
+
comparator.call(thing_A).must_equal false
|
45
|
+
comparator.call(thing_C).must_equal true
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
describe '#lt' do
|
51
|
+
|
52
|
+
it 'returns a function that sends <symbol> to two objects and compares the results with `<`' do
|
53
|
+
comparator = :lorem_ipsum.lt
|
54
|
+
|
55
|
+
comparator.call(thing_A, thing_B).must_equal false
|
56
|
+
comparator.call(thing_A, thing_C).must_equal true
|
57
|
+
comparator.call(thing_C, thing_B).must_equal false
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'if you give it a value, it returns a function that sends <symbol> to an object and compares the result to a given value using `<`' do
|
61
|
+
comparator = :lorem_ipsum.lt(123)
|
62
|
+
|
63
|
+
comparator.call(thing_A).must_equal false
|
64
|
+
comparator.call(thing_C).must_equal false
|
65
|
+
comparator.call(thing_D).must_equal true
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#lte' do
|
71
|
+
|
72
|
+
it 'returns a function that sends <symbol> to two objects and compares the results with `<=`' do
|
73
|
+
comparator = :lorem_ipsum.lte
|
74
|
+
|
75
|
+
comparator.call(thing_A, thing_B).must_equal true
|
76
|
+
comparator.call(thing_A, thing_C).must_equal true
|
77
|
+
comparator.call(thing_C, thing_B).must_equal false
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'if you give it a value, it returns a function that sends <symbol> to an object and compares the result to a given value using `<=`' do
|
81
|
+
comparator = :lorem_ipsum.lte(123)
|
82
|
+
|
83
|
+
comparator.call(thing_A).must_equal true
|
84
|
+
comparator.call(thing_C).must_equal false
|
85
|
+
comparator.call(thing_D).must_equal true
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
describe '#gt' do
|
91
|
+
|
92
|
+
it 'returns a function that sends <symbol> to two objects and compares the results with `>`' do
|
93
|
+
comparator = :lorem_ipsum.gt
|
94
|
+
|
95
|
+
comparator.call(thing_A, thing_B).must_equal false
|
96
|
+
comparator.call(thing_A, thing_C).must_equal false
|
97
|
+
comparator.call(thing_C, thing_B).must_equal true
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'if you give it a value, it returns a function that sends <symbol> to an object and compares the result to a given value using `>`' do
|
101
|
+
comparator = :lorem_ipsum.gt(123)
|
102
|
+
|
103
|
+
comparator.call(thing_A).must_equal false
|
104
|
+
comparator.call(thing_C).must_equal true
|
105
|
+
comparator.call(thing_D).must_equal false
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#gte' do
|
111
|
+
|
112
|
+
it 'returns a function that sends <symbol> to two objects and compares the results with `>=`' do
|
113
|
+
comparator = :lorem_ipsum.gte
|
114
|
+
|
115
|
+
comparator.call(thing_A, thing_B).must_equal true
|
116
|
+
comparator.call(thing_A, thing_C).must_equal false
|
117
|
+
comparator.call(thing_C, thing_B).must_equal true
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'if you give it a value, it returns a function that sends <symbol> to an object and compares the result to a given value using `>=`' do
|
121
|
+
comparator = :lorem_ipsum.gte(123)
|
122
|
+
|
123
|
+
comparator.call(thing_A).must_equal true
|
124
|
+
comparator.call(thing_C).must_equal true
|
125
|
+
comparator.call(thing_D).must_equal false
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
|
131
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: augmented
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- bruno
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-04 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
description: Adds a few useful extra methods to some of Ruby's core types, available
|
42
|
+
as refinements.
|
43
|
+
email:
|
44
|
+
- bruno@brunze.com
|
45
|
+
executables: []
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gitignore"
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE.txt
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- augmented.gemspec
|
55
|
+
- lib/augmented.rb
|
56
|
+
- lib/augmented/enumerators.rb
|
57
|
+
- lib/augmented/enumerators/indexing.rb
|
58
|
+
- lib/augmented/hashes.rb
|
59
|
+
- lib/augmented/hashes/polymorphable.rb
|
60
|
+
- lib/augmented/hashes/transformable.rb
|
61
|
+
- lib/augmented/objects.rb
|
62
|
+
- lib/augmented/objects/pickable.rb
|
63
|
+
- lib/augmented/objects/tackable.rb
|
64
|
+
- lib/augmented/objects/thru.rb
|
65
|
+
- lib/augmented/procs.rb
|
66
|
+
- lib/augmented/procs/chainable.rb
|
67
|
+
- lib/augmented/symbols.rb
|
68
|
+
- lib/augmented/symbols/arguable.rb
|
69
|
+
- lib/augmented/symbols/comparing.rb
|
70
|
+
- lib/augmented/version.rb
|
71
|
+
- test/augmented/enumerators/indexing_test.rb
|
72
|
+
- test/augmented/hashes/polymorphable_test.rb
|
73
|
+
- test/augmented/hashes/transformable_test.rb
|
74
|
+
- test/augmented/objects/pickable_test.rb
|
75
|
+
- test/augmented/objects/tackable_test.rb
|
76
|
+
- test/augmented/objects/thru_test.rb
|
77
|
+
- test/augmented/procs/chainable_test.rb
|
78
|
+
- test/augmented/symbols/arguable_test.rb
|
79
|
+
- test/augmented/symbols/comparing_test.rb
|
80
|
+
homepage: https://github.com/brunze/augmented
|
81
|
+
licenses:
|
82
|
+
- MIT
|
83
|
+
metadata: {}
|
84
|
+
post_install_message:
|
85
|
+
rdoc_options: []
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - ">="
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
requirements: []
|
99
|
+
rubyforge_project:
|
100
|
+
rubygems_version: 2.4.5
|
101
|
+
signing_key:
|
102
|
+
specification_version: 4
|
103
|
+
summary: Useful extra methods for some Ruby core types.
|
104
|
+
test_files:
|
105
|
+
- test/augmented/enumerators/indexing_test.rb
|
106
|
+
- test/augmented/hashes/polymorphable_test.rb
|
107
|
+
- test/augmented/hashes/transformable_test.rb
|
108
|
+
- test/augmented/objects/pickable_test.rb
|
109
|
+
- test/augmented/objects/tackable_test.rb
|
110
|
+
- test/augmented/objects/thru_test.rb
|
111
|
+
- test/augmented/procs/chainable_test.rb
|
112
|
+
- test/augmented/symbols/arguable_test.rb
|
113
|
+
- test/augmented/symbols/comparing_test.rb
|