klue-langcraft 0.1.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +30 -3
  3. data/CHANGELOG.md +21 -0
  4. data/bin/dsl_watcher.rb +10 -0
  5. data/bin/langcraft.rb +177 -0
  6. data/docs/dsl-class-diagram.md +97 -0
  7. data/docs/dsl-examples.md +9 -0
  8. data/docs/dsl-upgrade-plan.md +266 -0
  9. data/lib/base_process.rb +41 -0
  10. data/lib/dsl_folder_watcher.rb +50 -0
  11. data/lib/dsl_interpreter.rb +112 -0
  12. data/lib/dsl_process_data.rb +31 -0
  13. data/lib/klue/langcraft/dsl/interpreter.rb +114 -0
  14. data/lib/klue/langcraft/dsl/klue_runner.rb +68 -0
  15. data/lib/klue/langcraft/dsl/process_data_pipeline.rb +65 -0
  16. data/lib/klue/langcraft/dsl/process_matcher.rb +59 -0
  17. data/lib/klue/langcraft/dsl/processor_config.rb +35 -0
  18. data/lib/klue/langcraft/dsl/processors/file_collector_processor.rb +30 -0
  19. data/lib/klue/langcraft/dsl/processors/full_name_processor.rb +34 -0
  20. data/lib/klue/langcraft/dsl/processors/processor.rb +43 -0
  21. data/lib/klue/langcraft/dsl/watcher.rb +88 -0
  22. data/lib/klue/langcraft/dsl/webhook.rb +57 -0
  23. data/lib/klue/langcraft/version.rb +1 -1
  24. data/lib/klue/langcraft.rb +29 -2
  25. data/lib/process_file_collector.rb +92 -0
  26. data/package-lock.json +2 -2
  27. data/package.json +1 -1
  28. metadata +39 -7
  29. data/docs/dsl-samples/index.md +0 -4
  30. /data/lib/klue/langcraft/{-brief.md → tokenizer-old-needs-revisit/-brief.md} +0 -0
  31. /data/lib/klue/langcraft/{parser.rb → tokenizer-old-needs-revisit/parser.rb} +0 -0
  32. /data/lib/klue/langcraft/{sample_usage.rb → tokenizer-old-needs-revisit/sample_usage.rb} +0 -0
  33. /data/lib/klue/langcraft/{tokenizer.rb → tokenizer-old-needs-revisit/tokenizer.rb} +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c27050239df45678f9bf8b8527c20f033532235d023cf22bd255b06f0f48b38
4
- data.tar.gz: 22e0fab109f694eb0fc501885f38e94b8e0eae55da1e66381093fc8cedbece86
3
+ metadata.gz: 586607de2048a4670a8cc9cd1e06dd9a2d1636aa0c9825698af2b6bac51ea24d
4
+ data.tar.gz: 1973c38bf614abc045c9d8e2759f984791b52da023d10441d2564bf54f99c323
5
5
  SHA512:
6
- metadata.gz: 78d038c2e19671fc645c02215820345d9a89f8552869f9eb2f0d0f6769e348bef56f218ff39114bd6f8945ef44001d928b1f5a0f4896e8510ffcebe6ad9f49a3
7
- data.tar.gz: 80e94051e6ad5104cba4b199b472bbea3f62bdc23903a71974fb04d9399a92beb23627581ef30c9e02a0f8a821d1a9ef48cc9cb7a19a1cd223a1af95085b9c1c
6
+ metadata.gz: 9c20cb42da33cc805d4ec372f6101f796b44d127724fd1a611cd2620e4614e225148fc2162ab6f0e8d9bdf2fa1909575f0ed78da93ba057b4af38415bae739bd
7
+ data.tar.gz: 4ebe337655eba091ec8013700734afcea0a67006bc076ac5d94e075d4210fba5ee490e43f4eb92c716e2230d5c61918ce201fecde6b6919a081bfea1a95088c9
data/.rubocop.yml CHANGED
@@ -14,6 +14,7 @@ AllCops:
14
14
  Exclude:
15
15
  - ".builders/**/*"
16
16
  - "spec/samples/**/*"
17
+ - "lib/*" # OLD DSL INTERPRETER
17
18
 
