etcher 1.2.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16f4335440726392076ca31e69cd180b72ba62a0251f53df1d03360a4e457c3a
4
- data.tar.gz: 0c471d6980811751707ee8f1db95b3a8d8c58c29b3a486138e9608d4649f1f9f
3
+ metadata.gz: 4a7381f41e75ad83f44a491f33f396be3ffb5b401a1a72fdcef2086984424fff
4
+ data.tar.gz: '09b9c86d96af82fbc821e23c92e7966c97234e61625d3d23838471b930906629'
5
5
  SHA512:
6
- metadata.gz: d99e98d45fca9aef28ebda93b18a19ee5d29a03b723377a5ae17aa6bc37af096b1760b8c9a025347f41ee88f659017e451c9d1c414bdca2b1c29a7b2ffb61392
7
- data.tar.gz: 85af46afc0a618d7ea121743e7868d1a5f2b0a186fd0efcb26aefbd2ec7c75aa4ca2fcd87d4737476ec17f996321432a53a355cb9a115c6e13974fddd15ac189
6
+ metadata.gz: 3d3008b4581dfd66426ba9e9c2ed0158241d130c2e68372f124369dd42c5893fed86aff36fe9e1b14612fff2139f39cf7e4b07492b5faac13699a3f0d87f609c
7
+ data.tar.gz: 885d3f522cefba39eeb8765fe671ea822d52edefc700d1633997825fd7cb61c834db7d6111cbd92881c5c9d232519e70bb4aa120027cc6ad432e82286d15fb99
checksums.yaml.gz.sig CHANGED
Binary file
data/README.adoc CHANGED
@@ -4,7 +4,6 @@
4
4
 
5
5
  :data_link: link:https://alchemists.io/articles/ruby_data[Data]
6
6
  :demeter_link: link:https://en.wikipedia.org/wiki/Law_of_Demeter[Law of Demeter]
7
- :dry_container_link: link:https://dry-rb.org/gems/dry-container[Dry Container]
8
7
  :dry_monads_link: link:https://dry-rb.org/gems/dry-monads[Dry Monads]
9
8
  :dry_schema_link: link:https://dry-rb.org/gems/dry-schema[Dry Schema]
10
9
  :dry_types_link: link:https://dry-rb.org/gems/dry-types[Dry Types]
@@ -13,10 +12,10 @@
13
12
  :gitt_link: link:https://alchemists.io/projects/gitt[Gitt]
14
13
  :hash_link: link:https://rubyapi.org/o/hash[Hash]
15
14
  :json_link: link:https://rubyapi.org/o/json[JSON]
15
+ :pipeable_link: link:https://alchemists.io/projects/pipeable[Pipeable]
16
16
  :runcom_link: link:https://alchemists.io/projects/runcom[Runcom]
17
17
  :sod_link: link:https://alchemists.io/projects/sod[Sod]
18
18
  :struct_link: link:https://alchemists.io/articles/ruby_structs[Struct]
19
- :transactable_link: link:https://alchemists.io/projects/transactable[Transactable]
20
19
  :versionaire_link: link:https://alchemists.io/projects/versionaire[Versionaire]
21
20
  :xdg_link: link:https://alchemists.io/projects/xdg[XDG]
22
21
  :yaml_link: link:https://rubyapi.org/o/yaml[YAML]
@@ -29,7 +28,7 @@ ____
29
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.
30
29
  ____
31
30
 
32
- 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. Pairs well with the {xdg_link}, {runcom_link}, and {sod_link} gems.
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.
33
32
 
34
33
  toc::[]
35
34
 
@@ -89,7 +88,7 @@ etcher.call({one: 1, two: 2})
89
88
  # Success({:one=>1, :two=>2})
90
89
  ----
91
90
 
92
- Notice you get a monad -- either a `Success` or `Failure` -- as provided by the {dry_monads_link} gem. This allows you to create more sophisticated pipelines as found with the {transactable_link} gem or any kind of failsafe workflow you might need.
91
+ Notice you get a monad -- either a `Success` or `Failure` -- as provided by the {dry_monads_link} gem. This allows you to create more sophisticated pipelines as found with the {pipeable_link} gem or any kind of failsafe workflow you might need.
93
92
 
94
93
  By default, any attributes you message the instance with will only pass through what you gave it and always answer a `Success`. This is nice for initial experimentation but true power comes with full customization of the instance. Here's an advanced configuration showing all features:
95
94
 
@@ -107,8 +106,8 @@ end
107
106
 
108
107
  model = Data.define :user, :home
109
108
 
110
- transformer = lambda do |content, key = :user|
111
- Dry::Monads::Success content.merge! key => content[key].upcase
109
+ transformer = lambda do |attributes, key = :user|
110
+ Dry::Monads::Success attributes.merge! key => attributes[key].upcase
112
111
  end
113
112
 
114
113
  Etcher::Registry.new(contract:, model:, transformers: [transformer])
@@ -123,7 +122,7 @@ The above can be broken down into a series of steps:
123
122
  . A {dry_schema_link} contract -- loaded with {dry_monads_link} extensions -- is created to verify untrusted attributes.
124
123
  . A model is created with attributes: `user` and `home`.
125
124
  . A registry is constructed with a custom contract, model, loader, and transformer.
126
- . Finally, we see a _successfully_ built configuration for further use.
125
+ . Finally, we see a _successfully_ built configuration for further use within your application.
127
126
 
128
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.
129
128
 
@@ -133,11 +132,11 @@ While this is a more advanced use case, you'll usually only need to register a c
133
132
 
134
133
  As hinted at above, the complete sequence of steps are performed in the order listed:
135
134
 
136
- . *Load*: Each loader, if any, is called and merged with the previous loader to build initial content.
137
- . *Override*: Any overrides are merged with the result of the last loader to produce updated content. _In Version 2.0.0, this step happen after the Transform step._
138
- . *Transform*: Each transformer, if any, is called to transform and manipulate the content.
139
- . *Validate*: The contract is called to validate the content as previously loaded, overwritten, and transformed.
140
- . *Model*: The model consumes the content of the validated contract and creates a new record for you to use as needed.
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.
141
140
 
142
141
  You can use the above steps as a reference when using this gem. Each step is explained in greater detail below.
143
142
 
@@ -151,7 +150,7 @@ Etcher::Registry.new
151
150
  # #<data Etcher::Registry contract=#<Proc:0x000000010e393550 contract.rb:7 (lambda)>, model=Hash, loaders=[], transformers=[]>
152
151
  ----
153
152
 
154
- Since the registry is a {data_link}, you can initialize with everything you need:
153
+ Since the registry is {data_link}, you can initialize with everything you need:
155
154
 
156
155
  [source,ruby]
157
156
  ----
@@ -176,11 +175,11 @@ registry = Etcher::Registry.new
176
175
 
177
176
  === Contracts
178
177
 
179
- 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:
180
179
 
181
180
  * `#call`: Must be able to consume a {hash_link} and answer an object which can respond to `#to_monad`.
182
181
 
183
- 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:
184
183
 
185
184
  [source,ruby]
186
185
  ----
@@ -210,7 +209,7 @@ etcher.call from: "Mork", to: "Mindy"
210
209
  # Success({:from=>"Mork", :to=>"Mindy"})
211
210
  ----
212
211
 
213
- 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 model which which we'll look at next.
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.
214
213
 
215
214
  === Types
216
215
 
@@ -268,7 +267,7 @@ model = Data.define :from, :to
268
267
  etcher = Etcher::Registry[model:].then { |registry| Etcher.new registry }
269
268
 
270
269
  etcher.call
271
- # Failure({:step=>:record, :payload=>"Missing keywords: :from, :to."})
270
+ # Failure({:step=>:model, :payload=>"Missing keywords: :from, :to."})
272
271
 
273
272
  etcher.call from: "Mork", to: "Mindy"
274
273
  # Success(#<data Model from="Mork", to="Mindy">)
@@ -345,7 +344,7 @@ loader = Etcher::Loaders::JSON.new "your/path/to/configuration.json",
345
344
  loader.call # Success({})
346
345
  ----
347
346
 
348
- If the file did exist and had content, you'd get a `Success` with a `Hash` of the contents.
347
+ Otherwise, if the file exists with content, you'll get a `Hash` wrapped as a `Success`.
349
348
 
350
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.
351
350
 
@@ -370,7 +369,7 @@ loader = Etcher::Loaders::YAML.new "your/path/to/configuration.yml",
370
369
  loader.call # Success({})
371
370
  ----
372
371
 
