pub_sub_model_sync 1.0.beta2 → 1.0

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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +43 -0
  3. data/CHANGELOG.md +7 -4
  4. data/Gemfile.lock +3 -1
  5. data/README.md +174 -105
  6. data/docs/notifications-diagram.png +0 -0
  7. data/lib/pub_sub_model_sync.rb +1 -1
  8. data/lib/pub_sub_model_sync/base.rb +0 -20
  9. data/lib/pub_sub_model_sync/config.rb +1 -1
  10. data/lib/pub_sub_model_sync/initializers/before_commit.rb +3 -3
  11. data/lib/pub_sub_model_sync/message_processor.rb +32 -9
  12. data/lib/pub_sub_model_sync/message_publisher.rb +14 -10
  13. data/lib/pub_sub_model_sync/payload.rb +15 -12
  14. data/lib/pub_sub_model_sync/{publisher.rb → payload_builder.rb} +15 -10
  15. data/lib/pub_sub_model_sync/publisher_concern.rb +27 -16
  16. data/lib/pub_sub_model_sync/run_subscriber.rb +17 -13
  17. data/lib/pub_sub_model_sync/service_base.rb +5 -32
  18. data/lib/pub_sub_model_sync/service_google.rb +1 -1
  19. data/lib/pub_sub_model_sync/subscriber_concern.rb +6 -4
  20. data/lib/pub_sub_model_sync/transaction.rb +6 -2
  21. data/lib/pub_sub_model_sync/version.rb +1 -1
  22. data/samples/README.md +50 -0
  23. data/samples/app1/.gitattributes +8 -0
  24. data/samples/app1/.gitignore +28 -0
  25. data/samples/app1/Dockerfile +13 -0
  26. data/samples/app1/Gemfile +37 -0
  27. data/samples/app1/Gemfile.lock +171 -0
  28. data/samples/app1/README.md +24 -0
  29. data/samples/app1/Rakefile +6 -0
  30. data/samples/app1/app/models/application_record.rb +3 -0
  31. data/samples/app1/app/models/concerns/.keep +0 -0
  32. data/samples/app1/app/models/post.rb +19 -0
  33. data/samples/app1/app/models/user.rb +29 -0
  34. data/samples/app1/bin/bundle +114 -0
  35. data/samples/app1/bin/rails +5 -0
  36. data/samples/app1/bin/rake +5 -0
  37. data/samples/app1/bin/setup +33 -0
  38. data/samples/app1/bin/spring +14 -0
  39. data/samples/app1/config.ru +6 -0
  40. data/samples/app1/config/application.rb +40 -0
  41. data/samples/app1/config/boot.rb +4 -0
  42. data/samples/app1/config/credentials.yml.enc +1 -0
  43. data/samples/app1/config/database.yml +25 -0
  44. data/samples/app1/config/environment.rb +5 -0
  45. data/samples/app1/config/environments/development.rb +63 -0
  46. data/samples/app1/config/environments/production.rb +105 -0
  47. data/samples/app1/config/environments/test.rb +57 -0
  48. data/samples/app1/config/initializers/application_controller_renderer.rb +8 -0
  49. data/samples/app1/config/initializers/backtrace_silencers.rb +8 -0
  50. data/samples/app1/config/initializers/cors.rb +16 -0
  51. data/samples/app1/config/initializers/filter_parameter_logging.rb +6 -0
  52. data/samples/app1/config/initializers/inflections.rb +16 -0
  53. data/samples/app1/config/initializers/mime_types.rb +4 -0
  54. data/samples/app1/config/initializers/pubsub.rb +4 -0
  55. data/samples/app1/config/initializers/wrap_parameters.rb +14 -0
  56. data/samples/app1/config/locales/en.yml +33 -0
  57. data/samples/app1/config/puma.rb +43 -0
  58. data/samples/app1/config/routes.rb +3 -0
  59. data/samples/app1/config/spring.rb +6 -0
  60. data/samples/app1/db/migrate/20210513080700_create_users.rb +12 -0
  61. data/samples/app1/db/migrate/20210513134332_create_posts.rb +11 -0
  62. data/samples/app1/db/schema.rb +34 -0
  63. data/samples/app1/db/seeds.rb +7 -0
  64. data/samples/app1/docker-compose.yml +32 -0
  65. data/samples/app1/log/.keep +0 -0
  66. data/samples/app2/.gitattributes +8 -0
  67. data/samples/app2/.gitignore +28 -0
  68. data/samples/app2/Dockerfile +13 -0
  69. data/samples/app2/Gemfile +37 -0
  70. data/samples/app2/Gemfile.lock +171 -0
  71. data/samples/app2/README.md +24 -0
  72. data/samples/app2/Rakefile +6 -0
  73. data/samples/app2/app/models/application_record.rb +9 -0
  74. data/samples/app2/app/models/concerns/.keep +0 -0
  75. data/samples/app2/app/models/customer.rb +28 -0
  76. data/samples/app2/app/models/post.rb +10 -0
  77. data/samples/app2/bin/bundle +114 -0
  78. data/samples/app2/bin/rails +5 -0
  79. data/samples/app2/bin/rake +5 -0
  80. data/samples/app2/bin/setup +33 -0
  81. data/samples/app2/bin/spring +14 -0
  82. data/samples/app2/config.ru +6 -0
  83. data/samples/app2/config/application.rb +40 -0
  84. data/samples/app2/config/boot.rb +4 -0
  85. data/samples/app2/config/credentials.yml.enc +1 -0
  86. data/samples/app2/config/database.yml +25 -0
  87. data/samples/app2/config/environment.rb +5 -0
  88. data/samples/app2/config/environments/development.rb +63 -0
  89. data/samples/app2/config/environments/production.rb +105 -0
  90. data/samples/app2/config/environments/test.rb +57 -0
  91. data/samples/app2/config/initializers/application_controller_renderer.rb +8 -0
  92. data/samples/app2/config/initializers/backtrace_silencers.rb +8 -0
  93. data/samples/app2/config/initializers/cors.rb +16 -0
  94. data/samples/app2/config/initializers/filter_parameter_logging.rb +6 -0
  95. data/samples/app2/config/initializers/inflections.rb +16 -0
  96. data/samples/app2/config/initializers/mime_types.rb +4 -0
  97. data/samples/app2/config/initializers/pubsub.rb +4 -0
  98. data/samples/app2/config/initializers/wrap_parameters.rb +14 -0
  99. data/samples/app2/config/locales/en.yml +33 -0
  100. data/samples/app2/config/puma.rb +43 -0
  101. data/samples/app2/config/routes.rb +3 -0
  102. data/samples/app2/config/spring.rb +6 -0
  103. data/samples/app2/db/migrate/20210513080956_create_customers.rb +10 -0
  104. data/samples/app2/db/migrate/20210513135203_create_posts.rb +10 -0
  105. data/samples/app2/db/schema.rb +31 -0
  106. data/samples/app2/db/seeds.rb +7 -0
  107. data/samples/app2/docker-compose.yml +20 -0
  108. data/samples/app2/log/.keep +0 -0
  109. metadata +93 -5
@@ -6,16 +6,16 @@ module PubSubModelSync
6
6
  SERVICE_KEY = 'service_model_sync'
7
7
 
8
8
  def listen_messages
9
- raise 'method :listen_messages must be defined in service'
9
+ raise NoMethodError, 'method :listen_messages must be defined in service'
10
10
  end
11
11
 
12
12
  # @param _payload (Payload)
13
13
  def publish(_payload)
14
- raise 'method :publish must be defined in service'
14
+ raise NoMethodError, 'method :publish must be defined in service'
15
15
  end
16
16
 
17
17
  def stop
18
- raise 'method :stop must be defined in service'
18
+ raise NoMethodError, 'method :stop must be defined in service'
19
19
  end
20
20
 
21
21
  private
@@ -32,26 +32,13 @@ module PubSubModelSync
32
32
 
33
33
  # @param (String: Payload in json format)
34
34
  def process_message(payload_info)
35
- retries ||= 0
36
35
  payload = decode_payload(payload_info)
37
36
  return payload.process unless same_app_message?(payload)
38
37
 
39
38
  log("Skipping message from same origin: #{[payload]}") if config.debug
40
39
  rescue => e
41
- retry if can_retry_process_message?(e, payload, retries += 1)
42
- end
43
-
44
- def can_retry_process_message?(error, payload, retries)
45
- error_payload = [payload, error.message, error.backtrace]
46
- if retries <= 5
47
- sleep(retries)
48
- log("Error while starting to process a message (retrying #{retries} retries...): #{error_payload}", :error)
49
- rescue_database_connection if lost_db_connection_err?(error)
50
- true
51
- else
52
- log("Retried 5 times and error persists, exiting...: #{error_payload}", :error)
53
- Process.exit!(true)
54
- end
40
+ error_payload = [payload, e.message, e.backtrace]
41
+ log("Error while starting to process a message: #{error_payload}", :error)
55
42
  end
56
43
 
57
44
  # @return Payload
@@ -66,19 +53,5 @@ module PubSubModelSync
66
53
  key = payload.headers[:app_key]
67
54
  key && key == config.subscription_key
68
55
  end
69
-
70
- def lost_db_connection_err?(error)
71
- return true if error.class.name == 'PG::UnableToSend' # rubocop:disable Style/ClassEqualityComparison
72
-
73
- error.message.match?(/lost connection/i)
74
- end
75
-
76
- def rescue_database_connection
77
- log('Lost DB connection. Attempting to reconnect...', :warn)
78
- ActiveRecord::Base.connection.reconnect!
79
- rescue
80
- log('Cannot reconnect to database, exiting...', :error)
81
- Process.exit!(true)
82
- end
83
56
  end
84
57
  end
@@ -37,7 +37,7 @@ module PubSubModelSync
37
37
  message_topics = p_topic_names.map(&method(:find_topic))
38
38
  message_topics.each do |topic|
39
39
  topic.publish_async(encode_payload(payload), message_headers(payload)) do |res|
40
- raise 'Failed to publish the message.' unless res.succeeded?
40
+ raise StandardError, 'Failed to publish the message.' unless res.succeeded?
41
41
  end
42
42
  end
43
43
  end
@@ -10,7 +10,7 @@ module PubSubModelSync
10
10
 
11
11
  module ClassMethods
12
12
  # @param actions (Symbol|Array<Symbol>) Notification.action name: save|create|update|destroy|<any_other_action>
13
- # @param mapping (Array<String>) Attributes mapping with aliasing support, sample: ["id", "full_name:name"]
13
+ # @param mapping (Array<String,Symbol>) Attributes mapping with aliasing support, sample: ["id", "full_name:name"]
14
14
  # @param settings (Hash<:from_klass, :to_action, :id, :if, :unless>)
15
15
  # from_klass (String) Notification.class name
16
16
  # to_action (Symbol|Proc):
@@ -19,15 +19,17 @@ module PubSubModelSync
19
19
  # id (Symbol|Array<Symbol|String>) attribute(s) DB primary identifier(s). Supports for mapping format.
20
20
  # if (Symbol|Proc|Array<Symbol>) Method or block called as the conformation before calling the callback
21
21
  # unless (Symbol|Proc|Array<Symbol>) Method or block called as the negation before calling the callback
22
- def ps_subscribe(actions, mapping = [], settings = {})
23
- Array(actions).each do |action|
22
+ def ps_subscribe(actions, mapping = [], settings = {}, &block)
23
+ settings[:to_action] ||= block if block
24
+ Array(actions).map do |action|
24
25
  add_ps_subscriber(action, mapping, settings)
25
26
  end
26
27
  end
27
28
 
28
29
  # @param action (Symbol) Notification.action name
29
30
  # @param settings (Hash) @refer ps_subscribe.settings except(:id)
30
- def ps_class_subscribe(action, settings = {})
31
+ def ps_class_subscribe(action, settings = {}, &block)
32
+ settings[:to_action] ||= block if block
31
33
  add_ps_subscriber(action, nil, settings.merge(mode: :klass))
32
34
  end
33
35
 
@@ -19,7 +19,11 @@ module PubSubModelSync
19
19
  # @param payload (Payload)
20
20
  def add_payload(payload)
21
21
  payloads << payload
22
- deliver_payloads if payloads.count >= max_buffer
22
+ log("Payload added to current transaction: #{payload.inspect}") if config.debug
23
+ return unless payloads.count >= max_buffer
24
+
25
+ log("Payloads buffer was filled, delivering current payloads: #{payloads.count}")
26
+ deliver_payloads
23
27
  end
24
28
 
25
29
  def finish # rubocop:disable Metrics/AbcSize
@@ -38,7 +42,7 @@ module PubSubModelSync
38
42
  end
39
43
 
40
44
  def rollback
41
- log("rollback #{payloads.count} notifications", :warn) if children.any? && debug?
45
+ log("Rollback #{payloads.count} notifications", :warn) if children.any? && debug?
42
46
  self.children = []
43
47
  root&.rollback
44
48
  clean_publisher
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PubSubModelSync
4
- VERSION = '1.0.beta2'
4
+ VERSION = '1.0'
5
5
  end
data/samples/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # Sample model sync
2
+ This is a sample to sync information between rails applications using RabbitMQ
3
+
4
+ ## Installation
5
+ * Create manually the required network to share rabbitMQ (just if not exist):
6
+ ```docker network create shared_app_services```
7
+
8
+ * Start RabbitMQ server
9
+ ```docker-compose up pubsub```
10
+
11
+ * In another tab access to App1 to publish notifications (Wait for step 2)
12
+ - Access to the application
13
+ `cd samples/app1`
14
+
15
+ - Build docker and enter rails console
16
+ ```docker-compose run app bash -c "rails db:migrate && rails c"```
17
+
18
+ - Create a sample user
19
+ ```ruby
20
+ user = User.create!(name: 'User 1', posts_attributes: [{ title: 'Post 1' }, { title: 'Post 2' }])
21
+ ```
22
+ Note: Check app2 console to see notifications (3 notifications)
23
+ Note2: Access app2 console to see user and its posts
24
+
25
+ - Update previous user
26
+ ```ruby
27
+ user.update!(name: 'User 1 changed', posts_attributes: user.posts.map { |post| { id: post.id, title: "#{post.title} changed" } })
28
+ ```
29
+ Note: Check app2 console to see notifications (3 notifications)
30
+ Note2: Access app2 console to see changes for user and its posts
31
+
32
+ - Destroy previous user
33
+ ```ruby
34
+ user.destroy!
35
+ ```
36
+
37
+ * In another tab access to App2 to listen notifications (Wait for step 2)
38
+ - Access to the folder
39
+ `cd samples/app2`
40
+
41
+ - Build docker and start listener (Received notifications will be printed here)
42
+ ```docker-compose run listener```
43
+
44
+ - Optional: Open another tab to access application to ensure synced data
45
+ ```docker-compose run listener bash -c "rails c```
46
+ ```ruby
47
+ user = User.last.inspect
48
+ user.posts.inspect
49
+ ```
50
+
@@ -0,0 +1,8 @@
1
+ # See https://git-scm.com/docs/gitattributes for more about git attribute files.
2
+
3
+ # Mark the database schema as having been generated.
4
+ db/schema.rb linguist-generated
5
+
6
+
7
+ # Mark any vendored files as having been vendored.
8
+ vendor/* linguist-vendored
@@ -0,0 +1,28 @@
1
+ # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile '~/.gitignore_global'
6
+
7
+ # Ignore bundler config.
8
+ /.bundle
9
+
10
+ # Ignore the default SQLite database.
11
+ /db/*.sqlite3
12
+ /db/*.sqlite3-*
13
+
14
+ # Ignore all logfiles and tempfiles.
15
+ /log/*
16
+ /tmp/*
17
+ !/log/.keep
18
+ !/tmp/.keep
19
+
20
+ # Ignore pidfiles, but keep the directory.
21
+ /tmp/pids/*
22
+ !/tmp/pids/
23
+ !/tmp/pids/.keep
24
+
25
+ .byebug_history
26
+
27
+ # Ignore master key for decrypting credentials and more.
28
+ /config/master.key
@@ -0,0 +1,13 @@
1
+ FROM ruby:2.7.1 AS builder
2
+
3
+ # Allow apt to work with https-based sources
4
+ RUN apt-get update -yqq && apt-get install -yqq --no-install-recommends apt-transport-https
5
+
6
+ RUN mkdir /app
7
+ WORKDIR /app
8
+
9
+ # backend
10
+ COPY Gemfile.lock Gemfile /app/
11
+ RUN gem install bundler && bundle install
12
+
13
+ COPY . /app
@@ -0,0 +1,37 @@
1
+ source 'https://rubygems.org'
2
+ git_source(:github) { |repo| "https://github.com/#{repo}.git" }
3
+
4
+ # Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main'
5
+ gem 'rails', '~> 6.1.3', '>= 6.1.3.2'
6
+ # Use sqlite3 as the database for Active Record
7
+ gem 'sqlite3', '~> 1.4'
8
+ # Use Puma as the app server
9
+ gem 'puma', '~> 5.0'
10
+ # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
11
+ # gem 'jbuilder', '~> 2.7'
12
+ # Use Active Model has_secure_password
13
+ # gem 'bcrypt', '~> 3.1.7'
14
+
15
+ # Reduces boot times through caching; required in config/boot.rb
16
+ gem 'bootsnap', '>= 1.4.4', require: false
17
+
18
+ # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible
19
+ # gem 'rack-cors'
20
+
21
+ gem 'pub_sub_model_sync'
22
+ gem 'bunny' # to use rabbit-mq pub/sub service
23
+ gem 'annotate'
24
+
25
+ group :development, :test do
26
+ # Call 'byebug' anywhere in the code to stop execution and get a debugger console
27
+ gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
28
+ end
29
+
30
+ group :development do
31
+ gem 'listen', '~> 3.3'
32
+ # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
33
+ gem 'spring'
34
+ end
35
+
36
+ # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
37
+ gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
@@ -0,0 +1,171 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ actioncable (6.1.3.2)
5
+ actionpack (= 6.1.3.2)
6
+ activesupport (= 6.1.3.2)
7
+ nio4r (~> 2.0)
8
+ websocket-driver (>= 0.6.1)
9
+ actionmailbox (6.1.3.2)
10
+ actionpack (= 6.1.3.2)
11
+ activejob (= 6.1.3.2)
12
+ activerecord (= 6.1.3.2)
13
+ activestorage (= 6.1.3.2)
14
+ activesupport (= 6.1.3.2)
15
+ mail (>= 2.7.1)
16
+ actionmailer (6.1.3.2)
17
+ actionpack (= 6.1.3.2)
18
+ actionview (= 6.1.3.2)
19
+ activejob (= 6.1.3.2)
20
+ activesupport (= 6.1.3.2)
21
+ mail (~> 2.5, >= 2.5.4)
22
+ rails-dom-testing (~> 2.0)
23
+ actionpack (6.1.3.2)
24
+ actionview (= 6.1.3.2)
25
+ activesupport (= 6.1.3.2)
26
+ rack (~> 2.0, >= 2.0.9)
27
+ rack-test (>= 0.6.3)
28
+ rails-dom-testing (~> 2.0)
29
+ rails-html-sanitizer (~> 1.0, >= 1.2.0)
30
+ actiontext (6.1.3.2)
31
+ actionpack (= 6.1.3.2)
32
+ activerecord (= 6.1.3.2)
33
+ activestorage (= 6.1.3.2)
34
+ activesupport (= 6.1.3.2)
35
+ nokogiri (>= 1.8.5)
36
+ actionview (6.1.3.2)
37
+ activesupport (= 6.1.3.2)
38
+ builder (~> 3.1)
39
+ erubi (~> 1.4)
40
+ rails-dom-testing (~> 2.0)
41
+ rails-html-sanitizer (~> 1.1, >= 1.2.0)
42
+ activejob (6.1.3.2)
43
+ activesupport (= 6.1.3.2)
44
+ globalid (>= 0.3.6)
45
+ activemodel (6.1.3.2)
46
+ activesupport (= 6.1.3.2)
47
+ activerecord (6.1.3.2)
48
+ activemodel (= 6.1.3.2)
49
+ activesupport (= 6.1.3.2)
50
+ activestorage (6.1.3.2)
51
+ actionpack (= 6.1.3.2)
52
+ activejob (= 6.1.3.2)
53
+ activerecord (= 6.1.3.2)
54
+ activesupport (= 6.1.3.2)
55
+ marcel (~> 1.0.0)
56
+ mini_mime (~> 1.0.2)
57
+ activesupport (6.1.3.2)
58
+ concurrent-ruby (~> 1.0, >= 1.0.2)
59
+ i18n (>= 1.6, < 2)
60
+ minitest (>= 5.1)
61
+ tzinfo (~> 2.0)
62
+ zeitwerk (~> 2.3)
63
+ amq-protocol (2.3.2)
64
+ annotate (3.1.1)
65
+ activerecord (>= 3.2, < 7.0)
66
+ rake (>= 10.4, < 14.0)
67
+ bootsnap (1.7.5)
68
+ msgpack (~> 1.0)
69
+ builder (3.2.4)
70
+ bunny (2.17.0)
71
+ amq-protocol (~> 2.3, >= 2.3.1)
72
+ byebug (11.1.3)
73
+ concurrent-ruby (1.1.8)
74
+ crass (1.0.6)
75
+ erubi (1.10.0)
76
+ ffi (1.15.0)
77
+ globalid (0.4.2)
78
+ activesupport (>= 4.2.0)
79
+ i18n (1.8.10)
80
+ concurrent-ruby (~> 1.0)
81
+ listen (3.5.1)
82
+ rb-fsevent (~> 0.10, >= 0.10.3)
83
+ rb-inotify (~> 0.9, >= 0.9.10)
84
+ loofah (2.9.1)
85
+ crass (~> 1.0.2)
86
+ nokogiri (>= 1.5.9)
87
+ mail (2.7.1)
88
+ mini_mime (>= 0.1.1)
89
+ marcel (1.0.1)
90
+ method_source (1.0.0)
91
+ mini_mime (1.0.3)
92
+ mini_portile2 (2.5.1)
93
+ minitest (5.14.4)
94
+ msgpack (1.4.2)
95
+ nio4r (2.5.7)
96
+ nokogiri (1.11.3)
97
+ mini_portile2 (~> 2.5.0)
98
+ racc (~> 1.4)
99
+ pub_sub_model_sync (1.0.beta1)
100
+ rails
101
+ puma (5.3.1)
102
+ nio4r (~> 2.0)
103
+ racc (1.5.2)
104
+ rack (2.2.3)
105
+ rack-test (1.1.0)
106
+ rack (>= 1.0, < 3)
107
+ rails (6.1.3.2)
108
+ actioncable (= 6.1.3.2)
109
+ actionmailbox (= 6.1.3.2)
110
+ actionmailer (= 6.1.3.2)
111
+ actionpack (= 6.1.3.2)
112
+ actiontext (= 6.1.3.2)
113
+ actionview (= 6.1.3.2)
114
+ activejob (= 6.1.3.2)
115
+ activemodel (= 6.1.3.2)
116
+ activerecord (= 6.1.3.2)
117
+ activestorage (= 6.1.3.2)
118
+ activesupport (= 6.1.3.2)
119
+ bundler (>= 1.15.0)
120
+ railties (= 6.1.3.2)
121
+ sprockets-rails (>= 2.0.0)
122
+ rails-dom-testing (2.0.3)
123
+ activesupport (>= 4.2.0)
124
+ nokogiri (>= 1.6)
125
+ rails-html-sanitizer (1.3.0)
126
+ loofah (~> 2.3)
127
+ railties (6.1.3.2)
128
+ actionpack (= 6.1.3.2)
129
+ activesupport (= 6.1.3.2)
130
+ method_source
131
+ rake (>= 0.8.7)
132
+ thor (~> 1.0)
133
+ rake (13.0.3)
134
+ rb-fsevent (0.11.0)
135
+ rb-inotify (0.10.1)
136
+ ffi (~> 1.0)
137
+ spring (2.1.1)
138
+ sprockets (4.0.2)
139
+ concurrent-ruby (~> 1.0)
140
+ rack (> 1, < 3)
141
+ sprockets-rails (3.2.2)
142
+ actionpack (>= 4.0)
143
+ activesupport (>= 4.0)
144
+ sprockets (>= 3.0.0)
145
+ sqlite3 (1.4.2)
146
+ thor (1.1.0)
147
+ tzinfo (2.0.4)
148
+ concurrent-ruby (~> 1.0)
149
+ websocket-driver (0.7.3)
150
+ websocket-extensions (>= 0.1.0)
151
+ websocket-extensions (0.1.5)
152
+ zeitwerk (2.4.2)
153
+
154
+ PLATFORMS
155
+ ruby
156
+
157
+ DEPENDENCIES
158
+ annotate
159
+ bootsnap (>= 1.4.4)
160
+ bunny
161
+ byebug
162
+ listen (~> 3.3)
163
+ pub_sub_model_sync (= 1.0.beta2)
164
+ puma (~> 5.0)
165
+ rails (~> 6.1.3, >= 6.1.3.2)
166
+ spring
167
+ sqlite3 (~> 1.4)
168
+ tzinfo-data
169
+
170
+ BUNDLED WITH
171
+ 2.1.4