pub_sub_model_sync 1.0.beta → 1.0.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +43 -0
  3. data/.rubocop.yml +1 -2
  4. data/CHANGELOG.md +11 -4
  5. data/Gemfile.lock +7 -2
  6. data/README.md +182 -108
  7. data/docs/notifications-diagram.png +0 -0
  8. data/lib/pub_sub_model_sync.rb +1 -1
  9. data/lib/pub_sub_model_sync/base.rb +0 -20
  10. data/lib/pub_sub_model_sync/config.rb +3 -2
  11. data/lib/pub_sub_model_sync/initializers/before_commit.rb +23 -0
  12. data/lib/pub_sub_model_sync/message_processor.rb +32 -9
  13. data/lib/pub_sub_model_sync/message_publisher.rb +19 -15
  14. data/lib/pub_sub_model_sync/payload.rb +15 -12
  15. data/lib/pub_sub_model_sync/{publisher.rb → payload_builder.rb} +16 -11
  16. data/lib/pub_sub_model_sync/publisher_concern.rb +42 -22
  17. data/lib/pub_sub_model_sync/railtie.rb +6 -0
  18. data/lib/pub_sub_model_sync/run_subscriber.rb +17 -13
  19. data/lib/pub_sub_model_sync/runner.rb +3 -5
  20. data/lib/pub_sub_model_sync/service_base.rb +5 -32
  21. data/lib/pub_sub_model_sync/service_google.rb +2 -2
  22. data/lib/pub_sub_model_sync/service_kafka.rb +2 -2
  23. data/lib/pub_sub_model_sync/service_rabbit.rb +1 -1
  24. data/lib/pub_sub_model_sync/subscriber_concern.rb +11 -9
  25. data/lib/pub_sub_model_sync/transaction.rb +37 -21
  26. data/lib/pub_sub_model_sync/version.rb +1 -1
  27. data/samples/README.md +50 -0
  28. data/samples/app1/Dockerfile +13 -0
  29. data/samples/app1/Gemfile +37 -0
  30. data/samples/app1/Gemfile.lock +171 -0
  31. data/samples/app1/README.md +24 -0
  32. data/samples/app1/Rakefile +6 -0
  33. data/samples/app1/app/models/application_record.rb +3 -0
  34. data/samples/app1/app/models/concerns/.keep +0 -0
  35. data/samples/app1/app/models/post.rb +19 -0
  36. data/samples/app1/app/models/user.rb +29 -0
  37. data/samples/app1/bin/bundle +114 -0
  38. data/samples/app1/bin/rails +5 -0
  39. data/samples/app1/bin/rake +5 -0
  40. data/samples/app1/bin/setup +33 -0
  41. data/samples/app1/bin/spring +14 -0
  42. data/samples/app1/config.ru +6 -0
  43. data/samples/app1/config/application.rb +40 -0
  44. data/samples/app1/config/boot.rb +4 -0
  45. data/samples/app1/config/credentials.yml.enc +1 -0
  46. data/samples/app1/config/database.yml +25 -0
  47. data/samples/app1/config/environment.rb +5 -0
  48. data/samples/app1/config/environments/development.rb +63 -0
  49. data/samples/app1/config/environments/production.rb +105 -0
  50. data/samples/app1/config/environments/test.rb +57 -0
  51. data/samples/app1/config/initializers/application_controller_renderer.rb +8 -0
  52. data/samples/app1/config/initializers/backtrace_silencers.rb +8 -0
  53. data/samples/app1/config/initializers/cors.rb +16 -0
  54. data/samples/app1/config/initializers/filter_parameter_logging.rb +6 -0
  55. data/samples/app1/config/initializers/inflections.rb +16 -0
  56. data/samples/app1/config/initializers/mime_types.rb +4 -0
  57. data/samples/app1/config/initializers/pubsub.rb +4 -0
  58. data/samples/app1/config/initializers/wrap_parameters.rb +14 -0
  59. data/samples/app1/config/locales/en.yml +33 -0
  60. data/samples/app1/config/master.key +1 -0
  61. data/samples/app1/config/puma.rb +43 -0
  62. data/samples/app1/config/routes.rb +3 -0
  63. data/samples/app1/config/spring.rb +6 -0
  64. data/samples/app1/db/migrate/20210513080700_create_users.rb +12 -0
  65. data/samples/app1/db/migrate/20210513134332_create_posts.rb +11 -0
  66. data/samples/app1/db/schema.rb +34 -0
  67. data/samples/app1/db/seeds.rb +7 -0
  68. data/samples/app1/docker-compose.yml +32 -0
  69. data/samples/app1/log/.keep +0 -0
  70. data/samples/app2/Dockerfile +13 -0
  71. data/samples/app2/Gemfile +37 -0
  72. data/samples/app2/Gemfile.lock +171 -0
  73. data/samples/app2/README.md +24 -0
  74. data/samples/app2/Rakefile +6 -0
  75. data/samples/app2/app/models/application_record.rb +9 -0
  76. data/samples/app2/app/models/concerns/.keep +0 -0
  77. data/samples/app2/app/models/customer.rb +28 -0
  78. data/samples/app2/app/models/post.rb +10 -0
  79. data/samples/app2/bin/bundle +114 -0
  80. data/samples/app2/bin/rails +5 -0
  81. data/samples/app2/bin/rake +5 -0
  82. data/samples/app2/bin/setup +33 -0
  83. data/samples/app2/bin/spring +14 -0
  84. data/samples/app2/config.ru +6 -0
  85. data/samples/app2/config/application.rb +40 -0
  86. data/samples/app2/config/boot.rb +4 -0
  87. data/samples/app2/config/credentials.yml.enc +1 -0
  88. data/samples/app2/config/database.yml +25 -0
  89. data/samples/app2/config/environment.rb +5 -0
  90. data/samples/app2/config/environments/development.rb +63 -0
  91. data/samples/app2/config/environments/production.rb +105 -0
  92. data/samples/app2/config/environments/test.rb +57 -0
  93. data/samples/app2/config/initializers/application_controller_renderer.rb +8 -0
  94. data/samples/app2/config/initializers/backtrace_silencers.rb +8 -0
  95. data/samples/app2/config/initializers/cors.rb +16 -0
  96. data/samples/app2/config/initializers/filter_parameter_logging.rb +6 -0
  97. data/samples/app2/config/initializers/inflections.rb +16 -0
  98. data/samples/app2/config/initializers/mime_types.rb +4 -0
  99. data/samples/app2/config/initializers/pubsub.rb +4 -0
  100. data/samples/app2/config/initializers/wrap_parameters.rb +14 -0
  101. data/samples/app2/config/locales/en.yml +33 -0
  102. data/samples/app2/config/master.key +1 -0
  103. data/samples/app2/config/puma.rb +43 -0
  104. data/samples/app2/config/routes.rb +3 -0
  105. data/samples/app2/config/spring.rb +6 -0
  106. data/samples/app2/db/development.sqlite3 +0 -0
  107. data/samples/app2/db/migrate/20210513080956_create_customers.rb +10 -0
  108. data/samples/app2/db/migrate/20210513135203_create_posts.rb +10 -0
  109. data/samples/app2/db/schema.rb +31 -0
  110. data/samples/app2/db/seeds.rb +7 -0
  111. data/samples/app2/docker-compose.yml +20 -0
  112. data/samples/app2/log/.keep +0 -0
  113. metadata +93 -5