373
- If the file did exist and had content, you'd get a `Success` with a `Hash` of the contents.
372
+ Otherwise, if the file exists with content, you'll get a `Hash` wrapped as a `Success`.
374
373
 
375
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.
376
375
 
@@ -418,9 +417,9 @@ registry = Etcher::Registry.new.add_transformer MyTransformer
418
417
  Here are a few guidelines to using them:
419
418
 
420
419
  * They can be initialized with whatever requirements you might need.
421
- * They must respond to `#call` which takes a required `content` positional argument and answers a modified representation of this content as a monad with a `Hash` for content.
422
- * A second _optional_ positional `key` parameter should follow your `content` parameter when implementing your transformer. This allows you to quickly refactor the key later while also reducing key duplication throughout your implementation.
423
- * The `content` passed to your transformer will have symbolized keys so you don't need to do this yourself.
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.
424
423
 
425
424
  Here are a few examples of where you could go with this:
426
425
 
@@ -430,7 +429,7 @@ The following capitalizes all values (which may or may not be good depending on
430
429
  ----
431
430
  require "dry/monads"
432
431
 
433
- Capitalize = -> content { Dry::Monads::Success content.transform_values!(&:capitalize) }
432
+ Capitalize = -> attributes { Dry::Monads::Success attributes.transform_values!(&:capitalize) }
434
433
  Capitalize.call(name: "test")
435
434
 
436
435
  # Success({:name=>"Test"})
@@ -442,9 +441,9 @@ The following updates current time relative to when configuration was transforme
442
441
  ----
443
442
  require "dry/monads"
444
443
 
445
- CurrentTime = lambda do |content, key = :at, at: Time.now|
446
- content.fetch(key) { at }
447
- .then { |value| Dry::Monads::Success content.merge!(key => value) }
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) }
448
447
  end
449
448
 
450
449
  CurrentTime.call({})
@@ -470,7 +469,7 @@ class GitEmail
470
469
  @git = git
471
470
  end
472
471
 
473
- def call(content) = git.get("user.email").fmap { |value| content[key] = value }
472
+ def call(attributes) = git.get("user.email").fmap { |value| attributes[key] = value }
474
473
 
475
474
  private
476
475
 
@@ -517,7 +516,7 @@ Etcher.call name: "demo"
517
516
  # {:name=>"demo"}
518
517
  ----
519
518
 
520
- 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 fatal errors:
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:
521
520
 
522
521
  [source,ruby]
523
522
  ----
data/etcher.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "etcher"
5
- spec.version = "1.2.0"
5
+ spec.version = "1.4.0"
6
6
  spec.authors = ["Brooke Kuhlmann"]
7
7
  spec.email = ["brooke@alchemists.io"]
8
8
  spec.homepage = "https://alchemists.io/projects/etcher"
@@ -16,42 +16,39 @@ module Etcher
16
16
  end
17
17
 
18
18
  def call(**overrides)
19
- load(overrides.symbolize_keys!).then { |content| transform content }
20
- .bind { |content| validate content }
21
- .bind { |content| record content }
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 { |content| content.flatten_keys.symbolize_keys! } }
30
+ .map { |loader| loader.call.fmap { |pairs| pairs.flatten_keys.symbolize_keys! } }
33
31
  .each
34
- .with_object({}) { |content, all| content.bind { |body| all.merge! body } }
32
+ .with_object({}) { |attributes, all| attributes.bind { |body| all.merge! body } }
35
33
  .merge!(overrides.flatten_keys)
36
- .then { |content| Success content }
34
+ .then { |attributes| Success attributes }
37
35
  end
38
36
 
39
- # :reek:NestedIterators
40
- def transform content
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 content
43
+ def validate attributes
47
44
  registry.contract
48
- .call(content)
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 record content
54
- Success registry.model[**content.to_h].freeze
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
@@ -4,8 +4,8 @@ require "dry/monads"
4
4
 
5
5
  module Etcher
6
6
  # A simple passthrough contract.
7
- Contract = lambda do |content|
8
- def content.to_monad = Dry::Monads::Success self unless content.respond_to? :to_monad
9
- content
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
@@ -18,7 +18,7 @@ module Etcher
18
18
 
19
19
  def call(**overrides)
20
20
  case builder.call(**overrides)
21
- in Success(content) then content
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.2.0
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-03-03 00:00:00.000000000 Z
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.6
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