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
@@ -3,20 +3,22 @@
3
3
  module Rails
4
4
  module GraphQL
5
5
  configure do |config|
6
- # This helps to keep track of when things were cached and registered.
7
- # Cached objects with mismatching versions needs to be upgraded or simply
8
- # reloaded. A good way to use this is to set to the commit hash, but
9
- # beware to stick to 8 characters.
6
+ # This helps to keep track of when things were cached and registered. Cached
7
+ # objects with mismatching versions need to be upgraded or simply reloaded.
8
+ # An excellent way to use this is to set it to the commit hash. TypePap will
9
+ # always use only the first 8 characters for simplicity.
10
10
  config.version = nil
11
11
 
12
- # This will be automatically mapped to +Rails.cache+. Manually setting
13
- # this property means that the object in it complies with
14
- # +ActiveSupport::Cache::Store+.
12
+ # The instance responsible for caching all the information generated by
13
+ # requests and all the other components. Manually setting this property
14
+ # means that the object in it complies with `ActiveSupport::Cache::Store`.
15
+ # This will map automatically to `Rails.cache` if kept as `nil`. This can
16
+ # also be set per Schema.
15
17
  config.cache = nil
16
18
 
17
- # If Rails cache is not properly defined, by default it is set to a
18
- # NullStore, than fallback to this option to get a memory store because
19
- # cache is extremely important, especially for subscriptions
19
+ # If Rails cache is not properly defined or just set to use a NullStore,
20
+ # this fallback will set itself up with a memory store because cache is
21
+ # crucial, especially for subscriptions.
20
22
  config.cache_fallback = -> do
21
23
  ::ActiveSupport::Cache::MemoryStore.new(max_prune_time: nil)
22
24
  end
@@ -27,42 +29,47 @@ module Rails
27
29
 
28
30
  # The list of nested paths inside of the graphql folder that does not
29
31
  # require to be in their own namespace.
30
- config.paths = %w[directives fields sources enums inputs interfaces object
31
- scalars unions].to_set
32
+ config.paths = %w[directives fields sources enums inputs interfaces objects
33
+ scalars unions concerns].to_set
32
34
 
33
- # This exposes the clean path from where a GraphQL request was started.
35
+ # This is very similar to `ActiveRecord` verbose logs, which simply show the
36
+ # path of the file that started a GraphQL request.
34
37
  config.verbose_logs = true
35
38
 
36
- # The list of parameters to omit from logger when running a GraphQL
37
- # request. Those values will be better displayed in the internal runtime
38
- # logger controller.
39
+ # The list of parameters to omit from the logger when running a GraphQL
40
+ # request. Those values are displayed better in the internal runtime logger
41
+ # controller.
39
42
  config.omit_parameters = %w[query operationName operation_name variables graphql]
40
43
 
41
- # This list will actually affect what is displayed in the logs. When it is
42
- # set to nil, it will copy its value from Rails +filter_parameters+.
44
+ # Identical to the one available on a Rails application, but exclusive for
45
+ # GraphQL operations. The list of parameters to display as filtered in the
46
+ # logs. When it is nil, it will use the same as the Rails application.
43
47
  config.filter_parameters = nil
44
48
 
45
- # A list of ActiveRecord adapters and their specific internal naming used
46
- # to compound the accessors for direct query serialization.
49
+ # A list of all `ActiveRecord` adapters supported. When an adapter is
50
+ # supported, it will map the database types into GraphQL types using proper
51
+ # aliases. Plus, it will have the method to map models attributes to their
52
+ # equivalent fields.
47
53
  config.ar_adapters = {
48
54
  'Mysql2' => { key: :mysql, path: "#{__dir__}/adapters/mysql_adapter" },
49
55
  'PostgreSQL' => { key: :pg, path: "#{__dir__}/adapters/pg_adapter" },
50
56
  'SQLite' => { key: :sqlite, path: "#{__dir__}/adapters/sqlite_adapter" },
51
57
  }
52
58
 
