rails-graphql 1.0.0.beta → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/ext/gql_parser.c +1 -16
  3. data/ext/gql_parser.h +21 -0
  4. data/ext/shared.c +0 -5
  5. data/ext/shared.h +6 -6
  6. data/lib/generators/graphql/channel_generator.rb +27 -0
  7. data/lib/generators/graphql/controller_generator.rb +9 -4
  8. data/lib/generators/graphql/install_generator.rb +49 -0
  9. data/lib/generators/graphql/schema_generator.rb +9 -4
  10. data/lib/generators/graphql/templates/channel.erb +7 -0
  11. data/lib/generators/graphql/templates/config.rb +97 -0
  12. data/lib/generators/graphql/templates/controller.erb +2 -0
  13. data/lib/generators/graphql/templates/schema.erb +5 -3
  14. data/lib/gql_parser.so +0 -0
  15. data/lib/rails/graphql/alternative/field_set.rb +12 -0
  16. data/lib/rails/graphql/alternative/query.rb +9 -4
  17. data/lib/rails/graphql/alternative/subscription.rb +2 -1
  18. data/lib/rails/graphql/argument.rb +5 -3
  19. data/lib/rails/graphql/callback.rb +8 -7
  20. data/lib/rails/graphql/collectors/hash_collector.rb +12 -1
  21. data/lib/rails/graphql/collectors/json_collector.rb +21 -0
  22. data/lib/rails/graphql/config.rb +73 -57
  23. data/lib/rails/graphql/directive/include_directive.rb +0 -1
  24. data/lib/rails/graphql/directive/skip_directive.rb +0 -1
  25. data/lib/rails/graphql/directive/specified_by_directive.rb +24 -0
  26. data/lib/rails/graphql/directive.rb +30 -24
  27. data/lib/rails/graphql/event.rb +7 -6
  28. data/lib/rails/graphql/field/authorized_field.rb +0 -5
  29. data/lib/rails/graphql/field/input_field.rb +0 -5
  30. data/lib/rails/graphql/field/mutation_field.rb +5 -6
  31. data/lib/rails/graphql/field/output_field.rb +13 -2
  32. data/lib/rails/graphql/field/proxied_field.rb +5 -5
  33. data/lib/rails/graphql/field/resolved_field.rb +1 -1
  34. data/lib/rails/graphql/field/subscription_field.rb +35 -52
  35. data/lib/rails/graphql/field/typed_field.rb +26 -2
  36. data/lib/rails/graphql/field.rb +20 -19
  37. data/lib/rails/graphql/global_id.rb +5 -1
  38. data/lib/rails/graphql/helpers/inherited_collection/array.rb +1 -0
  39. data/lib/rails/graphql/helpers/inherited_collection/base.rb +2 -0
  40. data/lib/rails/graphql/helpers/inherited_collection/hash.rb +2 -1
  41. data/lib/rails/graphql/helpers/registerable.rb +1 -1
  42. data/lib/rails/graphql/helpers/with_arguments.rb +3 -2
  43. data/lib/rails/graphql/helpers/with_callbacks.rb +3 -3
  44. data/lib/rails/graphql/helpers/with_description.rb +10 -8
  45. data/lib/rails/graphql/helpers/with_directives.rb +5 -1
  46. data/lib/rails/graphql/helpers/with_events.rb +1 -0
  47. data/lib/rails/graphql/helpers/with_fields.rb +28 -22
  48. data/lib/rails/graphql/helpers/with_name.rb +3 -2
  49. data/lib/rails/graphql/helpers/with_schema_fields.rb +72 -48
  50. data/lib/rails/graphql/introspection.rb +1 -1
  51. data/lib/rails/graphql/railtie.rb +3 -2
  52. data/lib/rails/graphql/railties/app/base_channel.rb +10 -0
  53. data/lib/rails/graphql/railties/app/base_controller.rb +12 -0
  54. data/lib/rails/graphql/railties/app/views/_cable.js.erb +56 -0
  55. data/lib/rails/graphql/railties/app/views/_fetch.js.erb +20 -0
  56. data/lib/rails/graphql/railties/app/views/graphiql.html.erb +101 -0
  57. data/lib/rails/graphql/railties/base_generator.rb +3 -9
  58. data/lib/rails/graphql/railties/channel.rb +8 -8
  59. data/lib/rails/graphql/railties/controller.rb +45 -24
  60. data/lib/rails/graphql/request/arguments.rb +2 -1
  61. data/lib/rails/graphql/request/backtrace.rb +31 -10
  62. data/lib/rails/graphql/request/component/field.rb +15 -8
  63. data/lib/rails/graphql/request/component/fragment.rb +13 -7
  64. data/lib/rails/graphql/request/component/operation/subscription.rb +4 -6
  65. data/lib/rails/graphql/request/component/operation.rb +11 -4
  66. data/lib/rails/graphql/request/component/spread.rb +13 -4
  67. data/lib/rails/graphql/request/component/typename.rb +1 -1
  68. data/lib/rails/graphql/request/component.rb +2 -0
  69. data/lib/rails/graphql/request/context.rb +1 -1
  70. data/lib/rails/graphql/request/event.rb +6 -2
  71. data/lib/rails/graphql/request/helpers/directives.rb +1 -0
  72. data/lib/rails/graphql/request/helpers/selection_set.rb +10 -4
  73. data/lib/rails/graphql/request/helpers/value_writers.rb +8 -5
  74. data/lib/rails/graphql/request/prepared_data.rb +3 -1
  75. data/lib/rails/graphql/request/steps/organizable.rb +1 -1
  76. data/lib/rails/graphql/request/steps/preparable.rb +1 -1
  77. data/lib/rails/graphql/request/steps/resolvable.rb +1 -1
  78. data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +3 -3
  79. data/lib/rails/graphql/request/strategy.rb +18 -4
  80. data/lib/rails/graphql/request/subscription.rb +18 -16
  81. data/lib/rails/graphql/request.rb +67 -37
  82. data/lib/rails/graphql/schema.rb +39 -86
  83. data/lib/rails/graphql/shortcuts.rb +11 -5
  84. data/lib/rails/graphql/source/active_record/builders.rb +20 -21
  85. data/lib/rails/graphql/source/active_record_source.rb +93 -33
  86. data/lib/rails/graphql/source/base.rb +11 -39
  87. data/lib/rails/graphql/source/builder.rb +9 -22
  88. data/lib/rails/graphql/source/scoped_arguments.rb +10 -4
  89. data/lib/rails/graphql/source.rb +23 -37
  90. data/lib/rails/graphql/subscription/provider/action_cable.rb +10 -9
  91. data/lib/rails/graphql/subscription/provider/base.rb +6 -5
  92. data/lib/rails/graphql/subscription/store/base.rb +5 -9
  93. data/lib/rails/graphql/subscription/store/memory.rb +18 -9
  94. data/lib/rails/graphql/type/creator.rb +196 -0
  95. data/lib/rails/graphql/type/enum.rb +17 -9
  96. data/lib/rails/graphql/type/input.rb +20 -4
  97. data/lib/rails/graphql/type/interface.rb +15 -4
  98. data/lib/rails/graphql/type/object/directive_object.rb +6 -5
  99. data/lib/rails/graphql/type/object/input_value_object.rb +3 -4
  100. data/lib/rails/graphql/type/object/type_object.rb +40 -13
  101. data/lib/rails/graphql/type/object.rb +10 -5
  102. data/lib/rails/graphql/type/scalar/binary_scalar.rb +2 -0
  103. data/lib/rails/graphql/type/scalar/date_scalar.rb +2 -0
  104. data/lib/rails/graphql/type/scalar/date_time_scalar.rb +2 -0
  105. data/lib/rails/graphql/type/scalar/decimal_scalar.rb +2 -0
  106. data/lib/rails/graphql/type/scalar/json_scalar.rb +3 -1
  107. data/lib/rails/graphql/type/scalar/time_scalar.rb +3 -1
  108. data/lib/rails/graphql/type/scalar.rb +1 -1
  109. data/lib/rails/graphql/type/union.rb +7 -2
  110. data/lib/rails/graphql/type.rb +10 -2
  111. data/lib/rails/graphql/type_map.rb +18 -7
  112. data/lib/rails/graphql/uri.rb +5 -4
  113. data/lib/rails/graphql/version.rb +6 -2
  114. data/lib/rails/graphql.rb +9 -7
  115. data/test/assets/introspection-mem.txt +1 -1
  116. data/test/assets/introspection.gql +2 -0
  117. data/test/assets/mem.gql +74 -60
  118. data/test/assets/mysql.gql +69 -55
  119. data/test/assets/sqlite.gql +78 -64
  120. data/test/assets/translate.gql +50 -39
  121. data/test/config.rb +2 -1
  122. data/test/graphql/schema_test.rb +2 -31
  123. data/test/graphql/source_test.rb +0 -10
  124. data/test/graphql/type/interface_test.rb +8 -5
  125. data/test/graphql/type/object_test.rb +8 -2
  126. data/test/graphql/type_map_test.rb +13 -16
  127. data/test/integration/global_id_test.rb +4 -4
  128. data/test/integration/memory/star_wars_validation_test.rb +2 -2
  129. data/test/integration/mysql/star_wars_introspection_test.rb +1 -1
  130. data/test/integration/resolver_precedence_test.rb +1 -1
  131. data/test/integration/schemas/memory.rb +3 -4
  132. data/test/integration/sqlite/star_wars_global_id_test.rb +27 -21
  133. data/test/integration/sqlite/star_wars_introspection_test.rb +1 -1
  134. data/test/integration/translate_test.rb +26 -14
  135. metadata +20 -7
