active_shopify_graphql 0.3.0 → 0.4.0
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 +4 -4
- data/.rubocop.yml +1 -0
- data/README.md +29 -0
- data/lib/active_shopify_graphql/associations.rb +8 -4
- data/lib/active_shopify_graphql/finder_methods.rb +31 -8
- data/lib/active_shopify_graphql/graphql_associations.rb +245 -0
- data/lib/active_shopify_graphql/version.rb +1 -1
- data/lib/active_shopify_graphql.rb +2 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f0b5f3a4ce7ed92feb8483e2c064379788138d7a7336daa849a81715fd0dd3f2
|
|
4
|
+
data.tar.gz: b48985b14a5e07a5e5273431d82c06756dfc7feab40ddd27f65fa313c080d261
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5f7cd6debcc40234ea46da5b8e88332a6c715db5d78ef82040ec1827a963557b60589f70df2261d09f6b08e699233b2aad7eaf00eb20eb6a2f80eb35608d6258
|
|
7
|
+
data.tar.gz: e3d0c95ba71e3be17f0a57ba26cd6f25d7c6ea783dc4b0608609f6344bf783ef2aec0e5b532b83d1a90084c3e3389bc8018396911a02c2da06d9f2c523b33c66
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
|
@@ -323,6 +323,35 @@ end
|
|
|
323
323
|
|
|
324
324
|
The associations automatically handle Shopify GID format conversion, extracting numeric IDs when needed for querying related records.
|
|
325
325
|
|
|
326
|
+
## Bridging ActiveRecord with GraphQL
|
|
327
|
+
|
|
328
|
+
The `GraphQLAssociations` module allows ActiveRecord models (or duck-typed objects) to define associations to Shopify GraphQL models:
|
|
329
|
+
|
|
330
|
+
```ruby
|
|
331
|
+
class Reward < ApplicationRecord
|
|
332
|
+
include ActiveShopifyGraphQL::GraphQLAssociations
|
|
333
|
+
|
|
334
|
+
belongs_to_graphql :customer # Expects shopify_customer_id column
|
|
335
|
+
has_one_graphql :primary_address,
|
|
336
|
+
class_name: "Address",
|
|
337
|
+
foreign_key: :customer_id
|
|
338
|
+
has_many_graphql :variants,
|
|
339
|
+
class_name: "ProductVariant"
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
reward = Reward.find(1)
|
|
343
|
+
reward.customer # Loads Customer from shopify_customer_id
|
|
344
|
+
reward.primary_address # Queries Address.where(customer_id: reward.shopify_customer_id).first
|
|
345
|
+
reward.variants # Queries ProductVariant.where({})
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
**Available associations:**
|
|
349
|
+
- `belongs_to_graphql` - Loads single GraphQL object via stored GID/ID
|
|
350
|
+
- `has_one_graphql` - Queries first GraphQL object matching foreign key
|
|
351
|
+
- `has_many_graphql` - Queries multiple GraphQL objects with optional filtering
|
|
352
|
+
|
|
353
|
+
All associations support `class_name`, `foreign_key`, `primary_key`, and `loader_class` options. Results are automatically cached and setter methods are provided for testing.
|
|
354
|
+
|
|
326
355
|
## GraphQL Connections
|
|
327
356
|
|
|
328
357
|
ActiveShopifyGraphQL supports GraphQL connections for loading related data from Shopify APIs. Connections provide both lazy and eager loading patterns.
|
|
@@ -36,9 +36,6 @@ module ActiveShopifyGraphQL
|
|
|
36
36
|
primary_key_value = send(association_primary_key)
|
|
37
37
|
return @_association_cache[name] = [] if primary_key_value.blank?
|
|
38
38
|
|
|
39
|
-
# Extract numeric ID from Shopify GID if needed
|
|
40
|
-
primary_key_value = primary_key_value.to_plain_id if primary_key_value.gid?
|
|
41
|
-
|
|
42
39
|
association_class = association_class_name.constantize
|
|
43
40
|
@_association_cache[name] = association_class.where(association_foreign_key => primary_key_value)
|
|
44
41
|
end
|
|
@@ -73,7 +70,14 @@ module ActiveShopifyGraphQL
|
|
|
73
70
|
return @_association_cache[name] = nil if primary_key_value.blank?
|
|
74
71
|
|
|
75
72
|
# Extract numeric ID from Shopify GID if needed
|
|
76
|
-
|
|
73
|
+
if primary_key_value.is_a?(String)
|
|
74
|
+
begin
|
|
75
|
+
parsed_gid = URI::GID.parse(primary_key_value)
|
|
76
|
+
primary_key_value = parsed_gid.model_id
|
|
77
|
+
rescue URI::InvalidURIError, URI::BadURIError, ArgumentError
|
|
78
|
+
# Not a GID, use value as-is
|
|
79
|
+
end
|
|
80
|
+
end
|
|
77
81
|
|
|
78
82
|
association_class = association_class_name.constantize
|
|
79
83
|
@_association_cache[name] = association_class.find_by(association_foreign_key => primary_key_value)
|
|
@@ -8,19 +8,23 @@ module ActiveShopifyGraphQL
|
|
|
8
8
|
# Find a single record by ID using the provided loader
|
|
9
9
|
# @param id [String, Integer] The record ID (will be converted to GID automatically)
|
|
10
10
|
# @param loader [ActiveShopifyGraphQL::Loader] The loader to use for fetching data
|
|
11
|
-
# @return [Object
|
|
11
|
+
# @return [Object] The model instance
|
|
12
|
+
# @raise [ActiveShopifyGraphQL::ObjectNotFoundError] If the record is not found
|
|
12
13
|
def find(id, loader: default_loader)
|
|
13
14
|
gid = GidHelper.normalize_gid(id, model_name.name.demodulize)
|
|
14
15
|
|
|
15
16
|
# If we have included connections, we need to handle inverse_of properly
|
|
16
|
-
|
|
17
|
-
loader.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
result =
|
|
18
|
+
if loader.has_included_connections?
|
|
19
|
+
loader.load_with_instance(gid, self)
|
|
20
|
+
else
|
|
21
|
+
attributes = loader.load_attributes(gid)
|
|
22
|
+
attributes.nil? ? nil : new(attributes)
|
|
23
|
+
end
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
raise ObjectNotFoundError, "Couldn't find #{name} with id=#{id}" if result.nil?
|
|
26
|
+
|
|
27
|
+
result
|
|
24
28
|
end
|
|
25
29
|
|
|
26
30
|
# Returns the default loader for this model's queries
|
|
@@ -77,6 +81,25 @@ module ActiveShopifyGraphQL
|
|
|
77
81
|
selected_class
|
|
78
82
|
end
|
|
79
83
|
|
|
84
|
+
# Find a single record by attribute conditions
|
|
85
|
+
# @param conditions [Hash] The conditions to query (e.g., { email: "example@test.com", first_name: "John" })
|
|
86
|
+
# @param options [Hash] Options hash containing loader
|
|
87
|
+
# @option options [ActiveShopifyGraphQL::Loader] :loader The loader to use for fetching data
|
|
88
|
+
# @return [Object, nil] The first matching model instance or nil if not found
|
|
89
|
+
# @raise [ArgumentError] If any attribute is not valid for querying
|
|
90
|
+
#
|
|
91
|
+
# @example
|
|
92
|
+
# # Keyword argument style (recommended)
|
|
93
|
+
# Customer.find_by(email: "john@example.com")
|
|
94
|
+
# Customer.find_by(first_name: "John", country: "Canada")
|
|
95
|
+
# Customer.find_by(orders_count: { gte: 5 })
|
|
96
|
+
#
|
|
97
|
+
# # Hash style with options
|
|
98
|
+
# Customer.find_by({ email: "john@example.com" }, loader: custom_loader)
|
|
99
|
+
def find_by(conditions_or_first_condition = {}, *args, **options)
|
|
100
|
+
where(conditions_or_first_condition, *args, **options.merge(limit: 1)).first
|
|
101
|
+
end
|
|
102
|
+
|
|
80
103
|
# Query for multiple records using attribute conditions
|
|
81
104
|
# @param conditions [Hash] The conditions to query (e.g., { email: "example@test.com", first_name: "John" })
|
|
82
105
|
# @param options [Hash] Options hash containing loader and limit (when first arg is a Hash)
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveShopifyGraphQL
|
|
4
|
+
# Allows ActiveRecord (or duck-typed) objects to define associations to GraphQL objects
|
|
5
|
+
# This module bridges the gap between local database records and remote Shopify GraphQL data
|
|
6
|
+
#
|
|
7
|
+
# @example
|
|
8
|
+
# class Reward < ApplicationRecord
|
|
9
|
+
# include ActiveShopifyGraphQL::GraphQLAssociations
|
|
10
|
+
#
|
|
11
|
+
# belongs_to_graphql :customer
|
|
12
|
+
# has_many_graphql :variants, class: "ProductVariant", query_name: "productVariants"
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# reward = Reward.first
|
|
16
|
+
# customer = reward.customer # => ActiveShopifyGraphQL::Customer instance
|
|
17
|
+
# variants = reward.variants(first: 10) # => Array of ProductVariant instances
|
|
18
|
+
module GraphQLAssociations
|
|
19
|
+
extend ActiveSupport::Concern
|
|
20
|
+
|
|
21
|
+
included do
|
|
22
|
+
class << self
|
|
23
|
+
attr_accessor :graphql_associations
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
self.graphql_associations = {}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class_methods do
|
|
30
|
+
# Define a belongs_to relationship with a GraphQL object
|
|
31
|
+
# Fetches a single GraphQL object using a stored GID or foreign key
|
|
32
|
+
#
|
|
33
|
+
# @param name [Symbol] The association name (e.g., :customer)
|
|
34
|
+
# @param class_name [String] The target GraphQL model class name (defaults to name.to_s.classify)
|
|
35
|
+
# @param foreign_key [Symbol, String] The attribute/column storing the GID or ID (defaults to "shopify_#{name}_id")
|
|
36
|
+
# @param loader_class [Class] Custom loader class (defaults to target class's default_loader)
|
|
37
|
+
#
|
|
38
|
+
# @example Basic usage
|
|
39
|
+
# belongs_to_graphql :customer
|
|
40
|
+
# # Expects: shopify_customer_id column with GID like "gid://shopify/Customer/123"
|
|
41
|
+
#
|
|
42
|
+
# @example With custom foreign key
|
|
43
|
+
# belongs_to_graphql :customer, foreign_key: :customer_gid
|
|
44
|
+
#
|
|
45
|
+
# @example With custom class
|
|
46
|
+
# belongs_to_graphql :owner, class_name: "Customer"
|
|
47
|
+
def belongs_to_graphql(name, class_name: nil, foreign_key: nil, loader_class: nil)
|
|
48
|
+
association_class_name = class_name || name.to_s.classify
|
|
49
|
+
association_foreign_key = foreign_key || "shopify_#{name}_id"
|
|
50
|
+
|
|
51
|
+
# Store association metadata
|
|
52
|
+
graphql_associations[name] = {
|
|
53
|
+
type: :belongs_to,
|
|
54
|
+
class_name: association_class_name,
|
|
55
|
+
foreign_key: association_foreign_key,
|
|
56
|
+
loader_class: loader_class
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# Define the association method
|
|
60
|
+
define_method name do
|
|
61
|
+
return @_graphql_association_cache[name] if @_graphql_association_cache&.key?(name)
|
|
62
|
+
|
|
63
|
+
@_graphql_association_cache ||= {}
|
|
64
|
+
|
|
65
|
+
# Get the GID or ID value from the foreign key
|
|
66
|
+
gid_or_id = send(association_foreign_key)
|
|
67
|
+
return @_graphql_association_cache[name] = nil if gid_or_id.blank?
|
|
68
|
+
|
|
69
|
+
# Resolve the target class
|
|
70
|
+
target_class = association_class_name.constantize
|
|
71
|
+
|
|
72
|
+
# Determine which loader to use
|
|
73
|
+
loader = if self.class.graphql_associations[name][:loader_class]
|
|
74
|
+
self.class.graphql_associations[name][:loader_class].new(target_class)
|
|
75
|
+
else
|
|
76
|
+
target_class.default_loader
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Load and cache the GraphQL object
|
|
80
|
+
@_graphql_association_cache[name] = target_class.find(gid_or_id, loader: loader)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Define setter method for testing/mocking
|
|
84
|
+
define_method "#{name}=" do |value|
|
|
85
|
+
@_graphql_association_cache ||= {}
|
|
86
|
+
@_graphql_association_cache[name] = value
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Define a has_one relationship with a GraphQL object
|
|
91
|
+
# Fetches a single GraphQL object using a where clause
|
|
92
|
+
#
|
|
93
|
+
# @param name [Symbol] The association name (e.g., :primary_address)
|
|
94
|
+
# @param class_name [String] The target GraphQL model class name (defaults to name.to_s.classify)
|
|
95
|
+
# @param foreign_key [Symbol, String] The attribute on GraphQL objects to filter by (e.g., :customer_id)
|
|
96
|
+
# @param primary_key [Symbol, String] The local attribute to use as filter value (defaults to :id)
|
|
97
|
+
# @param loader_class [Class] Custom loader class (defaults to target class's default_loader)
|
|
98
|
+
#
|
|
99
|
+
# @example Basic usage
|
|
100
|
+
# has_one_graphql :primary_address, class_name: "Address", foreign_key: :customer_id
|
|
101
|
+
# customer.primary_address # Returns first Address where customer_id matches
|
|
102
|
+
def has_one_graphql(name, class_name: nil, foreign_key: nil, primary_key: nil, loader_class: nil)
|
|
103
|
+
association_class_name = class_name || name.to_s.classify
|
|
104
|
+
association_primary_key = primary_key || :id
|
|
105
|
+
association_loader_class = loader_class
|
|
106
|
+
|
|
107
|
+
# Store association metadata
|
|
108
|
+
graphql_associations[name] = {
|
|
109
|
+
type: :has_one,
|
|
110
|
+
class_name: association_class_name,
|
|
111
|
+
foreign_key: foreign_key,
|
|
112
|
+
primary_key: association_primary_key,
|
|
113
|
+
loader_class: association_loader_class
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
# Define the association method
|
|
117
|
+
define_method name do
|
|
118
|
+
return @_graphql_association_cache[name] if @_graphql_association_cache&.key?(name)
|
|
119
|
+
|
|
120
|
+
@_graphql_association_cache ||= {}
|
|
121
|
+
|
|
122
|
+
# Get primary key value
|
|
123
|
+
primary_key_value = send(association_primary_key)
|
|
124
|
+
return @_graphql_association_cache[name] = nil if primary_key_value.blank?
|
|
125
|
+
|
|
126
|
+
# Resolve the target class
|
|
127
|
+
target_class = association_class_name.constantize
|
|
128
|
+
|
|
129
|
+
# Determine which loader to use
|
|
130
|
+
loader = if self.class.graphql_associations[name][:loader_class]
|
|
131
|
+
self.class.graphql_associations[name][:loader_class].new(target_class)
|
|
132
|
+
else
|
|
133
|
+
target_class.default_loader
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Query with foreign key filter if provided
|
|
137
|
+
result = if self.class.graphql_associations[name][:foreign_key]
|
|
138
|
+
foreign_key_sym = self.class.graphql_associations[name][:foreign_key]
|
|
139
|
+
query_conditions = { foreign_key_sym => primary_key_value }
|
|
140
|
+
target_class.where(query_conditions, loader: loader).first
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Cache the result
|
|
144
|
+
@_graphql_association_cache[name] = result
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Define setter method for testing/mocking
|
|
148
|
+
define_method "#{name}=" do |value|
|
|
149
|
+
@_graphql_association_cache ||= {}
|
|
150
|
+
@_graphql_association_cache[name] = value
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Define a has_many relationship with GraphQL objects
|
|
155
|
+
# Queries multiple GraphQL objects using a where clause or connection
|
|
156
|
+
#
|
|
157
|
+
# @param name [Symbol] The association name (e.g., :variants)
|
|
158
|
+
# @param class_name [String] The target GraphQL model class name (defaults to name.to_s.classify.singularize)
|
|
159
|
+
# @param query_name [String] The GraphQL query field name (defaults to class_name.pluralize.camelize(:lower))
|
|
160
|
+
# @param foreign_key [Symbol, String] The attribute on GraphQL objects to filter by (e.g., :customer_id)
|
|
161
|
+
# @param primary_key [Symbol, String] The local attribute to use as filter value (defaults to :id)
|
|
162
|
+
# @param loader_class [Class] Custom loader class (defaults to target class's default_loader)
|
|
163
|
+
# @param query_method [Symbol] Method to use for querying (:where or :connection, defaults to :where)
|
|
164
|
+
#
|
|
165
|
+
# @example Basic usage with where query
|
|
166
|
+
# has_many_graphql :variants, class: "ProductVariant"
|
|
167
|
+
# reward.variants # Uses where to query
|
|
168
|
+
#
|
|
169
|
+
# @example With custom query_name for a connection
|
|
170
|
+
# has_many_graphql :line_items, query_name: "lineItems", query_method: :connection
|
|
171
|
+
# order.line_items(first: 10)
|
|
172
|
+
#
|
|
173
|
+
# @example With filtering by foreign key
|
|
174
|
+
# has_many_graphql :orders, foreign_key: :customer_id
|
|
175
|
+
# # Queries orders where customer_id matches the local record's id
|
|
176
|
+
def has_many_graphql(name, class_name: nil, query_name: nil, foreign_key: nil, primary_key: nil, loader_class: nil, query_method: :where)
|
|
177
|
+
association_class_name = class_name || name.to_s.singularize.classify
|
|
178
|
+
association_primary_key = primary_key || :id
|
|
179
|
+
association_loader_class = loader_class
|
|
180
|
+
association_query_method = query_method
|
|
181
|
+
|
|
182
|
+
# Auto-determine query_name if not provided
|
|
183
|
+
association_query_name = query_name || name.to_s.camelize(:lower)
|
|
184
|
+
|
|
185
|
+
# Store association metadata
|
|
186
|
+
graphql_associations[name] = {
|
|
187
|
+
type: :has_many,
|
|
188
|
+
class_name: association_class_name,
|
|
189
|
+
query_name: association_query_name,
|
|
190
|
+
foreign_key: foreign_key,
|
|
191
|
+
primary_key: association_primary_key,
|
|
192
|
+
loader_class: association_loader_class,
|
|
193
|
+
query_method: association_query_method
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
# Define the association method
|
|
197
|
+
define_method name do |**options|
|
|
198
|
+
return @_graphql_association_cache[name] if @_graphql_association_cache&.key?(name) && options.empty?
|
|
199
|
+
|
|
200
|
+
@_graphql_association_cache ||= {}
|
|
201
|
+
|
|
202
|
+
# Get primary key value
|
|
203
|
+
primary_key_value = send(association_primary_key)
|
|
204
|
+
return @_graphql_association_cache[name] = [] if primary_key_value.blank?
|
|
205
|
+
|
|
206
|
+
# Resolve the target class
|
|
207
|
+
target_class = association_class_name.constantize
|
|
208
|
+
|
|
209
|
+
# Determine which loader to use
|
|
210
|
+
loader = if self.class.graphql_associations[name][:loader_class]
|
|
211
|
+
self.class.graphql_associations[name][:loader_class].new(target_class)
|
|
212
|
+
else
|
|
213
|
+
target_class.default_loader
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Build query based on query_method
|
|
217
|
+
result = if association_query_method == :connection
|
|
218
|
+
# For connections, we need to handle this differently
|
|
219
|
+
# This would typically be a nested connection on a parent object
|
|
220
|
+
# For now, return empty array - real implementation would need parent context
|
|
221
|
+
[]
|
|
222
|
+
elsif self.class.graphql_associations[name][:foreign_key]
|
|
223
|
+
# Query with foreign key filter
|
|
224
|
+
foreign_key_sym = self.class.graphql_associations[name][:foreign_key]
|
|
225
|
+
query_conditions = { foreign_key_sym => primary_key_value }.merge(options)
|
|
226
|
+
target_class.where(query_conditions, loader: loader)
|
|
227
|
+
else
|
|
228
|
+
# No foreign key specified, just query with provided options
|
|
229
|
+
target_class.where(options, loader: loader)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Cache if no runtime options provided
|
|
233
|
+
@_graphql_association_cache[name] = result if options.empty?
|
|
234
|
+
result
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
# Define setter method for testing/mocking
|
|
238
|
+
define_method "#{name}=" do |value|
|
|
239
|
+
@_graphql_association_cache ||= {}
|
|
240
|
+
@_graphql_association_cache[name] = value
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
@@ -23,6 +23,7 @@ require_relative "active_shopify_graphql/loaders/customer_account_api_loader"
|
|
|
23
23
|
require_relative "active_shopify_graphql/loader_switchable"
|
|
24
24
|
require_relative "active_shopify_graphql/finder_methods"
|
|
25
25
|
require_relative "active_shopify_graphql/associations"
|
|
26
|
+
require_relative "active_shopify_graphql/graphql_associations"
|
|
26
27
|
require_relative "active_shopify_graphql/includes_scope"
|
|
27
28
|
require_relative "active_shopify_graphql/connections"
|
|
28
29
|
require_relative "active_shopify_graphql/attributes"
|
|
@@ -32,4 +33,5 @@ require_relative "active_shopify_graphql/base"
|
|
|
32
33
|
|
|
33
34
|
module ActiveShopifyGraphQL
|
|
34
35
|
class Error < StandardError; end
|
|
36
|
+
class ObjectNotFoundError < Error; end
|
|
35
37
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_shopify_graphql
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Nicolò Rebughini
|
|
@@ -79,6 +79,7 @@ files:
|
|
|
79
79
|
- lib/active_shopify_graphql/finder_methods.rb
|
|
80
80
|
- lib/active_shopify_graphql/fragment_builder.rb
|
|
81
81
|
- lib/active_shopify_graphql/gid_helper.rb
|
|
82
|
+
- lib/active_shopify_graphql/graphql_associations.rb
|
|
82
83
|
- lib/active_shopify_graphql/graphql_type_resolver.rb
|
|
83
84
|
- lib/active_shopify_graphql/includes_scope.rb
|
|
84
85
|
- lib/active_shopify_graphql/loader.rb
|