typed-config 1.3.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3551b257563c33aefb5d1da190ddc584bfb1893edc271c775d82ab3fe46c30f1
4
+ data.tar.gz: 2a7b822b9d3e22c2e4c678f4e00dd5b3618eb5d51ecd8210ff1af60c35f74d10
5
+ SHA512:
6
+ metadata.gz: 1c0477d6708d339e692e504503685d4757a4d00d36b968d385cf920006993b70e529a99413b8ac958ebf9bbb15ca31bab86a80decf672aca17944f4f8089f45b
7
+ data.tar.gz: 12cc17789e2b9531a8dc05f8770c53dd0f5005af125351dd8833606137931f6a7d5615e752f84a4ea473023992f72f4dcdcda4aa16b4c95907ff2b36367c28af
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
@@ -0,0 +1,109 @@
1
+ # TypedConfig
2
+
3
+ TypedConfig is a simple solution for managing multi-environment application
4
+ settings whilst maintaining type safety.
5
+
6
+ Currently, only Rails is supported out of the box.
7
+
8
+ TypedConfig is meant to run in projects using [Sorbet](https://sorbet.org/),
9
+ although it can run in projects not using it and still gain from the easy-to-configure
10
+ settings.
11
+
12
+ This project has been inspired by [Config](https://github.com/rubyconfig/config) and
13
+ it's meant to be an easy drop-in replacement with type-safety.
14
+
15
+ ## Installing
16
+
17
+ Add the gem to your `Gemfile`:
18
+ ```ruby
19
+ gem 'typed-config'
20
+ ```
21
+
22
+ Run the install rake task to initialize the config schema and settings files:
23
+ ```ruby
24
+ rails g typed_config:install
25
+ ```
26
+
27
+ This will generate the schema file `config/config.rb` where the schema of the
28
+ settings should be defined, and the settings file:
29
+ ```
30
+ config/settings.yml
31
+ config/settings.local.yml
32
+ config/settings/development.yml
33
+ config/settings/production.yml
34
+ config/settings/test.yml
35
+ ```
36
+ It will also update the existing `.gitignore` to local settings files.
37
+
38
+ ## Defining the Settings Properties
39
+ First, the schema of the Settings object must be defined in the `config/config.rb`
40
+ file, for example:
41
+ ```ruby
42
+ TypedConfig::Schema.configure do |s|
43
+ s.const :env, String
44
+ s.const :app do
45
+ s.const :domain, T.nilable(String)
46
+ s.const :port, T.nilable(Integer)
47
+ end
48
+ s.const :support do
49
+ s.const :email, String
50
+ end
51
+ end
52
+ ```
53
+
54
+ Now in the code, you can access any properties as a `T::Struct`. All properties will be
55
+ available across the application in a `Settings` object:
56
+ ```ruby
57
+ Settings.env
58
+ Settting.support.email
59
+
60
+ # Example:
61
+ config.action_mailer.asset_host = "https://#{Settings.app.domain}"
62
+ ```
63
+
64
+ ## Changing the Settings
65
+ The settings are defined in YAML (with ERB support). Once the schema is defined, update
66
+ one of the settings file to load the data:
67
+ ```yaml
68
+ env: <%= ENV['RACK_ENV'] %>
69
+ app:
70
+ domain: <%= ENV['APP_DOMAIN'] %>
71
+ port: <%= ENV.fetch('APP_PORT', 3000) %>
72
+ support:
73
+ email: 'support@typed-config.com
74
+ ```
75
+ When loaded, `typed-config` will try to automatically coerce the values to match the ones
76
+ specified in the schema.
77
+
78
+ You may define default (or different) settings for each environment by changing the
79
+ `config/settings/#{env}.yml`. For instance, to have different defaults in the test
80
+ environment, add the settings to `config/settings/test.yml`.
81
+
82
+ In order from top to bottom - the settings in the lower files overwrite the ones higher -
83
+ these are the files loaded:
84
+ ```
85
+ config/settings.yml
86
+ config/settings/#{environment}.yml
87
+
88
+ config/settings.local.yml
89
+ config/settings/#{environment}.local.yml
90
+ ```
91
+
92
+ ## Type Safety
93
+ To make use of the type-safety feature, you must re-generate the RBI files when the schema
94
+ is changed using a rake command:
95
+ ```shell
96
+ bundle exec rake typed-config:rbi
97
+ ```
98
+
99
+ Now you can run sorbet to check the types of the settings properties:
100
+ ```ruby
101
+ T.reveal_type(Settings.app.domain) # Revealed type: T.nilable(String)
102
+
103
+ sig { params(server_write_key: String).void }
104
+ def configure_segment(server_write_key); end
105
+
106
+
107
+ configure_segment(server_write_key: Settings.segment.server_write_key)
108
+ # Expected String but found T.nilable(String) for argument server_write_key
109
+ ```
@@ -0,0 +1,36 @@
1
+ # typed: ignore
2
+ # frozen_string_literal: true
3
+
4
+ module TypedConfig
5
+ module Generators
6
+ class InstallGenerator < ::Rails::Generators::Base
7
+ desc 'Generated typed-config files for Rails.'
8
+
9
+ source_root File.expand_path('templates', __dir__)
10
+
11
+ def copy_config
12
+ template 'config.rb', 'config/config.rb'
13
+ end
14
+
15
+ def copy_settings
16
+ template 'settings.yml', 'config/settings.yml'
17
+ template 'settings.local.yml', 'config/settings.local.yml'
18
+ directory 'settings', 'config/settings'
19
+ end
20
+
21
+ def modify_gitignore
22
+ create_file '.gitignore' unless File.file?('.gitignore')
23
+
24
+ append_to_file '.gitignore' do
25
+ "\nconfig/settings.local.yml\n" \
26
+ "config/settings/*.local.yml\n" \
27
+ "config/environments/*.local.yml\n"
28
+ end
29
+ end
30
+
31
+ def generate_rbi
32
+ rake 'typed-config:rbi'
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,9 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ TypedConfig::Schema.configure do |s|
5
+ s.const :aws do
6
+ s.const :access_key_id, T.nilable(String)
7
+ s.const :secret_access_key, T.nilable(String)
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ aws:
2
+ access_key_id: test-access-key-id
3
+ secret_access_key: test-secret-access-key
@@ -0,0 +1,3 @@
1
+ aws:
2
+ access_key_id: <%= ENV['AWS_ACCESS_KEY_ID'] %>
3
+ secret_access_key: <%= ENV['AWS_SECRET_ACCESS_KEY'] %>
@@ -0,0 +1,10 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'sorbet-runtime'
5
+
6
+ require 'typed_config/version'
7
+ require 'typed_config/setup'
8
+ require 'typed_config/schema'
9
+
10
+ require('typed_config/railtie') if defined?(::Rails)
@@ -0,0 +1,20 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'erb'
5
+ require 'yaml'
6
+
7
+ module TypedConfig
8
+ class Loader
9
+ extend T::Sig
10
+
11
+ sig { params(filename: String).returns(T::Hash[String, T.untyped]) }
12
+ def load(filename)
13
+ return {} unless File.file?(filename)
14
+
15
+ contents = File.read(filename)
16
+ yaml = YAML.safe_load(ERB.new(contents).result) || {}
17
+ T.cast(yaml, T::Hash[String, T.untyped])
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,24 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ require_relative 'setup'
5
+
6
+ module TypedConfig
7
+ class Railtie < ::Rails::Railtie
8
+ railtie_name :typed_config
9
+
10
+ rake_tasks do
11
+ path = File.expand_path(__dir__)
12
+ Dir.glob("#{path}/tasks/**/*.rake").each { |f| load f }
13
+ end
14
+
15
+ def preload
16
+ Setup.new.call(
17
+ config_path: ::Rails.root.join('config'),
18
+ env: ::Rails.env,
19
+ )
20
+ end
21
+
22
+ config.before_configuration { T.unsafe(self).preload }
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'erb'
5
+
6
+ module TypedConfig
7
+ class RBIBuilder
8
+ extend T::Sig
9
+
10
+ sig { void }
11
+ def initialize
12
+ @structs = T.let(
13
+ {},
14
+ T::Hash[
15
+ String,
16
+ T::Array[{ name: Symbol, type: String }],
17
+ ],
18
+ )
19
+ end
20
+
21
+ sig { params(struct: Class).void }
22
+ def add_struct(struct)
23
+ @structs[T.must(struct.name)] ||= []
24
+ end
25
+
26
+ sig { params(struct: Class, name: Symbol, type: T.untyped).void }
27
+ def add_const(struct, name, type)
28
+ add_struct(struct)
29
+ T.must(@structs[T.must(struct.name)]).push({ name: name, type: type.name })
30
+ end
31
+
32
+ sig { params(schema_const_name: String).returns(String) }
33
+ def build(schema_const_name)
34
+ b = binding
35
+ b.local_variable_set(:schema_const_name, schema_const_name)
36
+ b.local_variable_set(:structs, @structs)
37
+ template_file = File.read(File.join(File.dirname(__FILE__), 'structs.rbi.erb'))
38
+ ERB.new(template_file, trim_mode: '>').result(b)
39
+ end
40
+
41
+ private
42
+
43
+ sig { params(consts: T::Array[{ name: Symbol, type: String }]).returns(String) }
44
+ def build_consts(consts)
45
+ consts.inject('') do |prev, const|
46
+ prev + " const :#{const[:name]}, #{const[:type]}\n"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,42 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative './schema_builder'
5
+ require_relative './structs/_structs'
6
+
7
+ module TypedConfig
8
+ class Schema
9
+ @schema_builder = T.let(nil, T.nilable(SchemaBuilder))
10
+ @schema_const_name = T.let('Settings', String)
11
+
12
+ class << self
13
+ extend T::Sig
14
+
15
+ sig { returns(String) }
16
+ attr_reader :schema_const_name
17
+
18
+ sig do
19
+ params(
20
+ const_name: T.nilable(String),
21
+ _blk: T.proc.params(config: SchemaBuilder).void,
22
+ ).void
23
+ end
24
+ def configure(const_name = nil, &_blk)
25
+ @schema_const_name = const_name || schema_const_name
26
+ yield schema_builder
27
+ end
28
+
29
+ sig { returns(String) }
30
+ def rbi
31
+ schema_builder.build_rbi(schema_const_name)
32
+ end
33
+
34
+ private
35
+
36
+ sig { returns(SchemaBuilder) }
37
+ def schema_builder
38
+ @schema_builder ||= SchemaBuilder.new('Settings')
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,77 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative './rbi_builder'
5
+ require_relative './structs/_structs'
6
+
7
+ module TypedConfig
8
+ class SchemaBuilder
9
+ extend T::Sig
10
+
11
+ sig { params(struct: String, rbi_builder: RBIBuilder).void }
12
+ def initialize(struct, rbi_builder: RBIBuilder.new)
13
+ @rbi_builder = rbi_builder
14
+ @struct = T.let(create_struct(Structs, struct), Class)
15
+ end
16
+
17
+ sig do
18
+ params(
19
+ name: Symbol,
20
+ type: T.untyped,
21
+ blk: T.nilable(T.proc.void),
22
+ ).void
23
+ end
24
+ def const(name, type = nil, &blk)
25
+ clazz = retrieve_class(name: name, type: type, &blk)
26
+ raise ArgumentError, "Attribute '#{name}' must receive a type or block." if clazz.nil?
27
+
28
+ rbi_builder.add_const(struct, name, clazz)
29
+ struct.tap do |scope|
30
+ scope.const name, clazz
31
+ end
32
+ end
33
+
34
+ sig { params(schema_const_name: String).returns(String) }
35
+ def build_rbi(schema_const_name)
36
+ rbi_builder.build(schema_const_name)
37
+ end
38
+
39
+ protected
40
+
41
+ sig { returns(Class) }
42
+ attr_reader :struct
43
+
44
+ sig { returns(RBIBuilder) }
45
+ attr_reader :rbi_builder
46
+
47
+ sig do
48
+ params(
49
+ name: Symbol,
50
+ type: T.untyped,
51
+ blk: T.nilable(T.proc.void),
52
+ ).returns(T.untyped)
53
+ end
54
+ def retrieve_class(name:, type:, &blk)
55
+ return type unless block_given?
56
+
57
+ process_substruct(name: name, &blk)
58
+ end
59
+
60
+ sig { params(name: Symbol, blk: T.proc.void).returns(Class) }
61
+ def process_substruct(name:, &blk)
62
+ top_struct = struct
63
+ sub_struct = create_struct(struct, name.to_s)
64
+ @struct = sub_struct
65
+ instance_eval(&blk)
66
+ @struct = top_struct
67
+ sub_struct
68
+ end
69
+
70
+ sig { params(base: T.any(Module, Class), name: String).returns(Class) }
71
+ def create_struct(base, name)
72
+ new_struct = base.const_set(name.camelcase, Class.new(T::Struct))
73
+ rbi_builder.add_struct(new_struct)
74
+ new_struct
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,55 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require 'sorbet-coerce'
5
+ require_relative './loader'
6
+ require_relative './schema'
7
+
8
+ module TypedConfig
9
+ class Setup
10
+ extend T::Sig
11
+
12
+ sig { params(loader: Loader).void }
13
+ def initialize(loader: Loader.new)
14
+ @loader = loader
15
+ end
16
+
17
+ sig { params(config_path: T.any(Pathname, String), env: String).void }
18
+ def call(config_path:, env: T.cast(ENV['RACK_ENV'], String))
19
+ config_file = "#{config_path}/config.rb"
20
+ return unless File.file?(config_file)
21
+
22
+ require_relative config_file
23
+
24
+ settings = build_settings(config_path: config_path, env: env)
25
+ definition = TypeCoerce[Structs::Settings].new.from(settings)
26
+ Object.const_set(Schema.schema_const_name, definition)
27
+ end
28
+
29
+ private
30
+
31
+ sig { returns(Loader) }
32
+ attr_reader :loader
33
+
34
+ sig do
35
+ params(config_path: T.any(Pathname, String), env: String)
36
+ .returns(T::Hash[String, T.untyped])
37
+ end
38
+ def build_settings(config_path:, env:)
39
+ merge_settings([
40
+ loader.load("#{config_path}/settings.yml"),
41
+ loader.load("#{config_path}/settings/#{env}.yml"),
42
+ loader.load("#{config_path}/settings.local.yml"),
43
+ loader.load("#{config_path}/settings/#{env}.local.yml"),
44
+ ])
45
+ end
46
+
47
+ sig do
48
+ params(values: T::Array[T::Hash[String, T.untyped]])
49
+ .returns(T::Hash[String, T.untyped])
50
+ end
51
+ def merge_settings(values)
52
+ values.inject({}) { |prev, curr| prev.deep_merge(curr) }
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,12 @@
1
+ # typed: strong
2
+
3
+ ::<%= schema_const_name %> = T.let(nil, TypedConfig::Structs::Settings)
4
+ <% structs.each do |struct_name, consts| %>
5
+
6
+ class <%= struct_name %> < T::Struct
7
+ <% consts.each do |const| %>
8
+ const :<%= const[:name] %>, <%= const[:type] %>
9
+
10
+ <% end %>
11
+ end
12
+ <% end %>
@@ -0,0 +1,7 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ module TypedConfig
5
+ module Structs
6
+ end
7
+ end
@@ -0,0 +1,9 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ module TypedConfig
5
+ module Structs
6
+ class Settings < T::Struct
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ namespace 'typed-config' do
5
+ task rbi: :environment do
6
+ puts 'Generating RBI files...'
7
+ rbi = TypedConfig::Schema.rbi
8
+ FileUtils.mkdir_p(::Rails.root.join('sorbet', 'typed-config'))
9
+ File.write(::Rails.root.join('sorbet', 'typed-config', 'structs.rbi'), rbi)
10
+ puts 'RBI files updated in sorbet/typed-config.'
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ # typed: strong
2
+ # frozen_string_literal: true
3
+
4
+ module TypedConfig
5
+ VERSION = '1.3.8'
6
+ end
@@ -0,0 +1,15 @@
1
+ # typed: strong
2
+
3
+ module TypedConfig
4
+ class Schema
5
+ extend T::Sig
6
+
7
+ sig do
8
+ params(
9
+ const_name: T.nilable(String),
10
+ blk: T.proc.params(config: SchemaBuilder).void,
11
+ ).void
12
+ end
13
+ def self.configure(const_name = nil, &blk); end
14
+ end
15
+ end
@@ -0,0 +1,16 @@
1
+ # typed: strong
2
+
3
+ module TypedConfig
4
+ class SchemaBuilder
5
+ extend T::Sig
6
+
7
+ sig do
8
+ params(
9
+ name: Symbol,
10
+ type: T.untyped,
11
+ blk: T.nilable(T.proc.void),
12
+ ).void
13
+ end
14
+ def const(name, type = nil, &blk); end
15
+ end
16
+ end
metadata ADDED
@@ -0,0 +1,183 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: typed-config
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.3.8
5
+ platform: ruby
6
+ authors:
7
+ - Alex Santos
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-12-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: deep_merge
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.2'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.2.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.2'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.2.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: sorbet-coerce
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0.2'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0.2'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rails
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: 5.2.4
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: 5.2.4
61
+ - !ruby/object:Gem::Dependency
62
+ name: rubocop
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: 0.84.0
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: 0.84.0
75
+ - !ruby/object:Gem::Dependency
76
+ name: rubocop-performance
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 1.6.1
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: 1.6.1
89
+ - !ruby/object:Gem::Dependency
90
+ name: rubocop-sorbet
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: 0.4.0
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: 0.4.0
103
+ - !ruby/object:Gem::Dependency
104
+ name: rubocop-rspec
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: 1.39.0
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: 1.39.0
117
+ - !ruby/object:Gem::Dependency
118
+ name: sorbet-runtime
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ description: Manage multi-environment settings with type-safety in Ruby
132
+ email: hello@alexcsantos.com
133
+ executables: []
134
+ extensions: []
135
+ extra_rdoc_files: []
136
+ files:
137
+ - Gemfile
138
+ - README.md
139
+ - lib/generators/typed_config/install_generator.rb
140
+ - lib/generators/typed_config/templates/config.rb
141
+ - lib/generators/typed_config/templates/settings.local.yml
142
+ - lib/generators/typed_config/templates/settings.yml
143
+ - lib/generators/typed_config/templates/settings/development.yml
144
+ - lib/generators/typed_config/templates/settings/production.yml
145
+ - lib/generators/typed_config/templates/settings/test.yml
146
+ - lib/typed-config.rb
147
+ - lib/typed_config/loader.rb
148
+ - lib/typed_config/railtie.rb
149
+ - lib/typed_config/rbi_builder.rb
150
+ - lib/typed_config/schema.rb
151
+ - lib/typed_config/schema_builder.rb
152
+ - lib/typed_config/setup.rb
153
+ - lib/typed_config/structs.rbi.erb
154
+ - lib/typed_config/structs/_structs.rb
155
+ - lib/typed_config/structs/settings.rbi
156
+ - lib/typed_config/tasks/typed-config/rbi.rake
157
+ - lib/typed_config/version.rb
158
+ - rbi/schema.rbi
159
+ - rbi/schema_builder.rbi
160
+ homepage: https://rubygems.org/gems/typed-config
161
+ licenses:
162
+ - MIT
163
+ metadata: {}
164
+ post_install_message:
165
+ rdoc_options: []
166
+ require_paths:
167
+ - lib
168
+ required_ruby_version: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: 2.4.0
173
+ required_rubygems_version: !ruby/object:Gem::Requirement
174
+ requirements:
175
+ - - ">="
176
+ - !ruby/object:Gem::Version
177
+ version: '0'
178
+ requirements: []
179
+ rubygems_version: 3.0.6
180
+ signing_key:
181
+ specification_version: 4
182
+ summary: Type-safe settings for Rails
183
+ test_files: []