loom-core 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/lib/loom/facts/all.rb +1 -0
- data/lib/loom/facts/fact_set.rb +23 -45
- data/lib/loom/facts/provider.rb +43 -0
- data/lib/loom/mods/mod_loader.rb +1 -1
- data/lib/loom/mods/module.rb +9 -2
- data/lib/loom/version.rb +1 -1
- data/lib/loomext/corefacts/all.rb +1 -0
- data/lib/loomext/corefacts/facter_provider.rb +1 -0
- data/lib/loomext/corefacts/system_info_provider.rb +104 -0
- data/lib/loomext/coremods/files.rb +9 -2
- metadata +3 -1
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: ba730790e0a31bfadc0f837eb2574f9e21aed1833afd06881987cb76bc6bce2d
         | 
| 4 | 
            +
              data.tar.gz: 6e3936463bef27cf155cdd9873f0268547495d1e0609dce16e5e7089699bd8f5
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: bd86c4cafa91fcf0032f214999be470547f198f930cf1b4ff50e1aba0948aa6d78133205f05c91c81aedc2f4809266398365a5b57d878c6655ad92dc5f349100
         | 
| 7 | 
            +
              data.tar.gz: a9338a3ff37ed372683af92825b196e5a6c0db4ba9fc14b75a345357dcc92474aeb3008058243e88f24d09e752dcde000551fc79f76bfe3a08c42facc0c426c5
         | 
    
        data/.gitignore
    CHANGED
    
    | @@ -1,2 +1,2 @@ | |
| 1 1 | 
             
            pkg
         | 
| 2 | 
            -
            loom-core | 
| 2 | 
            +
            loom-core-*.gem
         | 
    
        data/lib/loom/facts/all.rb
    CHANGED
    
    
    
        data/lib/loom/facts/fact_set.rb
    CHANGED
    
    | @@ -1,45 +1,9 @@ | |
| 1 1 | 
             
            module Loom::Facts
         | 
| 2 2 |  | 
| 3 | 
            -
               | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
                  def disable_for_host(host_spec, klass)
         | 
| 8 | 
            -
                    Loom.log.warn "disabling fact provider => #{klass} on #{host_spec.hostname}"
         | 
| 9 | 
            -
                    @disabled_providers ||= {}
         | 
| 10 | 
            -
                    @disabled_providers[host_spec.hostname] ||= []
         | 
| 11 | 
            -
                    @disabled_providers[host_spec.hostname] << klass
         | 
| 12 | 
            -
                  end
         | 
| 13 | 
            -
             | 
| 14 | 
            -
                  def disabled_for_host?(host_spec, klass)
         | 
| 15 | 
            -
                    @disabled_providers ||= {}
         | 
| 16 | 
            -
                    @disabled_providers[host_spec.hostname] ||= []
         | 
| 17 | 
            -
                    @disabled_providers[host_spec.hostname].include? klass
         | 
| 18 | 
            -
                  end
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                  def register_factory(klass, &block)
         | 
| 21 | 
            -
                    @provider_factories ||= []
         | 
| 22 | 
            -
                    @provider_factories << block
         | 
| 23 | 
            -
                    Loom.log.debug1(self) { "registered fact provider => #{klass}" }
         | 
| 24 | 
            -
                  end
         | 
| 25 | 
            -
             | 
| 26 | 
            -
                  def create_fact_providers(host_spec, shell, loom_config)
         | 
| 27 | 
            -
                    @provider_factories.map do |block|
         | 
| 28 | 
            -
                      block.call(host_spec, shell, loom_config)
         | 
| 29 | 
            -
                    end.flatten
         | 
| 30 | 
            -
                  end
         | 
| 31 | 
            -
                end
         | 
| 32 | 
            -
             | 
| 33 | 
            -
                def disable(host_spec)
         | 
| 34 | 
            -
                  Provider.disable_for_host host_spec, self.class
         | 
| 35 | 
            -
                end
         | 
| 36 | 
            -
             | 
| 37 | 
            -
                # Should return a Hash
         | 
| 38 | 
            -
                def collect_facts
         | 
| 39 | 
            -
                  raise 'not implemented'
         | 
| 40 | 
            -
                end
         | 
| 41 | 
            -
              end
         | 
| 42 | 
            -
             | 
| 3 | 
            +
              ##
         | 
| 4 | 
            +
              # A factset is created by running each registered Loom::Facts::Provider and
         | 
| 5 | 
            +
              # calling Provider+collect_facts+. See ./fact_file_provider.rb and
         | 
