Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CableReady 5.0 installer #233

Merged
merged 53 commits into from
Feb 13, 2023
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
80953d9
new installer
leastbad Dec 27, 2022
bc21332
neutered channel generator tests
leastbad Dec 27, 2022
abdf012
mrujs not installed warning
leastbad Dec 27, 2022
497be6f
bump bundler to 2.4.1
leastbad Dec 28, 2022
d7cb7be
add copy to example page
leastbad Dec 28, 2022
c64dd7a
backup method needs to return early if file doesn't exist
leastbad Dec 28, 2022
5237f4c
update Gemfile.lock
leastbad Dec 28, 2022
d615494
make the standard gods happy
leastbad Dec 28, 2022
73db49e
text tweaks
leastbad Jan 2, 2023
5758b97
updates_for live example formatting
leastbad Jan 3, 2023
1ef4394
removed superfluous importmap include
leastbad Jan 3, 2023
7966a34
bump gems
leastbad Jan 3, 2023
ddd8271
lock standardrb to 1.19.1
leastbad Jan 4, 2023
7d6aca1
add option to change broadcast_later job queue to initializer
leastbad Jan 4, 2023
13b9169
standardrb
leastbad Jan 4, 2023
5a53779
resolve conflicts with master
leastbad Jan 4, 2023
b55596f
Gemfile lock fun
leastbad Jan 4, 2023
ba9c954
update Ruby version test matrix
leastbad Jan 4, 2023
23a74a0
must require version
leastbad Jan 4, 2023
aec081a
Merge branch 'master' into new_installer
julianrubisch Feb 10, 2023
2c5f1f9
Restore importmaps config
marcoroth Feb 10, 2023
36cea6e
Simplify installer
marcoroth Feb 10, 2023
7784d27
standardize
marcoroth Feb 10, 2023
987d16b
consider everything local
marcoroth Feb 10, 2023
4f548b2
no more icon
marcoroth Feb 10, 2023
fdac7df
standardize
marcoroth Feb 12, 2023
55df2c6
Merge branch 'master' into new_installer
marcoroth Feb 12, 2023
d7d69f3
fix setup links to docs
marcoroth Feb 12, 2023
0f49ea1
remove timeout options
marcoroth Feb 12, 2023
eb710b3
fix more links and remove example from footguns
marcoroth Feb 12, 2023
de65084
copy, dead links, enforce to_s in install:step
marcoroth Feb 12, 2023
9d3414c
fix CableReady importmap path
marcoroth Feb 12, 2023
41a7357
Imrpove log output messages
marcoroth Feb 13, 2023
4aba492
standardize
marcoroth Feb 13, 2023
419e527
fix: Doc links on example page
julianrubisch Feb 13, 2023
ffac2bf
chore: Remove turbo_stream format from example controller
julianrubisch Feb 13, 2023
3132ca2
chore: Add usage output for installer steps
julianrubisch Feb 13, 2023
bdd11df
`"required"` -> `"loaded"`
marcoroth Feb 13, 2023
715eaed
sort CR_STEPS in description and update copy
marcoroth Feb 13, 2023
5daf205
marcoroth Feb 13, 2023
73e1021
both `broadcaster` and `spring` steps should be skipable
marcoroth Feb 13, 2023
4993c84
chore: Run bundle and yarn after each singular executed step
julianrubisch Feb 13, 2023
31e0118
Merge branch 'new_installer' of github.com:stimulusreflex/cable_ready…
julianrubisch Feb 13, 2023
3b01cae
extract `updatable` into own installer step
marcoroth Feb 13, 2023
e3aa2ef
more copy improvements
marcoroth Feb 13, 2023
99c4457
chore: Skip yarn if no package.json is present
julianrubisch Feb 13, 2023
c7cdfa2
Merge branch 'new_installer' of github.com:stimulusreflex/cable_ready…
julianrubisch Feb 13, 2023
8a46613
Remove example
marcoroth Feb 13, 2023
205700d
standard
marcoroth Feb 13, 2023
1fbb6f5
more readable emoijs
marcoroth Feb 13, 2023
f0da91d
importmaps improvements
marcoroth Feb 13, 2023
aca7ee9
chore: Rename footgun => bundler
julianrubisch Feb 13, 2023
ab5b096
Lock `morphdom` to `2.6.1`
marcoroth Feb 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ GEM
nokogiri (1.14.0-x86_64-linux)
racc (~> 1.4)
parallel (1.22.1)
parser (3.1.3.0)
parser (3.2.0.0)
ast (~> 2.4.1)
pry (0.14.1)
coderay (~> 1.1)
Expand Down Expand Up @@ -191,7 +191,7 @@ GEM
timeout (0.3.1)
tzinfo (2.0.5)
concurrent-ruby (~> 1.0)
unicode-display_width (2.3.0)
unicode-display_width (2.4.1)
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
Expand Down
17 changes: 9 additions & 8 deletions app/jobs/cable_ready_broadcast_job.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# frozen_string_literal: true

