Class: Graphiti::ResourceProxy

Inherits:
Object
  • Object
show all
Includes:
Enumerable
Defined in:
lib/graphiti/resource_proxy.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(resource, scope, query, payload: nil, single: false, raise_on_missing: false, assign_action: nil, cache: nil, cache_expires_in: nil, cache_tag: nil) ⇒ ResourceProxy

Returns a new instance of ResourceProxy.



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/graphiti/resource_proxy.rb', line 7

def initialize(
  resource,
  scope,
  query,
  payload: nil,
  single: false,
  raise_on_missing: false,
  assign_action: nil,
  cache: nil,
  cache_expires_in: nil,
  cache_tag: nil
)

  @resource = resource
  @scope = scope
  @query = query
  @payload = payload
  @single = single
  @raise_on_missing = raise_on_missing
  @assign_action = assign_action
  @cache = cache
  @cache_expires_in = cache_expires_in
  @cache_tag = cache_tag
end

Instance Attribute Details

#cacheObject (readonly)

Returns the value of attribute cache.



5
6
7
# File 'lib/graphiti/resource_proxy.rb', line 5

def cache
  @cache
end

#cache_expires_inObject (readonly)

Returns the value of attribute cache_expires_in.



5
6
7
# File 'lib/graphiti/resource_proxy.rb', line 5

def cache_expires_in
  @cache_expires_in
end

#cache_tagObject (readonly)

Returns the value of attribute cache_tag.



5
6
7
# File 'lib/graphiti/resource_proxy.rb', line 5

def cache_tag
  @cache_tag
end

#payloadObject (readonly)

Returns the value of attribute payload.



5
6
7
# File 'lib/graphiti/resource_proxy.rb', line 5

def payload
  @payload
end

#queryObject (readonly)

Returns the value of attribute query.



5
6
7
# File 'lib/graphiti/resource_proxy.rb', line 5

def query
  @query
end

#resourceObject (readonly)

Returns the value of attribute resource.



5
6
7
# File 'lib/graphiti/resource_proxy.rb', line 5

def resource
  @resource
end

#scopeObject (readonly)

Returns the value of attribute scope.



5
6
7
# File 'lib/graphiti/resource_proxy.rb', line 5

def scope
  @scope
end

Instance Method Details

#[](val) ⇒ Object



50
51
52
# File 'lib/graphiti/resource_proxy.rb', line 50

def [](val)
  data[val]
end

#as_graphql(options = {}) ⇒ Object



81
82
83
# File 'lib/graphiti/resource_proxy.rb', line 81

def as_graphql(options = {})
  Renderer.new(self, options).as_graphql
end

#as_json(options = {}) ⇒ Object



69
70
71
# File 'lib/graphiti/resource_proxy.rb', line 69

def as_json(options = {})
  Renderer.new(self, options).as_json
end

#assign_attributes(params) ⇒ Object

Apply request params to the underlying model without saving it, Rails-style: the params are always passed explicitly, in the same request-params shape find/build accept. They are validated, deserialized, and become the payload #save will persist.

Idempotent per params - calling again with params that normalize to the same payload is a no-op, so the attributes callbacks fire once. Different params re-assign onto the same model instance.

Note the attributes callbacks fire here, outside any transaction opened during #save - the persistence hooks wrap only the save phase, receiving this assigned model.



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/graphiti/resource_proxy.rb', line 158

def assign_attributes(params)
  action = @assign_action || :update
  params = normalized_params_copy(params)
  add_endpoint_filter(params, action)
  validator = ::Graphiti::RequestValidator.new(@resource, params, action)
  validator.validate!

  if @assigned_model && same_write_payload?(validator.deserialized_payload)
    return @assigned_model
  end

  @payload = validator.deserialized_payload
  @assigned_model = @data = @resource.assign_with_relationships(
    @payload.meta(action: action),
    @payload.attributes,
    @payload.relationships,
    model_instance: @assigned_model || (data if action == :update)
  )
end

#cache?Boolean Also known as: cached?

Returns:

  • (Boolean)


32
33
34
# File 'lib/graphiti/resource_proxy.rb', line 32

def cache?
  !!@cache
end

#cache_keyObject



276
277
278
279
280
281
282
283
284
# File 'lib/graphiti/resource_proxy.rb', line 276

def cache_key
  ActiveSupport::Cache.expand_cache_key(
    [
      @scope.cache_key,
      @query.cache_key,
      resource_cache_tag
    ].compact_blank
  )
end

#cache_key_with_versionObject



286
287
288
289
290
291
292
293
294
# File 'lib/graphiti/resource_proxy.rb', line 286

def cache_key_with_version
  ActiveSupport::Cache.expand_cache_key(
    [
      @scope.cache_key_with_version,
      @query.cache_key,
      resource_cache_tag
    ].compact_blank
  )
end

#dataObject Also known as: to_a, resolve_data



91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/graphiti/resource_proxy.rb', line 91

def data
  return @data unless @data.nil?
  return assign_attributes(@payload.params) if @assign_action

  @data = begin
    records = @scope.resolve
    raise Graphiti::Errors::RecordNotFound if records.empty? && raise_on_missing?

    records = records[0] if single?
    records
  end
end

#data=(models) ⇒ Object

Records supplied directly, no scope resolution



86
87
88
89
# File 'lib/graphiti/resource_proxy.rb', line 86

def data=(models)
  @data = models
  [@data].flatten.compact.each { |record| @resource.decorate_record(record) }
end

#debug_requested?Boolean

Returns:

  • (Boolean)


258
259
260
# File 'lib/graphiti/resource_proxy.rb', line 258

def debug_requested?
  query.debug_requested?
end

#destroyObject



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/graphiti/resource_proxy.rb', line 214

def destroy
  resolve_data
  transaction_response = @resource.transaction do
     = {method: :destroy}
    model = @resource.destroy(@query.filters[:id], )
    model.instance_variable_set(:@__serializer_klass, @resource.serializer)
    @resource.after_graph_persist(model, )
    validator = ::Graphiti::Util::ValidationResponse.new \
      model, @payload
    validator.validate!
    @resource.before_commit(model, )

    {result: validator}
  end
  @data, success = transaction_response[:result].to_a
  success
end

#each(&blk) ⇒ Object



120
121
122
# File 'lib/graphiti/resource_proxy.rb', line 120

def each(&blk)
  to_a.each(&blk)
end

#errorsObject



46
47
48
# File 'lib/graphiti/resource_proxy.rb', line 46

def errors
  data.errors
end

#etagObject



266
267
268
# File 'lib/graphiti/resource_proxy.rb', line 266

def etag
  "W/#{ActiveSupport::Digest.hexdigest(cache_key_with_version.to_s)}"
end

#extra_fieldsObject



254
255
256
# File 'lib/graphiti/resource_proxy.rb', line 254

def extra_fields
  query.extra_fields
end

#fieldsObject



250
251
252
# File 'lib/graphiti/resource_proxy.rb', line 250

def fields
  query.fields
end

#future_resolve_dataObject



107
108
109
110
111
112
113
114
# File 'lib/graphiti/resource_proxy.rb', line 107

def future_resolve_data
  @scope.future_resolve.then do |records|
    raise Graphiti::Errors::RecordNotFound if records.empty? && raise_on_missing?

    records = records[0] if single?
    @data = records
  end
end

#include_hashObject



243
244
245
246
247
248
# File 'lib/graphiti/resource_proxy.rb', line 243

def include_hash
  @include_hash ||= begin
    base = @payload ? @payload.include_hash : {}
    base.deep_merge(@query.include_hash)
  end
end

#jsonapi_render_options(opts = {}) ⇒ Object



54
55
56
57
58
# File 'lib/graphiti/resource_proxy.rb', line 54

def jsonapi_render_options(opts = {})
  opts[:expose] ||= {}
  opts[:expose][:context] = Graphiti.context[:object]
  opts
end

#metaObject