| 6 | 
            +
              # lib/loomext/facts/facter_provider.rb for examples of fact providers.
         | 
| 43 7 | 
             
              class FactSet
         | 
| 44 8 |  | 
| 45 9 | 
             
                InvalidFactName = Class.new Loom::LoomError
         | 
| @@ -53,19 +17,33 @@ module Loom::Facts | |
| 53 17 | 
             
                    fact_providers.each do |provider|
         | 
| 54 18 | 
             
                      next if Provider.disabled_for_host?(host_spec, provider.class)
         | 
| 55 19 |  | 
| 56 | 
            -
                       | 
| 20 | 
            +
                      namespace = provider.namespace
         | 
| 21 | 
            +
                      Loom.log.debug { "loading facts from provider => #{provider}[#{namespace}]" }
         | 
| 57 22 |  | 
| 23 | 
            +
                      provider_facts = {}
         | 
| 58 24 | 
             
                      begin
         | 
| 59 25 | 
             
                        provider.collect_facts.each do |k, v|
         | 
| 60 26 | 
             
                          k = k.to_sym
         | 
| 61 | 
            -
                           | 
| 62 | 
            -
             | 
| 27 | 
            +
                          provider_facts[k] = v
         | 
| 28 | 
            +
                        end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                        if namespace
         | 
| 31 | 
            +
                          if fact_map[namespace]
         | 
| 32 | 
            +
                            Loom.log.warn "overriding fact[#{namespace}] with namespace"
         | 
| 33 | 
            +
                          end
         | 
| 34 | 
            +
                          fact_map[namespace] = provider_facts
         | 
| 35 | 
            +
                        else
         | 
| 36 | 
            +
                          provider_facts.each do |k, v|
         | 
| 37 | 
            +
                            if fact_map[k]
         | 
| 38 | 
            +
                              Loom.log.warn "overriding fact[#{k}]"
         | 
| 39 | 
            +
                            end
         | 
| 40 | 
            +
                            Loom.log.debug5(self) { "adding fact => #{k}=#{v.to_s}" }
         | 
| 41 | 
            +
                            fact_map[k] = v
         | 
| 63 42 | 
             
                          end
         | 
| 64 | 
            -
                          Loom.log.debug5(self) { "adding fact => #{k}=#{v.to_s}" }
         | 
| 65 | 
            -
                          fact_map[k] = v
         | 
| 66 43 | 
             
                        end
         | 
| 67 44 | 
             
                      rescue => e
         | 
| 68 45 | 
             
                        Loom.log.error "error executing fact provider #{provider.class} => #{e.message}"
         | 
| 46 | 
            +
                        Loom.log.debug(self) { e.backtrace.join("\n\t") }
         | 
| 69 47 | 
             
                        provider.disable(host_spec)
         | 
| 70 48 | 
             
                      end
         | 
| 71 49 | 
             
                    end
         | 
| @@ -0,0 +1,43 @@ | |
| 1 | 
            +
            module Loom::Facts
         | 
| 2 | 
            +
             | 
| 3 | 
            +
              class Provider
         | 
| 4 | 
            +
                # TODO: add documentation re: use of namespace in fact_set.rb
         | 
| 5 | 
            +
                attr_reader :fact_map, :namespace
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                class << self
         | 
| 8 | 
            +
                  def disable_for_host(host_spec, klass)
         | 
| 9 | 
            +
                    Loom.log.warn "disabling fact provider => #{klass} on #{host_spec.hostname}"
         | 
| 10 | 
            +
                    @disabled_providers ||= {}
         | 
| 11 | 
            +
                    @disabled_providers[host_spec.hostname] ||= []
         | 
| 12 | 
            +
                    @disabled_providers[host_spec.hostname] << klass
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  def disabled_for_host?(host_spec, klass)
         | 
| 16 | 
            +
                    @disabled_providers ||= {}
         | 
| 17 | 
            +
                    @disabled_providers[host_spec.hostname] ||= []
         | 
| 18 | 
            +
                    @disabled_providers[host_spec.hostname].include? klass
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  def register_factory(klass, &block)
         | 
| 22 | 
            +
                    @provider_factories ||= []
         | 
| 23 | 
            +
                    @provider_factories << block
         | 
| 24 | 
            +
                    Loom.log.debug1(self) { "registered fact provider => #{klass}" }
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  def create_fact_providers(host_spec, shell, loom_config)
         | 
