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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MThlMjQxM2JhNmQ0YjZlMGFhNDQ4ZTZmODM0NDM4MDA2ZjMwYTM1ZA==
4
+ NjZiNzFlNzI2YWUxYzhkZWFkYzJiMDZlMjU2ZTBmNTYxZTc4ZWZlNg==
5
5
  data.tar.gz: !binary |-
6
- ZmExNWYyYzI0YjM3NmM3ZjliOTRlNWRlNjgwMDJiMzQ4YzI2ZWY1Zg==
6
+ ZTk3ZjUyMDhkZmY0NTVkMzAyODc4MzBmMzk3NTg5OThhMjIzMDZlMw==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- YTJiZmVkZjFmMTFhN2UwODM2YWIyZjJjYTJiZDFjMDE3YTYyNzg1NjkzYzUy
10
- NDZmYTJjYWYwOTYwNmJjYmFmYmE0MjZjYjhkZDdhZjg1NjlmZTFlZjNjNTQ2
11
- MDMwMDdlNjNlZDQzMjMyYzAwNWE3MTZmNjc1OTY5ZWRhYmI3NzU=
9
+ MDgzNzIyYWZjNDA1M2YzMGE2ZjFkYTVjNDEyNmUxZjQwNDYxYjMwODI0OGY1
10
+ MTk2M2IxNGQ2MzAxOTA4MTViNzU5ZGYxNTdmMDA4YWI5ZDJiMTM5MmJmYjM3
11
+ NTU3MWNlNThjODhhOWU1YjMzZDE2ZDkzMGIxYmQ1ODVjZjA1ODM=
12
12
  data.tar.gz: !binary |-
13
- NzYzMTZmNGM2M2VlZmVmZGZhOWNlM2M3NjEzNjkyZDdkOTY1ZDE5ZTJjNjk2
14
- NjZjNDU4ZDBjNDM1YTNkN2M2MmZmZjUwZjBkMmRhYTA1MTJiYWFiYTZjZTVm
15
- NjdmZTZlMTI0YzFlYTAzMzEwN2I1NWY1MTNmM2NhMzUyOGY0ODg=
13
+ ZjA2MzNmZmJjZjQwZWE5NTg2M2U2Y2Q2NmFmN2M0NmI1ZDczN2Y3YjE4NWU1
14
+ ZGUxZjFlODBhNDRmNTNmYjMwMjE2NzQ5MDVhYjEzMjBmN2E0Njc3MDVjYTJj
15
+ NDlkYjAzZWU2OGYzYzQyOTkyMGU1YzI5YjZiNDAzMTA2YTI2MWE=
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ lib/procme.rb
2
+ --no-private
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 (brand new and yet unpublished to RubyGems):
6
+ With Bundler:
5
7
 
8
+ ```ruby
9
+ gem 'procme'
6
10
  ```
7
- gem 'procme', git: 'git://github.com/zverok/procme.git'
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
- # Giving you have
24
+ # Given you have
14
25
  class Person < Struct.new(:name, :age, :gender)
15
26
  def greet(who)
16
- puts "#{name}: Hello, #{who}!"
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: 'Ellis'))
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
- # Giving you have
4
+ # Given you have
5
5
  class Person < Struct.new(:name, :age, :gender)
6
6
  def greet(who)
7
- puts "#{name}: Hello, #{who}!"
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: 'Ellis'))
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
- # ProcMe: DRY and clean blocks for your code
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
- def filter(hash)
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
- def set(hash)
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
- hash.each{|k, v| o.send("#{k}=", v)}
86
+ attrs.each{|k, v| o.send("#{k}=", v)}
15
87
  o
16
88
  end
17
89
  end
18
90
 
19
- def call(hash)
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.each{|k, v| o.send(k, *v)}
22
- o
120
+ ProcMe._singularize(hash.map{|k, v| o.send(k, *v)})
23
121
  end
24
122
  end
25
123
 
26
- def get(*array)
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
- array.map{|v| o.send(*v)}
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.1'
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.1
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-15 00:00:00.000000000 Z
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