How do I catch a missing method on a passed block?

J

J2M

Whoops, that wasn't actually my code :-/ Just to restore a little
credibility it should have read;

def method_missing(m, *args, &block)

Which works fine ( I do want to reset @@return each time).
@@return is going to be a new variable each time you call bar with a
block, so it won't accumulate results from multiple calls (which I
think is what you want to do).

See if this either does what you want or suggests some possibilities:

class Foo

def bar(*args, &block)
method_calls = {}
if block
(class << block; self; end).class_eval do
define_method:)method_missing) do |m,*args|
method_calls.merge!({ m.to_sym => args })
end
end
attributes = block.instance_eval(&block)
p attributes
end
end
end

But this is nicer because the class variable was a kludge because I
couldn't figure out how to keep a variable from outside the block in
scope.

Thanks,
James
 
E

Erik Veenstra

I've some comments, on almost all code in this thread. Feel
free to ignore me...

All code is "polluted" with meta-code stuff and "class <<
object" stuff and "instance_eval" stuff and "class_eval" stuff
and other stuff. That doesn't feel good. Well, you need this
code to do the trick, but...

It's usually a good idea to separate business logic and low
level code. The novice programmers and mediocre programmers can
concentrate on the business level code, while the more seasoned
programmer can write the low-level code. The novice programmer
is the user of the code made by the smart guy.

The novice programmer only sees clean code: He understands what
he is doing and is happy. Smart Guy is the, well, uh, smart guy
and is happy too. Everybody's happy!

I've mixed business code and low-level code myself, in the
past. After one month, I simply couldn't read my own code,
solely because I was mixing both levels. So, even if you are a
smart guy and the only developer, it's still a good idea to
think and code on different levels.

I've rewritten one of the examples. Lines 1-12 are (is?)
low-level code (generic) and can and should be hidden in a
library. Lines 14-28 are (is?) the business code (non-generic).

On line 16, we wrap the received block, which could easily be
commented out, if necessary. Method_missing_inspect calls
block_with_method_missing with the given block and a
(non-generic) method_missing block. Block_with_method_missing
enriches the given block1 with a method_missing (which is
implemented by block2) and returns the enriched block1.

The result is that Foo#bar, the business logic, is still very
clean and very readable. Method_missing_inspect is a bit more
sophisticated, but still understandable for Joe Average.
Block_with_method_missing is the work of Smart A^HGuy...

Just my thoughts. Ignore me, if appropriate...

gegroet,
Erik V. - http://www.erikveen.dds.nl/

----------------------------------------------------------------

1 module Kernel
2 def block_with_method_missing(block1, &block2)
3 class << block1
4 self
5 end.module_eval do
6 define_method:)method_missing, &block2)
7 define_method:)call){block1.instance_eval(&block1)}
8 end
9
10 block1
11 end
12 end
13
14 class Foo
15 def bar(&block)
16 block = method_missing_inspect(block)
17
18 block.call
19 end
20
21 def method_missing_inspect(block)
22 block_with_method_missing(block) do |method_name, *args|
23 p [:mm, method_name, args]
24 end
25 end
26 end
27
28 Foo.new.bar{baz(1, 2, 3)}

----------------------------------------------------------------
 
J

J2M

Erik said:
I've some comments, on almost all code in this thread. Feel
free to ignore me...

All code is "polluted" with meta-code stuff and "class <<
object" stuff and "instance_eval" stuff and "class_eval" stuff
and other stuff. That doesn't feel good. Well, you need this
code to do the trick, but...

It's usually a good idea to separate business logic and low
level code. The novice programmers and mediocre programmers can
concentrate on the business level code, while the more seasoned
programmer can write the low-level code. The novice programmer
is the user of the code made by the smart guy.

The novice programmer only sees clean code: He understands what
he is doing and is happy. Smart Guy is the, well, uh, smart guy
and is happy too. Everybody's happy!

