Module: NRSER::Rash
- Includes:
- SemanticLogger::Loggable
- Defined in:
- lib/nrser/rash/functions.rb,
lib/nrser/rash/version.rb,
lib/nrser/rash/testing.rb,
lib/nrser/rash/config.rb,
lib/nrser/rash/util.rb,
lib/nrser/rash.rb
Overview
Definitions
Defined Under Namespace
Modules: CLI, Config, Formatters, Functions, Helpers, Testing Classes: Writer
Constant Summary collapse
- VERSION =
The current version.
'0.2.3'
- ROOT =
Absolute, expanded path to the gem's root directory.
( Pathname.new(__FILE__ ).dirname / '..' / '..' ).
- COMMAND_LINE_CONFIG =
{}
- ROOT_DIR =
Rash's project dir
File.(File.dirname(__FILE__) + '/..')
- EXECUTABLE =
Executable
ROOT_DIR + '/bin/rash'
Rash Function Class Methods collapse
-
.conflict?(name) ⇒ Boolean
TODO: doesn't work.
-
.function_map ⇒ Object
stub out all the singleton methods under Functions.
-
.function_names ⇒ Object
Stub out all the singleton methods under Functions.
-
.load_functions ⇒ Object
==========================================================================.
- .stub_function?(mod, method_name) ⇒ Boolean
- .stub_name(namespace, method_name) ⇒ Object
- .submodule?(obj) ⇒ Boolean
Testing Class Methods collapse
-
._test_function(module_or_function_name) ⇒ Object
runs tests declared for functions using the
_test_case
and_test_functions
macros.
Utility Class Methods collapse
- .chdir(path, &block) ⇒ Object
- .dedent(string = NRSER::Rash.stdin) ⇒ Object
-
.esc(*args) ⇒ Object
shortcut for
Shellwords.escape
. -
.find_indent(string) ⇒ Object
find the common indent of each line of a string.
-
.log_error(headline, error, message = '') ⇒ Object
==========================================================================.
-
.ref_path(*args) ⇒ Object
splits up string args that represent what i'm calling a "path to a reference" in Ruby.
-
.require_submodules(file_path, deep: false) ⇒ Object
finds direct submodules of
file_path
and requires them. - .stdin ⇒ Object
-
.sub(command, *subs) ⇒ Object
substitute stuff into a shell command after escaping with
Shellwords.escape
. -
.sys(command, *subs) ⇒ Object
execute a system command, using shellwords to escape substituted values.
-
.sys!(command, *subs) ⇒ Object
run and ignore exit code.
-
.sys?(command, *subs) ⇒ Boolean
just runs a command and return true if it exited with status 0.
Class Method Summary collapse
-
.config(name, *default) ⇒ Object
Get a config value by name.
-
.falsy?(string) ⇒ Boolean
Proxy to falsy?.
- .filter_and_set_config(args) ⇒ Object
-
.truthy?(string) ⇒ Boolean
Proxy to truthy?.
Class Method Details
._test_function(module_or_function_name) ⇒ Object
It seems like testing doesn't work in the state I came back to it 2018.02.22
runs tests declared for functions using the _test_case
and
_test_functions
macros. this is hackety-hack-hackety.
rational:
i'm using Test::Unit
. because it comes with the stdlib. Test::Unit
looks to be based on [MiniTest][https://github.com/seattlerb/minitest],
but i can't find great docs on using that either, and i though the
Test::Unit
API might be better known, so i went with that.
the only way i can really find using Test::Unit
documented is
to require 'test/unit'
, which runs all declared Test::Unit::TestCase
subclasses, reading options off the command line and spitting results
back to STDOUT
.
there is an optional --name <pattern>
arg that you can supply on the
command line when invoking a script with a require 'test/unit' line
in it, but it seems like it only matches against method names, not
TestCasesubclasses or anything. under this assumption, the necessary
pattern would need to be a part of all test method names. and i don't
really want a ton of method names like
test_project_dir_name_space_sub
so that i can turn
rash test Project.dir_name` into a pattern that
runs the NRSER::Rash::Functions::Project.dir_name tests.
the hack:
this gets around it by providing macros that will dynamically define
Test::Unit::TestCase
subclasses if and only if those tests are being
run.
NOTE: why does this start with an '_'?
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/nrser/rash/testing.rb', line 44 def self._test_function(module_or_function_name) # if the *first* arg to `rash test` is `--all`, then all test will be run, # which is done by supplying an empty `module_or_function_name` to # `Testing.start`. # # TODO: I just realized that it's currently impossible to test all the # functions defined in {NRSER::Rash::Functions} without testing all # the submodules as well. ugh. solutions include requiring an # explicit (never finished I guess...) # # if module_or_function_name == '--all' module_or_function_name = '' end NRSER::Rash::Testing.start module_or_function_name # going to need the functions loaded to find it load_functions if NRSER::Rash::Testing._test_cases.length == 0 raise NRSER::Rash::Testing::NoTestCasesError.new(module_or_function_name) end require 'minitest/autorun' end |
.chdir(path, &block) ⇒ Object
92 93 94 95 96 97 |
# File 'lib/nrser/rash/util.rb', line 92 def self.chdir path, &block path = File. path Dir.chdir path do block.(path) end end |
.config(name, *default) ⇒ Object
This example is out of date...
Get a config value by name. First looks for an env var defined with
an added 'RASH_'
prefix, then for a const in the NRSER::Rash
module with the provided name.
Easiest to explain by example:
NRSER::Rash::config('BLAH')
will first see if there is an env var named
'RASH_BLAH'
defined, and return it's value if so.
Otherwise, it will try and return
NRSER::RASH::Config::BLAH
raising an exception if it's not found.
Defined here so it can be used when defining the
NRSER::Rash::Config::<name>
consts.
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/nrser/rash/config.rb', line 58 def self.config name, *default if default.length > 1 raise ArgumentError, "wrong number of arguments" \ "(given #{ 1 + default.length }, expected 1-2)" end # command line config options take precedent (those that start with # '--RASH_', which are never passed to the function) if COMMAND_LINE_CONFIG.key? name COMMAND_LINE_CONFIG[name] elsif ENV.key? "RASH_#{ name }" ENV["RASH_#{ name }"] else begin Config.const_get(name) rescue NameError => e if default.empty? raise else default[0] end end end end |
.conflict?(name) ⇒ Boolean
TODO: doesn't work. was try to prevent overriding non-rash functions (without some yet to be implemented way of doing so explicitly) 'cause this can cause accidental CHAOS with other scripts
50 51 52 53 54 55 56 57 58 59 |
# File 'lib/nrser/rash/functions.rb', line 50 def self.conflict? name return false type = `type #{ name } 2>&1` if name == 'blah' raise type end return false if $?.exitstatus != 0 return true not type =~ /rash\ call/ end |
.dedent(string = NRSER::Rash.stdin) ⇒ Object
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
# File 'lib/nrser/rash/util.rb', line 183 def self.dedent(string = NRSER::Rash.stdin) indent = find_indent(string) # short-circuit if there is no indent return string if indent == '' # process the lines new_lines = string.lines.map do |line| # does the line start with the indent? if line[0...(indent.length)] == indent # it does, so chop it off line[(indent.length)..(line.length)] elsif line =~ /^\s+$/ # it does not start with the indent. we're going to let this slide # if it's only whitespace line else # we shouldn't get here if `find_indent` is doing it's job # i guess report an error and return the string? logger.error "should not be so" return string end end new_lines.join end |
.esc(*args) ⇒ Object
shortcut for Shellwords.escape
54 55 56 |
# File 'lib/nrser/rash/util.rb', line 54 def self.esc *args Shellwords.escape *args end |
.falsy?(string) ⇒ Boolean
Proxy to NRSER.falsy?
150 151 152 |
# File 'lib/nrser/rash/config.rb', line 150 def self.falsy? string return string.falsy? end |
.filter_and_set_config(args) ⇒ Object
158 159 160 161 162 163 164 165 166 167 168 169 170 |
# File 'lib/nrser/rash/config.rb', line 158 def self.filter_and_set_config(args) args.reject! do |arg| # long options of form '--<name>=<value>' if m = arg.match(/\A--RASH_([^=]*)(=?)(.*)/) COMMAND_LINE_CONFIG[m[1]] = if m[2] == '' true else m[3] end true end end end |
.find_indent(string) ⇒ Object
find the common indent of each line of a string.
ignores lines that are all whitespace (/^\s*$/
), which takes care
or newlines at the end of files, and prevents the method from
not working due to whitespace weirdness that you can't see.
note that inconsistent whitespace will still cause problems since there isn't anyway to guess the tabstop equating spaces and tabs.
170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/nrser/rash/util.rb', line 170 def self.find_indent(string) string.lines.reject {|line| # don't consider blank lines line =~ /^[\ \t]*$/ }.map {|line| # get the indent off each non-blank line line.match(/^([\ \t]*)/)[1] }.sort.reduce {|first, indent| return '' unless indent[0...(first.length)] == first first } end |
.function_map ⇒ Object
stub out all the singleton methods under Functions
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/nrser/rash/functions.rb', line 103 def self.function_map results = [] # method to recursively stub singleton functions in a module from_module = lambda do |mod| # when the name is split, 'Rash' and 'Functions' take up the # first two elements, so drop those to get the namespace # as an array of strings namespace = mod.name.split('::').drop(2) # get each singleton method (`def self.*` style) mod.singleton_methods.select {|method_name| # throw out those that should not be stubbed stub_function? mod, method_name }.each do |method_name| # the function name is the namespace plus the method name # with segments seperated by `'.'`, ex: # # namespace = ['Nrser', Url'] # method_name = 'parse' # function_name = 'Nrser.Url.parse' # # funcs << (namespace.clone << method_name) function_name = stub_name namespace, method_name call_arg = if namespace.empty? method_name.to_s else "#{ namespace.join('.') }.#{ method_name }" end results << {'name' => function_name, 'sig' => call_arg} end # descend into submodules mod.constants.map {|name| mod.const_get(name) }.select {|const| # exclude any TestCase classes # (const.is_a? Module) && (not const < Minitest::Test) submodule? const }.each {|submod| from_module.call submod } end from_module.call NRSER::Rash::Functions results end |
.function_names ⇒ Object
Stub out all the singleton methods under Functions
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/nrser/rash/functions.rb', line 62 def self.function_names call_args = [] # method to recursivly stub singleton functions in a module from_module = -> mod do # when the name is split, 'Rash' and 'Functions' take up the # first two elements, so drop those to get the namespace # as an array of strings namespace = mod.name.split( '::' ).drop( 3 ) # get each singleton method (`def self.*` style) mod.singleton_methods.select {|method_name| # throw out those that should not be stubbed stub_function? mod, method_name }.each do |method_name| call_arg = if namespace.empty? method_name.to_s else "#{ namespace.join('.') }.#{ method_name }" end call_args << call_arg.downcase end # descend into submodules mod.constants.map {|name| mod.const_get(name) }.select {|const| # exclude any TestCase classes # (const.is_a? Module) && (not const < Minitest::Test) submodule? const }.each {|submod| from_module.call submod } end # from_module from_module.call NRSER::Rash::Functions call_args end |
.load_functions ⇒ Object
==========================================================================
21 22 23 24 |
# File 'lib/nrser/rash/functions.rb', line 21 def self.load_functions path = config('FUNCTIONS').to_pn require path end |
.log_error(headline, error, message = '') ⇒ Object
==========================================================================
16 17 18 19 20 |
# File 'lib/nrser/rash/util.rb', line 16 def self.log_error(headline, error, ='') logger.error "#{ headline }:\n" + "#{ error } (#{ error.class })\n\t" + error.backtrace.join("\n\t") + "\n#{ }" end |
.ref_path(*args) ⇒ Object
157 158 159 160 161 |
# File 'lib/nrser/rash/util.rb', line 157 def self.ref_path(*args) args.map {|part| part.split('::').map {|_| _.split('.')} }.flatten end |
.require_submodules(file_path, deep: false) ⇒ Object
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/nrser/rash/util.rb', line 109 def self.require_submodules file_path, deep: false file_path = file_path.to_pn file_dir = file_path.dirname mod_dir = file_dir / file_path.basename( '.rb' ) glob = if deep mod_dir / '**' / '*.rb' else mod_dir / '*.rb' end logger.trace "Requiring submodules", file_path: file_path, file_dir: file_dir, mod_dir: mod_dir, glob: glob Pathname.glob( glob ).each { |abs_path| rel_path = abs_path.relative_path_from file_dir req_path = rel_path.dirname / rel_path.basename( '.rb' ) logger.trace "Requiring file", abs_path: abs_path, rel_path: rel_path, req_path: req_path begin require abs_path.to_s rescue Exception => error if NRSER::Rash.config( 'DEBUG', false ) raise error else logger.warn "Failed to load file #{ abs_path.to_s.inspect }", error end end } end |
.stdin ⇒ Object
148 149 150 |
# File 'lib/nrser/rash/util.rb', line 148 def self.stdin return $stdin.read unless $stdin.tty? end |
.stub_function?(mod, method_name) ⇒ Boolean
27 28 29 30 31 |
# File 'lib/nrser/rash/functions.rb', line 27 def self.stub_function?(mod, method_name) return false unless mod.method(method_name) return false if method_name.to_s[0] == '_'[0] true end |
.stub_name(namespace, method_name) ⇒ Object
34 35 36 37 38 39 |
# File 'lib/nrser/rash/functions.rb', line 34 def self.stub_name namespace, method_name ( (namespace.map {|s| s.downcase}) + [method_name] ).join(config('STUB_NAMESPACE_SEPERATOR')) end |
.sub(command, *subs) ⇒ Object
substitute stuff into a shell command after escaping with
Shellwords.escape
.
arguments after the first may be multiple values that will be treated like a positional list for substitution, or a single hash that will be treated like a key substitution.
any substitution value that is an Array will be treated like a list of
path segments and joined with File.join
.
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/nrser/rash/util.rb', line 32 def self.sub command, *subs quoted = if subs.length == 1 && subs[0].is_a?(Hash) Hash[ subs[0].map do |key, sub| sub = File.join(*sub) if sub.is_a? Array # shellwords in 1.9.3 can't handle symbols sub = sub.to_s if sub.is_a? Symbol [key, Shellwords.escape(sub)] end ] else subs.map do |sub| sub = File.join(*sub) if sub.is_a? Array # shellwords in 1.9.3 can't handle symbols sub = sub.to_s if sub.is_a? Symbol Shellwords.escape sub end end command % quoted end |
.submodule?(obj) ⇒ Boolean
42 43 44 |
# File 'lib/nrser/rash/functions.rb', line 42 def self.submodule? obj obj.is_a?(Module) && (not obj.is_a?(Class)) end |
.sys(command, *subs) ⇒ Object
execute a system command, using shellwords to escape substituted values.
arguments after the first may be multiple values that will be treated like a positional list for substitution, or a single hash that will be treated like a key substitution.
any substitution value that is an Array will be treated like a list of
path segments and joined with File.join
.
66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/nrser/rash/util.rb', line 66 def self.sys command, *subs cmd = sub command, *subs output = `#{ cmd }` if $?.exitstatus == 0 return output else raise SystemCallError.new( "cmd `#{ cmd }` failed with status #{ $?.exitstatus }", $?.exitstatus ) end end |
.sys!(command, *subs) ⇒ Object
run and ignore exit code
87 88 89 90 |
# File 'lib/nrser/rash/util.rb', line 87 def self.sys! command, *subs cmd = sub command, *subs `#{ cmd }` end |
.sys?(command, *subs) ⇒ Boolean
just runs a command and return true if it exited with status 0
80 81 82 83 84 |
# File 'lib/nrser/rash/util.rb', line 80 def self.sys? command, *subs cmd = sub command, *subs `#{ cmd }` $?.exitstatus == 0 end |
.truthy?(string) ⇒ Boolean
Proxy to NRSER.truthy?
140 141 142 |
# File 'lib/nrser/rash/config.rb', line 140 def self.truthy? string NRSER.truthy? string end |