How to override array in Typesafe config with environment variables? - typesafe

I've been using the environment variable substitution feature in Typesafe config:
foo = "foo"
foo = ${?FOO}
This results in a "default" value of "foo" if there is no environment variable named FOO. In this case, the second value declaration (foo = ${?FOO} is simply discarded). However, if a variable named FOO exists, the library will "substitute" the value of FOO and assign it to foo.
I would like similar behavior with arrays, but unfortunately, this does not work as expected:
foo = [ "1", "2" ]
foo = [ ${?f1}, ${?f2} ]
In the case where f1 and f2 are not defined, this simply results in foo being an empty array. My goal is to have a similar effect as above (discard the second foo if no environment variables f1 and f2 are defined). Any ideas/suggestions are appreciated. Thanks.

I found that using a = ${?VARNAME} in the HOCON config file with the Scala code expecting a List, and using -DVARNAME.0=something (or simply VARNAME.0=something) will result in the correct ["something"] value. (Tested with Play 2.6.13 and the AllowedHosts filter.)

Related

When is a line of Ruby parsed? evaluated? executed?

I was a little surprised to discover that person is defined by the following line of code, even when params[:person_id] doesn't exist:
person = Person.find(params[:person_id]) if params[:person_id]
I kind of expected that Ruby would first check the if statement and only define person then. In practice it seems person is defined earlier than that but remains nil.
While investigating that I tried the following:
irb> foo
# NameError (undefined local variable or method `foo' for main:Object)
irb> if false
irb> foo = 'bar'
irb> end
irb> foo
# => nil
Initially foo is undefined. But then it gets defined, even though it's only referenced inside an if block that isn't evaluated.
I'm now guessing that the whole program gets parsed(?) and that a foo node is added to the Abstract Syntax Tree (i.e. defined). The program is then executed(?), but that particular line is skipped (not evaluated(?)) and so foo is nil (defined but not set to a value).
I'm not sure how to confirm or refute that hunch though. How does one go about learning and digging into the Ruby internals and finding out what happens in this particular scenario?
Answering my own question, Jay's answer to a similar question linked to a section of the docs where it is explained:
The local variable is created when the parser encounters the assignment, not when the assignment occurs
There is a deeper analysis of this in the Ruby Hacking Guide (no section links available, search or scroll to the "Local Variable Definitions" section):
By the way, it is defined when “it appears”, this means it is defined even though it was not assigned. The initial value of a defined [but not yet assigned] variable is nil.
That answers the initial question but not how to learn more.
Jay and simonwo both suggested Ruby Under a Microscope by Pat Shaughnessy which I am keen to read.
Additionally, the rest of the Ruby Hacking Guide covers a lot of detail and actually examines the underlying C code. The Objects and Parser chapters were particularly relevant to the original question about variable assignment (not so much the Variables and constants chapter, it simply refers you back to the Objects chapter).
I also found that a useful tool to see how the parser works is the Parser gem. Once it is installed (gem install parser) you can start to examine different bits of code to see what the parser is doing with them.
That gem also bundles the ruby-parse utility which lets you examine the way Ruby parses different snippets of code. The -E and -L options are most interesting to us and the -e option is necessary if we just want to process a fragment of Ruby such as foo = 'bar'. For example:
> ruby-parse -E -e "foo = 'bar'"
foo = 'bar'
^~~ tIDENTIFIER "foo" expr_cmdarg [0 <= cond] [0 <= cmdarg]
foo = 'bar'
^ tEQL "=" expr_beg [0 <= cond] [0 <= cmdarg]
foo = 'bar'
^~~~~ tSTRING "bar" expr_end [0 <= cond] [0 <= cmdarg]
foo = 'bar'
^ false "$eof" expr_end [0 <= cond] [0 <= cmdarg]
(lvasgn :foo
(str "bar"))
ruby-parse -L -e "foo = 'bar'"
s(:lvasgn, :foo,
s(:str, "bar"))
foo = 'bar'
~~~ name
~ operator
~~~~~~~~~~~ expression
s(:str, "bar")
foo = 'bar'
~ end
~ begin
~~~~~ expression
Both of the references linked to at the top highlight an edge case. The Ruby docs used the example p a if a = 0.zero? whlie the Ruby Hacking Guide used an equivalent example p(lvar) if lvar = true, both of which raise a NameError.
Sidenote: Remember = means assign, == means compare. The if foo = true construct in the edge case tells Ruby to check if the expression foo = true evaluates to true. In other words, it assigns the value true to foo and then checks if the result of that assignment is true (it will be). That's easily confused with the far more common if foo == true which simply checks whether foo compares equally to true. Because the two are so easily confused, Ruby will issue a warning if we use the assignment operator in a conditional: warning: found `= literal' in conditional, should be ==.
Using the ruby-parse utility let's compare the original example, foo = 'bar' if false, with that edge case, foo if foo = true:
> ruby-parse -L -e "foo = 'bar' if false"
s(:if,
s(:false),
s(:lvasgn, :foo,
s(:str, "bar")), nil)
foo = 'bar' if false
~~ keyword
~~~~~~~~~~~~~~~~~~~~ expression
s(:false)
foo = 'bar' if false
~~~~~ expression
s(:lvasgn, :foo,
s(:str, "bar"))
foo = 'bar' if false # Line 13
~~~ name # <-- `foo` is a name
~ operator
~~~~~~~~~~~ expression
s(:str, "bar")
foo = 'bar' if false
~ end
~ begin
~~~~~ expression
As you can see above on lines 13 and 14 of the output, in the original example foo is a name (that is, a variable).
> ruby-parse -L -e "foo if foo = true"
s(:if,
s(:lvasgn, :foo,
s(:true)),
s(:send, nil, :foo), nil)
foo if foo = true
~~ keyword
~~~~~~~~~~~~~~~~~ expression
s(:lvasgn, :foo,
s(:true))
foo if foo = true # Line 10
~~~ name # <-- `foo` is a name
~ operator
~~~~~~~~~~ expression
s(:true)
foo if foo = true
~~~~ expression
s(:send, nil, :foo)
foo if foo = true # Line 18
~~~ selector # <-- `foo` is a selector
~~~ expression
In the edge case example, the second foo is also a variable (lines 10 and 11), but when we look at lines 18 and 19 we see the first foo has been identified as a selector (that is, a method).
This shows that it is the parser that decides whether a thing is a method or a variable and that it parses the line in a different order to how it will later be evaluated.
Considering the edge case...
When the parser runs:
it first sees the whole line as a single expression
it then breaks it up into two expressions separated by the if keyword
the first expression foo starts with a lower case letter so it must be a method or a variable. It isn't an existing variable and it IS NOT followed by an assignment operator so the parser concludes it must be a method
the second expression foo = true is broken up as expression, operator, expression. Again, the expression foo also starts with a lower case letter so it must be a method or a variable. It isn't an existing variable but it IS followed by an assignment operator so the parser knows to add it to the list of local variables.
Later when the evaluator runs:
it will first assign true to foo
it will then execute the conditional and check whether the result of that assignment is true (in this case it is)
it will then call the foo method (which will raise a NameError, unless we handle it with method_missing).

Ruby variable assignment in a conditional "if" modifier

I have a question about how the Ruby interpreter assigns variables:
I use this quite often:
return foo if (foo = bar.some_method)
where some_method returns an object or nil.
However, when I try this:
return foo if (true && (foo = bar.some_method))
I get: NameError: undefined local variable or method foo for main:Object.
What is the difference in evaluation between the first and second lines that causes the second line to error?
Read it carefully :
Another commonly confusing case is when using a modifier if:
p a if a = 0.zero?
Rather than printing true you receive a NameError, “undefined local variable or method 'a'”. Since Ruby parses the bare a left of the if first and has not yet seen an assignment to a it assumes you wish to call a method. Ruby then sees the assignment to a and will assume you are referencing a local method.
The confusion comes from the out-of-order execution of the expression. First the local variable is assigned-to then you attempt to call a nonexistent method.
As you said - None return foo if (foo = bar.some_method) and return foo if (true && (foo = bar.some_method)) will work, I bet you, it wouldn't work, if you didn't define foo before this line.

Ruby turn values inside a hash into local variables

Say I have this hash:
entry = {"director"=>"Chris Nolan", "producer"=>"Sum Duk", "writer"=>"Saad Bakk"}
I want to extract each key into its own local variable with the associated value:
director = "Chris Nolan"
producer = "Sum Duk"
...
By using a loop and not:
director = entry["director"]
Since there are a lot of values and I don't want to do them individually.
I found this which works almost perfectly except it creates an instance variable and I want a local variable, but local_variable_set doesn't exist for some reason.
entry.each_pair { |k, v| instance_variable_set("##{k}", v) }
Is there a solution? Or failing that, a way to turn an instance variable into a local one and delete the instance one without doing it one by one?
Option 1
I can't recommend this except for fun, but it mostly has the effect you are after:
entry.each |k, v|
singleton_class.send(:attr_accessor, k)
send("#{k}=", v)
end
director # => "Chris Nolan"
self.director = "Wes Anderson" # Unfortunately, must use self for assignment
director # => "Wes Anderson"
Instead of creating local variables it defines acccessor methods on the singleton class of the current object, which you can call as if they were local variables.
To make them more "local" you could use singleton_class.remove_method to remove these methods when you are done with them. You could even try to alias any existing singleton class methods with the same name and restore them afterwards.
Option 2
This is something I use in real code. It requires the ActiveSupport gem which comes with Ruby On Rails but can also be used on its own.
director, producer, writer = entry.values_at('director', 'producer', 'writer')
Unfortunately it requires typing each variable name twice, instead of zero times as you asked for. If you were certain about the order of the values in the hash, you could write:
director, producer, writer = entry.values # Not so good, IMO.
I feel uneasy about this version because now the code that created the hash has a non-obvious responsibility to ensure the hash is in a certain order.
Note
If your goal is just to reduce typing, here is an approach that only requires two more characters per variable access than if they were true local variables:
e = OpenStruct.new(entry)
e.director # => "Chris Nolan"
You can't create local variables, because of variable scope.
If you create a local variable inside a block, the variable would be only accessible inside the block itself.
Please refer to this question for more info.
Dynamically set local variables in Ruby
You can do this with eval, but all operations on those variables must be inside the scope they were defined in.
For example, this will work:
vals = {
"foo" => "bar",
"baz" => "qux"
}
eval <<-EOF
#{ vals.map {|k, v| "#{k} = \"#{v}\""}.join("\n") }
puts foo
EOF
However, this will not:
vals = {
"foo" => "bar",
"baz" => "qux"
}
eval <<-EOF
#{ vals.map {|k, v| "#{k} = \"#{v}\""}.join("\n") }
EOF
puts foo
as foo goes out of scope at the end of the eval. However, if all your work on the variables can be done inside the scope of the eval, it's quite doable.
Expanding on #antinome's note: If you want local variables just to exist, because you want to access them dynamically for instance by interpolating them in a String, then OpenStruct + instance_eval offers this powerful way:
require 'ostruct'
o = OpenStruct.new(entry)
o.instance_eval("Directed by #{director}, produced by #{producer}")
o.instance_eval {
puts "Directed by #{director}"
puts "Produced by #{producer}"
}

Get current value that a global variable is pointing to

I have some code written like this
$var1 = 2
$var2 = 4
...
keywords = {"one" => $var1, "two" => $var2, ...}
The problem is that I want to get the value stored in that variable when I hash into keywords, but ruby doesn't seem to be re-evaluating the variables when I need them. So if $var1 was changed during execution, keywords["one"] still returns the original value of 2
How can I change the hash so that I will first determine which variable I need to access, and then go and grab its value?
There are quite a number of global variables (they didn't use array?), and I don't want to have redundant case/when or if/elsif blocks.
EDIT: A more precise question would be
"How do I retrieve the value that a variable is currently pointing to?"
In general this seems like a bad idea. Instead of updating $var1 just update the hash and always access $var1 through keywords["one"]
You could use eval, but it's not a good solution:
$var1 = 2
$var2 = 4
...
keywords = {"one" => "$var1", "two" => "$var2", ...}
$var1 = 6
var1 = eval(keywords["one"]) # -> 6
Update: Note that the eval will take any code you pass in and execute it. You need to be very careful that the thing you're evaling is of the form you expect. It can be a big security risk.
Variables do not contain values, they reference them. When you write:
foo = 2
bar = [foo]
foo = 4
…it does not cause bar to be [4]. What ~happens is:
Create a value 2 in memory and set the variable foo to reference it.
Create a new array whose first slot references the value referenced by foo (i.e. 2), and set the variable bar to reference that array.
Create a value 4 in memory and change the variable foo to reference it instead.
Some object types that are mutable. For example, you can change the actual contents of a String or an Array to be different. In this case, all references to that value reflect the change:
foo = "hello"
bar = [foo]
foo[0] = "j"
p bar #=> ["jello"]
Numbers are not mutable, however. If you want to have a 'handle' to a number than you can change and have all other references to that number change, you will need to wrap the number in a mutable data type. For example:
$var1 = [2]
keywords = { "one"=>$var1 }
p keywords["one"][0] #=> 2
$var1[0] = 4
p keywords["one"][0] #=> 4
Now, if your values are not numbers, but are strings (as "keywords" implies) then you can mutate and wholly replace those:
$var1 = "foo"
keywords = { "one"=>$var1 }
$var1.replace( "bar" )
p keywords["one"] #=> "bar"
The key is that you must be calling a method on the object itself (and that method must change the object itself, not return a new object); you cannot make this work using variable assignment. For example, see String#replace.

Ruby hash value converts to string, don't know why

I have a 'strange' problem, the following code converts the location lat value into a string (With a + sign for each iteration) leading to an eventual exception when comparing values. I've tried the code with values for another location and it works fine. The only difference is that the other numbers were negatives.
location= {:lng => 2.0781,:lat => 41.2899}
while location[:lat] < top
sleep(1)
checkTweets(location)
bottom+=0.075
location[:lat] = bottom
end
The issue occurs before entering the check tweets location. The values for the hash are as follows
To to conclude, my question is can anyone explain to me why location[:lat] ends up being a string in this circumstance?
Bottom is initialized as 30.0400 which is assigned to the :lat value. The checkTweets method simply writes a file based on a mongodb query.
Right I found the solution to this. It was the twitter library which was turning the Hash float values into strings.
Am I wrong in assuming that the scope of the variable in the checkTweets method should not impact the location variable here, they are both declared in seperate methods, they are not class level.
I wrong in assuming that the scope of the variable in the checkTweets method should not impact the location variable here, they are both declared in seperate methods, they are not class level.
No, but variable scope is not the issue here. The location variable is local to your method and as such cannot be changed by the checkTweets method. That is correct.
However the object that is referenced by the location variable can be changed from the checkTweets method and that is exactly what happens (though I have to say that mutating arguments is very bad style).
A little example to illustrate reference semantics and mutation in ruby:
def f1(arr)
arr = [1,2,3] # Changes the variable arr, which is local to f1
# This change is not visible on the outside
end
def f2(arr)
arr.concat [1,2,3] # Changes the object that arr refers to
# This change will be visible any place where the same
# array is referenced
end
foo = [42,23]
f1(foo)
# foo is still [42, 23]
f2(foo)
# foo is now [42, 23, 1, 2, 3]
Here the variable foo hasn't been changed to refer to another object (that would not be possible from inside a method), but the object that foo refers to has been changed. The same happens in your checkTweets method.

Resources