Class: Quo::RelationBackedQuery

Inherits:
Query show all
Defined in:
lib/quo/relation_backed_query.rb,
sig/generated/quo/relation_backed_query.rbs

Overview

Query object backed by ActiveRecord relations

Constant Summary

Constants inherited from Query

Query::COERCE_TO_INT

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Query

#collection?, compose, #copy, define_props_on_class, #inspect, inspect, #is_collection?, #merge, #next_page_query, #offset, #paged?, #previous_page_query, #query, #quo_unwrap_unpaginated_query, #relation?, #sanitised_page_size, #test_relation, #to_s, to_s, #transform, #transform?, #transformer, #unwrap, #unwrap_unpaginated

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, **kwargs, &block) ⇒ void

This method returns an undefined value.

Implements a fluent API for query methods This allows methods to be chained like query.where(...).order(...).limit(...)

Parameters:

  • method_name (Symbol)
  • args (Object)
  • kwargs (Object)


105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/quo/relation_backed_query.rb', line 105

def method_missing(method_name, *args, **kwargs, &block)
  spec = @_specification || RelationBackedQuerySpecification.blank

  # Check if the method exists in RelationBackedQuerySpecification
  if spec.respond_to?(method_name)
    # Call the method on the specification and return a new query with the updated specification
    updated_spec = spec.method(method_name).call(*args, **kwargs, &block)
    return with_specification(updated_spec)
  end

  # Forward to underlying query if method not found in RelationBackedQuerySpecification
  super
end

Class Method Details

.from(relation) ⇒ Quo::WrappedRelationBackedQuery

Parameters:

  • relation (ActiveRecord::Relation)

Returns:



33
34
35
# File 'lib/quo/relation_backed_query.rb', line 33

def self.from(relation)
  Quo::WrappedRelationBackedQuery.new(wrapped: relation)
end

.sanitize_sql_for_conditions(conditions) ⇒ String

Parameters:

  • conditions (Object, nil)

Returns:

  • (String)


39
40
41
# File 'lib/quo/relation_backed_query.rb', line 39

def self.sanitize_sql_for_conditions(conditions)
  ActiveRecord::Base.sanitize_sql_for_conditions(conditions)
end

.sanitize_sql_parameter(value) ⇒ String

Parameters:

  • value (Object)

Returns:

  • (String)


51
52
53
# File 'lib/quo/relation_backed_query.rb', line 51

def self.sanitize_sql_parameter(value)
  sanitize_sql_for_conditions(["?", value])
end

.sanitize_sql_string(string) ⇒ String

Parameters:

  • string (String)

Returns:

  • (String)


45
46
47
# File 'lib/quo/relation_backed_query.rb', line 45

def self.sanitize_sql_string(string)
  sanitize_sql_for_conditions(["'%s'", string])
end

.wrap(query = nil, props: {}, &block) ⇒ void

This method returns an undefined value.

Parameters:

  • query (ActiveRecord::Relation, Quo::RelationBackedQuery) (defaults to: nil)
  • props: (Hash[Symbol, untyped]) (defaults to: {})


12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/quo/relation_backed_query.rb', line 12

def self.wrap(query = nil, props: {}, &block)
  raise ArgumentError, "either a query or a block must be provided" unless query || block

  if query && !(query.is_a?(::ActiveRecord::Relation) || query.is_a?(Quo::RelationBackedQuery))
    raise ArgumentError,
      "Quo::RelationBackedQuery.wrap requires an ActiveRecord::Relation or a Quo::RelationBackedQuery instance; got #{query.class}. " \
      "Use Quo::CollectionBackedQuery.wrap or Quo::CollectionBackedQuery.from for in-memory collections."
  end

  klass = Class.new(self)
  define_props_on_class(klass, props)
  if block
    klass.define_method(:query, &block)
  else
    klass.define_method(:query) { query }
  end
  klass
end

Instance Method Details

#configured_queryActiveRecord::Relation

The configured query is the underlying query with paging

Returns:

  • (ActiveRecord::Relation)


149
150
151
152
153
154
# File 'lib/quo/relation_backed_query.rb', line 149

def configured_query #: ActiveRecord::Relation
  q = underlying_query
  return q unless paged?

  q.offset(offset).limit(sanitised_page_size)
end

#klassuntyped?

Returns:

  • (untyped, nil)


42
# File 'sig/generated/quo/relation_backed_query.rbs', line 42

def klass: () -> (untyped | nil)

#modeluntyped?

Returns:

  • (untyped, nil)


40
# File 'sig/generated/quo/relation_backed_query.rbs', line 40

def model: () -> (untyped | nil)

#respond_to_missing?(method_name, include_private = false) ⇒ Boolean

Parameters:

  • method_name (Symbol)
  • include_private (Boolean) (defaults to: false)

Returns:

  • (Boolean)


122
123
124
125
126
# File 'lib/quo/relation_backed_query.rb', line 122

def respond_to_missing?(method_name, include_private = false)
  # Reuse the memoized blank specification rather than allocating a fresh
  # instance per probe — ActiveRecord's delegation hits respond_to? heavily.
  RelationBackedQuerySpecification.blank.respond_to?(method_name, include_private) || super
end

#resultsQuo::Results

: Quo::Results

Returns:



89
90
91
# File 'lib/quo/relation_backed_query.rb', line 89

def results #: Quo::Results
  Quo::RelationResults.new(self, transformer: transformer)
end

#to_collection(total_count: nil) ⇒ Quo::CollectionBackedQuery

Parameters:

  • total_count: (Integer, nil) (defaults to: nil)

Returns:



85
86
87
# File 'lib/quo/relation_backed_query.rb', line 85

def to_collection(total_count: nil)
  Quo.collection_backed_query_base_class.wrap(results.to_a).new(total_count:)
end

#to_sqlString?

Return the SQL string for this query if its a relation type query object

Returns:

  • (String, nil)


94
95
96
# File 'lib/quo/relation_backed_query.rb', line 94

def to_sql #: String?
  configured_query.to_sql if relation?
end

#underlying_queryActiveRecord::Relation

The underlying query is essentially the configured query with optional extras setup

Returns:

  • (ActiveRecord::Relation)


137
138
139
140
141
142
143
144
145
146
# File 'lib/quo/relation_backed_query.rb', line 137

def underlying_query #: ActiveRecord::Relation
  rel = quo_unwrap_unpaginated_query(validated_query)

  # Apply specification if it exists
  if @_specification
    @_specification.apply_to(rel)
  else
    rel
  end
end

#validated_queryObject

Returns:

  • (Object)


130
131
132
133
134
# File 'lib/quo/relation_backed_query.rb', line 130

def validated_query
  query.tap do |q|
    raise ArgumentError, "#query must return an ActiveRecord Relation or a Quo::Query instance" unless query.nil? || q.is_a?(::ActiveRecord::Relation) || q.is_a?(Quo::Query)
  end
end

#with(options = {}) ⇒ Quo::Query

Apply query options using the specification

Parameters:

  • options (Hash[Symbol, untyped]) (defaults to: {})

Returns:



72
73
74
75
# File 'lib/quo/relation_backed_query.rb', line 72

def with(options = {})
  spec = @_specification || RelationBackedQuerySpecification.blank
  with_specification(spec.merge(options))
end

#with_specification(specification) ⇒ Quo::Query

Apply a query specification to this query

Parameters:

Returns:



65
66
67
# File 'lib/quo/relation_backed_query.rb', line 65

def with_specification(specification)
  copy(_specification: specification)
end