| 28 | 
            +
                    @provider_factories.map do |block|
         | 
| 29 | 
            +
                      block.call(host_spec, shell, loom_config)
         | 
| 30 | 
            +
                    end.flatten
         | 
| 31 | 
            +
                  end
         | 
| 32 | 
            +
                end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                def disable(host_spec)
         | 
| 35 | 
            +
                  Provider.disable_for_host host_spec, self.class
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                # Should return a Hash of fact_name => fact_value entries
         | 
| 39 | 
            +
                def collect_facts
         | 
| 40 | 
            +
                  raise 'not implemented'
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
            end
         | 
    
        data/lib/loom/mods/mod_loader.rb
    CHANGED
    
    | @@ -30,7 +30,7 @@ module Loom::Mods | |
| 30 30 | 
             
                    raise ModDefinedError, name if instance_methods.include? name
         | 
| 31 31 |  | 
| 32 32 | 
             
                    define_mod_factory name, klass
         | 
| 33 | 
            -
                    Loom.log. | 
| 33 | 
            +
                    Loom.log.debug(self) { "registered mod => #{klass} as #{name}" }
         | 
| 34 34 |  | 
| 35 35 | 
             
                    opts.each do |k,v|
         | 
| 36 36 | 
             
                      case k
         | 
    
        data/lib/loom/mods/module.rb
    CHANGED
    
    | @@ -49,12 +49,19 @@ module Loom::Mods | |
| 49 49 |  | 
| 50 50 | 
             
                class << self
         | 
| 51 51 |  | 
| 52 | 
            +
                  ##
         | 
| 53 | 
            +
                  # Registers a mod as a new namespace on the loom object.
         | 
| 54 | 
            +
                  # Mods add actions either via a `mod_block` or via registering
         | 
| 55 | 
            +
                  # actions. Only 1 mod_block may be registered per module (which should
         | 
| 56 | 
            +
                  # be fixed), otherwise actions are imported to add module behavior.
         | 
| 57 | 
            +
                  # See loom/lib/loomext/coremods.rb and files.rb for examples.
         | 
| 52 58 | 
             
                  def register_mod(name, **opts, &block)
         | 
| 53 59 | 
             
                    Loom.log.debug2(self) { "registered mod => #{name}" }
         | 
| 54 60 |  | 
| 61 | 
            +
                    # TODO: allow multiple mod_blocks per mod. Should probably stop
         | 
| 62 | 
            +
                    # dynamically defining :mod_block to do so. Current behavior, is the
         | 
| 63 | 
            +
                    # last register_mod w/ a mod_block wins. This is obvsiously shitty.
         | 
| 55 64 | 
             
                    if block_given?
         | 
| 56 | 
            -
                      # TODO: i forget what mod_blocks are. read through, remember, and
         | 
| 57 | 
            -
                      # document them.
         | 
| 58 65 | 
             
                      Loom.log.debug2(self) { "acting as mod_block => #{name}:#{block}" }
         | 
| 59 66 | 
             
                      define_method :mod_block, &block
         | 
| 60 67 | 
             
                    end
         | 
    
        data/lib/loom/version.rb
    CHANGED
    
    
| @@ -0,0 +1,104 @@ | |
| 1 | 
            +
            module LoomExt::CoreFacts
         | 
| 2 | 
            +
              class SystemInfoProvider < Loom::Facts::Provider
         | 
| 3 | 
            +
             | 
| 4 | 
            +
                using Loom::CoreExt # using +underscore+ for key name creation
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                Loom::Facts::Provider.register_factory(self) do |host_spec, shell, loom_config|
         | 
| 7 | 
            +
                  SystemInfoProvider.new host_spec, shell
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def initialize(host_spec, shell)
         | 
| 11 | 
            +
                  @shell = shell
         | 
| 12 | 
            +
                  @namespace = :system_info
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def collect_facts
         | 
| 16 | 
            +
                  # see:
         | 
| 17 | 
            +
                  # https://linux.die.net/man/8/vmstat
         | 
| 18 | 
            +
                  # https://linux.die.net/man/5/proc
         | 
| 19 | 
            +
                  # https://linux.die.net/man/1/df
         | 
| 20 | 
            +
                  # https://www.cyberciti.biz/faq/linux-find-out-raspberry-pi-gpu-and-arm-cpu-temperature-command/
         | 
