propel_api 0.1.1
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/CHANGELOG.md +59 -0
- data/LICENSE +21 -0
- data/README.md +320 -0
- data/Rakefile +36 -0
- data/lib/generators/propel_api/USAGE +8 -0
- data/lib/generators/propel_api/controller/controller_generator.rb +208 -0
- data/lib/generators/propel_api/core/base.rb +19 -0
- data/lib/generators/propel_api/core/configuration_methods.rb +187 -0
- data/lib/generators/propel_api/core/named_base.rb +457 -0
- data/lib/generators/propel_api/core/path_generation_methods.rb +45 -0
- data/lib/generators/propel_api/core/relationship_inferrer.rb +117 -0
- data/lib/generators/propel_api/install/install_generator.rb +343 -0
- data/lib/generators/propel_api/resource/resource_generator.rb +433 -0
- data/lib/generators/propel_api/templates/config/propel_api.rb.tt +149 -0
- data/lib/generators/propel_api/templates/controllers/api_controller_graphiti.rb +79 -0
- data/lib/generators/propel_api/templates/controllers/api_controller_propel_facets.rb +76 -0
- data/lib/generators/propel_api/templates/controllers/example_controller.rb.tt +96 -0
- data/lib/generators/propel_api/templates/scaffold/facet_controller_template.rb.tt +80 -0
- data/lib/generators/propel_api/templates/scaffold/facet_model_template.rb.tt +141 -0
- data/lib/generators/propel_api/templates/scaffold/graphiti_controller_template.rb.tt +82 -0
- data/lib/generators/propel_api/templates/scaffold/graphiti_model_template.rb.tt +32 -0
- data/lib/generators/propel_api/templates/seeds/seeds_template.rb.tt +493 -0
- data/lib/generators/propel_api/templates/tests/controller_test_template.rb.tt +485 -0
- data/lib/generators/propel_api/templates/tests/fixtures_template.yml.tt +250 -0
- data/lib/generators/propel_api/templates/tests/integration_test_template.rb.tt +487 -0
- data/lib/generators/propel_api/templates/tests/model_test_template.rb.tt +252 -0
- data/lib/generators/propel_api/unpack/unpack_generator.rb +304 -0
- data/lib/propel_api.rb +3 -0
- metadata +95 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 39e7f4915254a37e32cd3ceacaac8402a3c0f32768d0169345cf69fdb742d52d
|
4
|
+
data.tar.gz: 302f7022de04cd30f8c0efac85267a4e636c175169478600094a7f9c9100c465
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f74c378babcf34e7077117ae21720335903589c58e4aeb68800b3abbfe8c621c323c1f0b69d12557e80e27b3c715c9aa5047a8b9c7389c834db884435e6e4cec
|
7
|
+
data.tar.gz: 6e93710aad4c65df5822e046239b1ea1a661dd44d7efdef050fd4fcfd734a216e4451150513df75d71f7d21748cabf83d9eb70eced4651270ff8bfa27f230e35
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
|
+
|
8
|
+
## [Unreleased]
|
9
|
+
|
10
|
+
### Planned Features
|
11
|
+
- GraphQL adapter support
|
12
|
+
- API versioning migration tools
|
13
|
+
- Advanced relationship inference patterns
|
14
|
+
- Custom serialization adapter framework
|
15
|
+
- Performance optimization tools
|
16
|
+
|
17
|
+
## [0.1.0] - 2025-01-XX
|
18
|
+
|
19
|
+
### Added
|
20
|
+
- **Self-extracting generator gem architecture** - Install temporarily, extract code, remove dependency
|
21
|
+
- **Rails generator system** with install and unpack commands
|
22
|
+
- **Dual serialization adapter support** - PropelFacets and Graphiti
|
23
|
+
- **Complete API resource generation** including:
|
24
|
+
- Models with smart association inference
|
25
|
+
- Controllers with full CRUD operations
|
26
|
+
- Migrations with proper constraints
|
27
|
+
- Routes with configurable namespacing/versioning
|
28
|
+
- Comprehensive test suite (model, controller, integration)
|
29
|
+
- Realistic seed data using Faker
|
30
|
+
- Test fixtures with sample data
|
31
|
+
|
32
|
+
### Features
|
33
|
+
- **Intelligent relationship inference** - Automatically detects and creates associations
|
34
|
+
- **Polymorphic association support** - Handles `commentable:references` and `resource_parent:references` patterns
|
35
|
+
- **Configurable API structure** - Customizable namespace and version patterns
|
36
|
+
- **PropelFacets integration** - JSON facet system with inheritance and field selection
|
37
|
+
- **Graphiti integration** - JSON:API compliant resources with automatic generation
|
38
|
+
- **Production-ready features** - Pagination, filtering, authentication integration
|
39
|
+
- **Comprehensive testing** - Full test coverage with realistic data
|
40
|
+
- **Flexible unpack system** - Extract and customize any component
|
41
|
+
|
42
|
+
### Configuration
|
43
|
+
- PropelApi configuration system with adapter/namespace/version defaults
|
44
|
+
- Command-line option support with priority hierarchy
|
45
|
+
- Integration with Propel orchestration system
|
46
|
+
|
47
|
+
### Dependencies
|
48
|
+
- Rails 7.0+ support
|
49
|
+
- Faker integration for realistic seed data
|
50
|
+
- Pagy pagination for PropelFacets adapter
|
51
|
+
- HasScope filtering for PropelFacets adapter
|
52
|
+
- Graphiti ecosystem support for JSON:API adapter
|
53
|
+
|
54
|
+
### Documentation
|
55
|
+
- Complete installation and usage guide
|
56
|
+
- Self-extracting architecture explanation
|
57
|
+
- Adapter comparison and selection guide
|
58
|
+
- Advanced relationship pattern examples
|
59
|
+
- Production deployment considerations
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 Propel API Generator
|
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,320 @@
|
|
1
|
+
# PropelApi
|
2
|
+
|
3
|
+
A comprehensive Rails generator that creates complete API resources with models, controllers, tests, and realistic seed data. Supports both PropelFacets (JSON Facet) and Graphiti serialization engines with **fixed route insertion** and proper indentation.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
PropelApi is designed as a **self-extracting generator gem**. You install it temporarily, run the generators to extract the code into your application, then remove the gem dependency.
|
8
|
+
|
9
|
+
### Step 1: Add to Gemfile as a Path Gem
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
# In your Gemfile
|
13
|
+
gem 'propel_api', path: 'propel_api'
|
14
|
+
```
|
15
|
+
|
16
|
+
### Step 2: Bundle Install
|
17
|
+
|
18
|
+
```bash
|
19
|
+
bundle install
|
20
|
+
```
|
21
|
+
|
22
|
+
### Step 3: Unpack the Generator (Optional)
|
23
|
+
|
24
|
+
If you want to customize the generator templates:
|
25
|
+
|
26
|
+
```bash
|
27
|
+
rails generate propel_api:unpack
|
28
|
+
```
|
29
|
+
|
30
|
+
This extracts the generator into `lib/generators/propel_api/` for customization.
|
31
|
+
|
32
|
+
### Step 4: Install PropelApi
|
33
|
+
|
34
|
+
```bash
|
35
|
+
rails generate propel_api:install --adapter=propel_facets
|
36
|
+
# or
|
37
|
+
rails generate propel_api:install --adapter=graphiti
|
38
|
+
```
|
39
|
+
|
40
|
+
This installs the API controller base class with your chosen serialization adapter.
|
41
|
+
|
42
|
+
### Step 5: Remove Gem Dependency (Optional)
|
43
|
+
|
44
|
+
After installation, you can remove the gem from your Gemfile. All functionality remains in your application.
|
45
|
+
|
46
|
+
## Usage
|
47
|
+
|
48
|
+
### Generate Complete API Resources
|
49
|
+
|
50
|
+
```bash
|
51
|
+
# Basic resource with PropelFacets
|
52
|
+
rails generate propel_api User name:string email:string role:string
|
53
|
+
|
54
|
+
# Resource with associations
|
55
|
+
rails generate propel_api Article title:string content:text user:references category:references
|
56
|
+
|
57
|
+
# Polymorphic associations
|
58
|
+
rails generate propel_api Comment content:text commentable:references
|
59
|
+
|
60
|
+
# With Graphiti adapter
|
61
|
+
rails generate propel_api Product name:string price:decimal --adapter=graphiti
|
62
|
+
```
|
63
|
+
|
64
|
+
This generates:
|
65
|
+
- **Model** with associations and facets/resources
|
66
|
+
- **Migration** with proper constraints
|
67
|
+
- **Controller** with full CRUD operations
|
68
|
+
- **Routes** with proper namespacing and **correct indentation**
|
69
|
+
- **Tests** (model, controller, integration)
|
70
|
+
- **Fixtures** with realistic test data
|
71
|
+
- **Seeds** with Faker-generated sample data
|
72
|
+
|
73
|
+
### Generate Controllers for Existing Models
|
74
|
+
|
75
|
+
```bash
|
76
|
+
# Auto-detect model attributes (with warning)
|
77
|
+
rails generate propel_api:controller User
|
78
|
+
|
79
|
+
# Explicitly auto-detect all attributes
|
80
|
+
rails generate propel_api:controller User --all-attributes
|
81
|
+
|
82
|
+
# Specify specific attributes
|
83
|
+
rails generate propel_api:controller User name:string email:string
|
84
|
+
|
85
|
+
# Custom namespace and version
|
86
|
+
rails generate propel_api:controller User --namespace=admin_api --version=v2
|
87
|
+
```
|
88
|
+
|
89
|
+
### API Controller Usage
|
90
|
+
|
91
|
+
#### PropelFacets Adapter
|
92
|
+
```ruby
|
93
|
+
class Api::V1::UsersController < Api::V1::ApiController
|
94
|
+
permitted_params :name, :email, :role
|
95
|
+
|
96
|
+
connect_facet :short, actions: [:index]
|
97
|
+
connect_facet :details, actions: [:show, :create, :update]
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
#### Graphiti Adapter
|
102
|
+
```ruby
|
103
|
+
class Api::V1::UsersController < Api::V1::ApiController
|
104
|
+
self.resource = UserResource
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
### Configuration Options
|
109
|
+
|
110
|
+
Configure PropelApi defaults in `config/initializers/propel_api.rb`:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
PropelApi.configure do |config|
|
114
|
+
# Default serialization adapter: 'propel_facets' or 'graphiti'
|
115
|
+
config.adapter = 'propel_facets'
|
116
|
+
|
117
|
+
# Default API namespace: 'api', 'admin_api', etc.
|
118
|
+
config.namespace = 'api'
|
119
|
+
|
120
|
+
# Default API version: 'v1', 'v2', etc.
|
121
|
+
config.version = 'v1'
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
## Features
|
126
|
+
|
127
|
+
### โ
Fixed Route Insertion
|
128
|
+
- **Proper indentation** - Routes are inserted with correct spacing
|
129
|
+
- **Namespace support** - Works with nested namespaces (api/v1)
|
130
|
+
- **No duplicates** - Prevents duplicate route insertion
|
131
|
+
- **Correct positioning** - Routes inserted in the right location
|
132
|
+
|
133
|
+
### Automatic Resource Generation
|
134
|
+
- **Smart associations** - Automatically infers relationships from field types
|
135
|
+
- **Polymorphic support** - Handles `commentable:references` patterns
|
136
|
+
- **Proper constraints** - Makes organization required, others optional
|
137
|
+
- **Realistic test data** - Uses Faker for meaningful sample data
|
138
|
+
|
139
|
+
### Dual Serialization Support
|
140
|
+
- **PropelFacets** - Flexible JSON views with inheritance and facet system
|
141
|
+
- **Graphiti** - JSON:API compliant with automatic resource generation
|
142
|
+
- **Adapter switching** - Change serialization strategy per project needs
|
143
|
+
|
144
|
+
### Complete Test Coverage
|
145
|
+
- **Model tests** - Validations, associations, business logic
|
146
|
+
- **Controller tests** - CRUD operations, parameter handling
|
147
|
+
- **Integration tests** - End-to-end API workflows
|
148
|
+
- **Fixtures** - Realistic test data for reliable testing
|
149
|
+
|
150
|
+
### Production-Ready Features
|
151
|
+
- **Pagination** - Built-in with Pagy (PropelFacets) or Graphiti
|
152
|
+
- **Filtering** - HasScope integration for PropelFacets
|
153
|
+
- **Authentication** - Ready for PropelAuth integration
|
154
|
+
- **Error handling** - Proper JSON error responses
|
155
|
+
|
156
|
+
## Self-Extracting Architecture
|
157
|
+
|
158
|
+
PropelApi follows a self-extracting pattern that provides:
|
159
|
+
|
160
|
+
- **No runtime dependencies** - all code lives in your application
|
161
|
+
- **Full control** - modify any component after installation
|
162
|
+
- **No black boxes** - transparent, readable Rails code
|
163
|
+
- **Easy maintenance** - standard Rails patterns throughout
|
164
|
+
|
165
|
+
After installation, you can:
|
166
|
+
- Remove the gem from your Gemfile
|
167
|
+
- Customize all generated code
|
168
|
+
- Maintain and extend functionality independently
|
169
|
+
- Switch between PropelFacets and Graphiti adapters
|
170
|
+
|
171
|
+
## Advanced Usage
|
172
|
+
|
173
|
+
### Custom Namespaces and Versions
|
174
|
+
```bash
|
175
|
+
# Admin API with different namespace
|
176
|
+
rails generate propel_api:install --namespace=admin_api --version=v2
|
177
|
+
|
178
|
+
# No namespace/version
|
179
|
+
rails generate propel_api:install --namespace=none --version=none
|
180
|
+
```
|
181
|
+
|
182
|
+
### Model Attribute Detection
|
183
|
+
PropelApi provides intelligent attribute detection with security filtering:
|
184
|
+
|
185
|
+
```bash
|
186
|
+
# Manual attributes (no filtering)
|
187
|
+
rails generate propel_api:controller User name:string email:string
|
188
|
+
|
189
|
+
# Auto-detect with explicit flag
|
190
|
+
rails generate propel_api:controller User --all-attributes
|
191
|
+
|
192
|
+
# Auto-detect with warning (when no attributes specified)
|
193
|
+
rails generate propel_api:controller User
|
194
|
+
```
|
195
|
+
|
196
|
+
**Security Filtering**: Sensitive attributes are automatically filtered:
|
197
|
+
- Passwords, tokens, keys, secrets
|
198
|
+
- Timestamps and Rails internals
|
199
|
+
- Large content fields
|
200
|
+
- Binary data
|
201
|
+
|
202
|
+
Customize filtering in `config/initializers/propel_api.rb`:
|
203
|
+
```ruby
|
204
|
+
PropelApi.attribute_filter.add_sensitive_pattern(/private_.*/)
|
205
|
+
PropelApi.attribute_filter.add_excluded_pattern(/legacy_.*/)
|
206
|
+
```
|
207
|
+
|
208
|
+
### Relationship Patterns
|
209
|
+
```bash
|
210
|
+
# Standard belongs_to/has_many
|
211
|
+
rails generate propel_api Article title:string user:references
|
212
|
+
|
213
|
+
# Polymorphic associations (commentable -> Comment belongs_to :commentable, polymorphic: true)
|
214
|
+
rails generate propel_api Comment content:text commentable:references
|
215
|
+
|
216
|
+
# Resource parent pattern (resource_parent -> Attachment belongs_to :resource, polymorphic: true)
|
217
|
+
rails generate propel_api Attachment name:string resource_parent:references
|
218
|
+
```
|
219
|
+
|
220
|
+
### Route Generation Examples
|
221
|
+
|
222
|
+
#### Nested Namespace (api/v1)
|
223
|
+
```ruby
|
224
|
+
# Input routes.rb:
|
225
|
+
Rails.application.routes.draw do
|
226
|
+
namespace :api do
|
227
|
+
namespace :v1 do
|
228
|
+
# Add your API routes here
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# After: rails generate propel_api:controller User
|
234
|
+
Rails.application.routes.draw do
|
235
|
+
namespace :api do
|
236
|
+
namespace :v1 do
|
237
|
+
resources :users # โ Correctly indented with 4 spaces
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
```
|
242
|
+
|
243
|
+
#### Single Namespace
|
244
|
+
```ruby
|
245
|
+
# After: rails generate propel_api:controller User --namespace=api --version=none
|
246
|
+
Rails.application.routes.draw do
|
247
|
+
namespace :api do
|
248
|
+
resources :users # โ Correctly indented with 2 spaces
|
249
|
+
end
|
250
|
+
end
|
251
|
+
```
|
252
|
+
|
253
|
+
### Seed Data Generation
|
254
|
+
```ruby
|
255
|
+
# Generated seeds use Faker for realistic data
|
256
|
+
User.create!([
|
257
|
+
{ name: Faker::Name.name, email: Faker::Internet.email, role: 'admin' },
|
258
|
+
{ name: Faker::Name.name, email: Faker::Internet.email, role: 'user' }
|
259
|
+
])
|
260
|
+
```
|
261
|
+
|
262
|
+
## Dependencies
|
263
|
+
|
264
|
+
### PropelFacets Adapter
|
265
|
+
- `propel_facets` - JSON facet system (install separately)
|
266
|
+
- `pagy` - Pagination
|
267
|
+
- `has_scope` - Filtering
|
268
|
+
- `ransack` - Advanced search
|
269
|
+
|
270
|
+
### Graphiti Adapter
|
271
|
+
- `graphiti` - JSON:API resource framework
|
272
|
+
- `graphiti-rails` - Rails integration
|
273
|
+
- `vandal_ui` - Interactive API documentation
|
274
|
+
|
275
|
+
## Route Insertion Technical Details
|
276
|
+
|
277
|
+
PropelApi includes **completely fixed route insertion** that handles:
|
278
|
+
|
279
|
+
### Indentation Rules
|
280
|
+
- **Nested namespaces** (api/v1): 4 spaces for resources
|
281
|
+
- **Single namespaces** (api): 2 spaces for resources
|
282
|
+
- **Rails route method**: Automatically adds 2 spaces to each line
|
283
|
+
|
284
|
+
### Insertion Logic
|
285
|
+
- Detects existing namespace structures
|
286
|
+
- Inserts at correct position (after namespace opening)
|
287
|
+
- Prevents duplicate routes
|
288
|
+
- Maintains proper Rails formatting
|
289
|
+
|
290
|
+
### Error Prevention
|
291
|
+
- Validates route placement before insertion
|
292
|
+
- Shows warnings for duplicate routes
|
293
|
+
- Maintains file structure integrity
|
294
|
+
|
295
|
+
## Documentation
|
296
|
+
|
297
|
+
After installation:
|
298
|
+
- API examples: `doc/api_controller_example.rb`
|
299
|
+
- Configuration: `config/initializers/propel_api.rb`
|
300
|
+
- Generated tests show usage patterns
|
301
|
+
- Seed files demonstrate data creation
|
302
|
+
|
303
|
+
## Development
|
304
|
+
|
305
|
+
```bash
|
306
|
+
# Run generator tests
|
307
|
+
cd propel_api
|
308
|
+
bundle exec rake test
|
309
|
+
|
310
|
+
# Test route insertion specifically
|
311
|
+
bundle exec ruby -Ilib:test test/generators/propel_api/route_insertion_test.rb
|
312
|
+
```
|
313
|
+
|
314
|
+
## Contributing
|
315
|
+
|
316
|
+
Bug reports and pull requests are welcome on GitHub.
|
317
|
+
|
318
|
+
## License
|
319
|
+
|
320
|
+
The gem is available as open source under the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "bundler/gem_tasks"
|
4
|
+
require "rake/testtask"
|
5
|
+
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.libs << "lib"
|
9
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
10
|
+
t.verbose = true
|
11
|
+
end
|
12
|
+
|
13
|
+
# Individual test tasks for better organization
|
14
|
+
namespace :test do
|
15
|
+
Rake::TestTask.new(:generators) do |t|
|
16
|
+
t.libs << "test"
|
17
|
+
t.libs << "lib"
|
18
|
+
t.test_files = FileList["test/generators/**/*_test.rb"]
|
19
|
+
t.description = "Run generator tests"
|
20
|
+
end
|
21
|
+
|
22
|
+
Rake::TestTask.new(:integration) do |t|
|
23
|
+
t.libs << "test"
|
24
|
+
t.libs << "lib"
|
25
|
+
t.test_files = FileList["test/integration/**/*_test.rb"]
|
26
|
+
t.description = "Run integration tests"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
task default: :test
|
31
|
+
|
32
|
+
desc "Run all tests with verbose output"
|
33
|
+
task :test_verbose do
|
34
|
+
ENV['VERBOSE'] = 'true'
|
35
|
+
Rake::Task[:test].invoke
|
36
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../core/named_base'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
##
|
7
|
+
# PropelApi controller generator that creates API controllers, routes, and tests for existing models
|
8
|
+
#
|
9
|
+
# Usage:
|
10
|
+
# rails generate propel_api:controller User # Auto-introspect (with warning)
|
11
|
+
# rails generate propel_api:controller User --all-attributes # Auto-introspect (explicit)
|
12
|
+
# rails generate propel_api:controller User name:string # Use specified attributes
|
13
|
+
# rails generate propel_api:controller User --namespace=admin_api --version=v2
|
14
|
+
#
|
15
|
+
# Attribute Detection Priority:
|
16
|
+
# 1. --all-attributes flag โ Always introspect model attributes (with configurable filtering)
|
17
|
+
# 2. Manual attributes โ Use specified attributes (no filtering applied)
|
18
|
+
# 3. No attributes + model exists โ Auto-introspect with educational warning (with configurable filtering)
|
19
|
+
# 4. No attributes + no model โ Error (model must exist)
|
20
|
+
#
|
21
|
+
# Configurable Security Filtering:
|
22
|
+
# Filtering is controlled by PropelApi.attribute_filter in config/initializers/propel_api.rb
|
23
|
+
# - Sensitive patterns: passwords, tokens, keys, SSNs, etc.
|
24
|
+
# - Excluded patterns: timestamps, Rails internals, binary data
|
25
|
+
# - Large content patterns: descriptions, content, body text, etc.
|
26
|
+
#
|
27
|
+
# Customize for your project's naming conventions:
|
28
|
+
# PropelApi.attribute_filter.add_sensitive_pattern(/private_.*/)
|
29
|
+
# PropelApi.attribute_filter.add_excluded_pattern(/legacy_.*/)
|
30
|
+
#
|
31
|
+
# View current patterns: PropelApi.attribute_filter.filtering_summary
|
32
|
+
#
|
33
|
+
# This generator assumes the model already exists and creates:
|
34
|
+
# - API controller with full CRUD operations
|
35
|
+
# - Routes with proper namespacing
|
36
|
+
# - Controller tests
|
37
|
+
# - Integration tests
|
38
|
+
#
|
39
|
+
module PropelApi
|
40
|
+
class ControllerGenerator < PropelApi::NamedBase
|
41
|
+
source_root File.expand_path("../templates", __dir__)
|
42
|
+
|
43
|
+
desc "Generate PropelApi controller, routes and tests for existing model"
|
44
|
+
|
45
|
+
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
46
|
+
|
47
|
+
def validate_model_exists
|
48
|
+
# Ensure attributes are parsed before validation
|
49
|
+
parse_attributes_from_args
|
50
|
+
|
51
|
+
# Only require model when auto-introspection is needed
|
52
|
+
model_file_exists = File.exist?(File.join(destination_root, "app/models/#{file_name}.rb"))
|
53
|
+
|
54
|
+
if should_auto_introspect? && !model_file_exists
|
55
|
+
raise Thor::Error, "Model #{class_name} does not exist. Please create the model first or use 'rails generate propel_api #{class_name}' to create the complete resource."
|
56
|
+
end
|
57
|
+
|
58
|
+
# Show educational warning when no explicit choice is made
|
59
|
+
if !options[:all_attributes] && (@attributes.nil? || @attributes.empty?) && model_file_exists
|
60
|
+
show_auto_introspection_warning
|
61
|
+
end
|
62
|
+
# Validate attributes if not using introspection
|
63
|
+
validate_attributes_exist unless should_auto_introspect?
|
64
|
+
end
|
65
|
+
|
66
|
+
def create_controller
|
67
|
+
# Use shared method from Base class
|
68
|
+
create_propel_controller
|
69
|
+
end
|
70
|
+
|
71
|
+
def create_routes
|
72
|
+
# Use shared method from Base class
|
73
|
+
create_propel_routes
|
74
|
+
end
|
75
|
+
|
76
|
+
def create_tests
|
77
|
+
# Use shared method from Base class with controller and integration tests only
|
78
|
+
create_propel_tests(test_types: [:controller, :integration])
|
79
|
+
end
|
80
|
+
|
81
|
+
def show_completion_message
|
82
|
+
# Use shared completion message framework from Base class
|
83
|
+
generated_files = [
|
84
|
+
"๐ฎ Controller: app/controllers/#{controller_path}/#{controller_file_name}.rb",
|
85
|
+
"๐งช Controller Tests: test/controllers/#{controller_path}/#{controller_file_name}_test.rb",
|
86
|
+
"๐งช Integration Tests: test/integration/#{file_name}_api_test.rb"
|
87
|
+
]
|
88
|
+
|
89
|
+
next_steps = [
|
90
|
+
"Test your API: GET #{api_route_path}",
|
91
|
+
"Run tests: rails test test/controllers/#{controller_path}/#{controller_file_name}_test.rb",
|
92
|
+
"Run integration tests: rails test test/integration/#{file_name}_api_test.rb"
|
93
|
+
]
|
94
|
+
|
95
|
+
if @adapter == 'propel_facets'
|
96
|
+
next_steps << "Customize facets in: app/models/#{file_name}.rb"
|
97
|
+
end
|
98
|
+
|
99
|
+
show_propel_completion_message(
|
100
|
+
resource_type: "Controller",
|
101
|
+
generated_files: generated_files,
|
102
|
+
next_steps: next_steps
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def route_name
|
109
|
+
file_name.pluralize
|
110
|
+
end
|
111
|
+
|
112
|
+
# Controller generator helper methods
|
113
|
+
def attributes
|
114
|
+
if should_auto_introspect?
|
115
|
+
# Auto-introspection: either --all-attributes or no attributes specified
|
116
|
+
# Return introspected attributes as attribute-like objects for template compatibility
|
117
|
+
introspect_model_attributes.map do |attr|
|
118
|
+
OpenStruct.new(name: attr[:name], type: attr[:type])
|
119
|
+
end
|
120
|
+
else
|
121
|
+
# Manual attributes specified: use those
|
122
|
+
# Return cached parsed attributes, or parse them if not done yet
|
123
|
+
@parsed_attributes || parse_attributes_from_args
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def permitted_param_names
|
128
|
+
if should_auto_introspect?
|
129
|
+
# Auto-introspection: either --all-attributes or no attributes specified
|
130
|
+
introspected_permitted_params
|
131
|
+
else
|
132
|
+
# Manual attributes specified: use those
|
133
|
+
attributes.map do |attr|
|
134
|
+
# Convert references to foreign key names for permitted params
|
135
|
+
if attr.type == :references
|
136
|
+
"#{attr.name}_id"
|
137
|
+
else
|
138
|
+
attr.name
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def parse_attributes_from_args
|
145
|
+
return [] unless @attributes
|
146
|
+
|
147
|
+
@parsed_attributes ||= @attributes.map do |attr_string|
|
148
|
+
# Parse "field:type" strings into attribute objects
|
149
|
+
if attr_string.include?(':')
|
150
|
+
name, type = attr_string.split(':', 2)
|
151
|
+
OpenStruct.new(name: name, type: type.to_sym)
|
152
|
+
else
|
153
|
+
# Default to string type if no type specified
|
154
|
+
OpenStruct.new(name: attr_string, type: :string)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def show_auto_introspection_warning
|
160
|
+
say "\n" + "="*70, :yellow
|
161
|
+
say "โ ๏ธ No attributes specified - auto-detecting from #{class_name} model", :yellow
|
162
|
+
say "="*70, :yellow
|
163
|
+
|
164
|
+
introspected_attrs = introspected_permitted_params
|
165
|
+
say "๐ Detected attributes: #{introspected_attrs.join(', ')}", :cyan
|
166
|
+
|
167
|
+
# Show filtering information
|
168
|
+
show_filtering_info
|
169
|
+
|
170
|
+
say "\n๐ก Best practice: Be explicit about your intent:", :green
|
171
|
+
say " Auto-detect all: rails g propel_api:controller #{class_name} --all-attributes", :green
|
172
|
+
say " Specify some: rails g propel_api:controller #{class_name} name:string email:string", :green
|
173
|
+
say "="*70, :yellow
|
174
|
+
say ""
|
175
|
+
end
|
176
|
+
|
177
|
+
def show_filtering_info
|
178
|
+
say "\nโ ๏ธ SECURITY NOTE:", :red
|
179
|
+
say " Sensitive attributes are automatically filtered using configurable patterns.", :red
|
180
|
+
|
181
|
+
# Try to show current filter configuration
|
182
|
+
begin
|
183
|
+
if defined?(PropelApi) && PropelApi.respond_to?(:attribute_filter)
|
184
|
+
filter = PropelApi.attribute_filter
|
185
|
+
total_patterns = filter.sensitive_patterns.size + filter.excluded_patterns.size + filter.large_content_patterns.size
|
186
|
+
say " Active filtering patterns: #{total_patterns} (#{filter.sensitive_patterns.size} sensitive, #{filter.excluded_patterns.size} excluded, #{filter.large_content_patterns.size} large content)", :red
|
187
|
+
say " ๐ View patterns: PropelApi.attribute_filter.filtering_summary", :cyan
|
188
|
+
say " ๐ง Customize in: config/initializers/propel_api.rb", :cyan
|
189
|
+
else
|
190
|
+
say " Using fallback patterns (configure in config/initializers/propel_api.rb)", :red
|
191
|
+
end
|
192
|
+
rescue => e
|
193
|
+
say " Using fallback patterns (PropelApi configuration not available)", :red
|
194
|
+
end
|
195
|
+
|
196
|
+
say " ๐จ Review filtering to ensure it matches your naming conventions!", :red
|
197
|
+
end
|
198
|
+
|
199
|
+
def should_auto_introspect?
|
200
|
+
# Auto-introspect when:
|
201
|
+
# 1. --all-attributes is explicitly passed (takes precedence over manual attributes)
|
202
|
+
# 2. No attributes specified AND model exists (auto-fallback with warning)
|
203
|
+
options[:all_attributes] || ((@attributes.nil? || @attributes.empty?) && model_exists?)
|
204
|
+
end
|
205
|
+
|
206
|
+
public
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'configuration_methods'
|
4
|
+
require_relative 'path_generation_methods'
|
5
|
+
|
6
|
+
##
|
7
|
+
# Base class for all PropelApi generators
|
8
|
+
# Provides shared configuration detection, validation, and path generation logic
|
9
|
+
#
|
10
|
+
module PropelApi
|
11
|
+
class Base < Rails::Generators::Base
|
12
|
+
include PropelApi::ConfigurationMethods
|
13
|
+
include PropelApi::PathGenerationMethods
|
14
|
+
|
15
|
+
|
16
|
+
|
17
|
+
private
|
18
|
+
end
|
19
|
+
end
|