@@ -4,21 +4,28 @@ module Rails
4
4
  module GraphQL
5
5
  # = GraphQL Subscription Field
6
6
  #
7
- # TODO: Finish and add description
7
+ # This is an extension of a normal output field, which will sign
8
+ # the request for updates of the field when the scope and arguments
9
+ # are the same.
8
10
  class Field::SubscriptionField < Field::OutputField
9
11
  redefine_singleton_method(:subscription?) { true }
10
12
  event_types(:subscribed, append: true)
11
13
 
12
- attr_reader :prepare_context
13
-
14
14
  module Proxied # :nodoc: all
15
15
  def full_scope
16
16
  field.full_scope + super
17
17
  end
18
+ end
18
19
 
19
- def prepare_context
20
- super || field.prepare_context
21
- end
20
+ # Just add the callbacks setup to the field
21
+ def self.included(other)
22
+ other.send(:expose_events!, :subscribed)
23
+ end
24
+
25
+ # Intercept the initializer to maybe set the +scope+
26
+ def initialize(*args, scope: nil, **xargs, &block)
27
+ @scope = Array.wrap(scope).freeze unless scope.nil?
28
+ super(*args, **xargs, &block)
22
29
  end
23
30
 
24
31
  # Change the schema type of the field
@@ -26,72 +33,63 @@ module Rails
26
33
  :subscription
27
34
  end
28
35
 
29
- # A kind of alias to the subscribe event available
30
- def subscribed(*args, **xargs, &block)
31
- on(:subscribed, *args, **xargs, &block)
32
- self
33
- end
34
-
35
36
  # Set the parts of the scope of the subscription
36
37
  def scope(*parts)
37
38
  (defined?(@scope) ? (@scope += parts) : (@scope = parts)).freeze
39
+ self
38
40
  end
39
41
 
40
42
  # Get the full scope of the field
41
43
  def full_scope
42
- return EMPTY_ARRAY unless defined?(@scope)
43
- end
44
-
45
- # Checks if the scope is correctly defined
46
- def validate!(*)
47
- super if defined? super
48
- return unless defined?(@scope)
49
-
50
- invalid = @scope.reject { |item| item.is_a?(Symbol) || item.is_a?(Proc) }
51
- raise ArgumentError, (+<<~MSG).squish if invalid.any?
52
- The "#{type_klass.gql_name}" has invalid values set for its scope: #{invalid.inspect}.
53
- MSG
44
+ defined?(@scope) ? @scope : EMPTY_ARRAY
54
45
  end
55
46
 
56
47
  # A shortcut for trigger when everything is related to the provided object
57
- # TODO: Maybe add support for object as an array of things
58
- # TODO: Add support for +data_for+. The only problem right now is that
59
- # providers run asynchronously, so passing data is a bit more complicated
60
- # and maybe dangerous (size speaking)
61
- def trigger_for(object, **xargs)
62
- match_valid_object!(object)
48
+ def trigger_for(object, and_prepare: true, **xargs)
63
49
  xargs[:args] ||= extract_args_from(object)
50
+
51
+ if and_prepare
52
+ xargs[:data_for] ||= {}
53
+ xargs[:data_for][+"subscription.#{gql_name}"] = object
54
+ end
55
+
64
56
  trigger(**xargs)
65
57
  end
66
58
 
67
59
  # A shortcut for unsubscribe when everything is related to the provided
68
60
  # object
69
- # TODO: Maybe add support for object as an array of things
70
61
  def unsubscribe_from(object, **xargs)
71
- match_valid_object!(object)
72
62
  xargs[:args] ||= extract_args_from(object)
