procme 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/.yardopts +2 -0
- data/LICENSE.txt +22 -0
- data/README.md +67 -5
- data/examples/example.rb +20 -3
- data/lib/procme.rb +131 -9
- data/procme.gemspec +4 -1
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NjZiNzFlNzI2YWUxYzhkZWFkYzJiMDZlMjU2ZTBmNTYxZTc4ZWZlNg==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
ZTk3ZjUyMDhkZmY0NTVkMzAyODc4MzBmMzk3NTg5OThhMjIzMDZlMw==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
MDgzNzIyYWZjNDA1M2YzMGE2ZjFkYTVjNDEyNmUxZjQwNDYxYjMwODI0OGY1
|
10
|
+
MTk2M2IxNGQ2MzAxOTA4MTViNzU5ZGYxNTdmMDA4YWI5ZDJiMTM5MmJmYjM3
|
11
|
+
NTU3MWNlNThjODhhOWU1YjMzZDE2ZDkzMGIxYmQ1ODVjZjA1ODM=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ZjA2MzNmZmJjZjQwZWE5NTg2M2U2Y2Q2NmFmN2M0NmI1ZDczN2Y3YjE4NWU1
|
14
|
+
ZGUxZjFlODBhNDRmNTNmYjMwMjE2NzQ5MDVhYjEzMjBmN2E0Njc3MDVjYTJj
|
15
|
+
NDlkYjAzZWU2OGYzYzQyOTkyMGU1YzI5YjZiNDAzMTA2YTI2MWE=
|
data/.yardopts
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014-15 Victor 'Zverok' Shepelev
|
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
CHANGED
@@ -1,19 +1,34 @@
|
|
1
1
|
# ProcMe
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/procme.svg)](http://badge.fury.io/rb/procme)
|
4
|
+
|
3
5
|
## Install
|
4
|
-
With Bundler
|
6
|
+
With Bundler:
|
5
7
|
|
8
|
+
```ruby
|
9
|
+
gem 'procme'
|
6
10
|
```
|
7
|
-
|
11
|
+
|
12
|
+
in your Gemfile, then `bundle install`.
|
13
|
+
|
14
|
+
Or without:
|
8
15
|
```
|
16
|
+
gem install procme
|
17
|
+
```
|
18
|
+
|
19
|
+
[YARD docs](http://www.rubydoc.info/gems/procme)
|
9
20
|
|
10
21
|
## Usage
|
11
22
|
|
12
23
|
```ruby
|
13
|
-
#
|
24
|
+
# Given you have
|
14
25
|
class Person < Struct.new(:name, :age, :gender)
|
15
26
|
def greet(who)
|
16
|
-
|
27
|
+
"#{name}: Hello, #{who}!"
|
28
|
+
end
|
29
|
+
|
30
|
+
def greet!(who)
|
31
|
+
puts greet(name)
|
17
32
|
end
|
18
33
|
|
19
34
|
def inspect
|
@@ -48,12 +63,25 @@ p people.map(&set(gender: 'female'))
|
|
48
63
|
# => [#<John, female, 30>, #<Jane, female, 23>, #<Jake, female, 48>, #<Judith, female, 16>]
|
49
64
|
|
50
65
|
# ProcMe::call(method: args) - bulk call method with arguments:
|
51
|
-
people.each(&call(greet
|
66
|
+
people.each(&call(greet!: 'Ellis'))
|
52
67
|
# Output:
|
53
68
|
# John: Hello, Ellis!
|
54
69
|
# Jane: Hello, Ellis!
|
55
70
|
# Jake: Hello, Ellis!
|
56
71
|
# Judith: Hello, Ellis!
|
72
|
+
|
73
|
+
# also works with #map:
|
74
|
+
people.map(&call(greet: 'Ellis'))
|
75
|
+
# => ["John: Hello, Ellis!", "Jane: Hello, Ellis!", "Jake: Hello, Ellis!", "Judith: Hello, Ellis!"]
|
76
|
+
|
77
|
+
# ...and with several arguments:
|
78
|
+
p people.map(&:name).map(&call(sub: ['J', 'F']))
|
79
|
+
# => ["Fohn", "Fane", "Fake", "Fudith"]
|
80
|
+
|
81
|
+
# ...and even several method you can call!
|
82
|
+
p people.map(&:name).map(&call(:downcase, :'+' => ', hello'))
|
83
|
+
# => [["john", "John, hello"], ["jane", "Jane, hello"], ["jake", "Jake, hello"], ["judith", "Judith, hello"]]
|
84
|
+
# Note that each method call is performed on ORIGINAL object
|
57
85
|
```
|
58
86
|
|
59
87
|
## Rationale
|
@@ -94,3 +122,37 @@ P = ProcMe # to be short
|
|
94
122
|
|
95
123
|
people.select(&P.fltr(gender: 'female'))
|
96
124
|
```
|
125
|
+
|
126
|
+
## Should you use it?
|
127
|
+
|
128
|
+
Frankly, I don't know. Things that mimic core language features can be
|
129
|
+
extremely useful, yet potentially they make your code more obscure for
|
130
|
+
reader. Though I think that ProcMe's syntax is pretty self-explanatory,
|
131
|
+
you colleagues may have other opinion.
|
132
|
+
|
133
|
+
As for me, since invention of this little thingy I've found it extremely
|
134
|
+
handy and useful.
|
135
|
+
|
136
|
+
## Small gotchas
|
137
|
+
|
138
|
+
`ProcMe.fltr` uses `#===` while comparing values. This way you can filter
|
139
|
+
strings by regular expressions or numbers by ranges. One counterintuitive
|
140
|
+
things is going on when you try to filter by object class:
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
['test', 'me'].select(&fltr(class: String)) # => []
|
144
|
+
```
|
145
|
+
|
146
|
+
It's because you should check object itself, not its class, to match with
|
147
|
+
`===`. The solution is simple:
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
['test', 'me'].select(&fltr(itself: String)) # => []
|
151
|
+
```
|
152
|
+
|
153
|
+
`#itself` method is available in Ruby >= 2.0, and easily backported to
|
154
|
+
earlier versions.
|
155
|
+
|
156
|
+
## License
|
157
|
+
|
158
|
+
MIT
|
data/examples/example.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require_relative '../lib/procme'
|
3
3
|
|
4
|
-
#
|
4
|
+
# Given you have
|
5
5
|
class Person < Struct.new(:name, :age, :gender)
|
6
6
|
def greet(who)
|
7
|
-
|
7
|
+
"#{name}: Hello, #{who}!"
|
8
|
+
end
|
9
|
+
|
10
|
+
def greet!(who)
|
11
|
+
puts greet(name)
|
8
12
|
end
|
9
13
|
|
10
14
|
def inspect
|
@@ -39,9 +43,22 @@ p people.map(&set(gender: 'female'))
|
|
39
43
|
# => [#<John, female, 30>, #<Jane, female, 23>, #<Jake, female, 48>, #<Judith, female, 16>]
|
40
44
|
|
41
45
|
# ProcMe::call(method: [args]) - bulk call method with arguments:
|
42
|
-
people.each(&call(greet
|
46
|
+
people.each(&call(greet!: 'Ellis'))
|
43
47
|
# Output:
|
44
48
|
# John: Hello, Ellis!
|
45
49
|
# Jane: Hello, Ellis!
|
46
50
|
# Jake: Hello, Ellis!
|
47
51
|
# Judith: Hello, Ellis!
|
52
|
+
|
53
|
+
# also works with #map:
|
54
|
+
p people.map(&call(greet: 'Ellis'))
|
55
|
+
# => ["John: Hello, Ellis!", "Jane: Hello, Ellis!", "Jake: Hello, Ellis!", "Judith: Hello, Ellis!"]
|
56
|
+
|
57
|
+
# ...and with several arguments:
|
58
|
+
p people.map(&:name).map(&call(sub: ['J', 'F']))
|
59
|
+
# => ["Fohn", "Fane", "Fake", "Fudith"]
|
60
|
+
|
61
|
+
# ...and even several method you can call!
|
62
|
+
p people.map(&:name).map(&call(:downcase, :'+' => ', hello'))
|
63
|
+
# => [["john", "John, hello"], ["jane", "Jane, hello"], ["jake", "Jake, hello"], ["judith", "Judith, hello"]]
|
64
|
+
# Note that each method call is performed on ORIGINAL object
|
data/lib/procme.rb
CHANGED
@@ -1,7 +1,70 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
2
|
+
|
3
|
+
# ProcMe is DRY and clean blocks for your code.
|
4
|
+
#
|
5
|
+
# It provides four methods:
|
6
|
+
#
|
7
|
+
# {#fltr} - checks object attribute values
|
8
|
+
#
|
9
|
+
# ['test', 'me', 'please'].select(&fltr(length: 4))
|
10
|
+
# # => ['test']
|
11
|
+
#
|
12
|
+
# {#get} - get object attribute values
|
13
|
+
#
|
14
|
+
# ['test', 'me', 'please'].map(&get(:upcase, :length))
|
15
|
+
# # => [['TEST', 4], ['ME', 2'], ['PLEASE', 6]]
|
16
|
+
#
|
17
|
+
# {#call} - call methods on object
|
18
|
+
#
|
19
|
+
# ['test', 'me', 'please'].map(&call(gsub: ['e', '*']))
|
20
|
+
# # => ['t*st', 'm*', 'pl*as*']
|
21
|
+
#
|
22
|
+
# {#set} - set object attribute values
|
23
|
+
#
|
24
|
+
# S = Struct.new(:name)
|
25
|
+
# arr = [S.new('test'), S.new('me')]
|
26
|
+
# arr.each(&set(name: 'please'))
|
27
|
+
# arr # => [#<struct S name="please">, #<struct S name="please">]
|
28
|
+
#
|
29
|
+
# You can use the module as is:
|
30
|
+
#
|
31
|
+
# ['test', 'me', 'please'].select(&ProcMe.fltr(length: 4))
|
32
|
+
#
|
33
|
+
# or include it and then use:
|
34
|
+
#
|
35
|
+
# include ProcMe
|
36
|
+
#
|
37
|
+
# ['test', 'me', 'please'].select(&fltr(length: 4))
|
38
|
+
#
|
39
|
+
#
|
3
40
|
module ProcMe
|
4
|
-
|
41
|
+
# Constructs block, able to check objects attribute values
|
42
|
+
#
|
43
|
+
# @param attrs [Hash] hash of !{attribute name => value}
|
44
|
+
# @return [Proc] block accepting any object and returning +true+ or
|
45
|
+
# +false+
|
46
|
+
#
|
47
|
+
# Use it like this:
|
48
|
+
#
|
49
|
+
# some_array.select(&fltr(attr: value, other_attr: other_value))
|
50
|
+
#
|
51
|
+
# values are checked with +#===+ method, so you can do something like:
|
52
|
+
#
|
53
|
+
# ['some', 'strings'].select(&fltr(length: 3..5)) # => ['some']
|
54
|
+
#
|
55
|
+
# or like this:
|
56
|
+
#
|
57
|
+
# ['other', 'strings'].select(&fltr(upcase: /^S/)) # => ['strings']
|
58
|
+
#
|
59
|
+
# This approach has one gotcha:
|
60
|
+
#
|
61
|
+
# # wrong
|
62
|
+
# ['some', 'strings'].select(&fltr(class: String)) # => []
|
63
|
+
#
|
64
|
+
# # right
|
65
|
+
# ['some', 'strings'].select(&fltr(itself: String)) # => ['some', 'strings']
|
66
|
+
#
|
67
|
+
def filter(attrs)
|
5
68
|
lambda do |o|
|
6
69
|
hash.all?{|k, v| v === o.send(k)} # rubocop:disable Style/CaseEquality
|
7
70
|
end
|
@@ -9,26 +72,85 @@ module ProcMe
|
|
9
72
|
|
10
73
|
alias_method :fltr, :filter
|
11
74
|
|
12
|
-
|
75
|
+
# Constructs block, able to set objects attribute values
|
76
|
+
#
|
77
|
+
# @param attrs [Hash] hash of !{attribute name => value}
|
78
|
+
# @return [Proc] block, accepting any object and returning it
|
79
|
+
#
|
80
|
+
# Use it like this:
|
81
|
+
#
|
82
|
+
# some_array.each(&set(attr: value))
|
83
|
+
#
|
84
|
+
def set(attrs)
|
13
85
|
lambda do |o|
|
14
|
-
|
86
|
+
attrs.each{|k, v| o.send("#{k}=", v)}
|
15
87
|
o
|
16
88
|
end
|
17
89
|
end
|
18
90
|
|
19
|
-
|
91
|
+
# Constructs block, able to call methods on object
|
92
|
+
#
|
93
|
+
# @param methods list of symbols or !{method => args} pairs
|
94
|
+
# @return [Proc] block, accepting any object and returning results of
|
95
|
+
# sending methods to it.
|
96
|
+
#
|
97
|
+
# Use it like this:
|
98
|
+
#
|
99
|
+
# some_array.each(&call(:method, other_method: [args]))
|
100
|
+
#
|
101
|
+
# If you call only one method, block will return just a result of call
|
102
|
+
# for each object:
|
103
|
+
#
|
104
|
+
# ['test', 'me'].map(&call(sub: ['e', '*'])) # => ['t*st', 'm*']
|
105
|
+
#
|
106
|
+
# If you call several methods, it would be array of results for each
|
107
|
+
# object:
|
108
|
+
#
|
109
|
+
# ['test', 'me'].map(&call(sub: ['e', '*'], index: 'e'))
|
110
|
+
# # => [['t*st', 2], ['m*', 2]]
|
111
|
+
#
|
112
|
+
# The latter example also shows that +#call+ sends each method to object
|
113
|
+
# itself, not the result of previous method call.
|
114
|
+
#
|
115
|
+
def call(*methods)
|
116
|
+
h = methods.last.is_a?(Hash) ? methods.pop : {}
|
117
|
+
hash = Hash[*methods.flat_map{|sym| [sym, []]}].merge(h)
|
118
|
+
|
20
119
|
lambda do |o|
|
21
|
-
hash.
|
22
|
-
o
|
120
|
+
ProcMe._singularize(hash.map{|k, v| o.send(k, *v)})
|
23
121
|
end
|
24
122
|
end
|
25
123
|
|
26
|
-
|
124
|
+
# Constructs block, able to receive attribute values from object
|
125
|
+
#
|
126
|
+
# @param attrs list of symbols
|
127
|
+
# @return [Proc] block, accepting any object and returning its attr
|
128
|
+
# values.
|
129
|
+
#
|
130
|
+
# Use it like this:
|
131
|
+
#
|
132
|
+
# some_array.map(&get(:attr, :other))
|
133
|
+
#
|
134
|
+
# Extremely useful for sorting:
|
135
|
+
#
|
136
|
+
# ['John', 'Alice', 'jane'].sort_by(&get(:length, :downcase)
|
137
|
+
# # => ['jane', 'John', 'Alice']
|
138
|
+
#
|
139
|
+
# As with {#call}, `#get` returns array of results for several methods
|
140
|
+
# and one result for only one method (with is not very useful anyways,
|
141
|
+
# as `map(&get(:length))` is a full equivalent of `map(&:length)`).
|
142
|
+
#
|
143
|
+
def get(*attrs)
|
27
144
|
lambda do |o|
|
28
|
-
|
145
|
+
ProcMe._singularize(attrs.map{|v| o.send(*v)})
|
29
146
|
end
|
30
147
|
end
|
31
148
|
|
32
149
|
# now we can use both include ProcMe & no include approach
|
33
150
|
extend self # rubocop:disable Style/ModuleFunction
|
151
|
+
|
152
|
+
# @private
|
153
|
+
def _singularize(arr)
|
154
|
+
arr.length == 1 ? arr.first : arr
|
155
|
+
end
|
34
156
|
end
|
data/procme.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'procme'
|
3
|
-
s.version = '0.0.
|
3
|
+
s.version = '0.0.2'
|
4
4
|
s.authors = ['Victor Shepelev']
|
5
5
|
s.email = 'zverok.offline@gmail.com'
|
6
6
|
s.homepage = 'https://github.com/zverok/procme'
|
@@ -25,5 +25,8 @@ Gem::Specification.new do |s|
|
|
25
25
|
end
|
26
26
|
s.require_paths = ["lib"]
|
27
27
|
|
28
|
+
s.has_rdoc = 'yard'
|
29
|
+
|
28
30
|
s.add_development_dependency 'rubocop', '~> 0.30'
|
31
|
+
s.add_development_dependency 'rspec', '~> 3'
|
29
32
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: procme
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Victor Shepelev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubocop
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.30'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3'
|
27
41
|
description: ! " ProcMe provides you with methods for DRY and clean processing
|
28
42
|
of\n enumerables.\n"
|
29
43
|
email: zverok.offline@gmail.com
|
@@ -31,6 +45,8 @@ executables: []
|
|
31
45
|
extensions: []
|
32
46
|
extra_rdoc_files: []
|
33
47
|
files:
|
48
|
+
- .yardopts
|
49
|
+
- LICENSE.txt
|
34
50
|
- README.md
|
35
51
|
- examples/example.rb
|
36
52
|
- lib/procme.rb
|
@@ -60,3 +76,4 @@ signing_key:
|
|
60
76
|
specification_version: 4
|
61
77
|
summary: Useful DRY proc-s for Ruby
|
62
78
|
test_files: []
|
79
|
+
has_rdoc: yard
|