klue-langcraft 0.2.0 → 0.3.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: '07528e25c004b36664752d235737a9f6f521e231eb2b9e900884861e2fbeccad'
4
- data.tar.gz: bd96ac7b1247f8f4fe123b4bb7923df50f1067120b1faa6e1121c6936fe400d1
3
+ metadata.gz: 586607de2048a4670a8cc9cd1e06dd9a2d1636aa0c9825698af2b6bac51ea24d
4
+ data.tar.gz: 1973c38bf614abc045c9d8e2759f984791b52da023d10441d2564bf54f99c323
5
5
  SHA512:
6
- metadata.gz: f270af8583b0ef01f5c290f0d69abe2de5534682169440381532fea9510c0b2b03f7c247c13dcde3fd13dc88b89fdadc3255af804138b6373513ea16a32b31d6
7
- data.tar.gz: e23279553bc9f644ebe56ce796dd99cda278c10170142806e2a9ed59839a3a51a541de9b0b75082474e46f130cd6e3e5d037d2c5e9c475585280ba032d84a6d8
6
+ metadata.gz: 9c20cb42da33cc805d4ec372f6101f796b44d127724fd1a611cd2620e4614e225148fc2162ab6f0e8d9bdf2fa1909575f0ed78da93ba057b4af38415bae739bd
7
+ data.tar.gz: 4ebe337655eba091ec8013700734afcea0a67006bc076ac5d94e075d4210fba5ee490e43f4eb92c716e2230d5c61918ce201fecde6b6919a081bfea1a95088c9
data/.rubocop.yml CHANGED
@@ -40,8 +40,23 @@ Metrics/BlockLength:
40
40
  - shared_examples_for
41
41
  - transaction
42
42
 
43
+ Metrics/AbcSize:
44
+ Exclude:
45
+ - bin/*
46
+
47
+
43
48
  Metrics/MethodLength:
44
49
  Max: 25
50
+ Exclude:
51
+ - bin/*
52
+
53
+ Metrics/PerceivedComplexity:
54
+ Exclude:
55
+ - bin/*
56
+
57
+ Metrics/CyclomaticComplexity:
58
+ Exclude:
59
+ - bin/*
45
60
 
46
61
  Layout/LineLength:
47
62
  Max: 200
@@ -55,7 +70,7 @@ Lint/UnusedMethodArgument:
55
70
  Style/BlockComments:
56
71
  Enabled: false
57
72
  Include:
58
- - "**/spec/*"
73
+ - "**/spec/**/*"
59
74
 
60
75
  # My Preferences - Start
61
76
  Metrics/ClassLength:
@@ -75,8 +90,8 @@ Style/EmptyMethod:
75
90
  Exclude:
76
91
  - "**/spec/**/*"
77
92
  Metrics/ParameterLists:
78
- Exclude:
79
- - "**/spec/**/*"
93
+ Enabled: false
94
+
80
95
  Layout/EmptyLineBetweenDefs:
81
96
  Exclude:
82
97
  - "**/spec/**/*"
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
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
+
1
15
  ## [0.1.1](https://github.com/appydave/klue-langcraft/compare/v0.1.0...v0.1.1) (2024-09-22)
2
16
 
3
17
 
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
@@ -17,18 +17,18 @@ module Klue
17
17
  attr_accessor :processed
18
18
 
19
19
  def initialize
20
- @data = {}
21
- @processed = false
20
+ klue_reset
22
21
  end
23
22
 
24
23
  def process(input: nil, input_file: nil, output_file: nil)
25
- validate_input_arguments(input, input_file)
26
- input_content = input_content(input, input_file)
24
+ klue_reset
25
+ klue_validate_input_arguments(input, input_file)
26
+ klue_input_content = klue_input_content(input, input_file)
27
27
 
28
28
  @processed = true
29
- instance_eval(input_content)
29
+ instance_eval(klue_input_content)
30
30
 
31
- write_output(output_file) if output_file
31
+ klue_write_output(output_file) if output_file
32
32
  data
33
33
  end
34
34
 
@@ -36,7 +36,7 @@ module Klue
36
36
  raise "You must call 'process' before using other methods" unless @processed
37
37
 
38
38
  key = method_name
39
- value = process_args(args, block)
39
+ value = klue_process_args(args, block)
40
40
 
41
41
  if @data[key]
42
42
  @data[key] = [@data[key]] unless @data[key].is_a?(Array)
@@ -50,7 +50,7 @@ module Klue
50
50
  @processed || super
51
51
  end
52
52
 
53
- def process_args(args, block)
53
+ def klue_process_args(args, block)
54
54
  positional_args = []
55
55
  named_args = {}
56
56
 
@@ -82,32 +82,32 @@ module Klue
82
82
 
83
83
  private
84
84
 
85
- def validate_input_arguments(input, input_file)
85
+ def klue_reset
86
+ @data = {}
87
+ @processed = false
88
+ end
89
+
90
+ def klue_validate_input_arguments(input, input_file)
86
91
  raise ArgumentError, 'Either input or input_file must be provided' unless input || input_file
87
92
  raise ArgumentError, 'Both input and input_file cannot be provided' if input && input_file
88
93
  end
89
94
 
90
- def input_content(input, input_file)
95
+ def klue_input_content(input, input_file)
91
96
  input_file ? File.read(input_file) : input
92
97
  end
93
98
 
94
- def write_output(output_file)
95
- output_path = get_output_path(output_file)
99
+ def klue_write_output(output_file)
100
+ output_path = klue_output_path(output_file)
96
101
  File.write(output_path, JSON.pretty_generate(data))
97
102
  end
98
103
 
99
- def get_output_path(output_file)
104
+ def klue_output_path(output_file)
100
105
  if Pathname.new(output_file).absolute?
101
106
  output_file
102
107
  else
103
108
  File.join(File.dirname(output_file), File.basename(output_file))
104
109
  end
105
110
  end
106
-
107
- # Convert to JSON
108
- def to_json(*_args)
109
- @data.to_json
110
- end
111
111
  end
112
112
  end
113
113
  end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Klue
