graphql 1.10.10 → 1.11.1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/object_generator.rb +50 -8
  3. data/lib/graphql.rb +3 -3
  4. data/lib/graphql/execution/interpreter.rb +1 -1
  5. data/lib/graphql/execution/interpreter/arguments.rb +1 -5
  6. data/lib/graphql/execution/interpreter/runtime.rb +20 -24
  7. data/lib/graphql/execution/multiplex.rb +1 -2
  8. data/lib/graphql/introspection/schema_type.rb +3 -3
  9. data/lib/graphql/invalid_null_error.rb +18 -0
  10. data/lib/graphql/pagination/connection.rb +13 -6
  11. data/lib/graphql/pagination/connections.rb +5 -4
  12. data/lib/graphql/query.rb +1 -2
  13. data/lib/graphql/schema.rb +26 -3
  14. data/lib/graphql/schema/argument.rb +6 -0
  15. data/lib/graphql/schema/build_from_definition.rb +7 -12
  16. data/lib/graphql/schema/enum.rb +9 -1
  17. data/lib/graphql/schema/field.rb +68 -80
  18. data/lib/graphql/schema/field/connection_extension.rb +2 -1
  19. data/lib/graphql/schema/input_object.rb +1 -3
  20. data/lib/graphql/schema/interface.rb +5 -0
  21. data/lib/graphql/schema/member.rb +1 -0
  22. data/lib/graphql/schema/member/has_arguments.rb +6 -0
  23. data/lib/graphql/schema/member/has_fields.rb +1 -1
  24. data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
  25. data/lib/graphql/schema/object.rb +7 -0
  26. data/lib/graphql/schema/resolver.rb +14 -0
  27. data/lib/graphql/schema/subscription.rb +1 -1
  28. data/lib/graphql/schema/union.rb +6 -0
  29. data/lib/graphql/schema/warden.rb +0 -1
  30. data/lib/graphql/subscriptions.rb +41 -8
  31. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +49 -5
  32. data/lib/graphql/subscriptions/broadcast_analyzer.rb +84 -0
  33. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
  34. data/lib/graphql/subscriptions/event.rb +16 -1
  35. data/lib/graphql/subscriptions/subscription_root.rb +13 -3
  36. data/lib/graphql/tracing.rb +1 -27
  37. data/lib/graphql/tracing/new_relic_tracing.rb +1 -12
  38. data/lib/graphql/tracing/platform_tracing.rb +39 -15
  39. data/lib/graphql/tracing/scout_tracing.rb +11 -0
  40. data/lib/graphql/tracing/statsd_tracing.rb +42 -0
  41. data/lib/graphql/types/iso_8601_date.rb +1 -1
  42. data/lib/graphql/types/iso_8601_date_time.rb +17 -13
  43. data/lib/graphql/version.rb +1 -1
  44. metadata +6 -2
@@ -43,9 +43,7 @@ module GraphQL
43
43
  when GraphQL::Language::Nodes::EnumTypeDefinition
44
44
  types[definition.name] = build_enum_type(definition, type_resolver)
45
45
  when GraphQL::Language::Nodes::ObjectTypeDefinition
46
- is_subscription_root = (definition.name == "Subscription" && (schema_definition.nil? || schema_definition.subscription.nil?)) || (schema_definition && (definition.name == schema_definition.subscription))
47
- should_extend_subscription_root = is_subscription_root && interpreter
48
- types[definition.name] = build_object_type(definition, type_resolver, default_resolve: default_resolve, extend_subscription_root: should_extend_subscription_root)
46
+ types[definition.name] = build_object_type(definition, type_resolver, default_resolve: default_resolve)
49
47
  when GraphQL::Language::Nodes::InterfaceTypeDefinition
50
48
  types[definition.name] = build_interface_type(definition, type_resolver)
51
49
  when GraphQL::Language::Nodes::UnionTypeDefinition
@@ -204,7 +202,7 @@ module GraphQL
204
202
  end
