eac_ruby_utils 0.6.0 → 0.7.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4129bee0e89112a9f51bbc007eaafe34ef061917568a27f25b7c92cb91a80e9
4
- data.tar.gz: f57abb8130a5de7c77087912fe417cc93cc4ff83ec3db6fa3bfeba9f889c2791
3
+ metadata.gz: 11c321836d0bd034a1ebd2c4835cbd97bcb423edf7a96c362a18259c2eeb698f
4
+ data.tar.gz: 4601661dd0057f7e2bd1dbe7b5adae2463f8419370848dd33bf48b2f8ab55860
5
5
  SHA512:
6
- metadata.gz: 7d867c393e6c63584767a81deabbf8f3752d70cc03970383f20e9f1f3afc4aa26fad71d91c535670bb5a73d9cf7b0c392a176eb2c4201640c47217499150d151
7
- data.tar.gz: fc385e0f681284d55edb35a33a2fa4b474b3112de67504102219106648ae8bd46e1acc653f9a834ab1eda492f4bdf72355331270256592fac2f32e95a035b4ed
6
+ metadata.gz: 0eeb4ee5d8f011a9a4607dbb94932092969465a1b530e8712df7b955b553d0d465dfbeb8427a5820e6262609be3f30b2f38f8850579839f4d0b7c3126e9f2979
7
+ data.tar.gz: 79e0b183401615aeb3f16d92c4b3505eca0d6569f8fa5b6eeabbe6d7e5853cab7cdad24eac03401e6083cf8d8c2cb7693df6c196e42f554f158de8facfb2cbcf
@@ -1,20 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'colorize'
4
- require 'docopt'
5
- require 'open3'
6
- require 'pp'
7
- require 'net/ssh'
8
-
9
3
  module EacRubyUtils
10
- require 'eac_ruby_utils/options_consumer'
11
- require 'eac_ruby_utils/console/docopt_runner'
12
- require 'eac_ruby_utils/console/speaker'
4
+ require 'eac_ruby_utils/arguments_consumer'
5
+ require 'eac_ruby_utils/console'
6
+ require 'eac_ruby_utils/contextualizable'
13
7
  require 'eac_ruby_utils/envs'
14
- require 'eac_ruby_utils/envs/base_env'
15
- require 'eac_ruby_utils/envs/command'
16
- require 'eac_ruby_utils/envs/local_env'
17
- require 'eac_ruby_utils/envs/process'
18
- require 'eac_ruby_utils/envs/ssh_env'
8
+ require 'eac_ruby_utils/options_consumer'
9
+ require 'eac_ruby_utils/patches'
10
+ require 'eac_ruby_utils/simple_cache'
19
11
  require 'eac_ruby_utils/yaml'
20
12
  end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/hash_with_indifferent_access'
