Thanks.
--
Luke | PGP: 0xFBE7D8AF
gose...@comcast.net | 2A44 9EB2 F541 C1F2 D969 56E3 8617 5B7F FBE7 D8AF
Here is James' example:
A simple example
# Meta-programming
# Add a public array property to an object instance
class Meta
def self.has_many( sym )
attr_tmp = "def #{sym.to_s} \n"
attr_tmp << " @#{sym.to_s} = [] unless @#{sym.to_s}\n"
attr_tmp << " @#{sym.to_s}; end"
eval ( attr_tmp )
end
end
class Foo < Meta
has_many :foo
has_many :baz
end
f = Foo.new
# See what methds the new object has ...
puts (f.methods - Object.methods ).inspect # ["foo", "baz"]
f.foo.push "X"
f.baz << "hello"
p f.foo.first # "X"
p f.baz # ["hello"]
James Britt
--
All the best
Glenn
Aylesbury, UK
Here is James' example:
A simple example
f = Foo.new
f.foo.push "X"
f.baz << "hello"
James Britt
You're absolutely correct that they're just class methods. In Ruby
(unlike many other languages) the code that defines a class is much
more than just a "definition". The code between "class Foo ... end" is
executed just like any other part of your program. Perhaps this simple
example using a global variable will help clear it up:
irb(main):001:0> $foo = 'bar'
=> "bar"
irb(main):002:0> $foo
=> "bar"
irb(main):003:0> class FooChanger
irb(main):004:1> $foo = 'baz'
irb(main):005:1> end
=> "baz"
irb(main):006:0> $foo
=> "baz"
--
Regards,
John Wilger
-----------
Alice came to a fork in the road. "Which road do I take?" she asked.
"Where do you want to go?" responded the Cheshire cat.
"I don't know," Alice answered.
"Then," said the cat, "it doesn't matter."
- Lewis Carrol, Alice in Wonderland
> more than just a "definition". The code between "class Foo ... end" is
> executed just like any other part of your program. Perhaps this simple
That's what i was thinking. So anything in between class Foo ... end
is like a static { } block in a java class. Thanks to both of you for
clearing that up.
I am not a Ruby expert (yet :-), but the first thing to realize is
that attr, etc are just normal methods. And remembering I am far from
a Ruby expert, I wrote the following code for personal exploration on
the same topic. It adds attr_class_(reader|writer|accessor) methods to
Module so that any other class or module can then provide easy access
to class data variables. Note all it does use class eval to "write"
the access methods -- I do not know if this is what module does or if
there is a substantially better technique. I would prefer to have used
a closure over class_eval, but could not figure out how (if it is
possible).
Patrick
class Module
def attr_class_reader(sym)
self.class_eval <<ATTR
def self.#{sym}
@@#{sym}
end
ATTR
end
def attr_class_writer(sym)
self.class_eval <<ATTR
def self.#{sym}=(new_value)
@@#{sym} = new_value
end
ATTR
end
def attr_class_accessor(sym)
attr_class_reader(sym)
attr_class_writer(sym)
end
end
> Here is James' example:
>
> A simple example
>
> # Meta-programming
> # Add a public array property to an object instance
>
> class Meta
> def self.has_many( sym )
> attr_tmp = "def #{sym.to_s} \n"
> attr_tmp << " @#{sym.to_s} = [] unless @#{sym.to_s}\n"
> attr_tmp << " @#{sym.to_s}; end"
> eval ( attr_tmp )
> end
> end
And here it is without an eval:
class Meta
def self.has_many(sym)
ivar = :"@#{sym}"
define_method(sym.to_sym) do
new_value = instance_variable_get(ivar) || []
instance_variable_set(ivar, new_value)
new_value
end
end
end
What is the difference between these two approaches?
* Is one more 'culturally correct'?
* Does one perform better?
* Is one more future-proof?
Thanks,
--binkley
Florian's will only work on Ruby 1.8 or later and avoids some of the
concerns that people have around "eval".
-austin
--
Austin Ziegler * halos...@gmail.com
* Alternate: aus...@halostatue.ca
Being new to Ruby, what sorts of concerns?
Thanks,
--binkley
So does there exist (as I cannot find it) a way to access @@vars via
symbols in a similar manner? @vars tied to the class are easy enough
(and can often fill the same niche), but I cannot find a method
similar to instance_variable_XXX for @@ vars.
Thanks
Patrick
I would take a stab, that minimally "public data" (e.g. web
processing) situation there is a significant code injection risk that
is minimized in the block based code.
Patrick
The second approach would generally be considered 'better'. eval must be
used with care (security issues, debugging is harder, etc.). The second
approach doesn't use eval so that's why it would generally be preferred.
Performance differences between the two are probably negligable. I
suspect that either approach would work in the future.
Sometimes you need to use eval (or it's cousins instance_eval and
class_eval - the latter should probably be preferred where possible) and
it's great to have it's power available so I'm not saying you should
_never_ use eval.
Phil
Two main issues come to mind:
1) security. What is someone malicious code that gets eval'ed?
"system(\"rm -f *\")"
2) debugging. It can be difficult to debug code that depends on eval.
Errors get reported, but you've got to match line numbers of the string
passed in to the eval function and this can be difficult.
Phil
James prefers Florian's way for real-life usage.
James wrote his as a sort of "Here's a simple example that may help show
what sort stuff is going on" thing.
In general, 'eval' is risky because, if you're not careful, you'll
execute the wrong raw code. It can also be harder to debug because
you're looking at code + code-in-strings, so the actual intent may be
obscured.
The eval example is perhaps more useful for someone looking to hack
around to see what else can be done using assorted meta-coding and such,
but overall you are probably better off if, once you've decide what to
accomplish, you find a non-eval way to do it.
James
> So does there exist (as I cannot find it) a way to access @@vars via
> symbols in a similar manner? @vars tied to the class are easy enough
> (and can often fill the same niche), but I cannot find a method
> similar to instance_variable_XXX for @@ vars.
No, probably because they are not supposed to be used too frequently.
But using just instance_eval("@@" + name) is still better than evaling
the whole method definition.
3) editing. If your editor understands Ruby syntax, it will highlight
and indent the code in a block passed to define_method. But editing
code encased in quoted strings makes your editor dumb.
--
Glenn Parker | glenn.parker-AT-comcast.net | <http://www.tetrafoil.com/>
They are simply methods.
Parentheses for method calls are optional for Ruby.
So
attr :foo
is the same as
attr(:foo)
:foo is a Symbol - similar to a String. You can also write
attr('foo')
...since the 'attr' method takes either a Symbol or a String.
If you don't know why Symbol exists (why we don't just use String all the
time), search the archives of this list, and ask again if you're still not
sure.
(The rest of the reason is explained but other people in this thread: Ruby
evaluates statements/expressions "as it sees them".)