Module: Rhino::HasValidation
- Extended by:
- ActiveSupport::Concern
- Included in:
- RhinoModel
- Defined in:
- lib/rhino/concerns/has_validation.rb
Overview
Format validation concern for models.
This concern runs ActiveModel validations on request data before it reaches the database. Field permissions (which fields each role can write) are controlled by the policy, not the model.
Also provides cross-tenant FK validation: any belongs_to FK in the submitted data is checked to ensure the referenced record belongs to the current organization (directly or via FK chain).
Usage:
class Post < ApplicationRecord
include Rhino::HasValidation
# Standard Rails validations for type/format (use allow_nil: true)
validates :title, length: { maximum: 255 }, allow_nil: true
validates :status, inclusion: { in: %w[draft published] }, allow_nil: true
end
Field permissions are defined on the policy:
class PostPolicy < Rhino::ResourcePolicy
def permitted_attributes_for_create(user)
has_role?(user, 'admin') ? ['*'] : ['title', 'content']
end
end
Instance Method Summary collapse
-
#rhino_validate_foreign_keys(data, organization) ⇒ Hash<String, Array<String>>
Public entry point for cross-tenant FK validation.
-
#validate_for_action(params, permitted_fields:, organization: nil) ⇒ Hash
deprecated
Deprecated.
Model-level validation is superseded by request classes (ModelStoreRequest / ModelUpdateRequest, see Rhino::ResourceRequest). It still works unchanged for every model that has no request class for the action and will be removed in 5.0.
Instance Method Details
#rhino_validate_foreign_keys(data, organization) ⇒ Hash<String, Array<String>>
Public entry point for cross-tenant FK validation.
The request-class path (Rhino::ResourceRequest) runs its own validations and therefore never calls validate_for_action, but it still needs the cross-tenant FK check — including the indirect case, where the referenced table reaches the organization through a FK chain rather than an organization_id column. This wraps the existing private implementation so the chain walk and its class-level caches stay in one place.
100 101 102 103 104 105 |
# File 'lib/rhino/concerns/has_validation.rb', line 100 def rhino_validate_foreign_keys(data, organization) return {} unless organization return {} unless data.is_a?(Hash) validate_foreign_keys_for_organization(data, organization) end |
#validate_for_action(params, permitted_fields:, organization: nil) ⇒ Hash
Model-level validation is superseded by request classes (ModelStoreRequest / ModelUpdateRequest, see Rhino::ResourceRequest). It still works unchanged for every model that has no request class for the action and will be removed in 5.0.
Validate data for a given action. Filters to only permitted fields, then runs ActiveModel validations and cross-tenant FK validation.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/rhino/concerns/has_validation.rb', line 45 def validate_for_action(params, permitted_fields:, organization: nil) # Filter to only permitted fields if permitted_fields == ['*'] filtered = params.each_with_object({}) { |(k, v), h| h[k.to_s] = v } else permitted = permitted_fields.map(&:to_s) filtered = params.each_with_object({}) do |(k, v), h| h[k.to_s] = v if permitted.include?(k.to_s) end end # Remove organization_id from validated data — managed by framework filtered.delete("organization_id") if organization # Run ActiveModel validations on a temp instance temp = self.class.new safe_attrs = filtered.select { |k, _| temp.respond_to?("#{k}=") } temp.assign_attributes(safe_attrs) errors = {} unless temp.valid? temp.errors.each do |error| field_name = error.attribute.to_s if filtered.key?(field_name) errors[field_name] ||= [] errors[field_name] << error. end end end # Cross-tenant FK validation if organization fk_errors = validate_foreign_keys_for_organization(filtered, organization) errors.merge!(fk_errors) end if errors.any? { valid: false, errors: errors, validated: {} } else { valid: true, errors: {}, validated: filtered } end end |