graphiti 1.2.21 → 1.2.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 36b58f545ff99d459f7739072dd54045a8eee7f06990f7cee8450c1311788feb
4
- data.tar.gz: bab55c98392e96c793b51bac6423c63b171782f36fddd3ef57531e4d3f280f9b
3
+ metadata.gz: 4a1198667bd97f2aed53ee9652bf58e778c9423541e85c40122fcca1e504af6a
4
+ data.tar.gz: 58b4a16c2344147408b8f6bae93ba0f7268f45db8e1fdb0782f2eb452257f6af
5
5
  SHA512:
6
- metadata.gz: dc9a8a3dce388ba6cec8100cbbd16c0c04e7aa0559acd2d3b028d00ce869537fce70c395e524c33351b4066142a48cecd3ee76bbf6c8d8b25e1fb38e997b0556
7
- data.tar.gz: fbf3374a770546d24e62ebbc96091264f823ddeaaf99329eb0f51dfcfb918df999dcf6025aeb87868754671c079058e9a3e664db15c77cf6e00f1958a1b80ddd
6
+ metadata.gz: 4000d1e9f172c3cb1b973d8545dc02d6771951ed16efac8e21ce948de2a7ebb457115128d375dfda7f0d05bc334ab2851f6d69b0de8c23530bae8c47702d12f4
7
+ data.tar.gz: a10bfea53ddbd2af821d9467204fd4550a288fe6a5f57b02e63915e7d14230b00924ec20a0f3e350c1282bf9aeaf75a043ce5309ca83bf892c900f5add59f776
@@ -19,7 +19,13 @@ env:
19
19
  - COMMAND="standardrb --no-fix --format progress"
20
20
  - COMMAND=rspec
21
21
  - COMMAND=rspec APPRAISAL_INITIALIZED=true
22
- matrix:
22
+ jobs:
23
+ allow_failures:
24
+ - rvm: ruby-head
25
+ include:
26
+ - env: COMMAND=rspec
27
+ gemfile: Gemfile
28
+ rvm: ruby-head
23
29
  exclude:
24
30
  # Don't run the appraisal version of the specs for the base gemfile
25
31
  - env: COMMAND=rspec APPRAISAL_INITIALIZED=true
