Ruby: Setting a global variable by name - ruby

I am trying to dynamically set (not create, it already has to exist) a global ruby variable in a method. The variable name is determined from the passed symbol. What I am currently doing is the following:
def baz(symbol)
eval("$#{symbol}_bar = 42")
end
$foo_bar = 0
baz(:foo)
puts $foo_bar # => 42
But to me, this kind of feels very wrong. Is this the way to do this? Or can it be done differently? Also, I don't know how evals perform in ruby. Does it run much slower than
$foo_bar = 42

The method looks fine to me. This guy says that eval efficiency is much worse, though the post is 3 years old.
I will point out that this method suggests you have a lot of global variables, which is generally a code smell if the code base is significant.

If you can use an instance variable instead, there is Object#instance_variable_set.
def baz(symbol)
instance_variable_set("##{symbol}_bar", 42)
end
Note that it only accepts variable names that can be accepted as an instance variable (starting with #). If you put anything else in the first argument, it will return an error. For the global variable counterpart to it, there is a discussion here: Forum: Ruby
Either way, you also have the problem of accessing the variable. How are you going to do that?

Related

Convert and String to Variable Ruby [duplicate]

I have a string, which has been created at runtime. I want to use this string as a variable to store some data into it. How can I convert the string into a variable name?
If you can forgive an # sign in front of the variable name, the following will work:
variable_name = ... # determine user-given variable name
instance_variable_set("##{variable_name}", :something)
This will create a variable named #whatever, with its value set to :something. The :something, clearly, could be anything you want. This appears to work in global scope, by declaring a spontaneous Object instance which binds everything (I cannot find a reference for this).
The instance_variable_get method will let you retrieve a value by name in the same manner.
instance_variable_get("##{variable_name}")
You can use eval() for this provided that you've declared your variable first:
>> foo = []
>> eval("foo")[1] = "bar"
>> foo[1]
=> "bar"
Here are the docs.
Rather than do that directly, why not consider using a hash instead. The string would be the key, and the value you want to store would be the value. Something like this:
string_values = { }
some_string = params[:some_string] # parameter was, say "Hello"
string_values[some_string] = 42
string_values # { 'Hello' => 42 }
some_number = string_values[some_string]
some_number # 42
This has a couple of benefits. First, it means you're not doing anything magic that might be hard to figure out later. Second, you're using a very common Ruby idiom that's used for similar functionality throughout Rails.
Now simply using instance_variable_set method, you can create a instance variable at runtime.
instance_variable_set('#' + 'users', User.all)
I don't mean to be negative, but tread carefully. Ruby gives you a lot of features for highly dynamic programming such as define_method, storing blocks as Proc objects to be called later, etc. Generally these are cleaner code and far safer. 99% of the time using eval() is a mistake.
And absolutely never use eval() on a string that contains user submitted input.
As Jason Watkins says, a lot of people might be quick to use eval() first thing and this would be a serious mistake. Most of the time you can use the technique described by Tack if you've taken care to use a class instance variable instead of a local one.
Local variables are generally not retrievable. Anything with the # or ## prefix is easily retrieved.

Ruby object variables and why they always equal the same name?

In the books I'm reading, I always see
def initialize (side_length)
#side_length = side_length
end
If the object variable always equals the variable with the same name why not write the language to just equal it without having to type it out?
For example:
def initialize (side_length)
#side_length # this could just equal side_length without stating it
end
That way we don't have to type it over with over.
In the example given:
def initialize(side_length)
#side_length = side_length
end
The #side_length = side_length is just an assignment, passing the available argument to an instance variable, in this case it happens to be the same name as the argument.
However those two values don't have to have same names - it's usually named that way for readability/convention reasons. That same code could just as easily be:
def initialize(side_length)
#muffin = side_length
end
The above code is perfectly fine, it just wouldn't read as well (and might slightly confuse someone giving it a first glance). Here's another example of the argument not being assigned to an instance variable of the same name.
It would be possible to write the language in a way which assumes that the argument variable should automatically be assigned to an instance variable of the same name, but that would mean more logic for handling arguments, and that same assumption may result in more restrictions for the developer, for example, someone who may actually want to assign side_length to #muffin for whatever reason.
Here's a SO question similar to this one - the accepted answer provides an interesting solution.
Hope this helps!
It is because "the object variable [does not] always [equal] the variable withthe [sic] same name".

Is it good practice having local variables starting with underscore?

I'm just getting into Ruby and come from the Java and C/C++ environment.
While coding a first little project in Ruby, I somehow got used to let all local variables start with an underscore. I guess my main motivation for this was a better readability and distinction from method calls.
As in principle there are only three types of variables ($global, #instance and local), the vast majority of variables start with an underscore. I'm not really sure, whether this is good or bad. Besides, in a lot other languages, the underscore would be substituted to some other character.
Is there somehow a best practice concerning variable naming beside the usual CamelCase and/or underscore separated? What are the habits of the professional "rubyists"? Have I overlooked some general Ruby conventions, when I chose the leading underscore?
edit
Thanks to all answers and suggestions. It helped me a lot.
Short Summary of Answers and Comments below
(for the short-on-time visitor)
Leading underscores go with:
method arguments: def my_method(_my_arg)
block arguments: e.g. my_array.each { |_x| puts _x}
All other local variables without leading underscores, as programmers coming from e.g. JavaScript might get confused about intended behaviour of the variables.
For visual separation between variable names and method calls, forcing oneself to use "(" brackets ")" with all method calls might increase readability significantly.
Existing answers to this question are now a few years old, and conventions have changed. You should only ever use a leading underscore (_some_param), or a standalone underscore (_), to indicate that you don't care about the value. The rubocop style linting tool will carp about a "useless assignment" if you assign a variable but don't use it, but it will ignore variables with a leading underscore. This allows you to expressly indicate that you don't care about the value and don't intend to use it.
Here's a somewhat-contrived example use-case in an RSpec context:
describe 'login' do
let(:user) { FactoryGirl.create(:user, login: 'bob') }
it 'must be unique' do
_user1 = user
user2 = User.new login: 'bob'
expect(user2.valid?).to be_false
end
end
Here we're indicating that our user helper has a side-effect and returns something, but we don't care about it. You could also just skip the assignment entirely, but seeing a bare user on a line by itself looks odd and doesn't reveal the intention as clearly:
describe 'login' do
let(:user) { FactoryGirl.create(:user, login: 'bob') }
it 'must be unique' do
user
user2 = User.new login: 'bob'
expect(user2.valid?).to be_false
end
end
Other scenarios include ignoring values in iterators, or overriding a method where you want to keep the original method signature but don't care about some of the values:
def greet(name, _title)
puts "Hi, #{name}!"
end
In my experience, underscore-prefixed variables in Ruby are much like underscore-prefixed variables in JavaScript: a "don't touch" flag. More specifically, they are used when the implementer is doing something that really is not supposed to be understood as a part of the object, or shouldn't be thought of as the conceptual interface of the object.
This is more clear in the JavaScript world, where somebody is emulating "private" by prefixing a variable with an underscore. They are encoding that there's part of the object that's under the hood and can be ignored when looking at the object from the outside.
In Ruby, I've only really seen this with things like a cache or a singleton instance - the kind of thing that should be invisible to consumers of your object. Non-underscored variables are things that people using your object might be interested to know are there.
In any case, they seem fairly rare, and I would avoid them unless you want to send a signal to the next guy that's coming along that there's some extra magic or voodoo happening.
As far as making a distinction for method calls, if you're worried that there can be confusion between a method and a local variable, I would call the method on self to clarify. For instance:
def foo
...
end
def some_method
foo # method
bar # variable
end
If this seems unclear for whatever reason, you can clarify with
def some_method
self.foo
bar
end
Nothing wrong with your idea. But if I was having trouble distinguishing local vars from method calls, I would probably just force myself to always use ()'s on methods. (My team at work has discussed making this part of our coding standards).
a = thing # var
b = thing() # method
The possible advantage to this is readability to others. Someone may wonder at your leading _'s, but using ()'s on all method calls should be clear to everyone.
Seeing as how instance variables have the # sign in front of them, and global variables have the $ sign in front of them already in ruby, it is probably unnecessary to put an underscore character in front of the variable names. That being said, I don't think it is a bad practice necessarily. If it helps you to read or write your code in Ruby, then you should use it.
I have sometimes seen Ruby code where an argument for an instance method on a class has an underscore in front of it. Such as:
def my_method(_argument1)
# do something
end
And I think that when you are dealing with a class that may have it's own attributes, like a model file in rails, for instance, this can be helpful so that you know you are dealing with a variable that has been passed into the method as opposed to one of the attributes that belongs to the class/model.

Access local variables from a different binding in Ruby

In Ruby, you can easily access local variables programmatically by using local_variables and eval. I would really like to have meta-programming access to these variables using a single method call such as
# define a variable in this scope, such as
x = 5
Foo.explore_locals # inside the Foo#explore_locals method, access x
where Foo is some external module. The idea is to display and export local variables in a nice way.
What should be inside the explore_locals method? Is there any way to make this possible? If absolutely necessary, I guess it could be
Foo.explore_locals binding
but this is much less elegant for the application I have in mind.
It's a shame there isn't a built-in way to get the caller's binding. The block trick seems to be the usual answer to this question.
However there is another 'trick' which existed for older 1.8 Ruby versions called binding_of_caller. Looks like quix ported it to 1.9. You might want to check that out:
https://github.com/quix/binding_of_caller
Here is an example (but it requires extra braces {} which I would rather avoid if possible):
module Foo
def self.explore_locals &block
p block.binding.eval 'local_variables'
end
end
local_1 = 3
Foo.explore_locals{} # shows [:local_1, :_]

Using a string as a variable at run time

I have a string, which has been created at runtime. I want to use this string as a variable to store some data into it. How can I convert the string into a variable name?
If you can forgive an # sign in front of the variable name, the following will work:
variable_name = ... # determine user-given variable name
instance_variable_set("##{variable_name}", :something)
This will create a variable named #whatever, with its value set to :something. The :something, clearly, could be anything you want. This appears to work in global scope, by declaring a spontaneous Object instance which binds everything (I cannot find a reference for this).
The instance_variable_get method will let you retrieve a value by name in the same manner.
instance_variable_get("##{variable_name}")
You can use eval() for this provided that you've declared your variable first:
>> foo = []
>> eval("foo")[1] = "bar"
>> foo[1]
=> "bar"
Here are the docs.
Rather than do that directly, why not consider using a hash instead. The string would be the key, and the value you want to store would be the value. Something like this:
string_values = { }
some_string = params[:some_string] # parameter was, say "Hello"
string_values[some_string] = 42
string_values # { 'Hello' => 42 }
some_number = string_values[some_string]
some_number # 42
This has a couple of benefits. First, it means you're not doing anything magic that might be hard to figure out later. Second, you're using a very common Ruby idiom that's used for similar functionality throughout Rails.
Now simply using instance_variable_set method, you can create a instance variable at runtime.
instance_variable_set('#' + 'users', User.all)
I don't mean to be negative, but tread carefully. Ruby gives you a lot of features for highly dynamic programming such as define_method, storing blocks as Proc objects to be called later, etc. Generally these are cleaner code and far safer. 99% of the time using eval() is a mistake.
And absolutely never use eval() on a string that contains user submitted input.
As Jason Watkins says, a lot of people might be quick to use eval() first thing and this would be a serious mistake. Most of the time you can use the technique described by Tack if you've taken care to use a class instance variable instead of a local one.
Local variables are generally not retrievable. Anything with the # or ## prefix is easily retrieved.

Resources