Module: ActiveRpc::Rpc::Concerns::Scopable

Extended by:
ActiveSupport::Concern
Included in:
QueryBuilder
Defined in:
lib/active_rpc/rpc/concerns/scopable.rb

Overview

The Scopable concern provides methods for applying named scopes to ActiveRecord queries in gRPC controllers.

Examples:

def list_users
  process_request do
    base_query = User.all
    query = apply_scopes(base_query, request.message)
    # ...
  end
end

Instance Method Summary collapse

Instance Method Details

#apply_scopes(query, params) ⇒ Object

Apply scopes to a query



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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
# File 'lib/active_rpc/rpc/concerns/scopable.rb', line 19

def apply_scopes(query, params)
  return query unless params.respond_to?(:scope) && params.scope.present?

  # Handle both new JSON format and old individual parameters for backward compatibility
  scope_hash = params.scope.to_h
  scope_params = if scope_hash.key?('args')
                   # New format: JSON-serialized scope arguments
                   begin
                     JSON.parse(scope_hash['args'])
                   rescue JSON::ParserError => e
                     Rails.logger.error("[Scopable] Failed to parse scope args: #{e.message}")
                     {}
                   end
  else
                   # Old format: individual string parameters (backward compatibility)
                   scope_hash
  end

  # Get available scopes
  available_scopes = get_available_scopes(query.model)

  # Apply each scope
  scope_params.each do |scope_name, scope_value|
    # Skip if the scope is not available and we have defined scopes
    scope_available = available_scopes.empty? ||
                     available_scopes.key?(scope_name.to_sym) ||
                     available_scopes.key?(scope_name.to_s)

    unless scope_available
      Rails.logger.debug("[Scopable] Skipping scope #{scope_name} - not in available_scopes")
      next
    end

    unless query.respond_to?(scope_name)
      Rails.logger.debug("[Scopable] Skipping scope #{scope_name} - query doesn't respond to it")
      next
    end

    begin
      # Handle regular scopes
      method = query.method(scope_name)
      if method.arity.abs <= 1
        Rails.logger.debug("[Scopable] Applying scope: #{scope_name}(#{scope_value.class})")
        query = scope_value.present? ? query.public_send(scope_name, scope_value) : query.public_send(scope_name)
      else
        Rails.logger.debug("[Scopable] Skipping scope #{scope_name} - arity too high: #{method.arity}")
      end
    rescue => e
      Rails.logger.error("[Scopable] Error applying scope '#{scope_name}': #{e.message}")
      # Continue with other scopes even if one fails
    end
  end

  query
end