53
- # For all the input object type defined, auto add the following prefix to
54
- # their name, so we don't have to define classes like +PointInputInput+.
59
+ # The suffix that is added automatically to all the Input type objects. This
60
+ # prevents situations like `PointInputInput`. If your inputs have a
61
+ # different suffix, change this value to it.
55
62
  config.auto_suffix_input_objects = 'Input'
56
63
 
57
- # Introspection is enabled by default. Changing this will affect all the
58
- # schemas off the application and reduce memory usage. This can also be
59
- # set at per schema.
60
- config.enable_introspection = true
64
+ # Introspection is enabled by default. It is recommended to only use
65
+ # introspection during development and tests, never in production.
66
+ # This can also be set per schema level.
67
+ config.enable_introspection = false
61
68
 
62
69
  # Define the names of the schema/operations types. The single "_" is a
63
- # suggestion so that in an application that has, most likely, a
64
- # Subscription type, it does not generate a conflict. Plus, it is easy to
65
- # spot that it is something internal.
70
+ # suggestion. In an application that has a Subscription object, it will
71
+ # prevent the conflict. Plus, it is easy to spot that it is something
72
+ # internal. This can also be set per Schema.
66
73
  config.schema_type_names = {
67
74
  query: '_Query',
68
75
  mutation: '_Mutation',
@@ -71,13 +78,15 @@ module Rails
71
78
 
72
79
  # For performance purposes, this gem implements a
73
80
  # {JsonCollector}[rdoc-ref:Rails::GraphQL::Collectors::JsonCollector].
74
- # If you prefer to use the normal hash to string serialization, you can
75
- # disable this option.
81
+ # You can disable this option if you prefer to use the standard
82
+ # hash-to-string serialization provided by `ActiveSupport`.
83
+ # This can also be set per Schema.
76
84
  config.enable_string_collector = true
77
85
 
78
86
  # Set what is de default expected output type of GraphQL requests. String
79
- # combined with the previous setting has the best performance. On console,
80
- # it will automatically shift to hash.
87
+ # combined with the previous setting has the best performance. On the
88
+ # console, it will automatically shift to Hash. This can also be set per
89
+ # Schema.
81
90
  config.default_response_format = :string
82
91
 
83
92
  # Specifies if the results of operations should be encoded with
@@ -85,60 +94,63 @@ module Rails
85
94
  # See also https://github.com/rails/rails/blob/master/activesupport/lib/active_support/json/encoding.rb
86
95
  config.encode_with_active_support = false
87
96
 
88
- # Enable the ability of a callback to dynamically inject arguments to the
97
+ # Enable the ability of a callback to inject arguments dynamically into the
89
98
  # calling method.
90
99
  config.callback_inject_arguments = true
91
100
 
92
- # Enable the ability of a callback to dynamically inject named arguments
93
- # to the calling method.
101
+ # Enable the ability of a callback to inject named arguments dynamically
102
+ # into the calling method.
94
103
  config.callback_inject_named_arguments = true
95
104
 
96
- # When importing fields into other places, if the given class is
97
- # incompatible it will display an warning. This can make such warning be
98
- # silenced.
105
+ # When importing fields from modules or other objects, a warning is
106
+ # displayed for any given element that was not able to be correctly
107
+ # imported. You can silence such warnings by changing this option.
99
108
  config.silence_import_warnings = false
100
109
 
101
- # Enable the ability to active custom descriptions using i18n
102
- config.enable_i18n_descriptions = true
110
+ # Enable the ability to define the description of any object, field, or
111
+ # argument using I18n. It is recommended for multi-language documentation.
112
+ config.enable_i18n_descriptions = false
103
113
 
104
- # Specify the scopes for I18n translations
114
+ # The list of scopes that will be used to locate the descriptions.
105
115
  config.i18n_scopes = [
106
116
  'graphql.%{namespace}.%{kind}.%{parent}.%{name}',
107
117
  'graphql.%{namespace}.%{kind}.%{name}',
108
118
  'graphql.%{namespace}.%{name}',
109
119
  'graphql.%{kind}.%{parent}.%{name}',
110
120
  'graphql.%{kind}.%{name}',
111
- 'graphql.%{name}'
121
+ 'graphql.%{name}',
112
122
  ]
113
123
 
114
- # A list of execution strategies. Each application can add their own by
115
- # simply append a class name, preferable as string, in this list.
124
+ # A list of execution strategies. Each application can add its own by
125
+ # appending a class, preferably as a string, in this list. This can also be
126
+ # set per Schema.
116
127
  config.request_strategies = [
117
128
  'Rails::GraphQL::Request::Strategy::MultiQueryStrategy',
118
129
  'Rails::GraphQL::Request::Strategy::SequencedStrategy',
119
130
  # 'Rails::GraphQL::Request::Strategy::CachedStrategy',
120
131
  ]
121
132
 
122
- # A list of all possible rails-graphql-compatible sources.
133
+ # A list of all possible ruby-to-graphql compatible sources.
123
134
  config.sources = [
124
135
  'Rails::GraphQL::Source::ActiveRecordSource',
125
136
  ]
126
137
 
127
- # A list of all available subscription providers which bases on
128
- # Rails::GraphQL::SubscriptionProvider::Base
138
+ # A list of all available subscription providers.
129
139
  config.subscription_providers = [
130
140
  'Rails::GraphQL::Subscription::Provider::ActionCable',
131
141
  ]
132
142
 
133
- # The default subscription provider for all the schemas
143
+ # The default subscription provider for all schemas. This can also be set
144
+ # per Schema.
134
145
  config.default_subscription_provider = config.subscription_providers.first
135
146
 
136
- # The default value for fields about their ability of being broadcasted
147
+ # The default value for fields about their ability to be broadcasted. This
148
+ # can also be set per Schema.
137
149
  config.default_subscription_broadcastable = nil
138
150
 
139
151
  # A list of known dependencies that can be requested and included in any
140
- # schema. This is the best place for other gems to add their own
141
- # dependencies and allow users to pick them.
152
+ # Schema. This is the best place for other gems to add their own resources
153
+ # and allow users to enable them.
142
154
  config.known_dependencies = {
143
155
  scalar: {
144
156
  any: "#{__dir__}/type/scalar/any_scalar",
@@ -150,20 +162,35 @@ module Rails
150
162
  time: "#{__dir__}/type/scalar/time_scalar",
151
163
  json: "#{__dir__}/type/scalar/json_scalar",
152
164
  },
165
+ enum: {},
166
+ input: {},
167
+ interface: {},
168
+ object: {},
169
+ union: {},
153
170
  directive: {
154
171
  # cached: "#{__dir__}/directive/cached_directive",
155
172
  },
156
173
  }
157
174
 
158
- # The method that should be used to parse literal input values when they
159
- # are provided as hash. +JSON.parse+ only supports keys wrapped in quotes,
160
- # to support keys without quotes, you can use +Psych.method(:safe_load)+,
161
- # which behaves closer to YAML, but the received value is ensure to be
162
- # wrapped in "{}". If that produces unexpected results, you can assign a
163
- # proc and then parse the value in any other way, like
164
- # +->(value) { anything }+
175
+ # The method that should be used to parse literal input values when they are
176
+ # provided as Hash. `JSON.parse` only supports keys wrapped in quotes. You
177
+ # can use `Psych.method(:safe_load)` to support keys without quotes, which
178
+ # behaves closer to YAML. The received value is ensured to be wrapped in
179
+ # "{}". If that produces unexpected results, you can assign a proc and then
180
+ # parse the value in any other way.
165
181
  config.literal_input_parser = JSON.method(:parse)
166
182
 
183
+ # A mapping for the internal parameters and where they should be taken
184
+ # from. You can point to nested values using dot notation.
185
+ # TODO: Needs implementation
186
+ config.params_mapping = {
187
+ query: 'query',
188
+ variables: 'variables',
189
+ operation_name: 'operation_name',
190
+ query_cache_key: 'extensions.persistedQuery.sha256Hash',
191
+ query_cache_version: 'extensions.persistedQuery.version',
192
+ }
193
+
167
194
  # TODO: To be implemented
168
195
  # allow_query_serialization
169
196
  end
@@ -16,7 +16,6 @@ module Rails
16
16
  When false, the underlying element will be automatically marked as null.
17
17
  DESC
18
18
 
19
- # TODO: On attach does not covers default value per operation variable scenario
20
19
  on(:attach) do |source|
21
20
  source.skip! unless args[:if]
22
21
  end
@@ -16,7 +16,6 @@ module Rails
16
16
  When true, the underlying element will be automatically marked as null.
17
17
  DESC
18
18
 
19
- # TODO: On attach does not covers default value per operation variable scenario
20
19
  on(:attach) do |source|
21
20
  source.skip! if args[:if]
22
21
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rails
4
+ module GraphQL
5
+ # = GraphQL Spec Specified By Directive
6
+ #
7
+ # Provides a scalar specification URL for specifying the behavior of
8
+ # custom scalar types.
9
+ class Directive::SpecifiedByDirective < Directive
10
+ self.spec_object = true
11
+
12
+ placed_on :scalar
13
+
14
+ desc <<~DESC
15
+ A built-in directive used within the type system definition language to provide
16
+ a scalar specification URL for specifying the behavior of custom scalar types.
17
+ DESC
18
+
19
+ argument :url, :string, null: false, desc: <<~DESC
20
+ Point to a human-readable specification of the data format.
21
+ DESC
22
+ end
23
+ end
24
+ end
@@ -44,6 +44,13 @@ module Rails
44
44
  :directive
45
45
  end
46
46
 
47
+ # Ensure to return the directive class
48
+ def base_type
49
+ GraphQL::Directive
50
+ end
51
+
52
+ alias gid_base_class base_type
53
+
47
54
  # Return the name of the object as a GraphQL name, ensure to use the
48
55
  # first letter as lower case when being auto generated
49
56
  def gql_name
@@ -71,11 +78,6 @@ module Rails
71
78
  @locations = list.to_set
72
79
  end
73
80
 
74
- # Ensure to return the directive class
75
- def gid_base_class
76
- GraphQL::Directive
77
- end
78
-
79
81
  # A helper method that allows directives to be initialized while
80
82
  # correctly parsing the arguments
81
83
  def build(**xargs)
@@ -97,10 +99,11 @@ module Rails
97
99
  def inspect
98
100
  return super if eql?(GraphQL::Directive)
99
101
 
102
+ repeatable = ' [repeatable]' if repeatable?
100
103
  args = all_arguments&.each_value&.map(&:inspect)
101
104
  args = args.force if args.respond_to?(:force)
102
105
  args = args.presence && "(#{args.join(', ')})"
103
- +"#<GraphQL::Directive @#{gql_name}#{args}>"
106
+ +"#<GraphQL::Directive @#{gql_name}#{repeatable}#{args}>"
104
107
  end
105
108
 
106
109
  private
@@ -126,7 +129,7 @@ module Rails
126
129
  GraphQL.enumerate(setting).map do |item|
127
130
  next item unless item.is_a?(String) || item.is_a?(Symbol)
128
131
  GraphQL.type_map.fetch(item, namespaces: namespaces) ||
129
- ::GraphQL.const_get(item)
132
+ ::GraphQL.const_get(item, false)
130
133
  end
131
134
  end
132
135
 
@@ -141,30 +144,23 @@ module Rails
141
144
  Invalid locations for @#{gql_name}: #{invalid.force.to_sentence}.
142
145
  MSG
143
146
  end
144
-
145
- # Allows checking value existence
146
- def respond_to_missing?(method_name, *)
147
- (const_defined?(method_name) rescue nil) || autoload?(method_name) || super
148
- end
149
-
150
- # Allow fast creation of values
151
- def method_missing(method_name, *args, **xargs, &block)
152
- const_get(method_name)&.new(*args, **xargs, &block) || super
153
- rescue ::NameError
154
- super
155
- end
156
147
  end
157
148
 
149
+ # Marks if the directive may be used repeatedly at a single location
150
+ class_attribute :repeatable, instance_accessor: false, default: false
151
+
158
152
  self.abstract = true
159
153
 
160
154
  autoload :DeprecatedDirective
161
155
  autoload :IncludeDirective
162
156
  autoload :SkipDirective
157
+ autoload :SpecifiedByDirective
163
158
 
164
159
  autoload :CachedDirective
165
160
 
166
- delegate :locations, :gql_name, :gid_base_class, to: :class
161
+ delegate :locations, :gql_name, :gid_base_class, :repeatable?, to: :class
167
162
 
163
+ # TODO: This filters are a bit confusing now, but `for` is working for @deprecated
168
164
  event_filter(:for) do |options, event|
169
165
  sanitize_objects(options).any?(&event.source.method(:of_type?))
170
166
  end
@@ -177,7 +173,7 @@ module Rails
177
173
  event.key?(:phase) && GraphQL.enumerate(options).include?(event[:phase])
178
174
  end
179
175
 
180
- attr_reader :args
176
+ attr_reader :args, :event
181
177
 
182
178
  def initialize(args = nil, **xargs)
183
179
  @args = args || OpenStruct.new(xargs.transform_keys { |key| key.to_s.underscore })
@@ -211,10 +207,11 @@ module Rails
211
207
 
212
208
  # When fetching all the events, embed the actual instance as the context
213
209
  # of the callback
210
+ # TODO: Maybe add a soft cached, based on the total number of events
214
211
  def all_events
215
212
  return unless self.class.events?
216
213
 
217
- @all_events ||= self.class.all_events.transform_values do |events|
214
+ self.class.all_events.transform_values do |events|
218
215
  events.map { |item| Callback.set_context(item, self) }
219
216
  end
220
217
  end
@@ -237,18 +234,27 @@ module Rails
237
234
  MSG
238
235
  end
239
236
 
237
+ # This allows combining directives
238
+ def +(other)
239
+ [self, other].flatten
240
+ end
241
+
242
+ alias_method :&, :+
243
+
240
244
  def inspect
241
- args = all_arguments&.map do |name, arg|
245
+ args = all_arguments&.filter_map do |name, arg|
242
246
  +"#{arg.gql_name}: #{@args[name].inspect}" unless @args[name].nil?
243
- end&.compact
247
+ end
244
248
 
245
249
  args = args.presence && +"(#{args.join(', ')})"
250
+ repeatable = ' [repeatable]' if repeatable?
246
251
  unbound = ' # unbound' unless defined?(@owner)
247
- +"@#{gql_name}#{args}#{unbound}"
252
+ +"@#{gql_name}#{repeatable}#{args}#{unbound}"
248
253
  end
249
254
 
250
255
  %i[to_global_id to_gid to_gid_param].each do |method_name|
251
256
  define_method(method_name) do
257
+ # TODO: The option is kind of broken, because they should always be a Hash
252
258
  self.class.public_send(method_name, args_as_json&.compact || '')
253
259
  end
254
260
  end
@@ -7,7 +7,7 @@ module Rails
7
7
  # This class is responsible for triggering events. It also contains the
8
8
  # +data+ that can be used on the event handlers.
9
9
  class Event
10
- attr_reader :source, :data, :name, :object, :last_result
10
+ attr_reader :source, :data, :event_name, :object, :last_result
11
11
 
12
12
  alias event itself
13
13
 
@@ -34,7 +34,7 @@ module Rails
34
34
  @collect = data.delete(:collect?)
35
35
  @reverse = data.delete(:reverse?)
36
36
 
37
- @name = name
37
+ @event_name = name
38
38
  @data = data
39
39
  @source = source
40
40
  @layers = []
@@ -42,9 +42,10 @@ module Rails
42
42
 
43
43
  # Check if the provided +other+ is equal to the source of the event. If
44
44
  # other is a directive, then check if the source is using that directive
45
+ # TODO: Other cannot be an instance
45
46
  def same_source?(other)
46
47
  if other.is_a?(Directive) || (other.is_a?(Module) && other < Directive)
47
- source.using?(other)
48
+ event_name == :attach || source.using?(other)
48
49
  else
49
50
  source == other
50
51
  end
@@ -81,6 +82,8 @@ module Rails
81
82
  end
82
83
  end
83
84
 
85
+ alias on_instance set_on
86
+
84
87
  # From the list of all given objects, run the +trigger_object+
85
88
  def trigger_all(*objects)
86
89
  catchable(:stack) do
@@ -102,7 +105,7 @@ module Rails
102
105
  old_items, old_object, old_result, @object = @items, @object, @last_result, object
103
106
 
104
107
  catchable(:object) do
105
- events ||= object.all_events.try(:[], name)
108
+ events ||= object.all_events.try(:[], event_name)
106
109
  stop if events.blank?
107
110
 
108
111
  @items = @reverse ? events.reverse_each : events.each
@@ -136,8 +139,6 @@ module Rails
136
139
  # Do not do anything when missing next/super
137
140
  end
138
141
 
139
- alias call_super call_next
140
-
141
142
  protected
142
143
 
143
144
  alias args_source itself
@@ -16,11 +16,6 @@ module Rails
16
16
  end
17
17
  end
18
18
 
19
- # Just add the callbacks setup to the field
20
- def self.included(other)
21
- other.event_types(:authorize, append: true)
22
- end
23
-
24
19
  # Add either settings for authorization or a block to be executed. It
25
20
  # returns +self+ for chain purposes
26
21
  def authorize(*args, **xargs, &block)
@@ -28,11 +28,6 @@ module Rails
28
28
  @default = deserialize(@default) if @default.is_a?(::GQLParser::Token)
29
29
  end
30
30
 
31
- # Prevent input fields from being further configured using a block
32
- def configure
33
- raise ArgumentError, +'Input fields can\'t be further configured using blocks'
34
- end
35
-
36
31
  # Allow change the default value for the input
37
32
  def apply_changes(**xargs, &block)
38
33
  @default = xargs[:default] if xargs.key?(:default)
@@ -5,7 +5,7 @@ module Rails
5
5
  # = GraphQL Mutation Field
6
6
  #
7
7
  # This is an extension of a normal output field, which just add extra
8
- # validation and ensurance that the +perform+ step can be executed
8
+ # validation and insurance that the +perform+ step can be executed
9
9
  #
10
10
  # ==== Options
11
11
  #
@@ -58,17 +58,16 @@ module Rails
58
58
  # Get the performer that can be already defined or used through the
59
59
  # +method_name+ if that is callable
60
60
  def performer
61
- @performer ||= callable?(perform_method_name) \
62
- ? Callback.new(self, :perform, perform_method_name) \
63
- : false
61
+ return @performer if defined?(@performer)
62
+
63
+ @performer = callable?(perform_method_name)
64
+ @performer = Callback.new(self, :perform, perform_method_name) if @performer
64
65
  end
65
66
 
66
67
  # Ensures that the performer is defined
67
68
  def validate!(*)
68
69
  super if defined? super
69
70
 
70
- binding.pry unless performer.present?
71
-
72
71
  raise ValidationError, (+<<~MSG).squish unless performer.present?
73
72
  The "#{gql_name}" mutation field must have a perform action through a given
74
73
  block or a method named #{method_name} on #{owner.class.name}.
@@ -29,10 +29,12 @@ module Rails
29
29
  include Field::TypedField
30
30
 
31
31
  module Proxied # :nodoc: all
32
+ Field.proxyable_methods %w[broadcastable?], klass: self
33
+
32
34
  def all_arguments
33
35
  inherited = field.all_arguments
34
36
  return inherited unless defined?(@arguments)
35
- inherited.blank? ? super : inherited + super
37
+ inherited.blank? ? super : inherited.merge(super)
36
38
  end
37
39
 
38
40
  def has_argument?(name)
@@ -43,12 +45,17 @@ module Rails
43
45
  super || field.arguments?
44
46
  end
45
47
 
48
+ # TODO: Break events into directive/type/local
49
+ # because type events should only be added
50
+ # from the proxy
46
51
  def all_events
47
52
  if (inherited = super).nil?
48
53
  field.all_events
49
54
  elsif (proxied = field.all_events).nil?
50
55
  inherited
51
56
  else
57
+ # The order is reversed because events from
58
+ # the proxy must come first
52
59
  Helpers.merge_hash_array(proxied, inherited)
53
60
  end
54
61
  end
@@ -163,6 +170,10 @@ module Rails
163
170
  defined?(@broadcastable) && @broadcastable
164
171
  end
165
172
 
173
+ def entry_point?
174
+ owner.is_a?(Helpers::WithSchemaFields)
175
+ end
176
+
166
177
  protected
167
178
 
168
179
  # Check if the given +value+ is a valid array as output
@@ -177,7 +188,7 @@ module Rails
177
188
 
178
189
  # Properly display the owner section when the field is owned by a Schema
179
190
  def inspect_owner
180
- owner.is_a?(Helpers::WithSchemaFields) ? +"#{owner.name}[:#{schema_type}]" : super
191
+ entry_point? ? +"#{owner.name}[:#{schema_type}]" : super
181
192
  end
182
193
 
183
194
  def proxied
@@ -25,10 +25,9 @@ module Rails
25
25
  # * <tt>:alias</tt> - Same as the +:as+ key (defaults to nil).
26
26
  module Field::ProxiedField
27
27
  delegate_missing_to :field
28
- delegate :leaf_type?, :array?, :internal?, :valid_input?, :valid_output?,
29
- :to_json, :as_json, :deserialize, :valid?, :proxied_owner, to: :field
28
+ delegate :leaf_type?, :array?, :internal?, :proxied_owner, to: :field
30
29
 
31
- Field.proxyable_methods %w[name gql_name method_name resolver description
30
+ Field.proxyable_methods %w[name gql_name method_name description
32
31
  null? nullable? enabled?], klass: self
33
32
 
34
33
  def initialize(field, owner:, **xargs, &block)
@@ -49,7 +48,7 @@ module Rails
49
48
  super || field == other
50
49
  end
51
50
 
52
- # Allow chaging most of the general kind-independent initialize settings
51
+ # Allow changing most of the general kind-independent initialize settings
53
52
  def apply_changes(**xargs, &block)
54
53
  if (deprecated = xargs[:deprecated])
55
54
  xargs[:directives] = ::Array.wrap(xargs[:directives])
@@ -62,7 +61,8 @@ module Rails
62
61
  @directives = GraphQL.directives_to_set(xargs[:directives], source: self) \
63
62
  if xargs.key?(:directives)
64
63
 
65
- @desc = xargs[:desc]&.strip_heredoc&.chomp if xargs.key?(:desc)
64
+ self.description = xargs[:desc] if xargs.key?(:desc)
65
+ self.description = xargs[:description] if xargs.key?(:description)
66
66
  @enabled = xargs.fetch(:enabled, !xargs.fetch(:disabled, false)) \
67
67
  if xargs.key?(:enabled) || xargs.key?(:disabled)
68
68
 
@@ -72,7 +72,7 @@ module Rails
72
72
 
73
73
  # Override this to include proxied owners
74
74
  def all_owners
75
- super + proxied_owner.all_owners
75
+ super + @field.all_owners
76
76
  end
77
77
 
78
78
  # Return the proxied field
@@ -18,7 +18,7 @@ module Rails
18
18
 
19
19
  # Just add the callbacks setup to the field
20
20
  def self.included(other)
21
- other.event_types(:prepare, :finalize, append: true, expose: true)
21
+ other.send(:expose_events!, :organized, :finalize, :prepared, :prepare)
22
22
  other.alias_method(:before_resolve, :prepare)
23
23
  other.alias_method(:after_resolve, :finalize)
24
24
  end