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
@@ -0,0 +1,76 @@
|
|
1
|
+
class <%= api_controller_class_name %> < ApplicationController
|
2
|
+
include HasScope
|
3
|
+
include Pagy::Backend
|
4
|
+
include FacetRenderer
|
5
|
+
include StrongParamsHelper
|
6
|
+
include PropelAuthentication
|
7
|
+
|
8
|
+
before_action :authenticate_user
|
9
|
+
before_action :set_resource, only: [:show, :update, :destroy]
|
10
|
+
|
11
|
+
# Connect default facets to actions - can be overridden in subclasses
|
12
|
+
connect_facet :short, actions: [:index]
|
13
|
+
connect_facet :details, actions: [:show, :update, :create]
|
14
|
+
|
15
|
+
def index
|
16
|
+
@pagy, resources = pagy(apply_scopes(resource_class.all))
|
17
|
+
render json: {
|
18
|
+
data: resources.map { |resource| resource_json(resource) },
|
19
|
+
pagination: pagy_metadata(@pagy)
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def show
|
24
|
+
render json: { data: resource_json(@resource) }
|
25
|
+
end
|
26
|
+
|
27
|
+
def create
|
28
|
+
@resource = resource_class.new(resource_params)
|
29
|
+
if @resource.save
|
30
|
+
render json: { data: resource_json(@resource) }, status: :created
|
31
|
+
else
|
32
|
+
render json: { errors: @resource.errors }, status: :unprocessable_entity
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def update
|
37
|
+
if @resource.update(resource_params)
|
38
|
+
render json: { data: resource_json(@resource) }
|
39
|
+
else
|
40
|
+
render json: { errors: @resource.errors }, status: :unprocessable_entity
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def destroy
|
45
|
+
@resource.destroy
|
46
|
+
head :no_content
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def resource_class
|
52
|
+
@resource_class ||= controller_name.classify.constantize
|
53
|
+
end
|
54
|
+
|
55
|
+
def set_resource
|
56
|
+
@resource = resource_class.find(params[:id])
|
57
|
+
end
|
58
|
+
|
59
|
+
# resource_params method is provided by StrongParamsHelper concern
|
60
|
+
# Define permitted parameters in your individual controllers using:
|
61
|
+
# permitted_params :field1, :field2, :field3
|
62
|
+
#
|
63
|
+
# Example for a UsersController:
|
64
|
+
# permitted_params :username, :email_address, :first_name, :last_name
|
65
|
+
|
66
|
+
def pagy_metadata(pagy)
|
67
|
+
{
|
68
|
+
current_page: pagy.page,
|
69
|
+
per_page: pagy.items,
|
70
|
+
total_pages: pagy.pages,
|
71
|
+
total_count: pagy.count,
|
72
|
+
next_page: pagy.next,
|
73
|
+
prev_page: pagy.prev
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# Example controller showing how to use the generated Api::ApiController
|
2
|
+
#
|
3
|
+
# This file demonstrates proper usage of both JSON Facet and Graphiti patterns.
|
4
|
+
# Choose the pattern that matches your serialization engine selection.
|
5
|
+
|
6
|
+
<% if @engine == 'json_facet' -%>
|
7
|
+
# JSON Facet Controller Example
|
8
|
+
# ============================
|
9
|
+
# Generate this controller with:
|
10
|
+
# rails generate controller api/<%= @resource_name.pluralize %> --no-helper --no-assets --no-view-specs
|
11
|
+
#
|
12
|
+
# Then replace the generated content with:
|
13
|
+
|
14
|
+
module Api
|
15
|
+
class <%= @resource_name.pluralize.camelize %>Controller < Api::ApiController
|
16
|
+
# Define which parameters are allowed for <%= @resource_name %> creation/updates
|
17
|
+
# This uses the StrongParamsHelper concern from the base controller
|
18
|
+
permitted_params :name, :description, :status # Replace with your <%= @resource_name.downcase %> attributes
|
19
|
+
|
20
|
+
# Optional: Override facet connections if needed
|
21
|
+
# (otherwise uses defaults from parent: :short for index, :details for show/create/update)
|
22
|
+
connect_facet :summary, actions: [:index]
|
23
|
+
connect_facet :full, actions: [:show, :create, :update]
|
24
|
+
|
25
|
+
# Optional: Add scopes using has_scope (if using Ransack or similar)
|
26
|
+
# has_scope :by_status
|
27
|
+
# has_scope :search, using: :name_or_description_cont
|
28
|
+
|
29
|
+
# All CRUD methods are inherited from Api::ApiController
|
30
|
+
# Override them here if you need custom behavior:
|
31
|
+
#
|
32
|
+
# def index
|
33
|
+
# # Custom index logic
|
34
|
+
# super
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# def create
|
38
|
+
# # Custom creation logic
|
39
|
+
# @resource = resource_class.new(resource_params)
|
40
|
+
# @resource.user = current_user # Example: set current user
|
41
|
+
#
|
42
|
+
# if @resource.save
|
43
|
+
# render json: { data: resource_json(@resource) }, status: :created
|
44
|
+
# else
|
45
|
+
# render json: { errors: @resource.errors }, status: :unprocessable_entity
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
<% elsif @engine == 'graphiti' -%>
|
52
|
+
# Graphiti Controller Example
|
53
|
+
# ===========================
|
54
|
+
# 1. First generate the Graphiti resource:
|
55
|
+
# rails generate graphiti:resource <%= @resource_name %>
|
56
|
+
#
|
57
|
+
# 2. Generate this controller with:
|
58
|
+
# rails generate controller api/<%= @resource_name.pluralize %> --no-helper --no-assets --no-view-specs
|
59
|
+
#
|
60
|
+
# 3. Replace the generated content with:
|
61
|
+
|
62
|
+
module Api
|
63
|
+
class <%= @resource_name.pluralize.camelize %>Controller < Api::ApiController
|
64
|
+
# Specify the Graphiti resource class for this controller
|
65
|
+
self.resource = <%= @resource_name %>Resource
|
66
|
+
|
67
|
+
# Alternative: Override the resource method instead
|
68
|
+
# private
|
69
|
+
#
|
70
|
+
# def resource
|
71
|
+
# <%= @resource_name %>Resource
|
72
|
+
# end
|
73
|
+
|
74
|
+
# All CRUD methods are inherited from Api::ApiController
|
75
|
+
# Override them here if you need custom behavior:
|
76
|
+
#
|
77
|
+
# def create
|
78
|
+
# # Custom creation logic before save
|
79
|
+
# resource_instance = resource.build(params)
|
80
|
+
# resource_instance.data.user = current_user # Example: set current user
|
81
|
+
#
|
82
|
+
# if resource_instance.save
|
83
|
+
# respond_with(resource_instance, status: :created)
|
84
|
+
# else
|
85
|
+
# respond_with(resource_instance.errors, status: :unprocessable_entity)
|
86
|
+
# end
|
87
|
+
# end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Don't forget to update your routes in config/routes.rb:
|
92
|
+
# namespace :api do
|
93
|
+
# resources :<%= @resource_name.pluralize.underscore %>
|
94
|
+
# end
|
95
|
+
|
96
|
+
<% end -%>
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class <%= controller_class_name_with_namespace %>Controller < <%= api_controller_class_name %>
|
4
|
+
# Define which parameters are allowed for <%= class_name %> creation/updates
|
5
|
+
# This uses the StrongParamsHelper concern from the base controller
|
6
|
+
permitted_params <%= permitted_param_names.map { |attribute| ":#{attribute}" }.join(', ') %>
|
7
|
+
|
8
|
+
# Connect facets to actions - customize as needed for your <%= class_name.downcase %> model
|
9
|
+
# Default facets (:short for index, :details for show/create/update) are inherited from parent
|
10
|
+
# Uncomment and customize these lines if you want different facet connections:
|
11
|
+
# connect_facet :summary, actions: [:index]
|
12
|
+
# connect_facet :full, actions: [:show, :create, :update]
|
13
|
+
|
14
|
+
# Optional: Add scopes using has_scope (if using Ransack or similar)
|
15
|
+
# Uncomment these lines if you have corresponding scopes in your <%= class_name %> model:
|
16
|
+
# has_scope :by_status
|
17
|
+
# has_scope :search, using: :name_cont
|
18
|
+
# has_scope :created_after, type: :date
|
19
|
+
|
20
|
+
# All CRUD methods (index, show, create, update, destroy) are inherited from <%= api_controller_class_name %>
|
21
|
+
# The inherited methods provide:
|
22
|
+
# - Automatic pagination via Pagy
|
23
|
+
# - Facet-based JSON rendering
|
24
|
+
# - Strong parameter filtering
|
25
|
+
# - Error handling
|
26
|
+
# - Resource finding and creation
|
27
|
+
|
28
|
+
# Callbacks can be added to run code before or after the default CRUD actions
|
29
|
+
# Use standard Rails before_action and after_action callbacks:
|
30
|
+
#
|
31
|
+
# before_action :your_method_name, only: [:create, :update]
|
32
|
+
# after_action :another_method, only: :destroy
|
33
|
+
#
|
34
|
+
# Example callbacks:
|
35
|
+
#
|
36
|
+
# before_action :set_defaults, only: :create
|
37
|
+
# before_action :check_ownership, only: [:update, :destroy]
|
38
|
+
# after_action :notify_users, only: [:create, :update, :destroy]
|
39
|
+
#
|
40
|
+
# def set_defaults
|
41
|
+
# # Set default values before creation
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# def check_ownership
|
45
|
+
# # Verify resource ownership
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# def notify_users
|
49
|
+
# # Send notifications after changes
|
50
|
+
# end
|
51
|
+
|
52
|
+
# Override any inherited methods here as a last result if you need custom behavior that can't be handled by callbacks:
|
53
|
+
#
|
54
|
+
# def index
|
55
|
+
# # Custom index logic (e.g., additional filtering)
|
56
|
+
# @pagy, resources = pagy(apply_scopes(resource_class.includes(:association)))
|
57
|
+
# render json: {
|
58
|
+
# data: resources.map { |resource| resource_json(resource) },
|
59
|
+
# pagination: pagy_metadata(@pagy)
|
60
|
+
# }
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# def create
|
64
|
+
# # Custom creation logic (e.g., set current user)
|
65
|
+
# @resource = resource_class.new(resource_params)
|
66
|
+
# @resource.user = current_user # Example: set current user
|
67
|
+
#
|
68
|
+
# if @resource.save
|
69
|
+
# render json: { data: resource_json(@resource) }, status: :created
|
70
|
+
# else
|
71
|
+
# render json: { errors: @resource.errors }, status: :unprocessable_entity
|
72
|
+
# end
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# def show
|
76
|
+
# # Custom show logic (e.g., check permissions)
|
77
|
+
# authorize! :read, @resource # Example: authorization check
|
78
|
+
# render json: { data: resource_json(@resource) }
|
79
|
+
# end
|
80
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class <%= class_name %> < ApplicationRecord
|
4
|
+
|
5
|
+
# Validations
|
6
|
+
<% unless options[:skip_tenancy] -%>
|
7
|
+
# Multi-tenancy: Organization is always required
|
8
|
+
validates :organization, presence: true
|
9
|
+
|
10
|
+
<% end -%>
|
11
|
+
<% attributes.reject { |attr| attr.type == :references }.each do |attribute| -%>
|
12
|
+
<% if attribute.required? -%>
|
13
|
+
validates :<%= attribute.name %>, presence: true
|
14
|
+
<% end -%>
|
15
|
+
<% case attribute.type -%>
|
16
|
+
<% when :integer -%>
|
17
|
+
validates :<%= attribute.name %>, numericality: { only_integer: true }, allow_nil: <%= !attribute.required? %>
|
18
|
+
<% when :decimal, :float -%>
|
19
|
+
validates :<%= attribute.name %>, numericality: true, allow_nil: <%= !attribute.required? %>
|
20
|
+
<% end -%>
|
21
|
+
<% if attribute.name.to_s.match?(/\A(email|email_address)\z/i) -%>
|
22
|
+
validates :<%= attribute.name %>, format: { with: URI::MailTo::EMAIL_REGEXP }, allow_nil: <%= !attribute.required? %>
|
23
|
+
<% end -%>
|
24
|
+
<% if attribute.name.to_s.match?(/\A(phone|phone_number)\z/i) -%>
|
25
|
+
validates :<%= attribute.name %>, format: { with: /\A\+?[\d\s-\(\)]+\z/ }, allow_nil: <%= !attribute.required? %>
|
26
|
+
<% end -%>
|
27
|
+
<% if attribute.name.to_s.match?(/\A(url|website|web_address)\z/i) -%>
|
28
|
+
validates :<%= attribute.name %>, format: { with: URI::DEFAULT_PARSER.make_regexp(%w[http https]) }, allow_nil: <%= !attribute.required? %>
|
29
|
+
<% end -%>
|
30
|
+
<% if attribute.name.to_s.match?(/\Alatitude\z/i) -%>
|
31
|
+
validates :<%= attribute.name %>, numericality: { greater_than_or_equal_to: -90, less_than_or_equal_to: 90 }, allow_nil: <%= !attribute.required? %>
|
32
|
+
<% end -%>
|
33
|
+
<% if attribute.name.to_s.match?(/\Alongitude\z/i) -%>
|
34
|
+
validates :<%= attribute.name %>, numericality: { greater_than_or_equal_to: -180, less_than_or_equal_to: 180 }, allow_nil: <%= !attribute.required? %>
|
35
|
+
<% end -%>
|
36
|
+
<% if attribute.name.to_s.match?(/\A(zip|postal)_code\z/i) -%>
|
37
|
+
validates :<%= attribute.name %>, format: { with: /\A\d{5}(-\d{4})?\z/ }, allow_nil: <%= !attribute.required? %>
|
38
|
+
<% end -%>
|
39
|
+
<% if attribute.name.to_s.match?(/\A(username)\z/i) -%>
|
40
|
+
validates :<%= attribute.name %>, format: { with: /\A[a-zA-Z0-9_-]+\z/ }, length: { minimum: 3, maximum: 30 }, allow_nil: <%= !attribute.required? %>
|
41
|
+
<% end -%>
|
42
|
+
<% if attribute.name.to_s.match?(/\A(ip_address)\z/i) -%>
|
43
|
+
validates :<%= attribute.name %>, format: { with: /\A((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\z/ }, allow_nil: <%= !attribute.required? %>
|
44
|
+
<% end -%>
|
45
|
+
<% end -%>
|
46
|
+
|
47
|
+
# Associations
|
48
|
+
<% unless options[:skip_tenancy] -%>
|
49
|
+
# Multi-tenancy: Always include organization (required) and agency (optional)
|
50
|
+
belongs_to :organization
|
51
|
+
belongs_to :agency, optional: true
|
52
|
+
|
53
|
+
<% end -%>
|
54
|
+
<% if defined?(relationship_inferrer) && relationship_inferrer.should_generate_associations? -%>
|
55
|
+
<% relationship_inferrer.belongs_to_relationships.each do |relationship| -%>
|
56
|
+
<% # Skip organization and agency if tenancy is included (they're already added above) -%>
|
57
|
+
<% unless !options[:skip_tenancy] && (relationship.include?('organization') || relationship.include?('agency')) -%>
|
58
|
+
<%= relationship %>
|
59
|
+
<% end -%>
|
60
|
+
<% end -%>
|
61
|
+
<% else -%>
|
62
|
+
<% attributes.select { |attr| attr.type == :references }.each do |attribute| -%>
|
63
|
+
<% # Skip organization and agency if tenancy is included (they're already added above) -%>
|
64
|
+
<% unless !options[:skip_tenancy] && (attribute.name == 'organization' || attribute.name == 'agency') -%>
|
65
|
+
<% if attribute.name == 'organization' -%>
|
66
|
+
belongs_to :organization
|
67
|
+
<% else -%>
|
68
|
+
belongs_to :<%= attribute.name %>, optional: true
|
69
|
+
<% end -%>
|
70
|
+
<% end -%>
|
71
|
+
<% end -%>
|
72
|
+
<% end -%>
|
73
|
+
# Add additional associations here
|
74
|
+
# has_many :comments, dependent: :destroy
|
75
|
+
|
76
|
+
# Scopes (useful for has_scope in controllers)
|
77
|
+
# scope :active, -> { where(active: true) }
|
78
|
+
# scope :by_status, ->(status) { where(status: status) }
|
79
|
+
# scope :created_after, ->(date) { where('created_at > ?', date) }
|
80
|
+
|
81
|
+
# Instance methods
|
82
|
+
# Add your custom methods here
|
83
|
+
# def full_name
|
84
|
+
# "#{first_name} #{last_name}"
|
85
|
+
# end
|
86
|
+
|
87
|
+
# Facets
|
88
|
+
json_facet :short, fields: [:id<%
|
89
|
+
# Include common identifying/summary fields for short facet
|
90
|
+
short_attributes = attributes.select do |attr|
|
91
|
+
# Include common identifying fields
|
92
|
+
identifying_fields = %w[name title label slug username email status state active published visible enabled]
|
93
|
+
# Include simple types but exclude large content and timestamps
|
94
|
+
simple_types = [:string, :integer, :boolean, :decimal, :float]
|
95
|
+
# Exclude large content fields, timestamps, and security-sensitive fields
|
96
|
+
excluded_patterns = /\A(description|content|body|notes|comment|bio|about|summary|created_at|updated_at|deleted_at|password|digest|token|secret|key|salt|encrypted|confirmation|unlock|reset|api_key|access_token|refresh_token)\z/i
|
97
|
+
|
98
|
+
# Always exclude if the field contains security-sensitive words
|
99
|
+
security_patterns = /(password|digest|token|secret|key|salt|encrypted|confirmation|unlock|reset|api_key)/i
|
100
|
+
|
101
|
+
identifying_fields.include?(attr.name.to_s) ||
|
102
|
+
(simple_types.include?(attr.type) &&
|
103
|
+
attr.name.to_s !~ excluded_patterns &&
|
104
|
+
attr.name.to_s !~ security_patterns &&
|
105
|
+
attr.name.to_s.length < 20)
|
106
|
+
end
|
107
|
+
short_attributes.each do |attribute| -%>, :<%= attribute.name %><% end %>]
|
108
|
+
json_facet :details, fields: [:id<%
|
109
|
+
# Include most fields except timestamps, security-sensitive, and internal Rails fields for details facet
|
110
|
+
detail_attributes = attributes.reject do |attr|
|
111
|
+
# Exclude timestamps and internal fields
|
112
|
+
excluded_patterns = /\A(created_at|updated_at|deleted_at|password_digest|reset_password_token|confirmation_token|unlock_token)\z/i
|
113
|
+
# Always exclude security-sensitive fields
|
114
|
+
security_patterns = /(password|digest|token|secret|key|salt|encrypted|confirmation|unlock|reset|api_key|access_token|refresh_token)/i
|
115
|
+
# Exclude binary and large data types
|
116
|
+
excluded_types = [:binary]
|
117
|
+
|
118
|
+
excluded_patterns.match?(attr.name.to_s) ||
|
119
|
+
security_patterns.match?(attr.name.to_s) ||
|
120
|
+
excluded_types.include?(attr.type)
|
121
|
+
end
|
122
|
+
detail_attributes.each do |attribute| -%>, :<%= attribute.name %><% end -%><% attributes.select { |attr| attr.type == :references }.each do |reference| -%>, :<%= reference.name %><% end -%><% unless options[:skip_tenancy] -%>, :organization, :agency<% end -%>]
|
123
|
+
|
124
|
+
# Example of more complex json_facet configurations:
|
125
|
+
#
|
126
|
+
# # Include associated models
|
127
|
+
# json_facet :author, include: [:user]
|
128
|
+
#
|
129
|
+
# # Include multiple associations
|
130
|
+
# json_facet :with_comments, include: [:user, :comments]
|
131
|
+
#
|
132
|
+
# # Include nested associations with custom names
|
133
|
+
# json_facet :detailed, include_as: { user: :author, comments: :replies }
|
134
|
+
#
|
135
|
+
# # Use methods for computed attributes
|
136
|
+
# json_facet :computed, methods: [:full_name, :calculated_total]
|
137
|
+
#
|
138
|
+
# # Combine fields, methods and includes
|
139
|
+
# json_facet :complete, :name, :email, methods: [:status], include: [:posts]
|
140
|
+
|
141
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class <%= controller_class_name_with_namespace %>Controller < <%= api_controller_class_name %>
|
4
|
+
# Specify the Graphiti resource class for this controller
|
5
|
+
self.resource = <%= class_name %>Resource
|
6
|
+
|
7
|
+
# All CRUD methods (index, show, create, update, destroy) are inherited from <%= api_controller_class_name %>
|
8
|
+
# The inherited methods provide:
|
9
|
+
# - JSON:API compliant responses
|
10
|
+
# - Automatic filtering, sorting, and pagination
|
11
|
+
# - Resource-based parameter handling
|
12
|
+
# - Error handling
|
13
|
+
# - Authentication
|
14
|
+
|
15
|
+
# Callbacks can be added to run code before or after the default CRUD actions
|
16
|
+
# Use standard Rails before_action and after_action callbacks:
|
17
|
+
#
|
18
|
+
# before_action :your_method_name, only: [:create, :update]
|
19
|
+
# after_action :another_method, only: :destroy
|
20
|
+
#
|
21
|
+
# Example callbacks:
|
22
|
+
#
|
23
|
+
# before_action :set_defaults, only: :create
|
24
|
+
# before_action :check_ownership, only: [:update, :destroy]
|
25
|
+
# after_action :notify_users, only: [:create, :update, :destroy]
|
26
|
+
#
|
27
|
+
# def set_defaults
|
28
|
+
# # Set default values before creation
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# def check_ownership
|
32
|
+
# # Verify resource ownership
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# def notify_users
|
36
|
+
# # Send notifications after changes
|
37
|
+
# end
|
38
|
+
|
39
|
+
# Override any inherited methods here as a last resort if you need custom behavior that can't be handled by callbacks:
|
40
|
+
#
|
41
|
+
# def index
|
42
|
+
# # Custom index logic (e.g., additional filtering)
|
43
|
+
# resources = resource.all(params)
|
44
|
+
# respond_with(resources)
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# def create
|
48
|
+
# # Custom creation logic (e.g., set current user)
|
49
|
+
# resource_instance = resource.build(params)
|
50
|
+
# resource_instance.data.user = current_user # Example: set current user
|
51
|
+
#
|
52
|
+
# if resource_instance.save
|
53
|
+
# respond_with(resource_instance, status: :created)
|
54
|
+
# else
|
55
|
+
# respond_with(resource_instance.errors, status: :unprocessable_entity)
|
56
|
+
# end
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# def show
|
60
|
+
# # Custom show logic (e.g., check permissions)
|
61
|
+
# resource_instance = resource.find(params)
|
62
|
+
# authorize! :read, resource_instance.data # Example: authorization check
|
63
|
+
# respond_with(resource_instance)
|
64
|
+
# end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
# Alternative: Override the resource method instead of using self.resource
|
69
|
+
# def resource
|
70
|
+
# <%= class_name %>Resource
|
71
|
+
# end
|
72
|
+
|
73
|
+
# Add any private helper methods specific to <%= class_name.downcase %> here:
|
74
|
+
#
|
75
|
+
# def authorize_<%= singular_table_name %>_access
|
76
|
+
# # Custom authorization logic
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# def additional_<%= singular_table_name %>_setup
|
80
|
+
# # Custom setup logic
|
81
|
+
# end
|
82
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class <%= class_name %> < ApplicationRecord
|
4
|
+
# Graphiti uses resource classes for serialization, so no facets needed here
|
5
|
+
# Serialization is handled by the Graphiti resource class in app/resources/
|
6
|
+
|
7
|
+
# Validations
|
8
|
+
# Add your model validations here
|
9
|
+
# validates :name, presence: true
|
10
|
+
# validates :email, presence: true, uniqueness: true
|
11
|
+
|
12
|
+
# Associations
|
13
|
+
# Add your model associations here
|
14
|
+
# belongs_to :user
|
15
|
+
# has_many :comments, dependent: :destroy
|
16
|
+
|
17
|
+
# Scopes (useful for Graphiti resource filtering)
|
18
|
+
# scope :active, -> { where(active: true) }
|
19
|
+
# scope :by_status, ->(status) { where(status: status) }
|
20
|
+
# scope :created_after, ->(date) { where('created_at > ?', date) }
|
21
|
+
|
22
|
+
# Instance methods
|
23
|
+
# Add your custom methods here
|
24
|
+
# def full_name
|
25
|
+
# "#{first_name} #{last_name}"
|
26
|
+
# end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
# Private methods
|
31
|
+
# Add any private helper methods here
|
32
|
+
end
|