4
+ module Langcraft
5
+ module DSL
6
+ # KlueRunner handles the processing of DSL input data
7
+ # It manages the execution of various processors and the output of processed data
8
+ class KlueRunner
9
+ attr_reader :interpreter, :pipeline, :webhook
10
+
11
+ def initialize
12
+ @interpreter = Klue::Langcraft::DSL::Interpreter.new
13
+ @pipeline = Klue::Langcraft::DSL::ProcessDataPipeline.new(Klue::Langcraft::DSL::ProcessMatcher.new)
14
+ @webhook = Klue::Langcraft::DSL::Webhook.new
15
+ end
16
+
17
+ # Run the KlueRunner with the given input data
18
+ # @param input [String] The input data to process
19
+ # @param input_file [String] The input file to process (input data and file are mutually exclusive)
20
+ # @param basic_output_file [String] The output file to write the processed data, this file is before any processing
21
+ # @param enhanced_output_file [String] The output file to write the processed data, this file is after processing
22
+ def run(
23
+ input: nil,
24
+ input_file: nil,
25
+ basic_output_file: nil,
26
+ enhanced_output_file: nil,
27
+ webhook_url: nil,
28
+ log_level: :none
29
+ )
30
+ @log_level = log_level
31
+
32
+ log_info('Processing input')
33
+ data = interpreter.process(input: input, input_file: input_file, output_file: basic_output_file)
34
+ log_detailed('Interpreter output:', data)
35
+
36
+ log_info('Executing pipeline - enhance')
37
+ enhanced_data = pipeline.execute(data)
38
+ log_detailed('Enhanced output:', enhanced_data)
39
+
40
+ if enhanced_output_file
41
+ log_info("Writing enhanced output to file: #{enhanced_output_file}")
42
+ @pipeline.write_output(enhanced_data, enhanced_output_file)
43
+ end
44
+
45
+ if webhook_url
46
+ log_info("Delivering data to webhook: #{webhook_url}")
47
+ @webhook.deliver(webhook_url, enhanced_data)
48
+ end
49
+
50
+ log_info('Processing complete')
51
+ end
52
+
53
+ private
54
+
55
+ def log_info(message)
56
+ puts message if %i[info detailed].include?(@log_level)
57
+ end
58
+
59
+ def log_detailed(message, data)
60
+ return unless @log_level == :detailed
61
+
62
+ puts message
63
+ pp data
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'listen'
4
+
5
+ module Klue
6
+ module Langcraft
7
+ module DSL
8
+ # Watcher class for monitoring file changes in specified directories.
9
+ # This class sets up a file system listener to watch for changes to files
10
+ # with specified extensions, and processes those files using a KlueRunner.
11
+ class Watcher
12
+ def initialize(directories, **options)
13
+ @directories = directories.map { |dir| File.expand_path(dir) }
14
+ @options = options
15
+ @klue_runner = Klue::Langcraft::DSL::KlueRunner.new
16
+ @extensions = options[:extensions] || ['.klue']
17
+ end
18
+
19
+ def start
20
+ listener = create_listener
21
+ log_watcher_info
22
+ listener.start
23
+ sleep
24
+ end
25
+
26
+ private
27
+
28
+ def create_listener
29
+ extension_regex = create_extension_regex
30
+ Listen.to(*@directories, only: extension_regex) do |modified, added, _removed|
31
+ process_changed_files(modified + added)
32
+ end
33
+ end
34
+
35
+ def create_extension_regex
36
+ Regexp.union(@extensions.map { |ext| /#{Regexp.escape(ext)}$/ })
37
+ end
38
+
39
+ def process_changed_files(files)
40
+ files.each { |file| process_file(file) }
41
+ end
42
+
43
+ def process_file(file)
44
+ log_info('Processing file', file)
45
+ options = create_file_options(file)
46
+ run_klue_runner(options)
47
+ rescue StandardError => e
48
+ log_error('Error processing file', "#{file}: #{e.message}")
49
+ end
50
+
51
+ def create_file_options(file)
52
+ options = @options.dup
53
+ options[:input_file] = file
54
+ options[:basic_output_file] = file.sub(/#{File.extname(file)}$/, '.json') if @options[:create_basic_json]
55
+ options[:enhanced_output_file] = file.sub(/#{File.extname(file)}$/, '.enhanced.json') if @options[:create_enhanced_json]
56
+ options
57
+ end
58
+
59
+ def run_klue_runner(options)
60
+ @klue_runner.run(
61
+ input_file: options[:input_file],
62
+ basic_output_file: options[:basic_output_file],
63
+ enhanced_output_file: options[:enhanced_output_file],
64
+ webhook_url: options[:webhook_url],
65
+ log_level: options[:log_level]
66
+ )
67
+ end
68
+
69
+ def log_watcher_info
70
+ log_info('Watching directories', @directories.join(', '))
71
+ log_info('Watching file extensions', @extensions.join(', '))
72
+ log_info('Create basic JSON', @options[:create_basic_json])
73
+ log_info('Create enhanced JSON', @options[:create_enhanced_json])
74
+ log_info('Webhook URL', @options[:webhook_url])
75
+ log_info('Log level', @options[:log_level])
76
+ end
77
+
78
+ def log_info(label, value)
79
+ puts "#{label.ljust(30)}: #{value}" if %i[info detailed].include?(@options[:log_level])
80
+ end
81
+
82
+ def log_error(label, value)
83
+ puts "#{label.ljust(30)}: #{value}"
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'json'
5
+ require 'uri'
6
+
7
+ module Klue
8
+ module Langcraft
9
+ module DSL
10
+ # Webhook class for handling HTTP POST requests to specified URLs.
11
+ # This class is responsible for sending processed data to external services
12
+ # via webhooks, handling the delivery process, and logging the results.
13
+ class Webhook
14
+ def deliver(webhook_url, data)
15
+ klue_type = extract_klue_type(data)
16
+ uri = build_uri(webhook_url, klue_type)
17
+ response = send_request(uri, build_payload(klue_type, data))
18
+ log_response(response, uri)
19
+ response
20
+ end
21
+
22
+ private
23
+
24
+ def extract_klue_type(data)
25
+ data.keys.first.to_s
26
+ end
27
+
28
+ def build_uri(webhook_url, klue_type)
29
+ URI.parse("#{webhook_url}?klue-type=#{klue_type}")
30
+ end
31
+
32
+ def build_payload(klue_type, data)
33
+ { klue_type: klue_type, data: data }
34
+ end
35
+
36
+ def send_request(uri, payload)
37
+ http = Net::HTTP.new(uri.host, uri.port)
38
+ request = Net::HTTP::Post.new(uri.path, { 'Content-Type' => 'application/json' })
39
+ request.body = payload.to_json
40
+ http.request(request)
41
+ end
42
+
43
+ def log_response(response, uri)
44
+ puts "Response: #{response.code} - #{response.message}"
45
+ puts "Endpoint: #{uri}"
46
+ log_response_body(response)
47
+ end
48
+
49
+ def log_response_body(response)
50
+ body = JSON.parse(response.body)
51
+ puts "DSL Type: #{body['type']}"
52
+ # puts JSON.pretty_generate(body['data'])
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Klue
4
4
  module Langcraft
5
- VERSION = '0.2.0'
5
+ VERSION = '0.3.0'
6
6
  end
7
7
  end
@@ -19,6 +19,9 @@ require 'klue/langcraft/dsl/processors/full_name_processor'
19
19
  require 'klue/langcraft/dsl/interpreter'
20
20
  require 'klue/langcraft/dsl/process_matcher'
21
21
  require 'klue/langcraft/dsl/process_data_pipeline'
22
+ require 'klue/langcraft/dsl/webhook'
23
+ require 'klue/langcraft/dsl/watcher'
24
+ require 'klue/langcraft/dsl/klue_runner'
22
25
 
23
26
  require 'base_process'
24
27
  require 'process_file_collector'
data/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "klue-langcraft",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "klue-langcraft",
9
- "version": "0.2.0",
9
+ "version": "0.3.0",
10
10
  "devDependencies": {
11
11
  "@klueless-js/semantic-release-rubygem": "github:klueless-js/semantic-release-rubygem",
12
12
  "@semantic-release/changelog": "^6.0.3",
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "klue-langcraft",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Domain Specific Language Crafting",
5
5
  "scripts": {
6
6
  "release": "semantic-release"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: klue-langcraft
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-10-25 00:00:00.000000000 Z
11
+ date: 2024-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: appydave-tools
@@ -62,6 +62,7 @@ files:
62
62
  - Rakefile
63
63
  - bin/console
64
64
  - bin/dsl_watcher.rb
65
+ - bin/langcraft.rb
65
66
  - bin/setup
66
67
  - docs/dsl-class-diagram.md
67
68
  - docs/dsl-examples.md
@@ -84,12 +85,15 @@ files:
84
85
  - lib/dsl_process_data.rb
85
86
  - lib/klue/langcraft.rb
86
87
  - lib/klue/langcraft/dsl/interpreter.rb
88
+ - lib/klue/langcraft/dsl/klue_runner.rb
87
89
  - lib/klue/langcraft/dsl/process_data_pipeline.rb
88
90
  - lib/klue/langcraft/dsl/process_matcher.rb
89
91
  - lib/klue/langcraft/dsl/processor_config.rb
90
92
  - lib/klue/langcraft/dsl/processors/file_collector_processor.rb
91
93
  - lib/klue/langcraft/dsl/processors/full_name_processor.rb
92
94
  - lib/klue/langcraft/dsl/processors/processor.rb
95
+ - lib/klue/langcraft/dsl/watcher.rb
96
+ - lib/klue/langcraft/dsl/webhook.rb
93
97
  - lib/klue/langcraft/tokenizer-old-needs-revisit/-brief.md
94
98
  - lib/klue/langcraft/tokenizer-old-needs-revisit/parser.rb
95
99
  - lib/klue/langcraft/tokenizer-old-needs-revisit/sample_usage.rb