hati-config 0.1.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.
@@ -0,0 +1,389 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'json'
5
+
6
+ # HatiConfig module provides functionality for managing HatiConfig features.
7
+ module HatiConfig
8
+ # rubocop:disable Metrics/ClassLength
9
+
10
+ # Setting class provides a configuration tree structure for managing settings.
11
+ #
12
+ # This class allows for dynamic configuration management, enabling
13
+ # the loading of settings from hashes, YAML, or JSON formats.
14
+ #
15
+ # @example Basic usage
16
+ # settings = Setting.new do
17
+ # config(:key1, value: "example")
18
+ # config(:key2, type: :int)
19
+ # end
20
+ #
21
+ class Setting
22
+ extend HatiConfig::Environment
23
+ include HatiConfig::Environment
24
+ extend HatiConfig::Schema
25
+ extend HatiConfig::Cache
26
+ extend HatiConfig::Encryption
27
+
28
+ # Dynamically define methods for each type in TypeMap.
29
+ #
30
+ # @!method int(value)
31
+ # Sets an integer configuration value.
32
+ # @param value [Integer] The integer value to set.
33
+ #
34
+ # @!method string(value)
35
+ # Sets a string configuration value.
36
+ # @param value [String] The string value to set.
37
+ #
38
+ # ... (other type methods)
39
+ HatiConfig::TypeMap.list_types.each do |type|
40
+ define_method(type.downcase) do |stng, lock = nil|
41
+ params = { type: type }
42
+ params[:lock] = lock if lock.nil?
43
+
44
+ config(stng, **params)
45
+ end
46
+ end
47
+
48
+ # Initializes a new Setting instance.
49
+ #
50
+ # @yield [self] Configures the instance upon creation if a block is given.
51
+ def initialize(&block)
52
+ @config_tree = {}
53
+ @schema = {}
54
+ @immutable_schema = {}
55
+ @encrypted_tree = {}
56
+
57
+ if self.class.encryption_config.key_provider
58
+ self.class.encryption do
59
+ key_provider :env
60
+ end
61
+ end
62
+
63
+ instance_eval(&block) if block_given?
64
+ end
65
+
66
+ # Loads configuration from a hash with an optional schema.
67
+ #
68
+ # @param data [Hash] The hash containing configuration data.
69
+ # @param schema [Hash] Optional schema for type validation.
70
+ # @raise [NoMethodError] If a method corresponding to a key is not defined.
71
+ # @raise [SettingTypeError] If a value doesn't match the specified type in the schema.
72
+ #
73
+ # @example Loading from a hash with type validation
74
+ # settings.load_from_hash({ name: "admin", max_connections: 10 }, schema: { name: :str, max_connections: :int })
75
+ def load_from_hash(data, schema: {}, lock_schema: {}, encrypted_fields: {})
76
+ data.each do |key, value|
77
+ key = key.to_sym
78
+ type = schema[key] if schema
79
+ lock = lock_schema[key] if lock_schema
80
+ encrypted = encrypted_fields[key] if encrypted_fields
81
+
82
+ if value.is_a?(Hash)
83
+ configure(key) do
84
+ load_from_hash(value,
85
+ schema: schema.is_a?(Hash) ? schema[key] : {},
86
+ lock_schema: lock_schema.is_a?(Hash) ? lock_schema[key] : {},
87
+ encrypted_fields: encrypted_fields.is_a?(Hash) ? encrypted_fields[key] : {})
88
+ end
89
+ elsif value.is_a?(Setting)
90
+ configure(key) do
91
+ load_from_hash(value.to_h, schema: schema[key], lock_schema: lock_schema[key],
92
+ encrypted_fields: encrypted_fields[key])
93
+ end
94
+ else
95
+ config(key => value, type: type, lock: lock, encrypted: encrypted)
96
+ end
97
+ end
98
+ end
99
+
100
+ # Configures a node of the configuration tree.
101
+ #
102
+ # @param node [Symbol, String] The name of the config node key.
103
+ # @yield [Setting] A block that configures the new node.
104
+ #
105
+ # @example Configuring a new node
106
+ # settings.configure(:database) do
107
+ # config(:host, value: "localhost")
108
+ # config(:port, value: 5432)
109
+ # end
110
+ def configure(node, &block)
111
+ if config_tree[node]
112
+ config_tree[node].instance_eval(&block)
113
+ else
114
+ create_new_node(node, &block)
115
+ end
116
+ end
117
+
118
+ # Configures a setting with a given name and type.
119
+ #
120
+ # @param setting [Symbol, Hash, nil] The name of the setting or a hash of settings.
121
+ # @param type [Symbol, nil] The expected type of the setting.
122
+ # @param opt [Hash] Additional options for configuration.
123
+ # @return [self] The current instance for method chaining.
124
+ # @raise [SettingTypeError] If the value does not match the expected type.
125
+ #
126
+ # @example Configuring a setting
127
+ # settings.config(max_connections: 10, type: :int)
128
+ def config(setting = nil, type: nil, lock: nil, encrypted: false, **opt)
129
+ return self if !setting && opt.empty?
130
+
131
+ # If setting is a symbol/string and we have keyword options, merge them
132
+ if (setting.is_a?(Symbol) || setting.is_a?(String)) && !opt.empty?
133
+ raw_stngs = opt.merge(setting => opt[:value])
134
+ raw_stngs.delete(:value)
135
+ else
136
+ raw_stngs = setting || opt
137
+ end
138
+ stngs = extract_setting_info(raw_stngs)
139
+
140
+ stng_lock = determine_lock(stngs, lock)
141
+ stng_type = determine_type(stngs, type)
142
+ stng_encrypted = determine_encrypted(stngs, encrypted)
143
+
144
+ if stng_encrypted
145
+ value = stngs[:value]
146
+ if value.nil? && config_tree[stngs[:name]]
147
+ value = config_tree[stngs[:name]]
148
+ value = self.class.encryption_config.decrypt(value) if @encrypted_tree[stngs[:name]]
149
+ end
150
+
151
+ if value.is_a?(HatiConfig::Setting)
152
+ # Handle nested settings
153
+ value.instance_eval(&block) if block_given?
154
+ elsif !value.nil?
155
+ # If we're setting a new value or updating an existing one
156
+ raise SettingTypeError.new('string (encrypted values must be strings)', value) unless value.is_a?(String)
157
+
158
+ stngs[:value] = self.class.encryption_config.encrypt(value)
159
+ @encrypted_tree[stngs[:name]] = true
160
+ # If we're just marking an existing value as encrypted
161
+ elsif config_tree[stngs[:name]]
162
+ value = config_tree[stngs[:name]]
163
+ raise SettingTypeError.new('string (encrypted values must be strings)', value) unless value.is_a?(String)
164
+
165
+ stngs[:value] = self.class.encryption_config.encrypt(value)
166
+ @encrypted_tree[stngs[:name]] = true
167
+ end
168
+ end
169
+
170
+ validate_and_set_configuration(stngs, stng_lock, stng_type, stng_encrypted)
171
+ self
172
+ end
173
+
174
+ # Returns the type schema of the configuration.
175
+ #
176
+ # @return [Hash] A hash representing the type schema.
177
+ # @example Retrieving the type schema
178
+ # schema = settings.type_schema
179
+ def type_schema
180
+ {}.tap do |hsh|
181
+ config_tree.each do |k, v|
182
+ v.is_a?(HatiConfig::Setting) ? (hsh[k] = v.type_schema) : hsh.merge!(schema)
183
+ end
184
+ end
185
+ end
186
+
187
+ def lock_schema
188
+ {}.tap do |hsh|
189
+ config_tree.each do |k, v|
190
+ v.is_a?(HatiConfig::Setting) ? (hsh[k] = v.lock_schema) : hsh.merge!(immutable_schema)
191
+ end
192
+ end
193
+ end
194
+
195
+ # Converts the configuration tree into a hash.
196
+ #
197
+ # @return [Hash] The config tree as a hash.
198
+ # @example Converting to hash
199
+ # hash = settings.to_h
200
+ def to_h
201
+ {}.tap do |hsh|
202
+ config_tree.each do |k, v|
203
+ hsh[k] = if v.is_a?(HatiConfig::Setting)
204
+ v.to_h
205
+ else
206
+ get_value(k)
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ # Converts the configuration tree into YAML format.
213
+ #
214
+ # @param dump [String, nil] Optional file path to dump the YAML.
215
+ # @return [String, nil] The YAML string or nil if dumped to a file.
216
+ # @example Converting to YAML
217
+ # yaml_string = settings.to_yaml
218
+ # settings.to_yaml(dump: "config.yml") # Dumps to a file
219
+ def to_yaml(dump: nil)
220
+ yaml = to_h.to_yaml
221
+ dump ? File.write(dump, yaml) : yaml
222
+ end
223
+
224
+ # Converts the configuration tree into JSON format.
225
+ #
226
+ # @return [String] The JSON representation of the configuration tree.
227
+ # @example Converting to JSON
228
+ # json_string = settings.to_json
229
+ def to_json(*_args)
230
+ to_h.to_json
231
+ end
232
+
233
+ # Provides hash-like access to configuration values
234
+ #
235
+ # @param key [Symbol, String] The key to access
236
+ # @return [Object] The value associated with the key
237
+ def [](key)
238
+ key = key.to_sym if key.is_a?(String)
239
+ return get_value(key) if config_tree.key?(key)
240
+
241
+ raise NoMethodError, "undefined method `[]' with key #{key} for #{self.class}"
242
+ end
243
+
244
+ # Sets a configuration value using hash-like syntax
245
+ #
246
+ # @param key [Symbol, String] The key to set
247
+ # @param value [Object] The value to set
248
+ def []=(key, value)
249
+ key = key.to_sym if key.is_a?(String)
250
+ config(key => value)
251
+ end
252
+
253
+ protected
254
+
255
+ # @return [Hash] The schema of configuration types.
256
+ attr_reader :schema, :immutable_schema
257
+
258
+ private
259
+
260
+ # @return [Hash] The tree structure of configuration settings.
261
+ attr_reader :config_tree
262
+
263
+ # Creates a new node in the configuration tree.
264
+ #
265
+ # @param node [Symbol] The name of the node to create.
266
+ # @yield [Setting] A block to configure the new setting.
267
+ # @return [Setting] The newly created setting node.
268
+ def create_new_node(node, &block)
269
+ new_node = HatiConfig::Setting.new
270
+ if self.class.encryption_config.key_provider
271
+ new_node.class.encryption do
272
+ key_provider :env
273
+ end
274
+ end
275
+ new_node.instance_eval(&block) if block_given?
276
+ config_tree[node] = new_node
277
+ define_node_methods(node)
278
+ new_node
279
+ end
280
+
281
+ # Defines singleton methods for the given node.
282
+ #
283
+ # @param node [Symbol] The name of the node to define methods for.
284
+ def define_node_methods(node)
285
+ define_singleton_method(node) do |*_args, &node_block|
286
+ if node_block
287
+ config_tree[node].instance_eval(&node_block)
288
+ else
289
+ config_tree[node]
290
+ end
291
+ end
292
+ end
293
+
294
+ # Extracts the setting information from the provided input.
295
+ #
296
+ # @param stngs [Symbol, Hash] The setting name or a hash containing the setting name and value.
297
+ # @return [Hash] A hash containing the setting name and its corresponding value.
298
+ def extract_setting_info(stngs)
299
+ val = nil
300
+ lock = nil
301
+ encrypted = nil
302
+
303
+ if stngs.is_a?(Symbol)
304
+ name = stngs
305
+ elsif stngs.is_a?(Hash)
306
+ lock = stngs.delete(:lock)
307
+ encrypted = stngs.delete(:encrypted)
308
+ name, val = stngs.to_a.first
309
+ end
310
+
311
+ { name: name, value: val, lock: lock, encrypted: encrypted }
312
+ end
313
+
314
+ def handle_value(key, value, type, lock, encrypted = false)
315
+ validate_mutable!(key, lock) if lock
316
+ validate_setting!(value, type) if type
317
+
318
+ config(key => value, type: type, lock: lock, encrypted: encrypted)
319
+ self
320
+ end
321
+
322
+ def get_value(key)
323
+ value = config_tree[key]
324
+ return value if value.is_a?(HatiConfig::Setting)
325
+ return self.class.encryption_config.decrypt(value) if @encrypted_tree[key]
326
+
327
+ value
328
+ end
329
+
330
+ # Validates the setting value against the expected type.
331
+ #
332
+ # @param stng_val [Object] The value of the setting to validate.
333
+ # @param stng_type [Symbol] The expected type of the setting.
334
+ # @raise [SettingTypeError] If the setting value does not match the expected type.
335
+ def validate_setting!(stng_val, stng_type)
336
+ is_valid = HatiConfig::TypeChecker.call(stng_val, type: stng_type)
337
+ raise HatiConfig::SettingTypeError.new(stng_type, stng_val) unless !stng_val || is_valid
338
+ end
339
+
340
+ def validate_mutable!(name, stng_lock)
341
+ raise "<#{name}> setting is immutable" if stng_lock
342
+ end
343
+
344
+ # Sets the configuration for a given setting name, value, and type.
345
+ #
346
+ # @param stng_name [Symbol] The name of the setting to configure.
347
+ # @param stng_val [Object] The value to assign to the setting.
348
+ # @param stng_type [Symbol] The type of the setting.
349
+ def set_configuration(stng_name:, stng_val:, stng_type:, stng_lock:)
350
+ schema[stng_name] = stng_type
351
+ config_tree[stng_name] = stng_val
352
+ immutable_schema[stng_name] = stng_lock
353
+
354
+ return if respond_to?(stng_name)
355
+
356
+ define_singleton_method(stng_name) do |value = nil, type: nil, lock: nil, encrypted: false|
357
+ return get_value(stng_name) unless value || encrypted
358
+
359
+ if encrypted && !value.nil? && !value.is_a?(String)
360
+ raise SettingTypeError.new('string (encrypted values must be strings)', value)
361
+ end
362
+
363
+ config(**{ stng_name => value, type: type, lock: lock, encrypted: encrypted })
364
+ end
365
+ end
366
+
367
+ def determine_lock(stngs, lock)
368
+ lock.nil? ? stngs[:lock] : lock
369
+ end
370
+
371
+ def determine_type(stngs, type)
372
+ type || schema[stngs[:name]] || :any
373
+ end
374
+
375
+ def determine_encrypted(stngs, encrypted)
376
+ encrypted.nil? ? stngs[:encrypted] : encrypted
377
+ end
378
+
379
+ def validate_and_set_configuration(stngs, stng_lock, stng_type, stng_encrypted)
380
+ validate_mutable!(stngs[:name], stng_lock) if stngs[:value] && config_tree[stngs[:name]] && !stng_lock
381
+ validate_setting!(stngs[:value], stng_type)
382
+
383
+ set_configuration(stng_name: stngs[:name], stng_val: stngs[:value], stng_type: stng_type, stng_lock: stng_lock)
384
+ @encrypted_tree[stngs[:name]] = stng_encrypted if stng_encrypted
385
+ self
386
+ end
387
+ end
388
+ # rubocop:enable Metrics/ClassLength
389
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HatiConfig
4
+ # Team module provides functionality for managing team-specific configurations.
5
+ module Team
6
+ # Defines team-specific configuration namespace.
7
+ #
8
+ # @param team_name [Symbol] The name of the team (e.g., :frontend, :backend, :mobile)
9
+ # @yield The configuration block for the team
10
+ # @example
11
+ # team :frontend do
12
+ # configure :settings do
13
+ # config api_endpoint: '/api/v1'
14
+ # config cache_ttl: 300
15
+ # end
16
+ # end
17
+ def team(team_name, &block)
18
+ team_module = Module.new do
19
+ extend HatiConfig::Configuration
20
+ extend HatiConfig::Environment
21
+ end
22
+
23
+ const_name = team_name.to_s.capitalize
24
+ const_set(const_name, team_module)
25
+ team_module.instance_eval(&block) if block_given?
26
+
27
+ # Define method for accessing team module
28
+ singleton_class.class_eval do
29
+ define_method(team_name) { const_get(const_name) }
30
+ end
31
+
32
+ team_module
33
+ end
34
+
35
+ # Gets a list of all defined teams.
36
+ #
37
+ # @return [Array<Symbol>] The list of team names
38
+ def teams
39
+ constants.select { |c| const_get(c).is_a?(Module) && const_get(c).respond_to?(:configure) }
40
+ end
41
+
42
+ # Gets a specific team's configuration module.
43
+ #
44
+ # @param team_name [Symbol] The name of the team
45
+ # @return [Module] The team's configuration module
46
+ # @raise [NameError] If the team does not exist
47
+ def [](team_name)
48
+ const_get(team_name.to_s.capitalize)
49
+ end
50
+
51
+ # Checks if a team exists.
52
+ #
53
+ # @param team_name [Symbol] The name of the team
54
+ # @return [Boolean] True if the team exists
55
+ def team?(team_name)
56
+ const_defined?(team_name.to_s.capitalize)
57
+ end
58
+
59
+ # Removes a team's configuration.
60
+ #
61
+ # @param team_name [Symbol] The name of the team
62
+ # @return [Boolean] True if the team was removed
63
+ def remove_team?(team_name)
64
+ const_name = team_name.to_s.capitalize
65
+ return false unless const_defined?(const_name)
66
+
67
+ remove_const(const_name)
68
+ true
69
+ end
70
+
71
+ # Temporarily switches to a team's configuration context.
72
+ #
73
+ # @param team_name [Symbol] The name of the team
74
+ # @yield The block to execute in the team's context
75
+ # @example
76
+ # with_team(:frontend) do
77
+ # # Configuration will use frontend team's context here
78
+ # end
79
+ def with_team(team_name)
80
+ raise NameError, "Team '#{team_name}' does not exist" unless team?(team_name)
81
+
82
+ yield self[team_name]
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ # HatiConfig module provides functionality for managing HatiConfig features.
4
+ module HatiConfig
5
+ # This class is responsible for type checking in the Hati configuration.
6
+ class TypeChecker
7
+ class << self
8
+ # Calls the appropriate validation method based on the type.
9
+ #
10
+ # @param value [Object] The value to validate.
11
+ # @param type [Symbol, Array<Symbol, Class>, Class] The type(s) to validate against.
12
+ # @return [Boolean] True if the value matches the type, false otherwise.
13
+ # @raise [TypeCheckerError] if the type is unsupported or not defined.
14
+ #
15
+ # @example
16
+ # TypeChecker.call(1, type: :int) # => true
17
+ # TypeChecker.call(1, type: :numeric) # => true
18
+ # TypeChecker.call("hello", type: [:str, Integer]) # => true
19
+ # TypeChecker.call(CustomClass.new, type: CustomClass) # => true
20
+ def call(value, type:, **_opts)
21
+ case type
22
+ when Symbol
23
+ base_type(value, fetch_type(type))
24
+ when Array
25
+ if type.length == 1 && type.first.is_a?(Symbol)
26
+ # Array type validation (e.g., [:string] for array of strings)
27
+ return false unless value.is_a?(Array)
28
+
29
+ value.all? { |v| call(v, type: type.first) }
30
+ else
31
+ # Union type validation (e.g., [:string, Integer] for string or integer)
32
+ one_of(value, type)
33
+ end
34
+ else
35
+ custom_type?(value, type)
36
+ end
37
+ end
38
+
39
+ # Validates if value matches the base type.
40
+ #
41
+ # @param value [Object] the value to validate
42
+ # @param type [Class] the type to validate against
43
+ # @return [Boolean] true if the value matches the base type
44
+ # @raise [TypeCheckerError] if the type is unsupported or not defined.
45
+ #
46
+ # @example
47
+ # TypeChecker.base_type(1, Integer) # => true
48
+ # TypeChecker.base_type(1, [:int, :float, :big_decimal]) # => true
49
+ def base_type(value, type)
50
+ type = fetch_type(type) if type.is_a?(Symbol)
51
+
52
+ return type.call(value) if type.is_a?(Proc)
53
+
54
+ type.is_a?(Array) ? one_of(value, type) : value.is_a?(type)
55
+ end
56
+
57
+ # Checks if value matches any type in the array.
58
+ #
59
+ # @param value [Object] the value to validate
60
+ # @param array_type [Array<Symbol, Class>] the array of types to validate against
61
+ # @return [Boolean] true if the value matches any type in the array
62
+ #
63
+ # @example
64
+ # TypeChecker.one_of(1, [Integer, :str]) # => true
65
+ # TypeChecker.one_of("hello", [Integer, String, :sym]) # => true
66
+ # TypeChecker.one_of(nil, [:null, Integer, String]) # => true
67
+ # TypeChecker.one_of(1.5, [:int, :float, :big_decimal]) # => true
68
+ def one_of(value, array_type)
69
+ array_type.any? { |type| type.is_a?(Symbol) ? base_type(value, type) : custom_type?(value, type) }
70
+ end
71
+
72
+ # Validates if value is of the specified custom type.
73
+ #
74
+ # @param value [Object] the value to validate
75
+ # @param type [Class] the custom type to validate against
76
+ # @return [Boolean] true if the value is of the specified custom type
77
+ #
78
+ # @example
79
+ # TypeChecker.custom_type(1, Integer) # => true
80
+ # TypeChecker.custom_type(CustomClass.new, CustomClass) # => true
81
+ def custom_type?(value, type)
82
+ value.is_a?(type)
83
+ end
84
+
85
+ # Fetches the basic type from the type map.
86
+ #
87
+ # @param type [Symbol] the type to fetch
88
+ # @return [Class] the corresponding basic type
89
+ # @raise [TypeCheckerError] if the type does not exist in TYPE_MAP
90
+ #
91
+ # @example
92
+ # TypeChecker.fetch_type(:int) # => Integer
93
+ # TypeChecker.fetch_type(:null) # => NilClass
94
+ # TypeChecker.fetch_type(:bool) # => [:truthy, :falsy]
95
+ def fetch_type(type)
96
+ basic_type = TypeMap.get(type)
97
+ raise HatiConfig::TypeCheckerError, type unless basic_type
98
+
99
+ basic_type
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bigdecimal'
4
+ require 'date'
5
+
6
+ # HatiConfig module provides functionality for managing HatiConfig features.
7
+ module HatiConfig
8
+ # A class that maps type symbols to their corresponding Ruby classes and provides
9
+ # predefined collections of type symbols for various categories.
10
+ #
11
+ # This class allows retrieval of Ruby classes based on type symbols and provides
12
+ # methods to access collections of specific type symbols such as booleans, numerics,
13
+ # and chronological types.
14
+ class TypeMap
15
+ TYPE_MAP = {
16
+ # base types
17
+ int: Integer,
18
+ integer: Integer,
19
+ str: String,
20
+ string: String,
21
+ sym: Symbol,
22
+ null: NilClass,
23
+ true_class: TrueClass,
24
+ false_class: FalseClass,
25
+ # data structures
26
+ hash: Hash,
27
+ array: Array,
28
+ # numeric types
29
+ big_decimal: BigDecimal,
30
+ float: Float,
31
+ complex: Complex,
32
+ rational: Rational,
33
+ # time types
34
+ date: Date,
35
+ date_time: DateTime,
36
+ time: Time,
37
+ # any type
38
+ any: Object,
39
+ # composite types
40
+ bool: %i[true_class false_class],
41
+ numeric: %i[int float big_decimal],
42
+ kernel_num: %i[int float big_decimal complex rational],
43
+ chrono: %i[date date_time time]
44
+ }.freeze
45
+
46
+ # Retrieves the Ruby class associated with a given type symbol.
47
+ #
48
+ # @param type [Symbol] The type symbol to look up. Must be one of the keys in TYPE_MAP.
49
+ # @return [Class, nil] The corresponding Ruby class or nil if the type symbol is not found.
50
+ #
51
+ # @example
52
+ # TypeMap.get(:int) # => Integer
53
+ # TypeMap.get(:str) # => String
54
+ # TypeMap.get(:unknown) # => nil
55
+ def self.get(type)
56
+ TYPE_MAP[type]
57
+ end
58
+
59
+ # Returns an array of all type symbols defined in TYPE_MAP.
60
+ #
61
+ # @return [Array<Symbol>] An array containing all keys from TYPE_MAP.
62
+ #
63
+ # @example
64
+ # TypeMap.list_types # => [:int, :str, :sym, :null, :true_class, :false_class,
65
+ # :hash, :array, :big_decimal, :float, :complex,
66
+ # :rational, :date, :date_time, :time, :any,
67
+ # :bool, :numeric, :kernel_num, :chrono]
68
+ def self.list_types
69
+ TYPE_MAP.keys
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module HatiConfig
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'hati_config/version'
4
+ require 'hati_config/errors'
5
+ require 'hati_config/type_map'
6
+ require 'hati_config/type_checker'
7
+ require 'hati_config/remote_loader'
8
+ require 'hati_config/environment'
9
+ require 'hati_config/team'
10
+ require 'hati_config/schema'
11
+ require 'hati_config/cache'
12
+ require 'hati_config/encryption'
13
+ require 'hati_config/setting'
14
+ require 'hati_config/configuration'
15
+ require 'hati_config/hati_configuration'