73
63
  unsubscribe(**xargs)
74
64
  end
75
65
 
76
66
  # Trigger an update to the subscription
77
- def trigger(args: nil, scope: nil)
67
+ def trigger(**xargs)
78
68
  provider = owner.subscription_provider
79
- provider.search_and_update(field: self, args: args, scope: scope)
69
+ provider.search_and_update(field: self, **xargs)
80
70
  end
81
71
 
82
72
  # Force matching subscriptions to be removed
83
- def unsubscribe(args: nil, scope: nil)
73
+ def unsubscribe(**xargs)
84
74
  provider = owner.subscription_provider
85
- provider.search_and_remove(field: self, args: args, scope: scope)
75
+ provider.search_and_remove(field: self, **xargs)
86
76
  end
87
77
 
88
78
  protected
89
79
 
90
80
  # Match any argument with properties from the given +object+ so it
91
81
  # produces all the possibilities of an update
92
- def extract_args_from(object)
82
+ def extract_args_from(object, iterate = true)
93
83
  return unless arguments?
94
84
 
85
+ # If can iterate and the provided object is an enumerable, then
86
+ # call itself with each item
87
+ if iterate && object.is_a?(Enumerable)
88
+ return object.each_with_object([]) do |item, result|
89
+ result.concat(extract_args_from(item, false))
90
+ end
91
+ end
92
+
95
93
  # Prepare all the possibilities
96
94
  keys = []
97
95
  hash_like = object.respond_to?(:[])
@@ -116,21 +114,6 @@ module Rails
116
114
  end
117
115
  end
118
116
 
119
- # Check if the provided +object+ is a match for the type that this field
120
- # is associated with
121
- def match_valid_object!(object)
122
- raise ::ArgumentError, (+<<~MSG).squish unless type_klass&.object?
123
- Cannot trigger with an object when the field is not associated to
124
- an object-like result.
125
- MSG
126
-
127
- assignable = !object.is_a?(Module) && type_klass.valid_member?(object)
128
- raise ::ArgumentError, (+<<~MSG).squish unless assignable
129
- The provided object "#{object.inspect}" is not a valid member of
130
- #{type_klass.inspect} for the :#{name} field.
131
- MSG
132
- end
133
-
134
117
  def proxied
135
118
  super if defined? super
136
119
  extend Field::SubscriptionField::Proxied
@@ -13,7 +13,8 @@ module Rails
13
13
 
14
14
  delegate :input_type?, :output_type?, :leaf_type?, :kind, to: :type_klass
15
15
 
16
- def initialize(name, type, *args, **xargs, &block)
16
+ def initialize(name, type = nil, *args, **xargs, &block)
17
+ type = (name == :id ? :id : :string) if type.nil?
17
18
  assign_type(type)
18
19
  super(name, *args, **xargs, &block)
19
20
  end
@@ -26,7 +27,7 @@ module Rails
26
27
 
27
28
  # Check if types are compatible
28
29
  def =~(other)
29
- other.is_a?(Field::TypedField) && other.type_klass =~ type_klass && super
30
+ super && other.is_a?(Field::TypedField) && other.type_klass =~ type_klass
30
31
  end
31
32
 
32
33
  # Sometimes the owner does not designate this, but it is safe to assume it
@@ -49,6 +50,8 @@ module Rails
49
50
  )
50
51
  end
51
52
 
53
+ alias type_class type_klass
54
+
52
55
  # Add the listeners from the associated type
53
56
  def all_listeners
54
57
  inherited = super
@@ -74,6 +77,27 @@ module Rails
74
77
  super || type_klass.events?
75
78
  end
76
79
 
80
+ # Transforms the given value to its representation in a JSON string
81
+ def to_json(value)
82
+ return 'null' if value.nil?
83
+ return type_klass.to_json(value) unless array?
84
+ value.map { |part| type_klass.to_json(part) }
85
+ end
86
+
87
+ # Turn the given value into a JSON string representation
88
+ def as_json(value)
89
+ return if value.nil?
90
+ return type_klass.as_json(value) unless array?
91
+ value.map { |part| type_klass.as_json(part) }
92
+ end
93
+
94
+ # Turn a user input of this given type into an ruby object
95
+ def deserialize(value)
96
+ return if value.nil?
97
+ return type_klass.deserialize(value) unless array?
98
+ value.map { |val| type_klass.deserialize(val) unless val.nil? }
99
+ end
100
+
77
101
  # Checks if the type of the field is valid
