eac_ruby_utils 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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: