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.



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/graphiti/resource_proxy.rb', line 152

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



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

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

#cache_key_with_versionObject



280
281
282
283
284
285
286
287
288
# File 'lib/graphiti/resource_proxy.rb', line 280

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



85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/graphiti/resource_proxy.rb', line 85

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

#debug_requested?Boolean

Returns:

  • (Boolean)


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

def debug_requested?
  query.debug_requested?
end

#destroyObject



208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/graphiti/resource_proxy.rb', line 208

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



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

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



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

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

#extra_fieldsObject



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

def extra_fields
  query.extra_fields
end

#fieldsObject



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

def fields
  query.fields
end

#future_resolve_dataObject



101
102
103
104
105
106
107
108
# File 'lib/graphiti/resource_proxy.rb', line 101

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



237
238
239
240
241
242
# File 'lib/graphiti/resource_proxy.rb', line 237

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



110
111
112
# File 'lib/graphiti/resource_proxy.rb', line 110

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

#paginationObject



136
137
138
# File 'lib/graphiti/resource_proxy.rb', line 136

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



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

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

  @resource.try(@cache_tag)
end

#save(action: :create) ⇒ Object



172
173
174
175
176
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
# File 'lib/graphiti/resource_proxy.rb', line 172

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



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/graphiti/resource_proxy.rb', line 118

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



229
230
231
232
233
# File 'lib/graphiti/resource_proxy.rb', line 229

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

#updated_atObject



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

def updated_at
  @scope.updated_at
end