@@ -8,6 +8,9 @@ Features:
8
8
  - [157](https://github.com/graphiti-api/graphiti/pull/157) Using attribute option schema: false.
9
9
  This option is default true and is not effected by only and except options. (@zeisler)
10
10
 
11
+ Fixes:
12
+ - [282] Support model names including "Resource"
13
+
11
14
  ## 1.1.0
12
15
 
13
16
  Features:
data/Gemfile CHANGED
@@ -3,8 +3,6 @@ source "https://rubygems.org"
3
3
  # Specify your gem's dependencies in graphiti.gemspec
4
4
  gemspec
5
5
 
6
- gem "standard", "0.4.7"
7
-
8
6
  group :test do
9
7
  gem "pry"
10
8
  gem "pry-byebug", platform: [:mri]
@@ -2,7 +2,7 @@
2
2
 
3
3
  source "https://rubygems.org"
4
4
 
5
- gem "rails", "~> 4.1"
5
+ gem "rails", "~> 4.2"
6
6
  gem "rspec-rails"
7
7
  gem "sqlite3", "~> 1.3.6"
8
8
  gem "database_cleaner"
@@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
16
16
  spec.bindir = "exe"
17
17
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
18
  spec.require_paths = ["lib"]
19
- spec.required_ruby_version = "~> 2.3"
19
+ spec.required_ruby_version = [">= 2.3", "< 3.1"]
20
20
 
21
21
  spec.add_dependency "jsonapi-serializable", "~> 0.3.0"
22
22
  spec.add_dependency "jsonapi-renderer", "~> 0.2", ">= 0.2.2"
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency "kaminari", "~> 0.17"
30
30
  spec.add_development_dependency "bundler"
31
31
  spec.add_development_dependency "rake", "~> 10.0"
32
+ spec.add_development_dependency "standard", "0.4.7"
32
33
  spec.add_development_dependency "activemodel", ">= 4.1"
33
34
  spec.add_development_dependency "graphiti_spec_helpers", "1.0.beta.4"
34
35
  end
@@ -1,6 +1,9 @@
1
1
  module Graphiti
2
2
  module Adapters
3
3
  class Abstract
4
+ require "graphiti/adapters/persistence/associations.rb"
5
+ include Graphiti::Adapters::Persistence::Associations
6
+
4
7
  attr_reader :resource
5
8
 
6
9
  def initialize(resource)
@@ -400,6 +403,13 @@ module Graphiti
400
403
  raise "you must override #destroy in an adapter subclass"
401
404
  end
402
405
 
406
+ def close
407
+ end
408
+
409
+ def persistence_attributes(persistance, attributes)
410
+ attributes
411
+ end
412
+
403
413
  def self.numerical_operators
404
414
  [:eq, :not_eq, :gt, :gte, :lt, :lte].freeze
405
415
  end
@@ -1,7 +1,7 @@
1
1
  module Graphiti
2
2
  module Adapters
3
3
  class ActiveRecord < ::Graphiti::Adapters::Abstract
4
- require "graphiti/adapters/active_record/inferrence"
4
+ require "graphiti/adapters/active_record/inference"
5
5
  require "graphiti/adapters/active_record/has_many_sideload"
6
6
  require "graphiti/adapters/active_record/belongs_to_sideload"
7
7
  require "graphiti/adapters/active_record/has_one_sideload"
@@ -191,7 +191,7 @@ module Graphiti
191
191
  # (see Adapters::Abstract#count)
192
192
  def count(scope, attr)
193
193
  if attr.to_sym == :total
194
- scope.distinct.count
194
+ scope.distinct.count(:all)
195
195
  else
196
196
  scope.distinct.count(attr)
197
197
  end
@@ -297,6 +297,10 @@ module Graphiti
297
297
  model_instance
298
298
  end
299
299
 
300
+ def close
301
+ ActiveRecord::Base.clear_active_connections!
302
+ end
303
+
300
304
  private
301
305
 
302
306
  def column_for(scope, name)
@@ -1,5 +1,5 @@
1
1
  class Graphiti::Adapters::ActiveRecord::BelongsToSideload < Graphiti::Sideload::BelongsTo
2
- include Graphiti::Adapters::ActiveRecord::Inferrence
2
+ include Graphiti::Adapters::ActiveRecord::Inference
3
3
 
4
4
  def default_base_scope
5
5
  resource_class.model.all
@@ -1,5 +1,5 @@
1
1
  class Graphiti::Adapters::ActiveRecord::HasManySideload < Graphiti::Sideload::HasMany
2
- include Graphiti::Adapters::ActiveRecord::Inferrence
2
+ include Graphiti::Adapters::ActiveRecord::Inference
3
3
 
4
4
  def default_base_scope
5
5
  resource_class.model.all
@@ -1,5 +1,5 @@
1
1
  class Graphiti::Adapters::ActiveRecord::HasOneSideload < Graphiti::Sideload::HasOne
2
- include Graphiti::Adapters::ActiveRecord::Inferrence
2
+ include Graphiti::Adapters::ActiveRecord::Inference
3
3
 
4
4
  def default_base_scope
5
5
  resource_class.model.all
@@ -1,6 +1,6 @@
1
- module Graphiti::Adapters::ActiveRecord::Inferrence
1
+ module Graphiti::Adapters::ActiveRecord::Inference
2
2
  # If going AR to AR, use AR introspection
3
- # If going AR to PORO, fall back to normal inferrence
3
+ # If going AR to PORO, fall back to normal inference
4
4
  def infer_foreign_key
5
5
  parent_model = parent_resource_class.model
6
6
  reflection = parent_model.reflections[association_name.to_s]
@@ -8,6 +8,18 @@ class Graphiti::Adapters::ActiveRecord::ManyToManySideload < Graphiti::Sideload:
8
8
  foreign_key.keys.first
9
9
  end
10
10
 
11
+ def inverse_filter
12
+ return @inverse_filter if @inverse_filter
13
+
14
+ inferred_name = infer_inverse_association
15
+
16
+ if inferred_name
17
+ "#{inferred_name.to_s.singularize}_id"
18
+ else
19
+ super
20
+ end
21
+ end
22
+
11
23
  def belongs_to_many_filter(scope, value)
12
24
  if polymorphic?
13
25
  clauses = value.group_by { |v| v["type"] }.map { |group|
@@ -36,8 +48,7 @@ class Graphiti::Adapters::ActiveRecord::ManyToManySideload < Graphiti::Sideload:
36
48
 
37
49
  def filter_for(scope, value, type = nil)
38
50
  scope
39
- .preload(through_relationship_name)
40
- .joins(through_relationship_name)
51
+ .includes(through_relationship_name)
41
52
  .where(belongs_to_many_clause(value, type))
42
53
  end
43
54
 
@@ -76,4 +87,11 @@ class Graphiti::Adapters::ActiveRecord::ManyToManySideload < Graphiti::Sideload:
76
87
  value = through_reflection.foreign_key.to_sym
77
88
  {key => value}
78
89
  end
90
+
91
+ def infer_inverse_association
92
+ through_class = through_reflection.klass
93
+
94
+ foreign_reflection = through_class.reflections[name.to_s.singularize]
95
+ foreign_reflection && foreign_reflection.options[:inverse_of]
96
+ end
79
97
  end
@@ -0,0 +1,72 @@
1
+ module Graphiti
2
+ module Adapters
3
+ module Persistence
4
+ module Associations
5
+ def process_belongs_to(persistence, attributes)
6
+ parents = [].tap do |processed|
7
+ persistence.iterate(only: [:polymorphic_belongs_to, :belongs_to]) do |x|
8
+ begin
9
+ id = x.dig(:attributes, :id)
10
+ x[:object] = x[:resource]
11
+ .persist_with_relationships(x[:meta], x[:attributes], x[:relationships])
12
+ processed << x
13
+ rescue Graphiti::Errors::RecordNotFound
14
+ path = "relationships/#{x.dig(:meta, :jsonapi_type)}"
15
+ raise Graphiti::Errors::RecordNotFound.new(x[:sideload].name, id, path)
16
+ end
17
+ end
18
+ end
19
+
20
+ update_foreign_key_for_parents(parents, attributes)
21
+ parents
22
+ end
23
+
24
+ def process_has_many(persistence, caller_model)
25
+ [].tap do |processed|
26
+ persistence.iterate(except: [:polymorphic_belongs_to, :belongs_to]) do |x|
27
+ update_foreign_key(caller_model, x[:attributes], x)
28
+
29
+ x[:object] = x[:resource]
30
+ .persist_with_relationships(x[:meta], x[:attributes], x[:relationships], caller_model, x[:foreign_key])
31
+
32
+ processed << x
33
+ end
34
+ end
35
+ end
36
+
37
+ def update_foreign_key_for_parents(parents, attributes)
38
+ parents.each do |x|
39
+ update_foreign_key(x[:object], attributes, x)
40
+ end
41
+ end
42
+
43
+ # The child's attributes should be modified to nil-out the
44
+ # foreign_key when the parent is being destroyed or disassociated
45
+ #
46
+ # This is not the case for HABTM, whose "foreign key" is a join table
47
+ def update_foreign_key(parent_object, attrs, x)
48
+ return if x[:sideload].type == :many_to_many
49
+
50
+ if [:destroy, :disassociate].include?(x[:meta][:method])
51
+ if x[:sideload].polymorphic_has_one? || x[:sideload].polymorphic_has_many?
52
+ attrs[:"#{x[:sideload].polymorphic_as}_type"] = nil
53
+ end
54
+ attrs[x[:foreign_key]] = nil
55
+ update_foreign_type(attrs, x, null: true) if x[:is_polymorphic]
56
+ else
57
+ if x[:sideload].polymorphic_has_one? || x[:sideload].polymorphic_has_many?
58
+ attrs[:"#{x[:sideload].polymorphic_as}_type"] = parent_object.class.name
59
+ end
60
+ attrs[x[:foreign_key]] = parent_object.send(x[:primary_key])
61
+ update_foreign_type(attrs, x) if x[:is_polymorphic]
62
+ end
63
+ end
64
+
65
+ def update_foreign_type(attrs, x, null: false)
66
+ grouping_field = x[:sideload].parent.grouper.field_name
67
+ attrs[grouping_field] = null ? nil : x[:sideload].group_name
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -6,7 +6,7 @@ module Graphiti
6
6
  end
7
7
 
8
8
  def links?
9
- @proxy.query.pagination_links?
9
+ @proxy.query.pagination_links? && @proxy.data.present?
10
10
  end
11
11
 
12
12
  def links
@@ -53,7 +53,7 @@ module Graphiti
53
53
  def item_count
54
54
  begin
55
55
  return @item_count if @item_count
56
- @item_count = @proxy.resource.stat(:total, :count).call(@proxy.scope.unpaginated_object, :total)
56
+ @item_count = item_count_from_proxy || item_count_from_stats
57
57
  unless @item_count.is_a?(Numeric)
58
58
  raise TypeError, "#{@proxy.resource}.stat(:total, :count) returned an invalid value #{@item_count}"
59
59
  end
@@ -68,6 +68,15 @@ module Graphiti
68
68
  @item_count
69
69
  end
70
70
 
71
+ def item_count_from_proxy
72
+ @proxy.stats.dig(:total, :count)
73
+ end
74
+
75
+ def item_count_from_stats
76
+ stats = Stats::Payload.new(@proxy.resource, @proxy.query, @proxy.scope.unpaginated_object, @proxy.data)
77
+ stats.calculate_stat(:total, @proxy.resource.stat(:total, :count))
78
+ end
79
+
71
80
  def current_page
72
81
  @current_page ||= (page_param[:number] || 1).to_i
73
82
  end
@@ -366,6 +366,20 @@ module Graphiti
366
366
  end
367
367
  end
368
368
 
369
+ class UndefinedIDLookup < Base
370
+ def initialize(resource_class)
371
+ @resource_class = resource_class
372
+ end
373
+
374
+ def message
375
+ <<~MSG
376
+ Tried to resolve #{@resource_class} with an :id filter, but the filter was nil.
377
+ This can result in unscoping a query, which can cause incorrect values to be
378
+ returned which may or may not bypass standard access controls.
379
+ MSG
380
+ end
381
+ end
382
+
369
383
  class UnknownAttribute < AttributeError
370
384
  def message
371
385
  "#{super}, but could not find an attribute with that name."
@@ -45,10 +45,10 @@ module Graphiti
45
45
  {}.tap do |hash|
46
46
  hash[:data] = if serializers.is_a?(Array)
47
47
  serializers.map do |s|
48
- s.to_hash(opts)
48
+ s.to_hash(**opts)
49
49
  end
50
50
  else
51
- serializers.to_hash(opts)
51
+ serializers.to_hash(**opts)
52
52
  end
53
53
  end
54
54
  end
@@ -6,18 +6,18 @@ module Graphiti
6
6
  :deserialized_payload,
7
7
  to: :@validator
8
8
 
9
- def initialize(root_resource, raw_params)
10
- @validator = ValidatorFactory.create(root_resource, raw_params)
9
+ def initialize(root_resource, raw_params, action)
10
+ @validator = ValidatorFactory.create(root_resource, raw_params, action)
11
11
  end
12
12
 
13
13
  class ValidatorFactory
14
- def self.create(root_resource, raw_params)
15
- case raw_params["action"]
16
- when "update" then
14
+ def self.create(root_resource, raw_params, action)
15
+ case action
16
+ when :update then
17
17
  RequestValidators::UpdateValidator
18
18
  else
19
19
  RequestValidators::Validator
20
- end.new(root_resource, raw_params)
20
+ end.new(root_resource, raw_params, action)
21
21
  end
22
22
  end
23
23
  end
@@ -3,10 +3,11 @@ module Graphiti
3
3
  class Validator
4
4
  attr_reader :errors
5
5
 
6
- def initialize(root_resource, raw_params)
6
+ def initialize(root_resource, raw_params, action)
7
7
  @root_resource = root_resource
8
8
  @raw_params = raw_params
9
9
  @errors = Graphiti::Util::SimpleErrors.new(raw_params)
10
+ @action = action
10
11
  end
11
12
 
12
13
  def validate
@@ -50,7 +51,7 @@ module Graphiti
50
51
  relationships: relationships
51
52
  }
52
53
 
53
- Graphiti::Util::RelationshipPayload.iterate(opts) do |x|
54
+ Graphiti::Util::RelationshipPayload.iterate(**opts) do |x|
54
55
  sideload_def = x[:sideload]
55
56
 
56
57
  unless sideload_def.writable?
@@ -68,6 +69,11 @@ module Graphiti
68
69
 
69
70
  def typecast_attributes(resource, attributes, payload_path)
70
71
  attributes.each_pair do |key, value|
72
+ # Only validate id if create action, otherwise it's only used for lookup
73
+ next if @action != :create &&
74
+ key == :id &&
75
+ resource.class.config[:attributes][:id][:writable] == false
76
+
71
77
  begin
72
78
  attributes[key] = resource.typecast(key, value, :writable)
73
79
  rescue Graphiti::Errors::UnknownAttribute
@@ -129,7 +129,7 @@ module Graphiti
129
129
  def get_attr(name, flag, opts = {})
130
130
  defaults = {request: false}
131
131
  opts = defaults.merge(opts)
132
- new.get_attr(name, flag, opts)
132
+ new.get_attr(name, flag, **opts)
133
133
  end
134
134
 
135
135
  def abstract_class?
@@ -149,14 +149,14 @@ module Graphiti
149
149
 
150
150
  def infer_type
151
151
  if name.present?
152
- name.demodulize.gsub("Resource", "").underscore.pluralize.to_sym
152
+ name.demodulize.sub(/.*\KResource/, "").underscore.pluralize.to_sym
153
153
  else
154
154
  :undefined_jsonapi_type
155
155
  end
156
156
  end
157
157
 
158
158
  def infer_model
159
- name&.gsub("Resource", "")&.safe_constantize
159
+ name&.sub(/.*\KResource/, "")&.safe_constantize
160
160
  end
161
161
 
162
162
  # @api private
@@ -247,7 +247,7 @@ module Graphiti
247
247
 
248
248
  def get_attr!(name, flag, options = {})
249
249
  options[:raise_error] = true
250
- get_attr(name, flag, options)
250
+ get_attr(name, flag, **options)
251
251
  end
252
252
 
253
253
  def get_attr(name, flag, request: false, raise_error: false)
@@ -5,6 +5,7 @@ module Graphiti
5
5
 
6
6
  class_methods do
7
7
  def filter(name, *args, &blk)
8
+ name = name.to_sym
8
9
  opts = args.extract_options!
9
10
  type_override = args[0]
10
11
 
@@ -23,6 +23,9 @@ module Graphiti
23
23
 
24
24
  # @api private
25
25
  def _find(params = {}, base_scope = nil)
26
+ guard_nil_id!(params[:data])
27
+ guard_nil_id!(params)
28
+
26
29
  id = params[:data].try(:[], :id) || params.delete(:id)
27
30
  params[:filter] ||= {}
28
31
  params[:filter][:id] = id if id
@@ -52,6 +55,13 @@ module Graphiti
52
55
  end
53
56
  end
54
57
  end
58
+
59
+ def guard_nil_id!(params)
60
+ return unless params
61
+ if params.key?(:id) && params[:id].nil?
62
+ raise Errors::UndefinedIDLookup.new(self)
63
+ end
64
+ end
55
65
  end
56
66
  end
57
67
  end
@@ -93,7 +93,7 @@ module Graphiti
93
93
  original = Graphiti.context[:namespace]
94
94
  begin
95
95
  Graphiti.context[:namespace] = action
96
- ::Graphiti::RequestValidator.new(@resource, @payload.params).validate!
96
+ ::Graphiti::RequestValidator.new(@resource, @payload.params, action).validate!
97
97
  validator = persist {
98
98
  @resource.persist_with_relationships \
99
99
  @payload.meta(action: action),
@@ -118,6 +118,7 @@ module Graphiti
118
118
  end
119
119
 
120
120
  def destroy
121
+ data
121
122
  transaction_response = @resource.transaction do
122
123
  metadata = {method: :destroy}
123
124
  model = @resource.destroy(@query.filters[:id], metadata)
@@ -135,6 +136,7 @@ module Graphiti
135
136
  end
136
137
 
137
138
  def update_attributes
139
+ data
138
140
  save(action: :update)
139
141
  end
140
142
 
@@ -9,8 +9,7 @@ module Graphiti
9
9
  @query = query
10
10
  @action = action
11
11
 
12
- validator = RequestValidator.new(jsonapi_resource, params)
13
-
12
+ validator = RequestValidator.new(jsonapi_resource, params, action)
14
13
  validator.validate!
15
14
 
16
15
  @deserialized_payload = validator.deserialized_payload
@@ -45,9 +45,7 @@ module Graphiti
45
45
  resolve_sideload = -> {
46
46
  Graphiti.context = graphiti_context
47
47
  sideload.resolve(results, q, parent_resource)
48
- if concurrent && defined?(ActiveRecord)
49
- ActiveRecord::Base.clear_active_connections!
50
- end
48
+ @resource.adapter.close if concurrent
51
49
  }
52
50
  if concurrent
53
51
  promises << Concurrent::Promise.execute(&resolve_sideload)
@@ -22,8 +22,8 @@ module Graphiti
22
22
  end
23
23
  end
24
24
 
25
- def as_jsonapi(*)
26
- super.tap do |hash|
25
+ def as_jsonapi(kwargs = {})
26
+ super(**kwargs).tap do |hash|
27
27
  strip_relationships!(hash) if strip_relationships?
28
28
  add_links!(hash)
29
29
  end
@@ -26,8 +26,8 @@ module Graphiti
26
26
  @foreign_key = opts[:foreign_key]
27
27
  @type = opts[:type]
28
28
  @base_scope = opts[:base_scope]
29
- @readable = evaluate_flag(opts[:readable])
30
- @writable = evaluate_flag(opts[:writable])
29
+ @readable = opts[:readable]
30
+ @writable = opts[:writable]
31
31
  @as = opts[:as]
32
32
  @link = opts[:link]
33
33
  @single = opts[:single]
@@ -426,6 +426,7 @@ module Graphiti
426
426
  Util::Class.namespace_for(klass)
427
427
  end
428
428
 
429
+ # TODO: call this at runtime to support procs
429
430
  def evaluate_flag(flag)
430
431
  return false if flag.blank?
431
432
 
@@ -31,16 +31,19 @@ module Graphiti
31
31
  stats[name] = {}
32
32
 
33
33
  each_calculation(name, calculation) do |calc, function|
34
- args = [@scope, name]
35
- args << @resource.context if function.arity >= 3
36
- args << @data if function.arity == 4
37
-
38
- stats[name][calc] = function.call(*args)
34
+ stats[name][calc] = calculate_stat(name, function)
39
35
  end
40
36
  end
41
37
  end
42
38
  end
43
39
 
40
+ def calculate_stat(name, function)
41
+ args = [@scope, name]
42
+ args << @resource.context if function.arity >= 3
43
+ args << @data if function.arity == 4
44
+ function.call(*args)
45
+ end
46
+
44
47
  private
45
48
 
46
49
  def each_calculation(name, calculations)
@@ -44,7 +44,7 @@ module Graphiti
44
44
  Graphiti::Errors::UnknownAttribute
45
45
 
46
46
  if raise_error?(opts[:exists])
47
- raise error_class.new(resource, name, flag, opts)
47
+ raise error_class.new(resource, name, flag, **opts)
48
48
  else
49
49
  false
50
50
  end
@@ -14,6 +14,7 @@ class Graphiti::Util::Persistence
14
14
  @relationships = relationships
15
15
  @caller_model = caller_model
16
16
  @foreign_key = foreign_key
17
+ @adapter = @resource.adapter
17
18
 
18
19
  # Find the correct child resource for a given jsonapi type
19
20
  if (meta_type = @meta[:type].try(:to_sym))
@@ -43,18 +44,16 @@ class Graphiti::Util::Persistence
43
44
  #
44
45
  # @return a model instance
45
46
  def run
46
- parents = process_belongs_to(@relationships)
47
- update_foreign_key_for_parents(parents)
47
+ attributes = @adapter.persistence_attributes(self, @attributes)
48
48
 
49
- persisted = persist_object(@meta[:method], @attributes)
49
+ parents = @adapter.process_belongs_to(self, attributes)
50
+ persisted = persist_object(@meta[:method], attributes)
50
51
  @resource.decorate_record(persisted)
51
52
  assign_temp_id(persisted, @meta[:temp_id])
52
53
 
53
54
  associate_parents(persisted, parents)
54
55
 
55
- children = process_has_many(@relationships, persisted) { |x|
56
- update_foreign_key(persisted, x[:attributes], x)
57
- }
56
+ children = @adapter.process_has_many(self, persisted)
58
57
 
59
58
  associate_children(persisted, children) unless @meta[:method] == :destroy
60
59
 
@@ -69,43 +68,21 @@ class Graphiti::Util::Persistence
69
68
  persisted
70
69
  end
71
70
 
72
- private
73
-
74
- def add_hook(prc, lifecycle_event)
75
- ::Graphiti::Util::TransactionHooksRecorder.add(prc, lifecycle_event)
76
- end
77
-
78
- # The child's attributes should be modified to nil-out the
79
- # foreign_key when the parent is being destroyed or disassociated
80
- #
81
- # This is not the case for HABTM, whose "foreign key" is a join table
82
- def update_foreign_key(parent_object, attrs, x)
83
- return if x[:sideload].type == :many_to_many
71
+ def iterate(only: [], except: [])
72
+ opts = {
73
+ resource: @resource,
74
+ relationships: @relationships
75
+ }.merge(only: only, except: except)
84
76
 
85
- if [:destroy, :disassociate].include?(x[:meta][:method])
86
- if x[:sideload].polymorphic_has_one? || x[:sideload].polymorphic_has_many?
87
- attrs[:"#{x[:sideload].polymorphic_as}_type"] = nil
88
- end
89
- attrs[x[:foreign_key]] = nil
90
- update_foreign_type(attrs, x, null: true) if x[:is_polymorphic]
91
- else
92
- if x[:sideload].polymorphic_has_one? || x[:sideload].polymorphic_has_many?
93
- attrs[:"#{x[:sideload].polymorphic_as}_type"] = parent_object.class.name
94
- end
95
- attrs[x[:foreign_key]] = parent_object.send(x[:primary_key])
96
- update_foreign_type(attrs, x) if x[:is_polymorphic]
77
+ Graphiti::Util::RelationshipPayload.iterate(**opts) do |x|
78
+ yield x
97
79
  end
98
80
  end
99
81
 
100
- def update_foreign_type(attrs, x, null: false)
101
- grouping_field = x[:sideload].parent.grouper.field_name
102
- attrs[grouping_field] = null ? nil : x[:sideload].group_name
103
- end
82
+ private
104
83
 
105
- def update_foreign_key_for_parents(parents)
106
- parents.each do |x|
107
- update_foreign_key(x[:object], @attributes, x)
108
- end
84
+ def add_hook(prc, lifecycle_event)
85
+ ::Graphiti::Util::TransactionHooksRecorder.add(prc, lifecycle_event)
109
86
  end
110
87
 
111
88
  def associate_parents(object, parents)
@@ -161,35 +138,6 @@ class Graphiti::Util::Persistence
161
138
  end
162
139
  end
163
140
 
164
- def process_has_many(relationships, caller_model)
165
- [].tap do |processed|
166
- iterate(except: [:polymorphic_belongs_to, :belongs_to]) do |x|
167
- yield x
168
-
169
- x[:object] = x[:resource]
170
- .persist_with_relationships(x[:meta], x[:attributes], x[:relationships], caller_model, x[:foreign_key])
171
-
172
- processed << x
173
- end
174
- end
175
- end
176
-
177
- def process_belongs_to(relationships)
178
- [].tap do |processed|
179
- iterate(only: [:polymorphic_belongs_to, :belongs_to]) do |x|
180
- begin
181
- id = x.dig(:attributes, :id)
182
- x[:object] = x[:resource]
183
- .persist_with_relationships(x[:meta], x[:attributes], x[:relationships])
184
- processed << x
185
- rescue Graphiti::Errors::RecordNotFound
186
- path = "relationships/#{x.dig(:meta, :jsonapi_type)}"
187
- raise Graphiti::Errors::RecordNotFound.new(x[:sideload].name, id, path)
188
- end
189
- end
190
- end
191
- end
192
-
193
141
  def post_process(caller_model, processed)
194
142
  groups = processed.group_by { |x| x[:meta][:method] }
195
143
  groups.each_pair do |method, group|
@@ -205,17 +153,6 @@ class Graphiti::Util::Persistence
205
153
  object.instance_variable_set(:@_jsonapi_temp_id, temp_id)
206
154
  end
207
155
 
208
- def iterate(only: [], except: [])
209
- opts = {
210
- resource: @resource,
211
- relationships: @relationships
212
- }.merge(only: only, except: except)
213
-
214
- Graphiti::Util::RelationshipPayload.iterate(opts) do |x|
215
- yield x
216
- end
217
- end
218
-
219
156
  def metadata
220
157
  {
221
158
  method: @meta[:method],
@@ -55,13 +55,16 @@ module Graphiti
55
55
  def guard
56
56
  method_name = @attr[:readable]
57
57
  instance = @resource.new
58
+ attribute = @name.to_s
58
59
 
59
60
  -> {
60
61
  method = instance.method(method_name)
61
62
  if method.arity.zero?
62
63
  instance.instance_eval(&method_name)
63
- else
64
+ elsif method.arity == 1
64
65
  instance.instance_exec(@object, &method)
66
+ else
67
+ instance.instance_exec(@object, attribute, &method)
65
68
  end
66
69
  }
67
70
  end
@@ -1,3 +1,3 @@
1
1
  module Graphiti
2
- VERSION = "1.2.21"
2
+ VERSION = "1.2.29"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphiti
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.21
4
+ version: 1.2.29
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Richmond
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-26 00:00:00.000000000 Z
11
+ date: 2020-10-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jsonapi-serializable
@@ -162,6 +162,20 @@ dependencies:
162
162
  - - "~>"
163
163
  - !ruby/object:Gem::Version
164
164
  version: '10.0'
165
+ - !ruby/object:Gem::Dependency
166
+ name: standard
167
+ requirement: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - '='
170
+ - !ruby/object:Gem::Version
171
+ version: 0.4.7
172
+ type: :development
173
+ prerelease: false
174
+ version_requirements: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - '='
177
+ - !ruby/object:Gem::Version
178
+ version: 0.4.7
165
179
  - !ruby/object:Gem::Dependency
166
180
  name: activemodel
167
181
  requirement: !ruby/object:Gem::Requirement
@@ -190,7 +204,7 @@ dependencies:
190
204
  - - '='
191
205
  - !ruby/object:Gem::Version
192
206
  version: 1.0.beta.4
193
- description:
207
+ description:
194
208
  email:
195
209
  - richmolj@gmail.com
196
210
  executables:
@@ -248,10 +262,11 @@ files:
248
262
  - lib/graphiti/adapters/active_record/belongs_to_sideload.rb
249
263
  - lib/graphiti/adapters/active_record/has_many_sideload.rb
250
264
  - lib/graphiti/adapters/active_record/has_one_sideload.rb
251
- - lib/graphiti/adapters/active_record/inferrence.rb
265
+ - lib/graphiti/adapters/active_record/inference.rb
252
266
  - lib/graphiti/adapters/active_record/many_to_many_sideload.rb
253
267
  - lib/graphiti/adapters/graphiti_api.rb
254
268
  - lib/graphiti/adapters/null.rb
269
+ - lib/graphiti/adapters/persistence/associations.rb
255
270
  - lib/graphiti/cli.rb
256
271
  - lib/graphiti/configuration.rb
257
272
  - lib/graphiti/context.rb
@@ -327,15 +342,18 @@ homepage: https://github.com/graphiti-api/graphiti
327
342
  licenses:
328
343
  - MIT
329
344
  metadata: {}
330
- post_install_message:
345
+ post_install_message:
331
346
  rdoc_options: []
332
347
  require_paths:
333
348
  - lib
334
349
  required_ruby_version: !ruby/object:Gem::Requirement
335
350
  requirements:
336
- - - "~>"
351
+ - - ">="
337
352
  - !ruby/object:Gem::Version
338
353
  version: '2.3'
354
+ - - "<"
355
+ - !ruby/object:Gem::Version
356
+ version: '3.1'
339
357
  required_rubygems_version: !ruby/object:Gem::Requirement
340
358
  requirements:
341
359
  - - ">="
@@ -343,7 +361,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
343
361
  version: '0'
344
362
  requirements: []
345
363
  rubygems_version: 3.0.6
346
- signing_key:
364
+ signing_key:
347
365
  specification_version: 4
348
366
  summary: Easily build jsonapi.org-compatible APIs
349
367
  test_files: []