graphiti 2.0.0.beta.1 → 2.0.0.beta.3

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: a741f7953d1f54e730d2e801715f3b1d718044a4e0bd59cfd8bb3b364373e715
4
- data.tar.gz: 99391699d407dfb92178cf851cce313d691ea930eb0ada81abe764b7a8310087
3
+ metadata.gz: 69a8294c01d96b3ca136060036e2b8592f46464a775965f6f2e42129915854bd
4
+ data.tar.gz: e133c0cf444718997a718e16a957b6e11d86aec78696ac2cd4bb4fe8e3b907eb
5
5
  SHA512:
6
- metadata.gz: 77d02ef7c1a525c286d56677ef103f56c7ea80873f565c6db72d2208d887fac967c8f9cddc6a06f940c4a62c6d7faf58390f5331f18fd2a562aa855fff002afd
7
- data.tar.gz: e4e83880da4609da20727c8e8bf7b21cddc6e7436d571218da2f874e682dfdbe03c6960799e12614716b936881a8af95ee0459d6458afcaf5f3818dae198543e
6
+ metadata.gz: fb38ebe38f207b016b61a960f0b39890fe4cb3aa8e8bc70a50f5f82894513c9360fe37ce02de50a7de14718d5a0f5865a50d6defe1ce15e2730409bc3324401c
7
+ data.tar.gz: cc5090ad354c15c4f7d5a2c9daa8aab78be9bc7b4f41c62569a5fa5edf7496dd79e2b0d86533027fd7989ed1f7295950fc325a1cf9475ae03992ddd6344a2e27
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.3
data/.tool-versions ADDED
@@ -0,0 +1 @@
1
+ ruby 3.3.4
data/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  graphiti changelog
2
2
 
