Is it possible to refresh ENV during runtime - ruby

I am using the dotenv Gem to read variables in my .env file. I created a console command that updates the .env file, but also in the same runtime, it reads them. The problem is that it reads the old values, even though I updated the .env file. Is there a way to refresh the ENV during runtime so it gets the latest values?
.env
FOOBAR=hello
ruby.rb
puts ENV['FOOBAR'] # Prints "hello"
EnvFile.update_variable('.env', 'FOOBAR', "How are you?")
Dotenv.load('.env')
puts ENV['FOOBAR'] # Prints "hello" even though looking in .env it has the new value "How are you?"

Do you mean you are using dotenv? rbenv manages ruby installs and doesn't read .env from what I searched in the source code. With dotenv, you can reload ENV from the .env file by calling Dotenv.load. It's probably going to give you warnings about constants being redefined. What I would do is make a config class to combine the default settings from ENV with the redefining feature/function you want. Config manages everything but reads defaults from .env.

You can refresh with the following:
Dotenv.overload('.env')
Thank you domcermak for answering this at: https://github.com/bkeepers/dotenv/issues/426

Related

How check is my file create/modyfy after other file?

I need a function similar 'make' program.
If my file not exist or if file need update (modyfy time is before my other file) tell me true.
I have one file dependencies to other file. How update it only if neccesary.
You might want to use FileUtils.uptodate?.
uptodate?(new, old_list)`
Returns true if new is newer than all old_list. Non-existent files are older than any file.
In your example you can use it like this:
unless FileUtils.uptodate?('file_a', ['file_b'])
# file_a needs to be updated
end
require 'time_difference'
TimeDifference.between(File.ctime("FilenameA"), File.ctime("FilenameB.txt")).humanize
Using gem 'time_difference'
trouble with other lang than english (withouth .humanize You get number)

Replacing variable value in ruby while setting the value using "set" command

I have a .properties files as below:
user:abcd
pwd:xyz
system:test
Next, I have a ruby script with Watir for browser automation. In this script, I have statements like
browser.text_field(:id => 'identifierId').set "#{user}:variable to be replaced by its value from .properties file".
Similarly, other values need to be replaced for "pwd" and "system".
I tried the solution per below posts:
Replace properties in one file from those in another in Ruby
However, "set" command is setting whatever has been paased as arguments to it instead of replacing the variable with its value.
Please help.
You have to read the information out of the file.
Most Watir users leverage yaml files for this.
config/properties.yml:
user: abcd
pwd: xyz
system: test
Then read the yaml file & parse your data:
properties = YAML.safe_load(IO.read('config/properties.yml'))
text_field = browser.text_field(id: 'identifierId')
text_field.set properties['user']
Alternately you can take a look at Cheezy's Fig Newton gem, which is designed to work with his Page Object gem

How to read Heroku's nested process.env vars / object in nconf?

I'm trying to deploy Ghost 1.2.0 to Heroku. With previous versions of Ghost (<= 0.11.x), they used a config.js file where you could just do:
database: {
client: 'postgres',
connection: {
host: process.env.POSTGRES_HOST,
user: process.env.POSTGRES_USER,
password: process.env.POSTGRES_PASSWORD,
database: process.env.POSTGRES_DATABASE,
port: process.env.POSTGRES_PORT
},
debug: false
}, …
But in this version they're using nconf that replaces this config.js for environment dependent JSON files like config.production.json.
JSON files unlike JS objects can't have vars as values. I could hardcode my credentials to the JSON file, but I don't want to do that because:
Looks like a bad practise to me, and
Heroku rotates credentials periodically and updates applications where this database is attached. (Quoted from Heroku)
After some digging into nconf README and issues I understood that it would be possible to imitate this expected database object just with:
nconf.env({
separator: '__' // Two dashes
});
and defining vars as:
heroku config:set DATABASE__CLIENT=postgres
heroku config:set DATABASE__CONNECTION__HOST=<value>
...
but, no matter what, I get undefined when I later call:
nconf.get('database');
nconf.get('DATABASE'); // In case it was case-sensitive...
Instead, if I call:
nconf.get('DATABASE__CLIENT'); // postgres
it works. I could try (and I will) to modify Ghost scripts to read all variables this way, but as long as it expects a database object it'll be so cool to get it working the right way.
So, has anybody figured out how to correctly recreate an object with Heroku's env vars?
I've finally found the solution.
Unless you want to modify nconf.env(settings) like:
nconf.env({
separator: '__', // Two dashes
lowerCase: true
});
This will make it possible to pass lowerCase: true to env() so that if an environment variable is called SOMETHING or SOMEthing, it will also be gettable using something [Source]
I recommend to use already lowercase env vars.
So,
heroku config:set database__client=postgres
will be readable using:
nconf.get('database:client');
Looks like nconf has a different character separator to define nested variables called separator and another one to read them called logicalSeparator (its default value is :)

How to load a ruby file in to IRB?

I have a file: options.rb
I open IRB and type:
require './options.rb'
#=> true
Try to call a variable in the options file such as key (yes this variable is there and the file is saved)
NameError: undefined local variable or method `key' for main:Object
from (irb):2
Why is this not working? By the way also tried to load the file as: irb -r ./options.rb
UPDATE
Also tried to do load './options.rb' which does return #=> true but this also does not work.
From the require docs:
Any constants or globals within the loaded source file will be available in the calling program’s global namespace. However, local variables will not be propagated to the loading environment.
So if in options.rb you have something like:
key = something
(i.e. key is a local in the file) then it will not be available in irb. If you make it a global (e.g. $key = 'something') or a constant (e.g. KEY = 'something') it should be available.
If you do not like global variables (as matt suggested) you might also make it an instance variable of the object irb is running on (an instance of Object available through self as ruby always has an object it is operating on) you may also assign
#key='value'
in your file which will give you access to #key in your irb-session afterwards. This will work with either require as with load, but require will only load the file if it has not already done so while load will always execute the code in the file and thus will end up overwriting the contents of your variable if it has been changed in the mean time.
Ruby is a interpreted language, so for the interpreter to notice your declarations you need to actually 'run' them, the corresponding command in irb is
load './options.rb'

