graphiform 0.2.8 → 0.2.12

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: 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