205
203
  end
206
204
 
207
- def build_object_type(object_type_definition, type_resolver, default_resolve:, extend_subscription_root:)
205
+ def build_object_type(object_type_definition, type_resolver, default_resolve:)
208
206
  builder = self
209
207
  type_def = nil
210
208
  typed_resolve_fn = ->(field, obj, args, ctx) { default_resolve.call(type_def, field, obj, args, ctx) }
@@ -214,10 +212,6 @@ module GraphQL
214
212
  graphql_name(object_type_definition.name)
215
213
  description(object_type_definition.description)
216
214
  ast_node(object_type_definition)
217
- if extend_subscription_root
218
- # This has to come before `field ...` configurations since it modifies them
219
- extend Subscriptions::SubscriptionRoot
220
- end
221
215
 
222
216
  object_type_definition.interfaces.each do |interface_name|
223
217
  interface_defn = type_resolver.call(interface_name)
@@ -303,7 +297,7 @@ module GraphQL
303
297
 
304
298
  field_definitions.map do |field_definition|
305
299
  type_name = resolve_type_name(field_definition.type)
306
-
300
+ resolve_method_name = "resolve_field_#{field_definition.name}"
307
301
  owner.field(
308
302
  field_definition.name,
309
303
  description: field_definition.description,
@@ -315,14 +309,15 @@ module GraphQL
315
309
  ast_node: field_definition,
316
310
  method_conflict_warning: false,
317
311
  camelize: false,
312
+ resolver_method: resolve_method_name,
318
313
  ) do
319
314
  builder.build_arguments(self, field_definition.arguments, type_resolver)
320
315
 
321
316
  # Don't do this for interfaces
322
317
  if default_resolve
323
- # TODO fragile hack. formalize this API?
324
- define_singleton_method :resolve_field_method do |obj, args, ctx|
325
- default_resolve.call(self, obj.object, args, ctx)
318
+ owner.send(:define_method, resolve_method_name) do |**args|
319
+ field_instance = self.class.get_field(field_definition.name)
320
+ default_resolve.call(field_instance, object, args, context)
326
321
  end
327
322
  end
328
323
  end
@@ -23,6 +23,9 @@ module GraphQL
23
23
  extend GraphQL::Schema::Member::AcceptsDefinition
24
24
  extend GraphQL::Schema::Member::ValidatesInput
25
25
 
26
+ class UnresolvedValueError < GraphQL::EnumType::UnresolvedValueError
27
+ end
28
+
26
29
  class << self
27
30
  # Define a value for this enum
28
31
  # @param graphql_name [String, Symbol] the GraphQL value for this, usually `SCREAMING_CASE`
@@ -94,7 +97,7 @@ module GraphQL
94
97
  if enum_value
95
98
  enum_value.graphql_name
96
99
  else
97
- raise(GraphQL::EnumType::UnresolvedValueError, "Can't resolve enum #{graphql_name} for #{value.inspect}")
100
+ raise(self::UnresolvedValueError, "Can't resolve enum #{graphql_name} for #{value.inspect}")
98
101
  end
99
102
  end
100
103
 
@@ -112,6 +115,11 @@ module GraphQL
112
115
  end
113
116
  end
114
117
 
118
+ def inherited(child_class)
119
+ child_class.const_set(:UnresolvedValueError, Class.new(Schema::Enum::UnresolvedValueError))
120
+ super
121
+ end
122
+
115
123
  private
116
124
 
117
125
  def own_values
@@ -36,9 +36,18 @@ module GraphQL
36
36
  # @return [Symbol] The method on the type to look up
37
37
  attr_reader :resolver_method
38
38
 
39
- # @return [Class] The type that this field belongs to
39
+ # @return [Class] The thing this field was defined on (type, mutation, resolver)
40
40
  attr_accessor :owner
41
41
 
