propel_api 0.2.1 โ†’ 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +58 -0
  3. data/README.md +239 -4
  4. data/lib/generators/propel_api/core/named_base.rb +92 -1
  5. data/lib/generators/propel_api/core/relationship_inferrer.rb +4 -11
  6. data/lib/generators/propel_api/install/install_generator.rb +2 -2
  7. data/lib/generators/propel_api/resource/resource_generator.rb +205 -63
  8. data/lib/generators/propel_api/templates/config/propel_api.rb.tt +26 -1
  9. data/lib/generators/propel_api/templates/controllers/api_controller_graphiti.rb +2 -2
  10. data/lib/generators/propel_api/templates/controllers/api_controller_propel_facets.rb +20 -10
  11. data/lib/generators/propel_api/templates/controllers/example_controller.rb.tt +2 -2
  12. data/lib/generators/propel_api/templates/scaffold/facet_controller_template.rb.tt +34 -9
  13. data/lib/generators/propel_api/templates/scaffold/facet_model_template.rb.tt +17 -8
  14. data/lib/generators/propel_api/templates/scaffold/graphiti_controller_template.rb.tt +1 -1
  15. data/lib/generators/propel_api/templates/scaffold/graphiti_resource_template.rb.tt +2 -2
  16. data/lib/generators/propel_api/templates/seeds/seeds_template.rb.tt +62 -14
  17. data/lib/generators/propel_api/templates/tests/controller_test_template.rb.tt +34 -6
  18. data/lib/generators/propel_api/templates/tests/fixtures_template.yml.tt +58 -15
  19. data/lib/generators/propel_api/templates/tests/integration_test_template.rb.tt +108 -36
  20. data/lib/generators/propel_api/templates/tests/model_test_template.rb.tt +20 -0
  21. data/lib/propel_api.rb +1 -1
  22. metadata +22 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8fb51b38b12561c709712a339a39cf3639cd1e8a16149f72c00f1566ee37bdcf
4
- data.tar.gz: a90352463962e6385b895cf339195b6860f0d5e98ccf1c4e6e3237957f6895cf
3
+ metadata.gz: 224fb8718c35e1b5d828201c8256a39eeedc0cc6f9ad096ac874f34c9a726c25
4
+ data.tar.gz: 6902529de75450cd89415ea357891100f2c2a2d4cc6c57ba6679257e693aaab3
5
5
  SHA512:
6
- metadata.gz: a2d01e1bcc8f7a749e8de4101d6f7be9dc5c4a7b2b0f2b7d5dc78e630dbba0102cedee82ff957cd210bde487a66c0b936a546b50c75f38e45db84dabcc74e6d0
7
- data.tar.gz: 94dad0a33d4fd5af9327d42b21faa0a69bcdd7a018d914293db3643e68496b4b4f979d99cb14f8683b0df3040f6f15b55b6c4ff47c72015da9eaf6d336d5fb83
6
+ metadata.gz: dcd7b7a650de42cf4d0f19e51e777535e399c7f01dd0947e6ecb97854504a2e1a79abfa0341f7bb2dae4dc128cc9261b0c42060c5218aa6129c465920eeb6546
7
+ data.tar.gz: 5ee7eace1077821dbb23b93f719d3544447e8a72d5ba0541df8712d55b6294054d2f335aaa4d8bad43e60ea4b69d7737733e4e63bc722bde3ad5fbd2b85a1418
data/CHANGELOG.md CHANGED
@@ -10,9 +10,67 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
10
10
  ### Planned Features
11
11
  - GraphQL adapter support
12
12
 
