standard, pretty, and flexible meta-methods?

G

Greg Weeks

The following code makes the meta-method "def_foo" available to any
class that inherits from Definer:

class Definer
def Definer.def_foo
def foo
17
end
end
end
# A TEST:
class User < Definer
def_foo
end
puts User.new.foo # prints 17

Is there a way to make meta-methods like "def_foo" available in class
definitions without using inheritance? Is there a standard way? Is it
pretty? (Or is it perhaps pointless?)
 
P

Phrogz

The following code makes the meta-method "def_foo" available to any
class that inherits from Definer:

class Definer
def Definer.def_foo
def foo
17
end
end
end
# A TEST:
class User < Definer
def_foo
end
puts User.new.foo # prints 17

Is there a way to make meta-methods like "def_foo" available in class
definitions without using inheritance? Is there a standard way? Is it
pretty? (Or is it perhaps pointless?)


module Def1
def make_method( name )
define_method( name ){
"Hello from #{name}"
}
end
end

class Foo
extend Def1
end
Foo.make_method( 'jimmy' )
p Foo.new.jimmy
#=> "Hello from jimmy"


Note the use of 'extend' instead of 'include'.

Some people like this idiom:
http://redcorundum.blogspot.com/2006/06/mixing-in-class-methods.html
 
P

Phrogz

Is there a way to make meta-methods like "def_foo" available in class
definitions without using inheritance? Is there a standard way? Is it
pretty? (Or is it perhaps pointless?)

In addition to using modules to mix in methods to only specific
classes, I should point out that you can define methods in the Module
class to have them available to every class and module you create. (Or
you can define them in the Class class to have them available only to
classes but not modules.)

class Module
def make_method( name )
define_method( name ){
"Hello from #{name}"
}
end
end

class Foo; end

Foo.make_method( 'jimmy' )
p Foo.new.jimmy
#=> "Hello from jimmy"

module Whee; end
class Foo
include Whee
end
Whee.make_method( 'junk' )

p Foo.new.junk
#=> "Hello from junk"

class Class
def classes_only!
"Classes are awesome. Modules suck!"
end
end

p Foo.classes_only!
#=> "Classes are awesome. Modules suck!"

p Whee.classes_only!
#=> undefined method `classes_only!' for Whee:Module (NoMethodError)
 
G

Gary Wright

Is there a way to make meta-methods like "def_foo" available in class
definitions without using inheritance? Is there a standard way?
Is it
pretty? (Or is it perhaps pointless?)

1) Put your method definition in a module:

module M
def my_class_method
# do something
end
end

2) extend your class object with your module

class A
extend M
end

3) use your methods from M

class A
my_class_method
end

Of course you can combine 2 and 3:

class A
extend M
my_class_method
end
 
G

Greg Weeks

Wow, what a rush of information, eg:

Gavin said:
define_method( name ){

"define_method" is what I was missing.

I'm still curious about the most common practice, if there is one,
specifically about creating little "mini-languages" that can be used
inside class definitions. (Yes, I'm wondering about Rails.) My example
with explicit class methods now smells pretty bad to me, for example.
Could modules (using "define_method" and other magic) "extended" into
class objects be considered the norm? This is a more vague question,
but I'm putting it out FWIW.

Anyway, thanks to all for the info so far.
 
P

Phrogz

I'm still curious about the most common practice, if there is one,
specifically about creating little "mini-languages" that can be used
inside class definitions. (Yes, I'm wondering about Rails.) My example
with explicit class methods now smells pretty bad to me, for example.
Could modules (using "define_method" and other magic) "extended" into
class objects be considered the norm? This is a more vague question,
but I'm putting it out FWIW.

It's what Rails and its component libraries mostly do. For example:

module ActiveRecord
module Associations
def self.included(base)
base.extend(ClassMethods)
end
...
module ClassMethods
def has_many(association_id, options = {}, &extension)
...
end
end
end
end

ActiveRecord::Base.class_eval do
...
include ActiveRecord::Associations
...
end


And that's how you get the has_many method:
class Person < ActiveRecord::Base
has_many :cats
end
 
P

Phrogz

It's what Rails and its component libraries mostly do. For example:

Actually, I would like to change my answer:

The various Rails libraries generally define class methods as
'instance' methods in modules and extend classes with those modules to
add the class methods to various classes.

However, as you've no doubt seen in rails, most of the end user
functionality of the framework is achieved by subclassing (as with my
example based on ActiveRecord). The Rails programmer does not write:

class Person
include ActiveRecord::Validations
include ActiveRecord::Locking::Optimistic
include ActiveRecord::Locking::pessimistic
include ActiveRecord::Callbacks
include ActiveRecord::Observing
include ActiveRecord::Timestamp
include ActiveRecord::Associations
include ActiveRecord::Aggregations
include ActiveRecord::Transactions
include ActiveRecord::Reflection
include ActiveRecord::Acts::Tree
include ActiveRecord::Acts::List
include ActiveRecord::Acts::NestedSet
include ActiveRecord::Calculations
include ActiveRecord::XmlSerialization
include ActiveRecord::AttributeMethods

# phew! time to actually code stuff up
end

So perhaps you don't care *how* the class methods were placed in the
parent class.

I would say:

* Define the class methods in the class itself if you only need them
for that class.

* Define the class methods in a common parent class if a strict
hierarchy suits your needs.

* Define the class methods in a module if you want to be able to pick
and choose which sets of class methods to bring into a particular
class.
 
G

Greg Weeks

Gavin said:
The various Rails libraries generally define class methods as
'instance' methods in modules and extend classes with those modules to
add the class methods to various classes.

However, as you've no doubt seen in rails, most of the end user
functionality of the framework is achieved by subclassing (as with my
example based on ActiveRecord). The Rails programmer does not write:

class Person
include ActiveRecord::Validations

(I assume that "include" above is a typo for "extend".)

Thanks again. I don't know why I was obsessed with this issue, but it
is a relief to resolve it. (The "However ..." part was obvious, but you
couldn't know that without reading my mind.)

BTW, I just got a hold of "The Ruby Way" a couple minutes ago. The very
first words inside it are: "Among other things, this book excels at
explaining metaprogramming, one of the most interesting aspects of Ruby.
Many of the early ideas for Rails were inspired by the first edition,
especially what is now chapter 11..." -- David Heinemeier Hansson,
Creator of Ruby on Rails.

So, combine that with your responses, and I'm about as assuaged
as a curious person can be.
 
P

Phrogz

(I assume that "include" above is a typo for "extend".)

Nope, only because (as seen in my earlier post)
ActiveRecord::Validations has a hook method that detects when it has
been included in something else, and automagically calls 'extend's
that something else with the methods in the
ActiveRecord::Validations::ClassMethods module.
 
G

Greg Weeks

Regarding:

module Associations
def self.included(base)
base.extend(ClassMethods)
end
...
module ClassMethods
def has_many(association_id, options = {}, &extension)
...
end
end
end

Sorry about my initial haste. I see now. Lessons learned (I hope):

- I must expect to see classy operations in instance method code.
- "define_method"
- the above "included" trick

Plus, of course, the concepts of A) extending a class object and B)
opening up Module.

Thanks again.
 

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

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top