116
117
118
# File 'lib/graphiti/resource_proxy.rb', line 116

def meta
  @meta ||= data.respond_to?(:meta) ? data.meta : {}
end

#paginationObject



142
143
144
# File 'lib/graphiti/resource_proxy.rb', line 142

def pagination
  @pagination ||= Delegates::Pagination.new(self)
end

#raise_on_missing?Boolean

Returns:

  • (Boolean)


42
43
44
# File 'lib/graphiti/resource_proxy.rb', line 42

def raise_on_missing?
  !!@raise_on_missing
end

#resource_cache_tagObject



270
271
272
273
274
# File 'lib/graphiti/resource_proxy.rb', line 270

def resource_cache_tag
  return unless @cache_tag.present? && @resource.respond_to?(@cache_tag)

  @resource.try(@cache_tag)
end

#save(action: :create) ⇒ Object



178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
# File 'lib/graphiti/resource_proxy.rb', line 178

def save(action: :create)
  # TODO: remove this. Only used for persisting many-to-many with AR
  # (see activerecord adapter)
  original = Graphiti.context[:namespace]
  begin
    Graphiti.context[:namespace] = action
    # An assigned model can only come from #assign_attributes, which
    # validated the payload it stored - re-validating here would run the
    # writable guards (and their guard_model lookups) a redundant time.
    unless @assigned_model
      ::Graphiti::RequestValidator.new(@resource, @payload.params, action).validate!
    end
    validator = persist {
      @resource.persist_with_relationships \
        @payload.meta(action: action),
        @payload.attributes,
        @payload.relationships,
        assigned_model: @assigned_model
    }
  ensure
    Graphiti.context[:namespace] = original
  end
  @data, success = validator.to_a

  if success
    # If the context namespace is `update` or `create`, certain
    # adapters will cause N+1 validation calls, so lets explicitly
    # switch to a lookup context.
    Graphiti.with_context(Graphiti.context[:object], :show) do
      @scope.resolve_sideloads([@data])
    end
  end

  success
end

#single?Boolean

Returns:

  • (Boolean)


38
39
40
# File 'lib/graphiti/resource_proxy.rb', line 38

def single?
  !!@single
end

#statsObject



124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/graphiti/resource_proxy.rb', line 124

def stats
  @stats ||= if @query.hash[:stats]
    scope = @scope.unpaginated_object
    if resource.adapter.can_group?
      if (group = @query.hash[:stats].delete(:group_by))
        scope = resource.adapter.group(scope, group[0])
      end
    end
    payload = Stats::Payload.new @resource,
      @query,
      scope,
      data
    payload.generate
  else
    {}
  end
end

#to_graphql(options = {}) ⇒ Object



77
78
79
# File 'lib/graphiti/resource_proxy.rb', line 77

def to_graphql(options = {})
  Renderer.new(self, options).to_graphql
end

#to_json(options = {}) ⇒ Object



65
66
67
# File 'lib/graphiti/resource_proxy.rb', line 65

def to_json(options = {})
  Renderer.new(self, options).to_json
end

#to_jsonapi(options = {}) ⇒ Object



60
61
62
63
# File 'lib/graphiti/resource_proxy.rb', line 60

def to_jsonapi(options = {})
  options = jsonapi_render_options(options)
  Renderer.new(self, options).to_jsonapi
end

#to_xml(options = {}) ⇒ Object



73
74
75
# File 'lib/graphiti/resource_proxy.rb', line 73

def to_xml(options = {})
  Renderer.new(self, options).to_xml
end

#update(params = nil) ⇒ Object Also known as: update_attributes

Rails-style: pass params to assign and save in one call, or call with no arguments to save a payload assigned earlier (via find or #assign_attributes).



235
236
237
238
239
# File 'lib/graphiti/resource_proxy.rb', line 235

def update(params = nil)
  assign_attributes(params) if params
  resolve_data
  save(action: :update)
end

#updated_atObject



262
263
264
# File 'lib/graphiti/resource_proxy.rb', line 262

def updated_at
  @scope.updated_at
end