require 'dotenv' require 'cloud_powers/helpers' module Smash module CloudPowers # This module provides some environment variable management and functionality # Hopefully it should provide us with some "Zen", when dealing with normally # annoying env issues. Meh, it probably won't but I like the name, so it stays :} # System ENV, dotenv ENV and instance variables are considered for now but this # will also use elasticache/redis...some other stuff too, in the coming versions module Zenv include Smash::CloudPowers::Helpers # Search through the {Dotenv}[https://github.com/bkeepers/dotenv] # variables for a key or if no key is given, return all the .env-vars # and their values # # Parameters # * key +String+ - def env_vars(key = '') return ENV if key.empty? || key.nil? ENV[to_snake(key).upcase] end # Search through the instance variables for a key or if no key is given, # return all the i-vars and their values # # Parameters [key ::Process#pid() def pid Process.pid end def lsof_cwd to_realpath((`lsof -p #{Process.pid} | grep cwd`).split(' ').last.strip) end def process_search search_results = proc_cwd || lsof_cwd || ps_cwd || nil paths_lcd(search_results, called_from) end # # Search the output of `ps ...` to find this process' working # # directory # # # # Returns # # * +Pathname+ # def path_resolution(one, two) # logger.debug("path_resolution request from #{caller.first} as #{proc_cwd}") # Pathname.new(sys_command_output).realpath # end # Ask Linux systems what directory the process is running from. # # Returns # * +Pathname+ - the lcoation containing the file that was called to run # this program # * +nil+ - if the OS doesn't support the proc methods, +nil+ is returned def proc_cwd begin to_realpath(`ls -l /proc/#{pid}/cwd`.split(/\->\s?/).last.strip) rescue NoMethodError => e logger.debug('this system does not support /proc files system queries') nil end end # Get the location of the current working directory for the project that # is calling this method. # # Returns # * +Pathname+ # # Notes: # * Tested on Ubuntu, Mac and Centos 7 # * Similar methods: # * * proc_cwd # * * called_from def ps_cwd begin ef = `ps -ef | grep #{pid}`.split("\n").first.split(' ').last aux = `ps aux | grep #{pid}`.split("\n").first.split(' ').last Pathname.new(/[A-Za-z]*\.[A-Za-z]+$/ =~ ef ? ef : aux).realpath rescue Exception => e if e.nil? logger.debug('This system does not support ps functionality') nil else super end end end # PROJECT_ROOT, project_root= your_project_root or # @project_root should be set as early as possible in this # Node's initilize process. This method tries to search for it, using # Zenv#zfind and if that fails, another search through system # tools is run to make a best attempt at finding the lowest common path. # # Returns # +Pathname+ - path to the project root or where ever `pwd` resolves # to for the caller # # Notes # * TODO: improve this...it needs to find the gem's method's caller's project # root or at least the gem's method's caller's file's location. # # Example # /home/ubuntu/cerebrum$ ruby -e cerebrum.rb 'Cerebrum.new.project_root' # # => '/home/ubuntu/cerebrum/' # or # project_root invoked from somewhere in # /Users/crazyman/.ssh/why/all/the/wasted_time # called_from is # /Users/crazyman/.ssh/why/all/the/insanity/go_nuts.rb#ok():(line -999999999.9) # and # /proc//cwd is /Users/crazyman/.ssh/why/all/the/madness/ # and # # caller is # [ # ... , /Users/crazyman/.rbenv/versions/2.4.0/lib/ruby/ # gems/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/hooks.rb, ...] # and # ps -ef | grep #{pid}... is [ # ..., /Users/crazyman/.rbenv/versions/2.4.0/lib/ruby/ # gems/2.4.0/gems/rspec-core-3.5.4/lib/rspec/core/hooks.rb, ...] # then # project_root # # => '/Users/crazyman/.ssh/why/all/the' def project_root if @project_root.nil? new_root = to_realpath(zfind('project root') || process_search) logger.info("project root set to:#{new_root} by:#{called_from}") @project_root = new_root else @project_root end end # Manually set the +@project_root+ i-var as a +Pathname+ object. # # Parameters # * +String+|+Pathname+ - new path to the project root # # Returns # +@project_root+ [+Pathname+] # # Example # project_root # # => '/home/ubuntu/cerebrum/' # project_root = Pathname.new(`pwd`) # project_root == `pwd` # # => true def project_root=(path) @project_root = to_pathname(path).realpath end # Search through the system environment variables for a key or if no key # is given, return all the system-env-vars and their values # # Parameters # * key +String+ - the key to search for # # Returns # * if a +key+ is given as a parameter, +String+ # * if no +key+ is given as a parameter, +Hash+ # with this structure +{ key => value, ... }+ is returned for all keys with a value. # Keys with no value are ommitted from the result. def system_vars(key = '') (key.empty? || key.nil?) ? ::ENV.to_h : ::ENV[to_snake(key).upcase] end # ZFind looks for the key in a preditermined order of importance: # * i-vars are considered first becuase they might be tracking different # locations for multiple jobs or something like that. # * dotenv files are second because they were manually set, so for sure # it's important # * System Env[@] variables are up next. Hopefully by this time we've found # our information but if not, it should "search" through the system env too. # # Parameters # * key +String+|+Symbol+ - the key to search for # # Returns # * +Object+ - whatever type you were looking for, storing or accidentally # now dealing with is what can be returned # # Notes # * TODO: implement a search for all 3 that can find close matches def zfind(key = '') i_vars(key) || env_vars(key) || system_vars(key) end # Get anything that matches, within the same area that #zfind # can search through. This has the feel of Hash#select./ def zselect(key = '') vars = system_vars.merge(env_vars).merge(i_vars) if (key.empty? || key.nil?) vars.values else vars.select { |k,v| %r"#{to_snake(key)}" =~ to_snake(k) }.values # results = vars.select { |k,v| %r"#{to_snake(key)}" =~ to_snake(k) } # (results.count == 1) ? results.values.first : results end end end end end