etcher 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/README.adoc +25 -25
- data/etcher.gemspec +1 -1
- data/lib/etcher/builder.rb +12 -15
- data/lib/etcher/contract.rb +3 -3
- data/lib/etcher/resolver.rb +1 -1
- data.tar.gz.sig +0 -0
- metadata +3 -3
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4a7381f41e75ad83f44a491f33f396be3ffb5b401a1a72fdcef2086984424fff
|
4
|
+
data.tar.gz: '09b9c86d96af82fbc821e23c92e7966c97234e61625d3d23838471b930906629'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3d3008b4581dfd66426ba9e9c2ed0158241d130c2e68372f124369dd42c5893fed86aff36fe9e1b14612fff2139f39cf7e4b07492b5faac13699a3f0d87f609c
|
7
|
+
data.tar.gz: 885d3f522cefba39eeb8765fe671ea822d52edefc700d1633997825fd7cb61c834db7d6111cbd92881c5c9d232519e70bb4aa120027cc6ad432e82286d15fb99
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/README.adoc
CHANGED
@@ -28,7 +28,7 @@ ____
|
|
28
28
|
[Use] strong acid or mordant to cut into the unprotected parts of a metal surface to create a design in intaglio (incised) in the metal.
|
29
29
|
____
|
30
30
|
|
31
|
-
By using Etcher, you have a reliable way to load default configurations (i.e. {environment_link}, {json_link}, {yaml_link}) which can be validated and etched into _frozen_ records (i.e. {hash_link}, {data_link}, {struct_link}) for consumption within your application which doesn't violate the {demeter_link}. This comes complete with transformations and validations all via a simple Object API
|
31
|
+
By using Etcher, you have a reliable way to load default configurations (i.e. {environment_link}, {json_link}, {yaml_link}) which can be validated and etched into _frozen_ records (i.e. {hash_link}, {data_link}, {struct_link}) for consumption within your application which doesn't violate the {demeter_link}. This comes complete with transformations and validations all via a simple Object API and pairs well with the {xdg_link}, {runcom_link}, and {sod_link} gems.
|
32
32
|
|
33
33
|
toc::[]
|
34
34
|
|
@@ -106,8 +106,8 @@ end
|
|
106
106
|
|
107
107
|
model = Data.define :user, :home
|
108
108
|
|
109
|
-
transformer = lambda do |
|
110
|
-
Dry::Monads::Success
|
109
|
+
transformer = lambda do |attributes, key = :user|
|
110
|
+
Dry::Monads::Success attributes.merge! key => attributes[key].upcase
|
111
111
|
end
|
112
112
|
|
113
113
|
Etcher::Registry.new(contract:, model:, transformers: [transformer])
|
@@ -122,7 +122,7 @@ The above can be broken down into a series of steps:
|
|
122
122
|
. A {dry_schema_link} contract -- loaded with {dry_monads_link} extensions -- is created to verify untrusted attributes.
|
123
123
|
. A model is created with attributes: `user` and `home`.
|
124
124
|
. A registry is constructed with a custom contract, model, loader, and transformer.
|
125
|
-
. Finally, we see a _successfully_ built configuration for further use.
|
125
|
+
. Finally, we see a _successfully_ built configuration for further use within your application.
|
126
126
|
|
127
127
|
While this is a more advanced use case, you'll usually only need to register a contract and model. The loaders and transformers provide additional firepower in situations where you need to do more with your data. We'll look at each of these components in greater detail next.
|
128
128
|
|
@@ -132,11 +132,11 @@ While this is a more advanced use case, you'll usually only need to register a c
|
|
132
132
|
|
133
133
|
As hinted at above, the complete sequence of steps are performed in the order listed:
|
134
134
|
|
135
|
-
. *Load*: Each loader, if any, is called and merged with the previous loader to build initial
|
136
|
-
. *Override*: Any overrides are merged with the result of the last loader to produce updated
|
137
|
-
. *Transform*: Each transformer, if any, is called to transform and manipulate the
|
138
|
-
. *Validate*: The contract is called to validate the
|
139
|
-
. *Model*: The model consumes the
|
135
|
+
. *Load*: Each loader, if any, is called and merged with the previous loader to build initial attributes.
|
136
|
+
. *Override*: Any overrides are merged with the result of the last loader to produce updated attributes. ⚠️ _In Version 2.0.0, this step will happen after the Transform step._
|
137
|
+
. *Transform*: Each transformer, if any, is called to transform and manipulate the attributes.
|
138
|
+
. *Validate*: The contract is called to validate the attributes as previously loaded, overwritten, and transformed.
|
139
|
+
. *Model*: The model consumes the attributes of the validated contract and creates a new record for you to use as needed.
|
140
140
|
|
141
141
|
You can use the above steps as a reference when using this gem. Each step is explained in greater detail below.
|
142
142
|
|
@@ -150,7 +150,7 @@ Etcher::Registry.new
|
|
150
150
|
# #<data Etcher::Registry contract=#<Proc:0x000000010e393550 contract.rb:7 (lambda)>, model=Hash, loaders=[], transformers=[]>
|
151
151
|
----
|
152
152
|
|
153
|
-
Since the registry is
|
153
|
+
Since the registry is {data_link}, you can initialize with everything you need:
|
154
154
|
|
155
155
|
[source,ruby]
|
156
156
|
----
|
@@ -175,11 +175,11 @@ registry = Etcher::Registry.new
|
|
175
175
|
|
176
176
|
=== Contracts
|
177
177
|
|
178
|
-
Contracts are critical piece of this workflow as they provide a way to validate incoming data, remove unwanted data, and create a sanitized record for use in your application. Any contract that has the following behavior will work:
|
178
|
+
Contracts are a critical piece of this workflow as they provide a way to validate incoming data, remove unwanted data, and create a sanitized record for use in your application. Any contract that has the following behavior will work:
|
179
179
|
|
180
180
|
* `#call`: Must be able to consume a {hash_link} and answer an object which can respond to `#to_monad`.
|
181
181
|
|
182
|
-
The best gems which adhere to this interface are: {dry_schema_link} and {dry_validation_link}. You'll also want to make sure the {dry_monads_link} extensions are loaded as briefly shown earlier so the result will respond to `#to_monad`. Here's how to enable monad support if using both gems:
|
182
|
+
The best gems which adhere to this interface are: {dry_schema_link} and {dry_validation_link}. You'll also want to make sure the {dry_monads_link} extensions are loaded, as briefly shown earlier, so the result will respond to `#to_monad`. Here's how to enable monad support if using both gems:
|
183
183
|
|
184
184
|
[source,ruby]
|
185
185
|
----
|
@@ -209,7 +209,7 @@ etcher.call from: "Mork", to: "Mindy"
|
|
209
209
|
# Success({:from=>"Mork", :to=>"Mindy"})
|
210
210
|
----
|
211
211
|
|
212
|
-
Here you can see the power of using a contract to validate your data both as a failure and a success. Unfortunately, with the success, we only get a {hash_link} as a record and it would be nice to structured
|
212
|
+
Here you can see the power of using a contract to validate your data both as a failure and a success. Unfortunately, with the success, we only get a {hash_link} as a record and it would be nice to structured record which will be explained shortly.
|
213
213
|
|
214
214
|
=== Types
|
215
215
|
|
@@ -267,7 +267,7 @@ model = Data.define :from, :to
|
|
267
267
|
etcher = Etcher::Registry[model:].then { |registry| Etcher.new registry }
|
268
268
|
|
269
269
|
etcher.call
|
270
|
-
# Failure({:step=>:
|
270
|
+
# Failure({:step=>:model, :payload=>"Missing keywords: :from, :to."})
|
271
271
|
|
272
272
|
etcher.call from: "Mork", to: "Mindy"
|
273
273
|
# Success(#<data Model from="Mork", to="Mindy">)
|
@@ -344,7 +344,7 @@ loader = Etcher::Loaders::JSON.new "your/path/to/configuration.json",
|
|
344
344
|
loader.call # Success({})
|
345
345
|
----
|
346
346
|
|
347
|
-
|
347
|
+
Otherwise, if the file exists with content, you'll get a `Hash` wrapped as a `Success`.
|
348
348
|
|
349
349
|
ℹ️ The logger is only used to log debug information when issues are encountered when reading from the file. This is done to reduce noise in your console when a configuration might have issues and can safely revert to the fallback in order to load the rest of the configuration.
|
350
350
|
|
@@ -369,7 +369,7 @@ loader = Etcher::Loaders::YAML.new "your/path/to/configuration.yml",
|
|
369
369
|
loader.call # Success({})
|
370
370
|
----
|
371
371
|
|
372
|
-
|
372
|
+
Otherwise, if the file exists with content, you'll get a `Hash` wrapped as a `Success`.
|
373
373
|
|
374
374
|
ℹ️ The logger is only used to log debug information when issues are encountered when reading from the file. This is done to reduce noise in your console when a configuration might have issues and can safely revert to the fallback in order to load the rest of the configuration.
|
375
375
|
|
@@ -417,9 +417,9 @@ registry = Etcher::Registry.new.add_transformer MyTransformer
|
|
417
417
|
Here are a few guidelines to using them:
|
418
418
|
|
419
419
|
* They can be initialized with whatever requirements you might need.
|
420
|
-
* They must respond to `#call` which takes a required `
|
421
|
-
* A second _optional_ positional `key` parameter should follow your `
|
422
|
-
* The `
|
420
|
+
* They must respond to `#call` which takes a required `attributes` positional argument and answers a modified version of these attributes (`Hash`) wrapped as a monad.
|
421
|
+
* A second _optional_ positional `key` parameter should follow your `attributes` parameter when implementing your transformer. This allows you to quickly refactor the key later while also reducing key duplication throughout your implementation.
|
422
|
+
* The `attributes` passed to your transformer will have symbolized keys so you don't need to transform them further.
|
423
423
|
|
424
424
|
Here are a few examples of where you could go with this:
|
425
425
|
|
@@ -429,7 +429,7 @@ The following capitalizes all values (which may or may not be good depending on
|
|
429
429
|
----
|
430
430
|
require "dry/monads"
|
431
431
|
|
432
|
-
Capitalize = ->
|
432
|
+
Capitalize = -> attributes { Dry::Monads::Success attributes.transform_values!(&:capitalize) }
|
433
433
|
Capitalize.call(name: "test")
|
434
434
|
|
435
435
|
# Success({:name=>"Test"})
|
@@ -441,9 +441,9 @@ The following updates current time relative to when configuration was transforme
|
|
441
441
|
----
|
442
442
|
require "dry/monads"
|
443
443
|
|
444
|
-
CurrentTime = lambda do |
|
445
|
-
|
446
|
-
|
444
|
+
CurrentTime = lambda do |attributes, key = :at, at: Time.now|
|
445
|
+
attributes.fetch(key) { at }
|
446
|
+
.then { |value| Dry::Monads::Success attributes.merge!(key => value) }
|
447
447
|
end
|
448
448
|
|
449
449
|
CurrentTime.call({})
|
@@ -469,7 +469,7 @@ class GitEmail
|
|
469
469
|
@git = git
|
470
470
|
end
|
471
471
|
|
472
|
-
def call(
|
472
|
+
def call(attributes) = git.get("user.email").fmap { |value| attributes[key] = value }
|
473
473
|
|
474
474
|
private
|
475
475
|
|
@@ -516,7 +516,7 @@ Etcher.call name: "demo"
|
|
516
516
|
# {:name=>"demo"}
|
517
517
|
----
|
518
518
|
|
519
|
-
When called
|
519
|
+
When called -- and there are no issues -- you'll get the fully formed record as a result (in this case a Hash which is the default model). You'll never a get a monad when using `Etcher.call` because this is meant to resolve the monadic pipeline for you. If any failure is encountered, then Etcher will _abort_ with a fatal log message. Here's a variation of earlier examples which demonstrates fatals:
|
520
520
|
|
521
521
|
[source,ruby]
|
522
522
|
----
|
data/etcher.gemspec
CHANGED
data/lib/etcher/builder.rb
CHANGED
@@ -16,42 +16,39 @@ module Etcher
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def call(**overrides)
|
19
|
-
load(overrides.symbolize_keys!).then { |
|
20
|
-
.bind { |
|
21
|
-
.bind { |
|
19
|
+
load(overrides.symbolize_keys!).then { |attributes| transform attributes }
|
20
|
+
.bind { |attributes| validate attributes }
|
21
|
+
.bind { |attributes| model attributes }
|
22
22
|
end
|
23
23
|
|
24
24
|
private
|
25
25
|
|
26
26
|
attr_reader :registry
|
27
27
|
|
28
|
-
# :reek:NestedIterators
|
29
|
-
# :reek:TooManyStatements
|
30
28
|
def load overrides
|
31
29
|
registry.loaders
|
32
|
-
.map { |loader| loader.call.fmap { |
|
30
|
+
.map { |loader| loader.call.fmap { |pairs| pairs.flatten_keys.symbolize_keys! } }
|
33
31
|
.each
|
34
|
-
.with_object({}) { |
|
32
|
+
.with_object({}) { |attributes, all| attributes.bind { |body| all.merge! body } }
|
35
33
|
.merge!(overrides.flatten_keys)
|
36
|
-
.then { |
|
34
|
+
.then { |attributes| Success attributes }
|
37
35
|
end
|
38
36
|
|
39
|
-
|
40
|
-
|
41
|
-
registry.transformers.reduce content do |all, transformer|
|
37
|
+
def transform attributes
|
38
|
+
registry.transformers.reduce attributes do |all, transformer|
|
42
39
|
all.bind { |body| transformer.call body }
|
43
40
|
end
|
44
41
|
end
|
45
42
|
|
46
|
-
def validate
|
43
|
+
def validate attributes
|
47
44
|
registry.contract
|
48
|
-
.call(
|
45
|
+
.call(attributes)
|
49
46
|
.to_monad
|
50
47
|
.or { |result| Failure step: __method__, payload: result.errors.to_h }
|
51
48
|
end
|
52
49
|
|
53
|
-
def
|
54
|
-
Success registry.model[**
|
50
|
+
def model attributes
|
51
|
+
Success registry.model[**attributes.to_h].freeze
|
55
52
|
rescue ArgumentError => error
|
56
53
|
Failure step: __method__, payload: "#{error.message.capitalize}."
|
57
54
|
end
|
data/lib/etcher/contract.rb
CHANGED
@@ -4,8 +4,8 @@ require "dry/monads"
|
|
4
4
|
|
5
5
|
module Etcher
|
6
6
|
# A simple passthrough contract.
|
7
|
-
Contract = lambda do |
|
8
|
-
def
|
9
|
-
|
7
|
+
Contract = lambda do |result|
|
8
|
+
def result.to_monad = Dry::Monads::Success self unless result.respond_to? :to_monad
|
9
|
+
result
|
10
10
|
end
|
11
11
|
end
|
data/lib/etcher/resolver.rb
CHANGED
@@ -18,7 +18,7 @@ module Etcher
|
|
18
18
|
|
19
19
|
def call(**overrides)
|
20
20
|
case builder.call(**overrides)
|
21
|
-
in Success(
|
21
|
+
in Success(attributes) then attributes
|
22
22
|
in Failure(step:, payload: String => payload)
|
23
23
|
logger.fatal { "Build failure: #{step.inspect}. #{payload}" }
|
24
24
|
kernel.abort
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: etcher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brooke Kuhlmann
|
@@ -35,7 +35,7 @@ cert_chain:
|
|
35
35
|
3n5C8/6Zh9DYTkpcwPSuIfAga6wf4nXc9m6JAw8AuMLaiWN/r/2s4zJsUHYERJEu
|
36
36
|
gZGm4JqtuSg8pYjPeIJxS960owq+SfuC+jxqmRA54BisFCv/0VOJi7tiJVY=
|
37
37
|
-----END CERTIFICATE-----
|
38
|
-
date: 2024-
|
38
|
+
date: 2024-05-11 00:00:00.000000000 Z
|
39
39
|
dependencies:
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: cogger
|
@@ -182,7 +182,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
182
182
|
- !ruby/object:Gem::Version
|
183
183
|
version: '0'
|
184
184
|
requirements: []
|
185
|
-
rubygems_version: 3.5.
|
185
|
+
rubygems_version: 3.5.10
|
186
186
|
signing_key:
|
187
187
|
specification_version: 4
|
188
188
|
summary: A monadic configuration loader, transformer, and validator.
|
metadata.gz.sig
CHANGED
Binary file
|