Project

procme

0.0
No commit activity in last 3 years
No release in over 3 years
ProcMe provides you with methods for DRY and clean processing of enumerables.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 3
~> 0.30
 Project Readme

ProcMe

Gem Version

Install

With Bundler:

gem 'procme'

in your Gemfile, then bundle install.

Or without:

gem install procme

YARD docs

Usage

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

  def greet!(who)
    puts greet(name)
  end

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

people = [
  Person.new('John', 30, 'male'),
  Person.new('Jane', 23, 'female'),
  Person.new('Jake', 48, 'male'),
  Person.new('Judith', 16, 'female')
]

# With ProcMe you can:
include ProcMe

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

# ProcMe::get(:method1, :method2) - bulk get attributes
p people.map(&get(:gender, :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 people.map(&set(gender: '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:
people.map(&call(greet: 'Ellis'))
# => ["John: Hello, Ellis!", "Jane: Hello, Ellis!", "Jake: Hello, Ellis!", "Judith: Hello, Ellis!"]

# ...and with several arguments:
p people.map(&:name).map(&call(sub: ['J', 'F']))
# => ["Fohn", "Fane", "Fake", "Fudith"]

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

Rationale

Look at symple example

people.select{|p| p.gender == 'female'}
people.select(&fltr(gender: '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:

aliens.select{|a| p.gender == 'female'}
#                 ^ bang! It was not DRY enough!

# With ProcMe
aliens.select(&fltr(gender: '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

people.select(&P.fltr(gender: '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.

License

MIT