13
+ ## [0.3.0] - 2025-01-15
14
+
15
+ ### ๐ŸŽ‰ Major Features Added
16
+ - **Full Polymorphic Association Support**: Complete implementation of polymorphic relationships in API resources
17
+ - New `--parents` command-line option for specifying valid parent models
18
+ - Automatic generation of correct fixtures, tests, and API parameters
19
+ - Support for quote-free polymorphic syntax: `field_name:polymorphic`
20
+ - Enhanced fixture generation using Rails' `fixture_name (ModelName)` syntax
21
+
22
+ ### โš ๏ธ Breaking Changes
23
+ - **Generator Syntax Change**: Tenancy associations are not automatically created you must pass organization:references and/or agency:references and configure the tenancy in the PropelApi configurations, any automated workflows or CI/CD that generate resources assuming the organization and/or agency references will be auto-injected will break with generator calls.
24
+ - **Required --parents Option**: Polymorphic resources now require `--parents field_name:Parent1,Parent2` for proper test generation
25
+ - **Fixture Format Change**: Polymorphic fixtures now use `field_name: fixture_name (ParentClass)` instead of hardcoded IDs
26
+ - **Test Template Updates**: All test templates now include polymorphic-aware parameter generation
27
+
28
+ ### ๐Ÿ”ง Generator Improvements
29
+ - **Enhanced Resource Generator**:
30
+ - Added polymorphic syntax conversion (`field_name:polymorphic` โ†’ `field_name:references{polymorphic}`)
31
+ - Improved argument processing to handle polymorphic parent specifications
32
+ - Fixed migration generation to use processed attributes instead of raw ARGV
33
+ - **Smart Fixture References**:
34
+ - Automatic fixture name resolution (e.g., uses `marketing_agency` instead of `one` for Agency models)
35
+ - Dynamic parent type selection for varied test data
36
+ - Proper Rails fixture syntax for polymorphic associations
37
+
38
+ ### ๐Ÿงช Test Generation Enhancements
39
+ - **Model Tests**:
40
+ - Polymorphic association validation with proper parent type checking
41
+ - Automatic setup of polymorphic parent variables (`@field_name`)
42
+ - Correct assertions for polymorphic relationships
43
+ - **Controller Tests**:
44
+ - Automatic inclusion of both `field_name_id` and `field_name_type` in test parameters
45
+ - Proper polymorphic parent setup in test fixtures
46
+ - **Integration Tests**:
47
+ - Enhanced error handling tests with polymorphic parameter support
48
+ - Multi-tenancy isolation tests work correctly with polymorphic associations
49
+ - Comprehensive API workflow testing
50
+
51
+ ### ๐Ÿ—๏ธ Architecture Improvements
52
+ - **Named Base Enhancements**:
53
+ - New `polymorphic_associations` helper method
54
+ - Enhanced `polymorphic_parent_for_fixture` with fixture name resolution
55
+ - Improved parent type parsing and validation
56
+ - **Template System**:
57
+ - All ERB templates now polymorphic-aware
58
+ - Consistent handling of polymorphic vs. regular references
59
+ - Better error handling and edge case management
60
+
61
+ ### ๐Ÿ› Bug Fixes
62
+ - **Migration Constraints**: Fixed polymorphic migrations to use `polymorphic: true` instead of incorrect `foreign_key: true`
63
+ - **Fixture Loading**: Resolved issues with hardcoded IDs causing fixture loading failures
64
+ - **Test Isolation**: Fixed multi-tenancy tests failing due to missing polymorphic associations
65
+ - **API Parameter Generation**: Corrected missing `_type` parameters in generated API calls
66
+
13
67
  ## [0.2.1] - 2025-01-14
14
68
 
15
69
  ### Fixed
70
+ - **Critical Pagy pagination bug**: Fixed `NoMethodError: undefined method 'items' for Pagy`
71
+ - API controller templates now use correct `pagy.limit` instead of non-existent `pagy.items`
72
+ - Fixes pagination metadata generation in API responses
73
+ - Affects all generated API controllers with pagination
16
74
  - **Dependency update**: Improved compatibility with PropelFacets 0.2.1
17
75
  - API controllers now work correctly with fixed `for_organization` scope installation
18
76
  - Resolves `NoMethodError` when using generated controllers in fresh Rails installations
data/README.md CHANGED
@@ -49,13 +49,16 @@ After installation, you can remove the gem from your Gemfile. All functionality
49
49
 
