graphiform 0.2.8 → 0.2.12

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: 432786d8d7975e7f6e64f28d1bc59d0755ce92235a20316464443a919269ae0c
4
- data.tar.gz: cd9e5a854f67606b057fb6e98d6a5b43958aa8d6434d8178e6bb85b568925b5e
3
+ metadata.gz: 68647a1e2a4781554b98f3e34e4b8d517bdee49c76459aa8445dffb13dd59f07
4
+ data.tar.gz: b66dcc28e233bf23a4468a1af531d1c650c50bf3d3054687e43831ee6fd3826f
5
5
  SHA512:
6
- metadata.gz: 5ab126d3a8225ac6142b1e6088939d8e30dea963b9b1413ada93261e79a9324153e6307764fe11c7ff76180015ce359bee435c33d0ee950b213713bd71e986e6
7
- data.tar.gz: 60e32edb27e043e3440d1b5c97128a7e7873a1df2df60386c86253653b7842b85f6f6300bb3b5b1ded0d02822db9af66f618ecd110b0fa22c13a43fb96f83b0e
6
+ metadata.gz: 9d87c2c3723f8e643a3370bce93a86231466aa6f8d90ef264b308942cc6b7d832e1c4db4e9ab3a503d6f13804f3beedaef70106fd253c7fb67f9ad4863af01b4
7
+ data.tar.gz: 6f4c3e3784b7c70aea0044fff3bda3dd9c79780e5835ee2ea2593025be264da83798a6379ea0f9acbbe807e08fb237946f7e04910c746f17a480ad083247bc2b
data/lib/graphiform.rb CHANGED
@@ -19,4 +19,12 @@ module Graphiform
19
19
  include Graphiform::Fields
20
20
  end
21
21
  end
22
+
23
+ def self.configuration
24
+ @configuration ||= {}
25
+ end
26
+
27
+ def self.configure
28
+ yield(configuration)
29
+ end
22
30
  end
@@ -0,0 +1,56 @@
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.where(@attribute => values)
21
+
22
+ query = query.includes(@options[:includes]) if @options[:includes].present? && query.respond_to?(:includes)
23
+ query = query.apply_filters(@options[:where].to_h) if @options[:where].present? && query.respond_to?(:apply_filters)
24
+ query = query.apply_sorts(@options[:sort].to_h) if @options[:sort].present? && query.respond_to?(:apply_sorts)
25
+
26
+ query
27
+ end
28
+
29
+ def normalize_value(value)
30
+ value = value.downcase if !@options[:case_sensitive] && value.is_a?(String)
31
+
32
+ value
33
+ end
34
+
35
+ def normalize_values(values)
36
+ type_for_attribute = @model.type_for_attribute(@attribute) if @model.respond_to?(:type_for_attribute)
37
+ values.map do |value|
38
+ value = type_for_attribute.cast(value) if type_for_attribute.present?
39
+ normalize_value(value)
40
+ end
41
+ end
42
+
43
+ def results(values, records)
44
+ record_attributes = records.map { |record| normalize_value(record[@attribute]) }
45
+ values.map do |value|
46
+ if @options[:multi]
47
+ indexes = record_attributes.each_index.select { |index| record_attributes[index] == value }
48
+ indexes.map { |index| index && records[index] }
49
+ else
50
+ index = record_attributes.index(value)
51
+ index && records[index]
52
+ end
53
+ end
54
+ end
55
+ end
56
+ 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
 
@@ -52,6 +54,15 @@ module Graphiform
52
54
  end
53
55
  end
54
56
 
57
+ def graphql_grouping
58
+ local_demodulized_name = demodulized_name
59
+ Helpers.get_const_or_create(local_demodulized_name, ::Inputs::Groupings) do
60
+ Class.new(::Inputs::Groupings::BaseGrouping) do
61
+ graphql_name "#{local_demodulized_name}Grouping"
62
+ end
63
+ end
64
+ end
65
+
55
66
  def graphql_edge
56
67
  Helpers.get_const_or_create("#{demodulized_name}Edge", ::Types) do
57
68
  node_type = graphql_type
@@ -99,9 +110,10 @@ module Graphiform
99
110
  @value
100
111
  end
101
112
 
102
- def apply_built_ins(where: nil, sort: nil, **)
113
+ def apply_built_ins(where: nil, sort: nil, group: nil, **)
103
114
  @value = @value.apply_filters(where.to_h) if where.present? && @value.respond_to?(:apply_filters)
104
115
  @value = @value.apply_sorts(sort.to_h) if sort.present? && @value.respond_to?(:apply_sorts)
116
+ @value = @value.apply_groupings(group.to_h) if group.present? && @value.respond_to?(:apply_groupings)
105
117
 
106
118
  @value
107
119
  end
@@ -115,6 +127,7 @@ module Graphiform
115
127
 
116
128
  local_graphql_filter = graphql_filter
117
129
  local_graphql_sort = graphql_sort
130
+ local_graphql_grouping = graphql_grouping
118
131
 
119
132
  model = self
120
133
  @base_resolver.class_eval do
@@ -126,6 +139,7 @@ module Graphiform
126
139
 
127
140
  argument :where, local_graphql_filter, required: false
128
141
  argument :sort, local_graphql_sort, required: false unless local_graphql_sort.arguments.empty?
142
+ argument :group, local_graphql_grouping, required: false unless local_graphql_grouping.arguments.empty?
129
143
  end
130
144
  end
131
145
 
@@ -141,7 +155,7 @@ module Graphiform
141
155
  def base_resolve(**args)
142
156
  @value = model.all
143
157
  apply_built_ins(**args)
144
- @value.first
158
+ @value.take
145
159
  end
146
160
  end
147
161
  end
@@ -161,18 +175,61 @@ module Graphiform
161
175
  end
162
176
  end
163
177
 
164
- 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], **)
165
179
  Class.new(graphql_base_resolver) do
166
180
  type resolver_type, null: null
167
181
 
168
182
  define_method :base_resolve do |**args|
169
183
  @value = object
170
184
 
171
- @value = instance_exec(@value, context, &read_resolve) if read_resolve
172
- @value = @value.public_send(method_name) if !read_resolve && @value.respond_to?(method_name)
173
- @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
187
+
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
+ where: args[:where],
208
+ sort: args[:sort],
209
+ multi: true,
210
+ case_sensitive: case_sensitive,
211
+ )
212
+ .load(
213
+ @value.public_send(join_keys.foreign_key)
214
+ )
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ def graphql_create_association_resolver(association_def, resolver_type, null: true, skip_dataloader: false, case_sensitive: nil, **)
221
+ Class.new(::Resolvers::BaseResolver) do
222
+ type resolver_type, null: null
223
+
224
+ define_method :resolve do |*|
225
+ join_keys = association_def.join_keys
226
+
227
+ skip_dataloader ||= !Helpers.dataloader_support?(dataloader, association_def, join_keys.foreign_key)
228
+
229
+ return object.public_send(association_def.name) if skip_dataloader
174
230
 
175
- apply_built_ins(**args)
231
+ value = object.public_send(join_keys.foreign_key)
232
+ dataloader.with(AssociationSource, association_def.klass, join_keys.key, case_sensitive: case_sensitive).load(value)
176
233
  end
177
234
  end
178
235
  end
@@ -12,15 +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)
23
+ graphql_add_scopes_to_filter(name, identifier)
24
+ graphql_field_to_sort(name, identifier)
25
+ graphql_field_to_grouping(name, identifier)
24
26
  end
25
27
 
26
28
  def graphql_writable_field(
@@ -120,9 +122,11 @@ module Graphiform
120
122
  type = association_def.klass.graphql_filter
121
123
  end
122
124
 
123
- non_sort_by_scopes = added_scopes.select { |scope_def| !scope_def.options || scope_def.options[:type] != :sort }
125
+ filter_only_by_scopes = added_scopes.select do |scope_def|
126
+ !scope_def.options || scope_def.options[:type].blank? || scope_def.options[:type] == :enum
127
+ end
124
128
 
125
- non_sort_by_scopes.each do |added_scope|
129
+ filter_only_by_scopes.each do |added_scope|
126
130
  scope_argument_type = type || added_scope.options[:argument_type]
127
131
  if added_scope.options[:type] == :enum
128
132
  enum = graphql_create_enum(name)
@@ -175,6 +179,28 @@ module Graphiform
175
179
  end
176
180
  end
177
181
 
182
+ def graphql_field_to_grouping(name, as)
183
+ column_def = column(as || name)
184
+ association_def = association(as || name)
185
+
186
+ type = GraphQL::Types::Boolean if column_def.present?
187
+ type = association_def.klass.graphql_grouping if Helpers.association_arguments_valid?(association_def, :graphql_grouping)
188
+
189
+ return if type.blank?
190
+
191
+ local_graphql_grouping = graphql_grouping
192
+
193
+ local_graphql_grouping.class_eval do
194
+ argument(
195
+ name,
196
+ type,
197
+ required: false,
198
+ as: as,
199
+ method_access: false
200
+ )
201
+ end
202
+ end
203
+
178
204
  def graphql_add_field_to_type(
179
205
  field_name,
180
206
  type,
@@ -241,6 +267,8 @@ module Graphiform
241
267
  include_connection: true,
242
268
  read_prepare: nil,
243
269
  read_resolve: nil,
270
+ skip_dataloader: false,
271
+ case_sensitive: Graphiform.configuration[:case_sensitive],
244
272
  **options
245
273
  )
246
274
  unless association_def.klass.respond_to?(:graphql_type)
@@ -266,7 +294,9 @@ module Graphiform
266
294
  klass.graphql_connection,
267
295
  read_prepare: read_prepare,
268
296
  read_resolve: read_resolve,
269
- null: false
297
+ null: false,
298
+ skip_dataloader: true,
299
+ case_sensitive: case_sensitive
270
300
  ),
271
301
  false,
272
302
  **options
@@ -281,10 +311,17 @@ module Graphiform
281
311
  [klass.graphql_type],
282
312
  read_prepare: read_prepare,
283
313
  read_resolve: read_resolve,
284
- null: false
314
+ null: false,
315
+ skip_dataloader: skip_dataloader,
316
+ case_sensitive: case_sensitive
285
317
  )
286
318
  else
287
- 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
+ )
288
325
  end
289
326
  )
290
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
 
@@ -32,6 +32,8 @@ module Graphiform
32
32
  GraphQL::Types::Float
33
33
  when :boolean
34
34
  GraphQL::Types::Boolean
35
+ when :json, :jsonb
36
+ GraphQL::Types::JSON
35
37
  else
36
38
  active_record_type
37
39
  end
@@ -70,5 +72,15 @@ module Graphiform
70
72
  association_def.klass.send(method).respond_to?(:arguments) &&
71
73
  !association_def.klass.send(method).arguments.empty?
72
74
  end
75
+
76
+ def self.dataloader_support?(dataloader, association_def, keys)
77
+ (
78
+ association_def.present? &&
79
+ !association_def.polymorphic? &&
80
+ !association_def.through_reflection? &&
81
+ !keys.is_a?(Array) &&
82
+ !dataloader.is_a?(GraphQL::Dataloader::NullDataloader)
83
+ )
84
+ end
73
85
  end
74
86
  end
@@ -45,6 +45,14 @@ module Graphiform
45
45
  Class.new(::GraphQL::Schema::InputObject)
46
46
  end
47
47
 
48
+ Helpers.get_const_or_create('Groupings', ::Inputs) do
49
+ Module.new
50
+ end
51
+
52
+ Helpers.get_const_or_create('BaseGrouping', ::Inputs::Groupings) do
53
+ Class.new(::GraphQL::Schema::InputObject)
54
+ end
55
+
48
56
  # Resolvers
49
57
  Helpers.get_const_or_create('Resolvers') do
50
58
  Module.new
@@ -1,3 +1,3 @@
1
1
  module Graphiform
2
- VERSION = '0.2.8'.freeze
2
+ VERSION = '0.2.12'.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.8
4
+ version: 0.2.12
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-01 00:00:00.000000000 Z
11
+ date: 2021-07-19 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.6
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.6
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