@@ -2,6 +2,8 @@
2
2
 
3
3
  require 'pub_sub_model_sync'
4
4
  require 'rails'
5
+ require 'active_record'
6
+ require 'pub_sub_model_sync/config'
5
7
  module PubSubModelSync
6
8
  class Railtie < ::Rails::Railtie
7
9
  railtie_name :pub_sub_model_sync
@@ -9,5 +11,9 @@ module PubSubModelSync
9
11
  rake_tasks do
10
12
  load 'pub_sub_model_sync/tasks/worker.rake'
11
13
  end
14
+
15
+ configure do
16
+ require 'pub_sub_model_sync/initializers/before_commit' if PubSubModelSync::Config.enable_rails4_before_commit
17
+ end
12
18
  end
13
19
  end
@@ -26,7 +26,7 @@ module PubSubModelSync
26
26
  def run_class_message
27
27
  model_class = subscriber.klass.constantize
28
28
  model_class.ps_processing_payload = payload # TODO: review for parallel notifications
29
- call_action(model_class, payload.data) if ensure_sync(model_class)
29
+ call_action(model_class) if ensure_sync(model_class)
30
30
  end
31
31
 
32
32
  # support for: create, update, destroy
@@ -48,16 +48,17 @@ module PubSubModelSync
48
48
  res
49
49
  end
50
50
 
51
- def call_action(object, *args)
52
- action_name = settings[:to_action]
53
- if action_name.is_a?(Proc)
54
- args.prepend(object) unless klass_subscription?
55
- action_name.call(*args)
56
- else # method name
57
- action_name = :save if %i[create update].include?(action_name.to_sym)
58
- object.send(action_name, *args)
59
- end
60
- raise(object.errors) if object.respond_to?(:errors) && object.errors.any?
51
+ def call_action(object)
52
+ callback = settings[:to_action]
53
+ callback.is_a?(Proc) ? object.instance_exec(payload.data, &callback) : call_action_method(object)
54
+ end
55
+
56
+ def call_action_method(object)
57
+ method_name = settings[:to_action]
58
+ method_name = :save! if %i[create update].include?(method_name.to_sym)
59
+ method_name = :destroy! if method_name.to_sym == :destroy
60
+ is_crud_action = %i[save! destroy!].include?(method_name)
61
+ is_crud_action ? object.send(method_name) : object.send(method_name, payload.data)
61
62
  end
62
63
 
63
64
  def parse_condition(condition, object)
@@ -74,10 +75,13 @@ module PubSubModelSync
74
75
  model_class = subscriber.klass.constantize
75
76
  return model_class.ps_find_model(payload.data) if model_class.respond_to?(:ps_find_model)
76
77
 
78
+ error_msg = 'No values provided for identifiers:'
79
+ raise(StandardError, "#{error_msg} #{[settings[:id], payload]}") if model_identifiers.empty?
80
+
77
81
  model_class.where(model_identifiers).first_or_initialize
78
82
  end
79
83
 
80
- # @param mappings (Array<String>) supports aliasing, sample: ["id", "full_name:name"]
84
+ # @param mappings (Array<String,Symbol>) supports aliasing, sample: ["id", "full_name:name"]
81
85
  # @return (Hash) hash with the correct attr names and its values
82
86
  def parse_mapping(mappings)
83
87
  mappings.map do |prop|
@@ -91,7 +95,7 @@ module PubSubModelSync
91
95
 
92
96
  # @return (Hash) hash including identifiers and its values
93
97
  def model_identifiers
94
- @model_identifiers ||= parse_mapping(Array(settings[:id]).map(&:to_s))
98
+ @model_identifiers ||= parse_mapping(Array(settings[:id]))
95
99
  end
96
100
 
97
101
  def populate_model
@@ -3,7 +3,6 @@
3
3
  require 'active_support/core_ext/module'
4
4
  module PubSubModelSync
5
5
  class Runner
6
- class ShutDown < StandardError; end
7
6
  delegate :preload_listeners, to: :class
8
7
  attr_accessor :connector
9
8
 
@@ -12,11 +11,10 @@ module PubSubModelSync
12
11
  end
13
12
 
14
13
  def run
14
+ at_exit { connector.stop }
15
15
  trap_signals!
16
16
  preload_listeners
17
17
  start_listeners
18
- rescue ShutDown
19
- connector.stop
20
18
  end
21
19
 
22
20
  def self.preload_listeners
@@ -32,8 +30,8 @@ module PubSubModelSync
32
30
 
33
31
  def trap_signals!
34
32
  handler = proc do |signal|
35
- puts "received #{Signal.signame(signal)}"
36
- raise ShutDown
33
+ puts "PS_MSYNC ==> received #{Signal.signame(signal)}"
34
+ exit
37
35
  end
38
36
  %w[INT QUIT TERM].each { |signal| Signal.trap(signal, handler) }
39
37
  end
@@ -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,14 +37,14 @@ 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
44
44
 
45
45
  def stop
46
46
  log('Listener stopping...')
47
- subscribers.each(&:stop!)
47
+ (subscribers || []).each(&:stop!)
48
48
  end
49
49
 
50
50
  private
@@ -76,8 +76,8 @@ module PubSubModelSync
76
76
  end
77
77
 
78
78
  # Check topic existence, create if missing topic
79
- # @param names (Array<String>|String)
80
- # @return (Array|String) return @param names
79
+ # @param names (Array<String>,String)
80
+ # @return (Array,String) return @param names
81
81
  def ensure_topics(names)
82
82
  missing_topics = Array(names) - (@known_topics || service.topics)
83
83
  missing_topics.each do |name|
@@ -8,7 +8,7 @@ end
8
8
  module PubSubModelSync
9
9
  class ServiceRabbit < ServiceBase
10
10
  QUEUE_SETTINGS = { durable: true, auto_delete: false }.freeze
11
- LISTEN_SETTINGS = { manual_ack: true }.freeze
11
+ LISTEN_SETTINGS = { manual_ack: false }.freeze
12
12
  PUBLISH_SETTINGS = {}.freeze
13
13
 
14
14
  # @!attribute topic_names (Array): ['Topic 1', 'Topic 2']
@@ -9,25 +9,27 @@ module PubSubModelSync
9
9
  end
10
10
 
11
11
  module ClassMethods
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"]
12
+ # @param actions (Symbol,Array<Symbol>) Notification.action name: save|create|update|destroy|<any_other_action>
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
- # to_action (Symbol|Proc):
16
+ # to_action (Symbol,Proc):
17
17
  # Symbol: Method to process the notification
18
18
  # Proc: Block to process the notification
19
- # id (Symbol|Array<Symbol|String>) attribute(s) DB primary identifier(s). Supports for mapping format.
20
- # if (Symbol|Proc|Array<Symbol>) Method or block called as the conformation before calling the callback
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|
19
+ # id (Symbol,Array<Symbol,String>) attribute(s) DB primary identifier(s). Supports for mapping format.
20
+ # if (Symbol,Proc,Array<Symbol>) Method or block called as the conformation before calling the callback
21
+ # unless (Symbol,Proc,Array<Symbol>) Method or block called as the negation before calling the callback
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
 
@@ -3,55 +3,71 @@
3
3
  module PubSubModelSync
4
4
  class Transaction < Base
5
5
  PUBLISHER_KLASS = PubSubModelSync::MessagePublisher
6
- attr_accessor :key, :payloads, :use_buffer, :parent, :children
6
+ attr_accessor :key, :payloads, :max_buffer, :root, :children, :finished
7
7
 
8
- # @param key (String|nil) Transaction key, if empty will use the ordering_key from first payload
9
- # @param use_buffer (Boolean, default: true) If false, payloads are delivered immediately
10
- # (no way to cancel/rollback if transaction failed)
11
- def initialize(key, use_buffer: config.transactions_use_buffer)
8
+ # @param key (String,Null) Transaction key, if empty will use the ordering_key from first payload
9
+ # @param max_buffer (Integer) Once this quantity of notifications is reached, then all notifications
10
+ # will immediately be delivered.
11
+ # Note: There is no way to rollback delivered notifications if current transaction fails
12
+ def initialize(key, max_buffer: config.transactions_max_buffer)
12
13
  @key = key
13
- @use_buffer = use_buffer
14
+ @max_buffer = max_buffer
14
15
  @children = []
15
16
  @payloads = []
16
17
  end
17
18
 
18
19
  # @param payload (Payload)
19
20
  def add_payload(payload)
20
- use_buffer ? payloads << payload : deliver_payload(payload)
21
+ payloads << payload
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
21
27
  end
22
28
 
23
- def deliver_all
24
- if parent
25
- parent.children = parent.children.reject { |t| t == self }
26
- parent.deliver_all
29
+ def finish # rubocop:disable Metrics/AbcSize
30
+ if root
31
+ root.children = root.children.reject { |t| t == self }
32
+ root.deliver_all if root.finished && root.children.empty?
27
33
  end
28
- payloads.each(&method(:deliver_payload)) if children.empty?
29
- clean_publisher
34
+ self.finished = true
35
+ deliver_all if children.empty?
30
36
  end
31
37
 
32
38
  def add_transaction(transaction)
33
- transaction.parent = self
39
+ transaction.root = self
34
40
  children << transaction
35
41
  transaction
36
42
  end
37
43
 
38
44
  def rollback
39
- log("rollback #{children.count} notifications", :warn) if children.any? && debug?
45
+ log("Rollback #{payloads.count} notifications", :warn) if children.any? && debug?
40
46
  self.children = []
41
- parent&.rollback
47
+ root&.rollback
42
48
  clean_publisher
43
49
  end
44
50
 
45
51
  def clean_publisher
46
- PUBLISHER_KLASS.current_transaction = nil if !parent && children.empty?
52
+ PUBLISHER_KLASS.current_transaction = nil if !root && children.empty?
53
+ end
54
+
55
+ def deliver_all
56
+ deliver_payloads
57
+ clean_publisher
47
58
  end
48
59
 
49
60
  private
50
61
 
51
- def deliver_payload(payload)
52
- PUBLISHER_KLASS.connector_publish(payload)
53
- rescue => e
54
- PUBLISHER_KLASS.send(:notify_error, e, payload)
62
+ def deliver_payloads
63
+ payloads.each do |payload|
64
+ begin # rubocop:disable Style/RedundantBegin (ruby 2.4 support)
65
+ PUBLISHER_KLASS.connector_publish(payload)
66
+ rescue => e
67
+ PUBLISHER_KLASS.send(:notify_error, e, payload)
68
+ end
69
+ end
70
+ self.payloads = []
55
71
  end
56
72
  end
57
73
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PubSubModelSync
4
- VERSION = '1.0.beta'
4
+ VERSION = '1.0.1'
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
+ ```cd samples/app1 && 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,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', '>= 1.0'
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]