Ruby: Unable to access global variable, not starting with $ - ruby

Normally every global variable in ruby ​​should start with a $. Are these parameters "param_string" and "param_array" also global variables or something else?
require 'json'
require 'fileutils'
param_string=String.new
param_array=Array.new
# global variable
$tester="hello"
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: staging.rb [options]"
opts.on("-s","--string TEST") do |test|
puts "test string #{test}"
param_string << test
end
end.parse!
def string_funktion()
puts $tester
# working as expected
puts "show my string #{param_string}"
# error: undefined local variable
end
Attempting to access one of these parameters "param_string" or "param_array" within a function and get the error message that the parameter is not defined. If I do the same with the variable $tester, the access works as desired.
What do I have to change for accessing the parameters "param_string" or "param_array" within a function?
EDIT:
Are there reasons or best practices for globally defined variables without naming $?

Normally every global variable in ruby ​​should start with a $.
This is wrong. This has nothing to do with "should".
If it starts with a $, then it is a global variable. If it doesn't, then it is not a global variable.
Are these parameters "param_string" and "param_array" also global variables or something else?
Variables that start with a lowercase letter are local variables. Local variables are called "local" variables, because they are local to the scope they are defined in. In this case, they are defined in the script body, so they are local to the script body.
There are 4 different scopes in Ruby:
Script
Module / Class definition
Method definition
Block / lambda literal
Block scopes are special, because they can be closures and are nested.
Attempting to access one of these parameters "param_string" or "param_array" within a function and get the error message that the parameter is not defined. If I do the same with the variable $tester, the access works as desired.
[Note: this is not a function, it is a method. Ruby doesn't have functions.]
param_string and param_array are local variables, which you can see because they start with a lowercase letter. Local variables are local to the scope they are defined in, in this case the script scope. Therefore, they are not defined in the method.
$tester is a global variable, which you can see because it starts with a $ sign. Global variables are, as the name implies, global. Therefore, it can be accessed everywhere. (Note that there are some builtin "magic" global variables like $! or $1…$9 that behave slightly differently. As a beginner, it is probably easiest to ignore those slight differences for now.)
What do I have to change for accessing the parameters "param_string" or "param_array" within a function?
You could make them global variables, but in general, global variables should be avoided. (Not just in Ruby.)
What the best design in your case would be is hard to tell from the code you provided. You could pass them into the method as arguments, but it is also possible that your design is missing an object or two.

Best practice would be to pass the local variables to the function as parameters.
def string_funktion(param_string, param_array)

Related

Why we are using _ prefix in some cases to define instance variables in ruby?

I see in ruby classes some people using _ prefix to define instance variables like #_property while in normal condition attr_writer uses the normal name like #property to define instance variables.
What is the point of doing this and what is the difference?
The _ prefix isn't a language feature, it doesn't do anything special, it is just a convention.
In most cases, the developer wants to indicate that a variable is not in use, for example, a block variable that is not used in a block. Or they want to indicate that the variable is an internal variable of a module or gem and that others should not read or modify this variable directly.
There are 2 possible answers here depending on type of a variable.
Instance variable
When _ is used to prefix an instances variable (like in your question) it is usually just a convention to make it clear that that instance variable is private implementation detail and should not be used outside of the current scope. You might encounter it especially in modules which are to be included in some other classes - in which case the instance variable is defined and used by that module, but it belongs and is scoped to the object itself. I personally prefer object-scoped registries for this, but "private" instance variable is a quick, dirty and popular way to go. Alos, prefixing instance variable name with _ reduces chances of name conflict.
# Library code
module SomeExternalLibraryModule
def foo
#_something ||= SomeExternalLibraryModule::Something.new(self)
end
end
# Application code
class User
include SomeExternalLibraryModule
attr_reader :something # name conflict "avoided"! Phew!
def bar
#_something.do_sth
# "_" means - you'd better know what you're doing here!
# There is no guarantee that instance variable will be present in the future release
end
end
Local variable
When local variable is prefixed with _ this means that that variable is not being used in the code. It is especially useful when using iterators, or consuming other multi-element inputs of which you're interested in only one.
It is quite common to see just _ as a variable name. It has a bit of a special meaning for parser, which is you explicitly saying "I do not care what that is". As such, it is the only argument name that is allowed multiple time in a single definition:
def foo(_, _, a)
a
end
foo(1,2,3) #=> 3
def bar(a,a,b); end #=> SyntaxError
However, this is usually the best practice to use "I do not care" with a name, which will make your life easier in the future if you actually decide that you need to use other arguments:
def foo(_name, _html_options, options)
options.delete(:some_option)
super
end

In Ruby, what does a top-level assignment do?

I have the following code at the top-level of a .rb file:
class Times
def initialize(n)
#n = n
end
def each()
(1..#n).each {yield}
end
end
three_times = Times.new(3)
def f()
Times.new(3).each {puts 'Test'}
end
f()
This works, and prints 'Test' three times, as expected. However, if I replace Times.new(3) in f with three_times, i.e. three_times.each {puts 'Test'}, I get an error:
`f': undefined local variable or method `three_times' for main:Object (NameError)
Why does this not work? Why is Times accessible from within f, but not three_times?
More generally, what exactly does an assignment at the top level (e.g. three_times = Times.new(3)) do?
Because
three_times is a local variable
local variables are only accessible inside of a specific scope
def in ruby creates a new scope
So when f is invoked, it does not see or have access to three_times
To access three_times change it to either a global variable $three_times or an instance variable #three_times
The reason that you are able to reference the class Times is that it is a constant and ruby goes through a separate process of lookup for constants.
Sidestepping issue with def
You could also access the local variable by using a block to define your method, which sidesteps the whole scope gate issue. I do this sometimes when writing rake tasks but rarely do it outside of scripts.
three_times = Times.new(3)
define_method :foo do
three_times.each { puts 'Tests'}
end
foo
Why does this not work? Why is Times accessible from within f, but not three_times?
Variables whose name starts with a lowercase letter are local variables. Local variables are local to the scope they are defined in (that's why they are called local variables.)
Variables whose name starts with an uppercase letter are constants. Constants are looked up first in the default constant scope, then lexically outwards, then dynamically upwards by inheritance.
More generally, what exactly does an assignment at the top level (e.g. three_times = Times.new(3)) do?
Nothing special. It does the same thing that an assignment anywhere else does. In this case, it:
Dereferences the variable (constant) Times, let's call this object o1.
Evaluates the literal integer expression 3, let's call the resulting object o2.
Sends the message new to o1, passing o2 as an argument. Let's call the answer to that message send o3.
Binds o3 to the local variable named three_times.
As you can see, there's nothing in there that is somehow specific to script scope or the top-level.
It's because it's looking for a local variable called "three_times". If you wish to make "three_times" to be "top-level" or "global", prepend the variable name with $ so that it's "$three_times".
Your code works for me, there is no errors. You can call f() sucecsfully

How do I use a predefined variable/constant in my Cucumber testing Scenario

I have defined a variable as userid in env.rb.
userid='1234'
In my Cucumber testing, Scenario, I wish to confirm that my response contains the correct userid. However, I do not wish to hard code the same in my Senario or step definition. Is it possible to do so?
I would place an additional file, let's say test_constanst.rb in the features/ dir. There, I would define a module like this:
module TestConstants
def self.user_id
1234
end
end
Like this, you have it separated from test configuration and code. You would just have to requrire the file from env.rb.
Variable scope in Ruby is controlled by sigils to some degree. Variables starting with $ are global, variables with # are instance variables, ## means class variables, and names starting with a capital letter are constants. Make the variable global and it will be available everywhere, i.e
$userid='1234'

Redundancy of notation in instance_variable_set, instance_variable_get

When I set or get instance variables using some name, for example #foo, I can do something like:
instance_variable_set("#foo", some_value)
...
instance_variable_get("#foo")
But often, I use a variable for the method name, which does not include the # prefix, so that I end up doing:
method = :foo
...
instance_variable_set("##{method}", some_value)
...
instance_variable_get("##{method}")
But since all instance variables are prefixed with #, I think it redundant to have to type "##{method}" instead of simply typing method. Why are the methods instance_variable_set and instance_variable_get not designed to accept string/symbol without # as its first argument like this:
method = :foo
...
instance_variable_set(method, some_value)
...
instance_variable_get(method)
where the variable to be actually set will be #foo rather than foo?
Is there any advantage with the way it is?
The reason is, quite simply, that the instance variable is named #foo, not foo. The # is part of the variable name, just as the $ is part of the global variable name $foo.
The reason that # is not necessary when calling attr_accessor and friends is because they define attribute methods, so it makes sense to provide the method names, not the variable names.
Of course there is no technical reason instance_variable_set cannot prepend the # itself. However, the method accepts a symbol that corresponds to the variable name. A symbol by definition represents the identifier with the given name. So the only symbol that corresponds to the instance variable #foo is :#foo. That is why you have to include the #, because we know that :foo does not correspond to any instance variable identifier at all. (And if you supply a string, it will be converted to a symbol internally first.)
Update: In the C Ruby implementation (MRI), there is actually no mention of # anywhere in the code that handles instance variables. Only the parser knows instance variables start with a #. So it seems that separating code parsing from implementation is another possible reason.

Semicolon in object variable name

There's a common LDAP attribute called userCertificate;binary. It actually has a semi-colon in the attribute name. In ruby, I turn an LDAP entry into a OpenStruct object called 'struct'.
>> struct.class
=> OpenStruct
But of course ruby thinks it's an end-of-line character.
?> struct.userCertificate;binary
NameError: undefined local variable or method `binary' for main:Object
from (irb):52
from :0
IRB knows that the local variable is there, because it gives me struct.userCertificate;binary from the tab auto-completion. I can also see the class variable when calling struct.methods on it.
>> struct.methods
=> ... "send", "methods", "userCertificate;binary=", "hash", ...
It's definitely there, I can see the contents if I print the whole variable to_s(). But how can I access the local variable when it has a semicolon in it? I have workarounds for this but I thought it was an interesting problem to post.
Syntactically, I think there is no way around the fact that a semicolon terminates the statement, so I can't imagine there is a way to do exactly what you'd like. However, you could use the send method to retrieve the value:
>> struct.send('userCertificate;binary')
Assigning to such a member would be similar:
>> struct.send('userCertificate;binary=', my_binary_data)
I'm a little bit confused. You are asking about how to access a local variable, but your code examples are about methods?
If it's a local variable, then I don't know any way to access it. However, if it is anything but a local variable, then you can use the appropriate reflection method to get access to it: Module#const_get for constants, Object#instance_variable_get for instance variables, Object#send for methods and so on.

Resources