graphiti 2.0.0.beta.6 → 2.0.0.beta.8

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.
Files changed (246) hide show
  1. checksums.yaml +4 -4
  2. data/.git-blame-ignore-revs +2 -0
  3. data/.github/workflows/ci.yml +55 -3
  4. data/.github/workflows/docs.yml +1 -1
  5. data/.github/workflows/notify-example-app.yml +18 -0
  6. data/.gitignore +2 -0
  7. data/.standard.yml +6 -0
  8. data/CHANGELOG.md +54 -0
  9. data/CONTRIBUTING.md +3 -1
  10. data/README.md +13 -206
  11. data/Rakefile +6 -3
  12. data/UPGRADING.md +1 -1
  13. data/graphiti.gemspec +8 -4
  14. data/lib/generators/graphiti/generator_mixin.rb +1 -1
  15. data/lib/generators/graphiti/install_generator.rb +1 -2
  16. data/lib/generators/graphiti/resource_generator.rb +6 -3
  17. data/lib/generators/graphiti/templates/application_resource.rb.erb +19 -0
  18. data/lib/generators/graphiti/templates/controller.rb.erb +26 -8
  19. data/lib/generators/graphiti/templates/index_request_spec.rb.erb +2 -2
  20. data/lib/generators/graphiti/templates/resource_reads_spec.rb.erb +4 -4
  21. data/lib/generators/graphiti/templates/show_request_spec.rb.erb +2 -2
  22. data/lib/graphiti/audit/report.rb +6 -6
  23. data/lib/graphiti/audit.rb +3 -4
  24. data/lib/graphiti/delegates/pagination.rb +2 -2
  25. data/lib/graphiti/deserializer.rb +3 -3
  26. data/lib/graphiti/errors.rb +6 -6
  27. data/lib/graphiti/extensions/temp_id.rb +1 -1
  28. data/lib/graphiti/query.rb +27 -8
  29. data/lib/graphiti/rails/context.rb +2 -1
  30. data/lib/graphiti/rails/controller.rb +3 -0
  31. data/lib/graphiti/rails/debugging.rb +1 -1
  32. data/lib/graphiti/rails/responders.rb +2 -1
  33. data/lib/graphiti/resource/configuration.rb +1 -0
  34. data/lib/graphiti/resource/dsl.rb +2 -2
  35. data/lib/graphiti/resource/interface.rb +3 -3
  36. data/lib/graphiti/resource/links.rb +1 -1
  37. data/lib/graphiti/resource/persistence.rb +1 -1
  38. data/lib/graphiti/resource/polymorphism.rb +1 -1
  39. data/lib/graphiti/resource.rb +2 -2
  40. data/lib/graphiti/resource_proxy.rb +0 -1
  41. data/lib/graphiti/runner.rb +8 -3
  42. data/lib/graphiti/scope.rb +13 -0
  43. data/lib/graphiti/scoping/filter.rb +5 -3
  44. data/lib/graphiti/scoping/paginate.rb +1 -1
  45. data/lib/graphiti/scoping/sort.rb +1 -1
  46. data/lib/graphiti/sideload/has_many.rb +1 -1
  47. data/lib/graphiti/spec_helpers/errors_proxy.rb +1 -1
  48. data/lib/graphiti/spec_helpers/matchers.rb +1 -1
  49. data/lib/graphiti/spec_helpers.rb +5 -0
  50. data/lib/graphiti/types.rb +5 -1
  51. data/lib/graphiti/util/cache_debug.rb +3 -3
  52. data/lib/graphiti/util/hash.rb +1 -1
  53. data/lib/graphiti/util/persistence.rb +6 -6
  54. data/lib/graphiti/util/serializer_relationships.rb +1 -1
  55. data/lib/graphiti/version.rb +1 -1
  56. data/lib/tasks/graphiti.rake +1 -1
  57. data/package.json +1 -0
  58. metadata +31 -197
  59. data/docs/concepts/backends-and-models.md +0 -122
  60. data/docs/concepts/endpoints.md +0 -183
  61. data/docs/concepts/links.md +0 -212
  62. data/docs/concepts/overview.md +0 -80
  63. data/docs/concepts/persisting.md +0 -376
  64. data/docs/concepts/relationships.md +0 -627
  65. data/docs/concepts/resources.md +0 -677
  66. data/docs/getting-started/first-api.md +0 -289
  67. data/docs/getting-started/installation.md +0 -185
  68. data/docs/intro.md +0 -307
  69. data/docs/js/authentication.md +0 -63
  70. data/docs/js/ddau.md +0 -20
  71. data/docs/js/extra-params.md +0 -41
  72. data/docs/js/index.md +0 -112
  73. data/docs/js/installation.md +0 -120
  74. data/docs/js/middleware.md +0 -72
  75. data/docs/js/models.md +0 -202
  76. data/docs/js/reads.md +0 -494
  77. data/docs/js/state-syncing.md +0 -100
  78. data/docs/js/writes.md +0 -373
  79. data/docs/reference/vandal.md +0 -63
  80. data/docs/reference/why.md +0 -13
  81. data/docs/topics/authorization.md +0 -155
  82. data/docs/topics/caching.md +0 -55
  83. data/docs/topics/customizing-sideloads.md +0 -156
  84. data/docs/topics/debugging.md +0 -242
  85. data/docs/topics/error-handling.md +0 -210
  86. data/docs/topics/etags.md +0 -46
  87. data/docs/topics/hopping-relationships.md +0 -149
  88. data/docs/topics/json-attributes.md +0 -77
  89. data/docs/topics/openstruct-models.md +0 -50
  90. data/docs/topics/remote-resources.md +0 -291
  91. data/docs/topics/testing.md +0 -894
  92. data/docs/topics/without-activerecord.md +0 -324
  93. data/docs/tutorial/index.md +0 -58
  94. data/docs/tutorial/step_0.md +0 -107
  95. data/docs/tutorial/step_1.md +0 -199
  96. data/docs/tutorial/step_2.md +0 -312
  97. data/docs/tutorial/step_3.md +0 -142
  98. data/docs/tutorial/step_4.md +0 -135
  99. data/docs/tutorial/step_5.md +0 -69
  100. data/docs/tutorial/step_6.md +0 -82
  101. data/docs/tutorial/step_7.md +0 -205
  102. data/docs/tutorial/step_8.md +0 -128
  103. data/docs/tutorial/step_9.md +0 -171
  104. data/docs/upgrading.md +0 -266
  105. data/website/.gitignore +0 -20
  106. data/website/README.md +0 -43
  107. data/website/docusaurus.config.js +0 -197
  108. data/website/package-lock.json +0 -19474
  109. data/website/package.json +0 -46
  110. data/website/sidebars.js +0 -82
  111. data/website/src/css/custom.css +0 -85
  112. data/website/src/pages/markdown-page.mdx +0 -7
  113. data/website/static/.nojekyll +0 -0
  114. data/website/static/1.13/2019/03/31/graphiti-1-0.html +0 -205
  115. data/website/static/1.13/2019/05/08/graphiti-1-1.html +0 -212
  116. data/website/static/1.13/2019/05/20/graphiti-1-2.html +0 -214
  117. data/website/static/1.13/2019/10/14/tutorial.html +0 -198
  118. data/website/static/1.13/CNAME +0 -1
  119. data/website/static/1.13/README.md +0 -16
  120. data/website/static/1.13/assets/css/syntax.css +0 -60
  121. data/website/static/1.13/assets/favicons/android-chrome-192x192.png +0 -0
  122. data/website/static/1.13/assets/favicons/android-chrome-256x256.png +0 -0
  123. data/website/static/1.13/assets/favicons/apple-touch-icon.png +0 -0
  124. data/website/static/1.13/assets/favicons/browserconfig.xml +0 -9
  125. data/website/static/1.13/assets/favicons/favicon-16x16.png +0 -0
  126. data/website/static/1.13/assets/favicons/favicon-32x32.png +0 -0
  127. data/website/static/1.13/assets/favicons/favicon.ico +0 -0
  128. data/website/static/1.13/assets/favicons/mstile-150x150.png +0 -0
  129. data/website/static/1.13/assets/favicons/safari-pinned-tab.svg +0 -1
  130. data/website/static/1.13/assets/favicons/site.webmanifest +0 -19
  131. data/website/static/1.13/assets/img/backend.gif +0 -0
  132. data/website/static/1.13/assets/img/conformity.png +0 -0
  133. data/website/static/1.13/assets/img/error_payload.png +0 -0
  134. data/website/static/1.13/assets/img/fancy-cushion.png +0 -0
  135. data/website/static/1.13/assets/img/gh.png +0 -0
  136. data/website/static/1.13/assets/img/home-bg.jpg +0 -0
  137. data/website/static/1.13/assets/img/lifecycle.gif +0 -0
  138. data/website/static/1.13/assets/img/logo-500.png +0 -0
  139. data/website/static/1.13/assets/img/logo.png +0 -0
  140. data/website/static/1.13/assets/img/love-graffiti.jpg +0 -0
  141. data/website/static/1.13/assets/img/meta_total_count.png +0 -0
  142. data/website/static/1.13/assets/img/persist.jpg +0 -0
  143. data/website/static/1.13/assets/img/resource.gif +0 -0
  144. data/website/static/1.13/assets/img/rest-graffiti.jpg +0 -0
  145. data/website/static/1.13/assets/img/rest1.gif +0 -0
  146. data/website/static/1.13/assets/img/rest2.gif +0 -0
  147. data/website/static/1.13/assets/img/rest3.gif +0 -0
  148. data/website/static/1.13/assets/img/rethink-rest-graffiti.jpg +0 -0
  149. data/website/static/1.13/assets/img/sunrise.png +0 -0
  150. data/website/static/1.13/assets/img/why.png +0 -0
  151. data/website/static/1.13/assets/js/highlight.pack.js +0 -2
  152. data/website/static/1.13/assets/main.css +0 -15518
  153. data/website/static/1.13/assets/main.css.map +0 -1
  154. data/website/static/1.13/bin/bundle +0 -109
  155. data/website/static/1.13/bin/jekyll +0 -27
  156. data/website/static/1.13/bin/kramdown +0 -27
  157. data/website/static/1.13/bin/listen +0 -27
  158. data/website/static/1.13/bin/rake +0 -27
  159. data/website/static/1.13/bin/rougify +0 -27
  160. data/website/static/1.13/bin/safe_yaml +0 -27
  161. data/website/static/1.13/bin/sass +0 -27
  162. data/website/static/1.13/bin/sass-convert +0 -27
  163. data/website/static/1.13/bin/scss +0 -27
  164. data/website/static/1.13/blog.html +0 -259
  165. data/website/static/1.13/cheatsheet.html +0 -316
  166. data/website/static/1.13/cookbooks/authorization.md +0 -0
  167. data/website/static/1.13/cookbooks/caching.md +0 -0
  168. data/website/static/1.13/cookbooks/customizing-sideloads.html +0 -325
  169. data/website/static/1.13/cookbooks/etags.md +0 -0
  170. data/website/static/1.13/cookbooks/hopping-relationships.html +0 -324
  171. data/website/static/1.13/cookbooks/json_attributes.md +0 -0
  172. data/website/static/1.13/cookbooks/openstruct-models.md +0 -0
  173. data/website/static/1.13/cookbooks/remote-resources.md +0 -0
  174. data/website/static/1.13/cookbooks/without-activerecord.html +0 -510
  175. data/website/static/1.13/features.html +0 -249
  176. data/website/static/1.13/feed.xml +0 -106
  177. data/website/static/1.13/guides/concepts/backends-and-models.html +0 -467
  178. data/website/static/1.13/guides/concepts/debugging.html +0 -440
  179. data/website/static/1.13/guides/concepts/endpoints.html +0 -432
  180. data/website/static/1.13/guides/concepts/error-handling.html +0 -396
  181. data/website/static/1.13/guides/concepts/links.html +0 -501
  182. data/website/static/1.13/guides/concepts/remote-resources.html +0 -536
  183. data/website/static/1.13/guides/concepts/resources.html +0 -2176
  184. data/website/static/1.13/guides/concepts/testing.html +0 -1469
  185. data/website/static/1.13/guides/getting-started/installation.html +0 -420
  186. data/website/static/1.13/guides/graphiti-rails-migration.html +0 -242
  187. data/website/static/1.13/guides/index.html +0 -269
  188. data/website/static/1.13/guides/overview.html +0 -325
  189. data/website/static/1.13/guides/upgrading-2-0.html +0 -193
  190. data/website/static/1.13/guides/upgrading.html +0 -314
  191. data/website/static/1.13/guides/vandal.html +0 -282
  192. data/website/static/1.13/guides/why.html +0 -1121
  193. data/website/static/1.13/index.html +0 -72
  194. data/website/static/1.13/js/authentication.html +0 -295
  195. data/website/static/1.13/js/ddau.html +0 -238
  196. data/website/static/1.13/js/extra-params.html +0 -270
  197. data/website/static/1.13/js/index.html +0 -321
  198. data/website/static/1.13/js/installation.html +0 -637
  199. data/website/static/1.13/js/introduction.html +0 -257
  200. data/website/static/1.13/js/middleware.html +0 -318
  201. data/website/static/1.13/js/reads/fieldsets.html +0 -271
  202. data/website/static/1.13/js/reads/filtering.html +0 -289
  203. data/website/static/1.13/js/reads/includes.html +0 -260
  204. data/website/static/1.13/js/reads/index.html +0 -497
  205. data/website/static/1.13/js/reads/nested-queries.html +0 -353
  206. data/website/static/1.13/js/reads/pagination.html +0 -260
  207. data/website/static/1.13/js/reads/sorting.html +0 -265
  208. data/website/static/1.13/js/reads/statistics.html +0 -289
  209. data/website/static/1.13/js/state-syncing.html +0 -340
  210. data/website/static/1.13/js/writes/deferred.html +0 -296
  211. data/website/static/1.13/js/writes/dirty-tracking.html +0 -399
  212. data/website/static/1.13/js/writes/index.html +0 -391
  213. data/website/static/1.13/js/writes/nested.html +0 -330
  214. data/website/static/1.13/js/writes/validations.html +0 -272
  215. data/website/static/1.13/quickstart.html +0 -660
  216. data/website/static/1.13/template +0 -161
  217. data/website/static/1.13/tutorial/index.html +0 -250
  218. data/website/static/1.13/tutorial/step_0.html +0 -292
  219. data/website/static/1.13/tutorial/step_1.html +0 -517
  220. data/website/static/1.13/tutorial/step_2.html +0 -481
  221. data/website/static/1.13/tutorial/step_3.html +0 -323
  222. data/website/static/1.13/tutorial/step_4.html +0 -318
  223. data/website/static/1.13/tutorial/step_5.html +0 -265
  224. data/website/static/1.13/tutorial/step_6.html +0 -276
  225. data/website/static/1.13/tutorial/step_7.html +0 -390
  226. data/website/static/1.13/tutorial/step_8.html +0 -316
  227. data/website/static/1.13/tutorial/step_9.html +0 -365
  228. data/website/static/CNAME +0 -1
  229. data/website/static/assets/img/error_payload.png +0 -0
  230. data/website/static/assets/img/legacy/legacy-0378a3bb39.png +0 -0
  231. data/website/static/assets/img/legacy/legacy-05bbd3e5fd.png +0 -0
  232. data/website/static/assets/img/legacy/legacy-07aa104495.png +0 -0
  233. data/website/static/assets/img/legacy/legacy-0c75a16b3a.gif +0 -0
  234. data/website/static/assets/img/legacy/legacy-3076df6209.png +0 -0
  235. data/website/static/assets/img/legacy/legacy-7f6889bc89.png +0 -0
  236. data/website/static/assets/img/legacy/legacy-a2cc4363c3.png +0 -0
  237. data/website/static/assets/img/legacy/legacy-f67cfa89ab.png +0 -0
  238. data/website/static/assets/img/meta_total_count.png +0 -0
  239. data/website/static/img/docusaurus-social-card.jpg +0 -0
  240. data/website/static/img/docusaurus.png +0 -0
  241. data/website/static/img/favicon.ico +0 -0
  242. data/website/static/img/logo.png +0 -0
  243. data/website/static/img/logo.svg +0 -1
  244. data/website/static/img/undraw_docusaurus_mountain.svg +0 -171
  245. data/website/static/img/undraw_docusaurus_react.svg +0 -170
  246. data/website/static/img/undraw_docusaurus_tree.svg +0 -40