4
+ require 'ostruct'
5
+
6
+ module EacRubyUtils
7
+ class ArgumentsConsumer
8
+ class << self
9
+ def parse(args, positional = [], options = {})
10
+ new(args, positional, options).data
11
+ end
12
+ end
13
+
14
+ attr_reader :positional, :default_options
15
+
16
+ def initialize(positional, default_options)
17
+ @positional = positional.dup.freeze
18
+ @default_options = default_options.dup.freeze
19
+ end
20
+
21
+ def parse(args)
22
+ Parser.new(self, args).data
23
+ end
24
+
25
+ def initialize2(args, positional, _default_options)
26
+ @positional = positional.dup
27
+ @positional.freeze
28
+ @args = args
29
+
30
+ @options_found = false
31
+ end
32
+
33
+ class Parser
34
+ attr_reader :data, :arguments_consumer
35
+
36
+ def initialize(arguments_consumer, args)
37
+ @arguments_consumer = arguments_consumer
38
+ @data = ::ActiveSupport::HashWithIndifferentAccess.new
39
+ @options_found = false
40
+ arguments_consumer.positional.each { |key| data[key] = nil }
41
+ data.merge!(arguments_consumer.default_options)
42
+ args.each_with_index { |value, index| add_arg(value, index) }
43
+ data.freeze
44
+ end
45
+
46
+ private
47
+
48
+ def add_arg(value, index)
49
+ arg = ::OpenStruct.new(value: value, index: index)
50
+ if arg.value.is_a?(::Hash)
51
+ add_hash_arg(arg)
52
+ else
53
+ add_positional_arg(arg)
54
+ end
55
+ end
56
+
57
+ def add_hash_arg(arg)
58
+ check_no_more_arguments(arg)
59
+ data.merge!(arg.value)
60
+ @options_found = true
61
+ end
62
+
63
+ def add_positional_arg(arg)
64
+ check_no_more_arguments(arg)
65
+ invalid_argument arg, 'No more valid positional' if
66
+ arg.index >= arguments_consumer.positional.count
67
+ data[arguments_consumer.positional[arg.index]] = arg.value
68
+ end
69
+
70
+ def check_no_more_arguments(arg)
71
+ return unless @options_found
72
+ invalid_argument arg, 'Hash already found - no more positional allowed'
73
+ end
74
+
75
+ def invalid_argument(arg, message)
76
+ raise InvalidArgumentError, self, arg, message
77
+ end
78
+ end
79
+
80
+ class InvalidArgumentError < StandardError
81
+ def initialize(args_consumer, arg, message)
82
+ @args_consumer = args_consumer
83
+ @arg = arg
84
+ super "#{message} (Arg: #{arg}, Args Consumer: #{args_consumer})"
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/console/docopt_runner'
4
+ require 'eac_ruby_utils/console/speaker'
@@ -1,25 +1,38 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/core_ext/hash/indifferent_access'
4
+ require 'active_support/core_ext/hash/slice'
5
+ require 'docopt'
6
+ require 'eac_ruby_utils/contextualizable'
7
+ require 'eac_ruby_utils/patches/hash/sym_keys_hash'
8
+ Dir["#{__dir__}/#{::File.basename(__FILE__, '.*')}/_*.rb"].each do |partial|
9
+ require partial
10
+ end
11
+
3
12
  module EacRubyUtils
4
13
  module Console
5
14
  class DocoptRunner
6
- attr_reader :options
15
+ include ::EacRubyUtils::Contextualizable
16
+
17
+ attr_reader :settings
7
18
 
8
- def initialize
9
- @options = Docopt.docopt(doc)
10
- run
11
- rescue Docopt::Exit => e
12
- puts e.message
19
+ def initialize(settings = {})
20
+ @settings = settings.with_indifferent_access.freeze
21
+ check_subcommands
13
22
  end
14
23
 
15
- private
24
+ def options
25
+ @options ||= Docopt.docopt(target_doc, docopt_options)
26
+ end
16
27
 
17
- def doc
18
- self.class.const_get('DOC').gsub('__PROGRAM__', program)
28
+ def parent
29
+ settings[:parent]
19
30
  end
20
31
 
21
- def program
22
- ENV['MYSELF_PROGRAM'] || $PROGRAM_NAME
32
+ protected
33
+
34
+ def docopt_options
35
+ settings.slice(:version, :argv, :help, :options_first).to_sym_keys_hash
23
36
  end
24
37
  end
25
38
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EacRubyUtils
4
+ module Console
5
+ class DocoptRunner
6
+ DOCOPT_ERROR_EXIT_CODE = 0x22220000
7
+
8
+ class << self
9
+ def run(options = {})
10
+ new(options).send(:run)
11
+ rescue Docopt::Exit => e
12
+ STDERR.write(e.message + "\n")
13
+ ::Kernel.exit(DOCOPT_ERROR_EXIT_CODE)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EacRubyUtils
4
+ module Console
5
+ class DocoptRunner
6
+ PROGRAM_MACRO = '__PROGRAM__'
7
+
8
+ def source_doc
9
+ setting_value(:doc)
10
+ end
11
+
12
+ def target_doc
13
+ source_doc.gsub(PROGRAM_MACRO, program_name).strip + "\n"
14
+ end
15
+
16
+ def program_name
17
+ setting_value(:program_name, false) || ENV['PROGRAM_NAME'] || $PROGRAM_NAME
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EacRubyUtils
4
+ module Console
5
+ class DocoptRunner
6
+ attr_reader :settings
7
+
8
+ private
9
+
10
+ def setting_value(key, required = true)
11
+ %i[setting_from_argument setting_from_constant].each do |method|
12
+ value = send(method, key)
13
+ return value if value
14
+ end
15
+ return nil unless required
16
+ raise "Setting \"#{key}\" not found. Declare #{setting_constant(key, true)} constant, " \
17
+ "pass #{key.to_sym} option to #{self.class.name}.new() method."
18
+ end
19
+
20
+ def setting_from_argument(key)
21
+ settings[key]
22
+ end
23
+
24
+ def setting_from_constant(key)
25
+ constant_name = setting_constant(key)
26
+ begin
27
+ self.class.const_get(constant_name)
28
+ rescue NameError
29
+ nil
30
+ end
31
+ end
32
+
33
+ def setting_constant(key, _fullname = false)
34
+ name = key.to_s.underscore.upcase
35
+ name = "#{self.class.name}::#{name}"
36
+ name
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/string/inflections'
4
+ require 'shellwords'
5
+
6
+ module EacRubyUtils
7
+ module Console
8
+ class DocoptRunner
9
+ SUBCOMMAND_ARG = '<subcommand>'
10
+ SUBCOMMAND_ARGS_ARG = '<subcommand-args>'
11
+ SUBCOMMANDS_MACRO = '__SUBCOMMANDS__'
12
+
13
+ def subcommands?
14
+ source_doc.include?(SUBCOMMANDS_MACRO)
15
+ end
16
+
17
+ def check_subcommands
18
+ return unless subcommands?
19
+ singleton_class.include(SubcommandsSupport)
20
+ check_subcommands_arg
21
+ return if singleton_class.method_defined?(:run)
22
+ singleton_class.send(:alias_method, :run, :run_with_subcommand)
23
+ end
24
+
25
+ module SubcommandsSupport
26
+ def check_subcommands_arg
27
+ if subcommand_arg_as_list?
28
+ singleton_class.include(SubcommandsSupport::SubcommandArgAsList)
29
+ else
30
+ singleton_class.include(SubcommandsSupport::SubcommandArgAsArg)
31
+ end
32
+ end
33
+
34
+ def run_with_subcommand
35
+ if options.fetch(SUBCOMMAND_ARG)
36
+ check_valid_subcommand
37
+ subcommand.run
38
+ else
39
+ run_without_subcommand
40
+ end
41
+ end
42
+
43
+ def subcommand
44
+ @subcommand ||= subcommand_class_name(subcommand_name).constantize.new(
45
+ argv: subcommand_args,
46
+ program_name: subcommand_program,
47
+ parent: self
48
+ )
49
+ end
50
+
51
+ def target_doc
52
+ super.gsub(SUBCOMMANDS_MACRO,
53
+ "#{target_doc_subcommand_arg} [#{SUBCOMMAND_ARGS_ARG}...]") +
54
+ "\n" + subcommands_target_doc
55
+ end
56
+
57
+ def docopt_options
58
+ super.merge(options_first: true)
59
+ end
60
+
61
+ def subcommand_class_name(subcommand)
62
+ "#{self.class.name}::#{subcommand.underscore.camelize}"
63
+ end
64
+
65
+ def subcommand_arg_as_list?
66
+ setting_value(:subcommand_arg_as_list, false) || false
67
+ end
68
+
69
+ def subcommand_args
70
+ options.fetch(SUBCOMMAND_ARGS_ARG)
71
+ end
72
+
73
+ def subcommand_program
74
+ subcommand_name
75
+ end
76
+
77
+ def subcommand_name
78
+ options.fetch(SUBCOMMAND_ARG)
79
+ end
80
+
81
+ def available_subcommands
82
+ (setting_value(:subcommands, false) || auto_available_subcommands).sort
83
+ end
84
+
85
+ def auto_available_subcommands
86
+ self.class.constants
87
+ .map { |name| self.class.const_get(name) }
88
+ .select { |c| c.instance_of? Class }
89
+ .select { |c| c < ::EacRubyUtils::Console::DocoptRunner }
90
+ .map { |c| c.name.demodulize.underscore.dasherize }
91
+ end
92
+
93
+ def run_without_subcommand
94
+ "Method #{__method__} should be overrided in #{self.class.name}"
95
+ end
96
+
97
+ protected
98
+
99
+ def check_valid_subcommand
100
+ return if available_subcommands.include?(subcommand_name)
101
+ raise ::Docopt::Exit, "\"#{subcommand_name}\" is not a valid subcommand" \
102
+ " (Valid: #{available_subcommands.join(', ')})"
103
+ end
104
+
105
+ module SubcommandArgAsArg
106
+ def target_doc_subcommand_arg
107
+ SUBCOMMAND_ARG
108
+ end
109
+
110
+ def subcommand_name
111
+ options.fetch(SUBCOMMAND_ARG)
112
+ end
113
+
114
+ def subcommands_target_doc
115
+ available_subcommands.inject("Subcommands:\n") do |a, e|
116
+ a + " #{e}\n"
117
+ end
118
+ end
119
+ end
120
+
121
+ module SubcommandArgAsList
122
+ def target_doc_subcommand_arg
123
+ '(' + available_subcommands.join('|') + ')'
124
+ end
125
+
126
+ def subcommand_name
127
+ available_subcommands.each do |subcommand|
128
+ return subcommand if options.fetch(subcommand)
129
+ end
130
+ nil
131
+ end
132
+
133
+ def subcommands_target_doc
134
+ "\n"
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'colorize'
4
+
3
5
  module EacRubyUtils