78
102
  def validate!(*)
79
103
  super if defined? super
@@ -22,7 +22,7 @@ module Rails
22
22
  # (defaults to false).
23
23
  # * <tt>:enabled</tt> - Mark the field as enabled
24
24
  # (defaults to true).
25
- # * <tt>:disabled</tt> - Works as the oposite of the enabled option
25
+ # * <tt>:disabled</tt> - Works as the opposite of the enabled option
26
26
  # (defaults to false).
27
27
  # * <tt>:directives</tt> - The list of directives associated with the value
28
28
  # (defaults to nil).
@@ -117,7 +117,7 @@ module Rails
117
117
  @array = full ? true : xargs.fetch(:array, false)
118
118
  @nullable = full ? false : xargs.fetch(:nullable, true)
119
119
 
120
- self.description = xargs[:desc]
120
+ self.description = xargs[:desc] || xargs[:description]
121
121
  @enabled = xargs.fetch(:enabled, !xargs.fetch(:disabled, false))
122
122
 
123
123
  configure(&block) if block.present?
@@ -137,12 +137,14 @@ module Rails
137
137
  enable! if xargs.fetch(:enabled, false)
138
138
 
139
139
  self.description = xargs[:desc] if xargs.key?(:desc)
140
+ self.description = xargs[:description] if xargs.key?(:description)
140
141
  configure(&block) if block.present?
141
142
  end
142
143
 
143
144
  # Allow extra configurations to be performed using a block
144
145
  def configure(&block)
145
146
  Field::ScopedConfig.new(self, block.binding.receiver).instance_exec(&block)
147
+ self
146
148
  end
147
149
 
148
150
  # Return the owner as the single item of the list
@@ -181,17 +183,22 @@ module Rails
181
183
  @nullable = false
182
184
  end
183
185
 
184
- # Checks if the argument can be null
186
+ # Checks if the field can be null
185
187
  def null?
186
188
  !!@null
187
189
  end
188
190
 
189
- # Checks if the argument can be an array
191
+ # Checks if the field cannot br null
192
+ def required?
193
+ !null?
194
+ end
195
+
196
+ # Checks if the field can be an array
190
197
  def array?
191
198
  !!@array
192
199
  end
193
200
 
194
- # Checks if the argument can have null elements in the array
201
+ # Checks if the field can have null elements in the array
195
202
  def nullable?
196
203
  !!@nullable
197
204
  end
@@ -227,24 +234,18 @@ module Rails
227
234
  end
228
235
 
229
236
  # Transforms the given value to its representation in a JSON string
230
- def to_json(value)
231
- return 'null' if value.nil?
232
- return type_klass.to_json(value) unless array?
233
- value.map { |part| type_klass.to_json(part) }
237
+ def to_json(*)
238
+ raise NotImplementedError, +"#{self.class.name} does not implement to_json"
234
239
  end
235
240
 
236
241
  # Turn the given value into a JSON string representation
237
- def as_json(value)
238
- return if value.nil?
239
- return type_klass.as_json(value) unless array?
240
- value.map { |part| type_klass.as_json(part) }
242
+ def as_json(*)
243
+ raise NotImplementedError, +"#{self.class.name} does not implement as_json"
241
244
  end
242
245
 
243
246
  # Turn a user input of this given type into an ruby object
244
- def deserialize(value)
245
- return if value.nil?
246
- return type_klass.deserialize(value) unless array?
247
- value.map { |val| type_klass.deserialize(val) unless val.nil? }
247
+ def deserialize(*)
248
+ raise NotImplementedError, +"#{self.class.name} does not implement deserialize"
248
249
  end
249
250
 
250
251
  # Check if the given value is valid using +valid_input?+ or
@@ -282,8 +283,8 @@ module Rails
282
283
 
283
284
  def inspect
284
285
  (+<<~INFO).squish << '>'
285
- #<#{self.class.name}
286
- #{inspect_owner}
286
+ #<GraphQL::#{self.class.name.split('::').last}
287
+ #{inspect_owner&.split(+'GraphQL::')&.last&.sub(+'::NestedTypes', '')}
287
288
  #{inspect_source}
288
289
  #{inspect_enabled}
289
290
  #{gql_name}#{inspect_arguments}#{inspect_type}
@@ -72,7 +72,11 @@ module Rails
72
72
  end
73
73
 
74
74
  def base_class
