Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
6 changes: 6 additions & 0 deletions gem/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ You can generate your components using `ruby_ui:component` generator.
bin/rails g ruby_ui:component Accordion
```

You can also generate multiple components at once.

```bash
bin/rails g ruby_ui:component Button Link Input Textarea
```

You also can generate all components using `ruby_ui:component:all` generator

## Documentation 📖
Expand Down
6 changes: 2 additions & 4 deletions gem/lib/generators/ruby_ui/component/all_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,9 @@ class AllGenerator < Rails::Generators::Base
def generate_components
say "Generating all components..."

Dir.children(self.class.source_root).each do |folder_name|
next if folder_name.ends_with?(".rb")
folder_names = Dir.children(self.class.source_root).reject { |folder_name| folder_name.ends_with?(".rb") }
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated

run "bin/rails generate ruby_ui:component #{folder_name} --force #{options["force"]}"
end
run "bin/rails generate ruby_ui:component #{folder_names.join(" ")} --force #{options["force"]}"
end
end
end
Expand Down
82 changes: 51 additions & 31 deletions gem/lib/generators/ruby_ui/component_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,69 +7,89 @@ class ComponentGenerator < Rails::Generators::Base
namespace "ruby_ui:component"

source_root File.expand_path("../../ruby_ui", __dir__)
argument :component_name, type: :string, required: true
argument :component_names, type: :array, required: true, banner: "Button Link Input"
class_option :force, type: :boolean, default: false
class_option :with_docs, type: :boolean, default: false

def generate_component
if component_not_found?
say "Component not found: #{component_name}", :red
exit
def generate_components
validate_components!

component_names.each do |component_name|
say "Generating #{component_name} files..."
copy_related_component_files(component_name)
copy_js_files(component_name)
install_dependencies(component_name)
end

say "Generating #{component_name} files..."
update_stimulus_manifest
end

def copy_related_component_files
private

def validate_components!
missing = component_names.reject { |name| component_exists?(name) }
return if missing.empty?

say "Component(s) not found: #{missing.join(", ")}", :red
exit
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
end

def copy_related_component_files(component_name)
say "Generating components"

components_file_paths.each do |file_path|
components_file_paths(component_name).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), force: options["force"]
copy_file file_path, Rails.root.join("app/components/ruby_ui", component_folder_name(component_name), component_file_name), force: options["force"]
end
end

def copy_js_files
return if js_controller_file_paths.empty?
def copy_js_files(component_name)
paths = js_controller_file_paths(component_name)
return if paths.empty?

say "Generating Stimulus controllers"

js_controller_file_paths.each do |file_path|
paths.each do |file_path|
controller_file_name = file_path.split("/").last
copy_file file_path, Rails.root.join("app/javascript/controllers/ruby_ui", controller_file_name), force: options["force"]
end

@stimulus_controllers_added = true
end

def update_stimulus_manifest
return unless @stimulus_controllers_added

# Importmap doesn't have controller manifest, instead it uses `eagerLoadControllersFrom("controllers", application)`
if !using_importmap?
say "Updating Stimulus controllers manifest"
run "rake stimulus:manifest:update"
end
return if using_importmap?

say "Updating Stimulus controllers manifest"
run "rake stimulus:manifest:update"
end

def install_dependencies
return if dependencies.blank?
def install_dependencies(component_name)
deps = dependencies(component_name)
return if deps.blank?

say "Installing dependencies"

install_components_dependencies(dependencies["components"])
install_gems_dependencies(dependencies["gems"])
install_js_packages(dependencies["js_packages"])
install_components_dependencies(deps["components"])
install_gems_dependencies(deps["gems"])
install_js_packages(deps["js_packages"])
end

private

def component_not_found? = !Dir.exist?(component_folder_path)
def component_exists?(component_name) = Dir.exist?(component_folder_path(component_name))

def component_folder_name = component_name.underscore
def component_folder_name(component_name) = component_name.underscore

def component_folder_path = File.join(self.class.source_root, component_folder_name)
def component_folder_path(component_name) = File.join(self.class.source_root, component_folder_name(component_name))

def components_file_paths
files = Dir.glob(File.join(component_folder_path, "*.rb"))
def components_file_paths(component_name)
files = Dir.glob(File.join(component_folder_path(component_name), "*.rb"))
options["with_docs"] ? files : files.reject { |f| f.end_with?("_docs.rb") }
end

def js_controller_file_paths = Dir.glob(File.join(component_folder_path, "*.js"))
def js_controller_file_paths(component_name) = Dir.glob(File.join(component_folder_path(component_name), "*.js"))

def install_components_dependencies(components)
components&.each do |component|
Expand All @@ -89,10 +109,10 @@ def install_js_packages(js_packages)
end
end

def dependencies
def dependencies(component_name)
@dependencies ||= YAML.load_file(File.join(__dir__, "dependencies.yml")).freeze

@dependencies[component_folder_name]
@dependencies[component_folder_name(component_name)]
end
end
end
Expand Down
21 changes: 21 additions & 0 deletions gem/test/generators/component_generator_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,25 @@ def test_tooltip_installs_typography_for_text_component

assert_includes dependencies.fetch("tooltip").fetch("components"), "Typography"
end

def test_resolves_component_folders_for_multiple_components
source_root = File.expand_path("../../lib/ruby_ui", __dir__)

# Folder names mirror ComponentGenerator's `component_name.underscore`.
{"Button" => "button", "Link" => "link", "Input" => "input", "Textarea" => "textarea"}.each do |component_name, folder_name|
folder_path = File.join(source_root, folder_name)

assert(Dir.exist?(folder_path),
"Expected folder for #{component_name} to exist at #{folder_path}")
end
end

def test_validation_collects_all_missing_components
source_root = File.expand_path("../../lib/ruby_ui", __dir__)
folder_names = {"Button" => "button", "NotARealComponent" => "not_a_real_component", "AnotherFakeOne" => "another_fake_one"}

missing = folder_names.reject { |_name, folder| Dir.exist?(File.join(source_root, folder)) }.keys

assert_equal %w[NotARealComponent AnotherFakeOne], missing
end
end
Loading