Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 0 additions & 17 deletions lib/generators/ruby_ui/base_generator.rb

This file was deleted.

141 changes: 41 additions & 100 deletions lib/generators/ruby_ui/component_generator.rb
Original file line number Diff line number Diff line change
@@ -1,136 +1,77 @@
module RubyUI
module Generators
class ComponentGenerator < RubyUI::Generators::BaseGenerator
class ComponentGenerator < Rails::Generators::Base
namespace "ruby_ui:component"

source_root File.expand_path("../../..", __dir__)
source_root File.expand_path("../../ruby_ui", __dir__)
argument :component_name, type: :string, required: true

def copy_component
return puts "This generator can only be run in a Rails environment." unless defined?(Rails::Generators::Base)
def generate_component
if component_not_found?
say "Component not found: #{component_name}", :red
exit
end

copy_common_files
copy_component_files
say "Generating component files"
end

private
def copy_main_component_file
say "Generating main component"

def copy_common_files
template "#{template_dir}/index.js", "#{destination_path}/index.js" unless File.exist?("#{destination_path}/index.js")
copy_file File.join(source_path, "base.rb"), File.join(destination_path, "base.rb")
copy_file File.join(component_folder_path, "#{component_folder_name}.rb"),
Rails.root.join("app/components/ruby_ui", "#{component_folder_name}.rb")
end

def copy_component_files
puts "Component #{component} not found in ruby_ui gem" if component_source.empty?
def copy_subcomponents_files
return if subcomponent_file_paths.empty?

component_files = Dir.glob("#{component_source}/*")
say "Generating subcomponents"

component_files.each do |file|
copy_file file, File.join(destination_path, component, File.basename(file))
subcomponent_file_paths.each do |file_path|
component_file_name = file_path.split("/").last
copy_file file_path, Rails.root.join("app/components/ruby_ui", component_folder_name, component_file_name)
end
update_index_file
end

def update_index_file
index_path = File.join(destination_root, "app/components/ruby_ui/index.js")

ruby_ui_index_content = File.read(index_path)

updated_ruby_ui_index_content = add_controller_registration(ruby_ui_index_content)

File.write(index_path, updated_ruby_ui_index_content)
end

def add_controller_registration(ruby_ui_index_content)
valid_controllers = get_valid_controllers
def copy_js_files
return if js_controller_file_paths.empty?

ruby_ui_index_content = update_imports(ruby_ui_index_content, valid_controllers)
update_registrations(ruby_ui_index_content, valid_controllers)
# Uncomment the following line if you want to update exports
# ruby_ui_index_content = update_exports(ruby_ui_index_content, valid_controllers)
end

def get_valid_controllers
all_js_controllers = Dir.glob(File.join(destination_path, "**", "*_controller.js"))
say "Generating Stimulus controllers"

all_js_controllers.map do |controller_file|
controller_info(controller_file)
js_controller_file_paths.each do |file_path|
component_file_name = file_path.split("/").last
copy_file file_path, Rails.root.join("app/javascript/controllers/ruby_ui", component_file_name)
end
end

def controller_info(controller_file)
# Get the relative path from the destination path to the controller file
relative_path = Pathname.new(controller_file).relative_path_from(Pathname.new(destination_path))

# Extract the file name without the .js extension
file_name = relative_path.basename(".js").to_s

# Remove '_controller' suffix to get the component name
component_name = file_name.sub(/_controller$/, "")

# Create the new controller name by camelizing the component name and adding 'Controller'
new_controller = "#{component_name.camelize}Controller"

# Build the new import path
new_import_path = new_import_path("./#{relative_path.dirname}/#{file_name}")

# Create the registration name by dasherizing the component name and prefixing with 'ruby-ui--'
registration_name = "ruby-ui--#{component_name.dasherize}"

# Return a hash with import, registration, and export statements
{
# Import statement for importmaps
import: "import #{new_controller} from \"#{new_import_path}\";",

# Registration statement for the Stimulus controller
registration: "application.register(\"#{registration_name}\", #{new_controller});",

# Export statement for the controller
export: "export { default as #{new_controller} } from \"#{new_import_path}\";"
}
say "Updating Stimulus controllers manifest"
run "rake stimulus:manifest:update"
end

def new_import_path(relative_path)
if using_importmap?
"ruby_ui/#{relative_path.sub(/^\.\//, "")}"
else
relative_path
def copy_dependencies
case component_folder_name
when "masked_input"
copy_masked_input_dependencies
end
end

def update_imports(content, controllers)
imports = controllers.map { |c| c[:import] }.sort.join("\n")
content.sub(/\/\/ Import all controller files.*?(?=\n\n)/m, "// Import all controller files\n#{imports}")
end
private

def update_registrations(content, controllers)
registrations = controllers.map { |c| c[:registration] }.sort.join("\n")
content.sub(/\/\/ Register all controllers.*?(?=\n\n)/m, "// Register all controllers\n#{registrations}")
end
def component_not_found? = !Dir.exist?(component_folder_path)

def update_exports(content, controllers)
exports = controllers.map { |c| c[:export] }.sort.join("\n")
content.sub(/\/\/ Export all controllers.*?(?=\n\n)/m, "// Export all controllers so user of npm package can lazy load controllers\n#{exports}")
end
def component_folder_name = component_name.underscore

def component
@component ||= component_name.downcase
end
def component_folder_path = File.join(self.class.source_root, component_folder_name)

def source_path
@source_path ||= "lib/ruby_ui"
end
def main_component_file_path = File.join(component_folder_path, "#{component_folder_name}.rb")

def destination_path
@destination_path ||= "app/components/ruby_ui"
end
def subcomponent_file_paths = Dir.glob(File.join(component_folder_path, "*.rb")) - [main_component_file_path]

def component_source
@component_source ||= File.join(self.class.source_root, source_path, component)
end
def js_controller_file_paths = Dir.glob(File.join(component_folder_path, "*.js"))

def copy_masked_input_dependencies
say "Generating masked input dependencies"

def template_dir
@template_dir ||= File.join(__dir__, "templates")
run "bin/rails generate ruby_ui:component Input"
end
end
end
Expand Down