orfeas_lyra 0.6.0

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.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +222 -0
  3. data/LICENSE +21 -0
  4. data/README.md +1165 -0
  5. data/Rakefile +728 -0
  6. data/app/controllers/lyra/application_controller.rb +23 -0
  7. data/app/controllers/lyra/dashboard_controller.rb +624 -0
  8. data/app/controllers/lyra/flow_controller.rb +224 -0
  9. data/app/controllers/lyra/privacy_controller.rb +182 -0
  10. data/app/views/lyra/dashboard/audit_trail.html.erb +324 -0
  11. data/app/views/lyra/dashboard/discrepancies.html.erb +125 -0
  12. data/app/views/lyra/dashboard/event_graph_view.html.erb +525 -0
  13. data/app/views/lyra/dashboard/heatmap_view.html.erb +155 -0
  14. data/app/views/lyra/dashboard/index.html.erb +119 -0
  15. data/app/views/lyra/dashboard/model_overview.html.erb +115 -0
  16. data/app/views/lyra/dashboard/projections.html.erb +302 -0
  17. data/app/views/lyra/dashboard/schema.html.erb +283 -0
  18. data/app/views/lyra/dashboard/schema_history.html.erb +78 -0
  19. data/app/views/lyra/dashboard/schema_version.html.erb +340 -0
  20. data/app/views/lyra/dashboard/verification.html.erb +370 -0
  21. data/app/views/lyra/flow/crud_mapping.html.erb +125 -0
  22. data/app/views/lyra/flow/timeline.html.erb +260 -0
  23. data/app/views/lyra/privacy/pii_detection.html.erb +148 -0
  24. data/app/views/lyra/privacy/policy.html.erb +188 -0
  25. data/app/workflows/es_async_mode_workflow.rb +80 -0
  26. data/app/workflows/es_sync_mode_workflow.rb +64 -0
  27. data/app/workflows/hijack_mode_workflow.rb +54 -0
  28. data/app/workflows/lifecycle_workflow.rb +43 -0
  29. data/app/workflows/monitor_mode_workflow.rb +39 -0
  30. data/config/privacy_policies.rb +273 -0
  31. data/config/routes.rb +48 -0
  32. data/lib/lyra/aggregate.rb +131 -0
  33. data/lib/lyra/associations/event_aware.rb +225 -0
  34. data/lib/lyra/command.rb +81 -0
  35. data/lib/lyra/command_handler.rb +155 -0
  36. data/lib/lyra/configuration.rb +124 -0
  37. data/lib/lyra/consistency/read_your_writes.rb +91 -0
  38. data/lib/lyra/correlation.rb +144 -0
  39. data/lib/lyra/dual_view.rb +231 -0
  40. data/lib/lyra/engine.rb +67 -0
  41. data/lib/lyra/event.rb +71 -0
  42. data/lib/lyra/event_analyzer.rb +135 -0
  43. data/lib/lyra/event_flow.rb +449 -0
  44. data/lib/lyra/event_mapper.rb +106 -0
  45. data/lib/lyra/event_store_adapter.rb +72 -0
  46. data/lib/lyra/id_generator.rb +137 -0
  47. data/lib/lyra/interceptors/association_interceptor.rb +169 -0
  48. data/lib/lyra/interceptors/crud_interceptor.rb +543 -0
  49. data/lib/lyra/privacy/gdpr_compliance.rb +161 -0
  50. data/lib/lyra/privacy/pii_detector.rb +85 -0
  51. data/lib/lyra/privacy/pii_masker.rb +66 -0
  52. data/lib/lyra/privacy/policy_integration.rb +253 -0
  53. data/lib/lyra/projection.rb +94 -0
  54. data/lib/lyra/projections/async_projection_job.rb +63 -0
  55. data/lib/lyra/projections/cached_projection.rb +322 -0
  56. data/lib/lyra/projections/cached_relation.rb +757 -0
  57. data/lib/lyra/projections/event_store_reader.rb +127 -0
  58. data/lib/lyra/projections/model_projection.rb +143 -0
  59. data/lib/lyra/schema/diff.rb +331 -0
  60. data/lib/lyra/schema/event_class_registrar.rb +63 -0
  61. data/lib/lyra/schema/generator.rb +190 -0
  62. data/lib/lyra/schema/reporter.rb +188 -0
  63. data/lib/lyra/schema/store.rb +156 -0
  64. data/lib/lyra/schema/validator.rb +100 -0
  65. data/lib/lyra/strict_data_access.rb +363 -0
  66. data/lib/lyra/verification/crud_lifecycle_workflow.rb +456 -0
  67. data/lib/lyra/verification/workflow_generator.rb +540 -0
  68. data/lib/lyra/version.rb +3 -0
  69. data/lib/lyra/visualization/activity_heatmap.rb +215 -0
  70. data/lib/lyra/visualization/event_graph.rb +310 -0
  71. data/lib/lyra/visualization/timeline.rb +398 -0
  72. data/lib/lyra.rb +150 -0
  73. data/lib/tasks/dist.rake +391 -0
  74. data/lib/tasks/gems.rake +185 -0
  75. data/lib/tasks/lyra_schema.rake +231 -0
  76. data/lib/tasks/lyra_workflows.rake +452 -0
  77. data/lib/tasks/public_release.rake +351 -0
  78. data/lib/tasks/stats.rake +175 -0
  79. data/lib/tasks/testbed.rake +479 -0
  80. data/lib/tasks/version.rake +159 -0
  81. metadata +221 -0
