Fun with proc
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Gem Version


With Bundler:

gem 'procme'

in your Gemfile, then bundle install.

Or without:

gem install procme

YARD docs


# Given you have
class Person <, :age, :gender)
  def greet(who)
    "#{name}: Hello, #{who}!"

  def greet!(who)
    puts greet(name)

  def inspect
    "#<#{name}, #{gender}, #{age}>"

people = ['John', 30, 'male'),'Jane', 23, 'female'),'Jake', 48, 'male'),'Judith', 16, 'female')

# With ProcMe you can:
include ProcMe

# ProcMe::fltr(method: match) - filter by attribute values/method calls:
p 'female', age: 18...30))
# => [#<Jane, female, 23>]

# ProcMe::get(:method1, :method2) - bulk get attributes
p, :age))
# => [["male", 30], ["female", 23], ["male", 48], ["female", 16]]

# ...which is really useful when sorting:
p people.sort_by(&get(:gender, :age))
# => [#<Judith, female, 16>, #<Jane, female, 23>, #<John, male, 30>, #<Jake, male, 48>]

# ProcMe::set(attr: value) - bulk set value:
p 'female'))
# => [#<John, female, 30>, #<Jane, female, 23>, #<Jake, female, 48>, #<Judith, female, 16>]

# ProcMe::call(method: args) - bulk call method with arguments:
people.each(&call(greet!: 'Ellis'))
# Output:
#   John: Hello, Ellis!
#   Jane: Hello, Ellis!
#   Jake: Hello, Ellis!
#   Judith: Hello, Ellis!

# also works with #map: 'Ellis'))
# => ["John: Hello, Ellis!", "Jane: Hello, Ellis!", "Jake: Hello, Ellis!", "Judith: Hello, Ellis!"]

# ...and with several arguments:
p ['J', 'F']))
# => ["Fohn", "Fane", "Fake", "Fudith"]

# ...and even several method you can call!
p, :'+' => ', hello'))
# => [["john", "John, hello"], ["jane", "Jane, hello"], ["jake", "Jake, hello"], ["judith", "Judith, hello"]]
# Note that each method call is performed on ORIGINAL object


Look at symple example{|p| p.gender == 'female'} 'female'))

At a first glance, there's not a very big difference, even in symbols count (ok, ProcMe version 1 symbol shorter :)

Yet there's one important difference: ProcMe version is DRY. It's not DRY for the sake of DRY, it's for better, cleaner and error-prone code.

Assume you are copying the code while rewriting it:{|a| p.gender == 'female'}
#                 ^ bang! It was not DRY enough!

# With ProcMe 'female'))
# just no repeating - no place for errors

In fact, the rationale it the same, as it was for inventing Symbol#to_proc.

ProcMe is very small and simple, without any monkey-patching of core classes. Only four methods (fltr, get, set, call), which you can include in any namespace or use without inclusion, for ex:

P = ProcMe # to be short 'female'))

Should you use it?

Frankly, I don't know. Things that mimic core language features can be extremely useful, yet potentially they make your code more obscure for reader. Though I think that ProcMe's syntax is pretty self-explanatory, you colleagues may have other opinion.

As for me, since invention of this little thingy I've found it extremely handy and useful.

Small gotchas

ProcMe.fltr uses #=== while comparing values. This way you can filter strings by regular expressions or numbers by ranges. One counterintuitive things is going on when you try to filter by object class:

['test', 'me'].select(&fltr(class: String)) # => []

It's because you should check object itself, not its class, to match with ===. The solution is simple:

['test', 'me'].select(&fltr(itself: String)) # => ['test', 'me']

#itself method is available in Ruby >= 2.2, and easily backported to earlier versions.