hati-rails-api 0.1.0.beta1
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +237 -0
- data/hati-rails-api.gemspec +39 -0
- data/lib/generators/hati_rails_api/context_generator.rb +270 -0
- data/lib/hati_rails_api/context/configuration/configuration.rb +50 -0
- data/lib/hati_rails_api/context/configuration/domain_configuration.rb +55 -0
- data/lib/hati_rails_api/context/core/error_handler.rb +76 -0
- data/lib/hati_rails_api/context/core/loader.rb +54 -0
- data/lib/hati_rails_api/context/core/migration.rb +68 -0
- data/lib/hati_rails_api/context/core/public_api.rb +114 -0
- data/lib/hati_rails_api/context/core.rb +18 -0
- data/lib/hati_rails_api/context/extensions/string_extensions.rb +11 -0
- data/lib/hati_rails_api/context/generators/base_generator.rb +33 -0
- data/lib/hati_rails_api/context/generators/domain_generator.rb +219 -0
- data/lib/hati_rails_api/context/generators/file_generation.rb +72 -0
- data/lib/hati_rails_api/context/generators/generator.rb +212 -0
- data/lib/hati_rails_api/context/generators/layer_component_generator.rb +88 -0
- data/lib/hati_rails_api/context/generators/model_endpoint_generator.rb +97 -0
- data/lib/hati_rails_api/context/generators/operation_generator.rb +182 -0
- data/lib/hati_rails_api/context/layers/operation_layer.rb +45 -0
- data/lib/hati_rails_api/context/layers/standard_layer.rb +44 -0
- data/lib/hati_rails_api/context/managers/rollback_manager.rb +123 -0
- data/lib/hati_rails_api/context/shared/content_generators.rb +125 -0
- data/lib/hati_rails_api/context/shared/layer_factory.rb +37 -0
- data/lib/hati_rails_api/context.rb +30 -0
- data/lib/hati_rails_api/errors/unsupported_operation_error.rb +11 -0
- data/lib/hati_rails_api/macro/serializer_macro.rb +13 -0
- data/lib/hati_rails_api/response_handler.rb +71 -0
- data/lib/hati_rails_api/version.rb +5 -0
- data/lib/hati_rails_api.rb +15 -0
- metadata +121 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 1402a4d0cd8d1aad576c25e026eabe7f59a4124f3263e8d709a5badb35666249
|
|
4
|
+
data.tar.gz: ef4cfd0e3e3d791692607b8be6223dd371d0ce1184914ca3ce524f2ee05053e8
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e57aa35cd26e896573e65ccaf2396eb275b9d8ce1ab9c68473f06439e0ec4f548b650c6411044880fa234007ec9e74512756529110b26f5b55c6da2fde055df2
|
|
7
|
+
data.tar.gz: f56b6a32a3f7d96000873df30e27180d04d8a6a94211fc69840af6845814f174e3867503eb67c019343aeb18bf53e1a5b8715ced60fdff5fb159e058b3ed6b31
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 hackico.ai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# Hati Rails API
|
|
2
|
+
|
|
3
|
+
A high-performance, scalable toolkit for building robust, domain-driven Rails APIs with migration-style code generation, clean architecture, and agentic/AI-ready patterns.
|
|
4
|
+
|
|
5
|
+
## What is Hati Rails API?
|
|
6
|
+
|
|
7
|
+
Hati Rails API is a Ruby gem that brings **migration-style, pattern-based code generation** to Rails APIs. It enables:
|
|
8
|
+
|
|
9
|
+
- **Domain-driven design** with clear context boundaries
|
|
10
|
+
- **Clean, SOLID architecture** for maintainable codebases
|
|
11
|
+
- **Fast, repeatable development** with migration files and rollback
|
|
12
|
+
- **Agentic/AI-ready workflows**: all code generation is explicit, declarative, and context-driven—perfect for automation and large-scale projects
|
|
13
|
+
|
|
14
|
+
## Framework Components & How They Work Together
|
|
15
|
+
|
|
16
|
+
### Context System
|
|
17
|
+
|
|
18
|
+
Centralizes domain logic, boundaries, and code generation. You define “contexts” (domains) in migration files. Each context can have operations, endpoints, models, and any number of custom layers (service, query, validation, serializer, etc.).
|
|
19
|
+
|
|
20
|
+
### Migration Engine
|
|
21
|
+
|
|
22
|
+
Provides a migration-style, repeatable, and rollbackable way to define and evolve your API structure. Migration files (class-based or DSL) describe what to generate. Running migrations creates or updates code; rollbacks revert changes.
|
|
23
|
+
|
|
24
|
+
### Generator
|
|
25
|
+
|
|
26
|
+
Orchestrates the actual file generation, tracking, and rollback. Reads migration files, generates code (models, controllers, operations, etc.), and tracks what was created for safe rollback.
|
|
27
|
+
|
|
28
|
+
### Layer System
|
|
29
|
+
|
|
30
|
+
Supports modular, extensible architecture. Built-in layers include:
|
|
31
|
+
|
|
32
|
+
- **Operation:** Business logic, step-based or simple
|
|
33
|
+
- **Service:** Application services, integrations
|
|
34
|
+
- **Query:** Data access/query objects
|
|
35
|
+
- **Validation:** Input/business validation
|
|
36
|
+
- **Serializer:** Output formatting
|
|
37
|
+
- **Custom:** Add your own (analytics, repository, etc.)
|
|
38
|
+
|
|
39
|
+
### ResponseHandler
|
|
40
|
+
|
|
41
|
+
Standardizes API responses and error handling. Controllers include this module to automatically handle operation results, errors, and JSON:API formatting.
|
|
42
|
+
|
|
43
|
+
### Rollback Manager
|
|
44
|
+
|
|
45
|
+
Enables safe, timestamped rollback of generated code. Tracks every file generated by a migration. Rollbacks remove files and clean up empty directories.
|
|
46
|
+
|
|
47
|
+
### Macro System
|
|
48
|
+
|
|
49
|
+
Provides reusable code patterns and metaprogramming helpers. Macros can be used in operations, layers, or anywhere in the context system to DRY up code and enforce patterns.
|
|
50
|
+
|
|
51
|
+
### Error Handling
|
|
52
|
+
|
|
53
|
+
Centralizes error types and handling logic. Custom error classes for unsupported operations, configuration issues, and more. Integrated with ResponseHandler for clean API error responses.
|
|
54
|
+
|
|
55
|
+
### Versioning
|
|
56
|
+
|
|
57
|
+
Tracks the version of the Hati Rails API gem. Ensures agents and humans know which features and patterns are available.
|
|
58
|
+
|
|
59
|
+
**How They Work Together:**
|
|
60
|
+
|
|
61
|
+
- You define your API’s structure and patterns in migration files.
|
|
62
|
+
- The Migration Engine and Generator read these files, using the Layer System to create all necessary code (operations, models, controllers, etc.).
|
|
63
|
+
- ResponseHandler ensures all endpoints behave consistently.
|
|
64
|
+
- Rollback Manager lets you safely undo changes, supporting rapid, iterative, and agent-driven development.
|
|
65
|
+
- Macros and Error Handling provide reusable patterns and robust error management.
|
|
66
|
+
- Versioning ensures compatibility and traceability.
|
|
67
|
+
|
|
68
|
+
The result is a highly modular, pattern-driven, and automation-friendly Rails API framework—ready for both human and AI/agentic development at scale.
|
|
69
|
+
|
|
70
|
+
## Quick Start
|
|
71
|
+
|
|
72
|
+
### 1. Install the Gem
|
|
73
|
+
|
|
74
|
+
Add to your Gemfile:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
gem 'hati-rails-api'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Then run:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
bundle install
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### 2. Initialize the Context System
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
rails generate hati_rails_api:context init
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
This creates:
|
|
93
|
+
|
|
94
|
+
- `config/contexts.rb` (global config)
|
|
95
|
+
- `config/contexts/` (migration files go here)
|
|
96
|
+
- `app/contexts/` (generated code)
|
|
97
|
+
|
|
98
|
+
### 3. Create a Migration File
|
|
99
|
+
|
|
100
|
+
Generate a migration for a domain:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
rails generate hati_rails_api:context user --operations=create,update,delete
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Edit the generated file in `config/contexts/` to define your domain, operations, and layers.
|
|
107
|
+
|
|
108
|
+
### 4. Run Migrations
|
|
109
|
+
|
|
110
|
+
Generate all code from migrations:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
rails generate hati_rails_api:context run --force
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### 5. Rollback (if needed)
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
rails generate hati_rails_api:context rollback --timestamp=YOUR_TIMESTAMP
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Basic Usage Example
|
|
123
|
+
|
|
124
|
+
A migration file (class-based, AI/agentic-ready):
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
# config/contexts/20250721001327_create_user_context.rb
|
|
128
|
+
class CreateUserContext < HatiRailsApi::Context::Migration
|
|
129
|
+
def change
|
|
130
|
+
domain :user do |domain|
|
|
131
|
+
domain.operation do |operation|
|
|
132
|
+
operation.component [:create, :update, :delete]
|
|
133
|
+
end
|
|
134
|
+
domain.endpoint true
|
|
135
|
+
domain.model true
|
|
136
|
+
domain.validation
|
|
137
|
+
domain.query
|
|
138
|
+
domain.service
|
|
139
|
+
domain.serializer
|
|
140
|
+
end
|
|
141
|
+
model [:user, :user_token]
|
|
142
|
+
endpoint [:user_status]
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
CreateUserContext.new.run
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
This will generate:
|
|
150
|
+
|
|
151
|
+
- Operations: `app/contexts/user/operation/create_operation.rb`, etc.
|
|
152
|
+
- Controller: `app/controllers/api/user_controller.rb`
|
|
153
|
+
- Models: `app/models/user.rb`, `app/models/user_token.rb`
|
|
154
|
+
- Additional layers: `app/contexts/user/validation/`, `query/`, `service/`, `serializer/`
|
|
155
|
+
|
|
156
|
+
## Advanced Usage & Patterns
|
|
157
|
+
|
|
158
|
+
### Granular/Agentic Steps
|
|
159
|
+
|
|
160
|
+
You can use granular steps to patternize operation logic based on other layers:
|
|
161
|
+
|
|
162
|
+
```ruby
|
|
163
|
+
domain.operation do |operation|
|
|
164
|
+
operation.component [:authenticate, :authorize]
|
|
165
|
+
operation.step true, granular: true # Will use all other domain layers as steps
|
|
166
|
+
end
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Custom Layers
|
|
170
|
+
|
|
171
|
+
Add any custom layer to your domain:
|
|
172
|
+
|
|
173
|
+
```ruby
|
|
174
|
+
domain.analytics do |analytics|
|
|
175
|
+
analytics.component [:event_tracker, :report_generator]
|
|
176
|
+
end
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### Explicit Steps
|
|
180
|
+
|
|
181
|
+
```ruby
|
|
182
|
+
domain.operation do |operation|
|
|
183
|
+
operation.component [:register]
|
|
184
|
+
operation.step :validate, :persist, :notify
|
|
185
|
+
end
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Full Example: Scalable, AI-Ready Context
|
|
189
|
+
|
|
190
|
+
```ruby
|
|
191
|
+
class CreateEcommerceContext < HatiRailsApi::Context::Migration
|
|
192
|
+
def change
|
|
193
|
+
domain :ecommerce do |domain|
|
|
194
|
+
domain.operation do |operation|
|
|
195
|
+
operation.component [:checkout, :refund]
|
|
196
|
+
operation.step true, granular: true
|
|
197
|
+
end
|
|
198
|
+
domain.endpoint true
|
|
199
|
+
domain.model true
|
|
200
|
+
domain.validation { |v| v.component [:payment_validator] }
|
|
201
|
+
domain.query { |q| q.component [:order_finder] }
|
|
202
|
+
domain.service { |s| s.component [:payment_gateway] }
|
|
203
|
+
domain.analytics { |a| a.component [:sales_reporter] }
|
|
204
|
+
end
|
|
205
|
+
model [:order, :payment]
|
|
206
|
+
endpoint [:order_status]
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
CreateEcommerceContext.new.run
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Library Architecture & Extensibility
|
|
214
|
+
|
|
215
|
+
- **Migration-Driven**: All code generation is declarative and repeatable
|
|
216
|
+
- **Layered**: Add/remove layers as your architecture evolves
|
|
217
|
+
- **Rollbackable**: Every generation is tracked and can be reverted
|
|
218
|
+
- **Composable**: Use as building blocks for agentic/AI workflows
|
|
219
|
+
- **Extensible**: Add new layer types, code templates, or DSL methods
|
|
220
|
+
|
|
221
|
+
## Why Hati Rails API for AI/Agentic Development?
|
|
222
|
+
|
|
223
|
+
- **Patternization**: All code is generated from explicit, context-rich patterns
|
|
224
|
+
- **Automation-Ready**: Migrations are just Ruby—easy to generate, modify, or analyze with AI agents
|
|
225
|
+
- **Scalable**: Designed for large, fast-moving teams and projects
|
|
226
|
+
- **Robust**: Rollback, test, and iterate safely
|
|
227
|
+
- **Clear Examples**: Every migration is a living, executable example
|
|
228
|
+
|
|
229
|
+
## Wrap Up
|
|
230
|
+
|
|
231
|
+
Hati Rails API brings the power of migration-driven, pattern-based, and agentic development to Rails APIs. Use it to:
|
|
232
|
+
|
|
233
|
+
- Rapidly scaffold and evolve complex API architectures
|
|
234
|
+
- Automate code generation with AI/agentic tools
|
|
235
|
+
- Maintain clean, robust, and scalable codebases
|
|
236
|
+
|
|
237
|
+
**For more examples, documentation, and advanced patterns, see the [project repo](https://github.com/hackico-ai/hati-rails-api).**
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
|
|
6
|
+
require 'hati_rails_api/version'
|
|
7
|
+
|
|
8
|
+
Gem::Specification.new do |spec|
|
|
9
|
+
spec.name = 'hati-rails-api'
|
|
10
|
+
spec.version = HatiRailsApi::VERSION
|
|
11
|
+
spec.authors = ['Mariya Giy', 'Yuri Gi']
|
|
12
|
+
spec.email = ['giy.mariya@gmail.com', 'yurigi.pro@gmail.com']
|
|
13
|
+
spec.license = 'MIT'
|
|
14
|
+
|
|
15
|
+
spec.summary = 'A Rails API tool for building API services.'
|
|
16
|
+
spec.description = 'A Rails API tool for building API services.'
|
|
17
|
+
spec.homepage = "https://github.com/hackico-ai/#{spec.name}"
|
|
18
|
+
|
|
19
|
+
spec.required_ruby_version = '>= 3.0.0'
|
|
20
|
+
|
|
21
|
+
spec.files = Dir['CHANGELOG.md', 'LICENSE', 'README.md', 'hati-rails-api.gemspec', 'lib/**/*']
|
|
22
|
+
spec.bindir = 'bin'
|
|
23
|
+
spec.executables = []
|
|
24
|
+
spec.require_paths = ['lib']
|
|
25
|
+
|
|
26
|
+
spec.metadata['repo_homepage'] = spec.homepage
|
|
27
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
|
28
|
+
|
|
29
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
30
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/blob/main/CHANGELOG.md"
|
|
31
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
|
32
|
+
spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
|
|
33
|
+
|
|
34
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
35
|
+
|
|
36
|
+
spec.add_dependency 'hati-operation'
|
|
37
|
+
spec.add_dependency 'hati-jsonapi-error'
|
|
38
|
+
spec.add_dependency 'railties', '>= 6.0'
|
|
39
|
+
end
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails/generators'
|
|
4
|
+
require 'rails/generators/base'
|
|
5
|
+
|
|
6
|
+
module HatiRailsApi
|
|
7
|
+
module Generators
|
|
8
|
+
# Rails generator for HatiRailsApi context operations
|
|
9
|
+
# Provides Rails integration for the context system with migration-style workflow
|
|
10
|
+
class ContextGenerator < Rails::Generators::Base
|
|
11
|
+
source_root File.expand_path('templates', __dir__)
|
|
12
|
+
|
|
13
|
+
argument :command, type: :string, default: 'init',
|
|
14
|
+
desc: 'Command: init, run, rollback, list, or domain name for quick generation'
|
|
15
|
+
|
|
16
|
+
class_option :force, type: :boolean, default: false,
|
|
17
|
+
desc: 'Force overwrite existing files'
|
|
18
|
+
|
|
19
|
+
class_option :timestamp, type: :string,
|
|
20
|
+
desc: 'Timestamp for rollback operation'
|
|
21
|
+
|
|
22
|
+
class_option :operations, type: :array, default: [],
|
|
23
|
+
desc: 'Operations to generate for the domain'
|
|
24
|
+
|
|
25
|
+
class_option :endpoint, type: :boolean, default: true,
|
|
26
|
+
desc: 'Generate API endpoint for the domain'
|
|
27
|
+
|
|
28
|
+
def perform_command
|
|
29
|
+
case command.downcase
|
|
30
|
+
when 'init', 'initialize'
|
|
31
|
+
initialize_context
|
|
32
|
+
when 'run', 'execute'
|
|
33
|
+
run_migrations
|
|
34
|
+
when 'rollback', 'rb'
|
|
35
|
+
rollback_generation
|
|
36
|
+
when 'list', 'ls'
|
|
37
|
+
list_generations
|
|
38
|
+
else
|
|
39
|
+
# Treat as domain name for migration creation
|
|
40
|
+
create_domain_migration(command)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def initialize_context
|
|
47
|
+
say 'Initializing HatiRailsApi Context System...', :green
|
|
48
|
+
create_configuration_files
|
|
49
|
+
create_directory_structure
|
|
50
|
+
create_example_migration
|
|
51
|
+
say "\nContext system initialized successfully!", :green
|
|
52
|
+
say 'Check the generated files and examples to get started.', :blue
|
|
53
|
+
say "\nMigration-style workflow:", :yellow
|
|
54
|
+
say ' 1. rails generate hati_rails_api:context user # Create domain migration'
|
|
55
|
+
say ' 2. rails generate hati_rails_api:context run # Execute migrations'
|
|
56
|
+
say ' 3. rails generate hati_rails_api:context rollback # Rollback last'
|
|
57
|
+
say ' 4. rails generate hati_rails_api:context list # Show generations'
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def create_domain_migration(domain_name)
|
|
61
|
+
say "Creating migration for domain: #{domain_name}", :green
|
|
62
|
+
timestamp = Time.now.strftime('%Y%m%d%H%M%S')
|
|
63
|
+
migration_file = "config/contexts/#{timestamp}_create_#{domain_name}_context.rb"
|
|
64
|
+
operations = if options[:operations].any?
|
|
65
|
+
options[:operations].flat_map { |op| op.split(',') }.map(&:strip)
|
|
66
|
+
else
|
|
67
|
+
['create', 'update', 'destroy']
|
|
68
|
+
end
|
|
69
|
+
endpoint_enabled = options[:endpoint]
|
|
70
|
+
create_file migration_file, domain_migration_template(domain_name, operations, endpoint_enabled)
|
|
71
|
+
say "Migration created: #{migration_file}", :green
|
|
72
|
+
say 'Edit the migration file to customize the domain configuration', :blue
|
|
73
|
+
say 'Run: rails generate hati_rails_api:context run', :yellow
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def run_migrations
|
|
77
|
+
say 'Running context migrations...', :green
|
|
78
|
+
migration_files = Dir[Rails.root.join('config', 'contexts', '*_create_*_context.rb')].sort
|
|
79
|
+
if migration_files.empty?
|
|
80
|
+
say_error 'No context migrations found!'
|
|
81
|
+
say 'Create a domain migration first: rails generate hati_rails_api:context user'
|
|
82
|
+
return
|
|
83
|
+
end
|
|
84
|
+
require_context_system
|
|
85
|
+
configure_defaults unless HatiRailsApi::Context.configured?
|
|
86
|
+
executed_count = 0
|
|
87
|
+
migration_files.each do |migration_file|
|
|
88
|
+
executed_count += 1 if execute_migration(migration_file)
|
|
89
|
+
end
|
|
90
|
+
if executed_count > 0
|
|
91
|
+
say "Successfully executed #{executed_count} migration(s)!", :green
|
|
92
|
+
say 'Use --force to override all existing files without prompting', :yellow unless options[:force]
|
|
93
|
+
else
|
|
94
|
+
say 'No new migrations to execute', :blue
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def execute_migration(migration_file)
|
|
99
|
+
say "Executing: #{File.basename(migration_file)}", :yellow
|
|
100
|
+
begin
|
|
101
|
+
require_context_system
|
|
102
|
+
configure_defaults unless HatiRailsApi::Context.configured?
|
|
103
|
+
migration_content = File.read(migration_file)
|
|
104
|
+
if migration_content.include?('< HatiRailsApi::Context::Migration')
|
|
105
|
+
load migration_file
|
|
106
|
+
else
|
|
107
|
+
HatiRailsApi::Context.generate(force: options[:force]) do |ctx|
|
|
108
|
+
ctx.instance_eval(migration_content, migration_file)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
say "Completed: #{File.basename(migration_file)}", :green
|
|
112
|
+
true
|
|
113
|
+
rescue StandardError => e
|
|
114
|
+
say_error "Error executing #{File.basename(migration_file)}: #{e.message}"
|
|
115
|
+
say_error "Location: #{e.backtrace&.first}"
|
|
116
|
+
false
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def rollback_generation
|
|
121
|
+
say 'Rolling back generation...', :yellow
|
|
122
|
+
require_context_system
|
|
123
|
+
result = if options[:timestamp]
|
|
124
|
+
HatiRailsApi::Context.rollback(options[:timestamp])
|
|
125
|
+
else
|
|
126
|
+
HatiRailsApi::Context.rollback
|
|
127
|
+
end
|
|
128
|
+
if result
|
|
129
|
+
say 'Rollback completed successfully!', :green
|
|
130
|
+
else
|
|
131
|
+
say_error 'Rollback failed!'
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def list_generations
|
|
136
|
+
say 'Listing all generations...', :blue
|
|
137
|
+
require_context_system
|
|
138
|
+
HatiRailsApi::Context.list_generations
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def create_configuration_files
|
|
142
|
+
create_file 'config/contexts.rb', global_config_template
|
|
143
|
+
create_file 'config/contexts/.gitkeep', ''
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def create_directory_structure
|
|
147
|
+
empty_directory 'app/contexts'
|
|
148
|
+
create_file 'app/contexts/.gitkeep', ''
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def create_example_migration
|
|
152
|
+
timestamp = Time.now.strftime('%Y%m%d%H%M%S')
|
|
153
|
+
create_file "config/contexts/#{timestamp}_create_example_context.rb", example_migration_template
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def require_context_system
|
|
157
|
+
require File.expand_path('../../hati_rails_api/context', __dir__)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def configure_defaults
|
|
161
|
+
HatiRailsApi::Context.configure do |config|
|
|
162
|
+
config.base_path 'app/contexts'
|
|
163
|
+
config.model path: 'app/models', base: 'ApplicationRecord'
|
|
164
|
+
config.endpoint path: 'app/controllers/api', base: 'ApplicationController'
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def global_config_template
|
|
169
|
+
<<~RUBY
|
|
170
|
+
# frozen_string_literal: true
|
|
171
|
+
|
|
172
|
+
# HatiRailsApi Global Context Configuration
|
|
173
|
+
# This file contains global settings for the context system
|
|
174
|
+
|
|
175
|
+
require 'hati_rails_api/context'
|
|
176
|
+
|
|
177
|
+
# Configure global settings
|
|
178
|
+
HatiRailsApi::Context.configure do |config|
|
|
179
|
+
config.base_path 'app/contexts'
|
|
180
|
+
config.model path: 'app/models', base: 'ApplicationRecord'
|
|
181
|
+
config.endpoint path: 'app/controllers/api', base: 'ApplicationController'
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Global domain configuration (applies to all domains)
|
|
185
|
+
HatiRailsApi::Context.configure do |config|
|
|
186
|
+
config.domain do |domain|
|
|
187
|
+
# Default operation layer with common base
|
|
188
|
+
domain.operation do |operation|
|
|
189
|
+
operation.base 'hati_operation/base'
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
RUBY
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def domain_migration_template(domain_name, operations, endpoint_enabled)
|
|
197
|
+
operation_list = operations.map(&:to_sym).inspect
|
|
198
|
+
timestamp = Time.now.strftime('%Y%m%d%H%M%S')
|
|
199
|
+
class_name = "Create#{domain_name.camelize}Context"
|
|
200
|
+
<<~RUBY
|
|
201
|
+
# frozen_string_literal: true
|
|
202
|
+
|
|
203
|
+
# Context Migration: Create #{domain_name.camelize} Domain
|
|
204
|
+
# Generated at #{Time.now}
|
|
205
|
+
|
|
206
|
+
class #{class_name} < HatiRailsApi::Context::Migration
|
|
207
|
+
def change
|
|
208
|
+
domain :#{domain_name} do |domain|
|
|
209
|
+
# Define operations for this domain
|
|
210
|
+
domain.operation do |operation|
|
|
211
|
+
operation.component #{operation_list}
|
|
212
|
+
end
|
|
213
|
+
# API endpoint configuration
|
|
214
|
+
domain.endpoint #{endpoint_enabled}
|
|
215
|
+
# Optional: Add custom layers
|
|
216
|
+
# domain.validation do |validation|
|
|
217
|
+
# validation.component [:input_validator, :business_validator]
|
|
218
|
+
# end
|
|
219
|
+
# domain.query do |query|
|
|
220
|
+
# query.component [:finder, :searcher]
|
|
221
|
+
# end
|
|
222
|
+
# domain.service do |service|
|
|
223
|
+
# service.component [:mailer, :notifier]
|
|
224
|
+
# end
|
|
225
|
+
# domain.serializer do |serializer|
|
|
226
|
+
# serializer.component [:json_serializer, :xml_serializer]
|
|
227
|
+
# end
|
|
228
|
+
end
|
|
229
|
+
# Optional: Generate domain-specific models
|
|
230
|
+
# model [:#{domain_name}]
|
|
231
|
+
# Optional: Generate additional endpoints
|
|
232
|
+
# endpoint [:#{domain_name}_status]
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
#{class_name}.new.run
|
|
237
|
+
RUBY
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def example_migration_template
|
|
241
|
+
<<~RUBY
|
|
242
|
+
# frozen_string_literal: true
|
|
243
|
+
|
|
244
|
+
# Example Context Migration
|
|
245
|
+
# This is an example - you can safely delete this file
|
|
246
|
+
|
|
247
|
+
# ctx.domain :ecommerce do |domain|
|
|
248
|
+
# domain.operation do |operation|
|
|
249
|
+
# operation.component [:payment_processing, :inventory_management, :order_fulfillment]
|
|
250
|
+
# end
|
|
251
|
+
# domain.endpoint enabled: true
|
|
252
|
+
# domain.service do |service|
|
|
253
|
+
# service.component [:notification, :analytics]
|
|
254
|
+
# end
|
|
255
|
+
# domain.query do |query|
|
|
256
|
+
# query.component [:product_finder, :order_searcher]
|
|
257
|
+
# end
|
|
258
|
+
# end
|
|
259
|
+
|
|
260
|
+
# ctx.model [:product, :order, :customer]
|
|
261
|
+
# ctx.endpoint [:health, :metrics]
|
|
262
|
+
RUBY
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
def say_error(message)
|
|
266
|
+
say message, :red
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'domain_configuration'
|
|
4
|
+
|
|
5
|
+
module HatiRailsApi
|
|
6
|
+
module Context
|
|
7
|
+
# Main configuration class for the context system
|
|
8
|
+
# Follows Single Responsibility Principle - handles global configuration
|
|
9
|
+
class Configuration
|
|
10
|
+
attr_accessor :model_config, :endpoint_config, :domain_config
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@base_path = 'app/contexts'
|
|
14
|
+
@model_config = { path: 'app/models', base: 'ApplicationRecord' }
|
|
15
|
+
@endpoint_config = { path: 'app/controllers/api', base: 'ApplicationController' }
|
|
16
|
+
@domain_config = DomainConfiguration.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def base_path(path = nil)
|
|
20
|
+
return @base_path unless path
|
|
21
|
+
|
|
22
|
+
@base_path = path
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def model(path: nil, base: nil)
|
|
26
|
+
@model_config.merge!(path: path) if path
|
|
27
|
+
@model_config.merge!(base: base) if base
|
|
28
|
+
@model_config
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def endpoint(enabled = true, **options)
|
|
32
|
+
return @endpoint_config unless enabled || !options.empty?
|
|
33
|
+
|
|
34
|
+
case enabled
|
|
35
|
+
when false
|
|
36
|
+
@endpoint_config = { enabled: false }
|
|
37
|
+
when Hash
|
|
38
|
+
@endpoint_config.merge!(enabled)
|
|
39
|
+
else
|
|
40
|
+
@endpoint_config.merge!(options)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def domain(&block)
|
|
45
|
+
@domain_config.instance_eval(&block) if block_given?
|
|
46
|
+
@domain_config
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../shared/layer_factory'
|
|
4
|
+
|
|
5
|
+
module HatiRailsApi
|
|
6
|
+
module Context
|
|
7
|
+
# Domain-specific configuration class
|
|
8
|
+
# Follows Single Responsibility Principle - handles domain layer configuration
|
|
9
|
+
class DomainConfiguration
|
|
10
|
+
include LayerFactory
|
|
11
|
+
|
|
12
|
+
DEFAULT_DOMAIN_STATE = { enabled: false }.freeze
|
|
13
|
+
|
|
14
|
+
attr_reader :layers
|
|
15
|
+
|
|
16
|
+
def initialize
|
|
17
|
+
@layers = {}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def operation(&block)
|
|
21
|
+
@layers[:operation] = create_operation_layer(&block)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def layer(layer_name, &block)
|
|
25
|
+
@layers[layer_name.to_sym] = create_standard_layer(layer_name, &block)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def endpoint(enabled: true, **options)
|
|
29
|
+
@domain_endpoint = create_endpoint_state(enabled, options)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def method_missing(method_name, ...)
|
|
33
|
+
result = create_dynamic_layer(method_name, ...)
|
|
34
|
+
return super unless result
|
|
35
|
+
|
|
36
|
+
@layers[method_name.to_sym] = result
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def respond_to_missing?(_method_name, _include_private = false)
|
|
40
|
+
true
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def create_endpoint_state(enabled, options)
|
|
46
|
+
case enabled
|
|
47
|
+
when false then DEFAULT_DOMAIN_STATE
|
|
48
|
+
when true then { enabled: true, options: options, explicit_components: nil }
|
|
49
|
+
when Array then { enabled: true, options: options, explicit_components: enabled }
|
|
50
|
+
when Hash then { enabled: true, options: enabled, explicit_components: nil }
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|