class CableReadyBroadcastJob < (defined?(ActiveJob::Base) ? ActiveJob::Base : Object)
include CableReady::Broadcaster
queue_as :default if defined?(ActiveJob::Base)
if defined?(ActiveJob::Base)
class CableReadyBroadcastJob < ActiveJob::Base
include CableReady::Broadcaster

def perform(identifier:, operations:, model: nil)
if model.present?
cable_ready[identifier.safe_constantize].apply!(operations).broadcast_to(model)
else
cable_ready[identifier].apply!(operations).broadcast
def perform(identifier:, operations:, model: nil)
if model.present?
cable_ready[identifier.safe_constantize].apply!(operations).broadcast_to(model)
else
cable_ready[identifier].apply!(operations).broadcast
end
end
end
end
12 changes: 8 additions & 4 deletions lib/cable_ready/channel.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,19 @@ def broadcast_to(model, clear: true)
clients_received
end

def broadcast_later(clear: true)
def broadcast_later(clear: true, queue: nil)
raise("Action Cable must be enabled to use broadcast_later") unless defined?(ActionCable)
CableReadyBroadcastJob.perform_later(identifier: identifier, operations: operations_payload)
CableReadyBroadcastJob
.set(queue: queue ? queue.to_sym : CableReady.config.broadcast_job_queue)
.perform_later(identifier: identifier, operations: operations_payload)
reset! if clear
end

def broadcast_later_to(model, clear: true)
def broadcast_later_to(model, clear: true, queue: nil)
raise("Action Cable must be enabled to use broadcast_later_to") unless defined?(ActionCable)
CableReadyBroadcastJob.perform_later(identifier: identifier.name, operations: operations_payload, model: model)
CableReadyBroadcastJob
.set(queue: queue ? queue.to_sym : CableReady.config.broadcast_job_queue)
.perform_later(identifier: identifier.name, operations: operations_payload, model: model)
reset! if clear
end
end
Expand Down
3 changes: 2 additions & 1 deletion lib/cable_ready/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,15 @@ class Config
include Observable
include Singleton

attr_accessor :on_failed_sanity_checks, :on_new_version_available, :precompile_assets
attr_accessor :on_failed_sanity_checks, :on_new_version_available, :broadcast_job_queue, :precompile_assets
attr_writer :verifier_key

def initialize
super
@operation_names = Set.new(default_operation_names)
@on_failed_sanity_checks = :exit
@on_new_version_available = :ignore
@broadcast_job_queue = :default
@precompile_assets = true
end

Expand Down
224 changes: 224 additions & 0 deletions lib/cable_ready/installer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
# frozen_string_literal: true

require "cable_ready/version"

### general utilities

def fetch(step_path, file)
relative_path = step_path + file
location = template_src + relative_path

Pathname.new(location)
end

def complete_step(step)
create_file "tmp/cable_ready_installer/#{step}", verbose: false
end

def create_or_append(path, *args, &block)
FileUtils.touch(path)
append_file(path, *args, &block)
end

def current_template
ENV["LOCATION"].split("/").last.gsub(".rb", "")
end

def pack_path_missing?
return false unless pack_path.nil?
halt "#{friendly_pack_path} is missing. You need a valid application pack file to proceed."
end