42
+ # @return [Class] The GraphQL type this field belongs to. (For fields defined on mutations, it's the payload type)
43
+ def owner_type
44
+ @owner_type ||= if owner < GraphQL::Schema::Mutation
45
+ owner.payload_type
46
+ else
47
+ owner
48
+ end
49
+ end
50
+
42
51
  # @return [Symbol] the original name of the field, passed in by the user
43
52
  attr_reader :original_name
44
53
 
@@ -191,9 +200,10 @@ module GraphQL
191
200
  # @param subscription_scope [Symbol, String] A key in `context` which will be used to scope subscription payloads
192
201
  # @param extensions [Array<Class, Hash<Class => Object>>] Named extensions to apply to this field (see also {#extension})
193
202
  # @param trace [Boolean] If true, a {GraphQL::Tracing} tracer will measure this scalar field
203
+ # @param broadcastable [Boolean] Whether or not this field can be distributed in subscription broadcasts
194
204
  # @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
195
205
  # @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
196
- def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: [], extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, arguments: EMPTY_HASH, &definition_block)
206
+ def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: [], extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: nil, arguments: EMPTY_HASH, &definition_block)
197
207
  if name.nil?
198
208
  raise ArgumentError, "missing first `name` argument or keyword `name:`"
199
209
  end
@@ -237,8 +247,8 @@ module GraphQL
237
247
  end
238
248
 
239
249
  # TODO: I think non-string/symbol hash keys are wrongly normalized (eg `1` will not work)
240
- method_name = method || hash_key || @underscored_name
241
- resolver_method ||= @underscored_name.to_sym
250
+ method_name = method || hash_key || name_s
251
+ resolver_method ||= name_s.to_sym
242
252
 
243
253
  @method_str = method_name.to_s
244
254
  @method_sym = method_name.to_sym
@@ -251,6 +261,7 @@ module GraphQL
251
261
  @max_page_size = max_page_size == :not_given ? nil : max_page_size
252
262
  @introspection = introspection
253
263
  @extras = extras
264
+ @broadcastable = broadcastable
254
265
  @resolver_class = resolver_class
255
266
  @scope = scope
256
267
  @trace = trace
@@ -295,6 +306,13 @@ module GraphQL
295
306
  end
296
307
  end
297
308
 
309
+ # If true, subscription updates with this field can be shared between viewers
310
+ # @return [Boolean, nil]
311
+ # @see GraphQL::Subscriptions::BroadcastAnalyzer
312
+ def broadcastable?
313
+ @broadcastable
314
+ end
315
+
298
316
  # @param text [String]
299
317
  # @return [String]
300
318
  def description(text = nil)
@@ -534,7 +552,7 @@ module GraphQL
534
552
  @resolve_proc.call(extended_obj, args, ctx)
535
553
  end
536
554
  else
537
- public_send_field(after_obj, ruby_args, ctx)
555
+ public_send_field(after_obj, ruby_args, query_ctx)
538
556
  end
539
557
  else
540
558
  err = GraphQL::UnauthorizedFieldError.new(object: inner_obj, type: obj.class, context: ctx, field: self)
@@ -556,34 +574,13 @@ module GraphQL
556
574
  begin
557
575
  # Unwrap the GraphQL object to get the application object.
558
576
  application_object = object.object
559
- if self.authorized?(application_object, args, ctx)
560
- # Apply field extensions
561
- with_extensions(object, args, ctx) do |extended_obj, extended_args|
562
- field_receiver = if @resolver_class
563
- resolver_obj = if extended_obj.is_a?(GraphQL::Schema::Object)
564
- extended_obj.object
565
- else
566
- extended_obj
567
- end
568
- @resolver_class.new(object: resolver_obj, context: ctx, field: self)
569
- else
570
- extended_obj
571
- end
572
-
573
- if field_receiver.respond_to?(@resolver_method)
574
- # Call the method with kwargs, if there are any
575
- if extended_args.any?
576
- field_receiver.public_send(@resolver_method, **extended_args)
577
- else
578
- field_receiver.public_send(@resolver_method)
579
- end
580
- else
581
- resolve_field_method(field_receiver, extended_args, ctx)
582
- end
577
+ ctx.schema.after_lazy(self.authorized?(application_object, args, ctx)) do |is_authorized|
578
+ if is_authorized
579
+ public_send_field(object, args, ctx)
580
+ else
581
+ err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
582
+ ctx.schema.unauthorized_field(err)
583
583
  end
