Help with some ruby metaprogramming

36 views
Skip to first unread message

Xavier Lange

unread,
Jan 31, 2011, 7:50:56 PM1/31/11
to sdr...@googlegroups.com
I'm building a HTML extractor on top of nokogiri which applies a
collection of CSS search strings and more to build a logical
extraction of data and I wanted to use something like this:

[1,2,3].send(:collect,Proc.new{|x| x.to_s + "!"})

This fails. Any ideas how I could work around this? How do you use
Object#send (or similar) with a block?

Xavier

Ben Hughes

unread,
Jan 31, 2011, 7:54:21 PM1/31/11
to sdr...@googlegroups.com
You're just missing one character:

[1,2,3].send(:collect, &Proc.new{|x| x.to_s + "!"})

& will pass that proc object in as a block to the method.

--
Ben Hughes
http://benhugh.es/


Adam Grant

unread,
Jan 31, 2011, 8:06:09 PM1/31/11
to sdr...@googlegroups.com
Any reason why you aren't calling "collect" directly from the Array?
_
Adam


On Mon, Jan 31, 2011 at 4:50 PM, Xavier Lange <xrl...@gmail.com> wrote:

Xavier Lange

unread,
Jan 31, 2011, 10:20:12 PM1/31/11
to sdr...@googlegroups.com
Adam,

I'm building a collection of transformations which can be described as
chained method invocations. How do you extract related entities from
an nokogiri document of an amazon review page? By applying these
transformations:
https://github.com/derdewey/amzn-scraper/blob/master/amazon_review.rb
. Find a common node to describe the entity, then extract each of the
elements and return a handy hash.

Ben,

So that works when it's being passed directly to send but it won't
work when passed in from a splatted array!

[1,2,3].send(*[:collect, &Proc.new{|x| x.to_s + "!"}])
-> SyntaxError: (irb):1: expecting ']'

My workaround, which is kinda lame, is to just extend the nokogiri class.

Xavier

Kevin Clark

unread,
Jan 31, 2011, 10:34:55 PM1/31/11
to sdr...@googlegroups.com
> Ben,
>
> So that works when it's being passed directly to send but it won't
> work when passed in from a splatted array!
>
>    [1,2,3].send(*[:collect, &Proc.new{|x| x.to_s + "!"}])
>    -> SyntaxError: (irb):1: expecting ']'

That doesn't work because you can't put a block in an array:

>> [:collect, &Proc.new{|x| x.to_s + '!'}]
SyntaxError: compile error

Instead of storing the call and args in an array, you might want to
consider a hash so you can label and handle blocks special case.
There's some ambiguity in just shoving it in at the end of the args
(is it a user argument or a handler?).

--
Kevin Clark
http://glu.ttono.us

Jordan Fowler

unread,
Jan 31, 2011, 10:41:28 PM1/31/11
to sdr...@googlegroups.com
You can also use a lambda:

ruby-1.9.2-p136 :004 > [:collect, lambda { |x| x.to_s + "!" }]
 => [:collect, #<Proc:0x000001009a29b8@(irb):4 (lambda)>]


--



--
Jordan A. Fowler
E-mail: m...@jordanfowler.com
Website: http://www.jordanfowler.com
Phone: (619) 339-6752

Jordan Fowler

unread,
Jan 31, 2011, 10:43:24 PM1/31/11
to sdr...@googlegroups.com
Oh wait, I missed the part about needing an anonymous block. Hmm...

Kevin Clark

unread,
Jan 31, 2011, 10:49:43 PM1/31/11
to sdr...@googlegroups.com
Yeah, doesn't matter if you could curry it or not - it needs to be
passed as a block. If you don't separate them, there's no way to
detect it *should* be separated. *args can't work. That's why method
missing takes them separate - blocks are considered their own part of
the call, and you only get one.

--
Kevin Clark
http://glu.ttono.us

Xavier Lange

unread,
Feb 1, 2011, 12:11:42 AM2/1/11
to sdr...@googlegroups.com
As my knowledge of Object#send stands now: it can't recreate the full
breadth of method invocations in ruby. Object#send should accept an
optional parameter:

[1,2,3].send(:collect, :block => lambda{|x| x.to_s+"!"})

That obviously wouldn't work because it's still ambiguous. Oh well.
I'll just extend the nokogiri class! Thanks for playing everyone!

Xavier

Mike O'Brien

unread,
Feb 1, 2011, 12:19:08 AM2/1/11
to sdr...@googlegroups.com
Hey man, what are you up to these days???

Are you still with powerset? How's it working for the man? Are you still in san jose?

Sent from my iPhone

Kevin Clark

unread,
Feb 1, 2011, 12:33:53 AM2/1/11
to sdr...@googlegroups.com
On Mon, Jan 31, 2011 at 9:11 PM, Xavier Lange <xrl...@gmail.com> wrote:
> As my knowledge of Object#send stands now: it can't recreate the full
> breadth of method invocations in ruby. Object#send should accept an
> optional parameter:
>
> [1,2,3].send(:collect, :block => lambda{|x| x.to_s+"!"})
>
> That obviously wouldn't work because it's still ambiguous. Oh well.
> I'll just extend the nokogiri class! Thanks for playing everyone!

Not quite. Send can pass the block just fine. But the way you're
storing your information doesn't allow you (as the person calling
send) to split it out.

=> {:method=>:collect, :block=>#<Proc:0x00000001012ee558@(irb):2>}
>> [1,2,3].send(data[:method], &data[:block])
=> ["1!", "2!", "3!"]

I was saying the way you're storing what essentially amount to bound
method calls is ambiguous:

REVIEW_EXTRACTION =
{
:most_common_node => [[:css, "a + br + div > div + div >
span > span > span"], [:collect, &Proc.new{|x|
x.parent_node.parent_node.parent_node.parent_node.parent_node}]],


[:collect, &...] could only express method(arg1, arg2, arg3, &myblock)
if you strip off the first and last item, and set args equal to the
rest. You couldn't just splat everything after collect and expect it
to work (since blocks aren't really a positional argument).

Does that makes sense? You don't need to extend the class, you just
need to tweak your data.

Matt Aimonetti

unread,
Feb 1, 2011, 12:41:25 AM2/1/11
to sdr...@googlegroups.com
Xavier,
 
[1,2,3].send(:collect, :block => lambda{|x| x.to_s+"!"})

That obviously wouldn't work because it's still ambiguous. Oh well.
I'll just extend the nokogiri class! Thanks for playing everyone!


I didn't read the entire thread but that would work:
[1,2,3].map &lambda{|x| x.to_s+"!"}

=> ["1!", "2!", "3!"]

>> [1,2,3].send(:map, &lambda{|x| x.to_s+"!"})

=> ["1!", "2!", "3!"]

Probably not what you're after tho.


Mike...., you might want to contact Kevin directly and not via the ML ;) (and super scoop, no he doesn't work for Powerset/Microsoft anymore ;))


- Matt

Dan Simpson

unread,
Feb 1, 2011, 11:18:09 AM2/1/11
to sdr...@googlegroups.com
I'll give one more option:

ruby-1.9.2-p0 > [1,2,3].send(:collect) { |x| x * 2 }
 => [2, 4, 6]

Not sure if that suits your use case.

--Dan
Reply all
Reply to author
Forward
0 new messages