Skip to content

Instantly share code, notes, and snippets.

@garrettdimon
Last active June 28, 2023 20:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save garrettdimon/f933c23ffe1b92e6305bd1f78bc7c6e4 to your computer and use it in GitHub Desktop.
Save garrettdimon/f933c23ffe1b92e6305bd1f78bc7c6e4 to your computer and use it in GitHub Desktop.
A basic example of a custom Rails generator with the full guide and explanation available at https://garrettdimon.com/journal/posts/creating-custom-rails-geenrators
# frozen_string_literal: true
# lib/generators/plain/templates/model.rb.tt
class <%= class_name %>
<% if active_model? -%>
include ActiveModel::Model
<% end -%>
<% if attributes_names.any? -%>
attr_accessor <%= attributes_names.map { |name| ":#{name}" }.join(', ') %>
<% end -%>
end
# frozen_string_literal: true
# lib/generators/plain/templates/model_test.rb.tt
require "test_helper"
class <%= class_name %>Test < ActiveSupport::TestCase
setup do
@<%= singular_name %> = <%= class_name %>.new
end
test "the truth" do
assert_predicate @<%= singular_name %>, present?
end
end
# frozen_string_literal: true
# lib/generators/plain/plain_generator.rb
class PlainGenerator < Rails::Generators::NamedBase
# Remember that NamedBase defines the `name` argument for us, so anything we declare here will be passed in addition
# to the `name` argument.
argument :attributes, type: :array, default: [], banner: "attribute attribute"
class_option :active_model, type: :boolean, default: true
source_root File.expand_path("templates", __dir__)
def create_model_file
template "model.rb", File.join("app/models", "#{file_name}.rb")
end
def create_model_test_file
template "model_test.rb", File.join("test/models", "#{file_name}_test.rb")
end
private
def active_model?
options[:active_model]
end
end
# frozen_string_literal: true
# test/lib/generators/plain/plain_generator_test.rb
require "test_helper"
require "generators/plain/plain_generator"
class PlainGeneratorTest < Rails::Generators::TestCase
tests PlainGenerator
destination Rails.root.join("tmp/generators")
setup :prepare_destination
test "generates the model file" do
run_generator ["Point"]
assert_file "app/models/point.rb" do |content|
assert_match(/class Point/, content)
assert_match(/include ActiveModel::Model/, content)
end
end
test "generates the model test file" do
run_generator ["Point"]
assert_file "test/models/point_test.rb" do |content|
assert_match(/class PointTest < ActiveSupport::TestCase/, content)
end
end
test "supports specifying attr_accessor fields for the model" do
run_generator %w[Point x y]
assert_file "app/models/point.rb" do |content|
assert_match(/attr_accessor :x, :y/, content)
end
end
test "supports specifying --active-model for the model" do
run_generator %w[Point x y --active-model]
assert_file "app/models/point.rb" do |content|
assert_match(/include ActiveModel::Model/, content)
end
end
test "supports specifying or --no-active-model for the model" do
run_generator %w[Point x y --no-active-model]
assert_file "app/models/point.rb" do |content|
assert_no_match(/include ActiveModel::Model/, content)
end
end
end
Description:
Generates a plain Ruby class with the given NAME
Example:
`bin/rails generate plain Name`
This will create:
app/models/name.rb
test/models/name_test.rb
`bin/rails generate plain Point x y`
This will create these files and add x and y attr_accessor:
app/models/name.rb
test/models/name_test.rb
`bin/rails generate plain Point x y --no-active-model`
This will create these files, add x and y attr_accessor, but skip the ActiveModel include:
app/models/name.rb
test/models/name_test.rb
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment