procme 0.0.1 → 0.0.2
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 +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
|
+
[](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
|