I've mixed business code and low-level code myself, in the
past. After one month, I simply couldn't read my own code,
solely because I was mixing both levels. So, even if you are a
smart guy and the only developer, it's still a good idea to
think and code on different levels.

--- snip ---

Erik, thank you for pre-empting my next challenge and giving me a good
lesson in structure. Making the transition from mediocre to smart could
be more about structuring the code than knowing the language better ;)

James
 
D

dblack

Hi --

I've some comments, on almost all code in this thread. Feel
free to ignore me...

All code is "polluted" with meta-code stuff and "class <<
object" stuff and "instance_eval" stuff and "class_eval" stuff
and other stuff. That doesn't feel good. Well, you need this
code to do the trick, but...

I guess "polluted" is relative. I admit I've never understood the
fuss that people make over these things, one way or the other -- that
is, I neither see them as a badge of some kind of wizardry, nor think
that they are to be shunned or stigmatized. They're just ways to live
productively in the Ruby landscape, where there are objects and
objects have singleton classes and there's a 'self' and so on. I
guess class << obj requires more ramping up than x = 1, but on the
whole I think Ruby is much flatter than it's often given credit for.

Most of what people need to grasp and embrace, in order to understand
and use the vast bulk of these techniques, and others, is:

* classes are objects
* each object (or almost each object) has both a "birth class" and a
singleton class
* objects don't have methods; they look for them along a
class/module search path, with well-defined rules of order and
inclusion
* there's one and only one 'self' at every point in the program
* instance variables belong to 'self', always
* class variables are completely unrelated to instance variables,
in spite of the use of @ in one and @@ in the other
* the def and class keywords start new local scopes
* the class keyword can take either a constant (for a named class)
or a "<< object" expression (for a singleton class)

I'm not saying that's the whole language, of course; but probably
about 90% of the problems I've seen people have with Ruby over the
years -- with the language generally, with understanding code they
see, and with solving specific problems in their own code -- come from
not understanding one or more of these points. Class variables are
perhaps the worst offenders: they cause no end of confusion. The role
of 'self', particularly as it governs ownership of instance variables,
is also a bit of a hurdle -- but not insurmountable.

I'm all for organizing code nicely, and grouping together things that
belong together, as you suggest. But what's been concerning me
recently is that it feels like people are being encouraged to feel
awestruck and out of their depth when they see "class << object".
Yes, it looks different from "class C" but I just don't think it's
*that* big a deal. The underlying principles are relatively few; the
language is expressive; and it all works together rather nicely.


David

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
J

J2M

Most of what people need to grasp and embrace, in order to understand
and use the vast bulk of these techniques, and others, is:

* classes are objects
* each object (or almost each object) has both a "birth class" and a
singleton class
* objects don't have methods; they look for them along a
class/module search path, with well-defined rules of order and
inclusion
* there's one and only one 'self' at every point in the program
* instance variables belong to 'self', always
* class variables are completely unrelated to instance variables,
in spite of the use of @ in one and @@ in the other
* the def and class keywords start new local scopes
* the class keyword can take either a constant (for a named class)
or a "<< object" expression (for a singleton class)

This is a useful summary of the key concepts that moved me forward. I
have a long way to go, but it clearly encapsulates the crossing of a
rubicon (sic).
I'm not saying that's the whole language, of course; but probably
about 90% of the problems I've seen people have with Ruby over the
years -- with the language generally, with understanding code they
see, and with solving specific problems in their own code -- come from
not understanding one or more of these points. Class variables are
perhaps the worst offenders: they cause no end of confusion. The role
of 'self', particularly as it governs ownership of instance variables,
is also a bit of a hurdle -- but not insurmountable.