@@ -62,7 +62,7 @@ module Graphiti
62
62
 
63
63
  def method_missing(id, *args, &blk)
64
64
  matches = @errors.select { |e| e.attribute.to_sym == id }
65
- matches.length == 1 ? matches[0] : matches
65
+ (matches.length == 1) ? matches[0] : matches
66
66
  end
67
67
 
68
68
  # Any name is answered: an attribute with no matching error yields an empty
@@ -73,7 +73,7 @@ module Graphiti
73
73
  attr_reader :target, :opts, :resource
74
74
 
75
75
  def assert_opt(opt)
76
- asserted_opt = opt == :resource ? :resource_class : opt
76
+ asserted_opt = (opt == :resource) ? :resource_class : opt
77
77
  return true if config.send(asserted_opt) == opts[opt]
78
78
 
79
79
  @opt_failures << opt_failure_message(opt, opts[opt], config.send(asserted_opt))
@@ -26,6 +26,11 @@ module Graphiti
26
26
  end
27
27
 
28
28
  module Sugar
29
+ def self.included(base)
30
+ Graphiti::DEPRECATOR.warn("Graphiti::SpecHelpers::Sugar is deprecated. Call jsonapi_data, jsonapi_included, jsonapi_errors, json_date and json_datetime directly.")
31
+ super
32
+ end
33
+
29
34
  def d