def halt(message)
say "❌ #{message}", :red
create_file "tmp/cable_ready_installer/halt", verbose: false
end

def backup(path, delete: false)
if !path.exist?
yield
return
end

backup_path = Pathname.new("#{path}.bak")
old_path = path.relative_path_from(Rails.root).to_s
filename = path.to_path.split("/").last

if backup_path.exist?
if backup_path.read == path.read
path.delete if delete
yield
return
end
backup_path.delete
end

copy_file(path, backup_path, verbose: false)
path.delete if delete

yield

if path.read != backup_path.read
create_or_append(backups_path, "#{old_path}\n", verbose: false)
end
say "📦 #{old_path} backed up as #{filename}.bak"
end

def add_gem(name)
create_or_append(add_gem_list, "#{name}\n", verbose: false)
say "➕ Added #{name} to the Gemfile"
end

def remove_gem(name)
create_or_append(remove_gem_list, "#{name}\n", verbose: false)
say "➖ Removed #{name} from Gemfile"
end

def add_package(name)
create_or_append(package_list, "#{name}\n", verbose: false)
say "➕ Enqueued #{name} to be added to dependencies"
end

def add_dev_package(name)
create_or_append(dev_package_list, "#{name}\n", verbose: false)
say "➕ Enqueued #{name} to be added to dev dependencies"
end

def drop_package(name)
create_or_append(drop_package_list, "#{name}\n", verbose: false)
say "➖ Enqueued #{name} to be removed from dependencies"
end

def gemfile_hash
Digest::MD5.hexdigest(gemfile_path.read)
end

### memoized values

def cr_npm_version
@cr_npm_version ||= CableReady::VERSION.gsub(".pre", "-pre")
end

def package_json
@package_json ||= Rails.root.join("package.json")
end

def entrypoint
@entrypoint ||= File.read("tmp/cable_ready_installer/entrypoint")
end

def footgun
@footgun ||= File.read("tmp/cable_ready_installer/footgun")
end

def config_path
@config_path ||= Rails.root.join(entrypoint, "config")
end

def importmap_path
@importmap_path ||= Rails.root.join("config/importmap.rb")
end

def friendly_importmap_path
@friendly_importmap_path ||= importmap_path.relative_path_from(Rails.root).to_s
end

def pack
@pack ||= pack_path.read
end

def friendly_pack_path
@friendly_pack_path ||= pack_path.relative_path_from(Rails.root).to_s
end

def pack_path
@pack_path ||= [
Rails.root.join(entrypoint, "application.js"),
Rails.root.join(entrypoint, "packs/application.js"),
Rails.root.join(entrypoint, "entrypoints/application.js")
].find(&:exist?)
end

def package_list
@package_list ||= Rails.root.join("tmp/cable_ready_installer/npm_package_list")
end

def dev_package_list
@dev_package_list ||= Rails.root.join("tmp/cable_ready_installer/npm_dev_package_list")
end

def drop_package_list
@drop_package_list ||= Rails.root.join("tmp/cable_ready_installer/drop_npm_package_list")
end

def template_src
@template_src ||= File.read("tmp/cable_ready_installer/template_src")
end

def controllers_path
@controllers_path ||= Rails.root.join(entrypoint, "controllers")
end

def gemfile_path
@gemfile_path ||= Rails.root.join("Gemfile")
end

def gemfile
@gemfile ||= gemfile_path.read
end

def prefix
# standard:disable Style/RedundantStringEscape
@prefix ||= {
"vite" => "..\/",
"webpacker" => "",
"shakapacker" => "",
"importmap" => "",
"esbuild" => ".\/"
}[footgun]
# standard:enable Style/RedundantStringEscape
end

def application_record_path
@application_record_path ||= Rails.root.join("app/models/application_record.rb")
end

def action_cable_initializer_path
@action_cable_initializer_path ||= Rails.root.join("config/initializers/action_cable.rb")
end

def action_cable_initializer_working_path
@action_cable_initializer_working_path ||= Rails.root.join(working, "action_cable.rb")
end

def development_path
@development_path ||= Rails.root.join("config/environments/development.rb")
end

def development_working_path
@development_working_path ||= Rails.root.join(working, "development.rb")
end

def backups_path
@backups_path ||= Rails.root.join("tmp/cable_ready_installer/backups")
end

def add_gem_list
@add_gem_list ||= Rails.root.join("tmp/cable_ready_installer/add_gem_list")
end

def remove_gem_list
@remove_gem_list ||= Rails.root.join("tmp/cable_ready_installer/remove_gem_list")
end

def options_path
@options_path ||= Rails.root.join("tmp/cable_ready_installer/options")
end

def options
@options ||= YAML.safe_load(File.read(options_path))
end

def working
@working ||= Rails.root.join("tmp/cable_ready_installer/working")
end
File renamed without changes.
63 changes: 51 additions & 12 deletions lib/generators/cable_ready/channel_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,71 @@ class CableReady::ChannelGenerator < Rails::Generators::NamedBase
class_option :stream_for, type: :string
class_option :stimulus, type: :boolean

def destroy_not_supported
if behavior == :revoke
puts "Sorry, we don't support destroying generated channels.\nDelete the Action Cable channel class, as well as any corresponding JavaScript classes."
exit
end
end

def check_options
raise "Can't specify --stream-from and --stream-for at the same time" if options.key?(:stream_from) && options.key?(:stream_for)
if options.key?(:stream_from) && options.key?(:stream_for)
puts "Can't specify --stream-from and --stream-for at the same time"
exit
end
end

def create_channel
generate "channel", file_name
generate "channel", file_name, "--skip"
end

def enhance_channels
@entrypoint = [
"app/javascript",
"app/frontend"
].find { |path| File.exist?(Rails.root.join(path)) } || "app/javascript"
puts "Where do JavaScript files live in your app? Our best guess is: \e[1m#{@entrypoint}\e[22m 🤔"
puts "Press enter to accept this, or type a different path."
print "> "
input = Rails.env.test? ? "tmp/app/javascript" : $stdin.gets.chomp
@entrypoint = input unless input.blank?
@js_channel = "#{@entrypoint}/channels/#{file_name}_channel.js"

if using_broadcast_to?
gsub_file "app/channels/#{file_name}_channel.rb", /# stream_from.*\n/, "stream_for #{resource}.find(params[:id])\n"
template "app/javascript/controllers/%file_name%_controller.js" if using_stimulus?
else
prepend_to_file "app/javascript/channels/#{file_name}_channel.js", "import CableReady from 'cable_ready'\n"
inject_into_file "app/javascript/channels/#{file_name}_channel.js", after: "// Called when there's incoming data on the websocket for this channel\n" do
<<-JS
if (data.cableReady) CableReady.perform(data.operations)
JS
if using_stimulus?
template("#{@entrypoint}/controllers/%file_name%_controller.js")
Rails.root.join(@js_channel).delete
else
gsub_file "app/channels/#{file_name}_channel.rb", /# stream_from.*\n/, "stream_for #{resource}.find(params[:id])\n", verbose: false
gsub_file @js_channel, /"#{resource}Channel"/, verbose: false do
<<-JS

{
channel: "#{resource}Channel",
id: 1
}
JS
end
doctor_javascript_channel_class
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."
end

gsub_file "app/channels/#{file_name}_channel.rb", /# stream_from.*\n/, "stream_from \"#{identifier}\"\n"
else
gsub_file "app/channels/#{file_name}_channel.rb", /# stream_from.*\n/, "stream_from \"#{identifier}\"\n", verbose: false
doctor_javascript_channel_class
end
end

private

def doctor_javascript_channel_class
prepend_to_file @js_channel, "import CableReady from 'cable_ready'\n", verbose: false
inject_into_file @js_channel, after: "// Called when there's incoming data on the websocket for this channel\n", verbose: false do
<<-JS
if (data.cableReady) CableReady.perform(data.operations)
JS
end
end

def option_given?
options.key?(:stream_from) || options.key?(:stream_for)
end
Expand Down
Loading