50
50
  ```bash
51
51
  # Basic resource with PropelFacets
52
- rails generate propel_api User name:string email:string role:string
52
+ rails generate propel_api:resource User name:string email:string role:string
53
53
 
54
54
  # Resource with associations
55
- rails generate propel_api Article title:string content:text user:references category:references
55
+ rails generate propel_api:resource Article title:string content:text user:references category:references
56
56
 
57
- # Polymorphic associations
58
- rails generate propel_api Comment content:text commentable:references
57
+ # ๐ŸŽ‰ NEW: Polymorphic associations (v0.3.0)
58
+ rails generate propel_api:resource Comment content:text commentable:references{polymorphic} --parents commentable:Post,Video,Product
59
+
60
+ # Polymorphic with tenancy
61
+ rails generate propel_api:resource Review rating:integer comment:text review_parent:polymorphic --parents review_parent:Product,Agency
59
62
 
60
63
  # With Graphiti adapter
61
64
  rails generate propel_api Product name:string price:decimal --adapter=graphiti
@@ -70,6 +73,238 @@ This generates:
70
73
  - **Fixtures** with realistic test data
71
74
  - **Seeds** with Faker-generated sample data
72
75
 
76
+ ## ๐ŸŽ‰ Polymorphic Association Support (v0.3.0)
77
+
78
+ PropelApi now provides comprehensive support for polymorphic associations with automatic test generation and intelligent fixture management.
79
+
80
+ ### Quick Start with Polymorphic Resources
81
+
82
+ ```bash
83
+ # Generate a Comment that can belong to Posts, Videos, or Products
84
+ rails generate propel_api:resource Comment body:text commentable:references{polymorphic} --parents commentable:Post,Video,Product
85
+
86
+ # Generate a Review with full tenancy support
87
+ rails generate propel_api:resource Review rating:integer comment:text review_parent:references{polymorphic} --parents reviewable:Product,Agency
88
+ ```
89
+
90
+ ### Key Features
91
+
92
+ #### โœจ Simple, Intuitive Syntax
93
+ - Additional support `field_name:polymorphic` instead of Rails' verbose `field_name:references{polymorphic}`
94
+ - No need for complex quote escaping or nested syntax
95
+
96
+ #### ๐ŸŽฏ Smart Parent Specification
97
+ - `--parents field_name:Parent1,Parent2,Parent3` defines valid parent models
98
+ - Automatically generates varied test data using different parent types
99
+ - Validates parent models exist in your application
100
+
101
+ #### ๐Ÿงช Complete Test Coverage
102
+ - **Model Tests**: Proper polymorphic association validation and parent type checking
103
+ - **Controller Tests**: Automatic inclusion of both `_id` and `_type` parameters
104
+ - **Integration Tests**: Full API workflow testing with polymorphic data
105
+ - **Fixtures**: Uses Rails' clean `fixture_name (ModelName)` syntax
106
+
107
+ #### ๐Ÿ”ง Generated Code Quality
108
+ ```ruby
109
+ # Generated Model
110
+ class Comment < ApplicationRecord
111
+ belongs_to :commentable, polymorphic: true
112
+ # ... other associations and validations
113
+ end
114
+
115
+ # Generated Migration
116
+ def change
117
+ create_table :comments do |t|
118
+ t.text :body
119
+ t.references :commentable, polymorphic: true, null: false
120
+ # ... other fields
121
+ end
122
+ end
123
+
124
+ # Generated Fixtures (clean Rails syntax)
125
+ one:
126
+ commentable: one (Post)
127
+ body: "Great post!"
128
+
129
+ two:
130
+ commentable: featured_video (Video)
131
+ body: "Amazing video content!"
132
+ ```
133
+
134
+ #### ๐Ÿš€ API-Ready Controllers
135
+ Controllers automatically include proper parameter handling:
136
+ ```ruby
137
+ class Api::V1::CommentsController < Api::V1::ApiController
138
+ permitted_params :body, :commentable_id, :commentable_type
139
+ end
140
+ ```
141
+
142
+ ### Breaking Changes from v0.2.x
143
+
144
+ โš ๏ธ **Migration Required**: The polymorphic syntax and test generation has been completely rewritten for better reliability and Rails compatibility.
145
+
146
+ - **Old**: `rails generate propel_api:resource Comment body:text commentable:references{polymorphic}`
147
+ - **New**: `rails generate propel_api:resource Comment body:text commentable:polymorphic --parents commentable:Post,Video`
148
+
149
+ The new approach provides:
150
+ - More reliable test generation
151
+ - Better fixture management
152
+ - Cleaner, more maintainable code
153
+ - Full Rails compatibility
154
+
155
+ ## ๐Ÿš€ API Explorer for Development
156
+
157
+ PropelApi includes a comprehensive test application with multiple API resources perfect for development and testing. The dummy app provides a fully functional API with authentication, multi-tenancy, and polymorphic associations.
158
+
159
+ ### Quick Start with the Test API
160
+
161
+ ```bash
162
+ # Navigate to the test application
163
+ cd test/dummy
164
+
165
+ # Set up the database
166
+ rails db:setup
167
+
168
+ # Start the development server
169
+ rails server
170
+
171
+ # Your API is now running at http://localhost:3000
172
+ ```
173
+
174
+ ### Available API Endpoints
175
+
176
+ The dummy app provides these fully functional endpoints:
177
+
178
+ #### Authentication
179
+ - `POST /api/v1/signup` - User registration
180
+ - `POST /api/v1/login` - User authentication
181
+ - `GET /api/v1/me` - Current user info
182
+ - `DELETE /api/v1/logout` - Logout
183
+ - `POST /api/v1/reset` - Password reset request
184
+ - `PATCH /api/v1/reset` - Password reset update
185
+
186
+ #### Core Resources
187
+ - `GET|POST /api/v1/users` - User management
188
+ - `GET|POST /api/v1/organizations` - Organization management
189
+ - `GET|POST|PATCH|DELETE /api/v1/products` - Product CRUD
190
+ - `GET|POST|PATCH|DELETE /api/v1/comments` - Comment CRUD
191
+ - `GET|POST|PATCH|DELETE /api/v1/attachments` - File attachments
192
+ - `GET|POST|PATCH|DELETE /api/v1/reviews` - Reviews (polymorphic!)
193
+
194
+ ### Setting Up API Explorer Tools
195
+
196
+ #### Option 1: Postman Collection
197
+
198
+ Create a Postman collection with these base settings:
199
+
200
+ ```json
201
+ {
202
+ "info": {
203
+ "name": "PropelApi Development",
204
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
205
+ },
206
+ "variable": [
207
+ {
208
+ "key": "base_url",
209
+ "value": "http://localhost:3000/api/v1",
210
+ "type": "string"
211
+ },
212
+ {
213
+ "key": "auth_token",
214
+ "value": "",
215
+ "type": "string"
216
+ }
217
+ ]
218
+ }
219
+ ```
220
+
221
+ #### Option 2: Insomnia Workspace
222
+
223
+ 1. Create a new workspace in Insomnia
224
+ 2. Set base URL: `http://localhost:3000/api/v1`
225
+ 3. Create environment variables:
226
+ - `base_url`: `http://localhost:3000/api/v1`
227
+ - `auth_token`: (will be populated after login)
228
+
229
+ #### Option 3: cURL Scripts
230
+
231
+ ```bash
232
+ # Login and get token
233
+ TOKEN=$(curl -s -X POST http://localhost:3000/api/v1/login \
234
+ -H "Content-Type: application/json" \
235
+ -d '{"data": {"email_address": "test@example.com", "password": "password123"}}' \
236
+ | jq -r '.token')
237
+
238
+ # Use token for authenticated requests
239
+ curl -H "Authorization: Bearer $TOKEN" \
240
+ -H "Content-Type: application/json" \
241
+ http://localhost:3000/api/v1/me
242
+ ```
243
+
244
+ ### API Response Format
245
+
246
+ All PropelApi endpoints follow consistent JSON:API-inspired formatting:
247
+
248
+ ```json
249
+ {
250
+ "data": {
251
+ "id": 1,
252
+ "name": "Example Product",
253
+ "price": 99.99,
254
+ "organization": {
255
+ "id": 1,
256
+ "name": "Test Organization"
257
+ }
258
+ },
259
+ "meta": {
260
+ "total": 1,
261
+ "page": 1,
262
+ "limit": 25
263
+ }
264
+ }
265
+ ```
266
+
267
+ ### Testing Polymorphic Associations
268
+
269
+ The Reviews resource demonstrates polymorphic associations:
270
+
271
+ ```bash
272
+ # Create a review for a product
273
+ curl -X POST http://localhost:3000/api/v1/reviews \
274
+ -H "Authorization: Bearer $TOKEN" \
275
+ -H "Content-Type: application/json" \
276
+ -d '{
277
+ "data": {
278
+ "rating": 5,
279
+ "comment": "Great product!",
280
+ "reviewable_id": 1,
281
+ "reviewable_type": "Product"
282
+ }
283
+ }'
284
+
285
+ # Create a review for an agency
286
+ curl -X POST http://localhost:3000/api/v1/reviews \
287
+ -H "Authorization: Bearer $TOKEN" \
288
+ -H "Content-Type: application/json" \
289
+ -d '{
290
+ "data": {
291
+ "rating": 4,
292
+ "comment": "Good service!",
293
+ "reviewable_id": 1,
294
+ "reviewable_type": "Agency"
295
+ }
296
+ }'
297
+ ```
298
+
299
+ ### Development Tips
300
+
301
+ 1. **Seed Data**: Run `rails db:seed` to populate with realistic test data
302
+ 2. **Test User**: Default login is `test@example.com` / `password123`
303
+ 3. **Admin User**: Admin login is `admin@example.com` / `password123`
304
+ 4. **Multi-tenancy**: All resources are scoped to organizations automatically
305
+ 5. **Error Handling**: All endpoints return consistent error formats
306
+ 6. **Validation**: Try invalid data to see validation error responses
307
+
73
308
  ### Generate Controllers for Existing Models
