Module: Rhino::ComputedAttributeSpec
- Defined in:
- lib/rhino/computed_attribute_spec.rb
Overview
Parses a model's computed-attribute declarations
(+rhino_record_computed_attributes+ / rhino_collection_computed_attributes)
and binds the arguments a client sent for ?attributes[param]=value
(and ?computed_attributes[param]=value) to the declared
parameters.
Declaration forms (both may be mixed in one hash):
{
# Legacy: anything that is not an extended spec is used as-is — a
# callable is called, any other value is serialized literally.
'active_users_count' => ->(scope, _user) { scope.count },
'schema_version' => 3,
# Extended: a hash carrying at least one of params/optional/with.
'revenue' => {
params: %i[from to], optional: [:to],
with: ->(scope, _user, from, to = nil) { ... }
}
}
Unlike named scopes, there is deliberately NO symbol or bare-list shorthand:
'version' => 'v3' and 'tags' => %w[a b] are valid literal
value declarations today, and reinterpreting them as parameter lists would
silently change what a shipped model returns. A declaration is an extended
spec if and only if it is a hash carrying params, optional or with —
those three keys are reserved inside a computed-attribute declaration.
Constant Summary collapse
- SUBJECT =
The noun every computed-attribute argument error message starts with.
"Computed attribute"- SPEC_KEYS =
The keys whose presence marks a declaration hash as an extended spec.
%i[params optional with].freeze
Class Method Summary collapse
-
.bind(name, spec, raw) ⇒ Object
Bind the raw value a client sent for one attribute to positional arguments, in the order the model declared them.
-
.names(declared) ⇒ Object
The declared attribute names only.
-
.normalize(declared) ⇒ Object
Normalize a raw declaration hash into name => { params:, optional:, target: }.
- .normalize_entry(value) ⇒ Object
-
.parameterised?(spec) ⇒ Boolean
Whether an entry is parameterised, and therefore must be called strictly with the bound arguments rather than through the tolerant arity branch a parameterless declaration keeps.
-
.requires_arguments?(spec) ⇒ Boolean
Whether the attribute declares at least one parameter that the client MUST supply.
-
.spec?(value) ⇒ Boolean
Whether a declared value is an extended spec rather than a legacy callable/literal declaration.
Class Method Details
.bind(name, spec, raw) ⇒ Object
Bind the raw value a client sent for one attribute to positional arguments, in the order the model declared them.
Callers MUST have already checked that the attribute is declared and policy-visible: the messages raised here name the attribute.
96 97 98 99 100 101 102 103 104 |
# File 'lib/rhino/computed_attribute_spec.rb', line 96 def bind(name, spec, raw) Rhino::ArgumentBinder.bind( subject: SUBJECT, name: name, spec: spec, raw: raw, error_class: Rhino::InvalidComputedAttributeArgumentsError ) end |
.names(declared) ⇒ Object
The declared attribute names only.
72 73 74 |
# File 'lib/rhino/computed_attribute_spec.rb', line 72 def names(declared) normalize(declared).keys end |
.normalize(declared) ⇒ Object
Normalize a raw declaration hash into name => { params:, optional:, target: }.
target is the callable (or the literal value) that produces the
attribute; for a legacy declaration it is the declared value itself.
45 46 47 48 49 50 51 |
# File 'lib/rhino/computed_attribute_spec.rb', line 45 def normalize(declared) return {} unless declared.is_a?(Hash) declared.each_with_object({}) do |(name, value), out| out[name.to_s] = normalize_entry(value) end end |
.normalize_entry(value) ⇒ Object
53 54 55 56 57 58 59 60 61 |
# File 'lib/rhino/computed_attribute_spec.rb', line 53 def normalize_entry(value) return { params: [], optional: [], target: value } unless spec?(value) spec = value.symbolize_keys Rhino::ArgumentBinder .normalize_params(spec[:params], spec[:optional]) .merge(target: spec[:with]) end |
.parameterised?(spec) ⇒ Boolean
Whether an entry is parameterised, and therefore must be called strictly with the bound arguments rather than through the tolerant arity branch a parameterless declaration keeps.
87 88 89 |
# File 'lib/rhino/computed_attribute_spec.rb', line 87 def parameterised?(spec) Array(spec[:params]).any? end |
.requires_arguments?(spec) ⇒ Boolean
Whether the attribute declares at least one parameter that the client MUST supply. Such attributes are skipped — never 403'd — when no selection was made (a bare GET /computed) and when a direct serialization call passes no arguments for them.
80 81 82 |
# File 'lib/rhino/computed_attribute_spec.rb', line 80 def requires_arguments?(spec) (Array(spec[:params]).map(&:to_s) - Array(spec[:optional]).map(&:to_s)).any? end |
.spec?(value) ⇒ Boolean
Whether a declared value is an extended spec rather than a legacy callable/literal declaration.
65 66 67 68 69 |
# File 'lib/rhino/computed_attribute_spec.rb', line 65 def spec?(value) return false unless value.is_a?(Hash) SPEC_KEYS.any? { |key| value.key?(key) || value.key?(key.to_s) } end |