Class: Serviced::Query
Overview
Base class for query objects: a named home for a complex read.
A query has the same typed, immutable inputs as a Service (see
Typed), but instead of a Result it returns whatever #call
returns, usually an ActiveRecord::Relation. Returning a relation keeps the
result composable: callers can still paginate, add includes, or chain more
scopes. A service then consumes the query and wraps its result in a Result.
class EnrolledPatientsQuery < Serviced::Query
attribute :clinic
attribute :program_external_id, :string
attribute :sort_direction, :string, default: "asc"
validates :sort_direction, inclusion: { in: %w[asc desc] }
def call
clinic.patients
.where("enrolled_in(?)", program_external_id)
.order("enrolled_at #{sort_direction}")
end
end
relation = EnrolledPatientsQuery.call(clinic:, program_external_id: "cardio")
relation.page(params[:page]) # still a relation
Invalid inputs raise InvalidQuery (a query has no failure channel, so bad input is treated as a programming error).
The SQL helpers (#quote, #count_of, ...) require ActiveRecord at runtime.
Class Method Summary collapse
-
.call(attributes = {}) ⇒ Object
Builds the query, validates it, and runs #call.
Instance Method Summary collapse
-
#call ⇒ ActiveRecord::Relation, Object
The query body.
Methods included from Typed
Class Method Details
.call(attributes = {}) ⇒ Object
Builds the query, validates it, and runs #call.
41 42 43 44 45 46 |
# File 'lib/serviced/query.rb', line 41 def call(attributes = {}) query = new(attributes) raise InvalidQuery, query.errors if query.invalid? query.call end |
Instance Method Details
#call ⇒ ActiveRecord::Relation, Object
The query body. Subclasses must implement it and return a relation (to stay composable) or a materialized value.
52 53 54 |
# File 'lib/serviced/query.rb', line 52 def call raise NotImplementedError, "#{self.class} must implement #call" end |