cable_ready 5.0.0.pre9 → 5.0.0.pre10
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.
- checksums.yaml +4 -4
- data/Gemfile +4 -1
- data/Gemfile.lock +119 -100
- data/README.md +2 -5
- data/app/assets/javascripts/cable_ready.js +457 -151
- data/app/assets/javascripts/cable_ready.min.js +1 -1
- data/app/assets/javascripts/cable_ready.min.js.map +1 -1
- data/app/assets/javascripts/cable_ready.umd.js +441 -165
- data/app/assets/javascripts/cable_ready.umd.min.js +1 -1
- data/app/assets/javascripts/cable_ready.umd.min.js.map +1 -1
- data/app/channels/cable_ready/stream.rb +7 -5
- data/app/helpers/cable_ready/view_helper.rb +58 -0
- data/app/jobs/cable_ready_broadcast_job.rb +9 -8
- data/app/models/concerns/cable_ready/updatable/collection_updatable_callbacks.rb +2 -0
- data/app/models/concerns/cable_ready/updatable/collections_registry.rb +2 -0
- data/app/models/concerns/cable_ready/updatable/model_updatable_callbacks.rb +5 -2
- data/app/models/concerns/cable_ready/updatable.rb +67 -19
- data/app/models/concerns/extend_has_many.rb +2 -0
- data/cable_ready.gemspec +4 -6
- data/lib/cable_ready/broadcaster.rb +2 -0
- data/lib/cable_ready/cable_car.rb +2 -0
- data/lib/cable_ready/channel.rb +12 -4
- data/lib/cable_ready/channels.rb +3 -1
- data/lib/cable_ready/config.rb +16 -2
- data/lib/cable_ready/engine.rb +19 -9
- data/lib/cable_ready/identifiable.rb +23 -5
- data/lib/cable_ready/importmap.rb +2 -0
- data/lib/cable_ready/installer.rb +224 -0
- data/lib/cable_ready/operation_builder.rb +1 -1
- data/lib/cable_ready/sanity_checker.rb +1 -31
- data/lib/cable_ready/version.rb +1 -1
- data/lib/cable_ready.rb +3 -8
- data/lib/cable_ready_helper.rb +13 -0
- data/lib/generators/cable_ready/channel_generator.rb +51 -12
- data/lib/generators/cable_ready/templates/config/initializers/cable_ready.rb +10 -6
- data/lib/install/action_cable.rb +144 -0
- data/lib/install/broadcaster.rb +109 -0
- data/lib/install/bundle.rb +54 -0
- data/lib/install/compression.rb +51 -0
- data/lib/install/config.rb +39 -0
- data/lib/install/development.rb +34 -0
- data/lib/install/esbuild.rb +101 -0
- data/lib/install/importmap.rb +96 -0
- data/lib/install/initializers.rb +15 -0
- data/lib/install/mrujs.rb +121 -0
- data/lib/install/npm_packages.rb +13 -0
- data/lib/install/shakapacker.rb +61 -0
- data/lib/install/spring.rb +54 -0
- data/lib/install/updatable.rb +34 -0
- data/lib/install/vite.rb +62 -0
- data/lib/install/webpacker.rb +81 -0
- data/lib/install/yarn.rb +56 -0
- data/lib/tasks/cable_ready/cable_ready.rake +249 -0
- data/package.json +28 -18
- data/{rollup.config.js → rollup.config.mjs} +9 -8
- data/web-test-runner.config.mjs +12 -0
- data/yarn.lock +2210 -404
- metadata +37 -161
- data/LATEST +0 -1
- data/app/helpers/cable_ready_helper.rb +0 -26
- data/lib/generators/cable_ready/helpers_generator.rb +0 -43
- data/lib/generators/cable_ready/initializer_generator.rb +0 -14
- data/test/dummy/app/channels/application_cable/channel.rb +0 -4
- data/test/dummy/app/channels/application_cable/connection.rb +0 -4
- data/test/dummy/app/controllers/application_controller.rb +0 -2
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/jobs/application_job.rb +0 -7
- data/test/dummy/app/mailers/application_mailer.rb +0 -4
- data/test/dummy/app/models/application_record.rb +0 -3
- data/test/dummy/app/models/dugong.rb +0 -4
- data/test/dummy/app/models/global_idable_entity.rb +0 -16
- data/test/dummy/app/models/post.rb +0 -4
- data/test/dummy/app/models/section.rb +0 -6
- data/test/dummy/app/models/team.rb +0 -6
- data/test/dummy/app/models/topic.rb +0 -4
- data/test/dummy/app/models/user.rb +0 -7
- data/test/dummy/config/application.rb +0 -22
- data/test/dummy/config/boot.rb +0 -5
- data/test/dummy/config/environment.rb +0 -5
- data/test/dummy/config/environments/development.rb +0 -76
- data/test/dummy/config/environments/production.rb +0 -120
- data/test/dummy/config/environments/test.rb +0 -59
- data/test/dummy/config/initializers/application_controller_renderer.rb +0 -8
- data/test/dummy/config/initializers/assets.rb +0 -12
- data/test/dummy/config/initializers/backtrace_silencers.rb +0 -8
- data/test/dummy/config/initializers/cable_ready.rb +0 -18
- data/test/dummy/config/initializers/content_security_policy.rb +0 -28
- data/test/dummy/config/initializers/cookies_serializer.rb +0 -5
- data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -6
- data/test/dummy/config/initializers/inflections.rb +0 -16
- data/test/dummy/config/initializers/mime_types.rb +0 -4
- data/test/dummy/config/initializers/permissions_policy.rb +0 -11
- data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/test/dummy/config/puma.rb +0 -43
- data/test/dummy/config/routes.rb +0 -3
- data/test/dummy/db/migrate/20210902154139_create_users.rb +0 -9
- data/test/dummy/db/migrate/20210902154153_create_posts.rb +0 -10
- data/test/dummy/db/migrate/20210904081930_create_topics.rb +0 -9
- data/test/dummy/db/migrate/20210904093607_create_sections.rb +0 -9
- data/test/dummy/db/migrate/20210913191735_create_teams.rb +0 -8
- data/test/dummy/db/migrate/20210913191759_add_team_reference_to_users.rb +0 -5
- data/test/dummy/db/migrate/20220329222959_create_dugongs.rb +0 -8
- data/test/dummy/db/migrate/20220329230221_create_active_storage_tables.active_storage.rb +0 -36
- data/test/dummy/db/schema.rb +0 -84
- data/test/dummy/test/models/dugong_test.rb +0 -7
- data/test/dummy/test/models/post_test.rb +0 -7
- data/test/dummy/test/models/section_test.rb +0 -7
- data/test/dummy/test/models/team_test.rb +0 -7
- data/test/dummy/test/models/topic_test.rb +0 -7
- data/test/dummy/test/models/user_test.rb +0 -7
- data/test/lib/cable_ready/cable_car_test.rb +0 -50
- data/test/lib/cable_ready/compoundable_test.rb +0 -26
- data/test/lib/cable_ready/helper_test.rb +0 -25
- data/test/lib/cable_ready/identifiable_test.rb +0 -69
- data/test/lib/cable_ready/operation_builder_test.rb +0 -189
- data/test/lib/cable_ready/updatable_test.rb +0 -135
- data/test/lib/generators/cable_ready/channel_generator_test.rb +0 -157
- data/test/support/generator_test_helpers.rb +0 -28
- data/test/test_helper.rb +0 -18
|
@@ -10,8 +10,7 @@ class CableReady::SanityChecker
|
|
|
10
10
|
return if called_by_generate_config?
|
|
11
11
|
return if called_by_rake?
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
instance.check_new_version_available
|
|
13
|
+
new
|
|
15
14
|
end
|
|
16
15
|
|
|
17
16
|
private
|
|
@@ -25,37 +24,8 @@ class CableReady::SanityChecker
|
|
|
25
24
|
end
|
|
26
25
|
end
|
|
27
26
|
|
|
28
|
-
def check_new_version_available
|
|
29
|
-
return if CableReady.config.on_new_version_available == :ignore
|
|
30
|
-
return if Rails.env.development? == false
|
|
31
|
-
return if using_preview_release?
|
|
32
|
-
begin
|
|
33
|
-
latest_version = URI.open("https://raw.githubusercontent.com/stimulusreflex/cable_ready/master/LATEST", open_timeout: 1, read_timeout: 1).read.strip
|
|
34
|
-
if latest_version != CableReady::VERSION
|
|
35
|
-
puts <<~WARN
|
|
36
|
-
|
|
37
|
-
👉 There is a new version of CableReady available!
|
|
38
|
-
Current: #{CableReady::VERSION} Latest: #{latest_version}
|
|
39
|
-
|
|
40
|
-
If you upgrade, it is very important that you update BOTH Gemfile and package.json
|
|
41
|
-
Then, run `bundle install && yarn install` to update to #{latest_version}.
|
|
42
|
-
|
|
43
|
-
WARN
|
|
44
|
-
exit if CableReady.config.on_new_version_available == :exit
|
|
45
|
-
end
|
|
46
|
-
rescue
|
|
47
|
-
puts "👉 CableReady #{CableReady::VERSION} update check skipped: connection timeout"
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
|
|
51
27
|
private
|
|
52
28
|
|
|
53
|
-
def using_preview_release?
|
|
54
|
-
preview = CableReady::VERSION.match?(LATEST_VERSION_FORMAT) == false
|
|
55
|
-
puts "👉 CableReady #{CableReady::VERSION} update check skipped: pre-release build" if preview
|
|
56
|
-
preview
|
|
57
|
-
end
|
|
58
|
-
|
|
59
29
|
def initializer_missing?
|
|
60
30
|
File.exist?(Rails.root.join("config", "initializers", "cable_ready.rb")) == false
|
|
61
31
|
end
|
data/lib/cable_ready/version.rb
CHANGED
data/lib/cable_ready.rb
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "open-uri"
|
|
4
|
-
require "
|
|
5
|
-
require "action_view"
|
|
6
|
-
require "active_support/all"
|
|
7
|
-
require "thread/local"
|
|
8
|
-
require "monitor"
|
|
9
|
-
require "observer"
|
|
10
|
-
require "singleton"
|
|
11
|
-
require "cable_ready/version"
|
|
4
|
+
require "active_support/message_verifier"
|
|
12
5
|
require "cable_ready/identifiable"
|
|
13
6
|
require "cable_ready/operation_builder"
|
|
14
7
|
require "cable_ready/config"
|
|
@@ -20,6 +13,8 @@ require "cable_ready/channel"
|
|
|
20
13
|
require "cable_ready/channels"
|
|
21
14
|
require "cable_ready/cable_car"
|
|
22
15
|
require "cable_ready/stream_identifier"
|
|
16
|
+
require "cable_ready/version"
|
|
17
|
+
require "cable_ready_helper"
|
|
23
18
|
|
|
24
19
|
module CableReady
|
|
25
20
|
class << self
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# TODO: remove me once CableReady 5.0 was released
|
|
4
|
+
|
|
5
|
+
require_relative "../app/helpers/cable_ready/view_helper"
|
|
6
|
+
|
|
7
|
+
module CableReadyHelper
|
|
8
|
+
def self.included(base)
|
|
9
|
+
warn "NOTICE: `CableReadyHelper` was renamed to `CableReady::ViewHelper`. Please update your include accordingly."
|
|
10
|
+
|
|
11
|
+
base.include(::CableReady::ViewHelper)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -7,32 +7,71 @@ class CableReady::ChannelGenerator < Rails::Generators::NamedBase
|
|
|
7
7
|
class_option :stream_for, type: :string
|
|
8
8
|
class_option :stimulus, type: :boolean
|
|
9
9
|
|
|
10
|
+
def destroy_not_supported
|
|
11
|
+
if behavior == :revoke
|
|
12
|
+
puts "Sorry, we don't support destroying generated channels.\nDelete the Action Cable channel class, as well as any corresponding JavaScript classes."
|
|
13
|
+
exit
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
10
17
|
def check_options
|
|
11
|
-
|
|
18
|
+
if options.key?(:stream_from) && options.key?(:stream_for)
|
|
19
|
+
puts "Can't specify --stream-from and --stream-for at the same time"
|
|
20
|
+
exit
|
|
21
|
+
end
|
|
12
22
|
end
|
|
13
23
|
|
|
14
24
|
def create_channel
|
|
15
|
-
generate "channel", file_name
|
|
25
|
+
generate "channel", file_name, "--skip"
|
|
16
26
|
end
|
|
17
27
|
|
|
18
28
|
def enhance_channels
|
|
29
|
+
@entrypoint = [
|
|
30
|
+
"app/javascript",
|
|
31
|
+
"app/frontend"
|
|
32
|
+
].find { |path| File.exist?(Rails.root.join(path)) } || "app/javascript"
|
|
33
|
+
puts "Where do JavaScript files live in your app? Our best guess is: \e[1m#{@entrypoint}\e[22m 🤔"
|
|
34
|
+
puts "Press enter to accept this, or type a different path."
|
|
35
|
+
print "> "
|
|
36
|
+
input = Rails.env.test? ? "tmp/app/javascript" : $stdin.gets.chomp
|
|
37
|
+
@entrypoint = input unless input.blank?
|
|
38
|
+
@js_channel = "#{@entrypoint}/channels/#{file_name}_channel.js"
|
|
39
|
+
|
|
19
40
|
if using_broadcast_to?
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
41
|
+
if using_stimulus?
|
|
42
|
+
template("#{@entrypoint}/controllers/%file_name%_controller.js")
|
|
43
|
+
Rails.root.join(@js_channel).delete
|
|
44
|
+
else
|
|
45
|
+
gsub_file "app/channels/#{file_name}_channel.rb", /# stream_from.*\n/, "stream_for #{resource}.find(params[:id])\n", verbose: false
|
|
46
|
+
gsub_file @js_channel, /"#{resource}Channel"/, verbose: false do
|
|
47
|
+
<<-JS
|
|
48
|
+
|
|
49
|
+
{
|
|
50
|
+
channel: "#{resource}Channel",
|
|
51
|
+
id: 1
|
|
52
|
+
}
|
|
53
|
+
JS
|
|
54
|
+
end
|
|
55
|
+
doctor_javascript_channel_class
|
|
56
|
+
puts "\nDon't forget to update the id in the channel subscription: #{@js_channel}\nIt's currently set to 1; you'll want to change that to a dynamic value based on something in your DOM."
|
|
28
57
|
end
|
|
29
|
-
|
|
30
|
-
gsub_file "app/channels/#{file_name}_channel.rb", /# stream_from.*\n/, "stream_from \"#{identifier}\"\n"
|
|
58
|
+
else
|
|
59
|
+
gsub_file "app/channels/#{file_name}_channel.rb", /# stream_from.*\n/, "stream_from \"#{identifier}\"\n", verbose: false
|
|
60
|
+
doctor_javascript_channel_class
|
|
31
61
|
end
|
|
32
62
|
end
|
|
33
63
|
|
|
34
64
|
private
|
|
35
65
|
|
|
66
|
+
def doctor_javascript_channel_class
|
|
67
|
+
prepend_to_file @js_channel, "import CableReady from 'cable_ready'\n", verbose: false
|
|
68
|
+
inject_into_file @js_channel, after: "// Called when there's incoming data on the websocket for this channel\n", verbose: false do
|
|
69
|
+
<<-JS
|
|
70
|
+
if (data.cableReady) CableReady.perform(data.operations)
|
|
71
|
+
JS
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
36
75
|
def option_given?
|
|
37
76
|
options.key?(:stream_from) || options.key?(:stream_for)
|
|
38
77
|
end
|
|
@@ -3,16 +3,20 @@
|
|
|
3
3
|
CableReady.configure do |config|
|
|
4
4
|
# Enable/disable exiting / warning when the sanity checks fail options:
|
|
5
5
|
# `:exit` or `:warn` or `:ignore`
|
|
6
|
-
|
|
6
|
+
#
|
|
7
7
|
# config.on_failed_sanity_checks = :exit
|
|
8
8
|
|
|
9
|
-
# Enable/disable
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
# config.
|
|
9
|
+
# Enable/disable assets compilation
|
|
10
|
+
# `true` or `false`
|
|
11
|
+
#
|
|
12
|
+
# config.precompile_assets = true
|
|
13
13
|
|
|
14
14
|
# Define your own custom operations
|
|
15
15
|
# https://cableready.stimulusreflex.com/customization#custom-operations
|
|
16
|
-
|
|
16
|
+
#
|
|
17
17
|
# config.add_operation_name :jazz_hands
|
|
18
|
+
|
|
19
|
+
# Change the default Active Job queue used for broadcast_later and broadcast_later_to
|
|
20
|
+
#
|
|
21
|
+
# config.broadcast_job_queue = :default
|
|
18
22
|
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cable_ready/installer"
|
|
4
|
+
|
|
5
|
+
# verify that Action Cable is installed
|
|
6
|
+
if defined?(ActionCable::Engine)
|
|
7
|
+
say "✅ ActionCable::Engine is loaded and in scope"
|
|
8
|
+
else
|
|
9
|
+
halt "ActionCable::Engine is not loaded, please add or uncomment `require \"action_cable/engine\"` to your `config/application.rb`"
|
|
10
|
+
return
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
return if pack_path_missing?
|
|
14
|
+
|
|
15
|
+
# verify that the Action Cable pubsub config is created
|
|
16
|
+
cable_config = Rails.root.join("config/cable.yml")
|
|
17
|
+
|
|
18
|
+
if cable_config.exist?
|
|
19
|
+
say "✅ config/cable.yml is present"
|
|
20
|
+
else
|
|
21
|
+
inside "config" do
|
|
22
|
+
template "cable.yml"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# verify that the Action Cable pubsub is set to use redis in development
|
|
27
|
+
yaml = YAML.safe_load(cable_config.read)
|
|
28
|
+
app_name = Rails.application.class.module_parent.name.underscore
|
|
29
|
+
|
|
30
|
+
if yaml["development"]["adapter"] == "redis"
|
|
31
|
+
say "✅ config/cable.yml is configured to use the redis adapter in development"
|
|
32
|
+
elsif yaml["development"]["adapter"] == "async"
|
|
33
|
+
yaml["development"] = {
|
|
34
|
+
"adapter" => "redis",
|
|
35
|
+
"url" => "<%= ENV.fetch(\"REDIS_URL\") { \"redis://localhost:6379/1\" } %>",
|
|
36
|
+
"channel_prefix" => "#{app_name}_development"
|
|
37
|
+
}
|
|
38
|
+
backup(cable_config) do
|
|
39
|
+
cable_config.write(yaml.to_yaml)
|
|
40
|
+
end
|
|
41
|
+
say "✅ config/cable.yml was updated to use the redis adapter in development"
|
|
42
|
+
else
|
|
43
|
+
say "🤷 config/cable.yml should use the redis adapter - or something like it - in development. You have something else specified, and we trust that you know what you're doing."
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if Rails::VERSION::MAJOR >= 7
|
|
47
|
+
add_gem "redis@~> 5"
|
|
48
|
+
else
|
|
49
|
+
add_gem "redis@~> 4"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# install action-cable-redis-backport gem if using Action Cable < 7.1
|
|
53
|
+
unless ActionCable::VERSION::MAJOR >= 7 && ActionCable::VERSION::MINOR >= 1
|
|
54
|
+
if !gemfile.match?(/gem ['"]action-cable-redis-backport['"]/)
|
|
55
|
+
add_gem "action-cable-redis-backport@~> 1"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# verify that the Action Cable channels folder and consumer class is available
|
|
60
|
+
step_path = "/app/javascript/channels/"
|
|
61
|
+
channels_path = Rails.root.join(entrypoint, "channels")
|
|
62
|
+
consumer_src = fetch(step_path, "consumer.js.tt")
|
|
63
|
+
consumer_path = channels_path / "consumer.js"
|
|
64
|
+
index_src = fetch(step_path, "index.js.#{bundler}.tt")
|
|
65
|
+
index_path = channels_path / "index.js"
|
|
66
|
+
friendly_index_path = index_path.relative_path_from(Rails.root).to_s
|
|
67
|
+
|
|
68
|
+
empty_directory channels_path unless channels_path.exist?
|
|
69
|
+
|
|
70
|
+
copy_file(consumer_src, consumer_path) unless consumer_path.exist?
|
|
71
|
+
|
|
72
|
+
if index_path.exist?
|
|
73
|
+
if index_path.read == index_src.read
|
|
74
|
+
say "✅ #{friendly_index_path} is present"
|
|
75
|
+
else
|
|
76
|
+
backup(index_path) do
|
|
77
|
+
copy_file(index_src, index_path, verbose: false)
|
|
78
|
+
end
|
|
79
|
+
say "✅ #{friendly_index_path} has been created"
|
|
80
|
+
end
|
|
81
|
+
else
|
|
82
|
+
copy_file(index_src, index_path)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# import Action Cable channels into application pack
|
|
86
|
+
channels_pattern = /import ['"](\.\.\/|\.\/)?channels['"]/
|
|
87
|
+
channels_commented_pattern = /\s*\/\/\s*#{channels_pattern}/
|
|
88
|
+
channel_import = "import \"#{prefix}channels\"\n"
|
|
89
|
+
|
|
90
|
+
if pack.match?(channels_pattern)
|
|
91
|
+
if pack.match?(channels_commented_pattern)
|
|
92
|
+
proceed = if options.key? "uncomment"
|
|
93
|
+
options["uncomment"]
|
|
94
|
+
else
|
|
95
|
+
!no?("✨ Action Cable seems to be commented out in your application.js. Do you want to uncomment it? (Y/n)")
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
if proceed
|
|
99
|
+
# uncomment_lines only works with Ruby comments 🙄
|
|
100
|
+
lines = pack_path.readlines
|
|
101
|
+
matches = lines.select { |line| line =~ channels_commented_pattern }
|
|
102
|
+
lines[lines.index(matches.last).to_i] = channel_import
|
|
103
|
+
pack_path.write lines.join
|
|
104
|
+
say "✅ channels imported in #{friendly_pack_path}"
|
|
105
|
+
else
|
|
106
|
+
say "🤷 your Action Cable channels are not being imported in your application.js. We trust that you have a reason for this."
|
|
107
|
+
end
|
|
108
|
+
else
|
|
109
|
+
say "✅ channels imported in #{friendly_pack_path}"
|
|
110
|
+
end
|
|
111
|
+
else
|
|
112
|
+
lines = pack_path.readlines
|
|
113
|
+
matches = lines.select { |line| line =~ /^import / }
|
|
114
|
+
lines.insert lines.index(matches.last).to_i + 1, channel_import
|
|
115
|
+
pack_path.write lines.join
|
|
116
|
+
say "✅ channels imported in #{friendly_pack_path}"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# create working copy of Action Cable initializer in tmp
|
|
120
|
+
if action_cable_initializer_path.exist?
|
|
121
|
+
FileUtils.cp(action_cable_initializer_path, action_cable_initializer_working_path)
|
|
122
|
+
else
|
|
123
|
+
# create Action Cable initializer if it doesn't already exist
|
|
124
|
+
create_file(action_cable_initializer_working_path, verbose: false) do
|
|
125
|
+
<<~RUBY
|
|
126
|
+
# frozen_string_literal: true
|
|
127
|
+
|
|
128
|
+
RUBY
|
|
129
|
+
end
|
|
130
|
+
say "✅ Action Cable initializer created"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# silence notoriously chatty Action Cable logs
|
|
134
|
+
if !action_cable_initializer_working_path.read.match?(/^[^#]*ActionCable.server.config.logger/)
|
|
135
|
+
append_file(action_cable_initializer_working_path, verbose: false) do
|
|
136
|
+
<<~RUBY
|
|
137
|
+
ActionCable.server.config.logger = Logger.new(nil)
|
|
138
|
+
|
|
139
|
+
RUBY
|
|
140
|
+
end
|
|
141
|
+
say "✅ Action Cable logger silenced for performance and legibility"
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
complete_step :action_cable
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cable_ready/installer"
|
|
4
|
+
|
|
5
|
+
proceed = if options.key? "broadcaster"
|
|
6
|
+
options["broadcaster"]
|
|
7
|
+
else
|
|
8
|
+
!no?("✨ Make CableReady::Broadcaster available to channels, controllers, jobs and models? (Y/n)")
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
unless proceed
|
|
12
|
+
complete_step :broadcaster
|
|
13
|
+
|
|
14
|
+
puts "⏩ Skipping."
|
|
15
|
+
return
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# include CableReady::Broadcaster in Action Cable Channel classes
|
|
19
|
+
channel_path = Rails.root.join("app/channels/application_cable/channel.rb")
|
|
20
|
+
if channel_path.exist?
|
|
21
|
+
lines = channel_path.readlines
|
|
22
|
+
if !lines.index { |line| line =~ /^\s*include CableReady::Broadcaster/ }
|
|
23
|
+
backup(channel_path) do
|
|
24
|
+
index = lines.index { |line| line.include?("class Channel < ActionCable::Channel::Base") }
|
|
25
|
+
lines.insert index + 1, " include CableReady::Broadcaster\n"
|
|
26
|
+
channel_path.write lines.join
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
puts "✅ include CableReady::Broadcaster in Action Cable channels"
|
|
30
|
+
else
|
|
31
|
+
puts "⏩ already included CableReady::Broadcaster in Action Cable channels. Skipping"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# include CableReady::Broadcaster in Action Controller classes
|
|
36
|
+
controller_path = Rails.root.join("app/controllers/application_controller.rb")
|
|
37
|
+
if controller_path.exist?
|
|
38
|
+
lines = controller_path.readlines
|
|
39
|
+
if !lines.index { |line| line =~ /^\s*include CableReady::Broadcaster/ }
|
|
40
|
+
backup(controller_path) do
|
|
41
|
+
index = lines.index { |line| line.include?("class ApplicationController < ActionController::Base") }
|
|
42
|
+
lines.insert index + 1, " include CableReady::Broadcaster\n"
|
|
43
|
+
controller_path.write lines.join
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
puts "✅ include CableReady::Broadcaster in Action Controller classes"
|
|
47
|
+
else
|
|
48
|
+
puts "⏩ already included CableReady::Broadcaster in Action Controller classes. Skipping"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# include CableReady::Broadcaster in Active Job classes, if present
|
|
53
|
+
if defined?(ActiveJob)
|
|
54
|
+
job_path = Rails.root.join("app/jobs/application_job.rb")
|
|
55
|
+
if job_path.exist?
|
|
56
|
+
lines = job_path.readlines
|
|
57
|
+
if !lines.index { |line| line =~ /^\s*include CableReady::Broadcaster/ }
|
|
58
|
+
backup(job_path) do
|
|
59
|
+
index = lines.index { |line| line.include?("class ApplicationJob < ActiveJob::Base") }
|
|
60
|
+
lines.insert index + 1, " include CableReady::Broadcaster\n"
|
|
61
|
+
job_path.write lines.join
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
puts "✅ include CableReady::Broadcaster in Active Job classes"
|
|
65
|
+
else
|
|
66
|
+
puts "⏩ already included CableReady::Broadcaster in Active Job classes. Skipping"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
else
|
|
70
|
+
puts "⏩ Active Job not available. Skipping."
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# include CableReady::Broadcaster in StateMachines, if present
|
|
74
|
+
if defined?(StateMachines)
|
|
75
|
+
lines = action_cable_initializer_working_path.read
|
|
76
|
+
if !lines.include?("StateMachines::Machine.prepend(CableReady::Broadcaster)")
|
|
77
|
+
inject_into_file action_cable_initializer_working_path, after: "CableReady.configure do |config|\n", verbose: false do
|
|
78
|
+
<<-RUBY
|
|
79
|
+
|
|
80
|
+
StateMachines::Machine.prepend(CableReady::Broadcaster)
|
|
81
|
+
|
|
82
|
+
RUBY
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
puts "✅ prepend CableReady::Broadcaster into StateMachines::Machine"
|
|
86
|
+
else
|
|
87
|
+
puts "⏩ already prepended CableReady::Broadcaster into StateMachines::Machine. Skipping"
|
|
88
|
+
end
|
|
89
|
+
else
|
|
90
|
+
puts "⏩ StateMachines not available. Skipping."
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# include CableReady::Broadcaster in Active Record model classes
|
|
94
|
+
if Rails.root.join(application_record_path).exist?
|
|
95
|
+
lines = application_record_path.readlines
|
|
96
|
+
if !lines.index { |line| line =~ /^\s*include CableReady::Broadcaster/ }
|
|
97
|
+
backup(application_record_path) do
|
|
98
|
+
index = lines.index { |line| line.include?("class ApplicationRecord < ActiveRecord::Base") }
|
|
99
|
+
lines.insert index + 1, " include CableReady::Broadcaster\n"
|
|
100
|
+
application_record_path.write lines.join
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
puts "✅ include CableReady::Broadcaster in Active Record model classes"
|
|
104
|
+
else
|
|
105
|
+
puts "⏩ already included CableReady::Broadcaster in Active Record model classes. Skipping"
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
complete_step :broadcaster
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cable_ready/installer"
|
|
4
|
+
|
|
5
|
+
hash = gemfile_hash
|
|
6
|
+
|
|
7
|
+
# run bundle only when gems are waiting to be added or removed
|
|
8
|
+
add = add_gem_list.exist? ? add_gem_list.readlines.map(&:chomp) : []
|
|
9
|
+
remove = remove_gem_list.exist? ? remove_gem_list.readlines.map(&:chomp) : []
|
|
10
|
+
|
|
11
|
+
if add.present? || remove.present?
|
|
12
|
+
lines = gemfile_path.readlines
|
|
13
|
+
|
|
14
|
+
remove.each do |name|
|
|
15
|
+
index = lines.index { |line| line =~ /gem ['"]#{name}['"]/ }
|
|
16
|
+
if index
|
|
17
|
+
if /^[^#]*gem ['"]#{name}['"]/.match?(lines[index])
|
|
18
|
+
lines[index] = "# #{lines[index]}"
|
|
19
|
+
say "✅ #{name} gem has been disabled"
|
|
20
|
+
else
|
|
21
|
+
say "⏩ #{name} gem is already disabled. Skipping."
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
add.each do |package|
|
|
27
|
+
matches = package.match(/(.+)@(.+)/)
|
|
28
|
+
name, version = matches[1], matches[2]
|
|
29
|
+
|
|
30
|
+
index = lines.index { |line| line =~ /gem ['"]#{name}['"]/ }
|
|
31
|
+
if index
|
|
32
|
+
if !lines[index].match(/^[^#]*gem ['"]#{name}['"].*#{version}['"]/)
|
|
33
|
+
lines[index] = "\ngem \"#{name}\", \"#{version}\"\n"
|
|
34
|
+
say "✅ #{name} gem has been installed"
|
|
35
|
+
else
|
|
36
|
+
say "⏩ #{name} gem is already installed. Skipping."
|
|
37
|
+
end
|
|
38
|
+
else
|
|
39
|
+
lines << "\ngem \"#{name}\", \"#{version}\"\n"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
gemfile_path.write lines.join
|
|
44
|
+
|
|
45
|
+
bundle_command("install --quiet", "BUNDLE_IGNORE_MESSAGES" => "1") if hash != gemfile_hash
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
FileUtils.cp(development_working_path, development_path)
|
|
49
|
+
say "✅ development environment configuration installed"
|
|
50
|
+
|
|
51
|
+
FileUtils.cp(action_cable_initializer_working_path, action_cable_initializer_path)
|
|
52
|
+
say "✅ Action Cable initializer installed"
|
|
53
|
+
|
|
54
|
+
complete_step :bundle
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cable_ready/installer"
|
|
4
|
+
|
|
5
|
+
initializer = action_cable_initializer_working_path.read
|
|
6
|
+
|
|
7
|
+
proceed = false
|
|
8
|
+
|
|
9
|
+
if initializer.exclude? "PermessageDeflate.configure"
|
|
10
|
+
proceed = if options.key? "compression"
|
|
11
|
+
options["compression"]
|
|
12
|
+
else
|
|
13
|
+
!no?("✨ Configure Action Cable to compress your WebSocket traffic with gzip? (Y/n)")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
if proceed
|
|
18
|
+
if !gemfile.match?(/gem ['"]permessage_deflate['"]/)
|
|
19
|
+
add_gem "permessage_deflate@>= 0.1"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# add permessage_deflate config to Action Cable initializer
|
|
23
|
+
if initializer.exclude? "PermessageDeflate.configure"
|
|
24
|
+
create_or_append(action_cable_initializer_working_path, verbose: false) do
|
|
25
|
+
<<~RUBY
|
|
26
|
+
module ActionCable
|
|
27
|
+
module Connection
|
|
28
|
+
class ClientSocket
|
|
29
|
+
alias_method :old_initialize, :initialize
|
|
30
|
+
def initialize(env, event_target, event_loop, protocols)
|
|
31
|
+
old_initialize(env, event_target, event_loop, protocols)
|
|
32
|
+
@driver.add_extension(
|
|
33
|
+
PermessageDeflate.configure(
|
|
34
|
+
level: Zlib::BEST_COMPRESSION,
|
|
35
|
+
max_window_bits: 13
|
|
36
|
+
)
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
RUBY
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
say "✅ Action Cable initializer patched to deflate WS traffic"
|
|
46
|
+
else
|
|
47
|
+
say "⏩ Action Cable initializer is already patched to deflate WS traffic. Skipping."
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
complete_step :compression
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cable_ready/installer"
|
|
4
|
+
|
|
5
|
+
return if pack_path_missing?
|
|
6
|
+
|
|
7
|
+
step_path = "/app/javascript/config/"
|
|
8
|
+
index_src = fetch(step_path, "index.js.tt")
|
|
9
|
+
index_path = config_path / "index.js"
|
|
10
|
+
cable_ready_src = fetch(step_path, "cable_ready.js.tt")
|
|
11
|
+
cable_ready_path = config_path / "cable_ready.js"
|
|
12
|
+
|
|
13
|
+
empty_directory config_path unless config_path.exist?
|
|
14
|
+
|
|
15
|
+
copy_file(index_src, index_path) unless index_path.exist?
|
|
16
|
+
|
|
17
|
+
index_pattern = /import ['"](\.\.\/|\.\/)?config['"]/
|
|
18
|
+
index_commented_pattern = /\s*\/\/\s*#{index_pattern}/
|
|
19
|
+
index_import = "import \"#{prefix}config\"\n"
|
|
20
|
+
|
|
21
|
+
if pack.match?(index_pattern)
|
|
22
|
+
if pack.match?(index_commented_pattern)
|
|
23
|
+
lines = pack_path.readlines
|
|
24
|
+
matches = lines.select { |line| line =~ index_commented_pattern }
|
|
25
|
+
lines[lines.index(matches.last).to_i] = index_import
|
|
26
|
+
pack_path.write lines.join
|
|
27
|
+
end
|
|
28
|
+
else
|
|
29
|
+
lines = pack_path.readlines
|
|
30
|
+
matches = lines.select { |line| line =~ /^import / }
|
|
31
|
+
lines.insert lines.index(matches.last).to_i + 1, index_import
|
|
32
|
+
pack_path.write lines.join
|
|
33
|
+
end
|
|
34
|
+
say "✅ CableReady configs will be imported in #{friendly_pack_path}"
|
|
35
|
+
|
|
36
|
+
# create entrypoint/config/cable_ready.js and make sure it's imported in application.js
|
|
37
|
+
copy_file(cable_ready_src, cable_ready_path) unless cable_ready_path.exist?
|
|
38
|
+
|
|
39
|
+
complete_step :config
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "cable_ready/installer"
|
|
4
|
+
|
|
5
|
+
# mutate working copy of development.rb to avoid bundle alerts
|
|
6
|
+
FileUtils.cp(development_path, development_working_path)
|
|
7
|
+
|
|
8
|
+
# add default_url_options to development.rb for Action Mailer
|
|
9
|
+
if defined?(ActionMailer)
|
|
10
|
+
lines = development_working_path.readlines
|
|
11
|
+
if lines.find { |line| line.include?("config.action_mailer.default_url_options") }
|
|
12
|
+
say "⏩ Action Mailer default_url_options already defined. Skipping."
|
|
13
|
+
else
|
|
14
|
+
index = lines.index { |line| line =~ /^Rails.application.configure do/ }
|
|
15
|
+
lines.insert index + 1, " config.action_mailer.default_url_options = {host: \"localhost\", port: 3000}\n\n"
|
|
16
|
+
development_working_path.write lines.join
|
|
17
|
+
|
|
18
|
+
say "✅ Action Mailer default_url_options defined"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# add default_url_options to development.rb for Action Controller
|
|
23
|
+
lines = development_working_path.readlines
|
|
24
|
+
if lines.find { |line| line.include?("config.action_controller.default_url_options") }
|
|
25
|
+
say "⏩ Action Controller default_url_options already defined. Skipping."
|
|
26
|
+
else
|
|
27
|
+
index = lines.index { |line| line =~ /^Rails.application.configure do/ }
|
|
28
|
+
lines.insert index + 1, " config.action_controller.default_url_options = {host: \"localhost\", port: 3000}\n"
|
|
29
|
+
development_working_path.write lines.join
|
|
30
|
+
|
|
31
|
+
say "✅ Action Controller default_url_options defined"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
complete_step :development
|