4
6
  module Console
5
7
  # https://github.com/fazibear/colorize
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EacRubyUtils
4
+ # Provides the method context which search and call a method in self and ancestor objects.
5
+ module Contextualizable
6
+ def context(method)
7
+ current = self
8
+ while current
9
+ return current.send(method) if current.respond_to?(method)
10
+ current = current.respond_to?(:parent) ? current.parent : nil
11
+ end
12
+ raise "Context method \"#{method}\" not found for #{self.class}"
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'eac_ruby_utils/envs/base_env'
4
+ require 'eac_ruby_utils/envs/command'
5
+ require 'eac_ruby_utils/envs/local_env'
6
+ require 'eac_ruby_utils/envs/process'
7
+ require 'eac_ruby_utils/envs/ssh_env'
8
+
3
9
  module EacRubyUtils
4
10
  module Envs
5
11
  class << self
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'eac_ruby_utils/envs/command'
4
+
3
5
  module EacRubyUtils
4
6
  module Envs
5
7
  class BaseEnv
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'command/extra_options'
3
+ require 'active_support/core_ext/hash/indifferent_access'
4
+ require 'eac_ruby_utils/console/speaker'
5
+ require 'eac_ruby_utils/envs/command/extra_options'
6
+ require 'eac_ruby_utils/envs/process'
7
+ require 'pp'
8
+ require 'shellwords'
4
9
 
5
10
  module EacRubyUtils
6
11
  module Envs
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/core_ext/hash/indifferent_access'
4
+ require 'shellwords'
5
+
3
6
  module EacRubyUtils
4
7
  module Envs
5
8
  class Command
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'eac_ruby_utils/envs/base_env'
4
+
3
5
  module EacRubyUtils
4
6
  module Envs
5
7
  class LocalEnv < ::EacRubyUtils::Envs::BaseEnv
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'open3'
4
+
3
5
  module EacRubyUtils
4
6
  module Envs
5
7
  class Process
@@ -1,5 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'addressable'
4
+ require 'eac_ruby_utils/envs/base_env'
5
+ require 'net/ssh'
6
+ require 'shellwords'
7
+
3
8
  module EacRubyUtils
4
9
  module Envs
5
10
  class SshEnv < ::EacRubyUtils::Envs::BaseEnv
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/core_ext/hash/indifferent_access'
4
+
3
5
  module EacRubyUtils
4
6
  class OptionsConsumer
