Module: Protobuf::ActiveRecord::Scope::ClassMethods

Defined in:
lib/protobuf/active_record/scope.rb

Instance Method Summary collapse

Instance Method Details

#field_scope(field, options = {}) ⇒ Object

Define fields that should be searchable via ‘search_scope`. Accepts a protobuf field and an already defined scope. If no scope is specified, the scope will be the field name, prefixed with `by_` (e.g. when the field is :guid, the scope will be :by_guid).

Optionally, a parser can be provided that will be called, passing the field value as an argument. This allows custom data parsers to be used so that they don’t have to be handled by scopes. Parsers can be procs, lambdas, or symbolized method names and must accept the value of the field as a parameter.

Examples:

class User < ActiveRecord::Base
  scope :by_guid, lambda { |*guids| where(:guid => guids) }
  scope :custom_guid_scope, lambda { |*guids| where(:guid => guids) }

  # Equivalent to `field_scope :guid, :by_guid`
  field_scope :guid

  # With a custom scope
  field_scope :guid, :scope => :custom_guid_scope

  # With a custom parser that converts the value to an integer
  field_scope :guid, :scope => :custom_guid_scope, :parser => lambda { |value| value.to_i }
end


43
44
45
46
47
48
49
50
51
52
53
# File 'lib/protobuf/active_record/scope.rb', line 43

def field_scope(field, options = {})
  scope_name = if options.include?(:scope)
    options[:scope]
  else
    # When no scope is defined, assume the scope is the field, prefixed with `by_`
    :"by_#{field}"
  end
  searchable_fields[field] = scope_name

  searchable_field_parsers[field] = options[:parser] if options[:parser]
end

#for_upsert(proto) ⇒ Object



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/protobuf/active_record/scope.rb', line 159

def for_upsert(proto)
  valid_upsert = upsert_keys.find do |upsert_key|
    upsert_key.all? do |field|
      proto.respond_to_and_has_and_present?(field)
    end
  end

  fail UpsertNotFoundError unless valid_upsert.present?

  upsert_scope = model_scope
  valid_upsert.each do |field|
    value = proto.__send__(field)
    upsert_scope = upsert_scope.__send__(searchable_fields[field], value)
  end

  upsert_scope.first_or_initialize
end

#model_scopeObject

Get an ARel relation to build off of. If we’re in Rails 4 we need to use ‘all` instead of `scoped`. :noapi:



58
59
60
# File 'lib/protobuf/active_record/scope.rb', line 58

def model_scope
  (::ActiveRecord::VERSION::MAJOR >= 4) ? all : scoped
end

#parse_search_values(proto, field) ⇒ Object

:noapi:



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/protobuf/active_record/scope.rb', line 63

def parse_search_values(proto, field)
  value = proto.__send__(field)

  if searchable_field_parsers[field]
    parser = searchable_field_parsers[field]

    value = if parser.respond_to?(:to_sym)
      __send__(parser.to_sym, value)
    else
      parser.call(value)
    end
  end

  values = [value].flatten
  values.map!(&:to_i) if proto.class.get_field(field, true).enum?
  values
end

#search_scope(proto) ⇒ Object

Builds and returns a Arel relation based on the fields that are present in the given protobuf message using the searchable fields to determine what scopes to use. Provides several aliases for variety.

Examples:

# Search starting with the default scope and searchable fields
User.search_scope(request)
User.by_fields(request)
User.scope_from_proto(request)


92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/protobuf/active_record/scope.rb', line 92

def search_scope(proto)
  search_relation = model_scope

  searchable_fields.each do |field, scope_name|
    next unless proto.respond_to_and_has_and_present?(field)

    search_values = parse_search_values(proto, field)
    search_relation = search_relation.__send__(scope_name, *search_values)
  end

  search_relation
end

#searchable_field_parsersObject

:noapi:



111
112
113
# File 'lib/protobuf/active_record/scope.rb', line 111

def searchable_field_parsers
  @_searchable_field_parsers ||= {}
end

#searchable_fieldsObject

:noapi:



106
107
108
# File 'lib/protobuf/active_record/scope.rb', line 106

def searchable_fields
  @_searchable_fields ||= {}
end

#upsert(proto) ⇒ Object



177
178
179
180
181
182
# File 'lib/protobuf/active_record/scope.rb', line 177

def upsert(proto)
  record = for_upsert(proto)
  record.assign_attributes(proto)
  record.save
  record
end

#upsert!(proto) ⇒ Object



184
185
186
187
188
189
# File 'lib/protobuf/active_record/scope.rb', line 184

def upsert!(proto)
  record = for_upsert(proto)
  record.assign_attributes(proto)
  record.save!
  record
end

#upsert_key(*fields) ⇒ Object

Defines a scope that is eligible for upsert. The scope will be used to initialize a record with first_or_initialize. An upsert scope declariation must specify one or more fields that are required to be present on the request and also must have a field_scope defined.

If multiple upsert scopes are specified, they will be searched in the order they are declared for the first valid scope.

Examples:

class User < ActiveRecord::Base
  scope :by_guid, lambda { |*guids| where(:guid => guids) }
  scope :by_external_guid, lambda { |*external_guids|
    where(:external_guid => exteranl_guids)
  }
  scope :by_client_guid, lambda { |*client_guids|
    joins(:client).where(
      :clients => { :guid => client_guids }
     )
  }

  field_scope :guid
  field_scope :client_guid
  field_scope :external_guid

  upsert_scope :external_guid, :client_guid
  upsert_scope :guid

end


145
146
147
148
149
150
151
152
153
# File 'lib/protobuf/active_record/scope.rb', line 145

def upsert_key(*fields)
  fields = fields.flatten

  fields.each do |field|
    fail UpsertScopeError unless searchable_fields[field].present?
  end

  upsert_keys << fields
end

#upsert_keysObject



155
156
157
# File 'lib/protobuf/active_record/scope.rb', line 155

def upsert_keys
  @_upsert_keys ||= []
end