abstract-synthesizer 0.0.12 → 0.0.13
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.
- checksums.yaml +4 -4
- data/.nix_shell_env.sh +2134 -0
- data/GEMINI.md +13 -0
- data/Gemfile.lock +4 -4
- data/docs/overview.md +36 -0
- data/docs/usage.md +116 -0
- data/flake.lock +6 -6
- data/gemset.nix +5 -5
- data/lib/GEMINI.md +26 -0
- data/lib/abstract-synthesizer/GEMINI.md +5 -0
- data/lib/abstract-synthesizer/errors/GEMINI.md +7 -0
- data/lib/abstract-synthesizer/primitives/GEMINI.md +5 -0
- data/lib/abstract-synthesizer/version.rb +1 -1
- data/lib/abstract-synthesizer.rb +1 -1
- metadata +9 -1
data/GEMINI.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# Project Root Directory
|
2
|
+
|
3
|
+
This directory contains the core files for the `abstract-synthesizer` Ruby gem.
|
4
|
+
|
5
|
+
- `README.md`: Provides a general overview of the project.
|
6
|
+
- `abstract-synthesizer.gemspec`: Defines the gem's metadata, dependencies, and files to be included in the gem. It specifies the gem's name, version, authors, description, and development dependencies.
|
7
|
+
- `Rakefile`: Contains Rake tasks for common development operations such as running tests (`rspec`) and linting (`rubocop`).
|
8
|
+
- `Gemfile`, `Gemfile.lock`, `gemset.nix`, `flake.nix`, `flake.lock`: These files are related to dependency management using Bundler and Nix. `gemset.nix` is generated by `bundix` for Nix integration.
|
9
|
+
- `.rubocop.yml`: Configuration file for RuboCop, a Ruby static code analyzer.
|
10
|
+
- `.gitignore`: Specifies files and directories to be ignored by Git.
|
11
|
+
- `LICENSE`: Contains the licensing information for the project (MIT License).
|
12
|
+
|
13
|
+
The primary purpose of this gem is to "create resource based configuration DSL".
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
abstract-synthesizer (0.0.
|
4
|
+
abstract-synthesizer (0.0.13)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
@@ -24,7 +24,7 @@ GEM
|
|
24
24
|
rspec-core (~> 3.13.0)
|
25
25
|
rspec-expectations (~> 3.13.0)
|
26
26
|
rspec-mocks (~> 3.13.0)
|
27
|
-
rspec-core (3.13.
|
27
|
+
rspec-core (3.13.5)
|
28
28
|
rspec-support (~> 3.13.0)
|
29
29
|
rspec-expectations (3.13.5)
|
30
30
|
diff-lcs (>= 1.2.0, < 2.0)
|
@@ -33,7 +33,7 @@ GEM
|
|
33
33
|
diff-lcs (>= 1.2.0, < 2.0)
|
34
34
|
rspec-support (~> 3.13.0)
|
35
35
|
rspec-support (3.13.4)
|
36
|
-
rubocop (1.
|
36
|
+
rubocop (1.77.0)
|
37
37
|
json (~> 2.3)
|
38
38
|
language_server-protocol (~> 3.17.0.2)
|
39
39
|
lint_roller (~> 1.1.0)
|
@@ -41,7 +41,7 @@ GEM
|
|
41
41
|
parser (>= 3.3.0.2)
|
42
42
|
rainbow (>= 2.2.2, < 4.0)
|
43
43
|
regexp_parser (>= 2.9.3, < 3.0)
|
44
|
-
rubocop-ast (>= 1.45.
|
44
|
+
rubocop-ast (>= 1.45.1, < 2.0)
|
45
45
|
ruby-progressbar (~> 1.7)
|
46
46
|
unicode-display_width (>= 2.4.0, < 4.0)
|
47
47
|
rubocop-ast (1.45.1)
|
data/docs/overview.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Abstract Synthesizer Overview
|
2
|
+
|
3
|
+
The `abstract-synthesizer` gem provides a Ruby DSL (Domain Specific Language) for defining hierarchical, resource-based configurations. It allows users to create structured data (manifests) using a declarative syntax, similar to how configuration files are often written.
|
4
|
+
|
5
|
+
## Core Concepts
|
6
|
+
|
7
|
+
- **Synthesizer**: The main component responsible for processing the DSL and building the manifest. It dynamically handles method calls to interpret resource definitions and field assignments.
|
8
|
+
- **Manifest**: The resulting hierarchical data structure (a Ruby Hash) generated by the synthesizer, representing the defined configuration.
|
9
|
+
- **Keys**: A predefined set of allowed methods that the synthesizer recognizes as valid resource or field names within the DSL.
|
10
|
+
- **Bury**: A utility module that extends the `Hash` class, enabling nested assignment of values, which is fundamental for building the hierarchical manifest.
|
11
|
+
|
12
|
+
## Architecture Diagram
|
13
|
+
|
14
|
+
```mermaid
|
15
|
+
graph TD
|
16
|
+
A[User DSL Input] --> B{AbstractSynthesizer};
|
17
|
+
B -- "method_missing calls" --> C[abstract_method_missing];
|
18
|
+
C -- "Validates method/args" --> D{Validation Logic};
|
19
|
+
D -- "Raises Errors" --> E[Custom Errors];
|
20
|
+
C -- "Builds nested hash" --> F[Bury Module (extends Hash)];
|
21
|
+
F --> G[Manifest (Ruby Hash)];
|
22
|
+
B -- "Returns" --> G;
|
23
|
+
|
24
|
+
subgraph SynthesizerFactory
|
25
|
+
H[create_synthesizer] --> B;
|
26
|
+
end
|
27
|
+
```
|
28
|
+
|
29
|
+
## How it Works
|
30
|
+
|
31
|
+
1. **Initialization**: A `Synthesizer` instance is created, optionally via the `SynthesizerFactory`, which injects the allowed `keys` for the DSL.
|
32
|
+
2. **DSL Evaluation**: The `synthesize` method evaluates a Ruby block or string. Inside this context, method calls are intercepted.
|
33
|
+
3. **Dynamic Method Handling**: The `method_missing` method (overridden in `AbstractSynthesizer` or dynamically defined by `SynthesizerFactory`) delegates to `abstract_method_missing`.
|
34
|
+
4. **Validation**: `abstract_method_missing` validates the called method against the allowed `keys` and checks argument counts.
|
35
|
+
5. **Manifest Building**: Based on the method and arguments, the synthesizer uses the `bury` method (provided by the `Bury` module) to insert data into the `translation[:manifest]` hash, creating nested structures as needed.
|
36
|
+
6. **Result**: The final `manifest` hash represents the structured configuration defined by the DSL.
|
data/docs/usage.md
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
# Usage Guide
|
2
|
+
|
3
|
+
This guide explains how to use the `abstract-synthesizer` gem to define resource-based configurations.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'abstract-synthesizer'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
```bash
|
16
|
+
bundle install
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
gem install abstract-synthesizer
|
23
|
+
```
|
24
|
+
|
25
|
+
## Basic Usage
|
26
|
+
|
27
|
+
To use the synthesizer, you typically create an instance using the `SynthesizerFactory` and then define your configuration within a `synthesize` block.
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
require 'abstract-synthesizer'
|
31
|
+
|
32
|
+
# Define the allowed keys for your DSL
|
33
|
+
# These keys will be the valid methods you can call in your DSL block
|
34
|
+
resource_keys = %i[server database user]
|
35
|
+
|
36
|
+
synthesizer = SynthesizerFactory.create_synthesizer(name: :my_config, keys: resource_keys)
|
37
|
+
|
38
|
+
synthesizer.synthesize do
|
39
|
+
server :web_server, :production do
|
40
|
+
host 'example.com'
|
41
|
+
port 8080
|
42
|
+
ssl true
|
43
|
+
end
|
44
|
+
|
45
|
+
server :api_server, :development do
|
46
|
+
host 'localhost'
|
47
|
+
port 3000
|
48
|
+
end
|
49
|
+
|
50
|
+
database :main_db, :mysql do
|
51
|
+
username 'admin'
|
52
|
+
password 'secret'
|
53
|
+
host 'db.example.com'
|
54
|
+
end
|
55
|
+
|
56
|
+
user :admin_user do
|
57
|
+
name 'Administrator'
|
58
|
+
email 'admin@example.com'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Access the generated manifest
|
63
|
+
puts synthesizer.synthesis.inspect
|
64
|
+
# Expected Output (simplified):
|
65
|
+
# {
|
66
|
+
# :server => {
|
67
|
+
# :web_server => {
|
68
|
+
# :production => {
|
69
|
+
# :host => "example.com", :port => 8080, :ssl => true
|
70
|
+
# }
|
71
|
+
# },
|
72
|
+
# :api_server => {
|
73
|
+
# :development => {
|
74
|
+
# :host => "localhost", :port => 3000
|
75
|
+
# }
|
76
|
+
# }
|
77
|
+
# },
|
78
|
+
# :database => {
|
79
|
+
# :main_db => {
|
80
|
+
# :mysql => {
|
81
|
+
# :username => "admin", :password => "secret", :host => "db.example.com"
|
82
|
+
# }
|
83
|
+
# }
|
84
|
+
# },
|
85
|
+
# :user => {
|
86
|
+
# :admin_user => {
|
87
|
+
# :name => "Administrator", :email => "admin@example.com"
|
88
|
+
# }
|
89
|
+
# }
|
90
|
+
# }
|
91
|
+
```
|
92
|
+
|
93
|
+
## DSL Structure
|
94
|
+
|
95
|
+
The DSL follows a hierarchical structure:
|
96
|
+
|
97
|
+
- **Resource Definition**: The top-level methods in your `synthesize` block correspond to the `keys` you provided to `create_synthesizer`. These methods can take multiple arguments, which become nested keys in the manifest.
|
98
|
+
```ruby
|
99
|
+
resource_key :first_level_identifier, :second_level_identifier do
|
100
|
+
# ... fields or nested resources
|
101
|
+
end
|
102
|
+
```
|
103
|
+
|
104
|
+
- **Field Assignment**: Inside a resource block, methods without a block are treated as field assignments. They take a single argument, which is the value for that field.
|
105
|
+
```ruby
|
106
|
+
field_name 'field_value'
|
107
|
+
another_field 123
|
108
|
+
```
|
109
|
+
|
110
|
+
## Error Handling
|
111
|
+
|
112
|
+
The gem provides specific error classes for common issues:
|
113
|
+
|
114
|
+
- `InvalidSynthesizerKeyError`: Raised when you try to use a method in your DSL that was not included in the `keys` provided to `SynthesizerFactory.create_synthesizer`.
|
115
|
+
- `TooManyFieldValuesError`: Raised when a field assignment method receives more than one argument.
|
116
|
+
- `NotEnoughResourceKeys`: Raised when a resource definition does not receive the expected number of arguments (e.g., if a top-level resource expects two identifiers but only one is provided).
|
data/flake.lock
CHANGED
@@ -20,11 +20,11 @@
|
|
20
20
|
},
|
21
21
|
"nixpkgs": {
|
22
22
|
"locked": {
|
23
|
-
"lastModified":
|
24
|
-
"narHash": "sha256-
|
23
|
+
"lastModified": 1751031243,
|
24
|
+
"narHash": "sha256-LE6HorMxlsjj0sJ5MzV0UMAQzzG2R438rVFjSM9/LCU=",
|
25
25
|
"owner": "NixOS",
|
26
26
|
"repo": "nixpkgs",
|
27
|
-
"rev": "
|
27
|
+
"rev": "618b6ce64508a2c821111fffc3f4153c6675acef",
|
28
28
|
"type": "github"
|
29
29
|
},
|
30
30
|
"original": {
|
@@ -59,11 +59,11 @@
|
|
59
59
|
"nixpkgs": "nixpkgs_2"
|
60
60
|
},
|
61
61
|
"locked": {
|
62
|
-
"lastModified":
|
63
|
-
"narHash": "sha256-
|
62
|
+
"lastModified": 1744623277,
|
63
|
+
"narHash": "sha256-0HWA2YD9v71SHyMF11PKnVJcHnrHhRLHDCldlUbzYII=",
|
64
64
|
"owner": "inscapist",
|
65
65
|
"repo": "ruby-nix",
|
66
|
-
"rev": "
|
66
|
+
"rev": "43964ced23803f49e2d307fbbfd4e03ed23760c0",
|
67
67
|
"type": "github"
|
68
68
|
},
|
69
69
|
"original": {
|
data/gemset.nix
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
path = ./.;
|
7
7
|
type = "path";
|
8
8
|
};
|
9
|
-
version = "0.0.
|
9
|
+
version = "0.0.13";
|
10
10
|
};
|
11
11
|
ast = {
|
12
12
|
groups = ["default" "development"];
|
@@ -146,10 +146,10 @@
|
|
146
146
|
platforms = [];
|
147
147
|
source = {
|
148
148
|
remotes = ["https://rubygems.org"];
|
149
|
-
sha256 = "
|
149
|
+
sha256 = "18sgga9zjrd5579m9rpb78l7yn9a0bjzwz51z5kiq4y6jwl6hgxb";
|
150
150
|
type = "gem";
|
151
151
|
};
|
152
|
-
version = "3.13.
|
152
|
+
version = "3.13.5";
|
153
153
|
};
|
154
154
|
rspec-expectations = {
|
155
155
|
dependencies = ["diff-lcs" "rspec-support"];
|
@@ -189,10 +189,10 @@
|
|
189
189
|
platforms = [];
|
190
190
|
source = {
|
191
191
|
remotes = ["https://rubygems.org"];
|
192
|
-
sha256 = "
|
192
|
+
sha256 = "1h48rhmp178ppzc4ybfj42a2savs4bxgy3bvw95i4ypgfm2hndhz";
|
193
193
|
type = "gem";
|
194
194
|
};
|
195
|
-
version = "1.
|
195
|
+
version = "1.77.0";
|
196
196
|
};
|
197
197
|
rubocop-ast = {
|
198
198
|
dependencies = ["parser" "prism"];
|
data/lib/GEMINI.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# lib/ Directory
|
2
|
+
|
3
|
+
This directory contains the main source code for the `abstract-synthesizer` gem.
|
4
|
+
|
5
|
+
- `abstract-synthesizer.rb`: This is the primary entry point for the gem. It defines the `AbstractSynthesizer` class and the `SynthesizerFactory` module.
|
6
|
+
|
7
|
+
## AbstractSynthesizer Class
|
8
|
+
|
9
|
+
The `AbstractSynthesizer` class is designed to create a resource-based configuration DSL (Domain Specific Language). It allows users to define hierarchical data structures (manifests) using a Ruby-like syntax.
|
10
|
+
|
11
|
+
Key features and methods:
|
12
|
+
- `initialize`: Initializes the synthesizer with a `translation` hash, which stores the `ancestors` (current path in the manifest), `manifest` (the generated data structure), and `context`.
|
13
|
+
- `clear!`: Resets the `manifest`.
|
14
|
+
- `synthesis`: Returns the generated `manifest`.
|
15
|
+
- `synthesize(content = nil, &block)`: Evaluates the provided content or block to build the manifest. This is where the DSL is processed.
|
16
|
+
- `manifest`: An alias for `synthesis`.
|
17
|
+
- `valid_method?(method, keys)`: Internal method to check if a method call is valid within the current context (resource definition or field assignment).
|
18
|
+
- `validate_method(method, keys)`: Raises `InvalidSynthesizerKeyError` if a method is not valid.
|
19
|
+
- `validate_args(args)`: Validates the number of arguments based on the current context, raising `NotEnoughResourceKeys` or `TooManyFieldValuesError`.
|
20
|
+
- `abstract_method_missing(method, keys, *args)`: This is the core of the DSL. It dynamically handles method calls, interpreting them as resource keys or field assignments, and building the `translation[:manifest]` accordingly. It uses the `Bury` module for nested data insertion.
|
21
|
+
|
22
|
+
## SynthesizerFactory Module
|
23
|
+
|
24
|
+
This module provides a factory method to create instances of `AbstractSynthesizer`.
|
25
|
+
|
26
|
+
- `create_synthesizer(name:, keys:)`: Creates a new `AbstractSynthesizer` instance and dynamically defines its `method_missing` method. This `method_missing` delegates to `abstract_method_missing` within the `AbstractSynthesizer` instance, passing the allowed `keys` for the DSL. This allows for flexible definition of the DSL structure based on the provided `keys`.
|
@@ -0,0 +1,5 @@
|
|
1
|
+
# lib/abstract-synthesizer/ Directory
|
2
|
+
|
3
|
+
This directory contains modules and core components that support the `AbstractSynthesizer`.
|
4
|
+
|
5
|
+
- `version.rb`: Defines the version number of the `abstract-synthesizer` gem within the `Meta` module. This file is used by the `gemspec` to set the gem's version.
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# lib/abstract-synthesizer/errors/ Directory
|
2
|
+
|
3
|
+
This directory contains custom error classes used by the `abstract-synthesizer` gem. These errors are raised when specific conditions are not met during the synthesis process, providing clear feedback to the user.
|
4
|
+
|
5
|
+
- `invalid_synthesizer_key_error.rb`: Defines `InvalidSynthesizerKeyError`. This error is raised when the synthesizer attempts to process a key (method call) that is not defined or recognized as a valid resource key within the DSL.
|
6
|
+
- `not_enough_resource_keys.rb`: Defines `NotEnoughResourceKeys`. This error is raised when an insufficient number of resource keys are provided, typically during the initial definition of a resource.
|
7
|
+
- `too_many_field_values.rb`: Defines `TooManyFieldValuesError`. This error is raised when too many field values are provided for a given field, indicating an incorrect usage of the DSL.
|
@@ -0,0 +1,5 @@
|
|
1
|
+
# lib/abstract-synthesizer/primitives/ Directory
|
2
|
+
|
3
|
+
This directory contains primitive modules or classes that provide fundamental functionalities used by the `abstract-synthesizer`.
|
4
|
+
|
5
|
+
- `bury.rb`: Defines the `Bury` module. This module extends the `Hash` class with a `bury` method. The `bury` method allows for nested assignment of values within a hash, creating intermediate hashes as needed. This is crucial for building the hierarchical `manifest` within the `AbstractSynthesizer`.
|
data/lib/abstract-synthesizer.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: abstract-synthesizer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- drzzln@protonmail.com
|
@@ -60,20 +60,28 @@ extensions: []
|
|
60
60
|
extra_rdoc_files: []
|
61
61
|
files:
|
62
62
|
- ".gitignore"
|
63
|
+
- ".nix_shell_env.sh"
|
63
64
|
- ".rubocop.yml"
|
65
|
+
- GEMINI.md
|
64
66
|
- Gemfile
|
65
67
|
- Gemfile.lock
|
66
68
|
- LICENSE
|
67
69
|
- README.md
|
68
70
|
- Rakefile
|
69
71
|
- abstract-synthesizer.gemspec
|
72
|
+
- docs/overview.md
|
73
|
+
- docs/usage.md
|
70
74
|
- flake.lock
|
71
75
|
- flake.nix
|
72
76
|
- gemset.nix
|
77
|
+
- lib/GEMINI.md
|
73
78
|
- lib/abstract-synthesizer.rb
|
79
|
+
- lib/abstract-synthesizer/GEMINI.md
|
80
|
+
- lib/abstract-synthesizer/errors/GEMINI.md
|
74
81
|
- lib/abstract-synthesizer/errors/invalid_synthesizer_key_error.rb
|
75
82
|
- lib/abstract-synthesizer/errors/not_enough_resource_keys.rb
|
76
83
|
- lib/abstract-synthesizer/errors/too_many_field_values.rb
|
84
|
+
- lib/abstract-synthesizer/primitives/GEMINI.md
|
77
85
|
- lib/abstract-synthesizer/primitives/bury.rb
|
78
86
|
- lib/abstract-synthesizer/version.rb
|
79
87
|
homepage: https://github.com/drzln/abstract-synthesizer
|