I'm trying to fill in class variables for a class instance from a file, and the only way I've managed to figure how to do this is something like
a=Thing.new
File.read("filename.ext").each_line do |arguments| #this will => things like #variable=str\n
eval( "a.instance_eval {" + arguments.chop + "}") #hence the awkward eval eval chop
end
The only problem I've found is that in trying to impliment this in a class method (to do this for several instances in a go), I don't know how to make this happen:
class Thing
attr_accessor :variable
def self.method
File.read("filename.ext").each_line do |arguments|
eval("instance.instance_eval{" + arguments.chop + "}") #this line
end
end
end
namely, the reference to the instance calling the method. self will just just be Thing in this case, so is there any way to do this? More pertinent might be a better way to go about this overall. I only just learned ruby last night, so I haven't had an opportunity to see some of the neater tricks that are to be had, and my language maturity is a little fresh yet as a result.
For context, Thing is a character in a game, loading its base values from a savefile.
Well, first off, take a look at Marshal. It's specifically used for dumping data structures to serialized formats and loading them back.
The said, if you want to persist in your direction, then try something like this:
class Thing
attr_accessor :variable
def self.method
File.read("filename.ext").each_line do |arguments|
ivar, val = arguments.strip.split("=", 2)
instance.instance_variable_set(ivar, val)
end
end
end
#instance_variable_set allows you to...well, set instance variables on an object by name. No ugly eval necessary!
By way of demonstration:
class Foo
attr_accessor :bar
end
foo = Foo.new
foo.instance_variable_set("#bar", "whatzits")
puts foo.bar # => whatzits
Related
I'm not sure where I have seen this, or if I just think I have seen it, but I would like to be able to call a method that creates an instance of a class with the same name. So, instead of:
# The class is called 'Vector3', for example:
Vector3.new(x,y,z)
I would like to have an eponymous method that instantiates the class, like so:
Vector3(x,y,z) #<- returns instance of Vector3 class
How would you define that in Ruby?
As #Eli mentioned, you can define a method in the kernel:
def Kernel.Vector3(*args)
Vector3.new(*args)
end
Ruby does this for Array, Complex, Float, Hash, Integer, Rational and String where you probably saw it.
The reason it works is that Object includes Kernel, hence all objects in your program (except ones directly inheriting from BasicObject) will have that method.
However, doing so is unidiomatic, unnecessary (you clutter all objects with an additional method), confusing (capitalized identifiers should be constants) and generally looked down upon.
AFAIK you can't. You can do something similar, Vector3[x, y, z].
class Vector3
def initialize(x, y, z)
# ...
end
def self.[](*args)
self.new(*args)
end
end
Note that the Ruby library uses this device as well. There's Hash.new(...) and Hash[...] , but no Hash(...). This parallels how Proc objects are invoked:
greet = Proc.new { |name| puts "Hello, #{name}" }
greet["Amadan"]
EDIT: I stand corrected:
module Kernel
def Vector3(*args)
Vector3.new(*args)
end
end
But, as Eli Sadoff said, it is impractical, violates encapsulation, and Ruby style (functions and methods should be lowercase).
This answers what I understood the question to be from the title. (Somehow I overlooked the example that contradicts that.) I will leave my answer because I think it includes some interesting elements.
class MyClass
def hi
"hi"
end
my_alias = to_s
singleton_class.class_eval { alias_method(my_alias, :new) }
end
MyClass.methods(false)
#=> [:MyClass]
my_instance = MyClass.MyClass
#=> #<MyClass:0x007fadc2092320>
my_instance.hi
#=> "hi"
Note that this works when the alias of new is passed arguments and/or a block.
See Object#singleton_class and Module#class_eval.
I'm trying to write a method that prints class variable names and their values. As an example:
class A
def printvars
???
end
end
class <<A
def varlist(*args)
???
end
end
class B < A
varlist :c
def initialize(c)
#c = c
end
b = B.new(10)
b.printvars()
And I would like the output to be c => 10. But I don't know what goes in the ???. I've tried using a self.class_eval in the body of varlist, but that won't let me store args. I've also tried keeping a hash in the class A and just printing it out in printvars, but the singleton class is a superclass of A and so has no access to this hash. So far everything I've tried doesn't work.
I think something similar must be possible, since Rails does something related with its validates_* methods. Ideally I could make this work exactly as expected, but even a pointer to how to print just the variable names (so just c as output) would be most appreciated.
You might like this answer: What is attr_accessor in Ruby?
Basically, as you surmised, varlist needs to be a class method which takes a variable list of arguments (*args). Once you have those arguments you could try any number of things using send, respond_to?, or maybe even instance_variable_get. Note, none of those are really recommended, but I wanted to answer your question a bit.
The other half is that you should probably look into method_missing in order to understand how things like validates_* are working. The * part necessitates that you do something like method_missing because you can't actually do module_eval until you know what you're looking for. In the case of the magic rails methods, you don't necessarily ever know what you're looking for! So we rely on the built in method_missing to let us know what got called.
For funzies, try this in IRB:
class A
def method_missing(method, *args, &block)
puts method, args.inspect
end
end
A.new.banana(13, 'snakes')
A.new.validates_serenity_of('Scooters', :within => [:calm, :uncalm])
Does that help?
Just use Module#class_variables
As far as I can tell, you're vastly over-complicating this. All you need is the pre-defined Module#class_variables method. You can call this directly on the class, or invoke it through self if you want to bind it to an instance of the class. For example:
class Foo
##bar = "baz"
def show_class_variables
self.class.class_variables
end
end
Foo.class_variables
#=> [:##bar]
foo = Foo.new
foo.show_class_variables
#=> [:##bar]
I personally don't have anything against this, apart from the fact that's is long, but what really bothers me is the word eval.
I do a lot of stuff in JavaScript and I run from anything resembling eval like it's the devil, I also don't fancy the fact that the parameter is a string (again, probably because it's eval).
I know I could write my own method to fix the method-name-length problem, my 'method name issue' and the parameter-being-a-string thingy, but what I really want to know is: Is there a better, shorter, fancier, yet native, way of doing class_eval to extract class variables?
Side note: I know about the existence of class_variable_get() and class_variables(), but they don't really look appealing to me; horribly long, aren't they?
EDIT: Updated the question to be more specific.
Thanks!
Use class_variable_get, but only if you must
class_variable_get is the better way, other than the fact that it is not "appealing" to you. If you are reaching inside a class and breaking encapsulation, perhaps it is appropriate to have this extra barrier to indicate that you're doing something wrong.
Create accessor methods for the variables you want to access
If these are your classes, and accessing the variables doesn't break encapsulation, then you should create class accessor methods for them to make it easier and prettier:
class Foo
def self.bar
##bar
end
end
p Foo.bar
If this is your class, however, are you sure that you need class variables? If you don't understand the implications (see below), you may actually be wanting instance variables of the class itself:
class Foo
class << self
attr_accessor :bar
end
end
Foo.bar = 42
p Foo.bar
The behavior of class variables
Class variables appear to newcomers like the right way to store information at a class level, mostly because of the name. They are also convenient because you can use the same syntax to read and write them whether you are in a method of the class or an instance method. However, class variables are shared between a class and all its subclasses.
For example, consider the following code:
class Rectangle
def self.instances
##instances ||= []
end
def initialize
(##instances ||= []) << self
end
end
class Square < Rectangle
def initialize
super
end
end
2.times{ Rectangle.new }
p Rectangle.instances
#=> [#<Rectangle:0x25c7808>, #<Rectangle:0x25c77d8>]
Square.new
p Square.instances
#=> [#<Rectangle:0x25c7808>, #<Rectangle:0x25c77d8>, #<Square:0x25c76d0>]
Ack! Rectangles are not squares! Here's a better way to do the same thing:
class Rectangle
def self.instances
#instances ||= []
end
def initialize
self.class.instances << self
end
end
class Square < Rectangle
def initialize
super
end
end
2.times{ Rectangle.new }
p Rectangle.instances
#=> [#<Rectangle:0x25c7808>, #<Rectangle:0x25c77d8>]
2.times{ Square.new }
p Square.instances
#=> [#<Square:0x25c76d0>, #<Square:0x25c76b8>]
By creating an instance variable and accesor methods on the class itself—which happens to be an instance of the Class class, similar to MyClass = Class.new—all instances of the class (and outsiders) have a common, clean location to read/write information that is not shared between other classes.
Note that explicitly tracking every instance created will prevent garbage collection on 'unused' instances. Use code like the above carefully.
Using class_eval in a cleaner manner
Finally, if you're going to use class_eval, note that it also has a block form that doesn't have to parse and lex the string to evaluate it:
Foo.class_eval('##bar') # ugh
Foo.class_eval{ ##bar } # yum
I'm trying to learn ruby by building a basic Campfire bot to screw around with at work. I've gotten pretty far (it works!) and learned a lot (it works!), but now I'm trying to make it a bit more complex by separating the actions to be performed out into their own classes, so that they can be easier to write / fix when broken. If you're interested in seeing all the (probably crappy) code, it's all up on GitHub. But for the sake of this question, I'll narrow the scope a bit.
Ideally, I would like to be able to create plugins easily, name them the same as the class name, and drop them into an "actions" directory in the root of the project, where they will be instantiated at runtime. I want the plugins themselves to be as simple as possible to write, so I want them all to inherit some basic methods and properties from an action class.
Here is action.rb as it currently exists:
module CampfireBot
class Action
#handlers = {}
def initialize(room)
#room = room
end
class << self
attr_reader :handlers
attr_reader :room
def hear(pattern, &action)
Action.handlers[pattern] = action
end
end
end
end
Where #room is the room object, and #handlers is a hash of patterns and blocks. I kind of don't understand why I have to do that class << self call, but that's the only way I could get the child plugin classes to see that hear method.
I then attempt to create a simple plugin like so (named Foo.rb):
class Foo < CampfireBot::Action
hear /foo/i do
#room.speak "bar"
end
end
I then have my plugins instantiated inside bot.rb like so:
def load_handlers(room)
actions = Dir.entries("#{BOT_ROOT}/actions").delete_if {|action| /^\./.match(action)}
action_classes = []
# load the source
actions.each do |action|
load "#{BOT_ROOT}/actions/#{action}"
action_classes.push(action.chomp(".rb"))
end
# and instantiate
action_classes.each do |action_class|
Kernel.const_get(action_class).new(room)
end
#handlers = Action.handlers
end
The blocks are then called inside room.rb when the pattern is matched by the following:
handlers.each do |pattern, action|
if pattern.match(msg)
action.call($~)
end
end
If I do puts #room inside the initialization of Action, I see the room object printed out in the console. And if I do puts "foo" inside Foo.rb's hear method, I see foo printed out on the console (so, the pattern match is working). But, I can't read that #room object from the parent class (it comes out as a nil object). So obviously I'm missing something about how this is supposed to be working.
Furthermore, if I do something to make the plugin a bit cleaner (for larger functions) and rewrite it like so:
class Foo < CampfireBot::Action
hear /foo/i do
say_bar
end
def say_bar
#room.speak "bar"
end
end
I get NoMethodError: undefined method 'say_bar' for Foo:Class.
The definition of hear can be pulled out of the class << self block and changed to:
def self.hear(pattern, &action)
Action.handlers[pattern] = action
end
to yield the exact same result. That also immediately explains the problem. hear Is a class method. say_bar is an instance method. You can't call an instance method from a class method, because there simply isn't an instance of the class available.
To understand the class << self bit, you'll have to do your own reading and experiments: I won't try to improve on what has already been said. I'll only say that within the class << self .. end block, self refers to the eigenclass or metaclass of the CampfireBot::Action class. This is the instance of the Class class that holds the definition of the CampfireBot::Action class.
Learning ruby. I'm under the impression that boolean attributes should be named as follows:
my_boolean_attribute?
However, I get syntax errors when attempting to do the following:
class MyClass
attr_accessor :my_boolean_attribute?
def initialize
:my_boolean_attribute? = false
end
end
Apparently ruby is hating the "?". Is this the convention? What am I doing wrong?
Edit: three-years later; the times, they are a-changin'…
Julik's answer is the simplest and best way to tackle the problem these days:
class Foo
attr_accessor :dead
alias_method :dead?, :dead # will pick up the reader method
end
My answer to the original question follows, for posterity…
The short version:
You can't use a question mark in the name of an instance variable.
The longer version:
Take, for example, attr_accessor :foo — it's simply conceptually a bit of syntactic sugar for the following:
def foo
#foo
end
def foo=(newfoo)
#foo = newfoo
end
Furthermore, the question-mark suffix is mostly just a convention to indicate that the return value of a method is a boolean.
The best approximation I can make of what you're going for here…
class MyClass
def initialize
#awesome = true
end
def awesome?
#awesome
end
end
In this case, there may be a case to be made for using attr_accessor — after all, it may be explicit that you're working directly with a boolean attribute. Generally, I save the question-mark suffix for when I am implementing a method whose boolean return value is based on slightly more complex conditions than just the value of an attribute.
Cheers!
Edit, two years later, after a recent comment:
Ruby enforces certain naming conventions. Symbols in Ruby can't have question marks. Thus invocations of :my_boolean_attribute? both will fail with a NameError. Edit: not correct, just use the quoted syntax for a symbol, e.g., :"my_attribute?"
Symbols are immutable, attempting to assign to one will throw a SyntaxError.
The easiest way to quickly add a "question method" is to use aliasing for your reader method
class Foo
attr_accessor :dead
alias_method :dead?, :dead # will pick up the reader method
end
The attr_accessor symbol implies that the variable name is #my_boolean_attribute, so that's what you should be setting (not the symbol).
Also, you can't use ? for variables, just method names.
? is convention for methodnames, not variables. You can't use an instance variable named #foo?, however you could use a variable named #foo and name the (manually created) getter method foo? if you wanted to.
Monkey-patching metaprogramming - maybe it can be made more elegant, this is only a quick draft, and I haven't done metaprogramming for a little while...
# inject the convenience method into the definition of the Object class
class Object
def Object::bool_attr(attrname)
class_eval { define_method(attrname.to_s,
lambda { instance_variable_get('#' + attrname.to_s.chop) }) }
class_eval { define_method(attrname.to_s.chop+"=",
lambda { |x| instance_variable_set('#'+attrname.to_s.chop, x) }) }
end
end
### somewhere later
class MyClass
bool_attr :my_boolean_attribute?
def initialize
#my_boolean_attribute = true
end
end
# yet even more later
foo = MyClass.new
bar = MyClass.new
foo.my_boolean_attribute = 1
puts foo.my_boolean_attribute?
puts bar.my_boolean_attribute?
With this approach, you can be DRY and get the nice questionmark too. You just might need to pick a better name than "bool_attr", like, "bool_attr_accessor" or something similar.
The definitions that I made are a bit cranky, in a sense that the question mark is present in the original symbol. Probably a cleaner approach would be to avoid the questionmark in the symbol name and append it during the definition of the method - should be less confusing.
Oh, and almost forgot to include the obligatory link: Seeing metaclasses clearly
I looked through the answers, and while the accepted answer is on-target, it introduces "extra" noise in the class. The way I'd suggest solving this issue is:
class Animal
attr_writer :can_swim
def initialize(animal_type_name)
#can_swim = true
#animal_type_name = animal_type_name
end
def can_swim?
#can_swim
end
def to_s
#animal_type_name
end
end
dog = Animal.new('Dog in a bag')
dog.can_swim = false
puts "Can this #{dog} Swim? --- [#{dog_without_legs.can_swim? ? 'YEP!' : 'NOPE!'}]"