I am supplying environment variables in docker run as docker run --env-file <env_file_path>..... The Dockerfile executes a ruby script which reads the env variables.
# environment variable file
TEST_1=NULL
TEST_2=
TEST_3=""
# ruby script
print ENV['TEST_1'] # "NULL"
print ENV['TEST_2'] # ""
print ENV['TEST_3'] # "\"\""
How can I receive a nil values in ruby from the environment variables? What should the supplied environment variable value be?
How can I receive a nil values in ruby from the environment variables? What should the supplied value be?
You can't, not directly. Unlike a language like ruby, environment variables do not have a concept of null values.
You'll have to check for and convert a chosen value like an empty string, NULL or nil to an actual nil in your code.
You could use the mere presence / absence of the env variable, but the issue with that is that you can't unset an environment variable once it's set with docker, only set it to empty string.
Does it have to be nil? Can you use undefined instead?
If you're doing an if check in the code: if env.test_1 then you could just not define it in the file.
The value of an environment variable (in terms of the operating system) is a string, and you can't beat this, in particular not with a language which has a very unusualy idea of a null pointer (since nil is an object of a full-featured class in Ruby, much different to i.e. C or Java).
Usually (when seen from i.e. shell programming), we sometimes distinguish between an environment variable which is empty, and one which is undefined. This you can do in your Ruby too:
ENV.has_key?('V') # -> true if defined, false if undefined
ENV['V'] # -> trueish if defined, nil if undefined
If you know that an environment variable is defined, you can check whether it is empty, by
ENV['V'].empty?
Transfering the Ruby type system to a data model established by the whole operating system, is usually futile, however there is a way, if you can live with restrictions:
Assuming that you need the environment variables to transport Ruby data (nil, arrays of strings, or whatever) from one Ruby process to another Ruby process, you can of course serialize the data. Serialization means that you create a string representation of the data (which makes it suitable for being stored in an environment variable) and deserialize it in the receiveing process.
Ruby comes with 3 serialization methods: JSON, YAML and (the most general and flexible one) Marshal. With JSON and YAML, you have even a chance to deserialize in a different language. With Marshal, you have to use the same Ruby implementation on the sending and receiving end.
With all serialization methods, you indeed can represent a Ruby nil in an Environment variable, for instance:
ENV['V']=Marshal.dump(nil) # serializing
v=Marshal.load(ENV['V']) # deserializing
Related
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)
I need to check if either of two environment variables env_http_proxy or http_proxy are set. If either is set, assign the value to a local variable. If neither of them exist, then the local variable should be set to nil.
http_proxy = defined?(ENV['env_http_proxy']) ? ENV['env_http_proxy'] : defined?(ENV['http_proxy']) ? ENV['http_proxy'] : nil
Whats wrong with this? this seem to work for the first variable but not for the second variable.
(PS: If it matters, I am trying to use this in a Vagrantfile)
Also, can someone please explain the above syntax. I am ruby noob. A quick search only showed the defined?() function. But not the above extended syntax.
t = ENV['env_http_proxy'] || ENV['http_proxy']
As a general rule, don't use defined? for anything. It's a metaprogramming primitive intended for implementing something occasionally called magic1..
The e1 ? e2 : e3 operator works just like C, testing expression e1 and then taking the value of e2 or e3. It's not used as much in Ruby as in other languages.
Since ENV is a hash-like object, it will return nil when no key exists, and one of the zillion awesome features of Ruby is the way the || operator returns the value of the true expression.
1. Magic: noun, see: Ruby on Rails.
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.
I am new to Ruby, so please bear my questions in case they might not make any sense. My question is,
A constant is where we assign values that should not be altered. Why is ARGV a constant, when we can pass as many arguments as we want to in it? Are the arguments not the values for ARGV? When we pass arguments to ARGV, are we assigning values or does ARGV already have its own sets of values?
A constant has to have its value newly assigned at some point. If you take the meaning of constant as something that is never newly assigned its value, then there would be no constant at all. A constant is therefore, a relative notion; you cannot define what a constant is without the relevant domain/scope. A constant remains consistant within that domain, but has its value assigned/changed outside of the scope.
In mathematics, suppose some mathematician used a constant A = 3 at some point in their life solving a certain problem. That does not mean that everyone from that point on using the constant A would always have to assume its value to be 3. In mathematics, the domain of a constant can be a single proof, an article, a book, or a convention throughout a subfield, etc.
For a computer program, the domain for a constant is usually the execution lifespan of a program. A constant remains constant relative to the execution of the program. ARGV has its values set prior to the execution of the Ruby program.
The point is that ARGV has constant value for the entire time span your program runs. Another reason is that you are not supposed to change the value of ARGV. From the Wikipedia page titled Constant (computer programming):
[…] a constant is an identifier whose associated value cannot typically be altered by the program during its execution […]
Ruby is a bit special because it allows you to reassign ARGV (as any other constant), although it will issue a warning. The following is valid Ruby code (but please don’t do this):
ARGV = [123]
# warning: already initialized constant ARGV
p ARGV
# [123]
ARGV is a constant array that is defined on initialization of a Ruby script, in which the values in that array are set to the arguments which were passed in to the script itself.
From the ARGF documentation:
ARGF is a stream designed for use in scripts that process files given as command-line arguments or passed in via STDIN.
The arguments passed to your script are stored in the ARGV Array, one argument per element. ARGF assumes that any arguments that aren't filenames have been removed from ARGV
See the documentation for ARGV for more details.
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.