rhales 0.4.0 → 0.5.3

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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/renovate.json5 +52 -0
  3. data/.github/workflows/ci.yml +123 -0
  4. data/.github/workflows/claude-code-review.yml +69 -0
  5. data/.github/workflows/claude.yml +49 -0
  6. data/.github/workflows/code-smells.yml +146 -0
  7. data/.github/workflows/ruby-lint.yml +78 -0
  8. data/.github/workflows/yardoc.yml +126 -0
  9. data/.gitignore +55 -0
  10. data/.pr_agent.toml +63 -0
  11. data/.pre-commit-config.yaml +89 -0
  12. data/.prettierignore +8 -0
  13. data/.prettierrc +38 -0
  14. data/.reek.yml +98 -0
  15. data/.rubocop.yml +428 -0
  16. data/.serena/.gitignore +3 -0
  17. data/.yardopts +56 -0
  18. data/CHANGELOG.md +44 -0
  19. data/CLAUDE.md +1 -1
  20. data/Gemfile +29 -0
  21. data/Gemfile.lock +189 -0
  22. data/README.md +686 -868
  23. data/Rakefile +46 -0
  24. data/debug_context.rb +25 -0
  25. data/demo/rhales-roda-demo/.gitignore +7 -0
  26. data/demo/rhales-roda-demo/Gemfile +32 -0
  27. data/demo/rhales-roda-demo/Gemfile.lock +151 -0
  28. data/demo/rhales-roda-demo/MAIL.md +405 -0
  29. data/demo/rhales-roda-demo/README.md +376 -0
  30. data/demo/rhales-roda-demo/RODA-TEMPLATE-ENGINES.md +192 -0
  31. data/demo/rhales-roda-demo/Rakefile +49 -0
  32. data/demo/rhales-roda-demo/app.rb +325 -0
  33. data/demo/rhales-roda-demo/bin/rackup +26 -0
  34. data/demo/rhales-roda-demo/config.ru +13 -0
  35. data/demo/rhales-roda-demo/db/migrate/001_create_rodauth_tables.rb +266 -0
  36. data/demo/rhales-roda-demo/db/migrate/002_create_rodauth_password_tables.rb +79 -0
  37. data/demo/rhales-roda-demo/db/migrate/003_add_admin_account.rb +68 -0
  38. data/demo/rhales-roda-demo/templates/change_login.rue +31 -0
  39. data/demo/rhales-roda-demo/templates/change_password.rue +36 -0
  40. data/demo/rhales-roda-demo/templates/close_account.rue +31 -0
  41. data/demo/rhales-roda-demo/templates/create_account.rue +40 -0
  42. data/demo/rhales-roda-demo/templates/dashboard.rue +150 -0
  43. data/demo/rhales-roda-demo/templates/home.rue +78 -0
  44. data/demo/rhales-roda-demo/templates/layouts/main.rue +168 -0
  45. data/demo/rhales-roda-demo/templates/login.rue +65 -0
  46. data/demo/rhales-roda-demo/templates/logout.rue +25 -0
  47. data/demo/rhales-roda-demo/templates/reset_password.rue +26 -0
  48. data/demo/rhales-roda-demo/templates/verify_account.rue +27 -0
  49. data/demo/rhales-roda-demo/test_full_output.rb +27 -0
  50. data/demo/rhales-roda-demo/test_simple.rb +24 -0
  51. data/docs/.gitignore +9 -0
  52. data/docs/architecture/data-flow.md +499 -0
  53. data/examples/dashboard-with-charts.rue +271 -0
  54. data/examples/form-with-validation.rue +180 -0
  55. data/examples/simple-page.rue +61 -0
  56. data/examples/vue.rue +136 -0
  57. data/generate-json-schemas.ts +158 -0
  58. data/json_schemer_migration_summary.md +172 -0
  59. data/lib/rhales/adapters/base_auth.rb +2 -0
  60. data/lib/rhales/adapters/base_request.rb +2 -0
  61. data/lib/rhales/adapters/base_session.rb +2 -0
  62. data/lib/rhales/adapters.rb +7 -0
  63. data/lib/rhales/configuration.rb +47 -0
  64. data/lib/rhales/core/context.rb +354 -0
  65. data/lib/rhales/{rue_document.rb → core/rue_document.rb} +56 -38
  66. data/lib/rhales/{template_engine.rb → core/template_engine.rb} +66 -59
  67. data/lib/rhales/{view.rb → core/view.rb} +112 -135
  68. data/lib/rhales/{view_composition.rb → core/view_composition.rb} +78 -8
  69. data/lib/rhales/core.rb +9 -0
  70. data/lib/rhales/errors/hydration_collision_error.rb +2 -0
  71. data/lib/rhales/errors.rb +2 -0
  72. data/lib/rhales/{earliest_injection_detector.rb → hydration/earliest_injection_detector.rb} +4 -0
  73. data/lib/rhales/hydration/hydration_data_aggregator.rb +487 -0
  74. data/lib/rhales/{hydration_endpoint.rb → hydration/hydration_endpoint.rb} +16 -12
  75. data/lib/rhales/{hydration_injector.rb → hydration/hydration_injector.rb} +4 -0
  76. data/lib/rhales/{hydration_registry.rb → hydration/hydration_registry.rb} +2 -0
  77. data/lib/rhales/hydration/hydrator.rb +102 -0
  78. data/lib/rhales/{link_based_injection_detector.rb → hydration/link_based_injection_detector.rb} +4 -0
  79. data/lib/rhales/{mount_point_detector.rb → hydration/mount_point_detector.rb} +4 -0
  80. data/lib/rhales/{safe_injection_validator.rb → hydration/safe_injection_validator.rb} +4 -0
  81. data/lib/rhales/hydration.rb +13 -0
  82. data/lib/rhales/{refinements → integrations/refinements}/require_refinements.rb +3 -1
  83. data/lib/rhales/{tilt.rb → integrations/tilt.rb} +22 -15
  84. data/lib/rhales/integrations.rb +6 -0
  85. data/lib/rhales/middleware/json_responder.rb +191 -0
  86. data/lib/rhales/middleware/schema_validator.rb +300 -0
  87. data/lib/rhales/middleware.rb +6 -0
  88. data/lib/rhales/parsers/handlebars_parser.rb +2 -0
  89. data/lib/rhales/parsers/rue_format_parser.rb +9 -7
  90. data/lib/rhales/parsers.rb +9 -0
  91. data/lib/rhales/{csp.rb → security/csp.rb} +27 -3
  92. data/lib/rhales/utils/json_serializer.rb +114 -0
  93. data/lib/rhales/utils/logging_helpers.rb +75 -0
  94. data/lib/rhales/utils/schema_extractor.rb +132 -0
  95. data/lib/rhales/utils/schema_generator.rb +194 -0
  96. data/lib/rhales/utils.rb +40 -0
  97. data/lib/rhales/version.rb +3 -1
  98. data/lib/rhales.rb +41 -24
  99. data/lib/tasks/rhales_schema.rake +197 -0
  100. data/package.json +10 -0
  101. data/pnpm-lock.yaml +345 -0
  102. data/pnpm-workspace.yaml +2 -0
  103. data/proofs/error_handling.rb +79 -0
  104. data/proofs/expanded_object_inheritance.rb +82 -0
  105. data/proofs/partial_context_scoping_fix.rb +168 -0
  106. data/proofs/ui_context_partial_inheritance.rb +236 -0
  107. data/rhales.gemspec +14 -6
  108. data/schema_vs_data_comparison.md +254 -0
  109. data/test_direct_access.rb +36 -0
  110. metadata +141 -23
  111. data/CLAUDE.locale.txt +0 -7
  112. data/lib/rhales/context.rb +0 -239
  113. data/lib/rhales/hydration_data_aggregator.rb +0 -221
  114. data/lib/rhales/hydrator.rb +0 -141
  115. data/lib/rhales/parsers/handlebars-grammar-review.txt +0 -39
