rails-graphql 0.2.1 → 1.0.0.beta
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 +646 -0
- data/ext/shared.c +482 -0
- data/ext/shared.h +177 -0
- 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 +36 -0
- data/lib/rails/graphql/alternative/mutation.rb +17 -0
- data/lib/rails/graphql/alternative/query.rb +93 -0
- data/lib/rails/graphql/alternative/subscription.rb +17 -0
- data/lib/rails/graphql/alternative.rb +20 -0
- data/lib/rails/graphql/argument.rb +21 -24
- data/lib/rails/graphql/callback.rb +24 -9
- data/lib/rails/graphql/collectors/hash_collector.rb +14 -6
- data/lib/rails/graphql/collectors/idented_collector.rb +10 -7
- data/lib/rails/graphql/collectors/json_collector.rb +22 -17
- data/lib/rails/graphql/collectors.rb +4 -4
- data/lib/rails/graphql/config.rb +130 -15
- 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 +5 -4
- data/lib/rails/graphql/directive/skip_directive.rb +5 -4
- data/lib/rails/graphql/directive.rb +118 -63
- data/lib/rails/graphql/errors.rb +33 -4
- data/lib/rails/graphql/event.rb +16 -5
- data/lib/rails/graphql/field/authorized_field.rb +19 -3
- data/lib/rails/graphql/field/input_field.rb +11 -10
- data/lib/rails/graphql/field/mutation_field.rb +42 -7
- data/lib/rails/graphql/field/output_field.rb +102 -13
- data/lib/rails/graphql/field/proxied_field.rb +31 -22
- data/lib/rails/graphql/field/resolved_field.rb +26 -24
- data/lib/rails/graphql/field/scoped_config.rb +10 -4
- data/lib/rails/graphql/field/subscription_field.rb +140 -0
- data/lib/rails/graphql/field/typed_field.rb +43 -22
- data/lib/rails/graphql/field.rb +70 -56
- data/lib/rails/graphql/global_id.rb +85 -0
- data/lib/rails/graphql/helpers/attribute_delegator.rb +5 -5
- data/lib/rails/graphql/helpers/inherited_collection/array.rb +50 -0
- data/lib/rails/graphql/helpers/inherited_collection/base.rb +43 -0
- data/lib/rails/graphql/helpers/inherited_collection/hash.rb +87 -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 +31 -27
- data/lib/rails/graphql/helpers/with_assignment.rb +6 -6
- data/lib/rails/graphql/helpers/with_callbacks.rb +25 -8
- data/lib/rails/graphql/helpers/with_description.rb +71 -0
- data/lib/rails/graphql/helpers/with_directives.rb +54 -30
- data/lib/rails/graphql/helpers/with_events.rb +21 -23
- data/lib/rails/graphql/helpers/with_fields.rb +76 -22
- data/lib/rails/graphql/helpers/with_global_id.rb +22 -0
- data/lib/rails/graphql/helpers/with_name.rb +43 -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 +137 -55
- 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 +88 -33
- data/lib/rails/graphql/railties/base_generator.rb +3 -9
- data/lib/rails/graphql/railties/channel.rb +157 -0
- data/lib/rails/graphql/railties/controller.rb +62 -17
- 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 +24 -49
- data/lib/rails/graphql/request/backtrace.rb +191 -0
- data/lib/rails/graphql/request/component/field.rb +86 -65
- data/lib/rails/graphql/request/component/fragment.rb +72 -24
- data/lib/rails/graphql/request/component/operation/subscription.rb +164 -4
- data/lib/rails/graphql/request/component/operation.rb +63 -31
- data/lib/rails/graphql/request/component/spread.rb +68 -25
- data/lib/rails/graphql/request/component/typename.rb +27 -12
- data/lib/rails/graphql/request/component.rb +75 -36
- data/lib/rails/graphql/request/context.rb +18 -8
- data/lib/rails/graphql/request/errors.rb +16 -6
- data/lib/rails/graphql/request/event.rb +19 -8
- data/lib/rails/graphql/request/helpers/directives.rb +68 -27
- data/lib/rails/graphql/request/helpers/selection_set.rb +51 -25
- data/lib/rails/graphql/request/helpers/value_writers.rb +18 -16
- data/lib/rails/graphql/request/prepared_data.rb +98 -0
- data/lib/rails/graphql/request/steps/authorizable.rb +24 -14
- data/lib/rails/graphql/request/steps/organizable.rb +110 -48
- data/lib/rails/graphql/request/steps/{prepareable.rb → preparable.rb} +20 -7
- data/lib/rails/graphql/request/steps/{resolveable.rb → resolvable.rb} +15 -6
- 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 +131 -75
- data/lib/rails/graphql/request/subscription.rb +80 -0
- data/lib/rails/graphql/request.rb +305 -86
- data/lib/rails/graphql/schema.rb +240 -48
- data/lib/rails/graphql/shortcuts.rb +22 -3
- data/lib/rails/graphql/source/active_record/builders.rb +49 -35
- data/lib/rails/graphql/source/active_record_source.rb +70 -54
- data/lib/rails/graphql/source/base.rb +111 -0
- data/lib/rails/graphql/source/builder.rb +128 -0
- data/lib/rails/graphql/source/scoped_arguments.rb +31 -19
- data/lib/rails/graphql/source.rb +89 -213
- data/lib/rails/graphql/subscription/provider/action_cable.rb +112 -0
- data/lib/rails/graphql/subscription/provider/base.rb +191 -0
- data/lib/rails/graphql/subscription/provider.rb +18 -0
- data/lib/rails/graphql/subscription/store/base.rb +145 -0
- data/lib/rails/graphql/subscription/store/memory.rb +127 -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/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 +34 -48
- data/lib/rails/graphql/type/input.rb +74 -23
- data/lib/rails/graphql/type/interface.rb +16 -26
- data/lib/rails/graphql/type/object/directive_object.rb +4 -4
- 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 +3 -3
- data/lib/rails/graphql/type/object/schema_object.rb +5 -8
- data/lib/rails/graphql/type/object/type_object.rb +29 -19
- data/lib/rails/graphql/type/object.rb +26 -23
- 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 +3 -3
- data/lib/rails/graphql/type/scalar/boolean_scalar.rb +8 -8
- data/lib/rails/graphql/type/scalar/date_scalar.rb +3 -3
- data/lib/rails/graphql/type/scalar/date_time_scalar.rb +3 -3
- data/lib/rails/graphql/type/scalar/decimal_scalar.rb +3 -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 +39 -0
- data/lib/rails/graphql/type/scalar/string_scalar.rb +18 -4
- data/lib/rails/graphql/type/scalar/time_scalar.rb +5 -5
- data/lib/rails/graphql/type/scalar.rb +25 -22
- data/lib/rails/graphql/type/union.rb +14 -16
- data/lib/rails/graphql/type.rb +34 -25
- data/lib/rails/graphql/type_map.rb +256 -164
- data/lib/rails/graphql/uri.rb +166 -0
- data/lib/rails/graphql/version.rb +15 -3
- data/lib/rails/graphql.rake +3 -0
- data/lib/rails/graphql.rb +85 -52
- 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/mem.gql +18 -45
- data/test/assets/mysql.gql +392 -0
- data/test/assets/sqlite.gql +21 -12
- data/test/assets/translate.gql +335 -0
- data/test/config.rb +18 -8
- data/test/graphql/schema_test.rb +12 -19
- data/test/graphql/source_test.rb +8 -75
- 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 +4 -4
- 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 +51 -66
- 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 +1 -1
- 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 +22 -7
- 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 +83 -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 +61 -0
- data/test/test_ext.rb +16 -13
- metadata +108 -157
- 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,191 @@
|
|
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)
|
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)
|
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, broadcasting: true) \
|
113
|
+
unless subscriptions.one? || first.broadcastable?
|
114
|
+
|
115
|
+
subscriptions.each { |item| update(item, data) }
|
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
|
+
update_all(*find_each(**options))
|
127
|
+
end
|
128
|
+
|
129
|
+
# Get the payload that should be used when unsubscribing
|
130
|
+
def unsubscribed_payload
|
131
|
+
Request::Component::Operation::Subscription::UNSUBSCRIBED_PAYLOAD
|
132
|
+
end
|
133
|
+
|
134
|
+
# Check if the given +value+ indicates that it is unsubscribing
|
135
|
+
def unsubscribing?(value)
|
136
|
+
value == Request::Component::Operation::Subscription::UNSUBSCRIBED_RESULT
|
137
|
+
end
|
138
|
+
|
139
|
+
protected
|
140
|
+
attr_reader :store, :logger
|
141
|
+
|
142
|
+
# Make sure to set sub provider as not abstract
|
143
|
+
def inherited(other)
|
144
|
+
other.abstract = false
|
145
|
+
super
|
146
|
+
end
|
147
|
+
|
148
|
+
# Check if the given +object+ is a subscription instance
|
149
|
+
def instance?(object)
|
150
|
+
object.is_a?(Request::Subscription)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Logo a given +event+ for the given +item+
|
154
|
+
def log(event, item = nil, &block)
|
155
|
+
data = { item: item, type: event, provider: self }
|
156
|
+
ActiveSupport::Notifications.instrument('subscription.graphql', **data, &block)
|
157
|
+
end
|
158
|
+
|
159
|
+
# Create a new request and execute, but using all the information
|
160
|
+
# stored in the provided +subscription+
|
161
|
+
def execute(subscription, broadcasting: false, **xargs)
|
162
|
+
catch(:skip_subscription_update) do
|
163
|
+
context = subscription.context.dup
|
164
|
+
context[:broadcasting] = true if broadcasting
|
165
|
+
xargs.reverse_merge(context: context, as: :string)
|
166
|
+
|
167
|
+
namespace = subscription.schema
|
168
|
+
Request.execute(nil, **xargs, namespace: namespace, hash: subscription.sid)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# Make sure to rewrite this method so that you can properly execute
|
173
|
+
# methods asynchronously. Remember to
|
174
|
+
def async_exec(method_name, *args, **xargs)
|
175
|
+
@mutex.synchronize { send(method_name, *args, **xargs) }
|
176
|
+
end
|
177
|
+
|
178
|
+
# Make sure that the settings provided are enough to operate
|
179
|
+
def validate!
|
180
|
+
valid = defined?(@store) && @store.is_a?(Subscription::Store::Base)
|
181
|
+
raise ValidationError, (+<<~MSG).squish unless valid
|
182
|
+
Unable to setup #{self.class.name} because a proper store was not
|
183
|
+
defined, "#{@store.inspect}" was provided.
|
184
|
+
MSG
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
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,145 @@
|
|
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
|
+
# Check if a given sid or instance is stored
|
69
|
+
def has?(item)
|
70
|
+
raise NotImplementedError, +"#{self.class.name} does not implement has?"
|
71
|
+
end
|
72
|
+
|
73
|
+
# Search one or more subscriptions by the list of provided options and
|
74
|
+
# return the list of sids that matched. A block can be provided to go
|
75
|
+
# through each of the found results, yield the object itself instead
|
76
|
+
# of the sid
|
77
|
+
def search(**options, &block)
|
78
|
+
raise NotImplementedError, +"#{self.class.name} does not implement search"
|
79
|
+
end
|
80
|
+
|
81
|
+
alias find_each search
|
82
|
+
|
83
|
+
protected
|
84
|
+
|
85
|
+
# Check if the given +object+ is a subscription instance
|
86
|
+
def instance?(object)
|
87
|
+
object.is_a?(Request::Subscription)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Transform a scope in several possible scopes, as in:
|
91
|
+
# nil => nil
|
92
|
+
# :user => [[:user]]
|
93
|
+
# User.find(1) => [[NNN1]] # .hash
|
94
|
+
# [User.find(1), :sample] => [[NNN1, :sample]]
|
95
|
+
# { User => 1, other: :profile } => [[NNN1, :profile]]
|
96
|
+
# { User => [1, 2], other: :profile } => [[NNN1, :profile], [NNN2, :profile]]
|
97
|
+
def possible_scopes(scope)
|
98
|
+
return if scope.nil? || scope === EMPTY_ARRAY
|
99
|
+
|
100
|
+
list = Array.wrap(scope).each_with_object([]) do |value, result|
|
101
|
+
result << options = []
|
102
|
+
|
103
|
+
next GraphQL.enumerate(value).each do |val|
|
104
|
+
options << hash_for(val)
|
105
|
+
end unless value.is_a?(Hash)
|
106
|
+
|
107
|
+
value.each.with_index do |(key, sub_value), idx|
|
108
|
+
result << options = [] if idx > 0
|
109
|
+
|
110
|
+
klass_arg = key if key.is_a?(Class)
|
111
|
+
GraphQL.enumerate(sub_value).each do |val|
|
112
|
+
options << hash_for(val, klass_arg)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
list.reduce(:product).flatten.each_slice(list.size).map { |a| a.reduce(:^) }
|
118
|
+
end
|
119
|
+
|
120
|
+
# By default, get the hash of the value. If class is provided, add
|
121
|
+
# it as part of the hash (similar to how ActiveRecord calculates
|
122
|
+
# the hash for a model's record)
|
123
|
+
def hash_for(value, klass = nil)
|
124
|
+
if !klass.nil?
|
125
|
+
klass.hash ^ value.hash
|
126
|
+
elsif extract_class_from?(value)
|
127
|
+
value.class.hash ^ value.id.hash
|
128
|
+
elsif value.is_a?(Numeric)
|
129
|
+
value
|
130
|
+
else
|
131
|
+
value.hash
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Check if ActiveRecord::Base is available and then if the object
|
136
|
+
# provided is an instance of it, so that the serialize can work
|
137
|
+
# correctly
|
138
|
+
def extract_class_from?(value)
|
139
|
+
defined?(ActiveRecord) && value.is_a?(ActiveRecord::Base)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,127 @@
|
|
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
|
+
list[subscription.sid] = subscription
|
53
|
+
index_set = subscription_to_index(subscription).reduce(index, &:[])
|
54
|
+
index_set << subscription.sid
|
55
|
+
subscription.sid
|
56
|
+
end
|
57
|
+
|
58
|
+
def fetch(*sids)
|
59
|
+
if sids.none?
|
60
|
+
nil
|
61
|
+
elsif sids.one?
|
62
|
+
list[sids.first]
|
63
|
+
else
|
64
|
+
sids.map(&list.method(:[]))
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def remove(item)
|
69
|
+
return unless has?(item)
|
70
|
+
|
71
|
+
instance = instance?(item) ? item : fetch(item)
|
72
|
+
path = subscription_to_index(instance)
|
73
|
+
index.delete(instance.sid)
|
74
|
+
|
75
|
+
f_level = index[path[0]]
|
76
|
+
s_level = f_level[path[1]]
|
77
|
+
a_level = s_level[path[2]]
|
78
|
+
|
79
|
+
a_level.delete(instance.sid)
|
80
|
+
s_level.delete(path[2]) if a_level.empty?
|
81
|
+
f_level.delete(path[1]) if s_level.empty?
|
82
|
+
index.delete(path[0]) if f_level.empty?
|
83
|
+
end
|
84
|
+
|
85
|
+
def has?(item)
|
86
|
+
list.key?(instance?(item) ? item.sid : item)
|
87
|
+
end
|
88
|
+
|
89
|
+
def search(**xargs, &block)
|
90
|
+
xargs = serialize(**xargs)
|
91
|
+
field, scope, args = xargs.values_at(:field, :scope, :args)
|
92
|
+
|
93
|
+
if field.nil? && args.nil? && scope.nil?
|
94
|
+
list.each(&block) unless block.nil?
|
95
|
+
return all
|
96
|
+
end
|
97
|
+
|
98
|
+
[].tap do |result|
|
99
|
+
GraphQL.enumerate(field || index.keys).each do |key1|
|
100
|
+
GraphQL.enumerate(scope || index[key1].keys).each do |key2|
|
101
|
+
GraphQL.enumerate(args || index[key2].keys).each do |key3|
|
102
|
+
items = index.fetch(key1, nil)&.fetch(key2, nil)&.fetch(key3, nil)
|
103
|
+
items.each(&list.method(:[])).each(&block) unless block.nil?
|
104
|
+
result.concat(items || EMPTY_ARRAY)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
alias find_each search
|
112
|
+
|
113
|
+
protected
|
114
|
+
|
115
|
+
# Turn the request subscription into into the path of the index
|
116
|
+
def subscription_to_index(subscription)
|
117
|
+
[
|
118
|
+
hash_for(subscription.field),
|
119
|
+
possible_scopes(subscription.scope)&.first,
|
120
|
+
hash_for(subscription.args),
|
121
|
+
]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
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
|
data/lib/rails/graphql/to_gql.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
require 'arel/visitors/visitor'
|
4
4
|
|
5
5
|
# rubocop:disable Naming/MethodParameterName, Naming/MethodName
|
6
|
-
module Rails
|
7
|
-
module GraphQL
|
6
|
+
module Rails
|
7
|
+
module GraphQL
|
8
8
|
# = GraphQL ToGQL
|
9
9
|
#
|
10
10
|
# This class can turn any class related to GraphQL into its GraphQL string
|
@@ -35,14 +35,13 @@ module Rails # :nodoc:
|
|
35
35
|
collector ||= Collectors::IdentedCollector.new
|
36
36
|
@with_descriptions = with_descriptions
|
37
37
|
@with_spec = with_spec.nil? ? schema.introspection? : with_spec
|
38
|
+
@namespace = schema.namespace
|
38
39
|
|
39
|
-
|
40
|
-
|
41
|
-
GraphQL.type_map.each_from(schema.namespace, base_class: :Type)
|
40
|
+
GraphQL.type_map.each_from(@namespace, base_class: :Type)
|
42
41
|
.group_by(&:kind).values_at(*DESCRIBE_TYPES)
|
43
|
-
.each do |items|
|
42
|
+
.prepend([schema]).each do |items|
|
44
43
|
items&.sort_by(&:gql_name)&.each do |item|
|
45
|
-
next if !@with_spec && item.internal?
|
44
|
+
next if !@with_spec && item.try(:internal?)
|
46
45
|
|
47
46
|
next visit_Rails_GraphQL_Type_Object(item, collector).eol \
|
48
47
|
if item.is_a?(::OpenStruct) && item.object?
|
@@ -109,8 +108,9 @@ module Rails # :nodoc:
|
|
109
108
|
visit_directives(o.directives, collector)
|
110
109
|
|
111
110
|
collector.indented(' {', '}') do
|
112
|
-
Helpers::WithSchemaFields::
|
111
|
+
Helpers::WithSchemaFields::TYPE_FIELD_CLASS.each_key do |key|
|
113
112
|
next unless key.eql?(:query) || o.fields_for(key).present?
|
113
|
+
name = o.type_name_for(key)
|
114
114
|
|
115
115
|
collector << key.to_s
|
116
116
|
collector << ': '
|
@@ -152,14 +152,6 @@ module Rails # :nodoc:
|
|
152
152
|
visit_Rails_GraphQL_Field(o, collector)
|
153
153
|
end
|
154
154
|
|
155
|
-
def visit_Rails_GraphQL_Field_OutputField(o, collector)
|
156
|
-
visit_Rails_GraphQL_Field(o, collector)
|
157
|
-
end
|
158
|
-
|
159
|
-
def visit_Rails_GraphQL_Field_InputField(o, collector)
|
160
|
-
visit_Rails_GraphQL_Field(o, collector)
|
161
|
-
end
|
162
|
-
|
163
155
|
def visit_Rails_GraphQL_Type_Enum(o, collector)
|
164
156
|
visit_description(o, collector)
|
165
157
|
collector << 'enum '
|
@@ -178,9 +170,7 @@ module Rails # :nodoc:
|
|
178
170
|
collector << o.gql_name
|
179
171
|
visit_directives(o.directives, collector)
|
180
172
|
|
181
|
-
collector.indented(' {', '}')
|
182
|
-
o.fields.each_value { |x| visit(x, collector) }
|
183
|
-
end
|
173
|
+
collector.indented(' {', '}') { visit_fields(o.fields, collector) }
|
184
174
|
end
|
185
175
|
|
186
176
|
def visit_Rails_GraphQL_Type_Interface(o, collector)
|
@@ -190,9 +180,7 @@ module Rails # :nodoc:
|
|
190
180
|
collector << o.gql_name
|
191
181
|
visit_directives(o.directives, collector)
|
192
182
|
|
193
|
-
collector.indented(' {', '}')
|
194
|
-
o.fields.each_value { |x| visit(x, collector) }
|
195
|
-
end
|
183
|
+
collector.indented(' {', '}') { visit_fields(o.fields, collector) }
|
196
184
|
end
|
197
185
|
|
198
186
|
def visit_Rails_GraphQL_Type_Object(o, collector)
|
@@ -211,9 +199,7 @@ module Rails # :nodoc:
|
|
211
199
|
|
212
200
|
visit_directives(o.directives, collector)
|
213
201
|
|
214
|
-
collector.indented(' {', '}')
|
215
|
-
o.fields.each_value { |x| visit(x, collector) }
|
216
|
-
end
|
202
|
+
collector.indented(' {', '}') { visit_fields(o.fields, collector) }
|
217
203
|
end
|
218
204
|
|
219
205
|
def visit_Rails_GraphQL_Type_Scalar(o, collector)
|
@@ -260,8 +246,16 @@ module Rails # :nodoc:
|
|
260
246
|
visit_typed_object(o, collector)
|
261
247
|
end
|
262
248
|
|
249
|
+
def visit_fields(list, collector)
|
250
|
+
return if list.blank?
|
251
|
+
|
252
|
+
list.values.sort_by do |field|
|
253
|
+
field.name == :id ? '' : field.gql_name
|
254
|
+
end.each { |x| visit(x, collector) }
|
255
|
+
end
|
256
|
+
|
263
257
|
def visit_arguments(list, collector)
|
264
|
-
return if list.
|
258
|
+
return if list.blank?
|
265
259
|
|
266
260
|
indented = @with_descriptions && list.values.any?(&:description?)
|
267
261
|
|
@@ -292,15 +286,18 @@ module Rails # :nodoc:
|
|
292
286
|
end
|
293
287
|
|
294
288
|
def visit_description(o, collector)
|
295
|
-
return unless @with_descriptions
|
289
|
+
return unless @with_descriptions
|
290
|
+
|
291
|
+
args = o.method(:description).arity == 0 ? [] : [@namespace]
|
292
|
+
return if (desc = o.description(*args)).nil?
|
296
293
|
|
297
|
-
if
|
298
|
-
collector <<
|
294
|
+
if desc.lines.size === 1
|
295
|
+
collector << desc.inspect
|
299
296
|
else
|
300
297
|
collector << '"""'
|
301
298
|
collector.eol
|
302
299
|
|
303
|
-
collector <<
|
300
|
+
collector << desc
|
304
301
|
collector.eol
|
305
302
|
|
306
303
|
collector << '"""'
|
@@ -344,7 +341,7 @@ module Rails # :nodoc:
|
|
344
341
|
collector << ' = ' << o.to_json(o.default) if o.try(:default_value?)
|
345
342
|
end
|
346
343
|
|
347
|
-
def visit(object, collector = nil)
|
344
|
+
def visit(object, collector = nil)
|
348
345
|
object_class = object.is_a?(Module) ? object : object.class
|
349
346
|
dispatch_method = dispatch[object_class]
|
350
347
|
if collector
|
@@ -358,7 +355,7 @@ module Rails # :nodoc:
|
|
358
355
|
respond_to?(dispatch[klass], true)
|
359
356
|
end
|
360
357
|
|
361
|
-
raise(::TypeError, "Cannot visit #{object_class}") unless superklass
|
358
|
+
raise(::TypeError, +"Cannot visit #{object_class}") unless superklass
|
362
359
|
dispatch[object_class] = dispatch[superklass]
|
363
360
|
retry
|
364
361
|
end
|