18
19
  Metrics/BlockLength:
19
20
  Exclude:
@@ -39,8 +40,23 @@ Metrics/BlockLength:
39
40
  - shared_examples_for
40
41
  - transaction
41
42
 
43
+ Metrics/AbcSize:
44
+ Exclude:
45
+ - bin/*
46
+
47
+
42
48
  Metrics/MethodLength:
43
49
  Max: 25
50
+ Exclude:
51
+ - bin/*
52
+
53
+ Metrics/PerceivedComplexity:
54
+ Exclude:
55
+ - bin/*
56
+
57
+ Metrics/CyclomaticComplexity:
58
+ Exclude:
59
+ - bin/*
44
60
 
45
61
  Layout/LineLength:
46
62
  Max: 200
@@ -54,7 +70,7 @@ Lint/UnusedMethodArgument:
54
70
  Style/BlockComments:
55
71
  Enabled: false
56
72
  Include:
57
- - "**/spec/*"
73
+ - "**/spec/**/*"
58
74
 
59
75
  # My Preferences - Start
60
76
  Metrics/ClassLength:
@@ -74,8 +90,8 @@ Style/EmptyMethod:
74
90
  Exclude:
75
91
  - "**/spec/**/*"
76
92
  Metrics/ParameterLists:
77
- Exclude:
78
- - "**/spec/**/*"
93
+ Enabled: false
94
+
79
95
  Layout/EmptyLineBetweenDefs:
80
96
  Exclude:
81
97
  - "**/spec/**/*"
@@ -104,3 +120,14 @@ RSpec/SpecFilePathSuffix:
104
120
  RSpec/NamedSubject:
105
121
  Exclude:
106
122
  - "**/spec/**/*"
