ruby的to_proc

1,先看api
[quote]Method#proc
meth.to_proc => prc
Returns a Proc object corresponding to this method.

prc.to_proc → prc
Part of the protocol for converting objects to Proc objects. Instances of class Proc simply return themselves.[/quote]
2,to_proc经常和&一起使用.
可以把block传递给带block的方法.block的to_proc返回的就是本身.
如果对象自带to_proc方法的话,就可以把它当作带"&"的参数传给带block方法(默认状态下,Proc、Method对象都有to_proc方法)。方法调用时会执行to_proc,它将返回Proc对象.
pobj = lambda {|v| p v }
[1,2,3].each(&pobj)
=> 1
2
3

class Foo
def to_proc
lambda {|v| p v}
end
end
[1,2,3].each(&Foo.new)

=> 1
2
3

class Symbol
def to_proc
proc { |obj, *args| obj.send(self, *args) }
end
end
projects.collect(&:name)
[1, 2, 3].map(&:to_s.to_proc) #=> ["1", "2", "3"]


class Person < ActiveRecord::Base; end;
[{:name => 'Dan'}, {:name => 'Josh'}].map(&Person.method(:new))
class Class
def to_proc
proc(&method(:new))
end
end
[{:name => 'Dan'}, {:name => 'Josh'}].map(&Person)

3,to_proc和method_missing

module Kernel
protected
def it() It.new end
alias its it
end

class It

undef_method(*(instance_methods - %w*__id__ __send__*))

def initialize
@methods = []
end

def method_missing(*args, &block)
@methods << [args, block] unless args == [:respond_to?, :to_proc]
self
end

def to_proc
lambda do |obj|
@methods.inject(obj) do |current,(args,block)|
current.send(*args, &block)
end
end
end
end

File.read("/etc/passwd").split.sort_by &it.split(":")[2]
User.find(:all).map &its.contacts.map(&its.last_name.capitalize)

4,Going crazy
可以让&使用多个参数,更性感。
What I always regretted though was not being to pass any arguments, so I hacked and monkeypatched a bit, and got:


class Symbol

def with(*args, &block)
@proc_arguments = { :args => args, :block => block }
self
end

def to_proc
@proc_arguments ||= {}
args = @proc_arguments[:args] || []
block = @proc_arguments[:block]
@proc_arguments = nil
Proc.new { |obj, *other| obj.send(self, *(other + args), &block) }
end

end

#So you can now write:
some_dates.map(&:strftime.with("%d-%M-%Y"))

Not that this is any shorter than just creating the darn block in the first place. But hey, it’s a good exercise in metaprogramming and show of more of Ruby’s awesome flexibility.

After this I remembered something similar that annoyed me before. It’s that Rails helper methods are just a bag of methods available to, because they are mixed in your template. So if you have an array of numbers that you want to format as currency, you’d have to do:
<%= @prices.map { |price| number_to_currency(price) }.to_sentence %>

What if I could apply some to_proc-love to that too? All these helper methods cannot be added to strings, fixnums, and the likes; that would clutter way to much. Rather, it might by a nice idea to use procs that understands helper methods. Here is what I created:

module ProcProxyHelper

def it(position = 1)
ProcProxy.new(self, position)
end

class ProcProxy

instance_methods.each { |m| undef_method(m) unless m.to_s =~ /^__|respond_to\?|method_missing/ }

def initialize(object, position = 1)
@object, @position = object, position
end

def to_proc
raise "Please specify a method to be called on the object" unless @delegation
Proc.new { |*values| @object.__send__(*@delegation[:args].dup.insert(@position, *values), &@delegation[:block]) }
end

def method_missing(*args, &block)
@delegation = { :args => args, :block => block }
self
end

end

end

I used a clean blank class (in Ruby 1.9, you’d want to inherit it from BasicObject), in which I will provide the proper proc-object. I play around with the argument list a bit, handling multiple arguments and blocks too. You can now use this syntax:

<%= @prices.map(&it.number_to_currency).to_sentence %>

That is a lot sexier if you as me. And you can use it in any object, not just inside views. And lets add some extra arguments and some Enumerator-love too:

class SomeClass
include ProcProxyHelper

def initialize(name, list)
@name, @list = name, list
end

def apply(value, index, seperator)
"#{@name}, #{index} #{separator} #{value}"
end

def applied_list
@list.map.with_index(&it.apply(":"))
end

end

In case you are wondering, the position you can specify is to tell where the arguments need to go. Position 0 is the method name, so you shouldn’t use that, but any other value is okay. An example might be that you cant to wrap an array of texts into span-tags:
<%= some_texts.map(&it(2).content_tag(:span, :class => "foo")).to_sentence %>

So there you have it. I’m probably solving a problem that doesn’t exist. It is however a nice example of the awesome power of Ruby. I hope you’ve enjoyed this little demonstration of the possible uses of to_proc.

最后的一部分来自于http://iain.nl/2010/02/going-crazy-with-to_proc/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值