rails-graphql 1.0.0.beta → 1.0.0.rc2

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.
Files changed (137) 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 +13 -8
  17. data/lib/rails/graphql/alternative/subscription.rb +2 -1
  18. data/lib/rails/graphql/alternative.rb +4 -0
  19. data/lib/rails/graphql/argument.rb +5 -3
  20. data/lib/rails/graphql/callback.rb +10 -8
  21. data/lib/rails/graphql/collectors/hash_collector.rb +12 -1
  22. data/lib/rails/graphql/collectors/json_collector.rb +21 -0
  23. data/lib/rails/graphql/config.rb +86 -59
  24. data/lib/rails/graphql/directive/include_directive.rb +0 -1
  25. data/lib/rails/graphql/directive/skip_directive.rb +0 -1
  26. data/lib/rails/graphql/directive/specified_by_directive.rb +24 -0
  27. data/lib/rails/graphql/directive.rb +31 -25
  28. data/lib/rails/graphql/event.rb +7 -6
  29. data/lib/rails/graphql/field/authorized_field.rb +0 -5
  30. data/lib/rails/graphql/field/input_field.rb +0 -5
  31. data/lib/rails/graphql/field/mutation_field.rb +5 -6
  32. data/lib/rails/graphql/field/output_field.rb +13 -2
  33. data/lib/rails/graphql/field/proxied_field.rb +6 -6
  34. data/lib/rails/graphql/field/resolved_field.rb +1 -1
  35. data/lib/rails/graphql/field/subscription_field.rb +35 -52
  36. data/lib/rails/graphql/field/typed_field.rb +26 -2
  37. data/lib/rails/graphql/field.rb +20 -19
  38. data/lib/rails/graphql/global_id.rb +5 -1
  39. data/lib/rails/graphql/helpers/inherited_collection/array.rb +1 -0
  40. data/lib/rails/graphql/helpers/inherited_collection/base.rb +3 -1
  41. data/lib/rails/graphql/helpers/inherited_collection/hash.rb +2 -1
  42. data/lib/rails/graphql/helpers/registerable.rb +1 -1
  43. data/lib/rails/graphql/helpers/with_arguments.rb +3 -2
  44. data/lib/rails/graphql/helpers/with_assignment.rb +5 -5
  45. data/lib/rails/graphql/helpers/with_callbacks.rb +3 -3
  46. data/lib/rails/graphql/helpers/with_description.rb +10 -8
  47. data/lib/rails/graphql/helpers/with_directives.rb +5 -1
  48. data/lib/rails/graphql/helpers/with_events.rb +1 -0
  49. data/lib/rails/graphql/helpers/with_fields.rb +30 -24
  50. data/lib/rails/graphql/helpers/with_name.rb +3 -2
  51. data/lib/rails/graphql/helpers/with_schema_fields.rb +75 -51
  52. data/lib/rails/graphql/introspection.rb +1 -1
  53. data/lib/rails/graphql/railtie.rb +3 -2
  54. data/lib/rails/graphql/railties/app/base_channel.rb +10 -0
  55. data/lib/rails/graphql/railties/app/base_controller.rb +12 -0
  56. data/lib/rails/graphql/railties/app/views/_cable.js.erb +56 -0
  57. data/lib/rails/graphql/railties/app/views/_fetch.js.erb +20 -0
  58. data/lib/rails/graphql/railties/app/views/graphiql.html.erb +101 -0
  59. data/lib/rails/graphql/railties/base_generator.rb +3 -9
  60. data/lib/rails/graphql/railties/channel.rb +8 -8
  61. data/lib/rails/graphql/railties/controller.rb +51 -26
  62. data/lib/rails/graphql/request/arguments.rb +2 -1
  63. data/lib/rails/graphql/request/backtrace.rb +31 -10
  64. data/lib/rails/graphql/request/component/field.rb +15 -8
  65. data/lib/rails/graphql/request/component/fragment.rb +13 -7
  66. data/lib/rails/graphql/request/component/operation/subscription.rb +4 -6
  67. data/lib/rails/graphql/request/component/operation.rb +12 -5
  68. data/lib/rails/graphql/request/component/spread.rb +13 -4
  69. data/lib/rails/graphql/request/component/typename.rb +1 -1
  70. data/lib/rails/graphql/request/component.rb +2 -0
  71. data/lib/rails/graphql/request/context.rb +1 -1
  72. data/lib/rails/graphql/request/event.rb +6 -2
  73. data/lib/rails/graphql/request/helpers/directives.rb +1 -0
  74. data/lib/rails/graphql/request/helpers/selection_set.rb +10 -4
  75. data/lib/rails/graphql/request/helpers/value_writers.rb +8 -5
  76. data/lib/rails/graphql/request/prepared_data.rb +3 -1
  77. data/lib/rails/graphql/request/steps/organizable.rb +1 -1
  78. data/lib/rails/graphql/request/steps/preparable.rb +1 -1
  79. data/lib/rails/graphql/request/steps/resolvable.rb +1 -1
  80. data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +3 -3
  81. data/lib/rails/graphql/request/strategy.rb +18 -4
  82. data/lib/rails/graphql/request/subscription.rb +18 -16
  83. data/lib/rails/graphql/request.rb +71 -41
  84. data/lib/rails/graphql/schema.rb +39 -86
  85. data/lib/rails/graphql/shortcuts.rb +11 -5
  86. data/lib/rails/graphql/source/active_record/builders.rb +22 -24
  87. data/lib/rails/graphql/source/active_record_source.rb +96 -34
  88. data/lib/rails/graphql/source/base.rb +13 -40
  89. data/lib/rails/graphql/source/builder.rb +14 -22
  90. data/lib/rails/graphql/source/scoped_arguments.rb +10 -4
  91. data/lib/rails/graphql/source.rb +31 -38
  92. data/lib/rails/graphql/subscription/provider/action_cable.rb +10 -9
  93. data/lib/rails/graphql/subscription/provider/base.rb +6 -5
  94. data/lib/rails/graphql/subscription/store/base.rb +5 -9
  95. data/lib/rails/graphql/subscription/store/memory.rb +18 -9
  96. data/lib/rails/graphql/type/creator.rb +198 -0
  97. data/lib/rails/graphql/type/enum.rb +17 -9
  98. data/lib/rails/graphql/type/input.rb +30 -7
  99. data/lib/rails/graphql/type/interface.rb +15 -4
  100. data/lib/rails/graphql/type/object/directive_object.rb +6 -5
  101. data/lib/rails/graphql/type/object/input_value_object.rb +3 -4
  102. data/lib/rails/graphql/type/object/type_object.rb +40 -13
  103. data/lib/rails/graphql/type/object.rb +11 -6
  104. data/lib/rails/graphql/type/scalar/binary_scalar.rb +2 -0
  105. data/lib/rails/graphql/type/scalar/date_scalar.rb +2 -0
  106. data/lib/rails/graphql/type/scalar/date_time_scalar.rb +2 -0
  107. data/lib/rails/graphql/type/scalar/decimal_scalar.rb +2 -0
  108. data/lib/rails/graphql/type/scalar/json_scalar.rb +3 -1
  109. data/lib/rails/graphql/type/scalar/time_scalar.rb +3 -1
  110. data/lib/rails/graphql/type/scalar.rb +2 -2
  111. data/lib/rails/graphql/type/union.rb +7 -2
  112. data/lib/rails/graphql/type.rb +10 -2
  113. data/lib/rails/graphql/type_map.rb +20 -7
  114. data/lib/rails/graphql/uri.rb +5 -4
  115. data/lib/rails/graphql/version.rb +6 -2
  116. data/lib/rails/graphql.rb +11 -8
  117. data/test/assets/introspection-mem.txt +1 -1
  118. data/test/assets/introspection.gql +2 -0
  119. data/test/assets/mem.gql +74 -60
  120. data/test/assets/mysql.gql +69 -55
  121. data/test/assets/sqlite.gql +78 -64
  122. data/test/assets/translate.gql +50 -39
  123. data/test/config.rb +2 -1
  124. data/test/graphql/schema_test.rb +2 -31
  125. data/test/graphql/source_test.rb +1 -11
  126. data/test/graphql/type/interface_test.rb +8 -5
  127. data/test/graphql/type/object_test.rb +8 -2
  128. data/test/graphql/type_map_test.rb +13 -16
  129. data/test/integration/global_id_test.rb +4 -4
  130. data/test/integration/memory/star_wars_validation_test.rb +2 -2
  131. data/test/integration/mysql/star_wars_introspection_test.rb +1 -1
  132. data/test/integration/resolver_precedence_test.rb +1 -1
  133. data/test/integration/schemas/memory.rb +3 -4
  134. data/test/integration/sqlite/star_wars_global_id_test.rb +27 -21
  135. data/test/integration/sqlite/star_wars_introspection_test.rb +1 -1
  136. data/test/integration/translate_test.rb +26 -14
  137. metadata +22 -9
@@ -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,10 +7,12 @@ 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
13
- InheritedCollection.const_get(klass).new(source, ivar, type)
15
+ InheritedCollection.const_get(klass, false).new(source, ivar, type)
14
16
  end
15
17
 
16
18
  def initialize(source, ivar, type)
@@ -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
@@ -61,12 +61,12 @@ module Rails
61
61
  return if abstract?
62
62
  return super unless assigned?
63
63
 
64
- result = super
65
- return result unless (klass = safe_assigned_class)
66
- return result if GraphQL.type_map.exist?(klass, namespaces: namespaces)
64
+ super.tap do
65
+ break unless (klass = safe_assigned_class)
66
+ break if GraphQL.type_map.exist?(klass, namespaces: namespaces)
67
67
 
68
- GraphQL.type_map.register_alias(klass, to_sym, namespaces: namespaces)
69
- result
68
+ GraphQL.type_map.register_alias(klass, gql_name, namespaces: namespaces)
69
+ end
70
70
  end
71
71
 
72
72
  protected
@@ -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
- object = mod.const_get(const_name)
155
-
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
162
+ object = mod.const_get(const_name, false)
163
+
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