74
309
 
75
310
  ```bash
@@ -93,7 +93,7 @@ module PropelApi
93
93
  attributes << attr_data
94
94
  end
95
95
  end
96
-
96
+
97
97
  # Look for basic validations that might indicate attributes
98
98
  # Exclude association names (they're already handled above as foreign keys)
99
99
  # NOTE: Only add validated attributes if they weren't already detected by schema introspection
@@ -514,5 +514,96 @@ module PropelApi
514
514
 
515
515
  content
516
516
  end
517
+
518
+ # Helper methods for templates to detect tenancy attributes
519
+ # These are shared between ResourceGenerator and ControllerGenerator
520
+ def has_organization_reference?
521
+ return false unless defined?(@attributes) && @attributes
522
+ @attributes.any? { |attr| attr.name == 'organization' && attr.type == :references }
523
+ end
524
+
525
+ def has_agency_reference?
526
+ return false unless defined?(@attributes) && @attributes
527
+ @attributes.any? { |attr| attr.name == 'agency' && attr.type == :references }
528
+ end
529
+
530
+ # Polymorphic associations helper methods
531
+ def polymorphic_parent_types
532
+ @polymorphic_parent_types ||= parse_polymorphic_parent_types
533
+ end
534
+
535
+ def polymorphic_associations
536
+ return [] unless defined?(@attributes) && @attributes
537
+
538
+ @polymorphic_associations ||= begin
539
+ polymorphic_attrs = @attributes.select { |attr| attr.type == :references && attr.respond_to?(:polymorphic?) && attr.polymorphic? }
540
+
541
+ polymorphic_attrs.map do |attr|
542
+ parent_types = polymorphic_parent_types[attr.name.to_s] || discover_available_models.first(1)
543
+
544
+ # Create one association entry per polymorphic field with all parent types
545
+ {
546
+ field_name: attr.name,
547
+ id_field: "#{attr.name}_id",
548
+ type_field: "#{attr.name}_type",
549
+ parent_types: parent_types,
550
+ # For fixture generation, we'll use the first parent type for fixture "one", second for "two", etc.
551
+ primary_parent_type: parent_types.first,
552
+ primary_parent_table: parent_types.first.underscore.pluralize
553
+ }
554
+ end
555
+ end
556
+ end
557
+
558
+ # Helper method for fixture generation to get the appropriate parent type for each fixture
559
+ def polymorphic_parent_for_fixture(field_name, fixture_index)
560
+ poly_assoc = polymorphic_associations.find { |assoc| assoc[:field_name].to_s == field_name.to_s }
561
+ return nil unless poly_assoc
562
+
563
+ parent_types = poly_assoc[:parent_types]
564
+ parent_type = parent_types[fixture_index % parent_types.length] || parent_types.first
565
+
566
+ # Determine the fixture name based on parent type and fixture index
567
+ fixture_name = case parent_type.underscore
568
+ when 'agency'
569
+ ['marketing_agency', 'tech_agency', 'sales_agency'][fixture_index] || 'marketing_agency'
570
+ else
571
+ ['one', 'two', 'three'][fixture_index] || 'one'
572
+ end
573
+
574
+ {
575
+ id_field: poly_assoc[:id_field],
576
+ type_field: poly_assoc[:type_field],
577
+ parent_type: parent_type,
578
+ parent_table: parent_type.underscore.pluralize,
579
+ fixture_name: fixture_name
580
+ }
581
+ end
582
+
583
+ private
584
+
585
+ def parse_polymorphic_parent_types
586
+ return {} unless defined?(options) && options[:parents]
587
+
588
+ parsed_types = {}
589
+ options[:parents].each do |field_name, parent_list|
590
+ parsed_types[field_name] = parent_list.split(',').map(&:strip).map(&:camelize)
591
+ end
592
+ parsed_types
593
+ end
594
+
595
+ def discover_available_models
596
+ # Auto-discover available models from app/models directory
597
+ models_dir = File.join(destination_root, 'app/models')
598
+ return ['Post'] unless File.directory?(models_dir) # Fallback
599
+
600
+ model_files = Dir[File.join(models_dir, '*.rb')]
601
+ model_files.map do |file|
602
+ File.basename(file, '.rb').camelize
603
+ end.reject do |model|
604
+ # Skip common non-domain models
605
+ %w[ApplicationRecord Concerns].include?(model)
606
+ end.sort
607
+ end
517
608
  end
518
609
  end
@@ -106,20 +106,13 @@ class RelationshipInferrer
106
106
  end
107
107
 
108
108
  def polymorphic_belongs_to(attribute)
109
- # Extract the base name for polymorphic association
110
- if attribute.name.to_s.end_with?('_parent')
111
- # resource_parent -> resource
112
- base_name = attribute.name.to_s.gsub(/_parent$/, '')
113
- elsif attribute.name.to_s.end_with?('able')
114
- # commentable -> commentable
115
- base_name = attribute.name.to_s
116
- else
117
- base_name = attribute.name.to_s
118
- end
109
+ # Keep the full attribute name for polymorphic associations
110
+ # Don't strip the '_parent' suffix - it's needed to match the schema columns
111
+ association_name = attribute.name.to_s
119
112
 
120
113
  # Polymorphic associations default to required (Rails convention)
121
114
  # Add 'optional: true' manually if optional behavior is needed
122
- "belongs_to :#{base_name}, polymorphic: true"
115
+ "belongs_to :#{association_name}, polymorphic: true"
123
116
  end
124
117
 
125
118
 
@@ -140,8 +140,8 @@ module PropelApi
140
140
 
141
141
  def add_propel_facets_gems
142
142
  add_gem_if_missing 'faker', '~> 3.5', 'Realistic seed data generation'
143
- add_gem_if_missing 'pagy', '~> 9.0', 'Pagination for Propel Facets API'
144
- add_gem_if_missing 'has_scope', '~> 0.8.0', 'Scope filtering for Propel Facets API'
143
+ add_gem_if_missing 'pagy', '~> 9.4', 'Pagination for Propel Facets API'
144
+ add_gem_if_missing 'has_scope', '~> 0.8.2', 'Scope filtering for Propel Facets API'
145
145
  add_gem_if_missing 'ransack', '~> 4.0', 'Advanced search for Propel Facets API'
146
146
 
147
147
  say "Don't forget to run: bundle install", :yellow