Is there a way to call check defined? on a dynamic instance variable name? What I want is to be able to do something like this:
dynamic_attr = 'foo'
define_method :my_method do
if defined? instance_variable_get("##{dynamic_attr}") # Obviously fails, but you see what I'm getting at...
....
end
end
Of course this fails because defined? is a special form that is looking at literals and thus returns "method" in this case because it's looking at the actual instance_variable_get method. And of course instance_variable_get("#foo") is going to return nil if I ever called #foo = nil so it's indistinguishable from having never been set. Is there any way I can see if #foo was defined with the same semantics as defined? but in a dynamic way?
Strange that nobody mentioned that. There is also Object#instance_variable_defined?.
dynamic_attr = 'foo'
define_method :my_method do
if instance_variable_defined?("##{dynamic_attr}")
....
end
end
If I did get your question, you'd need to check whether an instance variable is defined. Object#instance_variable_get retrieves a value of an existing variable or nil if not exists, not the instance variable name.
You can check the definition of dynamically created variable name by its membership to all existing like:
dynamic_attr = 'foo'
define_method :my_method do
if instance_variables.member? "##{dynamic_attr}".to_sym
....
end
end
See array returned with Object#instance_variables contains symbols so you need to convert an argument with String#to_sym to compare apples with apples.
Related
To test, whether a constant (say: a class) is known at a certain point in the code, I can write for instance:
if defined? :String
or I can write
if self.class.const_defined? :String
Is there a situation where I these two ways of testing would make a difference? Note that I don't ask about the case where I have an explicit receiver, such as MyModule.const_defined? :Something, but only for the case where I want to test whether a certain constant (which in my case happens to be a constant denoting a class) is already defined.
First things first, defined? is a keyword which behaves a bit similar similar to a method. It receives the name of the thing (variable, constant, ...) to check. What makes this method different from all others is that the parser will not resolve the value of the given name but rather check directly for whether it is defined (hence the keyword property). To check if a constant is defined, you thus have to pass the actual name (rather than a Symbol):
if defined?(String)
The const_defined? on the oither hand is more regular. It expects a Symbol or String with the name of a constant and checks whether it is defined on the receiver.
Now as for the differences between the two (when used correctly): if you use them both within the context of an instance method to check for the existence of a constant, they work the same.
When running e.g. in a class definition (such that self is e.g. a class), you need to make sure to use the correct receiver for your const_defined method, e.g. if self.const_defined? :String.
Also, defined? can check for a lot more than just constants (e.g. methods, expressions, variables, ...)
If you want to use this to make sure you actually have the name of a constant at hand in a given variable, you need to use const_defined?. If you want to "statically" check whether an constant was defined, you can use defined?.
defined? is a keyword that will check if an expression exists in the current scope.
const_defined? is a method that will check if a constant exists through the ancestor chain of the receiver.
planet = "Earth"
class Experiment
def diff
""
end
def show
puts "defined" if defined?(diff)
puts "Earth not found" if !defined?(planet)
puts "String constant defined" if self.class.const_defined?(:String)
end
end
Experiment.new.show
#=> defined
#=> Earth not found
#=> String constant defined
p Experiment.ancestors #=> [Experiment, Object, Kernel, BasicObject]
p String.superclass #=> Object
Here's an example of situations where this will make a difference:
Using defined?(Nothing's printed)
class Lab
class Coco
end
end
class Experiment
def diff
""
end
def show
puts "defined" if defined?(Coco) #=> Nothing printed
end
end
Experiment.new.show
Using self.class.const_defined? (Something's printed)
class Lab
class Coco
end
end
class Experiment < Lab
def diff
""
end
def show
puts "defined" if self.class.const_defined? :Coco #=> defined
end
end
Experiment.new.show
p Experiment.ancestors #=> [Experiment, Lab, Object, Kernel, BasicObject] We find 'Lab' class in the ancestor chain.
To test, whether a constant (say: a class) is known at a certain point in the code, I can write for instance:
if defined? :String
or I can write
if self.class.const_defined? :String
Is there a situation where I these two ways of testing would make a difference?
These two really do two completely different things. The first tests whether the Symbol literal :String is defined. Obviously, a literal will always be defined, so this expression will always be true.
The second will check whether the constant String is defined, but not starting at the current constant lookup scope, instead starting at the class of self.
TL;DR
There may be cases where you can use them interchangeably, but one is a keyword and the other a method. In addition, the semantics and return values of the two are quite different.
Keywords vs. Methods
Among other things, one key difference is that Module#const_defined? is a method on a class or module that looks up constants in a class and its ancestors, while defined? is a keyword that determines whether its argument is currently known at the calling point in your code.
For example:
char = 'a'
char.const_defined?
#=> NoMethodError (undefined method `const_defined?' for "a":String)
defined? char
#=> "local-variable"
Exceptions vs. Return Values
If you're only concerned about constants, then the main difference is that you can use defined? to determine whether a constant is currently in scope without triggering a NoMethodError exception. For example:
defined? String
#=> "constant"
defined? FooBarBaz
#=> nil
As a bonus, defined? will tell what type of object you're passing as an argument (e.g. "constant"), while #const_defined? returns a truthy value.
Float.constants
#=> [:ROUNDS, :RADIX, :MANT_DIG, :DIG, :MIN_EXP, :MAX_EXP, :MIN_10_EXP, :MAX_10_EXP, :MIN, :MAX, :EPSILON, :INFINITY, :NAN]
defined? Float::NAN
#=> "constant"
Float.const_defined? :NAN
#=> true
As a rule of thumb, it's often considered best practice to reserve exceptions for handling something unexpected that may require your application to halt. Introspection or branching should generally rely on return values or Booleans, so defined? is usually a better choice if you aren't already expecting a given class to already be defined and available within the current scope.
Is there a situation where I these two ways of testing would make a difference?
const_defined? only checks the receiver and its ancestors, but it doesn't take the current module nesting into account:
module Foo
ABC = 123
class Bar
def self.test
p defined?(ABC) #=> "constant"
p const_defined?(:ABC) #=> false
end
end
end
In order to do so, you have to traverse Module.nesting:
module Foo
ABC = 123
class Bar
def self.test
p defined?(ABC) #=> "constant"
p Module.nesting.any? { |m| m.const_defined?(:ABC) } #=> true
end
end
end
I am trying to learn ruby, and have come across something strange. This code:
if defined? branch
puts "param: #{branch}\n"
else
puts "no branch! #{branch}\n"
end
Outputs "no branch! thisisateststring\n", where 'thisisateststring' is the value that has been assigned to the variable branch earlier in the program. How can it be that the variable branch can have a value assigned to it, but not be defined?
Edit:
People seem to not be understanding my question. I am not asking you to figure out why, in this specific instance, the else is being executing; I am asking how can this happen in general. To put it another way, what code would need to be inserted before this, to cause my case to occur?
As I said, I am new to ruby, so I could easily be misunderstanding something basic. I am not trying to solve a specific problem; I am trying to improve my understanding, by learning more about how ruby works. In this instance, how ruby could output a string, while still thinking it is not defined.
#hirolau gave you an example that works. I'd like to reuse his idea and simplify it a little bit for you. Try the following:
defined? foo
=> nil
foo
NameError: undefined local variable or method 'foo' for main:Object
def method_missing(m, *params, &block)
if m.to_s == "foo"
return "cool stuff"
end
end
# foo is still not defined
defined? foo
=> nil
# but returns some value
foo
=> "cool stuff"
Generally no, but Ruby has a lot of ways to handle missing methods and undefined variables. Consider for example:
class Tree
def initialize
if defined? branch
p "param: #{branch}\n"
else
p "no branch! #{branch}\n"
end
end
def method_missing(method_call)
return 'this text comes from method_missing'
end
end
a = Tree.new #=> "no branch! this text comes from method_missing"
class Tree
def branch
return 'NOW I HAVE MY OWN METHOD'
end
end
a = Tree.new #=> "param: NOW I HAVE MY OWN METHOD"
Any more info regarding your problem is very hard to give without the context of the statement.
defined? in Ruby is a keyword.
It returns nil or a description of what the object is as a string.
In Ruby, the only two things that evaluate to false is false and nil
So, if the "thing" that you are trying to find out about is undefined, then defined? will return nil which will evaluate as false in your code, as shown in your question.
To answer your question: "How can a variable have a value and also not be defined?"
It can't. If it isn't defined as a variable, how could it even be a variable. But it can be defined as a variable and have a value of nil.
The defined? Ruby keyword does not check whether a target is nil or not, instead it checks that the target "refers to anything recognizable (literal object, local variable that has been initialized, method name visible from the current scope, etc.)."
Have a look at the documentation to understand its behaviour. http://ruby-doc.org/docs/keywords/1.9/Object.html#method-i-defined-3F
If you want to check if a variable is nil, you can do:
if branch.nil? or branch.empty?
puts "no branch!\n"
else
puts "param: #{branch}\n"
end
I'm trying to keep a hash local to one function that remembers its state between calls to the function. But I don't know how to declare it without a closure (as some users suggested in a similar thread).
I know C++ more thoroughly than ruby, and in C++, I would have ordinarily used a static local variable, like in the first example here: http://msdn.microsoft.com/en-us/library/s1sb61xd.aspx
I managed to hack something together in ruby using the defined? function:
def func x
if not defined? #hash
#hash = Hash.new
end
if #hash[x]
puts 'spaghetti'
else
#hash[x] = true
puts x.to_s
end
end
func 1
func 1
This prints, the following, which is kind of what I want. The only problem is that #hash can be accessed outside of that function.
1
spaghetti
Is there any "cleaner", more preferred way to declare a variable with this behavior (without a factory)? I was going to create two or three variables like #hash, so I was looking for a better way to express this concisely.
What you're doing is pretty common in Ruby, but also so common you don't need to make a big fuss about it. All #-type instance variables are local to that instance only. Keep in mind "instance" generally refers to an instance of a class, but it can refer to the instance of the class as well.
You can use ## to refer to a class instance variable from the context of an instance, but this tends to get messy in practice.
What you want to do is one of the following.
A variable that persists between method calls, but only within the context of a single object instance:
def func(x)
# Instance variables are always "defined" in the sense that
# they evaluate as nil by default. You won't get an error
# for referencing one without declaring it first like you do
# with regular variables.
#hash ||= { }
if #hash[x]
puts 'spaghetti'
else
#hash[x] = true
puts x.to_s
end
end
A variable that persists between method calls, but only within the context of all object instances:
def func(x)
# Instance variables are always "defined" in the sense that
# they evaluate as nil by default. You won't get an error
# for referencing one without declaring it first like you do
# with regular variables.
##hash ||= { }
if ##hash[x]
puts 'spaghetti'
else
##hash[x] = true
puts x.to_s
end
end
This is usually made cleaner by wrapping the ##hash into a class method. This has the secondary effect of making testing easier:
def self.func_hash
#func_hash ||= { }
end
def func(x)
if self.class.func_hash[x]
puts 'spaghetti'
else
self.class.func_hash[x] = true
puts x.to_s
end
end
When I invoke a method that doesn't exist, method_missing will tell me the name of the method. When I attempt to access a variable that hasn't been set, the value is simply nil.
I'm attempting to dynamically intercept access to nil instance variables and return a value based on the name of the variable being accessed. The closest equivalent would be PHP's __get. Is there any equivalent functionality in Ruby?
I do not believe this is possible in Ruby. The recommended way would be to use a ''user'' method rather than a ''#user'' instance var in your templates.
This is consistent with the way you deal with Ruby objects externally (''obj.user'' is a method which refers to ''#user'', but is actually not ''#user'' itself). If you need any kind of special logic with an attribute, your best bet is to use a method (or method_missing), regardless if you're accessing it from inside or outside the object.
See my answer to another similar question. But just because you can do it doesn't mean that it's a good idea. Sensible design can generally overcome the need for this kind of thing and allow you to produce more readable and hence maintainable code.
instance_variable_get seems to be the closest equivalent of PHP's __get from what I can see (although I'm not a PHP user).
Looking at the relevant Ruby source code, the only 'missing' method for variables is const_missing for constants, nothing for instance variables.
there isn't an instance_variable_missing (at least that I know of)
But why are you accessing randomly named instance variables anyway?
If your thread all the access to the object state through method calls (as you should anyway) then you wouldn't need this.
If you are looking for a way to define magic stuff without messing up with the method lookup, you may want to use const_missing.
A bit late but, instance_variable_missing is the same as method_missing to a point... Take the following class:
class Test
def method_missing(*args)
puts args.inspect
end
end
t = Test.new
Now let's get some instance variables:
t.pineapples #=> [:pineapples]
t.pineapples = 5 #=> [:pineapples=,5]
Not sure why the method is nil for you...
EDIT:
By the sounds of it you want to accomplish:
t = SomeClass.new
t.property.child = 1
So let's try returning a Test object from our previous example:
class Test
def method_missing(*args)
puts args.inspect
return Test.new
end
end
So what happens when we call:
t = Test.new
t.property.child = 1
#=>[:property]
#=>[:child=,1]
So this goes to show that this is indeed possible to do. OpenStruct uses this same technique to set instance variables dynamically. In the below example, I create EternalStruct which does exactly what you wanted:
require 'ostruct'
class EternalStruct < OpenStruct
def method_missing(*args)
ret = super(*args)
if !ret
newES = EternalStruct.new
self.__send__((args[0].to_s + "=").to_sym, newES)
return newES
end
end
end
Usage of EternalStruct:
t = EternalStruct.new
t.foo.bar.baz = "Store me!"
t.foo.bar.baz #=> "Store me!"
t.foo #=> #<EternalStruct bar=#<EternalStruct baz="Store me!">>
t.a = 1
t.a #=> 1
t.b #=> #<EternalStruct:...>
t.b = {}
t.b #=> {}
def t.c(arg)
puts arg
end
t.c("hi there") #=> "hi there"
In Ruby, inside a class's instance method, we use a getter by
foo
and we use a setter by
self.foo = something
One doesn't need to have a self. and the other does, is there a way to make them look more similar, and not using something like self.foo as the getter, as it also looks verbose.
(update: note that getter and setter may simply get or set an instance variable, but they might also do a lot of work, such as going into the DB and check the existence of a record and if not, create it, etc)
Since local scope takes precedence, when you say foo = something, a local variable foo will be created and assigned the contents of something.
The reason you can write foo in order to use the getter is because Ruby will move up in scope when it can't find a variable with that name and it will eventually find the method.
If there is a local variable with the same name as the getter method, Ruby will use its value instead:
class Foo
attr_accessor :foo
def initialize
#foo = :one
end
def f
foo = :two
foo
end
end
Foo.new.f
# => :two
In order to make it clear that you want to access the setter, you must write self.foo = something. That will tell Ruby you want to execute the foo= method on the self object with something as parameter.
If you are willing to break the conventions, you can write your setters using jQuery style, using the same method for getter and setter, depending of whether it has arguments or not:
def foo *args
return #foo if args.empty?
#foo = args.first
end
# => nil
foo
# => nil
foo(:bar) # foo = :bar
# => :bar
foo
# => :bar
As far as I know, there isn't a way around this in Ruby. I'm pretty confident this is simply how Ruby evaluates expressions.
When given a value, Ruby will first check if there is a local variable within the context which matches the one being called. If there is (perhaps 'foo' in your case), that will be the value used. If there is no such value, then Ruby will try to look up the value as a method call (falling through to "self" as the caller). If no such method exists in the look up path, an error will be raised.
The need to use "self" in the setter is to avoid Ruby setting the value as a local variable, while the lack of the use of "self" only works in the getter instance when there is no local variable of the same name being used in that context. It is probably better and clearer, albeit slightly more verbose, to be explicit with your use of self as to avoid confusion about where values are coming from.