Is there a .gemrc.local or equivalent? - ruby

Like many people I created a dotfiles repo and am trying extract bits that are not private into their respective dotfiles. I generally have a .whatever.local file loaded if it's present which might contain information I don't want checked into a repository. Rubygems use ~/.gemrc file, but I can't see a way to extract private information out of it into separate file. Does anyone know how this might be done?
In particular I'd like to have the list of sources external to the .gemrc file.

I do not see an equivalent to .local as per v2.4.6 (a recent, but not last version).
The source code of RubyGems states something relevant for what you want to achieve, though. For example, in src/ruby-2.3.0/lib/rubygems/config_file.rb:
##
# Gem::ConfigFile RubyGems options and gem command options from gemrc.
#
# gemrc is a YAML file that uses strings to match gem command arguments and
# symbols to match RubyGems options.
#
# Gem command arguments use a String key that matches the command name and
# +:sources+:: Sets Gem::sources
# +:verbose+:: See #verbose
#
# gemrc files may exist in various locations and are read and merged in
# the following order:
#
# - system wide (/etc/gemrc)
# - per user (~/.gemrc)
# - per environment (gemrc files listed in the GEMRC environment variable)
So you could use the GEMRC environment variable to load extra, private files as well.

Related

What does it mean $: in Ruby

I was reading the following tutorial.
It talked about including files in a Ruby file like require :
require(string) => true or false
Ruby tries to load the library named string, returning true if
successful. If the filename does not resolve to an absolute path, it
will be searched for in the directories listed in $:. If the file has
the extension ".rb", it is loaded as a source file; if the extension
is ".so", ".o", or ".dll", or whatever the default shared library
extension is on the current platform, Ruby loads the shared library as
a Ruby extension. Otherwise, Ruby tries adding ".rb", ".so", and so on
to the name. The name of the loaded feature is added to the array in
$:.
I just want to know what is $: in Ruby and what does $: means.
The variable $: is one of the execution environment variables, which is an array of places to search for loaded files.
The initial value is the value of the arguments passed via the -I command-line option, followed by an installation-defined standard library location.
See Pre-defined variables, $LOAD_PATH is its alias.
Its the load path
Just open in irb terminal and type this $:
This is what you would get. Ofcourse that depends on the ruby ur using.
2.1.1 :009 > $:
=> ["/Users/mac/.rvm/rubies/ruby-2.1.1/lib/ruby/site_ruby/2.1.0", "/Users/mac/.rvm/rubies/ruby-2.1.1/lib/ruby/site_ruby/2.1.0/x86_64-darwin12.0", "/Users/mac/.rvm/rubies/ruby-2.1.1/lib/ruby/site_ruby", "/Users/mac/.rvm/rubies/ruby-2.1.1/lib/ruby/vendor_ruby/2.1.0", "/Users/mac/.rvm/rubies/ruby-2.1.1/lib/ruby/vendor_ruby/2.1.0/x86_64-darwin12.0", "/Users/mac/.rvm/rubies/ruby-2.1.1/lib/ruby/vendor_ruby", "/Users/mac/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0", "/Users/mac/.rvm/rubies/ruby-2.1.1/lib/ruby/2.1.0/x86_64-darwin12.0"]
2.1.1 :010 >
In ruby $ refers to a predefined variable.
In this case, $: is short-hand for $LOAD_PATH. This is the list of directories you can require files from while giving a relative path. In other words, Ruby searches the directories listed in $:
Hope this helps.

How to write a file that is both valid ruby syntax and valid YAML syntax

In order to have only a single point of configuration for my app I need to make a YAML config file that is also valid ruby code. I.e. a mixed syntax file that can be parsed as YAML and parsed as ruby.
My application is a suite of processes managed by the god gem. I want to load a new group of maintained processes (watches) for each new configuration file.
God allows loading a new app.god (ruby) file with new watches defined, but I don't want an app.god and app.yml, just one file. Simplest might be to just have the app.god file and include the configuration within that, but I preferred the idea of a yml file that was also valid ruby code.
#I found this that might be helpful:
#This is a valid ruby and a valid YAML file
#Comments are the same in YAML and ruby
true ?true:
- <<YAML.to_i
# At this point in ruby it is the contents of a here doc (that will be
# converted to an integer and negated if true happens not to be true)
# In YAML it is a hash with the first entry having key "true ?true"
# representing a list containing the string "- <<YAML.to_i"
# If your YAML object should be a list not a hash you could remove the first line
any_valid_yaml: from here
a_list:
- or
- anything
- really
#Then mark the end of the YAML document with
---
#And YAML is done and ignores anything from here on
#Next terminate the ruby here document
YAML
#Now we're in ruby world
#this = "pure ruby"
def anything(ruby)
"here"
end

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

Using a different name for calling a ruby gem from the command line

Is there any way to change the name that the user has to use when calling from the command line? For example, I have a Thor command line app called super_awesome_gem. I want my gem to be called super_awesome_gem, but when the user calls it from the command line I just want them to be able to call sup_awe or something.
I've tried editing the gemspec file and the file and folder names, but I can't figure out what the proper way to do this would be, or even if there is one.
Is there a way to name a gem one way and have the command line call be a different name?
Your gem name and the executables it bundles don't have to be the same at all. In your gemspec, you can define a list of executables via executables:
Gem::Specification.new do |s|
s.name = "super_awesome_gem"
# other gemspec stuff
s.executables = ["sup_awe"]
end
As long as sup_awe is listed in the gemspec's files list, and is executable, that will be in the user's path after they install your gem. Bundler, when bootstrapping your gemspec, makes this even simpler
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
Anything in bin/ will be treated as an executable.
That is the long way of saying that your exectuable/bin file can be named whatever you want, and doesn't have to be named for your gem.
Another way to achieve this is an alias:
alias my_command=original_command
Just place it where it fits you best.
A third way is to use a wrapper_script, which is a script with the desired name which then calls the original command and passes it all arguments it got:
#!/bin/sh
original_command $#
or in cmd.exe on windows:
#echo off
original_command %*

How do I find the location of the gem?

I'm developing a library that provides access to gem metadata, including it's location on the file system. The idea was to let gem authors set it to a relative path from any script:
# $root/example.gemspec
Example::Gem.root '.' # => $root/
# $root/lib/example/gem.rb
Example::Gem.root '../..' # => $root/
Then, the path of the current script would be used to compute the absolute path. My implementation is currently as follows:
def root(relative_to = nil, file = __FILE__)
unless relative_to.nil?
#root = File.expand_path relative_to, File.dirname(file)
end
#root
end
I thought __FILE__ would return the path to the caller's script, but that assumption is wrong.
It worked within the library itself, but broke down when I tried to integrate it with one of my other gems; the generated path was always relative to the support library itself.
How can I implement this without having to pass the current __FILE__ on every call? Otherwise, there isn't much value to be gained; writing root('../..', __FILE__) is almost the same as writing an actual method to do the same thing.
If it's possible to figure out the path without having to specify anything, that would be even better, but I couldn't think of anything. How does Rails do it?
By the way, I'm aware of Gem::Specification#gem_dir, but it always returns paths relative to the installation directory, even if the gem is not actually there, which makes it useless in a development environment.
You can always make use of the backtrace facility provided:
caller.first
It produces an amalgam of file and line but is usually separated by :. I'd be careful to allow for filenames or paths that may contain colon for whatever reason by ignoring the line information but preserving the rest. In other words, do not split but sub:
caller.first.sub(/:\d+:in .*$/, '')

Resources