Skip to content

Instantly share code, notes, and snippets.

@bryanwoods
Last active December 7, 2015 21:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bryanwoods/eb307b9b44242114169d to your computer and use it in GitHub Desktop.
Save bryanwoods/eb307b9b44242114169d to your computer and use it in GitHub Desktop.
Clojure-inspired syntax sugar for Ruby lambdas
module Enumerable
def self.classes
ObjectSpace.each_object(Module).select { |m| m.include?(Enumerable) }
end
end
module LambdaSugar
require 'active_support'
require 'active_support/core_ext/module'
def self.included(base)
base.class_eval do
methods = Enumerable.instance_methods.reject { |m| m == :include? }
methods.map(&:to_s).each do |meth|
special_characters = %w(! ?)
punctuation = special_characters.include?(meth[-1]) ? meth[-1] : ""
method_name = meth.delete(special_characters.join(","))
sugared = "#{method_name}_with_sugar#{punctuation}"
desugared = "#{method_name}_without_sugar#{punctuation}"
define_method(sugared) do |p = nil, &block|
return send(desugared, &block) unless lambda_sugar?(p)
send(desugared) { |x| eval(p.gsub("%", "x")) }
end
alias_method_chain meth, :sugar
end
end
private
def predicate_or_bang?(meth)
return false unless meth.end_with?("?") || meth.end_with?("!")
meth[-1]
end
def lambda_sugar?(arg)
"#{arg}".include?("%")
end
end
end
Enumerable.classes.each { |klass| klass.send(:include, LambdaSugar) }
p [1,2,3].map("% * 2")
p [1,2,3].map("-%")
p [2,4,6].map { |x| x * 2 }
p 1.upto(10).drop_while("% < 5")
p 1.upto(10).partition("% > 5")
p 1.upto(10).partition("% > 5")
p 1.upto(10).reject("% == 1")
__END__
~$ ruby enumerable.rb
[2, 4, 6]
[-1, -2, -3]
[4, 8, 12]
[5, 6, 7, 8, 9, 10]
[[6, 7, 8, 9, 10], [1, 2, 3, 4, 5]]
[[6, 7, 8, 9, 10], [1, 2, 3, 4, 5]]
[2, 3, 4, 5, 6, 7, 8, 9, 10]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment