graphiti_gql 0.2.20 → 0.2.23

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: 17c54a15f86e3855f814dcea6a7093ceb0b0252de6685c57017a19f3c83160db
4
- data.tar.gz: e0f018dbe747eaa43401f3f02ef77a526e3f5f2a0dad8964dd138bc083a38ad6
3
+ metadata.gz: c5dfc016180ec5fd536f236be63b24cad42471a74d44ab234dee1966f2de942e
4
+ data.tar.gz: f97bbe243fa4a205004abdbd522942005b09e87fa37ba1db465000b383e355d0
5
5
  SHA512:
6
- metadata.gz: b863f5de471a4991d28f35c544567cf7420b78852c202c527b79399deb7e67f7c03e4f539cd628e9827354670e259ba44a8762191aaa88a97e3c474d753ff0de
7
- data.tar.gz: e857d78632c3055263d4ae6287ab30836752c28cdd41836f411a82f4b64119b1420a79447c5a042c2829d24b57eec040d14a1eb9d2705aacc70935612ce5d5f8
6
+ metadata.gz: '08c76642c2b6be4401a6efcce4c9070c52eee1661c333bcefc30a328826a0558aa6ed013028cb4d173bb2b47565aeeda18c2648a946d6ebe1dbcde2147325ced'
7
+ data.tar.gz: d16a38a32815b6df020817bb5797ac26ce6507a443375955aad3c2c1fe8fe29192dbf0f01a854d6d565946daae1fe9a713c67df050bbc230c320c282a35e339f
data/Gemfile.lock CHANGED
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- graphiti_gql (0.2.18)
4
+ graphiti_gql (0.2.23)
5
+ activemodel (> 6.0, < 8.0)
5
6
  graphiti (~> 1.3.9)
6
7
  graphql (~> 2.0)
7
8
  graphql-batch (~> 0.5)
@@ -47,13 +48,13 @@ GEM
47
48
  graphql-batch (0.5.1)
48
49
  graphql (>= 1.10, < 3)
49
50
  promise.rb (~> 0.7.2)
50
- i18n (1.10.0)
51
+ i18n (1.12.0)
51
52
  concurrent-ruby (~> 1.0)
52
53
  jsonapi-renderer (0.2.2)
53
54
  jsonapi-serializable (0.3.1)
54
55
  jsonapi-renderer (~> 0.2.0)
55
56
  method_source (1.0.0)
56
- minitest (5.15.0)
57
+ minitest (5.16.2)
57
58
  promise.rb (0.7.4)
58
59
  pry (0.13.1)
59
60
  coderay (~> 1.1)
@@ -75,14 +76,13 @@ GEM
75
76
  diff-lcs (>= 1.2.0, < 2.0)
76
77
  rspec-support (~> 3.11.0)
77
78
  rspec-support (3.11.0)
78
- tzinfo (2.0.4)
79
+ tzinfo (2.0.5)
79
80
  concurrent-ruby (~> 1.0)
80
81
 
81
82
  PLATFORMS
82
83
  arm64-darwin-21
83
84
 
84
85
  DEPENDENCIES
85
- activemodel (~> 7.0)
86
86
  bundler (~> 2.3)
87
87
  graphiti_gql!
88
88
  pry
data/graphiti_gql.gemspec CHANGED
@@ -38,9 +38,9 @@ Gem::Specification.new do |spec|
38
38
  spec.add_dependency "graphql", "~> 2.0"
39
39
  spec.add_dependency "graphql-batch", "~> 0.5"
40
40
  spec.add_dependency "graphiti", "~> 1.3.9"
41
+ spec.add_dependency "activemodel", ["> 6.0", "< 8.0"]
41
42
 
42
43
  spec.add_development_dependency "bundler", "~> 2.3"
43
44
  spec.add_development_dependency "rake", "~> 10.0"
44
45
  spec.add_development_dependency "rspec", "~> 3.0"
45
- spec.add_development_dependency "activemodel", "~> 7.0"
46
46
  end
@@ -74,6 +74,7 @@ module GraphitiGql
74
74
 
75
75
  def node(id = nil)
76
76
  if @resource.singular
77
+ data # fire query
77
78
  Node.new(underscore(data[data.keys.first]), @resource)
78
79
  else
79
80
  nodes.find { |n| n.id == id.to_s }
@@ -16,7 +16,10 @@ module GraphitiGql
16
16
  module ResourceExtras
17
17
  extend ActiveSupport::Concern
18
18
 
19
- included do
19
+ prepended do
20
+ extend ActiveModel::Callbacks
21
+ define_model_callbacks :query
22
+
20
23
  class << self
21
24
  attr_accessor :graphql_name, :singular
22
25
  end
@@ -57,7 +60,14 @@ module GraphitiGql
57
60
  @selections
58
61
  end
59
62
 
63
+ def around_scoping(original_scope, query_hash)
64
+ run_callbacks :query do
65
+ super { |scope| yield scope }
66
+ end
67
+ end
68
+
60
69
  class_methods do
70
+
61
71
  def attribute(*args)
62
72
  super(*args).tap do
63
73
  opts = args.extract_options!
@@ -85,7 +95,7 @@ module GraphitiGql
85
95
  end
86
96
  end
87
97
  end
88
- Graphiti::Resource.send(:include, ResourceExtras)
98
+ Graphiti::Resource.send(:prepend, ResourceExtras)
89
99
 
90
100
  module FilterExtras
91
101
  def filter_param
@@ -186,26 +196,14 @@ module GraphitiGql
186
196
  module ManyToManyExtras
187
197
  def self.prepended(klass)
188
198
  klass.class_eval do
189
- attr_reader :join_table_alias, :edge_magic
190
-
191
- class << self
192
- attr_reader :edge_resource
193
-
194
- def attribute(*args, &blk)
195
- @edge_resource ||= Class.new(Graphiti::Resource) do
196
- def self.abstract_class?
197
- true
198
- end
199
- end
200
- @edge_resource.attribute(*args, &blk)
201
- end
202
- end
199
+ attr_reader :join_table_alias, :edge_magic, :edge_resource
203
200
  end
204
201
  end
205
202
 
206
203
  def initialize(name, opts = {})
207
204
  @join_table_alias = opts[:join_table_alias]
208
205
  @edge_magic = opts[:edge_magic] == false ? false : true
206
+ @edge_resource = opts[:edge_resource]
209
207
  super
210
208
  end
211
209
 
@@ -269,7 +267,7 @@ module GraphitiGql
269
267
  module ActiveRecordAdapterExtras
270
268
  extend ActiveSupport::Concern
271
269
 
272
- included do
270
+ prepended do
273
271
  alias_method :filter_precise_datetime_lt, :filter_lt
274
272
  alias_method :filter_precise_datetime_lte, :filter_lte
275
273
  alias_method :filter_precise_datetime_gt, :filter_gt
@@ -277,16 +275,59 @@ module GraphitiGql
277
275
  alias_method :filter_precise_datetime_eq, :filter_eq
278
276
  alias_method :filter_precise_datetime_not_eq, :filter_not_eq
279
277
  end
278
+
279
+ # TODO: integration specs mysql vs postgres for case sensitivity
280
+ def mysql?(scope)
281
+ mysql = ActiveRecord::ConnectionAdapters::Mysql2Adapter
282
+ scope.model.connection.is_a?(mysql)
283
+ end
284
+
285
+ def filter_string_eq(scope, attribute, value, is_not: false)
286
+ if mysql?(scope)
287
+ clause = { attribute => value }
288
+ is_not ? scope.where.not(clause) : scope.where(clause)
289
+ else
290
+ # og behavior
291
+ column = column_for(scope, attribute)
292
+ clause = column.lower.eq_any(value.map(&:downcase))
293
+ end
294
+ end
295
+
296
+ def filter_string_eql(scope, attribute, value, is_not: false)
297
+ if mysql?(scope)
298
+ value = "BINARY #{value}"
299
+ end
300
+ # og behavior
301
+ clause = {attribute => value}
302
+ is_not ? scope.where.not(clause) : scope.where(clause)
303
+ end
304
+
305
+ def sanitized_like_for(scope, attribute, value, &block)
306
+ escape_char = "\\"
307
+ column = column_for(scope, attribute)
308
+ map = value.map { |v|
309
+ v = v.downcase unless mysql?(scope)
310
+ v = Sanitizer.sanitize_like(v, escape_char)
311
+ block.call v
312
+ }
313
+ arel = column
314
+ arel = arel.lower unless mysql?(scope)
315
+ arel.matches_any(map, escape_char, true)
316
+ end
280
317
  end
281
318
  if defined?(Graphiti::Adapters::ActiveRecord)
282
- Graphiti::Adapters::ActiveRecord.send(:include, ActiveRecordAdapterExtras)
319
+ Graphiti::Adapters::ActiveRecord.send(:prepend, ActiveRecordAdapterExtras)
283
320
  end
284
321
 
285
322
  Graphiti::Adapters::Abstract.class_eval do
286
323
  class << self
287
324
  alias :old_default_operators :default_operators
288
325
  def default_operators
289
- old_default_operators.merge(precise_datetime: numerical_operators)
326
+ old_default_operators.merge({
327
+ precise_datetime: numerical_operators,
328
+ string_enum: [:eq, :not_eq],
329
+ integer_enum: [:eq, :not_eq],
330
+ })
290
331
  end
291
332
  end
292
333
  end
@@ -16,13 +16,15 @@ module GraphitiGql
16
16
 
17
17
  private
18
18
 
19
+ def thru_model
20
+ thru = @sideload.foreign_key.keys.first
21
+ reflection = @sideload.parent_resource.model.reflect_on_association(thru)
22
+ reflection.klass
23
+ end
24
+
19
25
  def add_join_table_magic(proxy)
20
26
  return unless @sideload.edge_magic
21
27
  if defined?(ActiveRecord) && proxy.resource.model.ancestors.include?(ActiveRecord::Base)
22
- thru = @sideload.foreign_key.keys.first
23
- reflection = @sideload.parent_resource.model.reflect_on_association(thru)
24
- thru_model = reflection.klass
25
-
26
28
  thru_table_name = @sideload.join_table_alias || thru_model.table_name
27
29
  names = thru_model.column_names.map do |n|
28
30
  next if n == :id
@@ -3,12 +3,11 @@ module GraphitiGql
3
3
  module Fields
4
4
  class Attribute
5
5
  # If sideload is present, we're applying m2m metadata to an edge
6
- def initialize(resource, name, config, sideload = nil)
6
+ def initialize(resource, name, config)
7
7
  @resource = resource
8
8
  @config = config
9
9
  @name = name
10
10
  @alias = config[:alias]
11
- @sideload = sideload # is_edge: true
12
11
  end
13
12
 
14
13
  def apply(type)
@@ -16,13 +15,11 @@ module GraphitiGql
16
15
  _config = @config
17
16
  _name = @name
18
17
  _alias = @alias
19
- _sideload = @sideload
20
18
  opts = @config.slice(:null, :deprecation_reason)
21
19
  type.field(_name, field_type, **opts)
22
20
  type.define_method _name do
23
21
  if (readable = _config[:readable]).is_a?(Symbol)
24
22
  obj = object
25
- obj = object.node if _sideload
26
23
  resource = obj.instance_variable_get(:@__graphiti_resource)
27
24
  unless resource.send(readable)
28
25
  path = Graphiti.context[:object][:current_path].join(".")
@@ -30,25 +27,10 @@ module GraphitiGql
30
27
  end
31
28
  end
32
29
 
33
- edge_attrs = nil
34
- if _sideload
35
- edge_attrs = object.node.attributes
36
- .select { |k, v| k.to_s.starts_with?("_edge_") }
37
- edge_attrs.transform_keys! { |k| k.to_s.gsub("_edge_", "").to_sym }
38
- end
39
-
40
30
  value = if _config[:proc]
41
- if _sideload
42
- instance_exec(edge_attrs, object.node, &_config[:proc])
43
- else
44
- instance_eval(&_config[:proc])
45
- end
31
+ instance_eval(&_config[:proc])
46
32
  else
47
- if _sideload
48
- edge_attrs[_alias || _name]
49
- else
50
- object.send(_alias || _name)
51
- end
33
+ object.send(_alias || _name)
52
34
  end
53
35
  return if value.nil?
54
36
  Graphiti::Types[_config[:type]][:read].call(value)
@@ -35,7 +35,7 @@ module GraphitiGql
35
35
  end
36
36
 
37
37
  def customized_edge?
38
- @sideload.type == :many_to_many && @sideload.class.edge_resource
38
+ @sideload.type == :many_to_many && @sideload.edge_resource
39
39
  end
40
40
 
41
41
  def find_or_build_connection
@@ -59,21 +59,60 @@ module GraphitiGql
59
59
  end
60
60
 
61
61
  def build_edge_type_class(sideload_type)
62
- prior_edge_type_class = sideload_type.edge_type_class
63
- edge_type_class = Class.new(prior_edge_type_class)
64
- edge_resource = @sideload.class.edge_resource
65
- edge_resource.attributes.each_pair do |name, config|
66
- next if name == :id
67
- Schema::Fields::Attribute.new(edge_resource, name, config, @sideload).apply(edge_type_class)
62
+ klass = build_friendly_graphql_edge_type_class \
63
+ sideload_type.edge_type_class
64
+ name = edge_type_class_name(sideload_type)
65
+ klass.define_method(:graphql_name) { name }
66
+ klass.graphql_name(name)
67
+ edge_resource = @sideload.edge_resource
68
+ ResourceType.add_fields(klass, edge_resource, id: false)
69
+ ResourceType.add_relationships(edge_resource, klass)
70
+ klass
71
+ end
72
+
73
+ # Normally we reference 'object', but edges work differently
74
+ # This makes 'object' work everywhere
75
+ # Needed when evaluating fields/relationships for consistent interface
76
+ def build_friendly_graphql_edge_type_class(superklass)
77
+ klass = Class.new(superklass) do
78
+ alias :original_object :object
79
+ def object
80
+ return @_object if @_object # avoid conflict
81
+
82
+ node = original_object.node # the 'parent' record we joined with
83
+ edge_attrs = node.attributes.select { |k,v| k.to_s.starts_with?('_edge') }
84
+ edge_attrs.transform_keys! { |k| k.to_s.gsub('_edge_', '') }
85
+ edge_model = model.new(edge_attrs)
86
+ edge_model.instance_variable_set(:@__graphiti_resource, resource)
87
+ @_object = edge_model
88
+ @_object
89
+ end
90
+
91
+ def cursor
92
+ original_object.cursor
93
+ end
94
+
95
+ def node
96
+ original_object.node
97
+ end
68
98
  end
69
- registered_parent = Schema.registry.get(@sideload.parent_resource.class)
99
+
100
+ # used in #object
101
+ thru = @sideload.foreign_key.keys.first
102
+ reflection = @sideload.parent_resource.model.reflect_on_association(thru)
103
+ thru_model = reflection.klass
104
+ edge_resource = @sideload.edge_resource.new
105
+ klass.define_method(:model) { thru_model }
106
+ klass.define_method(:resource) { edge_resource }
107
+
108
+ klass
109
+ end
110
+
111
+ def edge_type_class_name(sideload_type)
112
+ registered_parent = Schema.registry.get \
113
+ @sideload.parent_resource.class
70
114
  parent_name = registered_parent[:type].graphql_name
71
- edge_type_class_name = "#{parent_name}To#{sideload_type.graphql_name}Edge"
72
- edge_type_class.define_method :graphql_name do
73
- edge_type_class_name
74
- end
75
- edge_type_class.graphql_name(edge_type_class_name)
76
- edge_type_class
115
+ "#{parent_name}To#{sideload_type.graphql_name}Edge"
77
116
  end
78
117
  end
79
118
  end
@@ -30,30 +30,9 @@ module GraphitiGql
30
30
  end
31
31
 
32
32
  def add_relationships
33
- each_relationship do |type, sideload_type, sideload|
34
- if [:has_many, :many_to_many, :has_one].include?(sideload.type)
35
- Fields::ToMany.new(sideload, sideload_type).apply(type)
36
- else
37
- Fields::ToOne.new(sideload, sideload_type).apply(type)
38
- end
39
- end
40
- end
41
-
42
- def each_relationship
43
33
  registry.resource_types.each do |registered|
44
- registered[:resource].sideloads.each do |name, sl|
45
- next unless sl.readable?
46
-
47
- registered_sl = if sl.type == :polymorphic_belongs_to
48
- PolymorphicBelongsToInterface
49
- .new(registered[:resource], sl)
50
- .build
51
- else
52
- registry.get(sl.resource.class)
53
- end
54
-
55
- yield registered[:type], registered_sl[:type], sl
56
- end
34
+ resource, type = registered[:resource], registered[:type]
35
+ ResourceType.add_relationships(resource, type)
57
36
  end
58
37
  end
59
38
  end
@@ -14,6 +14,36 @@ module GraphitiGql
14
14
  end