123
+
124
+ RSpec/MultipleExpectations:
125
+ Exclude:
126
+ - "**/spec/**/*"
127
+
128
+ RSpec/MultipleMemoizedHelpers:
129
+ Exclude:
130
+ - "**/spec/**/*"
131
+
132
+ RSpec/ExampleLength:
133
+ Max: 20
data/CHANGELOG.md CHANGED
@@ -1,3 +1,24 @@
1
+ # [0.2.0](https://github.com/appydave/klue-langcraft/compare/v0.1.1...v0.2.0) (2024-10-25)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * fix ci issue ([a3bfd35](https://github.com/appydave/klue-langcraft/commit/a3bfd35371b7c0ca70b0b57d70d4541c7a4ec54b))
7
+ * fix cop ([123070b](https://github.com/appydave/klue-langcraft/commit/123070bd1f636a81d01da469c2e5072c57384bae))
8
+ * update dsl_interpreter ([2507045](https://github.com/appydave/klue-langcraft/commit/2507045c3f7f205603a3f899785e9c5445c4c0bb))
9
+
10
+
11
+ ### Features
12
+
13
+ * refactor dsl processor ([5875dc9](https://github.com/appydave/klue-langcraft/commit/5875dc957789f113da762a6dcb55cda66bfb8a3d))
14
+
15
+ ## [0.1.1](https://github.com/appydave/klue-langcraft/compare/v0.1.0...v0.1.1) (2024-09-22)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * add chatgpt conversation link ([ae5448d](https://github.com/appydave/klue-langcraft/commit/ae5448d4e23ab1fd673c788e6f30e736d82afb44))
21
+
1
22
  # [0.1.0](https://github.com/appydave/klue-langcraft/compare/v0.0.7...v0.1.0) (2024-09-22)
2
23
 
3
24
 
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
5
+
6
+ require 'klue/langcraft'
7
+
8
+ BASE_PATH = ARGV[0] || '/Users/davidcruwys/dev/appydave/klueless'
9
+
10
+ DSLFolderWatcher.watch(BASE_PATH)
data/bin/langcraft.rb ADDED
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
5
+
6
+ require 'klue/langcraft'
7
+ require 'optparse'
8
+
9
+ # CLI class for the Klue Langcraft application.
10
+ # This class handles command-line interactions, parsing options,
11
+ # and executing the appropriate commands for processing files
12
+ # or watching directories.
13
+ class CLI
14
+ def initialize
15
+ @commands = {
16
+ 'process' => method(:process_files),
17
+ 'watch' => method(:watch_files)
18
+ }
19
+ end
20
+
21
+ def run
22
+ if ARGV.empty?
23
+ print_help
24
+ exit
25
+ end
26
+
27
+ command, *args = ARGV
28
+ if @commands.key?(command)
29
+ @commands[command].call(args)
30
+ else
31
+ puts "Unknown command: #{command}"
32
+ print_help
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def process_files(args)
39
+ options = {}
40
+ parser = OptionParser.new do |opts|
41
+ opts.banner = 'Usage: langcraft process [options]'
42
+
43
+ opts.on('-i', '--input FILE', 'Input file (required)') do |file|
44
+ options[:input] = file
45
+ end
46
+
47
+ opts.on('-b', '--basic-output FILE', 'Output file') do |file|
48
+ options[:basic_output] = file
49
+ end
50
+
51
+ opts.on('-e', '--enhanced-output FILE', 'Output file') do |file|
52
+ options[:enhanced_output] = file
53
+ end
54
+
55
+ opts.on('-u', '--webhook-url URL', 'Webhook URL') do |url|
56
+ options[:webhook_url] = url
57
+ end
58
+
59
+ opts.on('-d', '--debug LEVEL', %i[none info detailed], 'Debug level (none, info, detailed)') do |level|
60
+ options[:debug] = level
61
+ end
62
+
63
+ opts.on('-h', '--help', 'Show this message') do
64
+ puts opts
65
+ exit
66
+ end
67
+ end
68
+
69
+ parser.parse!(args)
70
+
71
+ if validate_options(options)
72
+ run_klue_runner(options)
73
+ else
74
+ print_usage(parser)
75
+ end
76
+ end
77
+
78
+ def validate_options(options)
79
+ if options[:input].nil?
80
+ puts 'Error: Input file is required.'
81
+ false
82
+ else
83
+ true
84
+ end
85
+ end
86
+
87
+ def run_klue_runner(options)
88
+ klue_runner = Klue::Langcraft::DSL::KlueRunner.new
89
+ klue_runner.run(
90
+ input_file: options[:input],
91
+ basic_output_file: options[:basic_output],
92
+ enhanced_output_file: options[:enhanced_output],
93
+ webhook_url: options[:webhook_url],
94
+ debug: options[:debug] || :none
95
+ )
96
+ rescue StandardError => e
97
+ puts "Error: #{e.message}"
98
+ exit 1
99
+ end
100
+
101
+ def print_usage(parser)
102
+ puts 'Error: Invalid or missing options.'
103
+ puts
104
+ puts parser
105
+ exit 1
106
+ end
107
+
108
+ def watch_files(args)
109
+ options = {
110
+ directories: [],
111
+ create_basic_json: false,
112
+ create_enhanced_json: false,
113
+ log_level: :info,
114
+ webhook_url: nil,
115
+ extensions: ['.klue'] # Default extension
116
+ }
117
+ OptionParser.new do |opts|
118
+ opts.banner = 'Usage: server watch [options]'
119
+
120
+ opts.on('-w', '--watch-directory DIRECTORY', 'Directory to watch (can be specified multiple times)') do |dir|
121
+ options[:directories] << File.expand_path(dir) # Expand the path here
122
+ end
123
+
124
+ opts.on('-f', '--flags TYPE', 'Set processing flags (none, basic, enhanced, all)') do |type|
125
+ types = type.split(',').map(&:strip).map(&:downcase)
126
+ options[:create_basic_json] = types.include?('basic') || types.include?('all')
127
+ options[:create_enhanced_json] = types.include?('enhanced') || types.include?('all')
128
+ end
129
+
130
+ opts.on('-l', '--log-level LEVEL', %i[none info detailed], 'Log level (none, info, detailed)') do |level|
131
+ options[:log_level] = level
132
+ end
133
+
134
+ opts.on('-u', '--webhook-url URL', 'Webhook URL') do |url|
135
+ options[:webhook_url] = url
136
+ end
137
+
138
+ opts.on('-e', '--extensions EXT1,EXT2,...', Array, 'File extensions to watch (default: .klue)') do |exts|
139
+ options[:extensions] = exts.map { |ext| ext.start_with?('.') ? ext : ".#{ext}" }
140
+ end
141
+
142
+ opts.on('-h', '--help', 'Show this message') do
143
+ puts opts
144
+ exit
145
+ end
146
+ end.parse!(args)
147
+
148
+ # If no directories were specified, use the current directory
149
+ options[:directories] = [Dir.pwd] if options[:directories].empty?
150
+
151
+ # Ensure all directories are expanded
152
+ options[:directories].map! { |dir| File.expand_path(dir) }
153
+
154
+ Klue::Langcraft::DSL::Watcher.new(
155
+ options[:directories],
156
+ create_basic_json: options[:create_basic_json],
157
+ create_enhanced_json: options[:create_enhanced_json],
158
+ log_level: options[:log_level],
159
+ webhook_url: options[:webhook_url],
160
+ extensions: options[:extensions]
161
+ ).start
162
+ end
163
+
164
+ def print_help
165
+ puts 'Klue Langcraft Server'
166
+ puts 'Usage: server [command] [options]'
167
+ puts ''
168
+ puts 'Commands:'
169
+ puts ' process Process a single file'
170
+ puts ' watch Watch directories for changes'
171
+ puts ''
172
+ puts "Run 'server [command] --help' for more information on a command."
173
+ end
174
+ end
175
+
176
+ # Run the CLI
177
+ CLI.new.run
@@ -0,0 +1,97 @@
1
+
2
+ # DSL Class Diagram
3
+
4
+ ```plaintext
5
+ +----------------------------+
6
+ | Klue::Langcraft::DSL |
7
+ |----------------------------|
8
+ | + Interpreter |
9
+ | + ProcessMatcher |
10
+ | + ProcessorConfig |
11
+ | + ProcessDataPipeline |
12
+ | + Processors |
13
+ +----------------------------+
14
+
15
+ |
16
+ v
17
+ +-----------------------------------+
18
+ | Interpreter |
19
+ |-----------------------------------|
20
+ | - data: Hash |
21
+ | - processed: Boolean |
22
+ |-----------------------------------|
23
+ | + initialize() |
24
+ | + process(input, output) |
25
+ | + method_missing() |
26
+ | + process_args(args, block) |
27
+ | + respond_to_missing?() |
28
+ | + to_json() |
29
+ +-----------------------------------+
30
+
31
+ |
32
+ v
33
+ +-----------------------------------+
34
+ | ProcessMatcher |
35
+ |-----------------------------------|
36
+ | + match_processors(nodes) |
37
+ |-----------------------------------|
38
+ | - traverse_nodes(node, &block) |
39
+ | - find_processor_for(key, value) |
40
+ |-----------------------------------|
41
+ | - processor_config: ProcessorConfig|
42
+ +-----------------------------------+
43
+
44
+ |
45
+ v
46
+ +-----------------------------------+
47
+ | ProcessDataPipeline |
48
+ |-----------------------------------|
49
+ | + execute(data) |
50
+ | + write_output(data, output_file) |
51
+ |-----------------------------------|
52
+ | - store_result(data, processor, |
53
+ | processed_data) |
54
+ | - calculate_index(data, processor)|
55
+ |-----------------------------------|
56
+ | - matcher: ProcessMatcher |
57
+ +-----------------------------------+
58
+
59
+ |
60
+ v
61
+ +-----------------------------------+
62
+ | ProcessorConfig |
63
+ |-----------------------------------|
64
+ | + register_processor(processor) |
65
+ | + processor_for(key) |
66
+ | + all_processors() |
67
+ |-----------------------------------|
68
+ | - processors: Hash |
69
+ +-----------------------------------+
70
+
71
+ |
72
+ v
73
+ +----------------------------+
74
+ | Processor |
75
+ |----------------------------|
76
+ | - data: Hash |
77
+ | - key: Symbol |
78
+ |----------------------------|
79
+ | + initialize(data, key) |
80
+ | + build_result() |
81
+ | + build_result_data() |
82
+ |----------------------------|
83
+ | + keys() (abstract method) |
84
+ +----------------------------+
85
+
86
+ |
87
+ v
88
+ +----------------------------+ +-------------------------+
89
+ | FileCollectorProcessor | | FullNameProcessor |
90
+ |----------------------------| +-------------------------+
91
+ | (inherits Processor) | | (inherits Processor) |
92
+ |----------------------------| +-------------------------+
93
+ | + build_result_data() | | + build_result_data() |
94
+ | + Auto-register with | | + Auto-register with |
95
+ | ProcessorConfig | | ProcessorConfig |
96
+ +----------------------------+ +-------------------------+
97
+ ```
data/docs/dsl-examples.md CHANGED
@@ -103,3 +103,12 @@ definition :workflow do
103
103
  end
104
104
  end
105
105
  ```
106
+
107
+
108
+ You're going to help me process a Ruby inspired DSL into JSON.
109
+
110
+ There will be two parts of this conversation, part one will be about exploring the concept with a simple reflection based system that will work with any Ruby compatible structure.
111
+
112
+ Part two, which I kick off after I finish part one will be to use an AST style of approach so that I can process the same concept, JavaScript or RUST.
113
+
114
+ To get started, I'm going to give you some sample Ruby DSL and you're going to convert them to an equivalent ruby hash
@@ -0,0 +1,266 @@
1
+ # DSL System Expansion Plan
2
+
3
+ ## Overview
4
+
5
+ The current system processes DSL input using an interpreter, matchers, processors, and pipelines. We need to expand the system by introducing several new features:
6
+
7
+ 1. **Processor Service**: A centralized service that orchestrates all operations, simplifying how the watcher and command-line tools work.
8
+ 2. **Data Saving Option**: An option to toggle whether the processed JSON data is saved to a file or not.
9
+ 3. **Endpoint for Data Delivery**: Add the capability to send the processed data to external HTTP endpoints, configurable via the command line.
10
+
11
+ The changes will affect the class structure, as well as introduce new command-line options and configurations. Below is a detailed explanation of the required features and how they will be implemented.
12
+
13
+ ---
14
+
15
+ ## New Components and Changes
16
+
17
+ ### 1. **Processor Service**
18
+
19
+ - **Purpose**: We need to introduce a new service (tentatively called `ProcessorService`) to orchestrate the different components. This class will:
20
+ - Call the interpreter to process the DSL input.
21
+ - Run the data through the processing pipeline to apply any necessary transformations.
22
+ - Handle the saving of the processed data to a file if configured.
23
+ - Send the resulting JSON data to external endpoints if URLs are provided via the command line.
24
+
25
+ - **Why**: Currently, the watcher or command-line tool is responsible for calling various components (interpreter, pipeline, etc.). By introducing this service, we centralize the logic and simplify the external interfaces. The watcher will only need to call this service, and any configuration options (like file saving or sending data to an endpoint) will be handled internally by the service.
26
+
27
+ - **Key Features**:
28
+ - **Options Handling**: The service will take in various options, such as:
29
+ - Whether to save the processed JSON to a file.
30
+ - URLs for sending data (e.g., one for simplified JSON, one for enhanced JSON).
31
+ - **Sequential Processing**: The service will sequentially call:
32
+ 1. The interpreter to process the DSL.
33
+ 2. The processing pipeline to enhance the data.
34
+ 3. Save the data to a file (if the option is enabled).
35
+ 4. Send the data to the endpoint (if URLs are provided).
36
+
37
+ ### 2. **Data Saving Option**
38
+
39
+ - **Purpose**: Introduce a command-line option (`--save-to-file`) that will enable or disable saving the processed data to a file. By default, the data should flow through the system, but whether it gets written to the filesystem should be configurable.
40
+
41
+ - **Why**: Not all users may want to persist the JSON data to disk. The ability to toggle this behavior will make the system more flexible.
42
+
43
+ - **Details**:
44
+ - When the `--save-to-file` option is enabled, the processed data should be saved to a file.
45
+ - If the option is not provided, the data will not be saved to a file, but it will still flow through the system for other uses (e.g., sending to endpoints).
46
+
47
+ ### 3. **Data Delivery to Endpoints**
48
+
49
+ - **Purpose**: Add the ability to send the JSON data (either simplified or enhanced) to external HTTP endpoints. These endpoints should be configurable via command-line options (`--endpoint-simplified` and `--endpoint-enhanced`).
50
+
51
+ - **Why**: The system should be able to integrate with external services by sending the resulting data to a web application. For example, the web app could receive simplified JSON for debugging and enhanced JSON for final usage. This allows seamless data transfer between the DSL processing system and external applications.
52
+
53
+ - **Details**:
54
+ - The `ProcessorService` will check if any URLs are provided via the command line.
55
+ - If the URLs are provided, it will send the resulting JSON data (using HTTP POST) to those endpoints.
56
+ - **Two endpoints**:
57
+ - One for simplified JSON.
58
+ - One for enhanced JSON.
59
+ - The system should allow sending data to either or both endpoints, depending on the provided options.
60
+
61
+ ### 4. **Command-Line Interface (Expanded)**
62
+
63
+ - The existing `CommandLineInterface` class should be expanded to handle the new options:
64
+ - `--save-to-file`: Toggles whether the JSON is saved to a file.
65
+ - `--endpoint-simplified`: Specifies the URL for sending simplified JSON.
66
+ - `--endpoint-enhanced`: Specifies the URL for sending enhanced JSON.
67
+
68
+ - These options should be passed to the `ProcessorService`, which will handle the actual behavior (e.g., saving the file, sending data to the endpoint).
69
+
70
+ ### 5. **Watcher Integration**
71
+
72
+ - The existing `Watcher` class, which uses `ListenGem` to monitor file changes, will now delegate all processing to the `ProcessorService`. When a file change is detected, the watcher should simply call the service with the necessary options.
73
+
74
+ - This means that the watcher doesn't need to know about interpreters, pipelines, or processors—it just needs to hand over the file to the `ProcessorService`, which will handle everything sequentially.
75
+
76
+ ---
77
+
78
+ ## Revised Class Structure
79
+
80
+ ### New Classes:
81
+
82
+ 1. **ProcessorService**:
83
+ - Central orchestrator for calling the interpreter, processing data, saving the file, and sending data to endpoints.
84
+ - Will take all options from the command line and watcher.
85
+
86
+ 2. **DataDeliveryService**:
87
+ - Handles sending JSON data to external HTTP endpoints.
88
+ - Accepts a URL and sends the provided data using HTTP POST.
89
+
90
+ ### Expanded Classes:
91
+
92
+ 1. **CommandLineInterface**:
93
+ - Now handles additional command-line options (`--save-to-file`, `--endpoint-simplified`, `--endpoint-enhanced`).
94
+ - Passes these options to the `ProcessorService`.
95
+
96
+ 2. **Watcher**:
97
+ - Instead of directly calling interpreters and pipelines, the watcher will now pass the detected file changes to the `ProcessorService` for handling.
98
+
99
+ ---
100
+
101
+ ## Command-Line Options
102
+
103
+ - **New Options**:
104
+ - `--save-to-file`: If provided, the resulting JSON will be saved to a file. Otherwise, the data will only flow through the system.
105
+ - `--endpoint-simplified`: A URL for sending the simplified JSON to an external endpoint.
106
+ - `--endpoint-enhanced`: A URL for sending the enhanced JSON to an external endpoint.
107
+
108
+ These options should be passed to the `ProcessorService`, which will then handle the appropriate behavior based on the configuration.
109
+
110
+ ---
111
+
112
+ ## Final Workflow
113
+
114
+ 1. **Command-Line Usage**:
115
+ - Users can pass options such as directories to watch, whether to save files, and URLs for endpoints.
116
+
117
+ 2. **Watcher**:
118
+ - Monitors the specified directories for file changes and passes any detected changes to the `ProcessorService`.
119
+
120
+ 3. **ProcessorService**:
121
+ - Orchestrates the sequence:
122
+ 1. Calls the `Interpreter` to process the DSL input.
123
+ 2. Runs the `ProcessDataPipeline` to enhance the data.
124
+ 3. Saves the resulting data to a file if the `--save-to-file` option is enabled.
125
+ 4. Sends the resulting data to the provided URL(s) if the `--endpoint-simplified` or `--endpoint-enhanced` options are specified.
126
+
127
+ 4. **Data Delivery**:
128
+ - The `DataDeliveryService` is responsible for sending the processed data to the external endpoints, handling any HTTP interactions required.
129
+
130
+
131
+ # SAMPLE CODE, NOT TESTED BUT MIGHT BE SUITABLE
132
+ ```ruby
133
+
134
+ # file: lib/klue/langcraft/dsl/data_delivery_service.rb
135
+ require 'net/http'
136
+ require 'uri'
137
+ require 'json'
138
+
139
+ module Klue
140
+ module Langcraft
141
+ module DSL
142
+ class DataDeliveryService
143
+ def initialize(url)
144
+ @url = URI.parse(url)
145
+ end
146
+
147
+ def send(data)
148
+ http = Net::HTTP.new(@url.host, @url.port)
149
+ request = Net::HTTP::Post.new(@url.request_uri, { 'Content-Type' => 'application/json' })
150
+ request.body = data.to_json
151
+ response = http.request(request)
152
+
153
+ puts "Data sent to #{@url}: #{response.code} #{response.message}"
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
159
+ ```
160
+
161
+ ```ruby
162
+ # file: lib/klue/langcraft/dsl/processor_service.rb
163
+ require_relative 'interpreter'
164
+ require_relative 'process_data_pipeline'
165
+ require_relative 'data_delivery_service'
166
+
167
+ module Klue
168
+ module Langcraft
169
+ module DSL
170
+ class ProcessorService
171
+ def initialize(options)
172
+ @options = options
173
+ @interpreter = Interpreter.new
174
+ @pipeline = ProcessDataPipeline.new(ProcessMatcher.new)
175
+ end
176
+
177
+ def run
178
+ data = call_interpreter
179
+ enhanced_data = run_pipeline(data)
180
+ save_to_file(enhanced_data) if @options[:save_to_file]
181
+ send_to_endpoint(enhanced_data)
182
+ end
183
+
184
+ private
185
+
186
+ def call_interpreter
187
+ @interpreter.process(input: 'path_to_input_file.dsl')
188
+ end
189
+
190
+ def run_pipeline(data)
191
+ @pipeline.execute(data)
192
+ end
193
+
194
+ def save_to_file(data)
195
+ File.write('output.json', JSON.pretty_generate(data))
196
+ end
197
+
198
+ def send_to_endpoint(data)
199
+ if @options[:endpoint_simplified]
200
+ DataDeliveryService.new(@options[:endpoint_simplified]).send(data)
201
+ end
202
+
203
+ if @options[:endpoint_enhanced]
204
+ DataDeliveryService.new(@options[:endpoint_enhanced]).send(data)
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
211
+
212
+ ```
213
+
214
+ ```ruby
215
+ # file: lib/klue/langcraft/dsl/command_line_interface.rb
216
+ require_relative 'watcher'
217
+ require_relative 'processor_service'
218
+
219
+ module Klue
220
+ module Langcraft
221
+ module DSL
222
+ class CommandLineInterface
223
+ def initialize
224
+ @watch_dirs = []
225
+ @options = {}
226
+ end
227
+
228
+ def start
229
+ parse_arguments
230
+ start_watcher_or_processor_service
231
+ end
232
+
233
+ private
234
+
235
+ def parse_arguments
236
+ ARGV.each_with_index do |arg, index|
237
+ case arg
238
+ when '--watch'
239
+ @watch_dirs << ARGV[index + 1]
240
+ when '--save-to-file'
241
+ @options[:save_to_file] = true
242
+ when '--endpoint-simplified'
243
+ @options[:endpoint_simplified] = ARGV[index + 1]
244
+ when '--endpoint-enhanced'
245
+ @options[:endpoint_enhanced] = ARGV[index + 1]
246
+ end
247
+ end
248
+
249
+ raise 'No directories specified for watching' if @watch_dirs.empty?
250
+ end
251
+
252
+ def start_watcher_or_processor_service
253
+ if @watch_dirs.any?
254
+ watcher = Watcher.new(@watch_dirs)
255
+ watcher.start
256
+ else
257
+ processor_service = ProcessorService.new(@options)
258
+ processor_service.run
259
+ end
260
+ end
261
+ end
262
+ end
263
+ end
264
+ end
265
+
266
+ ```