@@ -0,0 +1,351 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Rake tasks for extracting public files to lyra-engine repo
4
+ #
5
+ # Usage:
6
+ # rake public:build # Build lyra-engine directory
7
+ # rake public:sync # Sync changes to existing lyra-engine
8
+ # rake public:init # Initialize lyra-engine as git repo
9
+ # rake public:clean # Remove lyra-engine directory
10
+ # rake public:release[tag] # Full release workflow
11
+
12
+ require "fileutils"
13
+
14
+ namespace :public do
15
+ LYRA_ROOT = File.expand_path("../..", __dir__)
16
+ PUBLIC_DIR = File.join(LYRA_ROOT, "lyra-engine")
17
+
18
+ # Files and directories to include in public release
19
+ PUBLIC_INCLUDES = %w[
20
+ app/
21
+ bin/
22
+ config/
23
+ docs/
24
+ examples/blog_app/
25
+ examples/privacy_examples.rb
26
+ examples/privacy_policy_usage.rb
27
+ examples/usage_examples.rb
28
+ gems/
29
+ lib/
30
+ test/
31
+ .gitignore
32
+ .ruby-version
33
+ docker-compose.yml
34
+ Gemfile
35
+ Gemfile.lock
36
+ LICENSE
37
+ lyra.gemspec
38
+ Rakefile
39
+ README.md
40
+ CHANGELOG.md
41
+ ].freeze
42
+
43
+ # Patterns to exclude from public release (applied after includes)
44
+ PUBLIC_EXCLUDES = %w[
45
+ .DS_Store
46
+ *.log
47
+ /coverage/
48
+ /tmp/
49
+ /log/
50
+ /vendor/bundle/
51
+ /gems/*/.bundle/
52
+ /gems/*/coverage/
53
+ /test/dummy/db/*.sqlite3
54
+ /test/reports/
55
+ /lib/tasks/gems.rake
56
+ /lib/tasks/public_release.rake
57
+ /lib/tasks/testbed.rake
58
+ /lib/tasks/stats.rake
59
+ /lib/tasks/dist.rake
60
+ ].freeze
61
+
62
+ # Files that need sanitization (remove internal references)
63
+ SANITIZE_FILES = %w[
64
+ README.md
65
+ ].freeze
66
+
67
+ desc "Build lyra-engine directory with public files"
68
+ task :build => :clean do
69
+ puts "=" * 60
70
+ puts "Building lyra-engine public release"
71
+ puts "=" * 60
72
+
73
+ FileUtils.mkdir_p(PUBLIC_DIR)
74
+
75
+ PUBLIC_INCLUDES.each do |item|
76
+ src = File.join(LYRA_ROOT, item)
77
+ next unless File.exist?(src)
78
+
79
+ dest = File.join(PUBLIC_DIR, item)
80
+ if File.directory?(src)
81
+ sync_directory_public(src, dest)
82
+ puts " + #{item}"
83
+ else
84
+ FileUtils.mkdir_p(File.dirname(dest))
85
+ FileUtils.cp(src, dest)
86
+ puts " + #{item}"
87
+ end
88
+ end
89
+
90
+ # Sanitize files for public release
91
+ sanitize_readme
92
+ sanitize_changelog
93
+ sanitize_gitignore
94
+ sanitize_gemspec
95
+ remove_internal_rake_tasks
96
+
97
+ puts ""
98
+ puts "=" * 60
99
+ puts "lyra-engine built successfully!"
100
+ puts "Location: #{PUBLIC_DIR}"
101
+ puts "=" * 60
102
+ end
103
+
104
+ desc "Sync changes to existing lyra-engine directory"
105
+ task :sync do
106
+ unless File.directory?(PUBLIC_DIR)
107
+ puts "lyra-engine directory not found. Run 'rake public:build' first."
108
+ exit 1
109
+ end
110
+
111
+ puts "Syncing changes to lyra-engine..."
112
+ Rake::Task["public:build"].invoke
113
+ end
114
+
115
+ desc "Initialize lyra-engine as a git repository"
116
+ task :init => :build do
117
+ Dir.chdir(PUBLIC_DIR) do
118
+ unless File.directory?(".git")
119
+ system("git init")
120
+ system("git add .")
121
+ system("git commit -m 'Initial commit - Lyra Engine v#{lyra_version}'")
122
+ puts ""
123
+ puts "Git repository initialized."
124
+ puts "Add remote with: git remote add origin <url>"
125
+ else
126
+ puts "Git repository already initialized."
127
+ end
128
+ end
129
+ end
130
+
131
+ desc "Clean lyra-engine directory"
132
+ task :clean do
133
+ if File.directory?(PUBLIC_DIR)
134
+ # Preserve .git directory if it exists
135
+ git_dir = File.join(PUBLIC_DIR, ".git")
136
+ git_backup = File.join(LYRA_ROOT, ".lyra-engine-git-backup")
137
+
138
+ if File.directory?(git_dir)
139
+ FileUtils.mv(git_dir, git_backup)
140
+ end
141
+
142
+ # Remove all other contents
143
+ Dir.glob("#{PUBLIC_DIR}/*", File::FNM_DOTMATCH).each do |item|
144
+ next if item.end_with?(".", "..")
145
+ FileUtils.rm_rf(item)
146
+ end
147
+
148
+ # Restore .git directory
149
+ if File.directory?(git_backup)
150
+ FileUtils.mv(git_backup, git_dir)
151
+ puts "Cleaned lyra-engine (preserved .git)"
152
+ else
153
+ puts "Cleaned lyra-engine"
154
+ end
155
+ end
156
+ end
157
+
158
+ desc "Full release workflow: build, init, tag"
159
+ task :release, [:tag] => :init do |t, args|
160
+ tag = args[:tag] || "v#{lyra_version}"
161
+
162
+ Dir.chdir(PUBLIC_DIR) do
163
+ # Stage any new changes
164
+ system("git add .")
165
+
166
+ # Check if there are changes to commit
167
+ changes = `git status --porcelain`.strip
168
+ if changes.empty?
169
+ puts "No changes to commit."
170
+ else
171
+ system("git commit -m 'Release #{tag}'")
172
+ end
173
+
174
+ # Create tag
175
+ system("git tag -a #{tag} -m 'Release #{tag}'")
176
+ puts ""
177
+ puts "=" * 60
178
+ puts "Release #{tag} prepared!"
179
+ puts ""
180
+ puts "To push to remote:"
181
+ puts " cd lyra-engine"
182
+ puts " git remote add origin git@github.com:mpantel/lyra-engine.git"
183
+ puts " git push -u origin main"
184
+ puts " git push origin #{tag}"
185
+ puts "=" * 60
186
+ end
187
+ end
188
+
189
+ desc "Show what would be included in public release"
190
+ task :preview do
191
+ puts "Files to be included in lyra-engine:"
192
+ puts "-" * 40
193
+
194
+ PUBLIC_INCLUDES.each do |item|
195
+ src = File.join(LYRA_ROOT, item)
196
+ if File.exist?(src)
197
+ if File.directory?(src)
198
+ count = Dir.glob("#{src}/**/*").count { |f| File.file?(f) }
199
+ puts " #{item} (#{count} files)"
200
+ else
201
+ puts " #{item}"
202
+ end
203
+ else
204
+ puts " #{item} (not found)"
205
+ end
206
+ end
207
+
208
+ puts ""
209
+ puts "Excluded patterns:"
210
+ PUBLIC_EXCLUDES.each { |p| puts " - #{p}" }
211
+ end
212
+
213
+ # Helper methods
214
+
215
+ def sync_directory_public(src, dest)
216
+ FileUtils.mkdir_p(dest)
217
+
218
+ Dir.glob("#{src}/**/*", File::FNM_DOTMATCH).each do |path|
219
+ next if path.end_with?(".", "..")
220
+ next if File.directory?(path)
221
+ next if excluded_public?(path)
222
+
223
+ rel_path = path.sub("#{src}/", "")
224
+ dest_path = File.join(dest, rel_path)
225
+
226
+ FileUtils.mkdir_p(File.dirname(dest_path))
227
+ FileUtils.cp(path, dest_path)
228
+ end
229
+ end
230
+
231
+ def excluded_public?(path)
232
+ relative = path.sub(LYRA_ROOT, "")
233
+
234
+ PUBLIC_EXCLUDES.any? do |pattern|
235
+ if pattern.include?("*")
236
+ File.fnmatch(pattern, relative, File::FNM_PATHNAME)
237
+ else
238
+ relative.include?(pattern)
239
+ end
240
+ end
241
+ end
242
+
243
+ def lyra_version
244
+ # Extract version from lyra.gemspec or version file
245
+ version_file = File.join(LYRA_ROOT, "lib/lyra/version.rb")
246
+ if File.exist?(version_file)
247
+ content = File.read(version_file)
248
+ if content =~ /VERSION\s*=\s*["']([^"']+)["']/
249
+ return $1
250
+ end
251
+ end
252
+ "0.6.0"
253
+ end
254
+
255
+ def sanitize_readme
256
+ readme_path = File.join(PUBLIC_DIR, "README.md")
257
+ return unless File.exist?(readme_path)
258
+
259
+ content = File.read(readme_path)
260
+
261
+ # Remove aegean-epay-testbed section (case insensitive)
262
+ content.gsub!(/^##+ .*aegean.?e?pay.?testbed.*?(?=^##|\z)/mi, "")
263
+
264
+ # Remove solidus case study section (case insensitive)
265
+ content.gsub!(/^##+ .*solidus.*?(?=^##|\z)/mi, "")
266
+
267
+ # Remove any references to private examples
268
+ content.gsub!(/examples\/aegean_epay_testbed\/?/, "examples/blog_app/")
269
+ content.gsub!(/examples\/solidus_case_study\/?/, "examples/blog_app/")
270
+
271
+ # Clean up multiple consecutive blank lines
272
+ content.gsub!(/\n{3,}/, "\n\n")
273
+
274
+ File.write(readme_path, content)
275
+ puts " Sanitized README.md"
276
+ end
277
+
278
+ def sanitize_changelog
279
+ changelog_path = File.join(PUBLIC_DIR, "CHANGELOG.md")
280
+ return unless File.exist?(changelog_path)
281
+
282
+ content = File.read(changelog_path)
283
+
284
+ # Remove lines referencing private examples
285
+ lines = content.lines.reject do |line|
286
+ line =~ /aegean.?epay.?testbed|solidus.?case.?study/i
287
+ end
288
+
289
+ # Replace references in remaining text
290
+ content = lines.join
291
+ content.gsub!(/aegean.?epay.?testbed/i, "blog_app")
292
+ content.gsub!(/solidus.?case.?study/i, "blog_app")
293
+
294
+ # Clean up multiple consecutive blank lines
295
+ content.gsub!(/\n{3,}/, "\n\n")
296
+
297
+ File.write(changelog_path, content)
298
+ puts " Sanitized CHANGELOG.md"
299
+ end
300
+
301
+ def sanitize_gitignore
302
+ gitignore_path = File.join(PUBLIC_DIR, ".gitignore")
303
+ return unless File.exist?(gitignore_path)
304
+
305
+ content = File.read(gitignore_path)
306
+
307
+ # Remove lines referencing private directories
308
+ lines = content.lines.reject do |line|
309
+ line =~ /aegean.?epay.?testbed|solidus|docs-site|paper|papertse|papersp|thesis|proposal|research/i
310
+ end
311
+
312
+ File.write(gitignore_path, lines.join)
313
+ puts " Sanitized .gitignore"
314
+ end
315
+
316
+ def sanitize_gemspec
317
+ gemspec_path = File.join(PUBLIC_DIR, "lyra.gemspec")
318
+ return unless File.exist?(gemspec_path)
319
+
320
+ content = File.read(gemspec_path)
321
+
322
+ # Update homepage to public repo
323
+ content.gsub!(
324
+ /spec\.homepage\s*=\s*["'][^"']*["']/,
325
+ 'spec.homepage = "https://github.com/mpantel/lyra-engine"'
326
+ )
327
+
328
+ # Update metadata URLs
329
+ content.gsub!(
330
+ /github\.com\/mpantel\/lyra(?!-engine)/,
331
+ "github.com/mpantel/lyra-engine"
332
+ )
333
+
334
+ File.write(gemspec_path, content)
335
+ puts " Sanitized lyra.gemspec"
336
+ end
337
+
338
+ def remove_internal_rake_tasks
339
+ tasks_dir = File.join(PUBLIC_DIR, "lib/tasks")
340
+ return unless File.directory?(tasks_dir)
341
+
342
+ internal_tasks = %w[gems.rake public_release.rake testbed.rake stats.rake dist.rake]
343
+ internal_tasks.each do |task|
344
+ task_path = File.join(tasks_dir, task)
345
+ if File.exist?(task_path)
346
+ FileUtils.rm(task_path)
347
+ end
348
+ end
349
+ puts " Removed internal rake tasks"
350
+ end
351
+ end
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :lyra do
4
+ desc "Display code statistics for Lyra and its components"
5
+ task :stats do
6
+ class CodeStats
7
+ COMPONENTS = {
8
+ "Lyra Core (lib/)" => "lib",
9
+ "Controllers (app/)" => "app",
10
+ "Tests (test/)" => "test",
11
+ "PetriFlow Gem" => "gems/petri_flow",
12
+ "PAM DSL Gem" => "gems/pam_dsl",
13
+ "Examples" => "examples"
14
+ }.freeze
15
+
16
+ def initialize
17
+ @stats = {}
18
+ end
19
+
20
+ def run
21
+ print_header
22
+ calculate_all_stats
23
+ print_stats_table
24
+ print_summary
25
+ end
26
+
27
+ private
28
+
29
+ def print_header
30
+ puts "\n" + "=" * 70
31
+ puts "Lyra Code Statistics"
32
+ puts "=" * 70
33
+ puts "Generated: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}"
34
+ puts "=" * 70
35
+ end
36
+
37
+ def calculate_all_stats
38
+ COMPONENTS.each do |name, path|
39
+ next unless Dir.exist?(path)
40
+
41
+ @stats[name] = calculate_stats(path)
42
+ end
43
+ end
44
+
45
+ def calculate_stats(path)
46
+ ruby_files = Dir.glob("#{path}/**/*.rb")
47
+
48
+ total_lines = 0
49
+ code_lines = 0
50
+ comment_lines = 0
51
+ blank_lines = 0
52
+
53
+ ruby_files.each do |file|
54
+ File.readlines(file).each do |line|
55
+ total_lines += 1
56
+ stripped = line.strip
57
+
58
+ if stripped.empty?
59
+ blank_lines += 1
60
+ elsif stripped.start_with?("#")
61
+ comment_lines += 1
62
+ else
63
+ code_lines += 1
64
+ end
65
+ end
66
+ end
67
+
68
+ {
69
+ files: ruby_files.count,
70
+ total_lines: total_lines,
71
+ code_lines: code_lines,
72
+ comment_lines: comment_lines,
73
+ blank_lines: blank_lines
74
+ }
75
+ end
76
+
77
+ def print_stats_table
78
+ puts "\n%-25s %8s %10s %10s %10s %10s" % ["Component", "Files", "Total", "Code", "Comments", "Blank"]
79
+ puts "-" * 75
80
+
81
+ @stats.each do |name, data|
82
+ puts "%-25s %8d %10d %10d %10d %10d" % [
83
+ name,
84
+ data[:files],
85
+ data[:total_lines],
86
+ data[:code_lines],
87
+ data[:comment_lines],
88
+ data[:blank_lines]
89
+ ]
90
+ end
91
+
92
+ puts "-" * 75
93
+ end
94
+
95
+ def print_summary
96
+ totals = @stats.values.reduce({
97
+ files: 0, total_lines: 0, code_lines: 0, comment_lines: 0, blank_lines: 0
98
+ }) do |sum, data|
99
+ sum.each_key { |k| sum[k] += data[k] }
100
+ sum
101
+ end
102
+
103
+ puts "%-25s %8d %10d %10d %10d %10d" % [
104
+ "TOTAL",
105
+ totals[:files],
106
+ totals[:total_lines],
107
+ totals[:code_lines],
108
+ totals[:comment_lines],
109
+ totals[:blank_lines]
110
+ ]
111
+
112
+ # Additional metrics
113
+ puts "\n" + "=" * 70
114
+ puts "Summary Metrics"
115
+ puts "=" * 70
116
+
117
+ lib_stats = @stats["Lyra Core (lib/)"] || { code_lines: 0 }
118
+ test_stats = @stats["Tests (test/)"] || { code_lines: 0 }
119
+
120
+ if lib_stats[:code_lines] > 0
121
+ ratio = (test_stats[:code_lines].to_f / lib_stats[:code_lines]).round(2)
122
+ puts "Test to Code Ratio: #{ratio}:1"
123
+ end
124
+
125
+ if totals[:code_lines] > 0
126
+ comment_ratio = ((totals[:comment_lines].to_f / totals[:code_lines]) * 100).round(1)
127
+ puts "Comment Density: #{comment_ratio}%"
128
+ end
129
+
130
+ puts "Total Ruby Files: #{totals[:files]}"
131
+ puts "Total Lines of Code: #{totals[:code_lines]}"
132
+ puts "=" * 70 + "\n\n"
133
+ end
134
+ end
135
+
136
+ CodeStats.new.run
137
+ end
138
+
139
+ desc "Display detailed stats for a specific component"
140
+ task :stats_detail, [:component] do |_t, args|
141
+ component = args[:component] || "lib"
142
+
143
+ puts "\n" + "=" * 70
144
+ puts "Detailed Stats for: #{component}"
145
+ puts "=" * 70
146
+
147
+ ruby_files = Dir.glob("#{component}/**/*.rb").sort
148
+
149
+ if ruby_files.empty?
150
+ puts "No Ruby files found in #{component}"
151
+ next
152
+ end
153
+
154
+ puts "\n%-60s %8s" % ["File", "Lines"]
155
+ puts "-" * 70
156
+
157
+ total = 0
158
+ ruby_files.each do |file|
159
+ lines = File.readlines(file).count
160
+ total += lines
161
+ relative_path = file.sub("#{component}/", "")
162
+ puts "%-60s %8d" % [relative_path.truncate(60), lines]
163
+ end
164
+
165
+ puts "-" * 70
166
+ puts "%-60s %8d" % ["TOTAL (#{ruby_files.count} files)", total]
167
+ puts "=" * 70 + "\n\n"
168
+ end
169
+ end
170
+
171
+ class String
172
+ def truncate(max)
173
+ length > max ? "...#{self[-(max-3)..]}" : self
174
+ end
175
+ end