Absolutely, the questions I raised on this thread were founded in a
need to know how to apply these concepts, which is why everybody's
feedback has been incredibly valuable. I may have grasped the concepts
but the application takes a little longer.
I'm all for organizing code nicely, and grouping together things that
belong together, as you suggest. But what's been concerning me
recently is that it feels like people are being encouraged to feel
awestruck and out of their depth when they see "class << object".
Yes, it looks different from "class C" but I just don't think it's
*that* big a deal. The underlying principles are relatively few; the
language is expressive; and it all works together rather nicely.

Erik's code offered a clean way of extracting some of the aspects that
can be re-used, whether I call the remaining code business logic or not
in this example is irrelevant, but it does present a neat way of
structuring things but being reminded that I have a lot to learn
doesn't always feel great!

The big lesson I got from Erik was in reflecting on both your comments
and Erik's example code is the clarity of understanding comes from the
ability to grasp and reflect on a number of principles concurrently.
That can be a very hard thing to do - keeping all those principles in
mind at the same time. Breaking the code into those 3 methods helped
encapsulate those concepts in a way that was less effort to think
about. To me it wasn't about inspiring awe but rather removing the
complexity.

James
 
D

dblack

Hi --

Erik's code offered a clean way of extracting some of the aspects that
can be re-used, whether I call the remaining code business logic or not
in this example is irrelevant, but it does present a neat way of
structuring things but being reminded that I have a lot to learn
doesn't always feel great!

The big lesson I got from Erik was in reflecting on both your comments
and Erik's example code is the clarity of understanding comes from the
ability to grasp and reflect on a number of principles concurrently.
That can be a very hard thing to do - keeping all those principles in
mind at the same time. Breaking the code into those 3 methods helped
encapsulate those concepts in a way that was less effort to think
about. To me it wasn't about inspiring awe but rather removing the
complexity.

Definitely. I think breaking the code down is fine (I might avoid
adding to Kernel, but that's another story :) I was chiming in more
on some of the stuff Erik raised by way of context. And of course
there's a long history of the dividing of "regular" from "meta" in
Ruby, and a fairly long history of my skepticism of it. Erik's post
just put me in mind of the whole thing.


David

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
G

Giles Bowkett

Most of what people need to grasp and embrace, in order to understand
and use the vast bulk of these techniques, and others, is:

* classes are objects
* each object (or almost each object) has both a "birth class" and a
singleton class
* objects don't have methods; they look for them along a
class/module search path, with well-defined rules of order and
inclusion
* there's one and only one 'self' at every point in the program
* instance variables belong to 'self', always
* class variables are completely unrelated to instance variables,
in spite of the use of @ in one and @@ in the other
* the def and class keywords start new local scopes
* the class keyword can take either a constant (for a named class)
or a "<< object" expression (for a singleton class)

I have to admit, I don't understand what this means in practical
terms. In fact I have a question I need to find an answer to, and I
want to post it in this thread, rather than as its own thing, in the
possibly idealistic hope that somebody can frame an answer in terms of
these principles, and help me understand them.

Basically, I need to change a class method. It's the Ferret method
full_text_search, in a Rails app.

e.g.:

Thing.full_text_search("asdf")

I need to modify this class method. I tried alias, but did it wrong.

alias original_full_text_search full_text_search

but of course it doesn't work, because it's not an instance method but
a class method. Is this a good place for class_eval? I could be way
off.

Actually now that I re-read it, I think I do understand the basic
concepts, I think I'm just weak on the syntax.
 
D

dblack

Hi --

I have to admit, I don't understand what this means in practical
terms. In fact I have a question I need to find an answer to, and I
want to post it in this thread, rather than as its own thing, in the
possibly idealistic hope that somebody can frame an answer in terms of
these principles, and help me understand them.

If they're not of practical value, then they're not of value. So I'll
give it a try :)
Basically, I need to change a class method. It's the Ferret method
full_text_search, in a Rails app.

e.g.:

Thing.full_text_search("asdf")

I need to modify this class method. I tried alias, but did it wrong.

alias original_full_text_search full_text_search

but of course it doesn't work, because it's not an instance method but
a class method. Is this a good place for class_eval? I could be way
off.

