knowledge 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Knowledge
4
+ #
5
+ # === Description ===
6
+ #
7
+ # This error class should be considered as the lib's standard error.
8
+ # All error classes inherit from this one.
9
+ # The goal is to be able to rescue Knowledge::Error outside the lib and catch them all.
10
+ #
11
+ # === Usage ===
12
+ #
13
+ # You have many examples behind. Just inherit from this class and it's ok.
14
+ #
15
+ # @example:
16
+ # class ::MyCustomKnowledgeError < Knowledge::Error; end
17
+ #
18
+ class Error < ::StandardError; end
19
+
20
+ #
21
+ # === Description ===
22
+ #
23
+ # This error is used when, at some point, we can't find the adapter we're looking for
24
+ #
25
+ class AdapterNotFound < Error; end
26
+
27
+ #
28
+ # === Description ===
29
+ #
30
+ # This error is used when an adapter has no #run method declared
31
+ #
32
+ class AdapterRunMethodNotImplemented < Error; end
33
+
34
+ #
35
+ # === Description ===
36
+ #
37
+ # This error is pretty generic and is meant to be used when we're not able to gather infos in order to set vars.
38
+ #
39
+ class LearnError < Error; end
40
+
41
+ #
42
+ # === Description ===
43
+ #
44
+ # This error is used when we fail registering an adapter.
45
+ #
46
+ class RegisterError < Error; end
47
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Knowledge
4
+ #
5
+ # === Description ===
6
+ #
7
+ # We all need an initializer, here's this lib's initializer.
8
+ # Its role is to gather all informations and run the enabled adapters.
9
+ #
10
+ # === Usage ===
11
+ #
12
+ # @example:
13
+ # Knowledge::Initializer.new(adapters: adapters, params: { foo: :bar }, setter: setter, variables: variables).run
14
+ #
15
+ # Knowledge::Initializer.run(adapters: adapters, params: { foo: :bar }, setter: setter, variables: variables)
16
+ #
17
+ # === Attributes ===
18
+ #
19
+ # @attr_reader [Array<String | Symbol>] adapters
20
+ # @attr_reader [Hash] params
21
+ # @attr_reader [Class] setter
22
+ # @attr_reader [Hash] variables
23
+ #
24
+ class Initializer
25
+ # == Attributes ==================================================================================================
26
+ attr_reader :adapters, :params, :setter, :variables
27
+
28
+ # == Constructor =================================================================================================
29
+ #
30
+ # @option [Array<Class>] :adapters
31
+ # @option [Hash] :params
32
+ # @option [Class] :setter
33
+ # @option [Hash] :variables
34
+ #
35
+ def initialize(adapters:, params:, setter:, variables:)
36
+ @adapters = adapters
37
+ @params = params
38
+ @setter = setter
39
+ @variables = variables
40
+ end
41
+
42
+ # == Class methods ===============================================================================================
43
+ class << self
44
+ #
45
+ # === Description ===
46
+ #
47
+ # Instanciates the current class and runs all registered adapters.
48
+ #
49
+ # === Parameters ===
50
+ #
51
+ # @option [Hash{Symbol => Class}] :adapters
52
+ # @option [Hash] :params
53
+ # @option [Class] :setter
54
+ # @option [Hash] :variables
55
+ #
56
+ def run(adapters:, params:, setter:, variables: {})
57
+ new(adapters: adapters, params: params, setter: setter, variables: variables).run
58
+ end
59
+ end
60
+
61
+ # == Instance methods ============================================================================================
62
+ #
63
+ # === Description ===
64
+ #
65
+ # Runs all registered adapters.
66
+ #
67
+ def run
68
+ Hash(adapters).each do |name, adapter|
69
+ adapter.new(
70
+ params: params[name.to_sym] || params,
71
+ setter: setter,
72
+ variables: variables[name.to_sym] || variables
73
+ ).run
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,381 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ require 'knowledge/initializer'
6
+ require 'knowledge/adapters'
7
+ require 'knowledge/setter'
8
+
9
+ module Knowledge
10
+ #
11
+ # === Description ===
12
+ #
13
+ # Knowledge is something that needs some learning.
14
+ #
15
+ # === Usage ===
16
+ #
17
+ # @example:
18
+ #
19
+ # learner = Knowledge::Learner.new
20
+ #
21
+ # === Attributes ===
22
+ #
23
+ # @attr_reader [Hash] additionnal_params
24
+ # @attr_reader [Hash{Symbol => Class}] available_adapters
25
+ # @attr_reader [Hash{Symbol => Class}] enabled_adapters
26
+ # @attr [Class] setter
27
+ # @attr_reader [Hash] variables
28
+ #
29
+ class Learner
30
+ # == Attributes ====================================================================================================
31
+ attr_accessor :setter
32
+ attr_reader :additionnal_params, :available_adapters, :enabled_adapters, :variables
33
+
34
+ # == Constructor ===================================================================================================
35
+ def initialize
36
+ @additionnal_params = {}
37
+ @available_adapters = {}
38
+ @enabled_adapters = {}
39
+ @setter = ::Knowledge::Setter.new
40
+ @variables = {}
41
+ end
42
+
43
+ # == Class methods =================================================================================================
44
+ class << self
45
+ attr_reader :adapters
46
+
47
+ def register_default_adapter(names:, klass:)
48
+ @adapters ||= {}
49
+
50
+ names.each { |name| @adapters[name.to_sym] = klass }
51
+ end
52
+ end
53
+
54
+ # == Instance methods ==============================================================================================
55
+ #
56
+ # === Description ===
57
+ #
58
+ # Gathers all the knowledge and put it into your app.
59
+ #
60
+ # === Usage ===
61
+ #
62
+ # @example:
63
+ # learner = Knowledge::Learner.new
64
+ # # Do some config (add adapters, define your setter, etc.)
65
+ # learner.gather!
66
+ #
67
+ def gather!
68
+ ::Knowledge::Initializer.new(
69
+ adapters: enabled_adapters,
70
+ params: additionnal_params,
71
+ setter: setter,
72
+ variables: variables
73
+ ).run
74
+ end
75
+
76
+ # == Adapters methods ============================================================================================
77
+ #
78
+ # === Description ===
79
+ #
80
+ # Sets additional params to be passed to the adapter through params option.
81
+ #
82
+ # === Usage ===
83
+ #
84
+ # @example:
85
+ # learner = Knowledge::Learner.new
86
+ # learner.add_adapter_param(adapter: :custom, name: :base_path, value: '/base/path')
87
+ #
88
+ # === Parameters ===
89
+ #
90
+ # @option [String | Symbol] :adapter
91
+ # @option [String | Symbol] :name
92
+ # @option [any] :value
93
+ #
94
+ def add_adapter_param(adapter:, name:, value:)
95
+ @additionnal_params[adapter.to_sym] ||= {}
96
+ @additionnal_params[adapter.to_sym][name] = value
97
+ end
98
+
99
+ #
100
+ # === Description ===
101
+ #
102
+ # Sets additional params to be passed to the adapter through params option.
103
+ #
104
+ # === Usage ===
105
+ #
106
+ # @example:
107
+ # learner = Knowledge::Learner.new
108
+ # learner.add_adapter_param(name: :base_path, value: '/base/path')
109
+ #
110
+ # === Parameters ===
111
+ #
112
+ # @option [String | Symbol] :adapter
113
+ # @option [any] :params
114
+ #
115
+ def add_adapter_params(adapter:, params:)
116
+ @additionnal_params[adapter.to_sym] = params
117
+ end
118
+
119
+ #
120
+ # === Description ===
121
+ #
122
+ # Disables an adapter.
123
+ #
124
+ # === Usage ===
125
+ #
126
+ # @example:
127
+ # learner = Knowledge::Learner.new
128
+ # learner.register_adapter(name: :my_adapter, klass: MyAdapter, enable: true)
129
+ # # Somewhere else in the code
130
+ # learner.disable_adapter(name: :my_adapter) if should_disable_custom_adapter?
131
+ #
132
+ # === Parameters ===
133
+ #
134
+ # @option [String | Symbol] :name
135
+ #
136
+ def disable_adapter(name:)
137
+ @enabled_adapters.delete(name.to_sym)
138
+ end
139
+
140
+ #
141
+ # === Description ===
142
+ #
143
+ # Enables an adapter.
144
+ #
145
+ # === Usage ===
146
+ #
147
+ # @example:
148
+ # learner = Knowledge::Learner.new
149
+ # learner.register_adapter(name: :my_adapter, klass: MyAdapter)
150
+ # learner.enable_adapter(name: :my_adapter)
151
+ #
152
+ # === Errors ===
153
+ #
154
+ # @raises [Knowledge::AdapterNotFound] if adapter is not available
155
+ #
156
+ # === Parameters ===
157
+ #
158
+ # @option [String | Symbol] :name
159
+ # @option [Hash | String | nil] :variables
160
+ #
161
+ def enable_adapter(name:, variables: nil)
162
+ _key, klass = available_adapters.find { |key, _klass| key.to_sym == name.to_sym }
163
+
164
+ raise Knowledge::AdapterNotFound, "Cannot find \"#{name}\" in available adapters" if klass.nil?
165
+
166
+ @enabled_adapters[name.to_sym] = klass
167
+ set_adapter_variables(name: name, variables: variables)
168
+ end
169
+
170
+ #
171
+ # === Description ===
172
+ #
173
+ # Registers an adapter and enable it if asked.
174
+ #
175
+ # === Usage ===
176
+ #
177
+ # @example:
178
+ # learner = Knowledge::Learner.new
179
+ # learner.register_adapter(name: :my_adapter, klass: MyAdapter, enable: true)
180
+ #
181
+ # === Parameters ===
182
+ #
183
+ # @option [String | Symbol] :name
184
+ # @option [Class] :klass
185
+ # @option [Boolean] :enable
186
+ # @option [Hash | String | nil] :variables
187
+ #
188
+ def register_adapter(name:, klass:, enable: false, variables: nil)
189
+ @available_adapters[name.to_sym] = klass
190
+ enable_adapter(name: name) if enable
191
+ set_adapter_variables(name: name, variables: variables)
192
+ end
193
+
194
+ #
195
+ # === Description ===
196
+ #
197
+ # Sets variables for a given adapter
198
+ #
199
+ # === Usage ===
200
+ #
201
+ # @example:
202
+ # learner = Knowledge::Learner.new
203
+ # learner.set_adapter_variables(name: :default, variables: { foo: :bar })
204
+ #
205
+ # === Attributes ===
206
+ #
207
+ # @option [String | Symbol] :name
208
+ # @option [Hash | nil] :variables
209
+ #
210
+ def set_adapter_variables(name:, variables: nil)
211
+ return unless variables
212
+
213
+ case variables
214
+ when Hash
215
+ set_adapter_variables_by_hash(name: name, variables: variables)
216
+ when String
217
+ set_adapter_variables(name: name, variables: yaml_content(variables))
218
+ else
219
+ raise "Unknown variables type #{variables.class}"
220
+ end
221
+ rescue StandardError => e
222
+ raise ::Knowledge::LearnError, e.message
223
+ end
224
+
225
+ #
226
+ # === Description ===
227
+ #
228
+ # Sets variables as a hash for a given adapter
229
+ #
230
+ # === Usage ===
231
+ #
232
+ # @example
233
+ # learner = Knowledge::Learner.new
234
+ # learner.set_adapter_variables_by_hash(name: :default, variables: { foo: :bar })
235
+ #
236
+ # === Attributes ===
237
+ #
238
+ # @option [String | Symbol] :name
239
+ # @option [Hash] :variables
240
+ #
241
+ def set_adapter_variables_by_hash(name:, variables:)
242
+ variables = variables[name.to_s] if variables.key?(name.to_s)
243
+ variables = variables[name.to_sym] if variables.key?(name.to_sym)
244
+ @variables[name.to_sym] = variables
245
+ end
246
+
247
+ #
248
+ # === Description ===
249
+ #
250
+ # Unregisters an adapter and disable it.
251
+ #
252
+ # === Usage ===
253
+ #
254
+ # @example:
255
+ # learner = Knowledge::Learner.new
256
+ # learner.register_adapter(name: :my_adapter, klass: MyAdapter)
257
+ # # somewhere else in the code
258
+ # learner.unregister_adapter(name: :my_adapter)
259
+ #
260
+ # === Parameters ===
261
+ #
262
+ # @option [String | Symbol] :name
263
+ #
264
+ def unregister_adapter(name:)
265
+ disable_adapter(name: name)
266
+ @available_adapters.delete(name.to_sym)
267
+ end
268
+
269
+ #
270
+ # === Description ===
271
+ #
272
+ # Registers & enables one of the lib's adapters.
273
+ #
274
+ # === Usage ===
275
+ #
276
+ # @example:
277
+ # learner = Knowledge::Learner.new
278
+ # learner.use(name: :ssm)
279
+ #
280
+ # === Parameters ===
281
+ #
282
+ # @option [String | Symbol] name
283
+ # @option [Boolean] enable
284
+ #
285
+ def use(name:, enable: true)
286
+ adapter = self.class.adapters[name.to_sym]
287
+
288
+ raise ::Knowledge::RegisterError, "Unable to register following: #{name}" if adapter.nil?
289
+
290
+ register_adapter(name: name.to_sym, klass: adapter, enable: enable)
291
+ end
292
+
293
+ # == Variables config ==============================================================================================
294
+ #
295
+ # === Description ===
296
+ #
297
+ # Setter for the variables config.
298
+ #
299
+ # === Usage ===
300
+ #
301
+ # @example:
302
+ # learner = Knowledge::Learner.new
303
+ # learner.variables = { name: 'value' }
304
+ #
305
+ # @example:
306
+ # learner = Knowledge::Learner.new
307
+ # learner.use(name: :env)
308
+ # learner.variables = { name: 'ENV_KEY' }
309
+ #
310
+ # @example:
311
+ # learner = Knowledge::Learner.new
312
+ # learner.variables = 'path/to/vars/config/file.yml'
313
+ #
314
+ # === Errors ===
315
+ #
316
+ # @raise [Knowledge::LearnError] if parameter isn't a hash or a string
317
+ #
318
+ # === Parameters
319
+ #
320
+ # @param [String | Hash] path_or_descriptor
321
+ #
322
+ def variables=(path_or_descriptor)
323
+ case path_or_descriptor
324
+ when Hash
325
+ @variables = path_or_descriptor
326
+ when String
327
+ fetch_variables_config(path_or_descriptor)
328
+ else
329
+ raise ::Knowledge::LearnError, "Unable to understand following path or descriptor: #{path_or_descriptor}"
330
+ end
331
+ end
332
+
333
+ protected
334
+
335
+ #
336
+ # === Description ===
337
+ #
338
+ # Opens the config file and sets the variable config.
339
+ #
340
+ # === Parameters ===
341
+ #
342
+ # @param [String] path
343
+ #
344
+ def fetch_variables_config(path)
345
+ descriptor = yaml_content(path)
346
+ @variables = descriptor[::Knowledge.config.environment.to_s] || descriptor
347
+ end
348
+
349
+ #
350
+ # === Description ===
351
+ #
352
+ # Loads YAML file content
353
+ #
354
+ # === Parameters ===
355
+ #
356
+ # @param [String] path
357
+ #
358
+ # @return [Hash]
359
+ #
360
+ def yaml_content(path)
361
+ ::YAML.safe_load(::File.open(path))
362
+ end
363
+ end
364
+ end
365
+
366
+ # Registering default adapters
367
+
368
+ Knowledge::Learner.register_default_adapter(
369
+ klass: Knowledge::Adapters::Environment,
370
+ names: %i[env environment env_vars]
371
+ )
372
+
373
+ Knowledge::Learner.register_default_adapter(
374
+ klass: Knowledge::Adapters::KeyValue,
375
+ names: %i[default keyval key_value]
376
+ )
377
+
378
+ Knowledge::Learner.register_default_adapter(
379
+ klass: Knowledge::Adapters::File,
380
+ names: %i[config file config_file]
381
+ )