584
- else
585
- err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
586
- ctx.schema.unauthorized_field(err)
587
584
  end
588
585
  rescue GraphQL::UnauthorizedFieldError => err
589
586
  err.field ||= self
@@ -595,43 +592,6 @@ module GraphQL
595
592
  err
596
593
  end
597
594
 
598
- # Find a way to resolve this field, checking:
599
- #
600
- # - Hash keys, if the wrapped object is a hash;
601
- # - A method on the wrapped object;
602
- # - Or, raise not implemented.
603
- #
604
- # This can be overridden by defining a method on the object type.
605
- # @param obj [GraphQL::Schema::Object]
606
- # @param ruby_kwargs [Hash<Symbol => Object>]
607
- # @param ctx [GraphQL::Query::Context]
608
- def resolve_field_method(obj, ruby_kwargs, ctx)
609
- if obj.object.is_a?(Hash)
610
- inner_object = obj.object
611
- if inner_object.key?(@method_sym)
612
- inner_object[@method_sym]
613
- else
614
- inner_object[@method_str]
615
- end
616
- elsif obj.object.respond_to?(@method_sym)
617
- if ruby_kwargs.any?
618
- obj.object.public_send(@method_sym, **ruby_kwargs)
619
- else
620
- obj.object.public_send(@method_sym)
621
- end
622
- else
623
- raise <<-ERR
624
- Failed to implement #{@owner.graphql_name}.#{@name}, tried:
625
-
626
- - `#{obj.class}##{@resolver_method}`, which did not exist
627
- - `#{obj.object.class}##{@method_sym}`, which did not exist
628
- - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
629
-
630
- To implement this field, define one of the methods above (and check for typos)
631
- ERR
632
- end
633
- end
634
-
635
595
  # @param ctx [GraphQL::Query::Context::FieldResolutionContext]
636
596
  def fetch_extra(extra_name, ctx)
637
597
  if extra_name != :path && extra_name != :ast_node && respond_to?(extra_name)
@@ -702,24 +662,52 @@ module GraphQL
702
662
  end
703
663
  end
704
664
 
705
- def public_send_field(obj, ruby_kwargs, field_ctx)
706
- query_ctx = field_ctx.query.context
707
- with_extensions(obj, ruby_kwargs, query_ctx) do |extended_obj, extended_args|
665
+ def public_send_field(unextended_obj, unextended_ruby_kwargs, query_ctx)
666
+ with_extensions(unextended_obj, unextended_ruby_kwargs, query_ctx) do |obj, ruby_kwargs|
708
667
  if @resolver_class
709
- if extended_obj.is_a?(GraphQL::Schema::Object)
710
- extended_obj = extended_obj.object
668
+ if obj.is_a?(GraphQL::Schema::Object)
669
+ obj = obj.object
711
670
  end
712
- extended_obj = @resolver_class.new(object: extended_obj, context: query_ctx, field: self)
671
+ obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
713
672
  end
714
673
 
715
- if extended_obj.respond_to?(@resolver_method)
716
- if extended_args.any?
717
- extended_obj.public_send(@resolver_method, **extended_args)
674
+ # Find a way to resolve this field, checking:
675
+ #
676
+ # - A method on the type instance;
677
+ # - Hash keys, if the wrapped object is a hash;
678
+ # - A method on the wrapped object;
679
+ # - Or, raise not implemented.
680
+ #
681
+ if obj.respond_to?(@resolver_method)
682
+ # Call the method with kwargs, if there are any
683
+ if ruby_kwargs.any?
684
+ obj.public_send(@resolver_method, **ruby_kwargs)
685
+ else
686
+ obj.public_send(@resolver_method)
687
+ end
688
+ elsif obj.object.is_a?(Hash)
689
+ inner_object = obj.object
690
+ if inner_object.key?(@method_sym)
691
+ inner_object[@method_sym]
718
692
  else
719
- extended_obj.public_send(@resolver_method)
693
+ inner_object[@method_str]
694
+ end
695
+ elsif obj.object.respond_to?(@method_sym)
696
+ if ruby_kwargs.any?
697
+ obj.object.public_send(@method_sym, **ruby_kwargs)
698
+ else
699
+ obj.object.public_send(@method_sym)
720
700
  end
721
701
  else
722
- resolve_field_method(extended_obj, extended_args, query_ctx)
702
+ raise <<-ERR
703
+ Failed to implement #{@owner.graphql_name}.#{@name}, tried:
704
+
705
+ - `#{obj.class}##{@resolver_method}`, which did not exist
706
+ - `#{obj.object.class}##{@method_sym}`, which did not exist
707
+ - Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
708
+
709
+ To implement this field, define one of the methods above (and check for typos)
710
+ ERR
723
711
  end
724
712
  end
725
713
  end
@@ -31,6 +31,7 @@ module GraphQL
31
31
  elsif value.is_a?(GraphQL::Pagination::Connection)
32
32
  # update the connection with some things that may not have been provided
33
33
  value.context ||= context
34
+ value.parent ||= object.object
34
35
  value.first_value ||= arguments[:first]
35
36
  value.after_value ||= arguments[:after]
36
37
  value.last_value ||= arguments[:last]
@@ -41,7 +42,7 @@ module GraphQL
41
42
  value
42
43
  elsif context.schema.new_connections?
43
44
  wrappers = context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
44
- context.schema.connections.wrap(field, value, arguments, context, wrappers: wrappers)
45
+ context.schema.connections.wrap(field, object.object, value, arguments, context, wrappers: wrappers)
45
46
  else
46
47
  if object.is_a?(GraphQL::Schema::Object)
47
48
  object = object.object
@@ -21,10 +21,8 @@ module GraphQL
21
21
  @ruby_style_hash = @arguments.to_kwargs
22
22
  end
23
23
  # Apply prepares, not great to have it duplicated here.
24
- @arguments_by_keyword = {}
25
24
  maybe_lazies = []
26
- self.class.arguments.each do |name, arg_defn|
27
- @arguments_by_keyword[arg_defn.keyword] = arg_defn
25
+ self.class.arguments.each_value do |arg_defn|
28
26
  ruby_kwargs_key = arg_defn.keyword
29
27
 
30
28
  if @ruby_style_hash.key?(ruby_kwargs_key)
@@ -14,6 +14,7 @@ module GraphQL
14
14
  include GraphQL::Schema::Member::RelayShortcuts
15
15
  include GraphQL::Schema::Member::Scoped
16
16
  include GraphQL::Schema::Member::HasAstNode
17
+ include GraphQL::Schema::Member::HasUnresolvedTypeError
17
18
 
18
19
  # Methods defined in this block will be:
19
20
  # - Added as class methods to this interface
@@ -74,6 +75,10 @@ module GraphQL
74
75
  if overridden_graphql_name
75
76
  child_class.graphql_name(overridden_graphql_name)
76
77
  end
78
+ # If interfaces are mixed into each other, only define this class once
79
+ if !child_class.const_defined?(:UnresolvedTypeError, false)
80
+ add_unresolved_type_error(child_class)
81
+ end
77
82
  elsif child_class < GraphQL::Schema::Object
78
83
  # This is being included into an object type, make sure it's using `implements(...)`
79
84
  backtrace_line = caller(0, 10).find { |line| line.include?("schema/object.rb") && line.include?("in `implements'")}
@@ -5,6 +5,7 @@ require 'graphql/schema/member/cached_graphql_definition'
5
5
  require 'graphql/schema/member/graphql_type_names'
6
6
  require 'graphql/schema/member/has_ast_node'
7
7
  require 'graphql/schema/member/has_path'
8
+ require 'graphql/schema/member/has_unresolved_type_error'
8
9
  require 'graphql/schema/member/relay_shortcuts'
9
10
  require 'graphql/schema/member/scoped'
10
11
  require 'graphql/schema/member/type_system_helpers'
@@ -135,6 +135,12 @@ module GraphQL
135
135
  end
136
136
  end
137
137
 
138
+ def arguments_statically_coercible?
139
+ return @arguments_statically_coercible if defined?(@arguments_statically_coercible)
140
+
141
+ @arguments_statically_coercible = arguments.each_value.all?(&:statically_coercible?)
142
+ end
143
+
138
144
  module ArgumentClassAccessor
139
145
  def argument_class(new_arg_class = nil)
140
146
  if new_arg_class
@@ -47,7 +47,7 @@ module GraphQL
47
47
  # A list of GraphQL-Ruby keywords.
48
48
  #
49
49
  # @api private
50
- GRAPHQL_RUBY_KEYWORDS = [:context, :object, :method]
50
+ GRAPHQL_RUBY_KEYWORDS = [:context, :object, :method, :raw_value]
51
51
 
52
52
  # A list of field names that we should advise users to pick a different
53
53
  # resolve method name.
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GraphQL
4
+ class Schema
5
+ class Member
6
+ # Set up a type-specific error to make debugging & bug tracker integration better
7
+ module HasUnresolvedTypeError
8
+ private
9
+ def add_unresolved_type_error(child_class)
10
+ child_class.const_set(:UnresolvedTypeError, Class.new(GraphQL::UnresolvedTypeError))
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -71,6 +71,13 @@ module GraphQL
71
71
  end
72
72
 
73
73
  class << self
74
+ # Set up a type-specific invalid null error to use when this object's non-null fields wrongly return `nil`.
75
+ # It should help with debugging and bug tracker integrations.
76
+ def inherited(child_class)
77
+ child_class.const_set(:InvalidNullError, GraphQL::InvalidNullError.subclass_for(child_class))
78
+ super
79
+ end
80
+
74
81
  def implements(*new_interfaces, **options)
75
82
  new_memberships = []
76
83
  new_interfaces.each do |int|
@@ -250,6 +250,19 @@ module GraphQL
250
250
  @complexity || (superclass.respond_to?(:complexity) ? superclass.complexity : 1)
251
251
  end
252
252
 
253
+ def broadcastable(new_broadcastable)
254
+ @broadcastable = new_broadcastable
255
+ end
256
+
257
+ # @return [Boolean, nil]
258
+ def broadcastable?
259
+ if defined?(@broadcastable)
260
+ @broadcastable
261
+ else
262
+ (superclass.respond_to?(:broadcastable?) ? superclass.broadcastable? : nil)
263
+ end
264
+ end
265
+
253
266
  def field_options
254
267
  {
255
268
  type: type_expr,
@@ -261,6 +274,7 @@ module GraphQL
261
274
  null: null,
262
275
  complexity: complexity,
263
276
  extensions: extensions,
277
+ broadcastable: broadcastable?,
264
278
  }
265
279
  end
266
280
 
@@ -93,11 +93,11 @@ module GraphQL
93
93
  raise UnsubscribedError
94
94
  end
95
95
 
96
+ READING_SCOPE = ::Object.new
96
97
  # Call this method to provide a new subscription_scope; OR
97
98
  # call it without an argument to get the subscription_scope
98
99
  # @param new_scope [Symbol]
99
100
  # @return [Symbol]
100
- READING_SCOPE = ::Object.new
101
101
  def self.subscription_scope(new_scope = READING_SCOPE)
102
102
  if new_scope != READING_SCOPE
103
103
  @subscription_scope = new_scope