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
# 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



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

def [](val)
  data[val]
end

#as_graphql(options = {}) ⇒ Object



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

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

#as_json(options = {}) ⇒ Object



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

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.



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

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)


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

def cache?
  !!@cache
end

#cache_keyObject



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

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

#cache_key_with_versionObject



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

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



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

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



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

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

#debug_requested?Boolean

Returns:

  • (Boolean)


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

def debug_requested?
  query.debug_requested?
end

#destroyObject



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

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



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

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

#errorsObject



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

def errors
  data.errors
end

#etagObject



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

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

#extra_fieldsObject



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

def extra_fields
  query.extra_fields
end

#fieldsObject



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

def fields
  query.fields
end

#future_resolve_dataObject



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

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



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

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

#jsonapi_render_options(opts = {}) ⇒ Object



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

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

#metaObject



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

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

#paginationObject



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

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

#raise_on_missing?Boolean

Returns:

  • (Boolean)


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

def raise_on_missing?
  !!@raise_on_missing
end

#resource_cache_tagObject



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

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

  @resource.try(@cache_tag)
end

#save(action: :create) ⇒ Object



177
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
# File 'lib/graphiti/resource_proxy.rb', line 177

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)


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

def single?
  !!@single
end

#statsObject



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

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



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

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

#to_json(options = {}) ⇒ Object



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

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

#to_jsonapi(options = {}) ⇒ Object



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

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

#to_xml(options = {}) ⇒ Object



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

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).



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

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

#updated_atObject



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

def updated_at
  @scope.updated_at
end