@@ -1,141 +0,0 @@
1
- # lib/rhales/hydrator.rb
2
-
3
- require 'json'
4
- require 'securerandom'
5
-
6
- module Rhales
7
- # Data Hydrator for RSFC client-side data injection
8
- #
9
- # ## RSFC Security Model: Server-to-Client Security Boundary
10
- #
11
- # The Hydrator enforces a critical security boundary between server and client:
12
- #
13
- # ### Server Side (Template Rendering)
14
- # - Templates have FULL server context access (like ERB/HAML)
15
- # - Can access user objects, database connections, internal APIs
16
- # - Can access secrets, configuration, authentication state
17
- # - Can process sensitive business logic
18
- #
19
- # ### Client Side (Data Hydration)
20
- # - Only data declared in <data> section reaches the browser
21
- # - Creates explicit allowlist like designing a REST API
22
- # - Server-side variable interpolation processes secrets safely
23
- # - JSON serialization validates data structure
24
- #
25
- # ### Process Flow
26
- # 1. Server processes <data> section with full context access
27
- # 2. Variables like {{user.name}} are interpolated server-side
28
- # 3. Result is serialized as JSON and sent to client
29
- # 4. Client receives only the processed, safe data
30
- #
31
- # ### Example
32
- # ```rue
33
- # <data>
34
- # {
35
- # "user_name": "{{user.name}}", // Safe: just the name
36
- # "theme": "{{user.theme_preference}}" // Safe: just the theme
37
- # }
38
- # </data>
39
- # ```
40
- #
41
- # Server template can access {{user.admin?}} and {{internal_config}},
42
- # but client only gets the declared user_name and theme values.
43
- #
44
- # This creates an API-like boundary where data is serialized once and
45
- # parsed once, enforcing the same security model as REST endpoints.
46
- #
47
- # Note: With the new two-pass architecture, the Hydrator's role is
48
- # greatly simplified. All data merging happens server-side in the
49
- # HydrationDataAggregator, so this class only handles JSON generation
50
- # for individual templates (used during the aggregation phase).
51
- class Hydrator
52
- class HydrationError < StandardError; end
53
- class JSONSerializationError < HydrationError; end
54
-
55
- attr_reader :parser, :context, :window_attribute
56
-
57
- def initialize(parser, context)
58
- @parser = parser
59
- @context = context
60
- @window_attribute = parser.window_attribute || 'data'
61
- end
62
-
63
- # This method is now deprecated in favor of the two-pass architecture
64
- # It's kept for backward compatibility but will be removed in future versions
65
- def generate_hydration_html
66
- warn '[DEPRECATION] Hydrator#generate_hydration_html is deprecated. Use the two-pass rendering architecture instead.'
67
- ''
68
- end
69
-
70
- # Process <data> section and return JSON string
71
- def process_data_section
72
- data_content = @parser.section('data')
73
- return '{}' unless data_content
74
-
75
- # Process variable interpolations in the data section
76
- processed_content = process_data_variables(data_content)
77
-
78
- # Validate and return JSON
79
- validate_json(processed_content)
80
- processed_content
81
- rescue JSON::ParserError => ex
82
- raise JSONSerializationError, "Invalid JSON in data section: #{ex.message}"
83
- end
84
-
85
- # Get processed data as Ruby hash (for internal use)
86
- def processed_data_hash
87
- json_string = process_data_section
88
- JSON.parse(json_string)
89
- rescue JSON::ParserError => ex
90
- raise JSONSerializationError, "Cannot parse processed data as JSON: #{ex.message}"
91
- end
92
-
93
- private
94
-
95
- # Process variable interpolations in data section
96
- # Uses Rhales consistently for all template processing
97
- def process_data_variables(data_content)
98
- rhales = TemplateEngine.new(data_content, @context)
99
- rhales.render
100
- end
101
-
102
- # Validate that processed content is valid JSON
103
- def validate_json(json_string)
104
- JSON.parse(json_string)
105
- rescue JSON::ParserError => ex
106
- raise JSONSerializationError, "Processed data section is not valid JSON: #{ex.message}"
107
- end
108
-
109
- # Build template path with line number for error reporting
110
- # (Used by HydrationDataAggregator)
111
- def build_template_path
112
- data_node = @parser.section_node('data')
113
- line_number = data_node ? data_node.location.start_line : 1
114
-
115
- if @parser.file_path
116
- "#{@parser.file_path}:#{line_number}"
117
- else
118
- "<inline>:#{line_number}"
119
- end
120
- end
121
-
122
- class << self
123
- # Convenience method to generate hydration HTML
124
- # DEPRECATED: Use the two-pass rendering architecture instead
125
- def generate(parser, context)
126
- warn '[DEPRECATION] Hydrator.generate is deprecated. Use the two-pass rendering architecture instead.'
127
- new(parser, context).generate_hydration_html
128
- end
129
-
130
- # Generate only JSON data (for testing or API endpoints)
131
- def generate_json(parser, context)
132
- new(parser, context).process_data_section
133
- end
134
-
135
- # Generate data hash (for internal processing)
136
- def generate_data_hash(parser, context)
137
- new(parser, context).processed_data_hash
138
- end
139
- end
140
- end
141
- end
@@ -1,39 +0,0 @@
1
- According to Gemini 2.5 Pro.
2
-
3
-
4
- The `HandlebarsGrammar` class is **not a formal grammar definition**.
5
-
6
- Instead, it is a **parser** that **implements** the rules of the Handlebars grammar. This is a crucial distinction:
7
-
8
- 1. **Formal Grammar (The Blueprint):** A formal grammar is a set of abstract, mathematical rules that define a language. It specifies the valid sequences of symbols and their structure, often using a notation like Backus-Naur Form (BNF). It is a *specification*, not code.
9
-
10
- *Example (simplified BNF for a Handlebars variable):*
11
- ```
12
- expression ::= '{{' identifier '}}'
13
- identifier ::= letter ( letter | digit )*
14
- ```
15
-
16
- 2. **Parser (The Implementation):** A parser is a piece of software that takes an input string and determines if it conforms to the rules of a formal grammar. As a byproduct, it usually produces a data structure, like an Abstract Syntax Tree (AST), that represents the input's structure.
17
-
18
- The `HandlebarsGrammar` class is what is known as a **hand-rolled parser**. It was written manually in Ruby to recognize and structure Handlebars syntax, rather than being automatically generated by a tool.
19
-
20
- ### What Makes it a Parser (Not a Grammar)
21
-
22
- You can see the classic components of a hand-rolled parser right in its methods:
23
-
24
- * **Lexical Analysis (Scanning):** It consumes the input character by character and identifies tokens.
25
- * `current_char`, `peek_char`, `advance`: These methods manage the input stream.
26
- * `parse_text_until_handlebars`: This logic finds the boundary between plain text and a Handlebars expression (`{{`).
27
-
28
- * **Syntactic Analysis (Parsing):** It applies the grammar rules (as implemented in Ruby logic) to the token stream to build a structural representation.
29
- * `parse_template`: The main entry point that orchestrates the parsing process.
30
- * `create_if_block`, `create_each_block`: These methods correspond directly to the production rules for Handlebars block helpers. They build `Node` objects.
31
- * `Node` class: This defines the structure of the AST that the parser produces as its output.
32
-
33
- ### Why No Prism Reference?
34
-
35
- Parser generators like Prism, ANTLR, or Treetop work by taking a formal grammar definition as input and *generating* the parser code for you.
36
-
37
- Since `HandlebarsGrammar` is a hand-rolled parser, it doesn't need a generator. The developer has written the parsing logic directly in Ruby. This approach is common for languages with relatively simple, non-ambiguous syntax, as it avoids adding an external dependency and gives the developer full control over the parsing process and error handling.
38
-
39
- In summary: You've correctly identified that the class isn't a formal grammar. It's a **manual implementation of a parser** for that grammar, which is why it contains procedural logic for scanning and structuring text instead of a declarative set of grammar rules.