30
35
  jsonapi_data
31
36
  end
@@ -82,6 +82,10 @@ module Graphiti
82
82
  Dry::Types["params.hash"][input]
83
83
  }
84
84
 
85
+ ParamArray = Dry::Types["strict.array"].constructor { |input|
86
+ input.is_a?(String) ? [input] : input
87
+ }
88
+
85
89
  REQUIRED_KEYS = [:params, :read, :write, :kind, :description]
86
90
 
87
91
  def self.map
@@ -193,7 +197,7 @@ module Graphiti
193
197
 
194
198
  arrays[:"array_of_#{name.to_s.pluralize}"] = {
195
199
  canonical_name: name,
196
- params: Dry::Types["strict.array"].of(map[:params]),
200
+ params: ParamArray.of(map[:params]),
197
201
  read: Dry::Types["strict.array"].of(map[:read]),
198
202
  test: Dry::Types["strict.array"].of(map[:test]),
199
203
  write: Dry::Types["strict.array"].of(map[:write]),
@@ -28,7 +28,7 @@ module Graphiti
28
28
  expires_in: proxy.cache_expires_in,
29
29
  etag: proxy.etag,
30
30
  miss_count: last_version[:miss_count].to_i + (changed_key? ? 1 : 0),
31
- hit_count: last_version[:hit_count].to_i + (!changed_key? && !new_key? ? 1 : 0),
31
+ hit_count: last_version[:hit_count].to_i + ((!changed_key? && !new_key?) ? 1 : 0),
32
32
  request_count: last_version[:request_count].to_i + (last_version.present? ? 1 : 0)
33
33
  }