3
+ # [2.0.0-beta.3](https://github.com/graphiti-api/graphiti/compare/v2.0.0-beta.2...v2.0.0-beta.3) (2026-07-31)
4
+
5
+
6
+ ### Features
7
+
8
+ * carry the assigned model on the resource, not through override signatures ([8ad848d](https://github.com/graphiti-api/graphiti/commit/8ad848d9bcd24cd63efb67e8ce32d5c8f3cfe147))
9
+
10
+ # [2.0.0-beta.2](https://github.com/graphiti-api/graphiti/compare/v2.0.0-beta.1...v2.0.0-beta.2) (2026-07-30)
11
+
12
+
13
+ ### Features
14
+
15
+ * add Resource.wrap as an easy way to use graphiti serialization on models fetched via other means (and not via graphiti's finders) ([#513](https://github.com/graphiti-api/graphiti/issues/513)) ([fcd19e2](https://github.com/graphiti-api/graphiti/commit/fcd19e2d023091d85947dbdd89259b587e0f582b))
16
+ * deprecate mutating attributes in around_persistence hooks ([#514](https://github.com/graphiti-api/graphiti/issues/514)) [skip ci] ([8410ab0](https://github.com/graphiti-api/graphiti/commit/8410ab0a916077601cf85d8260a05ef9098149eb))
17
+
3
18
  # [2.0.0-beta.1](https://github.com/graphiti-api/graphiti/compare/v1.12.2...v2.0.0-beta.1) (2026-07-30)
4
19
 
5
20
 
@@ -14,6 +29,14 @@ graphiti changelog
14
29
  * around_persistence hooks receive the assigned model instead of the attributes hash. Move attribute-hash modifications to before_attributes, or set values on the model. Custom create/update overrides that should receive a pre-assigned model must accept an assigned_model: keyword. See UPGRADING.md
15
30
  * Ruby >= 3.0 / Rails >= 6 are now required.
16
31
 
32
+ # [1.13.0](https://github.com/graphiti-api/graphiti/compare/v1.12.2...v1.13.0) (2026-07-30)
33
+
34
+
35
+ ### Features
36
+
37
+ * add Resource.wrap as an easy way to use graphiti serialization on models fetched via other means (and not via graphiti's finders) ([#513](https://github.com/graphiti-api/graphiti/issues/513)) ([fcd19e2](https://github.com/graphiti-api/graphiti/commit/fcd19e2d023091d85947dbdd89259b587e0f582b))
38
+ * deprecate mutating attributes in around_persistence hooks ([#514](https://github.com/graphiti-api/graphiti/issues/514)) [skip ci] ([8410ab0](https://github.com/graphiti-api/graphiti/commit/8410ab0a916077601cf85d8260a05ef9098149eb))
39
+
17
40
  ## [1.12.2](https://github.com/graphiti-api/graphiti/compare/v1.12.1...v1.12.2) (2026-07-29)
18
41
 
19
42
 
data/UPGRADING.md CHANGED
@@ -54,16 +54,6 @@ To migrate, move attribute-hash modifications to `before_attributes` (which stil
54
54
 
55
55
  `before/around/after_attributes` and `before/around/after_save` are unchanged.
56
56
 
57
- ### Breaking: custom create/update overrides and pre-assigned models
58
-
59
- If a resource overrides `create` or `update` and callers inspect the model before saving, the override must accept the already-assigned model:
60
-
61
- ```ruby
62
- def create(attributes, meta = nil, assigned_model: nil)
63
- ```
64
-
65
- Without the keyword, saving a proxy whose model was inspected raises `Graphiti::Errors::AssignedModelNotSupported` rather than silently discarding changes made to the inspected instance. Overridden resources used only via plain `save` are unaffected.
66
-
67
57
  ### Fine print
68
58
 
69
59
  - If you inspect the model before saving, the attributes callbacks run at inspection time (in your controller, outside the save transaction). On the plain `save` path they run inside the transaction, at the same point as 1.x.
@@ -84,25 +84,6 @@ module Graphiti
84
84
  end
85
85
  end
86
86
 
87
- class AssignedModelNotSupported < Base
88
- def initialize(resource_class, method_name)
89
- @resource_class = resource_class
90
- @method_name = method_name
91
- end
92
-
93
- def message
94
- <<~MSG
95
- #{@resource_class}: the model was already assigned via #assign_attributes (or #data before save), but ##{@method_name} is overridden without a parameter to receive it, so changes made to the unsaved model would be silently lost.
96
-
97
- Accept and use the assigned model in your override:
98
-
99
- def #{@method_name}(attributes, meta = nil, assigned_model: nil)
100
-
101
- or persist with #save alone, without inspecting the model first. See UPGRADING.md.
102
- MSG
103
- end
104
- end
105
-
106
87
  class RemoteWrite < Base
107
88
  def initialize(resource_class)
108
89
  @resource_class = resource_class
@@ -486,6 +467,19 @@ module Graphiti
486
467
  end
487
468
  end
488
469
 
470
+ class InvalidWrapModel < Base
471
+ def initialize(resource_class, model)
472
+ @resource_class = resource_class
473
+ @model = model
474
+ end
475
+
476
+ def message
477
+ <<~MSG
478
+ #{@resource_class.name}.wrap was given a #{@model.class}, but this resource is for #{@resource_class.model} (subclasses are fine).
479
+ MSG
480
+ end
481
+ end
482
+
489
483
  class ResourceEndpointConflict < Base
490
484
  def initialize(path, action, resource_a, resource_b)
491
485
  @path = path
@@ -53,8 +53,28 @@ module Graphiti
53
53
  runner.proxy(base_scope, single: true, raise_on_missing: true, assign_action: :create)
54
54
  end
55
55
 
56
+ # Wrap models fetched outside graphiti so they render like any other proxy
57
+ def wrap(models, base_scope = nil)
58
+ validate_wrap_models!(models)
59
+ runner = Runner.new(self, {}, nil, :find)
60
+ runner.proxy(base_scope, bypass_required_filters: true).tap do |proxy|
61
+ proxy.data = models
62
+ end
63
+ end
64
+
56
65
  private
57
66
 
67
+ # Skip polymorphic parents - resource_for_model raises a better error for unknown children
68
+ def validate_wrap_models!(models)
69
+ return if abstract_class? || (polymorphic? && !polymorphic_child?)
70
+
71
+ [models].flatten.compact.each do |model|
72
+ unless model.is_a?(self.model)
73
+ raise Errors::InvalidWrapModel.new(self, model)
74
+ end
75
+ end
76
+ end
77
+
58
78
  def caching_options
59
79
  {cache: @cache_resource, cache_expires_in: @cache_expires_in, cache_tag: @cache_tag}
60
80
  end
@@ -93,11 +93,23 @@ module Graphiti
93
93
  model_instance
94
94
  end
95
95
 
96
+ # The model built by a prior ResourceProxy#assign_attributes, present
97
+ # for the duration of the save that persists it
98
+ attr_reader :assigned_model
99
+
100
+ # @api private
101
+ def with_assigned_model(model)
102
+ @assigned_model = model
103
+ yield
104
+ ensure
105
+ @assigned_model = nil
106
+ end
107
+
96
108
  # Attributes are assigned before the persistence callbacks fire, so
97
109
  # around_persistence receives the assigned model - its pre-yield
98
110
  # position is the last chance to touch the model before save, inside
99
111
  # the transaction. Modify attributes in before_attributes instead.
100
- def create(create_params, meta = nil, assigned_model: nil)
112
+ def create(create_params, meta = nil)
101
113
  model_instance = assigned_model || assign(create_params, meta, :create)
102
114
 
103
115
  run_callbacks :persistence, :create, model_instance, meta do
@@ -111,7 +123,7 @@ module Graphiti
111
123
  model_instance
112
124
  end
113
125
 
114
- def update(update_params, meta = nil, assigned_model: nil)
126
+ def update(update_params, meta = nil)
115
127
  model_instance = assigned_model || assign(update_params, meta, :update)
116
128
 
117
129
  run_callbacks :persistence, :update, model_instance, meta do
@@ -82,6 +82,12 @@ module Graphiti
82
82
  Renderer.new(self, options).as_graphql
83
83
  end
84
84
 
85
+ # Records supplied directly, no scope resolution
86
+ def data=(models)
87
+ @data = models
88
+ [@data].flatten.compact.each { |record| @resource.decorate_record(record) }
89
+ end
90
+
85
91
  def data
86
92
  return @data unless @data.nil?
87
93
  return assign_attributes(@payload.params) if @assign_action
@@ -190,22 +190,21 @@ class Graphiti::Util::Persistence
190
190
  }
191
191
  end
192
192
 
193
- def accepts_assigned_model?(method)
194
- method.parameters.any? { |type, name| name == :assigned_model && [:key, :keyreq].include?(type) }
195
- end
196
-
193
+ # The assigned model rides on the resource instance rather than through
194
+ # the method signature, so any override of #create/#update - whatever its
195
+ # arity - passes it through untouched.
197
196
  def call_resource_method(method_name, attributes, caller_model)
198
197
  method = @resource.method(method_name)
198
+ call = if method.arity == 1
199
+ -> { method.call(attributes) }
200
+ else
201
+ -> { method.call(attributes, metadata) }
202
+ end
199
203
 
200
204
  if @assigned_model && [:create, :update].include?(method_name)
201
- unless accepts_assigned_model?(method)
202
- raise Graphiti::Errors::AssignedModelNotSupported.new(@resource.class, method_name)
203
- end
204
- method.call(attributes, metadata, assigned_model: @assigned_model)
205
- elsif method.arity == 1
206
- method.call(attributes)
205
+ @resource.with_assigned_model(@assigned_model) { call.call }
207
206
  else
208
- method.call(attributes, metadata)
207
+ call.call
209
208
  end
210
209
  end
211
210
  end
@@ -1,3 +1,3 @@
1
1
  module Graphiti
2
- VERSION = "2.0.0.beta.1"
2
+ VERSION = "2.0.0.beta.3"
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: 2.0.0.beta.1
4
+ version: 2.0.0.beta.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Richmond
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-30 00:00:00.000000000 Z
11
+ date: 2026-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jsonapi-serializable
@@ -237,7 +237,9 @@ files:
237
237
  - ".github/workflows/release.yml"
238
238
  - ".gitignore"
239
239
  - ".rspec"
240
+ - ".ruby-version"
240
241
  - ".standard.yml"
242
+ - ".tool-versions"
241
243
  - ".yardopts"
242
244
  - Appraisals
243
245
  - CHANGELOG.md