runger_config 2.6.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.
Files changed (54) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +562 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +1121 -0
  5. data/lib/anyway/auto_cast.rb +53 -0
  6. data/lib/anyway/config.rb +473 -0
  7. data/lib/anyway/dynamic_config.rb +31 -0
  8. data/lib/anyway/ejson_parser.rb +40 -0
  9. data/lib/anyway/env.rb +73 -0
  10. data/lib/anyway/ext/deep_dup.rb +48 -0
  11. data/lib/anyway/ext/deep_freeze.rb +44 -0
  12. data/lib/anyway/ext/flatten_names.rb +37 -0
  13. data/lib/anyway/ext/hash.rb +40 -0
  14. data/lib/anyway/ext/string_constantize.rb +24 -0
  15. data/lib/anyway/loaders/base.rb +21 -0
  16. data/lib/anyway/loaders/doppler.rb +63 -0
  17. data/lib/anyway/loaders/ejson.rb +89 -0
  18. data/lib/anyway/loaders/env.rb +18 -0
  19. data/lib/anyway/loaders/yaml.rb +84 -0
  20. data/lib/anyway/loaders.rb +79 -0
  21. data/lib/anyway/option_parser_builder.rb +29 -0
  22. data/lib/anyway/optparse_config.rb +92 -0
  23. data/lib/anyway/rails/autoload.rb +42 -0
  24. data/lib/anyway/rails/config.rb +23 -0
  25. data/lib/anyway/rails/loaders/credentials.rb +64 -0
  26. data/lib/anyway/rails/loaders/secrets.rb +37 -0
  27. data/lib/anyway/rails/loaders/yaml.rb +9 -0
  28. data/lib/anyway/rails/loaders.rb +5 -0
  29. data/lib/anyway/rails/settings.rb +83 -0
  30. data/lib/anyway/rails.rb +24 -0
  31. data/lib/anyway/railtie.rb +28 -0
  32. data/lib/anyway/rbs.rb +92 -0
  33. data/lib/anyway/settings.rb +111 -0
  34. data/lib/anyway/testing/helpers.rb +36 -0
  35. data/lib/anyway/testing.rb +13 -0
  36. data/lib/anyway/tracing.rb +188 -0
  37. data/lib/anyway/type_casting.rb +144 -0
  38. data/lib/anyway/utils/deep_merge.rb +21 -0
  39. data/lib/anyway/utils/which.rb +18 -0
  40. data/lib/anyway/version.rb +5 -0
  41. data/lib/anyway.rb +3 -0
  42. data/lib/anyway_config.rb +54 -0
  43. data/lib/generators/anyway/app_config/USAGE +9 -0
  44. data/lib/generators/anyway/app_config/app_config_generator.rb +17 -0
  45. data/lib/generators/anyway/config/USAGE +13 -0
  46. data/lib/generators/anyway/config/config_generator.rb +51 -0
  47. data/lib/generators/anyway/config/templates/config.rb.tt +12 -0
  48. data/lib/generators/anyway/config/templates/config.yml.tt +13 -0
  49. data/lib/generators/anyway/install/USAGE +4 -0
  50. data/lib/generators/anyway/install/install_generator.rb +47 -0
  51. data/lib/generators/anyway/install/templates/application_config.rb.tt +17 -0
  52. data/sig/anyway_config.rbs +149 -0
  53. data/sig/manifest.yml +6 -0
  54. metadata +202 -0
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway
4
+ # Contains a mapping between type IDs/names and deserializers
5
+ class TypeRegistry
6
+ class << self
7
+ def default
8
+ @default ||= TypeRegistry.new
9
+ end
10
+ end
11
+
12
+ def initialize
13
+ @registry = {}
14
+ end
15
+
16
+ def accept(name_or_object, &block)
17
+ if !block && !name_or_object.respond_to?(:call)
18
+ raise ArgumentError, "Please, provide a type casting block or an object implementing #call(val) method"
19
+ end
20
+
21
+ registry[name_or_object] = block || name_or_object
22
+ end
23
+
24
+ def deserialize(raw, type_id, array: false)
25
+ return if raw.nil?
26
+
27
+ caster =
28
+ if type_id.is_a?(Symbol) || type_id.nil?
29
+ registry.fetch(type_id) { raise ArgumentError, "Unknown type: #{type_id}" }
30
+ else
31
+ raise ArgumentError, "Type must implement #call(val): #{type_id}" unless type_id.respond_to?(:call)
32
+ type_id
33
+ end
34
+
35
+ if array
36
+ raw_arr = raw.is_a?(String) ? raw.split(/\s*,\s*/) : Array(raw)
37
+ raw_arr.map { caster.call(_1) }
38
+ else
39
+ caster.call(raw)
40
+ end
41
+ end
42
+
43
+ def dup
44
+ new_obj = self.class.allocate
45
+ new_obj.instance_variable_set(:@registry, registry.dup)
46
+ new_obj
47
+ end
48
+
49
+ private
50
+
51
+ attr_reader :registry
52
+ end
53
+
54
+ TypeRegistry.default.tap do |obj|
55
+ obj.accept(nil, &:itself)
56
+ obj.accept(:string, &:to_s)
57
+ obj.accept(:integer, &:to_i)
58
+ obj.accept(:float, &:to_f)
59
+
60
+ obj.accept(:date) do
61
+ require "date" unless defined?(::Date)
62
+
63
+ next _1 if _1.is_a?(::Date)
64
+
65
+ next _1.to_date if _1.respond_to?(:to_date)
66
+
67
+ ::Date.parse(_1)
68
+ end
69
+
70
+ obj.accept(:datetime) do
71
+ require "date" unless defined?(::Date)
72
+
73
+ next _1 if _1.is_a?(::DateTime)
74
+
75
+ next _1.to_datetime if _1.respond_to?(:to_datetime)
76
+
77
+ ::DateTime.parse(_1)
78
+ end
79
+
80
+ obj.accept(:uri) do
81
+ require "uri" unless defined?(::URI)
82
+
83
+ next _1 if _1.is_a?(::URI)
84
+
85
+ ::URI.parse(_1)
86
+ end
87
+
88
+ obj.accept(:boolean) do
89
+ _1.to_s.match?(/\A(true|t|yes|y|1)\z/i)
90
+ end
91
+ end
92
+
93
+ unless "".respond_to?(:safe_constantize)
94
+ require "anyway/ext/string_constantize"
95
+ using Anyway::Ext::StringConstantize
96
+ end
97
+
98
+ # TypeCaster is an object responsible for type-casting.
99
+ # It uses a provided types registry and mapping, and also
100
+ # accepts a fallback typecaster.
101
+ class TypeCaster
102
+ using Ext::DeepDup
103
+ using Ext::Hash
104
+
105
+ def initialize(mapping, registry: TypeRegistry.default, fallback: ::Anyway::AutoCast)
106
+ @mapping = mapping.deep_dup
107
+ @registry = registry
108
+ @fallback = fallback
109
+ end
110
+
111
+ def coerce(key, val, config: mapping)
112
+ caster_config = config[key.to_sym]
113
+
114
+ return fallback.coerce(key, val) unless caster_config
115
+
116
+ case caster_config
117
+ in Hash[array:, type:, **nil]
118
+ registry.deserialize(val, type, array: array)
119
+ in Hash[config: subconfig]
120
+ subconfig = subconfig.safe_constantize if subconfig.is_a?(::String)
121
+ raise ArgumentError, "Config is not found: #{subconfig}" unless subconfig
122
+
123
+ subconfig.new(val)
124
+ in Hash
125
+ return val unless val.is_a?(Hash)
126
+
127
+ caster_config.each do |k, v|
128
+ ks = k.to_s
129
+ next unless val.key?(ks)
130
+
131
+ val[ks] = coerce(k, val[ks], config: caster_config)
132
+ end
133
+
134
+ val
135
+ else
136
+ registry.deserialize(val, caster_config)
137
+ end
138
+ end
139
+
140
+ private
141
+
142
+ attr_reader :mapping, :registry, :fallback
143
+ end
144
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway
4
+ using Anyway::Ext::DeepDup
5
+
6
+ module Utils
7
+ def self.deep_merge!(source, other)
8
+ other.each do |key, other_value|
9
+ this_value = source[key]
10
+
11
+ if this_value.is_a?(::Hash) && other_value.is_a?(::Hash)
12
+ deep_merge!(this_value, other_value)
13
+ else
14
+ source[key] = other_value.deep_dup
15
+ end
16
+ end
17
+
18
+ source
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway
4
+ module Utils
5
+ # Cross-platform solution
6
+ # taken from https://stackoverflow.com/a/5471032
7
+ def self.which(cmd)
8
+ exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
9
+ ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
10
+ exts.each do |ext|
11
+ exe = File.join(path, "#{cmd}#{ext}")
12
+ return exe if File.executable?(exe) && !File.directory?(exe)
13
+ end
14
+ end
15
+ nil
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anyway # :nodoc:
4
+ VERSION = "2.6.0"
5
+ end
data/lib/anyway.rb ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "anyway_config"
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ruby-next"
4
+
5
+ require "ruby-next/language/setup"
6
+ RubyNext::Language.setup_gem_load_path(transpile: true)
7
+
8
+ require "anyway/version"
9
+
10
+ require "anyway/ext/deep_dup"
11
+ require "anyway/ext/deep_freeze"
12
+ require "anyway/ext/hash"
13
+ require "anyway/ext/flatten_names"
14
+
15
+ require "anyway/utils/deep_merge"
16
+ require "anyway/utils/which"
17
+
18
+ require "anyway/settings"
19
+ require "anyway/tracing"
20
+ require "anyway/config"
21
+ require "anyway/auto_cast"
22
+ require "anyway/type_casting"
23
+ require "anyway/env"
24
+ require "anyway/loaders"
25
+ require "anyway/rbs"
26
+
27
+ module Anyway # :nodoc:
28
+ class << self
29
+ def env
30
+ @env ||= ::Anyway::Env.new
31
+ end
32
+
33
+ def loaders
34
+ @loaders ||= ::Anyway::Loaders::Registry.new
35
+ end
36
+ end
37
+
38
+ # Configure default loaders
39
+ loaders.append :yml, Loaders::YAML
40
+ loaders.append :ejson, Loaders::EJSON if Utils.which("ejson")
41
+ loaders.append :env, Loaders::Env
42
+
43
+ if ENV.key?("DOPPLER_TOKEN") && ENV["ANYWAY_CONFIG_DISABLE_DOPPLER"] != "true"
44
+ loaders.append :doppler, Loaders::Doppler
45
+ end
46
+ end
47
+
48
+ if defined?(::Rails::VERSION)
49
+ require "anyway/rails"
50
+ else
51
+ require "anyway/rails/autoload"
52
+ end
53
+
54
+ require "anyway/testing" if ENV["RACK_ENV"] == "test" || ENV["RAILS_ENV"] == "test"
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Generates a config class with the given name and list of parameters
3
+ and put it into `app/configs` folder.
4
+
5
+ Example:
6
+ rails generate app_config my_service param1 param2 ...
7
+
8
+ This will create:
9
+ app/configs/my_service_config.rb
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "generators/anyway/config/config_generator"
4
+
5
+ module Anyway
6
+ module Generators
7
+ class AppConfigGenerator < ConfigGenerator
8
+ source_root ConfigGenerator.source_root
9
+
10
+ private
11
+
12
+ def config_root
13
+ "app/configs"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ Description:
2
+ Generates a config class with the given name and list of parameters.
3
+
4
+ Use `--yml` / `--no-yml` option to specify whether you want to create a YAML config as well
5
+ (if no option is specified, the generator would ask you to choose during the run).
6
+
7
+ Use `--app` option to create config class in `app/configs` folder.
8
+
9
+ Example:
10
+ rails generate config my_service param1 param2 ...
11
+
12
+ This will create:
13
+ config/configs/my_service_config.rb
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module Anyway
6
+ module Generators
7
+ class ConfigGenerator < ::Rails::Generators::NamedBase
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ class_option :yml, type: :boolean
11
+ class_option :app, type: :boolean, default: false
12
+ argument :parameters, type: :array, default: [], banner: "param1 param2"
13
+
14
+ # check_class_collision suffix: "Config"
15
+
16
+ def run_install_if_needed
17
+ return if ::Rails.root.join(static_config_root, "application_config.rb").exist?
18
+ generate "anyway:install"
19
+ end
20
+
21
+ def create_config
22
+ template "config.rb", File.join(config_root, class_path, "#{file_name}_config.rb")
23
+ end
24
+
25
+ def create_yml
26
+ create_yml = options.fetch(:yml) { yes?("Would you like to generate a #{file_name}.yml file?") }
27
+ return unless create_yml
28
+ template "config.yml", File.join("config", "#{file_name}.yml")
29
+ end
30
+
31
+ private
32
+
33
+ def static_config_root
34
+ Anyway::Settings.autoload_static_config_path || Anyway::DEFAULT_CONFIGS_PATH
35
+ end
36
+
37
+ def config_root
38
+ if options[:app]
39
+ "app/configs"
40
+ else
41
+ static_config_root
42
+ end
43
+ end
44
+
45
+ def needs_config_name?
46
+ raise "No longer needed" if Gem::Version.new(Anyway::VERSION) >= Gem::Version.new("3.0.0")
47
+ file_name.include?("_")
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ <% module_namespacing do -%>
4
+ class <%= class_name %>Config < ApplicationConfig
5
+ <%- if needs_config_name? %>
6
+ config_name :<%= file_name %>
7
+ <%- end -%>
8
+ <%- unless parameters.empty? -%>
9
+ attr_config <%= parameters.map { |param| ":#{param}" }.join(", ") %>
10
+ <%- end -%>
11
+ end
12
+ <% end -%>
@@ -0,0 +1,13 @@
1
+ default: &default
2
+ <%- parameters.each do |param| -%>
3
+ # <%= param %>: ""
4
+ <%- end -%>
5
+
6
+ development:
7
+ <<: *default
8
+
9
+ test:
10
+ <<: *default
11
+
12
+ production:
13
+ <<: *default
@@ -0,0 +1,4 @@
1
+ Description:
2
+ Generates a base config class (ApplicationConfig) for your application and
3
+ add the required configuriton (.gitignore, config/application.rb).
4
+
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+
5
+ module Anyway
6
+ module Generators
7
+ class InstallGenerator < ::Rails::Generators::Base
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ class_option :configs_path, type: :string
11
+
12
+ def copy_application_config
13
+ template "application_config.rb", File.join(static_config_root, "application_config.rb")
14
+ end
15
+
16
+ def add_local_files_to_gitignore
17
+ if File.exist?(File.join(destination_root, ".gitignore"))
18
+ append_to_file ".gitignore", "\n/config/*.local.yml\n/config/credentials/local.*\n"
19
+ end
20
+ end
21
+
22
+ # rubocop:disable Layout/HeredocIndentation
23
+ def add_setup_autoload_to_config
24
+ maybe_comment_indented = default_configs_path? ? " # " : " "
25
+ inject_into_file "config/application.rb", after: %r{< Rails::Application\n} do
26
+ <<-RUBY
27
+ # Configure the path for configuration classes that should be used before initialization
28
+ # NOTE: path should be relative to the project root (Rails.root)
29
+ #{maybe_comment_indented}config.anyway_config.autoload_static_config_path = "#{static_config_root}"
30
+ #{maybe_comment_indented.sub(/\s+$/, "")}
31
+ RUBY
32
+ end
33
+ end
34
+ # rubocop:enable Layout/HeredocIndentation
35
+
36
+ private
37
+
38
+ def static_config_root
39
+ options[:configs_path] || Anyway::Settings.autoload_static_config_path || Anyway::DEFAULT_CONFIGS_PATH
40
+ end
41
+
42
+ def default_configs_path?
43
+ static_config_root == (Anyway::Settings.autoload_static_config_path || Anyway::DEFAULT_CONFIGS_PATH)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Base class for application config classes
4
+ class ApplicationConfig < Anyway::Config
5
+ class << self
6
+ # Make it possible to access a singleton config instance
7
+ # via class methods (i.e., without explicitly calling `instance`)
8
+ delegate_missing_to :instance
9
+
10
+ private
11
+
12
+ # Returns a singleton config instance
13
+ def instance
14
+ @instance ||= new
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,149 @@
1
+ module Anyway
2
+ def self.env: -> Env
3
+ def self.loaders: -> Loaders::Registry
4
+
5
+ class Settings
6
+ def self.default_config_path=: (String | Pathname | ^(untyped) -> String val) -> void
7
+ def self.future: -> Future
8
+ def self.current_environment: -> String?
9
+ def self.default_environmental_key: -> String?
10
+ def self.known_environments: -> Array[String]?
11
+
12
+ class Future
13
+ def self.setting: (untyped name, untyped default_value) -> untyped
14
+ def self.settings: -> Hash[untyped, untyped]
15
+ def use: (*untyped names) -> untyped
16
+ end
17
+ end
18
+
19
+ module Tracing
20
+ class Trace
21
+ def merge!: (Trace another_trace) -> void
22
+ end
23
+
24
+ def inspect: -> String
25
+ def self.capture: ?{ -> Hash[untyped, untyped]? } -> Trace
26
+ def self.trace_stack: -> Array[untyped]
27
+ def self.current_trace: -> Trace?
28
+ def self.source_stack: -> Array[untyped]
29
+ def self.current_trace_source: -> ({type: Symbol} & Hash[Symbol, untyped])
30
+ def self.with_trace_source: (untyped src) { -> void } -> untyped
31
+ def trace!: [V] (Symbol, *String paths, **untyped) ?{ -> V} -> V
32
+ def self.trace!: [V] (Symbol, *String paths, **untyped) ?{ -> V} -> V
33
+ end
34
+
35
+ module RBSGenerator
36
+ def to_rbs: -> String
37
+ end
38
+
39
+ module OptparseConfig
40
+ def option_parser: -> OptionParser
41
+ def parse_options!: (Array[String]) -> void
42
+
43
+ module ClassMethods
44
+ def ignore_options: (*Symbol args) -> void
45
+ def describe_options: (**(String | {desc: String, type: Module})) -> void
46
+ def flag_options: (*Symbol args) -> void
47
+ def extend_options: { (OptionParser, Config) -> void } -> void
48
+ end
49
+ end
50
+
51
+ module DynamicConfig
52
+ module ClassMethods
53
+ def for: (String | Symbol name, ?auto_cast: bool, **untyped) -> Hash[untyped, untyped]
54
+ end
55
+ end
56
+
57
+ type valueType = Symbol | nil
58
+ type arrayType = {array: bool, type: valueType}
59
+ type configType = {config: Class | String}
60
+ type hashType = Hash[Symbol, valueType | arrayType | hashType]
61
+
62
+ type mappingType = valueType | arrayType | hashType | configType
63
+ type envType = String | Symbol | Array[String | Symbol] | {except: String | Symbol | Array[String | Symbol]}
64
+
65
+ type requiredType = Array[Symbol | Hash[Symbol, requiredType]]
66
+
67
+ class Config
68
+ extend RBSGenerator
69
+ extend DynamicConfig::ClassMethods
70
+ extend OptparseConfig::ClassMethods
71
+ include DynamicConfig
72
+ include OptparseConfig
73
+
74
+ def self.attr_config: (*Symbol args, **untyped) -> void
75
+ def self.defaults: -> Hash[String, untyped]
76
+ def self.config_attributes: -> Array[Symbol]?
77
+ def self.required: (*Symbol names, ?env: envType, **requiredType) -> void
78
+ def self.required_attributes: -> Array[Symbol]
79
+ def self.on_load: (*Symbol callbacks) ?{ () [self: instance] -> void } -> void
80
+ def self.config_name: (?(Symbol | String) val) -> String?
81
+ def self.env_prefix: (?(Symbol | String) val) -> String
82
+ def self.coerce_types: (**mappingType) -> void
83
+ def self.coercion_mapping: -> Hash[untyped, untyped]?
84
+ def self.disable_auto_cast!: -> void
85
+
86
+ attr_reader config_name: String
87
+ attr_reader env_prefix: String
88
+
89
+ def initialize: (?Hash[Symbol | String, untyped] overrides) -> void
90
+ | (NilClass) -> void
91
+ def reload: (?Hash[Symbol | String, untyped] overrides) -> Config
92
+ def clear: -> void
93
+ def load: (Hash[Symbol | String, untyped] overrides) -> Config
94
+ | (NilClass) -> Config
95
+ def dig: (*(Symbol | String) keys) -> untyped
96
+ def to_h: -> Hash[untyped, untyped]
97
+ def dup: -> Config
98
+ def deconstruct_keys: (untyped keys) -> Hash[untyped, untyped]
99
+ def to_source_trace: -> Hash[String, untyped]
100
+ def inspect: -> String
101
+ def pretty_print: (untyped q) -> untyped
102
+ def as_env: -> Hash[String, String]
103
+
104
+ private
105
+ attr_reader values: Hash[untyped, untyped]
106
+ def raise_validation_error: (String msg) -> void
107
+ def flatten_hash: (Hash[untyped, untyped], String, Hash[String, String]) -> Hash[String, String]
108
+
109
+ class Error < StandardError
110
+ end
111
+
112
+ class ValidationError < Error
113
+ end
114
+ end
115
+
116
+ class Env
117
+ def clear: -> void
118
+ def fetch: (String prefix) -> untyped
119
+ def fetch_with_trace: (String prefix) -> [untyped, Tracing::Trace?]
120
+ end
121
+
122
+ module Loaders
123
+ class Base
124
+ include Tracing
125
+
126
+ def self.call: (?local: bool, **untyped) -> untyped
127
+ def initialize: (local: bool) -> void
128
+ def use_local?: -> bool
129
+ end
130
+
131
+ interface _Loader
132
+ def call: (**untyped) -> Hash[untyped, untyped]
133
+ end
134
+
135
+ class Registry
136
+ def prepend: (Symbol id, _Loader loader) -> void
137
+ | (Symbol id) { (**untyped) -> Hash[untyped, untyped] } -> void
138
+ def append: (Symbol id, _Loader loader) -> void
139
+ | (Symbol id) { (**untyped) -> Hash[untyped, untyped] } -> void
140
+ def insert_before: (Symbol another_id, Symbol id, _Loader loader) -> void
141
+ | (Symbol another_id, Symbol id) { (**untyped) -> Hash[untyped, untyped] } -> void
142
+ def insert_after: (Symbol another_id, Symbol id, _Loader loader) -> void
143
+ | (Symbol another_id, Symbol id) { (**untyped) -> Hash[untyped, untyped] } -> void
144
+ def override: (Symbol id, _Loader loader) -> void
145
+ | (Symbol id) { (**untyped) -> Hash[untyped, untyped] } -> void
146
+ def delete: (Symbol id) -> void
147
+ end
148
+ end
149
+ end
data/sig/manifest.yml ADDED
@@ -0,0 +1,6 @@
1
+ dependencies:
2
+ - name: optparse
3
+ - name: json
4
+ - name: pathname
5
+ - name: date
6
+ - name: time