Skip to content

Commit ebc7e13

Browse files
committed
add an ability to deliver messages asynchronously via adapters
1 parent bef401d commit ebc7e13

16 files changed

Lines changed: 571 additions & 36 deletions

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,34 @@ Or you can use `collect`
337337
end
338338
```
339339

340+
## Async delivering messages
341+
342+
If you use `Rails` application or you use `ActiveJob`, you can deliver messages using `ActiveJob`
343+
344+
### Configuration
345+
346+
You have the next configuration
347+
348+
```ruby
349+
GraphQL::AnyCable.configure do |config|
350+
# ... other configurations
351+
config.delivery_method = "inline" # the default value "inline", also can be "active_job"
352+
config.queue = "default" # the name of ActiveJob queue
353+
config.job_class = "GraphQL::Jobs::TriggerJob" # the name executor job
354+
end
355+
```
356+
357+
`delivery_method` can be either `inline` or `active_job`.
358+
`inline` means that delivering messaging will work sync.
359+
`active_job` - It will add delivering messages operations to `ActiveJob` with queue `default` and using job `GraphQL::Jobs::TriggerJob`
360+
361+
You can change the queue or job_class by changing it in the configuration
362+
363+
Or you can run code
364+
365+
```ruby
366+
GraphQL::AnyCable.delivery_method = "active_job", { queue: "broadcasting", job_class: "GraphQL::Jobs::TriggerJob" }
367+
```
340368

341369
## Testing applications which use `graphql-anycable`
342370

graphql-anycable.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,5 @@ Gem::Specification.new do |spec|
3737
spec.add_development_dependency "railties"
3838
spec.add_development_dependency "rake", ">= 12.3.3"
3939
spec.add_development_dependency "rspec", "~> 3.0"
40+
spec.add_development_dependency "activejob", "~> 6.0"
4041
end

lib/graphql-anycable.rb

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
require_relative "graphql/anycable/config"
88
require_relative "graphql/anycable/railtie" if defined?(Rails)
99
require_relative "graphql/anycable/stats"
10+
require_relative "graphql/anycable/delivery_adapter"
1011
require_relative "graphql/subscriptions/anycable_subscriptions"
1112

1213
module GraphQL
@@ -25,6 +26,19 @@ def self.stats(**options)
2526
Stats.new(**options).collect
2627
end
2728

29+
def self.delivery_method=(args)
30+
method_name, options = Array(args)
31+
options ||= {}
32+
33+
config.delivery_method = method_name
34+
config.queue = options[:queue] if options[:queue]
35+
config.job_class = options[:job_class] if options[:job_class]
36+
end
37+
38+
def self.delivery_adapter(object)
39+
DeliveryAdapter.lookup(executor_object: object)
40+
end
41+
2842
module_function
2943

3044
def redis
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# frozen_string_literal: true
2+
3+
module GraphQL
4+
module Adapters
5+
class ActiveJobAdapter < BaseAdapter
6+
def trigger(...)
7+
executor_class_job.set(queue: config.queue).perform_later(
8+
executor_object,
9+
executor_method,
10+
...
11+
)
12+
end
13+
14+
private
15+
16+
def executor_class_job
17+
config.job_class.constantize
18+
end
19+
20+
def config
21+
GraphQL::AnyCable.config
22+
end
23+
end
24+
end
25+
end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# frozen_string_literal: true
2+
3+
module GraphQL
4+
module Adapters
5+
class BaseAdapter
6+
attr_reader :executor_object, :executor_method
7+
8+
def initialize(executor_object:)
9+
@executor_object = executor_object
10+
@executor_method = executor_object.class::EXECUTOR_METHOD_NAME
11+
end
12+
13+
def trigger
14+
raise NoMethodError, "#{__method__} method should be implemented in concrete class"
15+
end
16+
end
17+
end
18+
end
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# frozen_string_literal: true
2+
3+
module GraphQL
4+
module Adapters
5+
class InlineAdapter < BaseAdapter
6+
def trigger(...)
7+
executor_object.public_send(executor_method, ...)
8+
end
9+
end
10+
end
11+
end

lib/graphql/anycable/config.rb

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,38 @@ class Config < Anyway::Config
1212
attr_config use_redis_object_on_cleanup: true
1313
attr_config use_client_provided_uniq_id: true
1414
attr_config redis_prefix: "graphql" # Here, we set clear redis_prefix without any hyphen. The hyphen is added at the end of this value on our side.
15+
16+
attr_config delivery_method: "inline", queue: "default", job_class: "GraphQL::Jobs::TriggerJob"
17+
18+
def job_class=(value)
19+
ensure_value_is_not_blank!("job_class", value)
20+
21+
super
22+
end
23+
24+
def queue=(value)
25+
ensure_value_is_not_blank!("queue", value)
26+
27+
super
28+
end
29+
30+
def delivery_method=(value)
31+
ensure_value_is_not_blank!("delivery_method", value)
32+
33+
super
34+
end
35+
36+
private
37+
38+
def empty_value?(value)
39+
value.nil? || value == ""
40+
end
41+
42+
def ensure_value_is_not_blank!(name, value)
43+
return unless empty_value?(value)
44+
45+
raise_validation_error("#{name} can not be blank")
46+
end
1547
end
1648
end
1749
end
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# frozen_string_literal: true
2+
3+
require "graphql/adapters/base_adapter"
4+
require "graphql/adapters/inline_adapter"
5+
require "graphql/adapters/active_job_adapter"
6+
7+
module GraphQL
8+
module AnyCable
9+
class DeliveryAdapter
10+
class << self
11+
def lookup(options)
12+
adapter_class_name = config.delivery_method.to_s.split("_").map(&:capitalize).join
13+
14+
Adapters.const_get("#{adapter_class_name}Adapter").new(**(options || {}))
15+
rescue NameError => e
16+
raise e.class, "Delivery adapter :#{config.delivery_method} haven't been found", e.backtrace
17+
end
18+
19+
def config
20+
GraphQL::AnyCable.config
21+
end
22+
end
23+
end
24+
end
25+
end

lib/graphql/anycable/railtie.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@
55
module GraphQL
66
module AnyCable
77
class Railtie < ::Rails::Railtie
8+
initializer "graphql_anycable.load_trigger_job" do
9+
ActiveSupport.on_load(:active_job) do
10+
require "graphql/jobs/trigger_job"
11+
require "graphql/serializers/anycable_subscription_serializer"
12+
13+
ActiveJob::Serializers.add_serializers(GraphQL::Serializers::AnyCableSubscriptionSerializer)
14+
end
15+
end
16+
817
rake_tasks do
918
path = File.expand_path(__dir__)
1019
Dir.glob("#{path}/tasks/**/*.rake").each { |f| load f }

lib/graphql/jobs/trigger_job.rb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# frozen_string_literal: true
2+
3+
module GraphQL
4+
module Jobs
5+
class TriggerJob < ActiveJob::Base
6+
def perform(executor_object, execute_method, event_name, args = {}, object = nil, options = {})
7+
executor_object.public_send(execute_method, event_name, args, object, **options)
8+
end
9+
end
10+
end
11+
end

0 commit comments

Comments
 (0)