34
34
  end
@@ -76,8 +76,8 @@ module Graphiti
76
76
  end
77
77
 
78
78
  def changes
79
- sub_keys_old = last_version[:cache_key]&.scan(/\w+\/query-[a-z0-9-]+\/args-[a-z0-9-]+/).to_a || []
80
- sub_keys_new = current_version[:cache_key]&.scan(/\w+\/query-[a-z0-9-]+\/args-[a-z0-9-]+/).to_a || []
79
+ sub_keys_old = last_version[:cache_key]&.scan(/\w+\/query-[a-z0-9-]+\/args-[a-z0-9-]+/).to_a
80
+ sub_keys_new = current_version[:cache_key]&.scan(/\w+\/query-[a-z0-9-]+\/args-[a-z0-9-]+/).to_a
81
81
 
82
82
  [sub_keys_old, sub_keys_new]
83
83
  end
@@ -43,7 +43,7 @@ module Graphiti
43
43
  # @return [Hash] the merged hash
44
44
  # @api private
45
45
  def self.deep_merge!(hash, other)
46
- merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
46
+ merger = proc { |key, v1, v2| (Hash === v1 && Hash === v2) ? v1.merge(v2, &merger) : v2 }
47
47
  hash.merge!(other, &merger)
48
48
  end
