hokipoki 0.3.4 โ†’ 0.5.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.
@@ -0,0 +1,425 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hokipoki
4
+ # Template Store - Dynamic content generation from compressed templates
5
+ # Implements the "SECRET SAUCE" 75% compression breakthrough
6
+ class TemplateStore
7
+
8
+ # Content templates for dynamic generation
9
+ TEMPLATES = {
10
+ # Ruby/Rails templates
11
+ 'active_record_model' => <<~TEMPLATE,
12
+ ActiveRecord model context: {{model_name}} with associations and validations.
13
+ Key methods: {{methods}}. Attributes: {{attributes}}.
14
+ Best practices: Use strong parameters, validate input, follow Rails conventions.
15
+ Common patterns: belongs_to, has_many, validates presence/uniqueness.
16
+ TEMPLATE
17
+
18
+ 'rails_controller' => <<~TEMPLATE,
19
+ Rails controller context: {{controller_name}} handling {{actions}}.
20
+ Key methods: {{methods}}. Security: Use strong parameters, authenticate users.
21
+ RESTful actions: index, show, new, create, edit, update, destroy.
22
+ Response formats: render, redirect_to, respond_with.
23
+ TEMPLATE
24
+
25
+ 'ruby_class' => <<~TEMPLATE,
26
+ Ruby class context: {{class_name}} with methods {{methods}}.
27
+ Object-oriented design: Use single responsibility principle.
28
+ Error handling: Rescue specific exceptions, provide meaningful messages.
29
+ Testing: Write unit tests, use dependency injection.
30
+ TEMPLATE
31
+
32
+ # Frontend templates
33
+ 'javascript_code' => <<~TEMPLATE,
34
+ JavaScript context: {{js_concepts}}. Modern practices: use const/let, arrow functions.
35
+ Async patterns: Promises, async/await for API calls.
36
+ DOM manipulation: querySelector, addEventListener for interactions.
37
+ Debugging: Use console.log, browser dev tools, proper error handling.
38
+ TEMPLATE
39
+
40
+ 'css_styles' => <<~TEMPLATE,
41
+ CSS styling context: {{css_classes}} for {{elements}}.
42
+ Layout: Flexbox for 1D, Grid for 2D layouts. Responsive design with media queries.
43
+ Performance: Minimize specificity, avoid !important, use CSS custom properties.
44
+ Modern CSS: Use logical properties, container queries, CSS Grid.
45
+ TEMPLATE
46
+
47
+ 'erb_template' => <<~TEMPLATE,
48
+ ERB template context: {{template_type}} rendering {{data}}.
49
+ Security: Use <%= %> for output, <% %> for logic, sanitize user input.
50
+ Helpers: Use Rails helpers, extract complex logic to helpers.
51
+ Performance: Avoid N+1 queries, use partials for reusable components.
52
+ TEMPLATE
53
+
54
+ # Testing templates
55
+ 'test_spec' => <<~TEMPLATE,
56
+ Testing context: {{test_type}} for {{subject}}.
57
+ Structure: Arrange-Act-Assert pattern. Use descriptive test names.
58
+ Mocking: Stub external dependencies, test behavior not implementation.
59
+ Coverage: Test happy path, edge cases, error conditions.
60
+ TEMPLATE
61
+
62
+ # Documentation templates
63
+ 'documentation' => <<~TEMPLATE,
64
+ Documentation context: {{doc_type}} for {{topic}}.
65
+ Structure: Clear headings, code examples, usage instructions.
66
+ Content: What it does, how to use it, common gotchas.
67
+ Maintenance: Keep examples current, explain the why not just what.
68
+ TEMPLATE
69
+
70
+ # Database templates
71
+ 'database_migration' => <<~TEMPLATE,
72
+ Database migration context: {{migration_type}} for {{table_name}}.
73
+ Safety: Use reversible migrations, test rollbacks.
74
+ Performance: Add indexes for queries, avoid large data changes.
75
+ Best practices: One concept per migration, use change method when possible.
76
+ TEMPLATE
77
+
78
+ # Configuration templates
79
+ 'configuration' => <<~TEMPLATE,
80
+ Configuration context: {{config_type}} settings for {{environment}}.
81
+ Security: Use environment variables for secrets, validate configuration.
82
+ Organization: Group related settings, document required values.
83
+ Deployment: Different configs per environment, version control safe values.
84
+ TEMPLATE
85
+
86
+ # Error/debugging templates
87
+ 'error_analysis' => <<~TEMPLATE,
88
+ Error analysis: {{error_type}} in {{context}}.
89
+ Common causes: {{likely_causes}}.
90
+ Debugging steps: Check logs, verify configuration, test incrementally.
91
+ Prevention: Input validation, error handling, monitoring.
92
+ TEMPLATE
93
+
94
+ # Implementation guidance templates
95
+ 'implementation_guide' => <<~TEMPLATE,
96
+ Implementation guide for {{feature}} using {{technologies}}.
97
+ Approach: Start simple, iterate, test early and often.
98
+ Architecture: Follow MVC pattern, separate concerns, use design patterns.
99
+ Code quality: Readable code, meaningful names, appropriate abstractions.
100
+ TEMPLATE
101
+ }.freeze
102
+
103
+ def initialize
104
+ @generation_cache = {}
105
+ @template_usage_stats = Hash.new(0)
106
+ end
107
+
108
+ # Generate content from template with keywords and context
109
+ def generate_content(template_type, keywords:, intent:, params: {})
110
+ $stdout.puts " ๐Ÿญ TEMPLATE: Generating #{template_type} content"
111
+
112
+ # Track template usage
113
+ @template_usage_stats[template_type] += 1
114
+
115
+ # Check cache first
116
+ cache_key = build_cache_key(template_type, keywords, intent, params)
117
+ cached_content = @generation_cache[cache_key]
118
+
119
+ if cached_content
120
+ $stdout.puts " ๐Ÿ’จ Cache hit for #{template_type}"
121
+ return cached_content
122
+ end
123
+
124
+ # Generate new content
125
+ generated_content = case template_type
126
+ when 'active_record_model'
127
+ generate_activerecord_content(keywords, intent, params)
128
+ when 'rails_controller'
129
+ generate_controller_content(keywords, intent, params)
130
+ when 'ruby_class'
131
+ generate_ruby_class_content(keywords, intent, params)
132
+ when 'javascript_code'
133
+ generate_javascript_content(keywords, intent, params)
134
+ when 'css_styles'
135
+ generate_css_content(keywords, intent, params)
136
+ when 'erb_template'
137
+ generate_erb_content(keywords, intent, params)
138
+ when 'test_spec'
139
+ generate_test_content(keywords, intent, params)
140
+ when 'documentation'
141
+ generate_documentation_content(keywords, intent, params)
142
+ when 'database_migration'
143
+ generate_migration_content(keywords, intent, params)
144
+ when 'configuration'
145
+ generate_configuration_content(keywords, intent, params)
146
+ else
147
+ generate_generic_content(template_type, keywords, intent, params)
148
+ end
149
+
150
+ # Cache generated content
151
+ @generation_cache[cache_key] = generated_content
152
+
153
+ # Clean cache if it gets too large
154
+ clean_cache_if_needed
155
+
156
+ $stdout.puts " โœ… Generated #{template_type} (#{generated_content.length} chars)"
157
+ generated_content
158
+ end
159
+
160
+ # Get template statistics
161
+ def statistics
162
+ {
163
+ templates_available: TEMPLATES.size,
164
+ cache_size: @generation_cache.size,
165
+ most_used_template: @template_usage_stats.max_by { |k, v| v }&.first,
166
+ total_generations: @template_usage_stats.values.sum
167
+ }
168
+ end
169
+
170
+ private
171
+
172
+ def generate_activerecord_content(keywords, intent, params)
173
+ model_keywords = keywords.select { |k| k.start_with?('class_', 'method_') }
174
+ model_name = extract_model_name(model_keywords)
175
+ methods = extract_methods(model_keywords)
176
+
177
+ template = TEMPLATES['active_record_model']
178
+ template.gsub('{{model_name}}', model_name)
179
+ .gsub('{{methods}}', methods.join(', '))
180
+ .gsub('{{attributes}}', infer_attributes(keywords).join(', '))
181
+ end
182
+
183
+ def generate_controller_content(keywords, intent, params)
184
+ controller_keywords = keywords.select { |k| k.start_with?('class_', 'method_') }
185
+ controller_name = extract_controller_name(controller_keywords)
186
+ actions = extract_controller_actions(controller_keywords)
187
+
188
+ template = TEMPLATES['rails_controller']
189
+ template.gsub('{{controller_name}}', controller_name)
190
+ .gsub('{{actions}}', actions.join(', '))
191
+ .gsub('{{methods}}', actions.join(', '))
192
+ end
193
+
194
+ def generate_ruby_class_content(keywords, intent, params)
195
+ class_keywords = keywords.select { |k| k.start_with?('class_', 'method_') }
196
+ class_name = extract_class_name(class_keywords)
197
+ methods = extract_methods(class_keywords)
198
+
199
+ template = TEMPLATES['ruby_class']
200
+ template.gsub('{{class_name}}', class_name)
201
+ .gsub('{{methods}}', methods.join(', '))
202
+ end
203
+
204
+ def generate_javascript_content(keywords, intent, params)
205
+ js_concepts = keywords.select { |k| k.include?('js') || k.include?('javascript') || k.include?('function') }
206
+
207
+ template = TEMPLATES['javascript_code']
208
+ template.gsub('{{js_concepts}}', js_concepts.join(', '))
209
+ end
210
+
211
+ def generate_css_content(keywords, intent, params)
212
+ css_keywords = keywords.select { |k| k.start_with?('css_') }
213
+ css_classes = css_keywords.map { |k| k.sub('css_', '') }
214
+ elements = infer_css_elements(css_classes)
215
+
216
+ template = TEMPLATES['css_styles']
217
+ template.gsub('{{css_classes}}', css_classes.join(', '))
218
+ .gsub('{{elements}}', elements.join(', '))
219
+ end
220
+
221
+ def generate_erb_content(keywords, intent, params)
222
+ template_type = infer_erb_template_type(keywords)
223
+ data_keywords = keywords.reject { |k| k.start_with?('css_', 'tech_') }
224
+
225
+ template = TEMPLATES['erb_template']
226
+ template.gsub('{{template_type}}', template_type)
227
+ .gsub('{{data}}', data_keywords.join(', '))
228
+ end
229
+
230
+ def generate_test_content(keywords, intent, params)
231
+ test_type = infer_test_type(keywords)
232
+ subject = infer_test_subject(keywords)
233
+
234
+ template = TEMPLATES['test_spec']
235
+ template.gsub('{{test_type}}', test_type)
236
+ .gsub('{{subject}}', subject)
237
+ end
238
+
239
+ def generate_documentation_content(keywords, intent, params)
240
+ doc_type = infer_documentation_type(keywords)
241
+ topic = keywords.reject { |k| k.include?('tech_') }.join(' ')
242
+
243
+ template = TEMPLATES['documentation']
244
+ template.gsub('{{doc_type}}', doc_type)
245
+ .gsub('{{topic}}', topic)
246
+ end
247
+
248
+ def generate_migration_content(keywords, intent, params)
249
+ migration_type = infer_migration_type(keywords)
250
+ table_name = infer_table_name(keywords)
251
+
252
+ template = TEMPLATES['database_migration']
253
+ template.gsub('{{migration_type}}', migration_type)
254
+ .gsub('{{table_name}}', table_name)
255
+ end
256
+
257
+ def generate_configuration_content(keywords, intent, params)
258
+ config_type = infer_config_type(keywords)
259
+ environment = infer_environment(keywords)
260
+
261
+ template = TEMPLATES['configuration']
262
+ template.gsub('{{config_type}}', config_type)
263
+ .gsub('{{environment}}', environment)
264
+ end
265
+
266
+ def generate_generic_content(template_type, keywords, intent, params)
267
+ # Fallback for unknown template types
268
+ relevant_keywords = keywords.first(5)
269
+
270
+ case intent
271
+ when :implementation
272
+ "Implementation guidance for #{template_type}: #{relevant_keywords.join(', ')}. Follow established patterns, write tests, consider edge cases."
273
+ when :debugging
274
+ "Debugging #{template_type}: Check #{relevant_keywords.join(', ')}. Verify configuration, check logs, test incrementally."
275
+ when :learning
276
+ "Learning about #{template_type}: Key concepts include #{relevant_keywords.join(', ')}. Start with documentation, practice with examples."
277
+ when :optimization
278
+ "Optimizing #{template_type}: Focus on #{relevant_keywords.join(', ')}. Profile first, optimize bottlenecks, measure results."
279
+ else
280
+ "Context for #{template_type}: #{relevant_keywords.join(', ')}. Apply best practices and follow conventions."
281
+ end
282
+ end
283
+
284
+ # Helper methods for extracting semantic information from keywords
285
+
286
+ def extract_model_name(keywords)
287
+ model_keyword = keywords.find { |k| k.start_with?('class_') }
288
+ model_keyword ? model_keyword.sub('class_', '').capitalize : 'Model'
289
+ end
290
+
291
+ def extract_controller_name(keywords)
292
+ controller_keyword = keywords.find { |k| k.start_with?('class_') && k.include?('controller') }
293
+ controller_keyword ? controller_keyword.sub('class_', '').capitalize : 'Controller'
294
+ end
295
+
296
+ def extract_class_name(keywords)
297
+ class_keyword = keywords.find { |k| k.start_with?('class_') }
298
+ class_keyword ? class_keyword.sub('class_', '').capitalize : 'Class'
299
+ end
300
+
301
+ def extract_methods(keywords)
302
+ method_keywords = keywords.select { |k| k.start_with?('method_') }
303
+ method_keywords.map { |k| k.sub('method_', '') }.first(5)
304
+ end
305
+
306
+ def extract_controller_actions(keywords)
307
+ method_keywords = extract_methods(keywords)
308
+ rest_actions = %w[index show new create edit update destroy]
309
+ actions = method_keywords & rest_actions
310
+ actions.any? ? actions : method_keywords.first(3)
311
+ end
312
+
313
+ def infer_attributes(keywords)
314
+ # Infer model attributes from keywords
315
+ attribute_hints = keywords.select { |k|
316
+ k.match?(/name|title|email|description|content|status|type|id/)
317
+ }
318
+ attribute_hints.any? ? attribute_hints.first(3) : ['name', 'description']
319
+ end
320
+
321
+ def infer_css_elements(css_classes)
322
+ # Map CSS classes to likely HTML elements
323
+ element_mapping = {
324
+ 'btn' => 'button',
325
+ 'card' => 'div',
326
+ 'container' => 'div',
327
+ 'nav' => 'nav',
328
+ 'header' => 'header',
329
+ 'footer' => 'footer',
330
+ 'form' => 'form',
331
+ 'input' => 'input',
332
+ 'text' => 'span'
333
+ }
334
+
335
+ css_classes.map { |cls|
336
+ element_mapping.find { |key, _| cls.include?(key) }&.last || 'div'
337
+ }.uniq
338
+ end
339
+
340
+ def infer_erb_template_type(keywords)
341
+ return 'layout' if keywords.any? { |k| k.include?('layout') }
342
+ return 'partial' if keywords.any? { |k| k.include?('partial') }
343
+ return 'form' if keywords.any? { |k| k.include?('form') }
344
+ 'view'
345
+ end
346
+
347
+ def infer_test_type(keywords)
348
+ return 'unit test' if keywords.any? { |k| k.include?('unit') }
349
+ return 'integration test' if keywords.any? { |k| k.include?('integration') }
350
+ return 'system test' if keywords.any? { |k| k.include?('system') }
351
+ 'spec'
352
+ end
353
+
354
+ def infer_test_subject(keywords)
355
+ class_keywords = keywords.select { |k| k.start_with?('class_') }
356
+ method_keywords = keywords.select { |k| k.start_with?('method_') }
357
+
358
+ if class_keywords.any?
359
+ class_keywords.first.sub('class_', '')
360
+ elsif method_keywords.any?
361
+ method_keywords.first.sub('method_', '')
362
+ else
363
+ 'component'
364
+ end
365
+ end
366
+
367
+ def infer_documentation_type(keywords)
368
+ return 'API documentation' if keywords.any? { |k| k.include?('api') }
369
+ return 'user guide' if keywords.any? { |k| k.include?('guide') }
370
+ return 'tutorial' if keywords.any? { |k| k.include?('tutorial') }
371
+ 'technical documentation'
372
+ end
373
+
374
+ def infer_migration_type(keywords)
375
+ return 'create table' if keywords.any? { |k| k.include?('create') }
376
+ return 'add column' if keywords.any? { |k| k.include?('add') }
377
+ return 'modify table' if keywords.any? { |k| k.include?('change') }
378
+ 'database change'
379
+ end
380
+
381
+ def infer_table_name(keywords)
382
+ table_hints = keywords.select { |k|
383
+ k.match?(/users|posts|articles|products|orders|customers/)
384
+ }
385
+ table_hints.first || 'table'
386
+ end
387
+
388
+ def infer_config_type(keywords)
389
+ return 'database config' if keywords.any? { |k| k.include?('database') }
390
+ return 'application config' if keywords.any? { |k| k.include?('application') }
391
+ return 'environment config' if keywords.any? { |k| k.include?('environment') }
392
+ 'configuration'
393
+ end
394
+
395
+ def infer_environment(keywords)
396
+ return 'production' if keywords.any? { |k| k.include?('production') }
397
+ return 'development' if keywords.any? { |k| k.include?('development') }
398
+ return 'test' if keywords.any? { |k| k.include?('test') }
399
+ 'environment'
400
+ end
401
+
402
+ def build_cache_key(template_type, keywords, intent, params)
403
+ # Create a stable cache key
404
+ key_parts = [
405
+ template_type,
406
+ keywords.sort.join('_'),
407
+ intent.to_s,
408
+ params.sort_by { |k, v| k.to_s }.to_h.to_s
409
+ ]
410
+
411
+ Digest::MD5.hexdigest(key_parts.join('|'))
412
+ end
413
+
414
+ def clean_cache_if_needed
415
+ # Keep cache size reasonable
416
+ if @generation_cache.size > 500
417
+ # Remove oldest entries (simple LRU approximation)
418
+ keys_to_remove = @generation_cache.keys.first(100)
419
+ keys_to_remove.each { |key| @generation_cache.delete(key) }
420
+
421
+ $stdout.puts " ๐Ÿงน Cache cleaned: removed #{keys_to_remove.size} old entries"
422
+ end
423
+ end
424
+ end
425
+ end