| 21 | 
            +
                  {
         | 
| 22 | 
            +
                    :vmstat => facts_from_vmstat,
         | 
| 23 | 
            +
                    :loadavg => facts_from_proc_loadavg,
         | 
| 24 | 
            +
                    :df => facts_from_df,
         | 
| 25 | 
            +
                    :os_release => facts_from_etc_os_release,
         | 
| 26 | 
            +
                    :cpu_temp => facts_from_sys_class_thermal_zones
         | 
| 27 | 
            +
                  }
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                private
         | 
| 31 | 
            +
                def facts_from_vmstat
         | 
| 32 | 
            +
                  # NB: requires the host to support `vmstat -s` for table formatted output
         | 
| 33 | 
            +
                  vmstat_table = @shell.capture :vmstat, "-s"
         | 
| 34 | 
            +
                  vmstat_table.each_line.reduce({}) do |memo, line|
         | 
| 35 | 
            +
                    _, str_value, label = line.match(/\s*(\d+)\s(.*)/).to_a
         | 
| 36 | 
            +
                    int_value = str_value.to_i
         | 
| 37 | 
            +
                    value = int_value.to_s == str_value ? int_value : str_value
         | 
| 38 | 
            +
                    memo.merge label.strip.underscore.to_sym => value
         | 
| 39 | 
            +
                  end
         | 
| 40 | 
            +
                end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                def facts_from_proc_loadavg
         | 
| 43 | 
            +
                  loadavg = @shell.capture :cat, "/proc/loadavg"
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                  one, five, fifteen, kernel_scheduling_entities, lastpid = loadavg.split
         | 
| 46 | 
            +
                  current_entities, total_entities = kernel_scheduling_entities.split("/")
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  {
         | 
| 49 | 
            +
                    :"1_min_avg" => one.to_f,
         | 
| 50 | 
            +
                    :"5_min_avg" => five.to_f,
         | 
| 51 | 
            +
                    :"15_min_avg" => fifteen.to_f,
         | 
| 52 | 
            +
                    :kernel_scheduling_entities => kernel_scheduling_entities,
         | 
| 53 | 
            +
                    :current_scheduling_entities => current_entities.to_i,
         | 
| 54 | 
            +
                    :total_scheduling_entities => total_entities.to_i,
         | 
| 55 | 
            +
                    :lastpid => lastpid
         | 
| 56 | 
            +
                  }
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                def facts_from_df
         | 
| 60 | 
            +
                  df = @shell.capture :df
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  headers = df.lines.first.split.map do |h|
         | 
| 63 | 
            +
                    h = "UsePercent" if h == "Use%"
         | 
| 64 | 
            +
                    h.strip.underscore.to_sym
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  info_lines = df.lines.drop(1).map { |l| l.strip }
         | 
| 68 | 
            +
                  info_lines.map do |info|
         | 
| 69 | 
            +
                    info_parts = info.split.map { |i| i.strip }
         | 
| 70 | 
            +
                    header_info_pairs = headers.zip(info_parts)
         | 
| 71 | 
            +
                    header_info_pairs.reduce({}) do |memo, pair|
         | 
| 72 | 
            +
                      str_value = pair[1]
         | 
| 73 | 
            +
                      int_value = str_value.to_i
         | 
| 74 | 
            +
                      value = int_value.to_s == str_value ? int_value : str_value
         | 
| 75 | 
            +
                      memo.merge pair[0] => value
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
                end
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                def facts_from_etc_os_release
         | 
| 81 | 
            +
                  os_release = @shell.capture :cat, "/etc/os-release"
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                  facts = {}
         | 
| 84 | 
            +
                  os_release.each_line do |l|
         | 
| 85 | 
            +
                    l = l.strip
         | 
| 86 | 
            +
                    key, value = l.split("=")
         | 
| 87 | 
            +
                    unquoted_value = value.gsub(/^"(.+)"$/) { |m| $1 }
         | 
| 88 | 
            +
                    facts[key.to_sym] = unquoted_value
         | 
| 89 | 
            +
                  end
         | 
| 90 | 
            +
                  facts
         | 
| 91 | 
            +
                end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                def facts_from_sys_class_thermal_zones
         | 
| 94 | 
            +
                  thermal_zones = @shell.capture :ls, "/sys/class/thermal"
         | 
| 95 | 
            +
                  thermal_zones
         | 
| 96 | 
            +
                    .split
         | 
| 97 | 
            +
                    .delete_if { |f| !f.strip.match(/^thermal_zone/) }
         | 
| 98 | 
            +
                    .map do |tz_dir|
         | 