49
49
 
@@ -156,12 +156,12 @@ class Graphiti::Util::Persistence
156
156
 
157
157
  def persist_object(method, attributes)
158
158
  case method
159
- when :destroy
160
- call_resource_method(:destroy, attributes[:id], @caller_model)
161
- when :update, nil, :disassociate
162
- call_resource_method(:update, attributes, @caller_model)
163
- else
164
- call_resource_method(:create, attributes, @caller_model)
159
+ when :destroy
160
+ call_resource_method(:destroy, attributes[:id], @caller_model)
161
+ when :update, nil, :disassociate
162
+ call_resource_method(:update, attributes, @caller_model)
163
+ else
164
+ call_resource_method(:create, attributes, @caller_model)
165
165
  end
166
166
  end
167
167
 
@@ -173,7 +173,7 @@ module Graphiti
173
173
  def validate_link_for_sideload!(sideload)
174
174
  return if sideload.resource.remote?
175
175
 
176
- action = sideload.type == :belongs_to ? :show : :index
176
+ action = (sideload.type == :belongs_to) ? :show : :index
177
177
  cache_key = :"#{@sideload.object_id}-#{action}"
178
178
  return if self.class.validated_link_cache.include?(cache_key)
179
179
  prc = Graphiti.config.context_for_endpoint
@@ -1,3 +1,3 @@
1
1
  module Graphiti
2
- VERSION = "2.0.0.beta.6"
2
+ VERSION = "2.0.0.beta.8"
3
3
  end
@@ -20,7 +20,7 @@ namespace :graphiti do
20
20
  task audit: [:environment] do
21
21
  helpers.setup_rails!
22
22
  rows = Graphiti::Audit.run
23
- puts Graphiti::Audit::Report.new(rows).to_s
23
+ puts Graphiti::Audit::Report.new(rows)
24
24
 
25
25
  exit 1 if rows.any?(&:error?)
26
26
  end
data/package.json CHANGED
@@ -22,6 +22,7 @@
22
22
  },
23
23
  "release": {
24
24
  "branches": [
25
+ "1.x",
25
26
  "main",
26
27
  {
27
28
  "name": "beta",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphiti
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0.beta.6
4
+ version: 2.0.0.beta.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Richmond
@@ -129,30 +129,36 @@ dependencies:
129
129
  name: faraday
130
130
  requirement: !ruby/object:Gem::Requirement
131
131
  requirements:
132
- - - "~>"
132
+ - - ">="
133
133
  - !ruby/object:Gem::Version
134
- version: '0.15'
134
+ version: '2.0'
135
+ - - "<"
136
+ - !ruby/object:Gem::Version
137
+ version: '3'
135
138
  type: :development
136
139
  prerelease: false
137
140
  version_requirements: !ruby/object:Gem::Requirement
138
141
  requirements:
139
- - - "~>"
142
+ - - ">="
140
143
  - !ruby/object:Gem::Version
141
- version: '0.15'
144
+ version: '2.0'
145
+ - - "<"
146
+ - !ruby/object:Gem::Version
147
+ version: '3'
142
148
  - !ruby/object:Gem::Dependency
143
149
  name: kaminari
144
150
  requirement: !ruby/object:Gem::Requirement
145
151
  requirements:
146
152
  - - "~>"
147
153
  - !ruby/object:Gem::Version
148
- version: '0.17'
154
+ version: '1.2'
149
155
  type: :development
150
156
  prerelease: false
151
157
  version_requirements: !ruby/object:Gem::Requirement
152
158
  requirements:
153
159
  - - "~>"
154
160
  - !ruby/object:Gem::Version
155
- version: '0.17'
161
+ version: '1.2'
156
162
  - !ruby/object:Gem::Dependency
157
163
  name: bundler
158
164
  requirement: !ruby/object:Gem::Requirement
@@ -187,14 +193,14 @@ dependencies:
187
193
  requirements:
188
194
  - - "~>"
189
195
  - !ruby/object:Gem::Version
190
- version: 1.4.0
196
+ version: '1.56'
191
197
  type: :development
192
198
  prerelease: false
193
199
  version_requirements: !ruby/object:Gem::Requirement
194
200
  requirements:
195
201
  - - "~>"
196
202
  - !ruby/object:Gem::Version
197
- version: 1.4.0
203
+ version: '1.56'
198
204
  - !ruby/object:Gem::Dependency
199
205
  name: activemodel
200
206
  requirement: !ruby/object:Gem::Requirement
@@ -209,6 +215,20 @@ dependencies:
209
215
  - - ">="
210
216
  - !ruby/object:Gem::Version
211
217
  version: '7.1'
218
+ - !ruby/object:Gem::Dependency
219
+ name: rexml
220
+ requirement: !ruby/object:Gem::Requirement
221
+ requirements:
222
+ - - ">="
223
+ - !ruby/object:Gem::Version
224
+ version: '0'
225
+ type: :development
226
+ prerelease: false
227
+ version_requirements: !ruby/object:Gem::Requirement
228
+ requirements:
229
+ - - ">="
230
+ - !ruby/object:Gem::Version
231
+ version: '0'
212
232
  - !ruby/object:Gem::Dependency
213
233
  name: rspec
214
234
  requirement: !ruby/object:Gem::Requirement
@@ -230,9 +250,11 @@ executables:
230
250
  extensions: []
231
251
  extra_rdoc_files: []
232
252
  files:
253
+ - ".git-blame-ignore-revs"
233
254
  - ".github/probots.yml"
234
255
  - ".github/workflows/ci.yml"
235
256
  - ".github/workflows/docs.yml"
257
+ - ".github/workflows/notify-example-app.yml"
236
258
  - ".github/workflows/release.yml"
237
259
  - ".gitignore"
238
260
  - ".npmrc"
@@ -255,52 +277,6 @@ files:
255
277
  - bin/console
256
278
  - bin/rspec
257
279
  - bin/setup
258
- - docs/concepts/backends-and-models.md
259
- - docs/concepts/endpoints.md
260
- - docs/concepts/links.md
261
- - docs/concepts/overview.md
262
- - docs/concepts/persisting.md
263
- - docs/concepts/relationships.md
264
- - docs/concepts/resources.md
265
- - docs/getting-started/first-api.md
266
- - docs/getting-started/installation.md
267
- - docs/intro.md
268
- - docs/js/authentication.md
269
- - docs/js/ddau.md
270
- - docs/js/extra-params.md
271
- - docs/js/index.md
272
- - docs/js/installation.md
273
- - docs/js/middleware.md
274
- - docs/js/models.md
275
- - docs/js/reads.md
276
- - docs/js/state-syncing.md
277
- - docs/js/writes.md
278
- - docs/reference/vandal.md
279
- - docs/reference/why.md
280
- - docs/topics/authorization.md
281
- - docs/topics/caching.md
282
- - docs/topics/customizing-sideloads.md
283
- - docs/topics/debugging.md
284
- - docs/topics/error-handling.md
285
- - docs/topics/etags.md
286
- - docs/topics/hopping-relationships.md
287
- - docs/topics/json-attributes.md
288
- - docs/topics/openstruct-models.md
289
- - docs/topics/remote-resources.md
290
- - docs/topics/testing.md
291
- - docs/topics/without-activerecord.md
292
- - docs/tutorial/index.md
293
- - docs/tutorial/step_0.md
294
- - docs/tutorial/step_1.md
295
- - docs/tutorial/step_2.md
296
- - docs/tutorial/step_3.md
297
- - docs/tutorial/step_4.md
298
- - docs/tutorial/step_5.md
299
- - docs/tutorial/step_6.md
300
- - docs/tutorial/step_7.md
301
- - docs/tutorial/step_8.md
302
- - docs/tutorial/step_9.md
303
- - docs/upgrading.md
304
280
  - exe/graphiti
305
281
  - gemfiles/.bundle/config
306
282
  - gemfiles/rails_7_1.gemfile
@@ -434,148 +410,6 @@ files:
434
410
  - lib/tasks/graphiti.rake
435
411
  - package-lock.json
436
412
  - package.json
437
- - website/.gitignore
438
- - website/README.md
439
- - website/docusaurus.config.js
440
- - website/package-lock.json
441
- - website/package.json
442
- - website/sidebars.js
443
- - website/src/css/custom.css
444
- - website/src/pages/markdown-page.mdx
445
- - website/static/.nojekyll
446
- - website/static/1.13/2019/03/31/graphiti-1-0.html
447
- - website/static/1.13/2019/05/08/graphiti-1-1.html
448
- - website/static/1.13/2019/05/20/graphiti-1-2.html
449
- - website/static/1.13/2019/10/14/tutorial.html
450
- - website/static/1.13/CNAME
451
- - website/static/1.13/README.md
452
- - website/static/1.13/assets/css/syntax.css
453
- - website/static/1.13/assets/favicons/android-chrome-192x192.png
454
- - website/static/1.13/assets/favicons/android-chrome-256x256.png
455
- - website/static/1.13/assets/favicons/apple-touch-icon.png
456
- - website/static/1.13/assets/favicons/browserconfig.xml
457
- - website/static/1.13/assets/favicons/favicon-16x16.png
458
- - website/static/1.13/assets/favicons/favicon-32x32.png
459
- - website/static/1.13/assets/favicons/favicon.ico
460
- - website/static/1.13/assets/favicons/mstile-150x150.png
461
- - website/static/1.13/assets/favicons/safari-pinned-tab.svg
462
- - website/static/1.13/assets/favicons/site.webmanifest
463
- - website/static/1.13/assets/img/backend.gif
464
- - website/static/1.13/assets/img/conformity.png
465
- - website/static/1.13/assets/img/error_payload.png
466
- - website/static/1.13/assets/img/fancy-cushion.png
467
- - website/static/1.13/assets/img/gh.png
468
- - website/static/1.13/assets/img/home-bg.jpg
469
- - website/static/1.13/assets/img/lifecycle.gif
470
- - website/static/1.13/assets/img/logo-500.png
471
- - website/static/1.13/assets/img/logo.png
472
- - website/static/1.13/assets/img/love-graffiti.jpg
473
- - website/static/1.13/assets/img/meta_total_count.png
474
- - website/static/1.13/assets/img/persist.jpg
475
- - website/static/1.13/assets/img/resource.gif
476
- - website/static/1.13/assets/img/rest-graffiti.jpg
477
- - website/static/1.13/assets/img/rest1.gif
478
- - website/static/1.13/assets/img/rest2.gif
479
- - website/static/1.13/assets/img/rest3.gif
480
- - website/static/1.13/assets/img/rethink-rest-graffiti.jpg
481
- - website/static/1.13/assets/img/sunrise.png
482
- - website/static/1.13/assets/img/why.png
483
- - website/static/1.13/assets/js/highlight.pack.js
484
- - website/static/1.13/assets/main.css
485
- - website/static/1.13/assets/main.css.map
486
- - website/static/1.13/bin/bundle
487
- - website/static/1.13/bin/jekyll
488
- - website/static/1.13/bin/kramdown
489
- - website/static/1.13/bin/listen
490
- - website/static/1.13/bin/rake
491
- - website/static/1.13/bin/rougify
492
- - website/static/1.13/bin/safe_yaml
493
- - website/static/1.13/bin/sass
494
- - website/static/1.13/bin/sass-convert
495
- - website/static/1.13/bin/scss
496
- - website/static/1.13/blog.html
497
- - website/static/1.13/cheatsheet.html
498
- - website/static/1.13/cookbooks/authorization.md
499
- - website/static/1.13/cookbooks/caching.md
500
- - website/static/1.13/cookbooks/customizing-sideloads.html
501
- - website/static/1.13/cookbooks/etags.md
502
- - website/static/1.13/cookbooks/hopping-relationships.html
503
- - website/static/1.13/cookbooks/json_attributes.md
504
- - website/static/1.13/cookbooks/openstruct-models.md
505
- - website/static/1.13/cookbooks/remote-resources.md
506
- - website/static/1.13/cookbooks/without-activerecord.html
507
- - website/static/1.13/features.html
508
- - website/static/1.13/feed.xml
509
- - website/static/1.13/guides/concepts/backends-and-models.html
510
- - website/static/1.13/guides/concepts/debugging.html
511
- - website/static/1.13/guides/concepts/endpoints.html
512
- - website/static/1.13/guides/concepts/error-handling.html
513
- - website/static/1.13/guides/concepts/links.html
514
- - website/static/1.13/guides/concepts/remote-resources.html
515
- - website/static/1.13/guides/concepts/resources.html
516
- - website/static/1.13/guides/concepts/testing.html
517
- - website/static/1.13/guides/getting-started/installation.html
518
- - website/static/1.13/guides/graphiti-rails-migration.html
519
- - website/static/1.13/guides/index.html
520
- - website/static/1.13/guides/overview.html
521
- - website/static/1.13/guides/upgrading-2-0.html
522
- - website/static/1.13/guides/upgrading.html
523
- - website/static/1.13/guides/vandal.html
524
- - website/static/1.13/guides/why.html
525
- - website/static/1.13/index.html
526
- - website/static/1.13/js/authentication.html
527
- - website/static/1.13/js/ddau.html
528
- - website/static/1.13/js/extra-params.html
529
- - website/static/1.13/js/index.html
530
- - website/static/1.13/js/installation.html
531
- - website/static/1.13/js/introduction.html
532
- - website/static/1.13/js/middleware.html
533
- - website/static/1.13/js/reads/fieldsets.html
534
- - website/static/1.13/js/reads/filtering.html
535
- - website/static/1.13/js/reads/includes.html
536
- - website/static/1.13/js/reads/index.html
537
- - website/static/1.13/js/reads/nested-queries.html
538
- - website/static/1.13/js/reads/pagination.html
539
- - website/static/1.13/js/reads/sorting.html
540
- - website/static/1.13/js/reads/statistics.html
541
- - website/static/1.13/js/state-syncing.html
542
- - website/static/1.13/js/writes/deferred.html
543
- - website/static/1.13/js/writes/dirty-tracking.html
544
- - website/static/1.13/js/writes/index.html
545
- - website/static/1.13/js/writes/nested.html
546
- - website/static/1.13/js/writes/validations.html
547
- - website/static/1.13/quickstart.html
548
- - website/static/1.13/template
549
- - website/static/1.13/tutorial/index.html
550
- - website/static/1.13/tutorial/step_0.html
551
- - website/static/1.13/tutorial/step_1.html
552
- - website/static/1.13/tutorial/step_2.html
553
- - website/static/1.13/tutorial/step_3.html
554
- - website/static/1.13/tutorial/step_4.html
555
- - website/static/1.13/tutorial/step_5.html
556
- - website/static/1.13/tutorial/step_6.html
557
- - website/static/1.13/tutorial/step_7.html
558
- - website/static/1.13/tutorial/step_8.html
559
- - website/static/1.13/tutorial/step_9.html
560
- - website/static/CNAME
561
- - website/static/assets/img/error_payload.png
562
- - website/static/assets/img/legacy/legacy-0378a3bb39.png
563
- - website/static/assets/img/legacy/legacy-05bbd3e5fd.png
564
- - website/static/assets/img/legacy/legacy-07aa104495.png
565
- - website/static/assets/img/legacy/legacy-0c75a16b3a.gif
566
- - website/static/assets/img/legacy/legacy-3076df6209.png
567
- - website/static/assets/img/legacy/legacy-7f6889bc89.png
568
- - website/static/assets/img/legacy/legacy-a2cc4363c3.png
569
- - website/static/assets/img/legacy/legacy-f67cfa89ab.png
570
- - website/static/assets/img/meta_total_count.png
571
- - website/static/img/docusaurus-social-card.jpg
572
- - website/static/img/docusaurus.png
573
- - website/static/img/favicon.ico
574
- - website/static/img/logo.png
575
- - website/static/img/logo.svg
576
- - website/static/img/undraw_docusaurus_mountain.svg
577
- - website/static/img/undraw_docusaurus_react.svg
578
- - website/static/img/undraw_docusaurus_tree.svg
579
413
  homepage: https://github.com/graphiti-api/graphiti
580
414
  licenses:
581
415
  - MIT
@@ -1,122 +0,0 @@
1
- ---
2
- title: 'Backends and Models'
3
- ---
4
-
5
- # Backends and Models
6
-
7
- A Resource queries a **Backend** and returns **Models** from what comes back. Graphiti serializes the Models.
8
-
9
- With ActiveRecord those are the same object. `Employee` is both the thing you query and the thing you render, and you can skip most of this page. It matters when they're separate: a search index, an HTTP service, a document store. Then the Backend is whatever you query, and the Model is whatever you hand back.
10
-
11
- ## Scopes {#scopes}
12
-
13
- A **scope** is whatever your backend needs to run a query. Graphiti doesn't care what it is. For ActiveRecord it's an `ActiveRecord::Relation`. Here it's a plain hash:
14
-
15
- ```ruby
16
- class EmployeeResource < ApplicationResource
17
- self.adapter = Graphiti::Adapters::Null
18
-
19
- attribute :name, :string
20
-
21
- def base_scope
22
- { conditions: {}, sort: {} }
23
- end
24
-
25
- filter :name do
26
- eq do |scope, value|
27
- scope[:conditions].merge!(value)
28
- scope
29
- end
30
- end
31
-
32
- sort :name do |scope, direction|
33
- scope[:sort] = { name: direction }
34
- scope
35
- end
36
-
37
- def resolve(scope)
38
- results = Backend.query(scope)
39
- results.map { |result| Employee.new(result) }
40
- end
41
- end
42
- ```
43
-
44
- `base_scope` is the starting point, each `filter` and `sort` block mutates it based on request params, and `resolve` runs the query and returns Models.
45
-
46
- **Every block must return the scope.** Returning the result of `merge!` or an assignment instead of the scope itself is the most common way to break this.
47
-
48
- Writing that per Resource gets old. Once the pattern stabilizes, move it into an [Adapter](/topics/without-activerecord#adapters) and Resources go back to being declarative:
49
-
50
- ```ruby
51
- class EmployeeResource < ApplicationResource
52
- self.adapter = BackendAdapter
53
- attribute :name, :string
54
- end
55
- ```
56
-
57
- ## What a Model has to do {#model-requirements}
58
-
59
- **Respond to `id`, uniquely.** Graphiti uses `model.id` to tell records apart when rendering. Duplicate ids produce wrong output, not an error.
60
-
61
- If the underlying record has no id, generate one:
62
-
63
- ```ruby
64
- def id
65
- @id ||= SecureRandom.uuid
66
- end
67
- ```
68
-
69
- **Respond to its readable attributes.** `attribute :name, :string` calls `model.name`. If your Model doesn't have that method, pass a block instead:
70
-
71
- ```ruby
72
- attribute :name, :string do
73
- @object.full_name
74
- end
75
- ```
76
-
77
- **Include `ActiveModel::Validations` if you want validation errors.** Graphiti checks models on write requests and renders a [JSON:API errors payload](http://jsonapi.org/format/#errors) from `model.errors`. Without it, an invalid model saves silently:
78
-
79
- ```ruby
80
- class Employee
81
- include ActiveModel::Validations
82
-
83
- validates :name, presence: true
84
- end
85
- ```
86
-
87
- ## Writing a Model {#model-implementations}
88
-
89
- Graphiti has no opinion here. A plain class works:
90
-
91
- ```ruby
92
- class Employee
93
- attr_accessor :id, :first_name, :last_name, :age
94
-
95
- def initialize(attrs = {})
96
- attrs.each_pair { |key, value| send(:"#{key}=", value) }
97
- end
98
- end
99
- ```
100
-
101
- [ActiveModel::Model](https://api.rubyonrails.org/classes/ActiveModel/Model.html) gives you the constructor and validations for free:
102
-
103
- ```ruby
104
- class Employee
105
- include ActiveModel::Model
106
-
107
- attr_accessor :id, :first_name, :last_name, :age
108
- end
109
- ```
110
-
111
- [Dry::Struct](https://dry-rb.org/gems/dry-struct) adds type enforcement, and dry-types is already a Graphiti dependency:
112
-
113
- ```ruby
114
- class Employee < Dry::Struct
115
- attribute :id, Types::Integer
116
- attribute :first_name, Types::String
117
- attribute :last_name, Types::String
118
- attribute :age, Types::Integer
119
- end
120
- ```
121
-
122
- `OpenStruct` also works and is what Graphiti uses internally for remote resources, but it fails quietly in ways the others don't. See [OpenStruct Models](/topics/openstruct-models) before reaching for it.