75
- ::GraphQL.const_get(class_name, false)
75
+ if %w[Schema Directive Type].include?(class_name)
76
+ GraphQL.const_get(class_name, false)
77
+ else
78
+ GraphQL.type_map.fetch(class_name, namespace: namespace)
79
+ end
76
80
  end
77
81
 
78
82
  def ==(other)
@@ -4,6 +4,7 @@ module Rails
4
4
  # A inherited collection of arrays that can be unique when it is a set
5
5
  class InheritedCollection::Array < InheritedCollection::Base
6
6
  alias size count
7
+ alias to_ary eager
7
8
 
8
9
  # Provide similar functionality of any? but returns the object instead
9
10
  def find(value = nil, &block)
@@ -7,6 +7,8 @@ module Rails
7
7
  class InheritedCollection::Base
8
8
  include Enumerable
9
9
 
10
+ delegate :eager, to: :each
11
+
10
12
  # Just a little helper to initialize the iterator form a given +source+
11
13
  def self.handle(source, ivar, type)
12
14
  klass = (type == :array || type == :set) ? :Array : :Hash
@@ -5,6 +5,7 @@ module Rails
5
5
  # or sets
6
6
  class InheritedCollection::Hash < InheritedCollection::Base
7
7
  delegate :transform_values, :transform_keys, to: :to_hash
8
+ delegate :filter_map, :map, to: :eager
8
9
 
9
10
  # Simply go over each definition and check for the given key
10
11
  def key?(value)
@@ -44,7 +45,7 @@ module Rails
44
45
  keys.lazy.map(&method(:[]))
45
46
  end
46
47
 
47
- # Basically allow this lazy operator to be merged with other hashes
48
+ # Basically returns the plain result value
48
49
  def to_hash
49
50
  each.to_h
50
51
  end
@@ -25,7 +25,7 @@ module Rails
25
25
  GraphQL.type_map.postpone_registration(subclass)
26
26
  end
27
27
 
28
- # Check if the class is already registered in the typemap
28
+ # Check if the class is already registered in the type map
29
29
  def registered?
30
30
  GraphQL.type_map.object_exist?(self, exclusive: true)
31
31
  end
@@ -59,14 +59,15 @@ module Rails
59
59
  end
60
60
 
61
61
  # See {Argument}[rdoc-ref:Rails::GraphQL::Argument] class.
62
- def argument(name, base_type, **xargs)
63
- object = GraphQL::Argument.new(name, base_type, **xargs, owner: self)
62
+ def argument(name, type = nil, **xargs)
63
+ object = GraphQL::Argument.new(name, type, **xargs, owner: self)
64
64
 
65
65
  raise DuplicatedError, (+<<~MSG).squish if has_argument?(object.name)
66
66
  The #{name.inspect} argument is already defined and can't be redefined.
67
67
  MSG
68
68
 
69
69
  arguments[object.name] = object
70
+ self
70
71
  rescue DefinitionError => e
71
72
  raise e.class, +"#{e.message}\n Defined at: #{caller(2)[0]}"
72
73
  end
@@ -5,11 +5,11 @@ module Rails
5
5
  module Helpers
6
6
  # Callbacks is an extension of the events which works with the
7
7
  # {Callback}[rdoc-ref:Rails::GraphQL::Callback] class, then having extra
8
- # powers when actually executing the event against procs or owner-based
9
- # symbolic methods
8
+ # powers when actually executing the event against Procs or owner-based
9
+ # methods, when provided a symbol
10
10
  module WithCallbacks
11
11
  DEFAULT_EVENT_TYPES = %i[query mutation subscription request attach
12
- authorize organized prepared finalize].freeze
12
+ authorize organized prepared finalize prepare subscribed].freeze
13
13
 
14
14
  def self.extended(other)
15
15
  other.extend(WithCallbacks::Setup)
@@ -12,17 +12,19 @@ module Rails
12
12
 
13
13
  # Define and format description
14
14
  def description=(value)
15
- @description = value.to_s.presence&.strip_heredoc&.chomp
15
+ if !value.nil?
16
+ @description = value.to_s.presence&.strip_heredoc&.chomp
17
+ elsif defined?(@description)
18
+ @description = nil
19
+ end
16
20
  end
17
21
 
18
- # Return the description of the argument
22
+ # Return a description, by defined value or I18n
19
23
  def description(namespace = nil, kind = nil)
20
24
  return @description if description?
21
25
  return unless GraphQL.config.enable_i18n_descriptions
22
26
 
