rails-graphql 0.2.1 → 1.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/console.rb +18 -0
- data/ext/extconf.h +3 -0
- data/ext/extconf.rb +1 -54
- data/ext/gql_parser.c +631 -0
- data/ext/gql_parser.h +21 -0
- data/ext/shared.c +477 -0
- data/ext/shared.h +177 -0
- data/lib/generators/graphql/channel_generator.rb +27 -0
- data/lib/generators/graphql/controller_generator.rb +9 -4
- data/lib/generators/graphql/install_generator.rb +49 -0
- data/lib/generators/graphql/schema_generator.rb +9 -4
- data/lib/generators/graphql/templates/channel.erb +7 -0
- data/lib/generators/graphql/templates/config.rb +97 -0
- data/lib/generators/graphql/templates/controller.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +5 -3
- data/lib/gql_parser.so +0 -0
- data/lib/rails/graphql/adapters/mysql_adapter.rb +59 -0
- data/lib/rails/graphql/adapters/pg_adapter.rb +25 -22
- data/lib/rails/graphql/adapters/sqlite_adapter.rb +17 -14
- data/lib/rails/graphql/alternative/field_set.rb +48 -0
- data/lib/rails/graphql/alternative/mutation.rb +17 -0
- data/lib/rails/graphql/alternative/query.rb +98 -0
- data/lib/rails/graphql/alternative/subscription.rb +18 -0
- data/lib/rails/graphql/alternative.rb +20 -0
- data/lib/rails/graphql/argument.rb +25 -26
- data/lib/rails/graphql/callback.rb +30 -14
- data/lib/rails/graphql/collectors/hash_collector.rb +26 -7
- data/lib/rails/graphql/collectors/idented_collector.rb +10 -7
- data/lib/rails/graphql/collectors/json_collector.rb +43 -17
- data/lib/rails/graphql/collectors.rb +4 -4
- data/lib/rails/graphql/config.rb +154 -23
- data/lib/rails/graphql/directive/cached_directive.rb +33 -0
- data/lib/rails/graphql/directive/deprecated_directive.rb +10 -10
- data/lib/rails/graphql/directive/include_directive.rb +4 -4
- data/lib/rails/graphql/directive/skip_directive.rb +4 -4
- data/lib/rails/graphql/directive/specified_by_directive.rb +24 -0
- data/lib/rails/graphql/directive.rb +134 -73
- data/lib/rails/graphql/errors.rb +33 -4
- data/lib/rails/graphql/event.rb +21 -9
- data/lib/rails/graphql/field/authorized_field.rb +17 -6
- data/lib/rails/graphql/field/input_field.rb +8 -12
- data/lib/rails/graphql/field/mutation_field.rb +43 -9
- data/lib/rails/graphql/field/output_field.rb +112 -12
- data/lib/rails/graphql/field/proxied_field.rb +35 -26
- data/lib/rails/graphql/field/resolved_field.rb +27 -25
- data/lib/rails/graphql/field/scoped_config.rb +10 -4
- data/lib/rails/graphql/field/subscription_field.rb +123 -0
- data/lib/rails/graphql/field/typed_field.rb +69 -24
- data/lib/rails/graphql/field.rb +89 -74
- data/lib/rails/graphql/global_id.rb +89 -0
- data/lib/rails/graphql/helpers/attribute_delegator.rb +5 -5
- data/lib/rails/graphql/helpers/inherited_collection/array.rb +51 -0
- data/lib/rails/graphql/helpers/inherited_collection/base.rb +45 -0
- data/lib/rails/graphql/helpers/inherited_collection/hash.rb +88 -0
- data/lib/rails/graphql/helpers/inherited_collection.rb +25 -76
- data/lib/rails/graphql/helpers/instantiable.rb +15 -0
- data/lib/rails/graphql/helpers/leaf_from_ar.rb +7 -7
- data/lib/rails/graphql/helpers/registerable.rb +44 -62
- data/lib/rails/graphql/helpers/unregisterable.rb +16 -0
- data/lib/rails/graphql/helpers/with_arguments.rb +33 -28
- data/lib/rails/graphql/helpers/with_assignment.rb +6 -6
- data/lib/rails/graphql/helpers/with_callbacks.rb +28 -11
- data/lib/rails/graphql/helpers/with_description.rb +73 -0
- data/lib/rails/graphql/helpers/with_directives.rb +58 -30
- data/lib/rails/graphql/helpers/with_events.rb +22 -23
- data/lib/rails/graphql/helpers/with_fields.rb +86 -26
- data/lib/rails/graphql/helpers/with_global_id.rb +22 -0
- data/lib/rails/graphql/helpers/with_name.rb +44 -0
- data/lib/rails/graphql/helpers/with_namespace.rb +7 -4
- data/lib/rails/graphql/helpers/with_owner.rb +8 -7
- data/lib/rails/graphql/helpers/with_schema_fields.rb +162 -56
- data/lib/rails/graphql/helpers/with_validator.rb +9 -9
- data/lib/rails/graphql/helpers.rb +10 -3
- data/lib/rails/graphql/introspection.rb +43 -36
- data/lib/rails/graphql/railtie.rb +89 -33
- data/lib/rails/graphql/railties/app/base_channel.rb +10 -0
- data/lib/rails/graphql/railties/app/base_controller.rb +12 -0
- data/lib/rails/graphql/railties/app/views/_cable.js.erb +56 -0
- data/lib/rails/graphql/railties/app/views/_fetch.js.erb +20 -0
- data/lib/rails/graphql/railties/app/views/graphiql.html.erb +101 -0
- data/lib/rails/graphql/railties/base_generator.rb +5 -17
- data/lib/rails/graphql/railties/channel.rb +157 -0
- data/lib/rails/graphql/railties/controller.rb +91 -25
- data/lib/rails/graphql/railties/controller_runtime.rb +5 -5
- data/lib/rails/graphql/railties/log_subscriber.rb +81 -14
- data/lib/rails/graphql/request/arguments.rb +26 -50
- data/lib/rails/graphql/request/backtrace.rb +212 -0
- data/lib/rails/graphql/request/component/field.rb +98 -70
- data/lib/rails/graphql/request/component/fragment.rb +80 -26
- data/lib/rails/graphql/request/component/operation/subscription.rb +162 -4
- data/lib/rails/graphql/request/component/operation.rb +73 -34
- data/lib/rails/graphql/request/component/spread.rb +79 -27
- data/lib/rails/graphql/request/component/typename.rb +28 -13
- data/lib/rails/graphql/request/component.rb +77 -36
- data/lib/rails/graphql/request/context.rb +19 -9
- data/lib/rails/graphql/request/errors.rb +16 -6
- data/lib/rails/graphql/request/event.rb +23 -8
- data/lib/rails/graphql/request/helpers/directives.rb +69 -27
- data/lib/rails/graphql/request/helpers/selection_set.rb +57 -25
- data/lib/rails/graphql/request/helpers/value_writers.rb +24 -19
- data/lib/rails/graphql/request/prepared_data.rb +100 -0
- data/lib/rails/graphql/request/steps/authorizable.rb +24 -14
- data/lib/rails/graphql/request/steps/organizable.rb +111 -49
- data/lib/rails/graphql/request/steps/{prepareable.rb → preparable.rb} +21 -8
- data/lib/rails/graphql/request/steps/{resolveable.rb → resolvable.rb} +16 -7
- data/lib/rails/graphql/request/strategy/cached_strategy.rb +64 -0
- data/lib/rails/graphql/request/strategy/dynamic_instance.rb +6 -6
- data/lib/rails/graphql/request/strategy/multi_query_strategy.rb +6 -13
- data/lib/rails/graphql/request/strategy/sequenced_strategy.rb +9 -9
- data/lib/rails/graphql/request/strategy.rb +147 -77
- data/lib/rails/graphql/request/subscription.rb +82 -0
- data/lib/rails/graphql/request.rb +353 -104
- data/lib/rails/graphql/schema.rb +251 -106
- data/lib/rails/graphql/shortcuts.rb +33 -8
- data/lib/rails/graphql/source/active_record/builders.rb +64 -51
- data/lib/rails/graphql/source/active_record_source.rb +158 -82
- data/lib/rails/graphql/source/base.rb +83 -0
- data/lib/rails/graphql/source/builder.rb +115 -0
- data/lib/rails/graphql/source/scoped_arguments.rb +39 -21
- data/lib/rails/graphql/source.rb +90 -228
- data/lib/rails/graphql/subscription/provider/action_cable.rb +113 -0
- data/lib/rails/graphql/subscription/provider/base.rb +192 -0
- data/lib/rails/graphql/subscription/provider.rb +18 -0
- data/lib/rails/graphql/subscription/store/base.rb +141 -0
- data/lib/rails/graphql/subscription/store/memory.rb +136 -0
- data/lib/rails/graphql/subscription/store.rb +19 -0
- data/lib/rails/graphql/subscription.rb +17 -0
- data/lib/rails/graphql/to_gql.rb +29 -32
- data/lib/rails/graphql/type/creator.rb +196 -0
- data/lib/rails/graphql/type/enum/directive_location_enum.rb +11 -11
- data/lib/rails/graphql/type/enum/type_kind_enum.rb +3 -3
- data/lib/rails/graphql/type/enum.rb +44 -50
- data/lib/rails/graphql/type/input.rb +92 -25
- data/lib/rails/graphql/type/interface.rb +29 -28
- data/lib/rails/graphql/type/object/directive_object.rb +10 -9
- data/lib/rails/graphql/type/object/enum_value_object.rb +3 -3
- data/lib/rails/graphql/type/object/field_object.rb +24 -6
- data/lib/rails/graphql/type/object/input_value_object.rb +6 -7
- data/lib/rails/graphql/type/object/schema_object.rb +5 -8
- data/lib/rails/graphql/type/object/type_object.rb +62 -25
- data/lib/rails/graphql/type/object.rb +34 -26
- data/lib/rails/graphql/type/scalar/any_scalar.rb +30 -0
- data/lib/rails/graphql/type/scalar/bigint_scalar.rb +5 -5
- data/lib/rails/graphql/type/scalar/binary_scalar.rb +5 -3
- data/lib/rails/graphql/type/scalar/boolean_scalar.rb +8 -8
- data/lib/rails/graphql/type/scalar/date_scalar.rb +5 -3
- data/lib/rails/graphql/type/scalar/date_time_scalar.rb +5 -3
- data/lib/rails/graphql/type/scalar/decimal_scalar.rb +5 -3
- data/lib/rails/graphql/type/scalar/float_scalar.rb +5 -5
- data/lib/rails/graphql/type/scalar/id_scalar.rb +6 -5
- data/lib/rails/graphql/type/scalar/int_scalar.rb +6 -5
- data/lib/rails/graphql/type/scalar/json_scalar.rb +41 -0
- data/lib/rails/graphql/type/scalar/string_scalar.rb +18 -4
- data/lib/rails/graphql/type/scalar/time_scalar.rb +8 -6
- data/lib/rails/graphql/type/scalar.rb +26 -23
- data/lib/rails/graphql/type/union.rb +21 -18
- data/lib/rails/graphql/type.rb +43 -26
- data/lib/rails/graphql/type_map.rb +268 -165
- data/lib/rails/graphql/uri.rb +167 -0
- data/lib/rails/graphql/version.rb +19 -3
- data/lib/rails/graphql.rake +3 -0
- data/lib/rails/graphql.rb +91 -56
- data/lib/rails-graphql.rb +1 -1
- data/test/assets/en.yml +29 -0
- data/test/assets/introspection-mem.txt +1 -1
- data/test/assets/introspection.gql +2 -0
- data/test/assets/mem.gql +86 -99
- data/test/assets/mysql.gql +406 -0
- data/test/assets/sqlite.gql +96 -73
- data/test/assets/translate.gql +346 -0
- data/test/config.rb +19 -8
- data/test/graphql/schema_test.rb +14 -50
- data/test/graphql/source_test.rb +8 -85
- data/test/graphql/type/enum_test.rb +207 -203
- data/test/graphql/type/input_test.rb +14 -9
- data/test/graphql/type/interface_test.rb +12 -9
- data/test/graphql/type/object_test.rb +8 -2
- data/test/graphql/type/scalar/any_scalar_test.rb +38 -0
- data/test/graphql/type/scalar/boolean_scalar_test.rb +6 -3
- data/test/graphql/type/scalar/json_scalar_test.rb +23 -0
- data/test/graphql/type_map_test.rb +63 -81
- data/test/graphql/type_test.rb +0 -19
- data/test/graphql_test.rb +1 -1
- data/test/integration/{authorization/authorization_test.rb → authorization_test.rb} +40 -14
- data/test/integration/config.rb +36 -3
- data/test/integration/customization_test.rb +39 -0
- data/test/integration/global_id_test.rb +99 -0
- data/test/integration/memory/star_wars_introspection_test.rb +24 -16
- data/test/integration/memory/star_wars_query_test.rb +54 -3
- data/test/integration/memory/star_wars_validation_test.rb +3 -3
- data/test/integration/mysql/star_wars_introspection_test.rb +25 -0
- data/test/integration/persisted_query_test.rb +87 -0
- data/test/integration/resolver_precedence_test.rb +154 -0
- data/test/integration/schemas/memory.rb +24 -10
- data/test/integration/schemas/mysql.rb +62 -0
- data/test/integration/schemas/sqlite.rb +21 -12
- data/test/integration/sqlite/star_wars_global_id_test.rb +89 -0
- data/test/integration/sqlite/star_wars_introspection_test.rb +10 -0
- data/test/integration/sqlite/star_wars_query_test.rb +14 -1
- data/test/integration/translate_test.rb +73 -0
- data/test/test_ext.rb +16 -13
- metadata +125 -161
- data/ext/depend +0 -3
- data/ext/graphqlparser/Ast.cpp +0 -346
- data/ext/graphqlparser/Ast.h +0 -1214
- data/ext/graphqlparser/AstNode.h +0 -36
- data/ext/graphqlparser/AstVisitor.h +0 -137
- data/ext/graphqlparser/GraphQLParser.cpp +0 -76
- data/ext/graphqlparser/GraphQLParser.h +0 -55
- data/ext/graphqlparser/JsonVisitor.cpp +0 -161
- data/ext/graphqlparser/JsonVisitor.cpp.inc +0 -456
- data/ext/graphqlparser/JsonVisitor.h +0 -121
- data/ext/graphqlparser/JsonVisitor.h.inc +0 -110
- data/ext/graphqlparser/VERSION +0 -1
- data/ext/graphqlparser/c/GraphQLAst.cpp +0 -324
- data/ext/graphqlparser/c/GraphQLAst.h +0 -180
- data/ext/graphqlparser/c/GraphQLAstForEachConcreteType.h +0 -44
- data/ext/graphqlparser/c/GraphQLAstNode.cpp +0 -25
- data/ext/graphqlparser/c/GraphQLAstNode.h +0 -33
- data/ext/graphqlparser/c/GraphQLAstToJSON.cpp +0 -21
- data/ext/graphqlparser/c/GraphQLAstToJSON.h +0 -24
- data/ext/graphqlparser/c/GraphQLAstVisitor.cpp +0 -55
- data/ext/graphqlparser/c/GraphQLAstVisitor.h +0 -53
- data/ext/graphqlparser/c/GraphQLParser.cpp +0 -35
- data/ext/graphqlparser/c/GraphQLParser.h +0 -54
- data/ext/graphqlparser/dump_json_ast.cpp +0 -48
- data/ext/graphqlparser/lexer.lpp +0 -324
- data/ext/graphqlparser/parser.ypp +0 -693
- data/ext/graphqlparser/parsergen/lexer.cpp +0 -2633
- data/ext/graphqlparser/parsergen/lexer.h +0 -528
- data/ext/graphqlparser/parsergen/location.hh +0 -189
- data/ext/graphqlparser/parsergen/parser.tab.cpp +0 -3300
- data/ext/graphqlparser/parsergen/parser.tab.hpp +0 -646
- data/ext/graphqlparser/parsergen/position.hh +0 -179
- data/ext/graphqlparser/parsergen/stack.hh +0 -156
- data/ext/graphqlparser/syntaxdefs.h +0 -19
- data/ext/libgraphqlparser/AstNode.h +0 -36
- data/ext/libgraphqlparser/CMakeLists.txt +0 -148
- data/ext/libgraphqlparser/CONTRIBUTING.md +0 -23
- data/ext/libgraphqlparser/GraphQLParser.cpp +0 -76
- data/ext/libgraphqlparser/GraphQLParser.h +0 -55
- data/ext/libgraphqlparser/JsonVisitor.cpp +0 -161
- data/ext/libgraphqlparser/JsonVisitor.h +0 -121
- data/ext/libgraphqlparser/LICENSE +0 -22
- data/ext/libgraphqlparser/README.clang-tidy +0 -7
- data/ext/libgraphqlparser/README.md +0 -84
- data/ext/libgraphqlparser/ast/ast.ast +0 -203
- data/ext/libgraphqlparser/ast/ast.py +0 -61
- data/ext/libgraphqlparser/ast/c.py +0 -100
- data/ext/libgraphqlparser/ast/c.pyc +0 -0
- data/ext/libgraphqlparser/ast/c_impl.py +0 -61
- data/ext/libgraphqlparser/ast/c_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/c_visitor_impl.py +0 -39
- data/ext/libgraphqlparser/ast/c_visitor_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/casing.py +0 -26
- data/ext/libgraphqlparser/ast/casing.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx.py +0 -197
- data/ext/libgraphqlparser/ast/cxx.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_impl.py +0 -61
- data/ext/libgraphqlparser/ast/cxx_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_header.py +0 -42
- data/ext/libgraphqlparser/ast/cxx_json_visitor_header.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.py +0 -80
- data/ext/libgraphqlparser/ast/cxx_json_visitor_impl.pyc +0 -0
- data/ext/libgraphqlparser/ast/cxx_visitor.py +0 -64
- data/ext/libgraphqlparser/ast/cxx_visitor.pyc +0 -0
- data/ext/libgraphqlparser/ast/js.py +0 -65
- data/ext/libgraphqlparser/ast/license.py +0 -10
- data/ext/libgraphqlparser/ast/license.pyc +0 -0
- data/ext/libgraphqlparser/c/GraphQLAstNode.cpp +0 -25
- data/ext/libgraphqlparser/c/GraphQLAstNode.h +0 -33
- data/ext/libgraphqlparser/c/GraphQLAstToJSON.cpp +0 -21
- data/ext/libgraphqlparser/c/GraphQLAstToJSON.h +0 -24
- data/ext/libgraphqlparser/c/GraphQLAstVisitor.cpp +0 -55
- data/ext/libgraphqlparser/c/GraphQLAstVisitor.h +0 -53
- data/ext/libgraphqlparser/c/GraphQLParser.cpp +0 -35
- data/ext/libgraphqlparser/c/GraphQLParser.h +0 -54
- data/ext/libgraphqlparser/clang-tidy-all.sh +0 -3
- data/ext/libgraphqlparser/cmake/version.cmake +0 -16
- data/ext/libgraphqlparser/dump_json_ast.cpp +0 -48
- data/ext/libgraphqlparser/go/README.md +0 -20
- data/ext/libgraphqlparser/go/callbacks.go +0 -18
- data/ext/libgraphqlparser/go/gotest.go +0 -64
- data/ext/libgraphqlparser/lexer.lpp +0 -324
- data/ext/libgraphqlparser/libgraphqlparser.pc.in +0 -11
- data/ext/libgraphqlparser/parser.ypp +0 -693
- data/ext/libgraphqlparser/parsergen/lexer.cpp +0 -2633
- data/ext/libgraphqlparser/parsergen/lexer.h +0 -528
- data/ext/libgraphqlparser/parsergen/location.hh +0 -189
- data/ext/libgraphqlparser/parsergen/parser.tab.cpp +0 -3300
- data/ext/libgraphqlparser/parsergen/parser.tab.hpp +0 -646
- data/ext/libgraphqlparser/parsergen/position.hh +0 -179
- data/ext/libgraphqlparser/parsergen/stack.hh +0 -156
- data/ext/libgraphqlparser/python/CMakeLists.txt +0 -14
- data/ext/libgraphqlparser/python/README.md +0 -5
- data/ext/libgraphqlparser/python/example.py +0 -31
- data/ext/libgraphqlparser/syntaxdefs.h +0 -19
- data/ext/libgraphqlparser/test/BuildCAPI.c +0 -5
- data/ext/libgraphqlparser/test/CMakeLists.txt +0 -25
- data/ext/libgraphqlparser/test/JsonVisitorTests.cpp +0 -28
- data/ext/libgraphqlparser/test/ParserTests.cpp +0 -352
- data/ext/libgraphqlparser/test/kitchen-sink.graphql +0 -59
- data/ext/libgraphqlparser/test/kitchen-sink.json +0 -1
- data/ext/libgraphqlparser/test/schema-kitchen-sink.graphql +0 -78
- data/ext/libgraphqlparser/test/schema-kitchen-sink.json +0 -1
- data/ext/libgraphqlparser/test/valgrind.supp +0 -33
- data/ext/version.cpp +0 -21
- data/lib/graphqlparser.so +0 -0
- data/lib/rails/graphql/native/functions.rb +0 -38
- data/lib/rails/graphql/native/location.rb +0 -41
- data/lib/rails/graphql/native/pointers.rb +0 -23
- data/lib/rails/graphql/native/visitor.rb +0 -349
- data/lib/rails/graphql/native.rb +0 -56
- data/test/integration/schemas/authorization.rb +0 -12
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'action_cable'
|
4
|
+
|
5
|
+
module Rails
|
6
|
+
module GraphQL
|
7
|
+
module Subscription
|
8
|
+
module Provider
|
9
|
+
# = GraphQL Action Cable Subscription Provider
|
10
|
+
#
|
11
|
+
# The subscription provider associated with Rails Action Cable, that
|
12
|
+
# delivers subscription notifications through an Action Cable Channel
|
13
|
+
# TODO: Try to serialize and deserialize the origin
|
14
|
+
class ActionCable < Base
|
15
|
+
INTERNAL_CHANNEL = 'rails-graphql:events'
|
16
|
+
|
17
|
+
attr_reader :cable, :prefix
|
18
|
+
|
19
|
+
def initialize(*args, **options)
|
20
|
+
@cable = options.fetch(:cable, ::ActionCable)
|
21
|
+
@prefix = options.fetch(:prefix, 'rails-graphql')
|
22
|
+
|
23
|
+
@event_callback = ->(message) do
|
24
|
+
method_name, args, xargs = Marshal.load(message)
|
25
|
+
@mutex.synchronize { send(method_name, *args, **xargs) }
|
26
|
+
end
|
27
|
+
|
28
|
+
super
|
29
|
+
end
|
30
|
+
|
31
|
+
def shutdown
|
32
|
+
@pubsub = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def accepts?(operation)
|
36
|
+
operation.request.origin.is_a?(::ActionCable::Channel::Base)
|
37
|
+
end
|
38
|
+
|
39
|
+
def add(*subscriptions)
|
40
|
+
with_pubsub do
|
41
|
+
subscriptions.each do |item|
|
42
|
+
log(:added, item)
|
43
|
+
store.add(item)
|
44
|
+
stream_from(item)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def async_remove(item)
|
50
|
+
return if (item = store.fetch(item)).nil?
|
51
|
+
cable.server.broadcast(stream_name(item), unsubscribed_payload)
|
52
|
+
store.remove(item)
|
53
|
+
|
54
|
+
log(:removed, item)
|
55
|
+
end
|
56
|
+
|
57
|
+
def async_update(item, data = nil, **xargs)
|
58
|
+
return if (item = store.fetch(item)).nil?
|
59
|
+
removing = false
|
60
|
+
|
61
|
+
log(:updated, item) do
|
62
|
+
data = execute(item, **xargs) if data.nil?
|
63
|
+
store.update!(item)
|
64
|
+
|
65
|
+
unless (removing = unsubscribing?(data))
|
66
|
+
data = { 'result' => data, 'more' => true }
|
67
|
+
cable.server.broadcast(stream_name(item), data)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
async_remove(item) if removing
|
72
|
+
end
|
73
|
+
|
74
|
+
def stream_name(item)
|
75
|
+
"#{prefix}:#{item.sid}"
|
76
|
+
end
|
77
|
+
|
78
|
+
protected
|
79
|
+
|
80
|
+
def stream_from(item)
|
81
|
+
item.origin.stream_from(stream_name(item))
|
82
|
+
end
|
83
|
+
|
84
|
+
def execute(item, **xargs)
|
85
|
+
super(item, origin: item.origin, **xargs, as: :hash)
|
86
|
+
end
|
87
|
+
|
88
|
+
def async_exec(method_name, *args, **xargs)
|
89
|
+
payload = [method_name, args, store.serialize(**xargs)]
|
90
|
+
with_pubsub { @pubsub.broadcast(INTERNAL_CHANNEL, Marshal.dump(payload)) }
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
|
94
|
+
def with_pubsub(&callback)
|
95
|
+
success = -> { cable.server.event_loop.post(&callback) }
|
96
|
+
return success.call if defined?(@pubsub) && !@pubsub.nil?
|
97
|
+
|
98
|
+
cable.server.pubsub.subscribe(INTERNAL_CHANNEL, @event_callback, success)
|
99
|
+
@pubsub = cable.server.pubsub
|
100
|
+
end
|
101
|
+
|
102
|
+
def validate!
|
103
|
+
super
|
104
|
+
|
105
|
+
raise ValidationError, (+<<~MSG).squish if @prefix.blank?
|
106
|
+
Unable to setup #{self.class.name} because a proper prefix was not provided.
|
107
|
+
MSG
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module GraphQL
|
5
|
+
module Subscription
|
6
|
+
module Provider
|
7
|
+
# = GraphQL Base Subscription Provider
|
8
|
+
#
|
9
|
+
# The base class for all the other subscription providers, which defines
|
10
|
+
# the necessary interfaces to install and stream the subscription to
|
11
|
+
# their right places
|
12
|
+
#
|
13
|
+
# As a way to properly support ActiveRecord objects as part of the scope
|
14
|
+
# in a way that does not require queries and instance, a hash scope,
|
15
|
+
# where we have the class as the key and one or more ids as values,
|
16
|
+
# must be supported. In general, a implementation using +.hash+ is
|
17
|
+
# recommended because +User.find(1).hash == User.class.hash ^ 1.hash+
|
18
|
+
class Base
|
19
|
+
# An abstract type won't appear in the introspection and will not be
|
20
|
+
# instantiated by requests
|
21
|
+
class_attribute :abstract, instance_accessor: false, default: false
|
22
|
+
|
23
|
+
delegate :fetch, :search, :find_each, to: :store
|
24
|
+
|
25
|
+
class << self
|
26
|
+
|
27
|
+
# Make sure that abstract classes cannot be instantiated
|
28
|
+
def new(*, **)
|
29
|
+
return super unless self.abstract
|
30
|
+
|
31
|
+
raise StandardError, (+<<~MSG).squish
|
32
|
+
#{name} is abstract and cannot be used as a subscription provider.
|
33
|
+
MSG
|
34
|
+
end
|
35
|
+
|
36
|
+
# Make sure to run the provided +methods+ in async mode. Use the
|
37
|
+
# lock to identify if it's already running in async or not
|
38
|
+
def async_exec(*method_names)
|
39
|
+
method_names.each do |method_name|
|
40
|
+
async_method_name = :"async_#{method_name}"
|
41
|
+
|
42
|
+
class_eval do
|
43
|
+
return warn((+<<~MSG).squish) if method_defined?(async_method_name)
|
44
|
+
Already async #{method_name}
|
45
|
+
MSG
|
46
|
+
|
47
|
+
alias_method async_method_name, method_name
|
48
|
+
|
49
|
+
define_method(method_name) do |*args, **xargs|
|
50
|
+
if @mutex.owned?
|
51
|
+
send(async_method_name, *args, **xargs)
|
52
|
+
else
|
53
|
+
async_exec(async_method_name, *args, **xargs)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialize(**options)
|
63
|
+
@store = options.fetch(:store, Store::Memory.new)
|
64
|
+
@logger = options.fetch(:logger, GraphQL.logger)
|
65
|
+
@mutex = Mutex.new
|
66
|
+
|
67
|
+
validate!
|
68
|
+
end
|
69
|
+
|
70
|
+
# Use this method to remove variables that needs to be restarted when
|
71
|
+
# the provider is doing a refresh. Remember to keep the data in the
|
72
|
+
# store so that it can still recover and keep posting updates
|
73
|
+
def shutdown
|
74
|
+
end
|
75
|
+
|
76
|
+
# Before even generating the item, check if the operation can be
|
77
|
+
# subscribed
|
78
|
+
def accepts?(operation)
|
79
|
+
raise NotImplementedError, +"#{self.class.name} does not implement accepts?"
|
80
|
+
end
|
81
|
+
|
82
|
+
# Add one or more subscriptions to the provider
|
83
|
+
def add(*subscriptions)
|
84
|
+
raise NotImplementedError, +"#{self.class.name} does not implement add"
|
85
|
+
end
|
86
|
+
|
87
|
+
# Remove one subscription from the provider, assuming that they will
|
88
|
+
# be properly notified about the removal. For a group operation, use
|
89
|
+
# +search_and_remove+
|
90
|
+
async_exec def remove(item)
|
91
|
+
raise NotImplementedError, +"#{self.class.name} does not implement remove"
|
92
|
+
end
|
93
|
+
|
94
|
+
# Update one single subscription, for broadcasting, use +update_all+
|
95
|
+
# or +search_and_update+. You can provide the +data+ that will be sent
|
96
|
+
# to upstream, skipping it from being collected from a request
|
97
|
+
async_exec def update(item, data = nil, **xargs)
|
98
|
+
raise NotImplementedError, +"#{self.class.name} does not implement update"
|
99
|
+
end
|
100
|
+
|
101
|
+
# A simple shortcut for calling remove on each individual sid
|
102
|
+
async_exec def remove_all(*items)
|
103
|
+
items.each(&method(:remove))
|
104
|
+
end
|
105
|
+
|
106
|
+
# A simple shortcut for calling update on each individual sid
|
107
|
+
async_exec def update_all(*sids, **xargs)
|
108
|
+
return if sids.blank?
|
109
|
+
|
110
|
+
enum = GraphQL.enumerate(store.fetch(*sids))
|
111
|
+
enum.group_by(&:operation_id).each_value do |subscriptions|
|
112
|
+
data = execute(subscriptions.first, **xargs, broadcasting: true) \
|
113
|
+
unless subscriptions.one? || first.broadcastable?
|
114
|
+
|
115
|
+
subscriptions.each { |item| update(item, data, **xargs) }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# A shortcut for finding the subscriptions and then remove them
|
120
|
+
async_exec def search_and_remove(**options)
|
121
|
+
find_each(**options, &method(:remove))
|
122
|
+
end
|
123
|
+
|
124
|
+
# A shortcut for finding the subscriptions and then updating them
|
125
|
+
async_exec def search_and_update(**options)
|
126
|
+
xargs = options.slice(:data_for)
|
127
|
+
update_all(*find_each(**options), **xargs)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Get the payload that should be used when unsubscribing
|
131
|
+
def unsubscribed_payload
|
132
|
+
Request::Component::Operation::Subscription::UNSUBSCRIBED_PAYLOAD
|
133
|
+
end
|
134
|
+
|
135
|
+
# Check if the given +value+ indicates that it is unsubscribing
|
136
|
+
def unsubscribing?(value)
|
137
|
+
value == Request::Component::Operation::Subscription::UNSUBSCRIBED_RESULT
|
138
|
+
end
|
139
|
+
|
140
|
+
protected
|
141
|
+
attr_reader :store, :logger
|
142
|
+
|
143
|
+
# Make sure to set sub provider as not abstract
|
144
|
+
def inherited(other)
|
145
|
+
other.abstract = false
|
146
|
+
super
|
147
|
+
end
|
148
|
+
|
149
|
+
# Check if the given +object+ is a subscription instance
|
150
|
+
def instance?(object)
|
151
|
+
object.is_a?(Request::Subscription)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Logo a given +event+ for the given +item+
|
155
|
+
def log(event, item = nil, &block)
|
156
|
+
data = { item: item, type: event, provider: self }
|
157
|
+
ActiveSupport::Notifications.instrument('subscription.graphql', **data, &block)
|
158
|
+
end
|
159
|
+
|
160
|
+
# Create a new request and execute, but using all the information
|
161
|
+
# stored in the provided +subscription+
|
162
|
+
def execute(subscription, broadcasting: false, **xargs)
|
163
|
+
catch(:skip_subscription_update) do
|
164
|
+
context = subscription.context.dup
|
165
|
+
context[:broadcasting] = true if broadcasting
|
166
|
+
xargs.reverse_merge(context: context, as: :string)
|
167
|
+
|
168
|
+
namespace = subscription.schema
|
169
|
+
Request.execute(nil, **xargs, namespace: namespace, hash: subscription.sid)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Make sure to rewrite this method so that you can properly execute
|
174
|
+
# methods asynchronously. Remember to
|
175
|
+
def async_exec(method_name, *args, **xargs)
|
176
|
+
@mutex.synchronize { send(method_name, *args, **xargs) }
|
177
|
+
end
|
178
|
+
|
179
|
+
# Make sure that the settings provided are enough to operate
|
180
|
+
def validate!
|
181
|
+
valid = defined?(@store) && @store.is_a?(Subscription::Store::Base)
|
182
|
+
raise ValidationError, (+<<~MSG).squish unless valid
|
183
|
+
Unable to setup #{self.class.name} because a proper store was not
|
184
|
+
defined, "#{@store.inspect}" was provided.
|
185
|
+
MSG
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module GraphQL
|
5
|
+
module Subscription
|
6
|
+
# = GraphQL Subscription Provider
|
7
|
+
#
|
8
|
+
# Subscription provider holds all the possible options for handlers of
|
9
|
+
# subscriptions, which all should inherit from Provider::Base
|
10
|
+
module Provider
|
11
|
+
extend ActiveSupport::Autoload
|
12
|
+
|
13
|
+
autoload :Base
|
14
|
+
autoload :ActionCable
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module GraphQL
|
5
|
+
module Subscription
|
6
|
+
module Store
|
7
|
+
# = GraphQL Base Subscription Store
|
8
|
+
#
|
9
|
+
# The base class for all the other subscription stores, which defines
|
10
|
+
# the necessary interfaces to keep track of all the subscriptions
|
11
|
+
#
|
12
|
+
# Be careful with each subscription context. Although there are ways to
|
13
|
+
# clean it up (by implementing a +subscription_context+ callback into
|
14
|
+
# the field), it is still the most dangerous and heavy object that can
|
15
|
+
# be placed into the store. The problem with in memory store is that it
|
16
|
+
# does not work with a Rails application running cross-processes. On the
|
17
|
+
# other hand, file, Redis, or Database-based stores can find it
|
18
|
+
# difficult to save the context and bring it back to Rails again
|
19
|
+
#
|
20
|
+
# The closest best way to be safe about the context is relying on
|
21
|
+
# +ActiveJob::Arguments+ to serialize and deserialize it (which aligns
|
22
|
+
# with all possible arguments that jobs and receive and how they are
|
23
|
+
# usually properly stored in several different providers for ActiveJob)
|
24
|
+
class Base
|
25
|
+
# An abstract type won't appear in the introspection and will not be
|
26
|
+
# instantiated by requests
|
27
|
+
class_attribute :abstract, instance_accessor: false, default: false
|
28
|
+
|
29
|
+
class << self
|
30
|
+
|
31
|
+
# Make sure that abstract classes cannot be instantiated
|
32
|
+
def new(*)
|
33
|
+
return super unless self.abstract
|
34
|
+
|
35
|
+
raise StandardError, (+<<~MSG).squish
|
36
|
+
#{name} is abstract and cannot be used as a subscription store.
|
37
|
+
MSG
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get the list of provided +xargs+ for search and serialize them
|
43
|
+
def serialize(**xargs)
|
44
|
+
xargs
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return all the sids stored
|
48
|
+
def all
|
49
|
+
raise NotImplementedError, +"#{self.class.name} does not implement all"
|
50
|
+
end
|
51
|
+
|
52
|
+
# Add a new subscription to the store, saving in a way it can be easily
|
53
|
+
# searched at any point
|
54
|
+
def add(subscription)
|
55
|
+
raise NotImplementedError, +"#{self.class.name} does not implement add"
|
56
|
+
end
|
57
|
+
|
58
|
+
# Fetch one or more subscriptions by their ids
|
59
|
+
def fetch(*sids)
|
60
|
+
raise NotImplementedError, +"#{self.class.name} does not implement fetch"
|
61
|
+
end
|
62
|
+
|
63
|
+
# Remove a given subscription from the store by its id or instance
|
64
|
+
def remove(item)
|
65
|
+
raise NotImplementedError, +"#{self.class.name} does not implement remove"
|
66
|
+
end
|
67
|
+
|
68
|
+
# Marks that a subscription has received an update
|
69
|
+
def update!(item)
|
70
|
+
raise NotImplementedError, +"#{self.class.name} does not implement update!"
|
71
|
+
end
|
72
|
+
|
73
|
+
# Check if a given sid or instance is stored
|
74
|
+
def has?(item)
|
75
|
+
raise NotImplementedError, +"#{self.class.name} does not implement has?"
|
76
|
+
end
|
77
|
+
|
78
|
+
# Search one or more subscriptions by the list of provided options and
|
79
|
+
# return the list of sids that matched. A block can be provided to go
|
80
|
+
# through each of the found results, yield the object itself instead
|
81
|
+
# of the sid
|
82
|
+
def search(**options, &block)
|
83
|
+
raise NotImplementedError, +"#{self.class.name} does not implement search"
|
84
|
+
end
|
85
|
+
|
86
|
+
alias find_each search
|
87
|
+
|
88
|
+
protected
|
89
|
+
|
90
|
+
# Check if the given +object+ is a subscription instance
|
91
|
+
def instance?(object)
|
92
|
+
object.is_a?(Request::Subscription)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Transform a scope in several possible scopes, as in:
|
96
|
+
# nil => nil
|
97
|
+
# :user => [[:user]]
|
98
|
+
# User.find(1) => [[NNN1]] # .hash
|
99
|
+
# [User.find(1), :sample] => [[NNN1, :sample]]
|
100
|
+
# { User => 1, other: :profile } => [[NNN1, :profile]]
|
101
|
+
# { User => [1, 2], other: :profile } => [[NNN1, :profile], [NNN2, :profile]]
|
102
|
+
def possible_scopes(scope)
|
103
|
+
return if scope.nil? || scope === EMPTY_ARRAY
|
104
|
+
|
105
|
+
list = Array.wrap(scope).each_with_object([]) do |value, result|
|
106
|
+
result << options = []
|
107
|
+
|
108
|
+
next GraphQL.enumerate(value).each do |val|
|
109
|
+
options << hash_for(val)
|
110
|
+
end unless value.is_a?(Hash)
|
111
|
+
|
112
|
+
value.each.with_index do |(key, sub_value), idx|
|
113
|
+
result << options = [] if idx > 0
|
114
|
+
|
115
|
+
klass_arg = key if key.is_a?(Class)
|
116
|
+
GraphQL.enumerate(sub_value).each do |val|
|
117
|
+
options << hash_for(val, klass_arg)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
list.reduce(:product).flatten.each_slice(list.size).map { |a| a.reduce(:^) }
|
123
|
+
end
|
124
|
+
|
125
|
+
# By default, get the hash of the value. If class is provided, add
|
126
|
+
# it as part of the hash (similar to how ActiveRecord calculates
|
127
|
+
# the hash for a model's record)
|
128
|
+
def hash_for(value, klass = nil)
|
129
|
+
if !klass.nil?
|
130
|
+
klass.hash ^ value.hash
|
131
|
+
elsif value.is_a?(Numeric)
|
132
|
+
value
|
133
|
+
else
|
134
|
+
value.hash
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module GraphQL
|
5
|
+
module Subscription
|
6
|
+
module Store
|
7
|
+
# = GraphQL Memory Subscription Store
|
8
|
+
#
|
9
|
+
# This store will save all the subscriptions in a very similar way that
|
10
|
+
# the TypeMap works, using nested concurrent maps that points out to
|
11
|
+
# them. Everything is based on the sid of the subscription
|
12
|
+
class Memory < Base
|
13
|
+
attr_reader :list, :index
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
# The list store a simple association between sid and
|
17
|
+
@list = Concurrent::Map.new
|
18
|
+
|
19
|
+
# This store the index in a way that is possible to search
|
20
|
+
# subscriptions in a fast manner
|
21
|
+
@index = Concurrent::Map.new do |h1, key1| # Fields
|
22
|
+
scopes = Concurrent::Map.new do |h2, key2| # Scopes
|
23
|
+
arguments = Concurrent::Map.new do |h3, key3| # Arguments
|
24
|
+
h3.fetch_or_store(key3, Concurrent::Array.new) # SIDs
|
25
|
+
end
|
26
|
+
|
27
|
+
h2.fetch_or_store(key2, arguments)
|
28
|
+
end
|
29
|
+
|
30
|
+
h1.fetch_or_store(key1, scopes)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def serialize(**xargs)
|
35
|
+
return xargs if !xargs.key?(:field) || xargs[:field].is_a?(Numeric)
|
36
|
+
|
37
|
+
xargs[:field] = hash_for(xargs[:field])
|
38
|
+
xargs[:scope] = possible_scopes(xargs[:scope])
|
39
|
+
xargs[:args] = Array.wrap(xargs[:args]).map(&method(:hash_for))
|
40
|
+
xargs
|
41
|
+
end
|
42
|
+
|
43
|
+
def all
|
44
|
+
list.keys
|
45
|
+
end
|
46
|
+
|
47
|
+
def add(subscription)
|
48
|
+
if has?(subscription.sid)
|
49
|
+
raise ::ArgumentError, +"SID #{subscription.sid} is already taken."
|
50
|
+
end
|
51
|
+
|
52
|
+
# Rewrite the scope, to save memory
|
53
|
+
scope = possible_scopes(subscription.scope)&.first
|
54
|
+
subscription.instance_variable_set(:@scope, scope)
|
55
|
+
|
56
|
+
# Save to the list and to the index
|
57
|
+
list[subscription.sid] = subscription
|
58
|
+
index_set = subscription_to_index(subscription).reduce(index, &:[])
|
59
|
+
index_set << subscription.sid
|
60
|
+
subscription.sid
|
61
|
+
end
|
62
|
+
|
63
|
+
def fetch(*sids)
|
64
|
+
return if sids.none?
|
65
|
+
|
66
|
+
items = sids.map do |item|
|
67
|
+
instance?(item) ? item : list[item]
|
68
|
+
end
|
69
|
+
|
70
|
+
items.one? ? items.first : items
|
71
|
+
end
|
72
|
+
|
73
|
+
def remove(item)
|
74
|
+
return unless has?(item)
|
75
|
+
|
76
|
+
instance = instance?(item) ? item : fetch(item)
|
77
|
+
path = subscription_to_index(instance)
|
78
|
+
index.delete(instance.sid)
|
79
|
+
|
80
|
+
f_level = index[path[0]]
|
81
|
+
s_level = f_level[path[1]]
|
82
|
+
a_level = s_level[path[2]]
|
83
|
+
|
84
|
+
a_level.delete(instance.sid)
|
85
|
+
s_level.delete(path[2]) if a_level.empty?
|
86
|
+
f_level.delete(path[1]) if s_level.empty?
|
87
|
+
index.delete(path[0]) if f_level.empty?
|
88
|
+
end
|
89
|
+
|
90
|
+
def update!(item)
|
91
|
+
(instance?(item) ? item : fetch(item)).update!
|
92
|
+
end
|
93
|
+
|
94
|
+
def has?(item)
|
95
|
+
list.key?(instance?(item) ? item.sid : item)
|
96
|
+
end
|
97
|
+
|
98
|
+
def search(**xargs, &block)
|
99
|
+
xargs = serialize(**xargs)
|
100
|
+
field, scope, args = xargs.values_at(:field, :scope, :args)
|
101
|
+
|
102
|
+
if field.nil? && args.nil? && scope.nil?
|
103
|
+
list.each(&block) unless block.nil?
|
104
|
+
return all
|
105
|
+
end
|
106
|
+
|
107
|
+
[].tap do |result|
|
108
|
+
GraphQL.enumerate(field || index.keys).each do |key1|
|
109
|
+
GraphQL.enumerate(scope || index[key1].keys).each do |key2|
|
110
|
+
GraphQL.enumerate(args || index[key2].keys).each do |key3|
|
111
|
+
items = index.fetch(key1, nil)&.fetch(key2, nil)&.fetch(key3, nil)
|
112
|
+
items.each(&list.method(:[])).each(&block) unless block.nil?
|
113
|
+
result.concat(items || EMPTY_ARRAY)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
alias find_each search
|
121
|
+
|
122
|
+
protected
|
123
|
+
|
124
|
+
# Turn the request subscription into into the path of the index
|
125
|
+
def subscription_to_index(subscription)
|
126
|
+
[
|
127
|
+
subscription.field.hash,
|
128
|
+
subscription.scope,
|
129
|
+
subscription.args.hash,
|
130
|
+
]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module GraphQL
|
5
|
+
module Subscription
|
6
|
+
# = GraphQL Subscription Store
|
7
|
+
#
|
8
|
+
# Subscription store holds all the possible options for storing the
|
9
|
+
# subscriptions, allowing to segmentation by field, variables, and several
|
10
|
+
# other things according to the necessity
|
11
|
+
module Store
|
12
|
+
extend ActiveSupport::Autoload
|
13
|
+
|
14
|
+
autoload :Base
|
15
|
+
autoload :Memory
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Rails
|
4
|
+
module GraphQL
|
5
|
+
# = GraphQL Subscription
|
6
|
+
#
|
7
|
+
# A namespace for storing subscription-related objects like the provider
|
8
|
+
# for a stream/websocket provider, and the store, for where the
|
9
|
+
# subscriptions are stored
|
10
|
+
module Subscription
|
11
|
+
extend ActiveSupport::Autoload
|
12
|
+
|
13
|
+
autoload :Store
|
14
|
+
autoload :Provider
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|