5
7
  def initialize(data)
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir["#{File.dirname(__FILE__)}/#{::File.basename(__FILE__, '.*')}/*.rb"].each do |path|
4
+ require path
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir["#{File.dirname(__FILE__)}/#{::File.basename(__FILE__, '.*')}/*.rb"].each do |path|
4
+ require path
5
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'eac_ruby_utils/options_consumer'
4
+
5
+ class Hash
6
+ # Returns an <tt>EacRubyUtils::OptionsConsumer</tt> out of its receiver.
7
+ def to_options_consumer
8
+ ::EacRubyUtils::OptionsConsumer.new(self)
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Hash
4
+ def to_sym_keys_hash
5
+ each_with_object({}) do |(key, value), memo|
6
+ symbol_key = key.respond_to?(:to_sym) ? key.to_sym : key.to_s.to_sym
7
+ memo[symbol_key] = value
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EacRubyUtils
4
+ module SimpleCache
5
+ UNCACHED_METHOD_PATTERN = /\A(\s+)_uncached\z/
6
+
7
+ def method_missing(method, *args, &block)
8
+ uncached_method = "#{method}_uncached"
9
+ if respond_to?(uncached_method, true)
10
+ call_method_with_cache(uncached_method, args, &block)
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ def respond_to_missing?(method, include_all = false)
17
+ if method.to_s.end_with?('_uncached')
18
+ super
19
+ else
20
+ respond_to?("#{method}_uncached", true) || super
21
+ end
22
+ end
23
+
24
+ def reset_cache
25
+ @cache_keys = nil
26
+ end
27
+
28
+ private
29
+
30
+ def call_method_with_cache(method, args, &block)
31
+ raise 'Não é possível realizar o cache de métodos com bloco' if block
32
+ key = ([method] + args).join('@@@')
33
+ cache_keys[key] = send(method, *args) unless cache_keys.key?(key)
34
+ cache_keys[key]
35
+ end
36
+
37
+ def cache_keys
38
+ @cache_keys ||= {}
39
+ end
40
+ end
41
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EacRubyUtils
4
- VERSION = '0.6.0'
4
+ VERSION = '0.7.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eac_ruby_utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Esquilo Azul Company
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-04 00:00:00.000000000 Z
11
+ date: 2019-07-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '4.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: addressable
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.6'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: colorize
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -66,6 +80,20 @@ dependencies:
66
80
  - - "~>"
67
81
  - !ruby/object:Gem::Version
68
82
  version: '4.2'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '3.8'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '3.8'
69
97
  description:
70
98
  email:
71
99
  executables: []
@@ -74,8 +102,15 @@ extra_rdoc_files: []
74
102
  files:
75
103
  - README.rdoc
76
104
  - lib/eac_ruby_utils.rb
105
+ - lib/eac_ruby_utils/arguments_consumer.rb
106
+ - lib/eac_ruby_utils/console.rb
77
107
  - lib/eac_ruby_utils/console/docopt_runner.rb
108
+ - lib/eac_ruby_utils/console/docopt_runner/_class_methods.rb
109
+ - lib/eac_ruby_utils/console/docopt_runner/_doc.rb
110
+ - lib/eac_ruby_utils/console/docopt_runner/_settings.rb
111
+ - lib/eac_ruby_utils/console/docopt_runner/_subcommands.rb
78
112
  - lib/eac_ruby_utils/console/speaker.rb
113
+ - lib/eac_ruby_utils/contextualizable.rb
79
114
  - lib/eac_ruby_utils/envs.rb
80
115
  - lib/eac_ruby_utils/envs/base_env.rb
81
116
  - lib/eac_ruby_utils/envs/command.rb
@@ -84,6 +119,11 @@ files:
84
119
  - lib/eac_ruby_utils/envs/process.rb
85
120
  - lib/eac_ruby_utils/envs/ssh_env.rb
86
121
  - lib/eac_ruby_utils/options_consumer.rb
122
+ - lib/eac_ruby_utils/patches.rb
123
+ - lib/eac_ruby_utils/patches/hash.rb
124
+ - lib/eac_ruby_utils/patches/hash/options_consumer.rb
125
+ - lib/eac_ruby_utils/patches/hash/sym_keys_hash.rb
126
+ - lib/eac_ruby_utils/simple_cache.rb
87
127
  - lib/eac_ruby_utils/version.rb
88
128
  - lib/eac_ruby_utils/yaml.rb
89
129
  homepage: