cable_ready 5.0.0.pre0 → 5.0.0.pre4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +87 -8
  3. data/Gemfile.lock +105 -97
  4. data/README.md +10 -6
  5. data/app/channels/cable_ready/stream.rb +12 -0
  6. data/app/helpers/cable_ready_helper.rb +24 -0
  7. data/app/jobs/cable_ready_broadcast_job.rb +14 -0
  8. data/app/models/concerns/cable_ready/updatable/collection_updatable_callbacks.rb +19 -0
  9. data/app/models/concerns/cable_ready/updatable/collections_registry.rb +33 -0
  10. data/app/models/concerns/cable_ready/updatable/model_updatable_callbacks.rb +28 -0
  11. data/app/models/concerns/cable_ready/updatable.rb +98 -0
  12. data/app/models/concerns/extend_has_many.rb +13 -0
  13. data/lib/cable_ready/channels.rb +1 -1
  14. data/lib/cable_ready/compoundable.rb +1 -1
  15. data/lib/cable_ready/config.rb +2 -0
  16. data/lib/cable_ready/identifiable.rb +13 -2
  17. data/lib/cable_ready/operation_builder.rb +25 -14
  18. data/lib/cable_ready/sanity_checker.rb +50 -50
  19. data/lib/cable_ready/version.rb +1 -1
  20. data/lib/cable_ready.rb +3 -0
  21. data/lib/generators/cable_ready/{stream_from_generator.rb → helpers_generator.rb} +1 -1
  22. data/test/dummy/app/channels/application_cable/channel.rb +4 -0
  23. data/test/dummy/app/channels/application_cable/connection.rb +4 -0
  24. data/test/dummy/app/controllers/application_controller.rb +2 -0
  25. data/test/dummy/app/helpers/application_helper.rb +2 -0
  26. data/test/dummy/app/jobs/application_job.rb +7 -0
  27. data/test/dummy/app/mailers/application_mailer.rb +4 -0
  28. data/test/dummy/app/models/application_record.rb +3 -0
  29. data/test/dummy/app/models/global_idable_entity.rb +16 -0
  30. data/test/dummy/app/models/post.rb +4 -0
  31. data/test/dummy/app/models/section.rb +6 -0
  32. data/test/dummy/app/models/team.rb +6 -0
  33. data/test/dummy/app/models/topic.rb +4 -0
  34. data/test/dummy/app/models/user.rb +7 -0
  35. data/test/dummy/config/application.rb +22 -0
  36. data/test/dummy/config/boot.rb +5 -0
  37. data/test/dummy/config/environment.rb +5 -0
  38. data/test/dummy/config/environments/development.rb +76 -0
  39. data/test/dummy/config/environments/production.rb +120 -0
  40. data/test/dummy/config/environments/test.rb +59 -0
  41. data/test/dummy/config/initializers/application_controller_renderer.rb +8 -0
  42. data/test/dummy/config/initializers/assets.rb +12 -0
  43. data/test/dummy/config/initializers/backtrace_silencers.rb +8 -0
  44. data/test/dummy/config/initializers/cable_ready.rb +18 -0
  45. data/test/dummy/config/initializers/content_security_policy.rb +28 -0
  46. data/test/dummy/config/initializers/cookies_serializer.rb +5 -0
  47. data/test/dummy/config/initializers/filter_parameter_logging.rb +6 -0
  48. data/test/dummy/config/initializers/inflections.rb +16 -0
  49. data/test/dummy/config/initializers/mime_types.rb +4 -0
  50. data/test/dummy/config/initializers/permissions_policy.rb +11 -0
  51. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  52. data/test/dummy/config/puma.rb +43 -0
  53. data/test/dummy/config/routes.rb +3 -0
  54. data/test/dummy/db/migrate/20210902154139_create_users.rb +9 -0
  55. data/test/dummy/db/migrate/20210902154153_create_posts.rb +10 -0
  56. data/test/dummy/db/migrate/20210904081930_create_topics.rb +9 -0
  57. data/test/dummy/db/migrate/20210904093607_create_sections.rb +9 -0
  58. data/test/dummy/db/migrate/20210913191735_create_teams.rb +8 -0
  59. data/test/dummy/db/migrate/20210913191759_add_team_reference_to_users.rb +5 -0
  60. data/test/dummy/db/schema.rb +49 -0
  61. data/test/dummy/test/models/post_test.rb +7 -0
  62. data/test/dummy/test/models/section_test.rb +7 -0
  63. data/test/dummy/test/models/team_test.rb +7 -0
  64. data/test/dummy/test/models/topic_test.rb +7 -0
  65. data/test/dummy/test/models/user_test.rb +7 -0
  66. data/test/lib/cable_ready/cable_car_test.rb +25 -3
  67. data/test/lib/cable_ready/compoundable_test.rb +26 -0
  68. data/test/lib/cable_ready/helper_test.rb +25 -0
  69. data/test/lib/cable_ready/identifiable_test.rb +0 -6
  70. data/test/lib/cable_ready/operation_builder_test.rb +89 -28
  71. data/test/lib/cable_ready/updatable_test.rb +112 -0
  72. data/test/test_helper.rb +4 -1
  73. metadata +126 -14
  74. data/cable_ready.gemspec +0 -27
  75. data/package.json +0 -39
  76. data/tags +0 -80
  77. data/yarn.lock +0 -2562
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CableReady
4
+ module Updatable
5
+ extend ::ActiveSupport::Concern
6
+
7
+ included do |base|
8
+ if base < ActiveRecord::Base
9
+ include ExtendHasMany
10
+
11
+ after_commit CollectionUpdatableCallbacks.new(:create), on: :create
12
+ after_commit CollectionUpdatableCallbacks.new(:update), on: :update
13
+ after_commit CollectionUpdatableCallbacks.new(:destroy), on: :destroy
14
+
15
+ def self.enable_updates(*options)
16
+ options = options.extract_options!
17
+ options = {
18
+ on: [:create, :update, :destroy],
19
+ if: -> { true }
20
+ }.merge(options)
21
+
22
+ enabled_operations = Array(options[:on])
23
+
24
+ after_commit(ModelUpdatableCallbacks.new(:create, enabled_operations), {on: :create, if: options[:if]})
25
+ after_commit(ModelUpdatableCallbacks.new(:update, enabled_operations), {on: :update, if: options[:if]})
26
+ after_commit(ModelUpdatableCallbacks.new(:destroy, enabled_operations), {on: :destroy, if: options[:if]})
27
+ end
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ module ClassMethods
34
+ def has_many(name, scope = nil, **options, &extension)
35
+ option = options.delete(:enable_updates)
36
+ broadcast = option.present?
37
+ result = super
38
+ enrich_association_with_updates(name, option) if broadcast
39
+ result
40
+ end
41
+
42
+ def cable_ready_collections
43
+ @cable_ready_collections ||= CollectionsRegistry.new
44
+ end
45
+
46
+ def cable_ready_update_collection(resource, name)
47
+ identifier = resource.to_global_id.to_s + ":" + name.to_s
48
+ ActionCable.server.broadcast(identifier, {})
49
+ end
50
+
51
+ def enrich_association_with_updates(name, option)
52
+ reflection = reflect_on_association(name)
53
+
54
+ inverse_of = reflection.inverse_of&.name&.to_s
55
+ through_association = nil
56
+
57
+ if reflection.through_reflection?
58
+ inverse_of = reflection.through_reflection.inverse_of&.name&.to_s
59
+ through_association = reflection.through_reflection.name.to_s.singularize
60
+ end
61
+
62
+ options = {
63
+ on: [:create, :update, :destroy],
64
+ if: ->(resource) { true }
65
+ }
66
+
67
+ case option
68
+ when TrueClass
69
+ # proceed!
70
+ when FalseClass
71
+ options[:on] = []
72
+ when Array
73
+ options[:on] = option
74
+ when Symbol
75
+ options[:on] = [option]
76
+ when Hash
77
+ option[:on] = Array(option[:on]) if option[:on]
78
+ options = options.merge!(option)
79
+ when Proc
80
+ options[:if] = option
81
+ else
82
+ raise ArgumentError, "Invalid enable_updates option #{option}"
83
+ end
84
+
85
+ reflection.klass.send(:include, CableReady::Updatable) unless reflection.klass.respond_to?(:cable_ready_collections)
86
+
87
+ reflection.klass.cable_ready_collections.register({
88
+ klass: self,
89
+ foreign_key: reflection.foreign_key,
90
+ name: name,
91
+ inverse_association: inverse_of,
92
+ through_association: through_association,
93
+ options: options
94
+ })
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ExtendHasMany
4
+ extend ::ActiveSupport::Concern
5
+
6
+ class_methods do
7
+ def has_many(*args, &block)
8
+ options = args.extract_options!
9
+ options[:extend] = Array(options[:extend]).push(ClassMethods)
10
+ super(*args, **options, &block)
11
+ end
12
+ end
13
+ end
@@ -13,7 +13,7 @@ module CableReady
13
13
 
14
14
  def [](*keys)
15
15
  keys.select!(&:itself)
16
- identifier = keys.many? || (keys.one? && keys.first.is_a?(ActiveRecord::Base)) ? compound(keys) : keys.pop
16
+ identifier = keys.many? || (keys.one? && keys.first.respond_to?(:to_global_id)) ? compound(keys) : keys.pop
17
17
  @channels[identifier] ||= CableReady::Channel.new(identifier)
18
18
  end
19
19
 
@@ -4,7 +4,7 @@ module CableReady
4
4
  module Compoundable
5
5
  def compound(keys)
6
6
  keys.map { |key|
7
- key.class < ActiveRecord::Base ? key.to_global_id.to_s : key.to_s
7
+ key.respond_to?(:to_global_id) ? key.to_global_id.to_s : key.to_s
8
8
  }.join(":")
9
9
  end
10
10
  end
@@ -54,6 +54,8 @@ module CableReady
54
54
  outer_html
55
55
  prepend
56
56
  push_state
57
+ redirect_to
58
+ reload
57
59
  remove
58
60
  remove_attribute
59
61
  remove_css_class
@@ -3,9 +3,13 @@
3
3
  module CableReady
4
4
  module Identifiable
5
5
  def dom_id(record, prefix = nil)
6
+ return record.to_dom_selector if record.respond_to?(:to_dom_selector)
7
+
6
8
  prefix = prefix.to_s.strip if prefix
7
9
 
8
- id = if record.is_a?(ActiveRecord::Relation)
10
+ id = if record.respond_to?(:to_dom_id)
11
+ record.to_dom_id
12
+ elsif record.is_a?(ActiveRecord::Relation)
9
13
  [prefix, record.model_name.plural].compact.join("_")
10
14
  elsif record.is_a?(ActiveRecord::Base)
11
15
  ActionView::RecordIdentifier.dom_id(record, prefix)
@@ -13,7 +17,14 @@ module CableReady
13
17
  [prefix, record.to_s.strip].compact.join("_")
14
18
  end
15
19
 
16
- "##{id}".squeeze("#").strip
20
+ "##{id}".squeeze("#").strip.downcase
21
+ end
22
+
23
+ def identifiable?(obj)
24
+ obj.respond_to?(:to_dom_selector) ||
25
+ obj.respond_to?(:to_dom_id) ||
26
+ obj.is_a?(ActiveRecord::Relation) ||
27
+ obj.is_a?(ActiveRecord::Base)
17
28
  end
18
29
  end
19
30
  end
@@ -24,17 +24,32 @@ module CableReady
24
24
  def add_operation_method(name)
25
25
  return if respond_to?(name)
26
26
  singleton_class.public_send :define_method, name, ->(*args) {
27
- selector, options = nil, args.first || {} # 1 or 0 params
28
- selector, options = options, {} unless options.is_a?(Hash) # swap if only selector provided
29
- selector, options = args[0, 2] if args.many? # 2 or more params
30
- options.stringify_keys!
27
+ if args.one? && args.first.respond_to?(:to_operation_options) && [Array, Hash].include?(args.first.to_operation_options.class)
28
+ case args.first.to_operation_options
29
+ when Array
30
+ selector, options = nil, args.first.to_operation_options
31
+ .select { |e| e.is_a?(Symbol) && args.first.respond_to?("to_#{e}".to_sym) }
32
+ .each_with_object({}) { |option, memo| memo[option.to_s] = args.first.send("to_#{option}".to_sym) }
33
+ when Hash
34
+ selector, options = nil, args.first.to_operation_options
35
+ else
36
+ raise TypeError, ":to_operation_options returned an #{args.first.to_operation_options.class.name}. Must be an Array or Hash."
37
+ end
38
+ else
39
+ selector, options = nil, args.first || {} # 1 or 0 params
40
+ selector, options = options, {} unless options.is_a?(Hash) # swap if only selector provided
41
+ selector, options = args[0, 2] if args.many? # 2 or more params
42
+ options.stringify_keys!
43
+ options.each { |key, value| options[key] = value.send("to_#{key}".to_sym) if value.respond_to?("to_#{key}".to_sym) }
44
+ end
31
45
  options["selector"] = selector if selector && options.exclude?("selector")
32
46
  options["selector"] = previous_selector if previous_selector && options.exclude?("selector")
33
47
  if options.include?("selector")
34
48
  @previous_selector = options["selector"]
35
- options["selector"] = previous_selector.is_a?(ActiveRecord::Base) || previous_selector.is_a?(ActiveRecord::Relation) ? dom_id(previous_selector) : previous_selector
49
+ options["selector"] = identifiable?(previous_selector) ? dom_id(previous_selector) : previous_selector
36
50
  end
37
- @enqueued_operations[name.to_s] << options
51
+ options["operation"] = name.to_s.camelize(:lower)
52
+ @enqueued_operations << options
38
53
  self
39
54
  }
40
55
  end
@@ -43,26 +58,22 @@ module CableReady
43
58
  @enqueued_operations.to_json(*args)
44
59
  end
45
60
 
46
- def apply!(operations = "{}")
61
+ def apply!(operations = "[]")
47
62
  operations = begin
48
63
  JSON.parse(operations.is_a?(String) ? operations : operations.to_json)
49
64
  rescue JSON::ParserError
50
65
  {}
51
66
  end
52
- operations.each do |name, operation|
53
- operation.each do |enqueued_operation|
54
- @enqueued_operations[name.to_s] << enqueued_operation
55
- end
56
- end
67
+ @enqueued_operations.push(operations)
57
68
  self
58
69
  end
59
70
 
60
71
  def operations_payload
61
- @enqueued_operations.select { |_, list| list.present? }.deep_transform_keys { |key| key.to_s.camelize(:lower) }
72
+ @enqueued_operations.map { |operation| operation.deep_transform_keys! { |key| key.to_s.camelize(:lower) } }
62
73
  end
63
74
 
64
75
  def reset!
65
- @enqueued_operations = Hash.new { |hash, key| hash[key] = [] }
76
+ @enqueued_operations = []
66
77
  @previous_selector = nil
67
78
  end
68
79
  end
@@ -7,48 +7,59 @@ class CableReady::SanityChecker
7
7
 
8
8
  class << self
9
9
  def check!
10
+ return if ENV["SKIP_SANITY_CHECK"]
10
11
  return if CableReady.config.on_failed_sanity_checks == :ignore
11
12
  return if called_by_generate_config?
13
+ return if called_by_rake?
12
14
 
13
15
  instance = new
14
- instance.check_javascript_package_version
16
+ instance.check_package_versions_match
15
17
  instance.check_new_version_available
16
18
  end
17
19
 
18
20
  private
19
21
 
20
22
  def called_by_generate_config?
21
- ARGV.include? "cable_ready:initializer"
23
+ ARGV.include?("cable_ready:initializer")
24
+ end
25
+
26
+ def called_by_rake?
27
+ File.basename($PROGRAM_NAME) == "rake"
22
28
  end
23
29
  end
24
30
 
25
- def check_javascript_package_version
26
- if javascript_package_version.nil?
31
+ def check_package_versions_match
32
+ if npm_version.nil?
27
33
  warn_and_exit <<~WARN
28
- Can't locate the cable_ready npm package.
34
+ 👉 Can't locate the cable_ready npm package.
35
+
36
+ yarn add cable_ready@#{gem_version}
37
+
29
38
  Either add it to your package.json as a dependency or use "yarn link cable_ready" if you are doing development.
30
39
  WARN
31
40
  end
32
41
 
33
- unless javascript_version_matches?
42
+ if package_version_mismatch?
34
43
  warn_and_exit <<~WARN
35
- The cable_ready npm package version (#{javascript_package_version}) does not match the Rubygem version (#{gem_version}).
44
+ 👉 The cable_ready npm package version (#{npm_version}) does not match the Rubygem version (#{gem_version}).
45
+
36
46
  To update the cable_ready npm package:
47
+
37
48
  yarn upgrade cable_ready@#{gem_version}
38
49
  WARN
39
50
  end
40
51
  end
41
52
 
42
53
  def check_new_version_available
43
- return unless Rails.env.development?
44
54
  return if CableReady.config.on_new_version_available == :ignore
45
- return unless using_stable_release
55
+ return if Rails.env.development? == false
56
+ return if using_preview_release?
46
57
  begin
47
58
  latest_version = URI.open("https://raw.githubusercontent.com/stimulusreflex/cable_ready/master/LATEST", open_timeout: 1, read_timeout: 1).read.strip
48
59
  if latest_version != CableReady::VERSION
49
60
  puts <<~WARN
50
61
 
51
- There is a new version of CableReady available!
62
+ 👉 There is a new version of CableReady available!
52
63
  Current: #{CableReady::VERSION} Latest: #{latest_version}
53
64
 
54
65
  If you upgrade, it is very important that you update BOTH Gemfile and package.json
@@ -58,31 +69,31 @@ class CableReady::SanityChecker
58
69
  exit if CableReady.config.on_new_version_available == :exit
59
70
  end
60
71
  rescue
61
- puts "CableReady #{CableReady::VERSION} update check skipped: connection timeout"
72
+ puts "👉 CableReady #{CableReady::VERSION} update check skipped: connection timeout"
62
73
  end
63
74
  end
64
75
 
65
76
  private
66
77
 
67
- def javascript_version_matches?
68
- javascript_package_version == gem_version
78
+ def package_version_mismatch?
79
+ npm_version != gem_version
69
80
  end
70
81
 
71
- def using_stable_release
72
- stable = CableReady::VERSION.match?(LATEST_VERSION_FORMAT)
73
- puts "CableReady #{CableReady::VERSION} update check skipped: pre-release build" unless stable
74
- stable
82
+ def using_preview_release?
83
+ preview = CableReady::VERSION.match?(LATEST_VERSION_FORMAT) == false
84
+ puts "👉 CableReady #{CableReady::VERSION} update check skipped: pre-release build" if preview
85
+ preview
75
86
  end
76
87
 
77
88
  def gem_version
78
89
  @_gem_version ||= CableReady::VERSION.gsub(".pre", "-pre")
79
90
  end
80
91
 
81
- def javascript_package_version
82
- @_js_version ||= find_javascript_package_version
92
+ def npm_version
93
+ @_npm_version ||= find_npm_version
83
94
  end
84
95
 
85
- def find_javascript_package_version
96
+ def find_npm_version
86
97
  if (match = search_file(package_json_path, regex: /version/))
87
98
  match[JSON_VERSION_FORMAT, 1]
88
99
  elsif (match = search_file(yarn_lock_path, regex: /^cable_ready/))
@@ -91,7 +102,7 @@ class CableReady::SanityChecker
91
102
  end
92
103
 
93
104
  def search_file(path, regex:)
94
- return unless File.exist?(path)
105
+ return if File.exist?(path) == false
95
106
  File.foreach(path).grep(regex).first
96
107
  end
97
108
 
@@ -103,49 +114,38 @@ class CableReady::SanityChecker
103
114
  Rails.root.join("yarn.lock")
104
115
  end
105
116
 
106
- def initializer_path
107
- @_initializer_path ||= Rails.root.join("config", "initializers", "cable_ready.rb")
117
+ def initializer_missing?
118
+ File.exist?(Rails.root.join("config", "initializers", "cable_ready.rb")) == false
108
119
  end
109
120
 
110
121
  def warn_and_exit(text)
111
- puts "WARNING:"
122
+ puts
123
+ puts "Heads up! 🔥"
124
+ puts
112
125
  puts text
113
- exit_with_info if CableReady.config.on_failed_sanity_checks == :exit
114
- end
115
-
116
- def exit_with_info
117
126
  puts
118
-
119
- if File.exist?(initializer_path)
127
+ if CableReady.config.on_failed_sanity_checks == :exit
120
128
  puts <<~INFO
121
- If you know what you are doing and you want to start the application anyway,
122
- you can add the following directive to the CableReady initializer,
123
- which is located at #{initializer_path}
129
+ To ignore any warnings and start the application anyway, you can set the SKIP_SANITY_CHECK environment variable:
124
130
 
125
- CableReady.configure do |config|
126
- config.on_failed_sanity_checks = :warn
127
- end
128
-
129
- INFO
130
- else
131
- puts <<~INFO
132
- If you know what you are doing and you want to start the application anyway,
133
- you can create a CableReady initializer with the command:
134
-
135
- bundle exec rails generate cable_ready:config
131
+ SKIP_SANITY_CHECK=true rails
136
132
 
137
- Then open your initializer at
138
-
139
- #{initializer_path}
140
-
141
- and then add the following directive:
133
+ To do this permanently, add the following directive to the CableReady initializer:
142
134
 
143
135
  CableReady.configure do |config|
144
136
  config.on_failed_sanity_checks = :warn
145
137
  end
146
138
 
147
139
  INFO
140
+ if initializer_missing?
141
+ puts <<~INFO
142
+ You can create a CableReady initializer with the command:
143
+
144
+ bundle exec rails generate cable_ready:initializer
145
+
146
+ INFO
147
+ end
148
+ exit false if Rails.env.test? == false
148
149
  end
149
- exit false unless Rails.env.test?
150
150
  end
151
151
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CableReady
4
- VERSION = "5.0.0.pre0"
4
+ VERSION = "5.0.0.pre4"
5
5
  end
data/lib/cable_ready.rb CHANGED
@@ -30,8 +30,11 @@ module CableReady
30
30
  initializer "renderer" do
31
31
  ActiveSupport.on_load(:action_controller) do
32
32
  ActionController::Renderers.add :operations do |operations, options|
33
+ response.content_type ||= Mime[:cable_ready]
33
34
  render json: operations.dispatch
34
35
  end
36
+
37
+ Mime::Type.register "application/vnd.cable-ready.json", :cable_ready
35
38
  end
36
39
  end
37
40
  end
@@ -4,7 +4,7 @@ require "rails/generators"
4
4
  require "fileutils"
5
5
 
6
6
  module CableReady
7
- class StreamFromGenerator < Rails::Generators::Base
7
+ class HelpersGenerator < Rails::Generators::Base
8
8
  desc "Initializes CableReady with a reference to the shared ActionCable consumer"
9
9
  source_root File.expand_path("templates", __dir__)
10
10
 
@@ -0,0 +1,4 @@
1
+ module ApplicationCable
2
+ class Channel < ActionCable::Channel::Base
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ApplicationCable
2
+ class Connection < ActionCable::Connection::Base
3
+ end
4
+ end
@@ -0,0 +1,2 @@
1
+ class ApplicationController < ActionController::Base
2
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,7 @@
1
+ class ApplicationJob < ActiveJob::Base
2
+ # Automatically retry jobs that encountered a deadlock
3
+ # retry_on ActiveRecord::Deadlocked
4
+
5
+ # Most jobs are safe to ignore if the underlying records are no longer available
6
+ # discard_on ActiveJob::DeserializationError
7
+ end
@@ -0,0 +1,4 @@
1
+ class ApplicationMailer < ActionMailer::Base
2
+ default from: 'from@example.com'
3
+ layout 'mailer'
4
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end
@@ -0,0 +1,16 @@
1
+ class GlobalIdableEntity
2
+ include GlobalID::Identification
3
+ include CableReady::Updatable
4
+
5
+ def id
6
+ "fake-id"
7
+ end
8
+
9
+ def self.find(id)
10
+ new if id == "fake-id"
11
+ end
12
+
13
+ def fake_update
14
+ ModelUpdatableCallbacks.new(:update).after_commit(self)
15
+ end
16
+ end
@@ -0,0 +1,4 @@
1
+ class Post < ApplicationRecord
2
+ include CableReady::Updatable
3
+ belongs_to :user
4
+ end
@@ -0,0 +1,6 @@
1
+ class Section < ApplicationRecord
2
+ include CableReady::Updatable
3
+ enable_updates if: -> { updates_enabled }
4
+
5
+ attribute :updates_enabled, :boolean, default: false
6
+ end
@@ -0,0 +1,6 @@
1
+ class Team < ApplicationRecord
2
+ include CableReady::Updatable
3
+ enable_updates
4
+
5
+ has_many :users, enable_updates: true
6
+ end
@@ -0,0 +1,4 @@
1
+ class Topic < ApplicationRecord
2
+ include CableReady::Updatable
3
+ enable_updates on: :create
4
+ end
@@ -0,0 +1,7 @@
1
+ class User < ApplicationRecord
2
+ include CableReady::Updatable
3
+ enable_updates
4
+
5
+ has_many :posts, enable_updates: true
6
+ belongs_to :team, optional: true, touch: true
7
+ end
@@ -0,0 +1,22 @@
1
+ require_relative "boot"
2
+
3
+ require "rails/all"
4
+
5
+ # Require the gems listed in Gemfile, including any gems
6
+ # you've limited to :test, :development, or :production.
7
+ Bundler.require(*Rails.groups)
8
+ require "cable_ready"
9
+
10
+ module Dummy
11
+ class Application < Rails::Application
12
+ config.load_defaults Rails::VERSION::STRING.to_f
13
+
14
+ # Configuration for the application, engines, and railties goes here.
15
+ #
16
+ # These settings can be overridden in specific environments using the files
17
+ # in config/environments, which are processed later.
18
+ #
19
+ # config.time_zone = "Central Time (US & Canada)"
20
+ # config.eager_load_paths << Rails.root.join("extras")
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ # Set up gems listed in the Gemfile.
2
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../../Gemfile', __dir__)
3
+
4
+ require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"])
5
+ $LOAD_PATH.unshift File.expand_path('../../../lib', __dir__)
@@ -0,0 +1,5 @@
1
+ # Load the Rails application.
2
+ require_relative "application"
3
+
4
+ # Initialize the Rails application.
5
+ Rails.application.initialize!