Reading in variables from a file in Ruby

Is there a way to read in a file of environment variables?
In bash I have a file env.sh that I can use
env.sh
foo="bar"
bash file
set -a
source env.sh
This would allow me to just use foo as if I had delcared it in the ruby script.
Also is it there a way to make sure that this file is unreadable so that passwords could be stored in this file?
It sounds like you should provide a file example for the user/admin to modify for their personal environment, and then populate the environment from that, while avoiding, perhaps, having that file with the sensitive information in a repository. Note: per file security is going to be addressed by where the file is located and your operating system, and server software.
If this is the case, then you can provide a file that holds a template of the kind of things that you would require from the administrator/user of the program you are configuring.
Ruby has the ENV constant that acts like a Hash and holds the environment of the shell you are using.
As an example, there is a file called environment.rb.sample that gets shared with anyone, publicly. It has instructions and holds the template that users can modify freely, with instructions to copy the file to environment.rb. The sample file looks like this:
# environment.rb.sample
# Copy this file to environment.rb and change the name and password to your credentials
ENV['temp_user_name'] = 'Foo Bar'
ENV['temp_password'] = 'Dazz Kezz
The file is then copied to this, perhaps:
# environment.rb
ENV['temp_user_name'] = 'Joe Admin'
ENV['temp_password'] = 'Super Secure Password'
The file that loads this and uses it is just a Ruby file that is freely modified by the user/administrator of the software, and looks like this and is also shared publicly.
# load_environment
require './environment'
puts ENV['temp_user_name']
puts ENV['temp_password']
This loads the file and uses the ENV that is a globally scoped constant for the application.
The file permissions are then managed by the user/administrator of the system and secured like any other sensitive information on their system. The sensitive file should also be listed in the repository's ignore mechanism. It should never be made public.
Yes, there is, and if for some bizzare, arcane reason you must use it, it's eval:
WARNING: Never use this on a user-supplied file
And, unless you have a very, very specific need, don't use it in production code.
eval(File.read("name_of_var_file"), binding)
If what you're really trying to do is write a configuration file, use YAML. A file like this:
config.yaml:
foo: "bar"
Can be accessed like this:
require 'yaml'
conf = YAML.loads(File.read("config.yaml"))
conf['foo'] #=> 'bar'
This is secure and manageable, not to mention standard practice.
As for making the file inaccessible, that is an operating system level problem that can't be solved without information on the environment, OS, setup, etc.
The purpose of a local variable is to be used temporally within a method definition or a block. Using it outside of such environments, particularly across files defeats the purpose of it. You should not need to do it, and Ruby does not have a simple way to do it.
If you are using variables correctly, and want to share variables between files, that should be other types of variables such as instance, class, or global variables. Or, for the purpose of setting environments, you should be using constants. Among them, global variables and constants can be written in a file, loaded in a different file, and be used.
file-a.rb
$foo = 1
FOO = 2
file-b.rb
load "file-a.rb"
$foo # => 1
FOO # => 2
As for instance and class variables, they belong to a class or an instance of it, so they should be defined in such environment. And you can reopen the same class within a different file, and load it in another file.
file-a.rb
class Bar
##foo = 1
def initialize; #foo = 2 end
end
file-b.rb
load "file-a.rb"
Bar.class_variable_get("##foo") # => 1
Bar.new.instance_variable_get("#foo") # => 2

Resources