How do I specify the type of global variable in sorbet? - ruby

I have a global variable that is defined like this:
# -- in rollout_setup.rb
$rollout = Rollout.new(::CachedRolloutStore.new)
T.reveal_type($rollout) # got Rollout
# -- in foo.rb
T.reveal_type($rollout) # got T.untyped
Within the file, the type of $rollout is inferred correctly. However, it is inferred as T.untyped anywhere else that use the variable. I've tried to use T.let but it doesn't work.

Related

In Ruby, using Rdoc, how can aliases of methods to ENV be documented?

Starting with the very concrete issue I face, I want Rdoc to recognize documentation I placed around my monkeypatch of ENV:
# ENV is a hash-like accessor for environment variables.
ENV.singleton_class.class_eval do
##
# :singleton-method: ENV::[name]
# Returns the value for the environment variable +name+ if it exists.
#
# Note that unlike +ENV::fetch+, this method does not raise any error if the
# requested key is valid and not set, but simply returns +nil+.
#
# This difference of behavior is taken into account in provided aliases.
##
# Alternative to +ENV::[]+, that is, returns the value for the environment
# variable name if it exists.
#
# This alias provide a trigraph which is moreover lexically close to the
# classical get/set accessor identifiers. This let +get+ as a possible
# alias for fetch, while giving a good hint on the fact that this method
# is generally a quicker accessor – with less safety net.
#
# Merriam-Webster gives the following pertaining definition for jet:
# transitive verb : to emit in a stream : spout
# See: https://www.merriam-webster.com/dictionary/jet
alias_method :jet, :[]
##
# Trigraph alternative to +ENV::[]=+ and +ENV::store+.
#
# Merriam-Webster gives the following pertaining definition for sow:
# To spread abroad; to propagate.
# See: https://www.merriam-webster.com/dictionary/sow
alias_method :sow, :[]=
end
So my goal is that when rdoc is run, it generates an entry for ENV in the Class and Module Index and document ::[] and its provided aliases. How might I do that?

difference between node.run_state vs global varibale in a ruby file chef

would like to know the difference and recommended approach to use between global variable vs node.run_state
test.rb
dbpassword=''
ruby_block "load_databag_secret" do
block do
secret_key = Chef::EncryptedDataBagItem.load_secret("/home/test/db_key")
db_keys = Chef::EncryptedDataBagItem.load("mydatabag", "mydatabagitem", secret_key)
end
dbpassword=db_keys['DB_PASSWORD']
node.run_state['password']=db_keys['DB_PASSWORD']
end
end
execute "Enable on hosts" do
command lazy { "echo #{node.run_state['password']} > /home/app/db.txt" }
end
template "/config/properties" do
source "properties.erb"
variables(lazy {
:db_password => { node.run_state['password'] },
})
or using node.run_state['password'] in place of global variable in this .rb file
Now execute command worked fine im able to see the password on the echoed file db.txt where as when i used lazy in template variables it outputed as empty value for db_password in template.
So a few issues, first what you have there isn't a global variable, it's a local variable. Globals in Ruby start with $. Second, you can't assign to a local variable from an enclosing scope like that in Ruby (or, indeed, in most languages). That assignment just creates a second dbpassword local variable scoped to the block. You could, however, use a mutation rather than a variable assignment (e.g. dbpassword << whatever). Third, you can't actually use lazy deeply inside the variables hash like that, it has to be at the top level. Fourth, you can straight up side-step all of this if you're only using the value you once like in that example:
template "/config/properties" do
source "properties.erb"
variables lazy {
secret_key = Chef::EncryptedDataBagItem.load_secret("/home/test/db_key")
db_keys = Chef::EncryptedDataBagItem.load("mydatabag", "mydatabagitem", secret_key)
{db_password: db_keys['DB_PASSWORD']}
}
end
Just for completeness in case others find this via Google, with real global variables the biggest difference is unit testing, the run state is tied to the converge so individual unit tests won't see each other's values which is always nice (though of course you could work around this in your code).

Using require to use file with hash

I am trying to refer to a hash written in another file, and tried:
require './filewithhash' #this file has hash
puts name_hash['somename'] #just trying access by the index
and got undefined local variable or method error.
The problem is that when requiring file in ruby, local variables are out of scope. Therefore any local variables defined in required file will be not available later on.
Solutions:
Make your program object oriented and add this hash as a field in some class
Make your variable constant (like this: NAME_HASH)
Make your variable global (like this: $name_hash)
Make your variable an instance variable (like this: #name_hash)
If program is very simple, I would choose option 3. However if it's suppose to grow, 1 is your best choice.
Requires in Ruby don't just inline the file like in PHP; each file has its own scope. That is, the code in the file will execute in a scope that means that all local variables are all constrained to that file's scope, and will not be visible outside of it.
In order to execute a file in the current scope, you're going to have to eval it with the current binding:
file = "file_with_hash.rb"
File.open(file) {|f| eval f.read, binding, file }
This is a bad idea and you shouldn't do it unless you know what you're doing and why. Instead, you should expose that hash as a member of a constant (which transcends the file scope):
# foo.rb
module Foo
PROGRAMMER_WORDS = {foo: 1, bar: 2, baz: 3}
...
end
# another file
require 'foo'
Foo::PROGRAMMER_WORDS # => {:foo=>1, :bar=>2, :baz=>3}

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'

Capistrano Checking for undefined variable in Task

In Capistrano using the Multi-stage extension I have two environments: prod and testing.
I need a few variables in testing.rb that are not needed in prod.rb and I want some of my tasks to be able to check if the variable is defined and use it if it is, but ignore it if it is not set.
So, in testing.rb I would have something like:
set :foo, 'bar'
prod.rb wouldn't have any reference to :foo since it doesn't need it. In one of my tasks, I would like to do something like:
if defined?(foo)
# do something with foo
else
# do something without foo
end
But I keep getting the error:
undefined local variable or method 'foo'
Is there a way to test for undefined global variables in the task? Or do I have to do something like:
set :foo, ''
In all my environments that don't need the :foo variable?
Try using exists?(:foo) instead of defined?(foo), as recommended in the Capistrano docs.

Resources