15
15
  end
16
16
 
17
+ def self.add_fields(type, resource, id: true)
18
+ resource.attributes.each_pair do |name, config|
19
+ next if name == :id && id == false
20
+ if config[:readable]
21
+ Fields::Attribute.new(resource, name, config).apply(type)
22
+ end
23
+ end
24
+ end
25
+
26
+ def self.add_relationships(resource, type)
27
+ resource.sideloads.each do |name, sideload|
28
+ next unless sideload.readable?
29
+
30
+ registered_sl = if sideload.type == :polymorphic_belongs_to
31
+ PolymorphicBelongsToInterface
32
+ .new(resource, sideload)
33
+ .build
34
+ else
35
+ Schema.registry.get(sideload.resource.class)
36
+ end
37
+ sideload_type = registered_sl[:type]
38
+
39
+ if [:has_many, :many_to_many, :has_one].include?(sideload.type)
40
+ Fields::ToMany.new(sideload, sideload_type).apply(type)
41
+ else
42
+ Fields::ToOne.new(sideload, sideload_type).apply(type)
43
+ end
44
+ end
45
+ end
46
+
17
47
  def initialize(resource, implements: nil)
18
48
  @resource = resource
19
49
  @implements = implements
@@ -86,14 +116,10 @@ module GraphitiGql
86
116
  def name
87
117
  registry.key_for(@resource)
88
118
  end
89
-
119
+
90
120
  def add_fields(type, resource)
91
- resource.attributes.each_pair do |name, config|
92
- if config[:readable]
93
- Fields::Attribute.new(@resource, name, config).apply(type)
94
- end
95
- end
96
- end
121
+ self.class.add_fields(type, resource)
122
+ end
97
123
 
98
124
  def build_connection_class
99
125
  klass = Class.new(GraphQL::Types::Relay::BaseConnection)
@@ -1,3 +1,3 @@
1
1
  module GraphitiGql
2
- VERSION = "0.2.20"
2
+ VERSION = "0.2.23"
3
3
  end
data/lib/graphiti_gql.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require "active_support/core_ext/object/json"
2
2
  require "graphql"
3
3
  require 'graphql/batch'
4
+ require 'active_model'
4
5
  require "graphiti_gql/graphiti_hax"
5
6
  require "graphiti_gql/version"
6
7
  require "graphiti_gql/errors"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphiti_gql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.20
4
+ version: 0.2.23
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lee Richmond
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-08-01 00:00:00.000000000 Z
11
+ date: 2022-08-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -52,6 +52,26 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: 1.3.9
55
+ - !ruby/object:Gem::Dependency
56
+ name: activemodel
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">"
60
+ - !ruby/object:Gem::Version
61
+ version: '6.0'
62
+ - - "<"
63
+ - !ruby/object:Gem::Version
64
+ version: '8.0'
65
+ type: :runtime
66
+ prerelease: false
67
+ version_requirements: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">"
70
+ - !ruby/object:Gem::Version
71
+ version: '6.0'
72
+ - - "<"
73
+ - !ruby/object:Gem::Version
74
+ version: '8.0'
55
75
  - !ruby/object:Gem::Dependency
56
76
  name: bundler
57
77
  requirement: !ruby/object:Gem::Requirement
@@ -94,21 +114,7 @@ dependencies:
94
114
  - - "~>"
95
115
  - !ruby/object:Gem::Version
96
116
  version: '3.0'
97
- - !ruby/object:Gem::Dependency
98
- name: activemodel
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: '7.0'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: '7.0'
111
- description:
117
+ description:
112
118
  email:
113
119
  - richmolj@gmail.com
114
120
  executables: []
@@ -169,7 +175,7 @@ licenses:
169
175
  - MIT
170
176
  metadata:
171
177
  homepage_uri: https://www.graphiti.dev
172
- post_install_message:
178
+ post_install_message:
173
179
  rdoc_options: []
174
180
  require_paths:
175
181
  - lib
@@ -184,8 +190,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
184
190
  - !ruby/object:Gem::Version
185
191
  version: '0'
186
192
  requirements: []
187
- rubygems_version: 3.0.3.1
188
- signing_key:
193
+ rubygems_version: 3.3.7
194
+ signing_key:
189
195
  specification_version: 4
190
196
  summary: GraphQL support for Graphiti
191
197
  test_files: []