23
- # If it has been defined, but as nil, then it will always be nil
24
- return if defined?(@description)
25
- @description = i18n_description(namespace, kind)
27
+ i18n_description(namespace, kind)
26
28
  end
27
29
 
28
30
  # Checks if a description was provided
@@ -34,7 +36,8 @@ module Rails
34
36
 
35
37
  # Return a description from I18n
36
38
  def i18n_description(namespace = nil, kind = nil)
37
- return if (parent = try(:owner)).try(:spec_object?)
39
+ return if (parent = try(:owner)).try(:spec_object?) &&
40
+ parent.kind != :schema
38
41
 
39
42
  values = {
40
43
  kind: kind || try(:kind),
@@ -49,8 +52,7 @@ module Rails
49
52
  next if key.include?('..')
50
53
 
51
54
  result = catch(:exception) { ::I18n.translate(key, throw: true) }
52
- return result unless result.is_a?(Hash) ||
53
- result.is_a?(I18n::MissingTranslation)
55
+ return result if result.is_a?(String)
54
56
  end
55
57
  end
56
58
 
@@ -59,12 +59,14 @@ module Rails
59
59
  list << item_or_symbol
60
60
  end
61
61
 
62
- directives += GraphQL.directives_to_set(list, all_directives, source: self)
62
+ directives.merge(GraphQL.directives_to_set(list, all_directives, source: self))
63
+ self
63
64
  rescue DefinitionError => e
64
65
  raise e.class, +"#{e.message}\n Defined at: #{caller(2)[0]}"
65
66
  end
66
67
 
67
68
  # Check whether a given directive is being used
69
+ # TODO: This does not work with the instance
68
70
  def using?(item)
69
71
  directive = (item.is_a?(Symbol) || item.is_a?(String)) ? fetch!(item) : item
70
72
  raise ArgumentError, (+<<~MSG).squish unless directive < GraphQL::Directive
@@ -76,6 +78,8 @@ module Rails
76
78
 
77
79
  alias has_directive? using?
78
80
 
81
+ # TODO: Maybe implement a method to fetch a specific directive
82
+
79
83
  # Override the +all_listeners+ method since callbacks can eventually be
80
84
  # attached to objects that have directives, which then they need to
81
85
  # be combined
@@ -72,6 +72,7 @@ module Rails
72
72
 
73
73
  listeners << event_name
74
74
  events[event_name].send(unshift ? :unshift : :push, callback || block)
75
+ self
75
76
  end
76
77
  end
77
78
  end
@@ -19,7 +19,7 @@ module Rails
19
19
  fields.each_value(&subclass.method(:proxy_field))
20
20
  end
21
21
 
22
- # Return the list of fileds, only initialize when explicitly told
22
+ # Return the list of fields, only initialize when explicitly told
23
23
  def fields(initialize = nil)
24
24
  return @fields if defined?(@fields)
25
25
  return unless initialize
@@ -51,7 +51,7 @@ module Rails
51
51
  def field(name, *args, **xargs, &block)
52
52
  object = field_type.new(name, *args, **xargs, owner: self, &block)
53
53
 
54
- raise DuplicatedError, (+<<~MSG).squish if field?(object.name)
54
+ raise DuplicatedError, (+<<~MSG).squish if has_field?(object.name)
55
55
  The #{name.inspect} field is already defined and can't be redefined.
56
56
  MSG
57
57
 
@@ -63,13 +63,14 @@ module Rails
63
63
  # Add a new field to the list but use a proxy instead of a hard copy of
64
64
  # a given +field+
65
65
  def proxy_field(field, *args, **xargs, &block)
66
+ field = field.field if field.is_a?(Module) && field <= Alternative::Query
66
67
  raise ArgumentError, (+<<~MSG).squish unless field.is_a?(field_type)
67
68
  The #{field.class.name} is not a valid field.
68
69
  MSG
69
70
 
70
71
  xargs[:owner] = self
71
72
  object = field.to_proxy(*args, **xargs, &block)
72
- raise DuplicatedError, (+<<~MSG).squish if field?(object.name)
73
+ raise DuplicatedError, (+<<~MSG).squish if has_field?(object.name)
73
74
  The #{field.name.inspect} field is already defined and can't be replaced.
74
75
  MSG
75
76
 
@@ -99,8 +100,8 @@ module Rails
99
100
  list.flatten.map { |item| self[item]&.enable! }
100
101
  end
101
102
 
102
- # Check wheter a given field +object+ is defined in the list of fields
103
- def field?(object)
103
+ # Check whether a given field +object+ is defined in the list of fields
104
+ def has_field?(object)
104
105
  return false unless fields?
105
106
  object = object.name if object.is_a?(GraphQL::Field)
106
107
  fields.key?(object.is_a?(String) ? object.underscore.to_sym : object)
@@ -122,7 +123,7 @@ module Rails
122
123
  MSG
123
124
  end
124
125
 
125
- # Get the list of GraphQL names of all the fields difined
126
+ # Get the list of GraphQL names of all the fields defined
126
127
  def field_names(enabled_only = true)
127
128
  (enabled_only ? enabled_fields : lazy_each_field)&.map(&:gql_name)&.eager
128
129
  end
@@ -133,32 +134,35 @@ module Rails
133
134
  end
134
135
 
135
136
  # Import one or more field into the current list of fields
136
- def import(klass, ignore_abstract: false)
137
- return if ignore_abstract && klass.try(:abstract?)
137
+ def import(source)
138
+ # Import an alternative declaration of a field
139
+ if source.is_a?(Module) && source <= Alternative::Query
140
+ return proxy_field(type, source.field)
141
+ end
138
142
 
139
- if klass.is_a?(Module) && klass <= Alternative::Query
140
- # Import an alternative declaration of a field
141
- proxy_field(klass.field)
142
- elsif klass.is_a?(Helpers::WithFields)
143
+ case source
144
+ when Array
145
+ # Import a list of fields
146
+ source.each { |field| proxy_field(type, field) }
147
+ when Hash, Concurrent::Map
148
+ # Import a keyed list of fields
149
+ source.each_value { |field| proxy_field(type, field) }
150
+ when Helpers::WithFields
143
151
  # Import a set of fields
144
- klass.fields.each_value { |field| proxy_field(field) }
152
+ source.fields.each_value { |field| proxy_field(type, field) }
145
153
  else
146
154
  return if GraphQL.config.silence_import_warnings
147
- GraphQL.logger.warn(+"Unable to import #{klass.inspect} into #{self.name}.")
155
+ GraphQL.logger.warn(+"Unable to import #{source.inspect} into #{self.name}.")
148
156
  end
149
157
  end
150
158
 
151
159
  # Import a module containing several classes to be imported
152
- def import_all(mod, recursive: false, ignore_abstract: false)
160
+ def import_all(mod, recursive: false, **xargs)
153
161
  mod.constants.each do |const_name|
154
162
  object = mod.const_get(const_name)
155
163
 
156
- if object.is_a?(Class)
157
- import(object, ignore_abstract: ignore_abstract)
158
- elsif object.is_a?(Module) && recursive
159
- # TODO: Maybe add deepness into the recursive value
160
- import_all(object, recursive: recursive, ignore_abstract: ignore_abstract)
161
- end
164
+ import(object, **xargs) if object.is_a?(Class)
165
+ import_all(object, recursive: recursive, **xargs) if recursive && object.is_a?(Module)
162
166
  end
163
167
  end
164
168
 
@@ -179,11 +183,13 @@ module Rails
179
183
  protected
180
184
 
181
185
  # A little helper to define arguments using the :arguments key
182
- def arg(*args, **xargs, &block)
186
+ def argument(*args, **xargs, &block)
183
187
  xargs[:owner] = self
184
188
  GraphQL::Argument.new(*args, **xargs, &block)
185
189
  end
186
190
 
191
+ alias arg argument
192
+
187
193
  private
188
194
 
189
195
  def lazy_each_field
@@ -7,7 +7,7 @@ module Rails
7
7
  module Helpers
8
8
  # Helper module responsible for name stuff
9
9
  module WithName
10
- NAME_EXP = /GraphQL::(?:Type::\w+::|Directive::)?([:\w]+?)([A-Z][a-z]+)?\z/.freeze
10
+ NAME_EXP = /GraphQL::(?:Type::\w+::|Directive::)?([:\w]+)\z/.freeze
11
11
 
12
12
  # Here we define a couple of attributes used by registration
13
13
  def self.extended(other)
@@ -20,7 +20,8 @@ module Rails
20
20
  # Return the name of the object as a GraphQL name
21
21
  def gql_name
22
22
  @gql_name ||= begin
23
- name.match(NAME_EXP).try(:[], 1)&.tr(':', '')
23
+ result = name.match(NAME_EXP).try(:[], 1)
24
+ result.tr(':', '').chomp(base_type.name.demodulize) unless result.nil?
24
25
  end unless anonymous?
25
26
  end
26
27