Actually now that I re-read it, I think I do understand the basic
concepts, I think I'm just weak on the syntax.

In general, if you want to do:

alias old_x x

you want to do so inside a class definition body for the class where x
is defined (or a subclass of that class).

A "class method" is basically a singleton method of a Class object.
That means that the class where it's defined is the singleton method
of that class object (because that's what singleton classes are: the
places where objects' singleton methods are defined).

In your case, the object in question is Thing. To get into a class
definition body for a singleton class, you use the class keyword plus
the "<< object" construct:

class << Thing

You can now perform the aliasing:

alias original_full_text_search full_text_search
# redefinitions etc. will follow here

end

Here's a complete mini-example:

class Thing
def self.x
puts "old x"
end
end

class << Thing
alias old_x x
def x
puts "new x..."
old_x
end
end

Thing.x


David

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
C

Christopher Coleman-Smith

Simple question, i come from a C/++ background and i'm just really
starting to get "real things done with ruby" ( that should be a book
title). Anyway as i start writing methods that return objects and
variables and take objects and variables i've started to wonder how they
actually get passed around. I haven't found a specific pointer or
reference type construction, although since everything i've tried to do
has worked i've not tried very hard.

I'm guessing that everything gets passed around by reference? Is that
true? Also what about situations where you'd really want to use a
pointer, like referring to huge things or ,god forbid, doing grimy
things with arrays. It is very nice having all these things automated,
like the garbage collection etc, but i still find myself wanting to free
things. I've only just gotten over wanting to semi-colon every statement.

Probably by asking this i'm showing how i really haven't groked ruby at
all. I'm happy to admit that.

Thanks
 
W

Wilson Bilkovich

Simple question, i come from a C/++ background and i'm just really
starting to get "real things done with ruby" ( that should be a book
title). Anyway as i start writing methods that return objects and
variables and take objects and variables i've started to wonder how they
actually get passed around. I haven't found a specific pointer or
reference type construction, although since everything i've tried to do
has worked i've not tried very hard.

I'm guessing that everything gets passed around by reference? Is that
true? Also what about situations where you'd really want to use a
pointer, like referring to huge things or ,god forbid, doing grimy
things with arrays. It is very nice having all these things automated,
like the garbage collection etc, but i still find myself wanting to free
things. I've only just gotten over wanting to semi-colon every statement.

Probably by asking this i'm showing how i really haven't groked ruby at
all. I'm happy to admit that.

Everything is being passed by reference, but the references themselves
are 'hidden', to enable GC.
data = File.read('ridiculously large file')
some_object.some_method(data)

The data is not copied; some_method just receives a reference, and a
binding (the String object containing the data is bound to the name
'data').

Calling methods on 'data' implicitly dereferences it (this isn't
really how it's referred to in Ruby, but it seems to be how C people
like to think of it. heh.)

All you need to do to 'free' memory is keep your methods short. Local
variables will go out of scope, and be collected for you.

By the way.. the "real things done with ruby" book is called 'The Ruby
Way - 2nd Edition', by Hal Fulton. =)
 
D

dblack

Hi --

Simple question, i come from a C/++ background and i'm just really starting
to get "real things done with ruby" ( that should be a book title). Anyway as
i start writing methods that return objects and variables and take objects
and variables i've started to wonder how they actually get passed around. I
haven't found a specific pointer or reference type construction, although
since everything i've tried to do has worked i've not tried very hard.

I'm guessing that everything gets passed around by reference? Is that true?
Also what about situations where you'd really want to use a pointer, like
referring to huge things or ,god forbid, doing grimy things with arrays. It
is very nice having all these things automated, like the garbage collection
etc, but i still find myself wanting to free things. I've only just gotten
over wanting to semi-colon every statement.

Basically, everything (or almost everything) is a reference:

a = [1,2,3] # a is assigned a reference to the array
b = a # b gets a copy of that reference
b.push(4) # the array now has four elements
p a # as you'll see if you print it via any reference

When you pass objects around via variables, technically you're passing
by value... but the "value" of the variable is a reference to an
object.

When you pass objects around via literals (like puts "hello"), a
reference to the literal is sent.

All of this means that if you do something to the arguments in a
method, the changes are permanent in the original object:

def add_element(array)
array.push("new element!")
end

a = [1,2,3]
add_element(a)
p a

So you'll see a fair amount of "dup"ing going on in methods that want
to be safe.

There's no reference-to-a-reference. It's not like C pointers, which
can point to other pointers. Every Ruby reference is exactly one step
away from the object it refers to, and all references to an object are
equivalent to all other references to that object.

Some values are immediate; i.e., when you assign a variable to them,
they get stored directly and not via a reference: integers, true,
false, nil, and symbols (and any I've forgotten). Hence the "almost
everything" above.


David

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
G

Giles Bowkett

Most of what people need to grasp and embrace, in order to understand
If they're not of practical value, then they're not of value. So I'll
give it a try :)

Now I actually have the inverse problem. The good news is the code
works perfectly -- thank you, by the way -- but the bad news is I
don't totally know why.
In general, if you want to do:

alias old_x x

you want to do so inside a class definition body for the class where x
is defined (or a subclass of that class).

A "class method" is basically a singleton method of a Class object.
That means that the class where it's defined is the singleton method
of that class object (because that's what singleton classes are: the
places where objects' singleton methods are defined).

Ah, ok. The class object creates a singleton, which is where all the
method definitions live?
In your case, the object in question is Thing. To get into a class
definition body for a singleton class, you use the class keyword plus
the "<< object" construct:

class << Thing

And this is because the class, having been defined, now has a
singleton containing all its methods, and we don't want to create a
new patch, we just want to append new methods to the existing
singleton?
You can now perform the aliasing:

alias original_full_text_search full_text_search
# redefinitions etc. will follow here

end

Here's a complete mini-example:

class Thing
def self.x
puts "old x"
end
end

class << Thing
alias old_x x
def x
puts "new x..."
old_x
end
end

Thing.x

I think I do get it, although I admit I'm still puzzled on the
singleton class / birth class thing.
 
D

dblack

Hi --

Hi --

All of this means that if you do something to the arguments in a
method, the changes are permanent in the original object:

def add_element(array)
array.push("new element!")
end

a = [1,2,3]
add_element(a)
p a

Just to make it clearer: anything destructive or mutating you do to the
object inside the method will propagate to the original object.

And yet clearer: the object inside the method IS the original object
:)
I think this can just be considered to be an implementation detail,
frankly. Fixnums etc. just do not have any destructive methods that
one could employ inside a method.

The immediate-value thing does play a role in some language-design
things, though, particularly the absence of x++ on the grounds that
doing 1++ doesn't make any sense and, given x = 1, x *is* 1.


David

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
D

dblack

Hi --

Now I actually have the inverse problem. The good news is the code
works perfectly -- thank you, by the way -- but the bad news is I
don't totally know why.


Ah, ok. The class object creates a singleton, which is where all the
method definitions live?

All the singleton method definitions of that class object, yes. It's
really just a not-very-special case of the general principle that an
object's singleton methods live in the object's singleton class.

It's perhaps easier to see the workings of it when the object isn't a
class:

str = "I am a string
class << str
def just_for_me
puts "This method is just for me!"
end
end

I've defined a singleton method on the one String object, str. No
other string will have that method. If I'd defined it in String, they
would. But I've defined it in str's singleton class (class << str),
so only str will have it:

str.just_for_me # This method is just for me!
"other".just_for_me # NoMethodError

Now, watch as I take that example -- and the explanation -- and
substitute a Class object for the String object:

class Thing; end
class << Thing
def just_for_me
puts "This method is just for me!
end
end

I've defined a singleton method on the one Class object, Thing. No
other class will have that method. If I'd defined it in Class, they
would. But I've defined it in Thing's singleton class (class <<
Thing), so only Thing will have it:

Thing.just_for_me # This method is just for me!
Array.just_for_me # NoMethodError

Note that in the Thing example, just_for_me is what we would call a
"class method". A class method, then, is just a singleton method
defined for a class object.
And this is because the class, having been defined, now has a
singleton containing all its methods, and we don't want to create a
new patch, we just want to append new methods to the existing
singleton?

Yes, we want to define methods inside the singleton class of Thing.
The singleton class of Thing is not actually created until we ask to
get inside it, but that's really an implementation detail.
I think I do get it, although I admit I'm still puzzled on the
singleton class / birth class thing.

It's nature vs. nurture. Every object comes into existence with
certain capabilities -- that is, when you send it messages, it
searches through specific classes and modules, in a specific order,
looking for corresponding methods. From that perspective, all
instances of a given class are the same as each other.

But you can also add capabilities to an object. An object that is
"born" ignorant of a certain message need not remain that way. You
can do this by adding to the object's class -- if you want *all*
instances of that class to have the new capability. Or, if you just
want one object to have it, you can add a method to that object's
singleton class.

So by its nature, a String instance can do certain things:

str = "hello there"
str.split(/ll/)
str.upcase
str.reverse

etc. By way of nurture, we can add to this string by adding to all
strings:

class String
def new_method
...
end
end

or by adding a method to this one string's singleton class, as in the
examples above.

(You can, by the way, also insert a method into an object's singleton
class with the "def" keyword, like this:

def str.new_method
...
end

or

def Thing.some_class_method
...
end

)

The main thing going on in all this is that Ruby objects can change
over the course of their lives. That's the principle being enforced;
and singleton classes are just the way Matz has chosen to bring that
principle to life.

I get a lot of mileage out of thinking of the method look-up path.
When you send a message to an object, it walks the path -- a
succession of classes and modules, like rooms strung along a corridor
-- and looks for the method. First, it checks in its own singleton
class (if you've created one), and in any modules mixed in to the
singleton class. If no method with the right name is found, it
proceeds to its "birth" class, and then to the modules mixed in there.
And so on -- until it gets to Object, and the module that's mixed in
*there* (Kernel). Then, if it still hasn't found the method, it gives
up.

This post could probably do with some more editing but I don't have it
in me right now :) I hope it's helpful. You might also have a look
at a little essay that covers similar material:
http://www.wobblini.net/singletons.html. (And my book, of course :)


David

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 
G

Giles Bowkett

This post could probably do with some more editing but I don't have it
in me right now :) I hope it's helpful. You might also have a look
at a little essay that covers similar material:
http://www.wobblini.net/singletons.html. (And my book, of course :)

Actually I might as well give you some free advertizing, I did in fact
check your book on this. I only just saw this e-mail today, missed it
somehow, but I was thinking about the question, so I re-read bits of
your book and it did actually clear some of this up for me.
Specifically the singleton/birth class thing.
 
D

dblack

Hi --

Actually I might as well give you some free advertizing, I did in fact
check your book on this. I only just saw this e-mail today, missed it
somehow, but I was thinking about the question, so I re-read bits of
your book and it did actually clear some of this up for me.
Specifically the singleton/birth class thing.

Cool -- on all counts: the advertising of course :) but particularly
the fact that it's coming clear for you.


David

--
Q. What's a good holiday present for the serious Rails developer?
A. RUBY FOR RAILS by David A. Black (http://www.manning.com/black)
aka The Ruby book for Rails developers!
Q. Where can I get Ruby/Rails on-site training, consulting, coaching?
A. Ruby Power and Light, LLC (http://www.rubypal.com)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,989
Messages
2,570,207
Members
46,783
Latest member
RickeyDort

Latest Threads

Top