graphiform 0.2.9 → 0.2.13

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 841f7ddfa6fd9f56cda166df64e77ec0d94fac684c872ce44b5fc024afe3d433
4
- data.tar.gz: 10c339a1f949bf05dfa2dcf94a8bf3332f3a1c468699c605ab8ac60966644fd0
3
+ metadata.gz: 24d0d05956c47d3c131958d9b273951c53d313f28dd622a5f841724ce18e1a4a
4
+ data.tar.gz: 82bd9b1ba24f07a5abc4cb164bd64ea8d217409d434613b543a2441edf5ba202
5
5
  SHA512:
6
- metadata.gz: e72009f747cf7bf8b6655940eb443900cf2c08e419cb25e1786aafc1b36347fb0f6916bc7d837a82681e46280a6bc5b6fe617b80ba081556f7acf319f01ed40b
7
- data.tar.gz: d13b40bf891ccad27f83e7a23c55b20b5c94c594bc83d28ee07b762aca7a5740030aeaec20789d2cccc590ca701a566e0257555d5e0c2ee67fe02608e61c25ec
6
+ metadata.gz: b5480858bde4de83a235a691ee1e7ec05f49f7348d44e0e8b3fd6e739ab17211f1f4ea7e0e651cd43edbe7b34b4227c429c7c87d542bea668a406cd679fff9b5
7
+ data.tar.gz: 96d29abd576caf4dfe334f10df9eddf89a32055c9677bcb600dccb07529a24d7531c5a2fb9e5a25595626164e9398bc900a0211b07537269a09472240480416a
data/lib/graphiform.rb CHANGED
@@ -19,4 +19,34 @@ module Graphiform
19
19
  include Graphiform::Fields
20
20
  end
21
21
  end
22
+
23
+ def self.configuration
24
+ @configuration ||= {
25
+ scalar_mappings: {
26
+ string: GraphQL::Types::String,
27
+ text: GraphQL::Types::String,
28
+ # nchar: GraphQL::Types::String,
29
+
30
+ date: GraphQL::Types::ISO8601Date,
31
+
32
+ time: GraphQL::Types::ISO8601DateTime,
33
+ datetime: GraphQL::Types::ISO8601DateTime,
34
+ timestamp: GraphQL::Types::ISO8601DateTime,
35
+
36
+ integer: GraphQL::Types::Int,
37
+
38
+ float: GraphQL::Types::Float,
39
+ decimal: GraphQL::Types::Float,
40
+
41
+ boolean: GraphQL::Types::Boolean,
42
+
43
+ json: GraphQL::Types::JSON,
44
+ jsonb: GraphQL::Types::JSON,
45
+ },
46
+ }
47
+ end
48
+
49
+ def self.configure
50
+ yield(configuration)
51
+ end
22
52
  end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Graphiform
4
+ class AssociationSource < GraphQL::Dataloader::Source
5
+ def initialize(model, attribute, **options)
6
+ super()
7
+
8
+ @model = model
9
+ @attribute = attribute
10
+ @options = options
11
+ end
12
+
13
+ def fetch(values)
14
+ normalized_values = normalize_values(values)
15
+ records = query(normalized_values.uniq).to_a
16
+ results(normalized_values, records)
17
+ end
18
+
19
+ def query(values)
20
+ query = @model
21
+ query = query.merge(@model.instance_exec(&@options[:scope])) if @options[:scope].present?
22
+ query = query.where(@attribute => values)
23
+
24
+ query = query.includes(@options[:includes]) if @options[:includes].present? && query.respond_to?(:includes)
25
+ query = query.apply_filters(@options[:where].to_h) if @options[:where].present? && query.respond_to?(:apply_filters)
26
+ query = query.apply_sorts(@options[:sort].to_h) if @options[:sort].present? && query.respond_to?(:apply_sorts)
27
+
28
+ query
29
+ end
30
+
31
+ def normalize_value(value)
32
+ value = value.downcase if !@options[:case_sensitive] && value.is_a?(String)
33
+
34
+ value
35
+ end
36
+
37
+ def normalize_values(values)
38
+ type_for_attribute = @model.type_for_attribute(@attribute) if @model.respond_to?(:type_for_attribute)
39
+ values.map do |value|
40
+ value = type_for_attribute.cast(value) if type_for_attribute.present?
41
+ normalize_value(value)
42
+ end
43
+ end
44
+
45
+ def results(values, records)
46
+ record_attributes = records.map { |record| normalize_value(record[@attribute]) }
47
+ values.map do |value|
48
+ if @options[:multi]
49
+ indexes = record_attributes.each_index.select { |index| record_attributes[index] == value }
50
+ indexes.map { |index| index && records[index] }
51
+ else
52
+ index = record_attributes.index(value)
53
+ index && records[index]
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -3,6 +3,7 @@
3
3
  require 'active_support/concern'
4
4
 
5
5
  require 'graphiform/helpers'
6
+ require 'graphiform/association_source'
6
7
 
7
8
  module Graphiform
8
9
  module Core
@@ -37,6 +38,7 @@ module Graphiform
37
38
  end
38
39
  @filter.class_eval do
39
40
  argument 'OR', [self], required: false
41
+ argument 'AND', [self], required: false
40
42
  end
41
43
  end
42
44
 
@@ -153,7 +155,7 @@ module Graphiform
153
155
  def base_resolve(**args)
154
156
  @value = model.all
155
157
  apply_built_ins(**args)
156
- @value.first
158
+ @value.take
157
159
  end
158
160
  end
159
161
  end
@@ -173,18 +175,62 @@ module Graphiform
173
175
  end
174
176
  end
175
177
 
176
- def graphql_create_resolver(method_name, resolver_type = graphql_type, read_prepare: nil, read_resolve: nil, null: true, **)
178
+ def graphql_create_resolver(method_name, resolver_type = graphql_type, read_prepare: nil, read_resolve: nil, null: true, skip_dataloader: false, case_sensitive: Graphiform.configuration[:case_sensitive], **)
177
179
  Class.new(graphql_base_resolver) do
178
180
  type resolver_type, null: null
179
181
 
180
182
  define_method :base_resolve do |**args|
181
183
  @value = object
182
184
 
183
- @value = instance_exec(@value, context, &read_resolve) if read_resolve
184
- @value = @value.public_send(method_name) if !read_resolve && @value.respond_to?(method_name)
185
- @value = instance_exec(@value, context, &read_prepare) if read_prepare
185
+ association_def = @value.association(method_name)&.reflection
186
+ join_keys = association_def&.join_keys
186
187
 
187
- apply_built_ins(**args)
188
+ skip_dataloader ||=
189
+ !association_def ||
190
+ !Helpers.dataloader_support?(dataloader, association_def, join_keys.foreign_key) ||
191
+ read_resolve ||
192
+ read_prepare ||
193
+ args[:group]
194
+
195
+ if skip_dataloader
196
+ @value = instance_exec(@value, context, &read_resolve) if read_resolve
197
+ @value = @value.public_send(method_name) if !read_resolve && @value.respond_to?(method_name)
198
+ @value = instance_exec(@value, context, &read_prepare) if read_prepare
199
+
200
+ apply_built_ins(**args)
201
+ else
202
+ dataloader
203
+ .with(
204
+ AssociationSource,
205
+ association_def.klass,
206
+ join_keys.key,
207
+ scope: association_def.scope,
208
+ where: args[:where],
209
+ sort: args[:sort],
210
+ multi: true,
211
+ case_sensitive: case_sensitive,
212
+ )
213
+ .load(
214
+ @value.public_send(join_keys.foreign_key)
215
+ )
216
+ end
217
+ end
218
+ end
219
+ end
220
+
221
+ def graphql_create_association_resolver(association_def, resolver_type, null: true, skip_dataloader: false, case_sensitive: nil, **)
222
+ Class.new(::Resolvers::BaseResolver) do
223
+ type resolver_type, null: null
224
+
225
+ define_method :resolve do |*|
226
+ join_keys = association_def.join_keys
227
+
228
+ skip_dataloader ||= !Helpers.dataloader_support?(dataloader, association_def, join_keys.foreign_key)
229
+
230
+ return object.public_send(association_def.name) if skip_dataloader
231
+
232
+ value = object.public_send(join_keys.foreign_key)
233
+ dataloader.with(AssociationSource, association_def.klass, join_keys.key, scope: association_def.scope, case_sensitive: case_sensitive).load(value)
188
234
  end
189
235
  end
190
236
  end
@@ -12,16 +12,17 @@ module Graphiform
12
12
  as: nil,
13
13
  **options
14
14
  )
15
- column_def = column(as || name)
16
- association_def = association(as || name)
15
+ identifier = as || name
16
+ column_def = column(identifier)
17
+ association_def = association(identifier)
17
18
 
18
19
  graphql_add_column_field(name, column_def, as: as, **options) if column_def.present?
19
20
  graphql_add_association_field(name, association_def, as: as, **options) if association_def.present?
20
21
  graphql_add_method_field(name, as: as, **options) unless column_def.present? || association_def.present?
21
22
 
22
- graphql_add_scopes_to_filter(name, as || name)
23
- graphql_field_to_sort(name, as || name)
24
- graphql_field_to_grouping(name, as || name)
23
+ graphql_add_scopes_to_filter(name, identifier)
24
+ graphql_field_to_sort(name, identifier)
25
+ graphql_field_to_grouping(name, identifier)
25
26
  end
26
27
 
27
28
  def graphql_writable_field(
@@ -266,6 +267,8 @@ module Graphiform
266
267
  include_connection: true,
267
268
  read_prepare: nil,
268
269
  read_resolve: nil,
270
+ skip_dataloader: false,
271
+ case_sensitive: Graphiform.configuration[:case_sensitive],
269
272
  **options
270
273
  )
271
274
  unless association_def.klass.respond_to?(:graphql_type)
@@ -291,7 +294,9 @@ module Graphiform
291
294
  klass.graphql_connection,
292
295
  read_prepare: read_prepare,
293
296
  read_resolve: read_resolve,
294
- null: false
297
+ null: false,
298
+ skip_dataloader: true,
299
+ case_sensitive: case_sensitive
295
300
  ),
296
301
  false,
297
302
  **options
@@ -306,10 +311,17 @@ module Graphiform
306
311
  [klass.graphql_type],
307
312
  read_prepare: read_prepare,
308
313
  read_resolve: read_resolve,
309
- null: false
314
+ null: false,
315
+ skip_dataloader: skip_dataloader,
316
+ case_sensitive: case_sensitive
310
317
  )
311
318
  else
312
- klass.graphql_type
319
+ klass.graphql_create_association_resolver(
320
+ association_def,
321
+ klass.graphql_type,
322
+ skip_dataloader: skip_dataloader,
323
+ case_sensitive: case_sensitive
324
+ )
313
325
  end
314
326
  )
315
327
 
@@ -5,7 +5,7 @@ module Graphiform
5
5
  def self.logger
6
6
  return Rails.logger if Rails.logger.present?
7
7
 
8
- @logger ||= Logger.new(STDOUT)
8
+ @logger ||= Logger.new($stdout)
9
9
  @logger
10
10
  end
11
11
 
@@ -19,22 +19,7 @@ module Graphiform
19
19
  def self.graphql_type_single(active_record_type)
20
20
  return active_record_type unless active_record_type.respond_to?(:to_sym)
21
21
 
22
- case active_record_type.to_sym
23
- when :string, :text
24
- GraphQL::Types::String
25
- when :date
26
- GraphQL::Types::ISO8601Date
27
- when :time, :datetime, :timestamp
28
- GraphQL::Types::ISO8601DateTime
29
- when :integer
30
- GraphQL::Types::Int
31
- when :float, :decimal
32
- GraphQL::Types::Float
33
- when :boolean
34
- GraphQL::Types::Boolean
35
- else
36
- active_record_type
37
- end
22
+ Graphiform.configuration[:scalar_mappings][active_record_type.to_sym] || active_record_type
38
23
  end
39
24
 
40
25
  def self.resolver?(val)
@@ -70,5 +55,19 @@ module Graphiform
70
55
  association_def.klass.send(method).respond_to?(:arguments) &&
71
56
  !association_def.klass.send(method).arguments.empty?
72
57
  end
58
+
59
+ def self.dataloader_support?(dataloader, association_def, keys)
60
+ (
61
+ association_def.present? &&
62
+ !association_def.polymorphic? &&
63
+ !association_def.through_reflection? &&
64
+ (
65
+ !association_def.scope ||
66
+ association_def.scope.arity.zero?
67
+ ) &&
68
+ !keys.is_a?(Array) &&
69
+ !dataloader.is_a?(GraphQL::Dataloader::NullDataloader)
70
+ )
71
+ end
73
72
  end
74
73
  end
@@ -1,3 +1,3 @@
1
1
  module Graphiform
2
- VERSION = '0.2.9'.freeze
2
+ VERSION = '0.2.13'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphiform
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.9
4
+ version: 0.2.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - jayce.pulsipher
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-24 00:00:00.000000000 Z
11
+ date: 2021-08-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.2.7
47
+ version: 0.2.9
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.2.7
54
+ version: 0.2.9
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: appraisal
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -120,6 +120,7 @@ files:
120
120
  - Rakefile
121
121
  - lib/graphiform.rb
122
122
  - lib/graphiform/active_record_helpers.rb
123
+ - lib/graphiform/association_source.rb
123
124
  - lib/graphiform/core.rb
124
125
  - lib/graphiform/fields.rb
125
126
  - lib/graphiform/helpers.rb