| 99 | 
            +
                    temp = @shell.capture :cat, "/sys/class/thermal/#{tz_dir}/temp"
         | 
| 100 | 
            +
                    temp.strip.to_f / 1000
         | 
| 101 | 
            +
                  end
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
              end
         | 
| 104 | 
            +
            end
         | 
| @@ -2,6 +2,7 @@ module LoomExt::CoreMods | |
| 2 2 | 
             
              class Files < Loom::Mods::Module
         | 
| 3 3 |  | 
| 4 4 | 
             
                register_mod :files
         | 
| 5 | 
            +
                required_commands :cat, :mv, :tee, :chown, :rm, :touch, :sudo, :mkdir, :echo
         | 
| 5 6 |  | 
| 6 7 | 
             
                # TODO: document loom file statements like:
         | 
| 7 8 | 
             
                #     `loom.files("some", "different/paths").cat`
         | 
| @@ -88,6 +89,8 @@ module LoomExt::CoreMods | |
| 88 89 | 
             
                    each_path :action => :mkdir, :flags => flags
         | 
| 89 90 | 
             
                  end
         | 
| 90 91 |  | 
| 92 | 
            +
                  # TODO this sudo line is just another instance of needing the harness to
         | 
| 93 | 
            +
                  # work properly (in this case to provides a hack for sudo_append)
         | 
| 91 94 | 
             
                  def ensure_line(line, sudo: false)
         | 
| 92 95 | 
             
                    if loom.is_sudo?
         | 
| 93 96 | 
             
                      Loom.log.warn "do not use files.ensure_line in sudo  due to poor command escaping" +
         | 
| @@ -97,17 +100,21 @@ module LoomExt::CoreMods | |
| 97 100 | 
             
                    each_path do |p|
         | 
| 98 101 | 
             
                      file = shell.capture :cat, p
         | 
| 99 102 |  | 
| 100 | 
            -
                       | 
| 103 | 
            +
                      matches = file.match(/^#{line}$/)
         | 
| 104 | 
            +
                      unless matches
         | 
| 101 105 | 
             
                        if sudo
         | 
| 102 106 | 
             
                          sudo_append(line)
         | 
| 103 107 | 
             
                        else
         | 
| 104 108 | 
             
                          append(line)
         | 
| 105 109 | 
             
                        end
         | 
| 110 | 
            +
                      else
         | 
| 111 | 
            +
                        Loom.log.debug(self) { "ensure_line match found: #{matches[0]}"}
         | 
| 106 112 | 
             
                      end
         | 
| 107 113 | 
             
                    end
         | 
| 108 114 | 
             
                  end
         | 
| 109 115 |  | 
| 110 | 
            -
                  # this is a hack to accomodate append being | 
| 116 | 
            +
                  # TODO: Get the harness working, this is a hack to accomodate append being
         | 
| 117 | 
            +
                  # f'd inside sudo blocks
         | 
| 111 118 | 
             
                  def sudo_append(text="")
         | 
| 112 119 | 
             
                    if text.index "\n"
         | 
| 113 120 | 
             
                      Loom.log.warn "append lines individually until cmd escaping is fixed.... "
         | 
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: loom-core
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0. | 
| 4 | 
            +
              version: 0.0.3
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Erick Johnson
         | 
| @@ -214,6 +214,7 @@ files: | |
| 214 214 | 
             
            - lib/loom/facts/all.rb
         | 
| 215 215 | 
             
            - lib/loom/facts/fact_file_provider.rb
         | 
| 216 216 | 
             
            - lib/loom/facts/fact_set.rb
         | 
| 217 | 
            +
            - lib/loom/facts/provider.rb
         | 
| 217 218 | 
             
            - lib/loom/host_spec.rb
         | 
| 218 219 | 
             
            - lib/loom/inventory.rb
         | 
| 219 220 | 
             
            - lib/loom/logger.rb
         | 
| @@ -249,6 +250,7 @@ files: | |
| 249 250 | 
             
            - lib/loomext/corefacts.rb
         | 
| 250 251 | 
             
            - lib/loomext/corefacts/all.rb
         | 
| 251 252 | 
             
            - lib/loomext/corefacts/facter_provider.rb
         | 
| 253 | 
            +
            - lib/loomext/corefacts/system_info_provider.rb
         | 
| 252 254 | 
             
            - lib/loomext/coremods.rb
         | 
| 253 255 | 
             
            - lib/loomext/coremods/all.rb
         | 
| 254 256 | 
             
            - lib/loomext/coremods/exec.rb
         |