Ruby Bytes template project generator Enhanced
Generate a new project to build an application template with Ruby Bytes
Used 3 times
M
Magnous
Usage
Run this command in your Rails app directory in the terminal:
rails app:template LOCATION="https://railsbytes.com/script/VRZsL2"
Template Source
Review the code before running this template on your machine.
# frozen_string_literal: true
name = nil
git_email = nil
git_username = nil
loop do
name = ask("What is the name of your template?") || ""
if name.empty?
say "Hm, empty name doesn't work. Let's try again"
else
break
end
end
loop do
git_email = ask("What is your git email?") || ""
if git_email.empty?
say "Hm, empty email doesn't work. Let's try again"
else
break
end
end
loop do
git_username = ask("What is your git username?") || ""
if git_username.empty?
say "Hm, empty username doesn't work. Let's try again"
else
break
end
end
root_dir = ask("Where do you want to store the code? (Default: #{File.join(Dir.pwd, name)})")
if root_dir.nil? || root_dir.empty?
root_dir = File.join(Dir.pwd, name)
end
human_name = name.split(/[-_]/).map(&:capitalize).join(" ")
needs_rails = yes? "Is this a Rails application template?"
inside(root_dir) do
file "template/_install_gems.rb", <<~CODE
# Hint to avoid duplicates:
# gem "oj", "~> 3.16" if needs_gem?("oj")
def install_gems
end
CODE
file "template/#{name}.rb", <<~CODE
say "Hey! Let's start with the #{human_name} installation"
<%= include "helpers" %>
<%= include "facts" %>
<%= include "git_setup" %>
<%= include "install_gems" %>
<%= include "core" %>
<%= include "launch" %>
CODE
file "Gemfile", <<~CODE
# frozen_string_literal: true
source "https://rubygems.org"
gem "debug"
gem "rbytes", github: "rolling-space/rbytes"
gem "rake"
gem "minitest"
gem "minitest-focus"
gem "minitest-reporters"
CODE
file "template/_helpers.rb", <<-'CODE'
# frozen_string_literal: true
require 'uri'
require 'open-uri'
require 'fileutils'
# DEV_MODE=true rails new rvmd --database=postgresql --css=tailwind --skip-javascript --skip-sprockets --template="rails_prototype/template.rb" --skip-kamal
# TODO: add business specs foe existing operations
def source_paths
[File.expand_path(__dir__)]
end
# plugins = []
# base_configs = []
# extensions = []
# gems = []
#
# specs = []
# deps = []
if Rails.version < '8.0.0'
fail 'please use rails 8.0.0 or above'
end
def in_root(&block)
inside Rails.application.root, &block
end
def do_bundle
# Custom bundle command ensures dependencies are correctly installed
Bundler.with_unbundled_env { run "bundle install" }
end
def bundle_add(*packages)
packages.each do |package|
say_status :info, "Adding #{package} to Gemfile"
run "bundle add #{package}"
end
end
def yarn(*packages)
run("yarn add #{packages.join(" ")}")
end
def ruby_version
in_root do
if File.file?("Gemfile.lock")
bundler_parser = Bundler::LockfileParser.new(Bundler.read_file("Gemfile.lock"))
specs = bundler_parser.specs.map(&:name)
locked_version = bundler_parser.ruby_version.match(/(\d+\.\d+\.\d+)/)&.[](1) if bundler_parser.ruby_version
# ruby_version = Gem::Version.new(locked_version).segments[0..1].join(".") if locked_version
# maybe_ruby_version = bundler_parser.ruby_version&.match(/ruby (\d+\.\d+\.\d+)./i)&.[](1)
Gem::Version.new(locked_version).segments[0..1].join(".")
end
end
end
def specs
in_root do
if File.file?("Gemfile.lock")
bundler_parser = Bundler::LockfileParser.new(Bundler.read_file("Gemfile.lock"))
bundler_parser.specs.map(&:name)
end
end
end
def deps
in_root do
if File.file?("Gemfile")
bundler_parser = Bundler::Dsl.new
bundler_parser.eval_gemfile("Gemfile")
bundler_parser.dependencies.map(&:name)
end
end
end
def has_gem?(name)
deps.include?(name) || specs.include?(name)
end
def needs_gem?(name)
!has_gem?(name)
end
def has_rails
((specs | deps) & %w[activerecord actionpack rails]).any?
end
def has_rspec
in_root do
((specs | deps) & %w[rspec-core]).any? && File.directory?("spec")
end
end
def download_file(from_path, to_path = from_path)
if ENV['DEV_MODE']
# for local development and upgrades
copy_file from_path, to_path
else
base_url = 'https://raw.githubusercontent.com/OrestF/rails_prototype/rails_api'
get([base_url, from_path].join('/'), to_path)
end
end
def gem_add(gem_name, **args)
if Gem.loaded_specs.key?(gem_name)
say_status :info, "Ruby gem #{gem_name} already loaded"
else
gem gem_name, **args
end
end
def app_name
Rails.application.class.name.partition('::').first.parameterize
end
def root_dir
Dir.pwd
end
def name
root_dir.rpartition("/").last
end
def human_name
name.split(/[-_]/).map(&:capitalize).join(" ")
end
def find_and_replace_in_file(file_name, old_content, new_content)
text = File.read(file_name)
new_contents = text.gsub(old_content, new_content)
File.open(file_name, 'w') { |file| file.write new_contents }
end
CODE
file "template/_facts.rb", <<-'CODE'
## Gathered Facts
def say_facts
say "Gathering facts..."
say "=================="
say "Ruby version: #{ruby_version}"
say "Gemspecs: #{specs.join(", ")}"
say "=================="
say "App name: #{app_name}"
say "Human name: #{human_name}"
say "Root dir: #{root_dir}"
say "Original Name: #{name}"
say "=================="
say "Git Base Config:"
say "Git user name: #{git config: 'get user.name'}"
say "Git user email: #{git config: 'get user.email'}"
end
CODE
file "template/_git_setup.rb", <<-'CODE'
def git_setup
if File.exist?('/.dockerenv')
git_username = `git config get user.name`.strip
git_email = `git config get user.email`.strip
say_status :info, "Setting up git..."
say_status :info, "Setup git user info..."
git config: "--global user.email #{git_email}"
git config: "--global user.name #{git_username}"
say_status :info, "Setup git global config..."
say_status :info, "Setup the editor to vim..."
git config: "--global core.editor 'vim -w'"
say_status :info, "Set the global gitignore..."
git config: "--global core.excludesfile '~/.gitignore_global'"
say_status :info, "Set the default branch..."
git config: "--global init.defaultBranch main"
say_status :info, "Set the global pull options..."
git config: "--global pull.rebase true"
say_status :info, "Set the pull ff strategy..."
git config: "--global pull.ff only"
say_status :info, "Set the safe directories..."
git config: "--global safe.directory /project"
git config: "--global safe.directory /bench"
end
end
CODE
file "template/_launch.rb", <<-'CODE'
say "_launch.rb"
say "Launching...!"
say_facts
git_setup
install_gems
after_bundle do
add_core
end
CODE
file "template/_core.rb", <<-'CODE'
say "_core.rb"
def add_core
end
CODE
file "Rakefile", ERB.new(
*[
<<~'TCODE'
# frozen_string_literal: true
require "rake/testtask"
Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.libs << "lib"
t.test_files = FileList["test/**/*_test.rb"]
t.warning = false
end
namespace :test do
task :isolated do
Dir.glob("test/**/*_test.rb").all? do |file|
sh(Gem.ruby, "-I#{__dir__}/lib:#{__dir__}/test", file)
end || raise("Failures")
end
end
desc "Generate installation template"
task :compile do
require "ruby_bytes/cli"
RubyBytes::CLI.new.run("compile", "./template/<%= name %>.rb")
end
task default: %w[test:isolated]
TCODE
], trim_mode: "<>").result(binding)
file "README.md", ERB.new(
*[
<<~'TCODE'
# <%= human_name %> template
TODO: Provide a description for your template.
## Usage
<% if needs_rails %>
You can install this template via RailsBytes by running the following command:
```sh
bin/rails app:template LOCATION=<TODO:url>
```
Alternatively, you can install it via [Ruby Bytes][] as follows:
```sh
rbytes install <TODO:url>
```
<% else %>
You can install this template via [Ruby Bytes][] as follows:
```sh
rbytes install <TODO:url>
```
<% end %>
[Ruby Bytes]: https://github.com/palkan/rbytes
TCODE
], trim_mode: "<>").result(binding)
file ".gitignore", ERB.new(
*[
<<~'TCODE'
# Numerous always-ignore extensions
*.diff
*.err
*.orig
*.log
*.rej
*.swo
*.swp
*.vi
*~
*.sass-cache
*.iml
.idea/
# Sublime
*.sublime-project
*.sublime-workspace
# OS or Editor folders
.DS_Store
.cache
.project
.settings
.tmproj
Thumbs.db
.bundle/
log/*.log
pkg/
spec/dummy/db/*.sqlite3
spec/dummy/db/*.sqlite3-journal
spec/dummy/tmp/
Gemfile.lock
Gemfile.local
.rspec-local
.ruby-version
*.gem
tmp/
.rbnext/
gemfiles/*.lock
TCODE
], trim_mode: "<>").result(binding)
file "test/test_helper.rb", ERB.new(
*[
<<~'TCODE'
# frozen_string_literal: true
begin
require "debug"
rescue LoadError
end
$LOAD_PATH.unshift File.expand_path("../lib", __dir__)
require "rbytes"
require "ruby_bytes/test_case"
require "minitest/autorun"
require "minitest/focus"
require "minitest/reporters"
Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
class GeneratorTestCase < RubyBytes::TestCase
root File.join(__dir__, "../template")
<% if needs_rails %>
dummy_app File.join(__dir__, "fixtures", "basic_rails_app")
<% end %>
end
TCODE
], trim_mode: "<>").result(binding)
file "test/template_test.rb", ERB.new(
*[
<<~'TCODE'
# frozen_string_literal: true
require "test_helper"
class TemplateTest < Minitest::Test
def test_template_compiles
assert RubyBytes::Compiler.new(File.join(__dir__, "../template/<%= name %>.rb")).render
end
end
TCODE
], trim_mode: "<>").result(binding)
file "test/template/example_test.rb", ERB.new(
*[
<<~'TCODE'
# frozen_string_literal: true
require "test_helper"
class ExampleTest < GeneratorTestCase
template <<~'CODE'
<%%= include "example" %>
CODE
def test_name
run_generator do |output|
assert_line_printed(
output,
"Hey from the included template!"
)
end
end
end
TCODE
], trim_mode: "<>").result(binding)
if needs_rails
file "test/fixtures/basic_rails_app/config.ru", ERB.new(
*[
<<~'TCODE'
# frozen_string_literal: true
# This file is used by Rack-based servers to start the application.
require_relative "config/environment"
run Rails.application
TCODE
], trim_mode: "<>").result(binding)
file "test/fixtures/basic_rails_app/Gemfile", ERB.new(
*[
<<~'TCODE'
# frozen_string_literal: true
source "https://rubygems.org"
ruby "~> 3.0"
gem "rails"
TCODE
], trim_mode: "<>").result(binding)
file "test/fixtures/basic_rails_app/Gemfile.lock", ERB.new(
*[
<<~'TCODE'
GEM
remote: https://rubygems.org/
specs:
actioncable (7.0.4.3)
actionpack (= 7.0.4.3)
activesupport (= 7.0.4.3)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (7.0.4.3)
actionpack (= 7.0.4.3)
activejob (= 7.0.4.3)
activerecord (= 7.0.4.3)
activestorage (= 7.0.4.3)
activesupport (= 7.0.4.3)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
actionmailer (7.0.4.3)
actionpack (= 7.0.4.3)
actionview (= 7.0.4.3)
activejob (= 7.0.4.3)
activesupport (= 7.0.4.3)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
actionpack (7.0.4.3)
actionview (= 7.0.4.3)
activesupport (= 7.0.4.3)
rack (~> 2.0, >= 2.2.0)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (7.0.4.3)
actionpack (= 7.0.4.3)
activerecord (= 7.0.4.3)
activestorage (= 7.0.4.3)
activesupport (= 7.0.4.3)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.0.4.3)
activesupport (= 7.0.4.3)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
activejob (7.0.4.3)
activesupport (= 7.0.4.3)
globalid (>= 0.3.6)
activemodel (7.0.4.3)
activesupport (= 7.0.4.3)
activerecord (7.0.4.3)
activemodel (= 7.0.4.3)
activesupport (= 7.0.4.3)
activestorage (7.0.4.3)
actionpack (= 7.0.4.3)
activejob (= 7.0.4.3)
activerecord (= 7.0.4.3)
activesupport (= 7.0.4.3)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (7.0.4.3)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
builder (3.2.4)
concurrent-ruby (1.2.2)
crass (1.0.6)
date (3.3.3)
erubi (1.12.0)
globalid (1.1.0)
activesupport (>= 5.0)
i18n (1.12.0)
concurrent-ruby (~> 1.0)
loofah (2.19.1)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.8.1)
mini_mime (>= 0.1.1)
net-imap
net-pop
net-smtp
marcel (1.0.2)
method_source (1.0.0)
mini_mime (1.1.2)
minitest (5.18.0)
net-imap (0.3.4)
date
net-protocol
net-pop (0.1.2)
net-protocol
net-protocol (0.2.1)
timeout
net-smtp (0.3.3)
net-protocol
nio4r (2.5.8)
nokogiri (1.14.2-arm64-darwin)
racc (~> 1.4)
racc (1.6.2)
rack (2.2.6.4)
rack-test (2.1.0)
rack (>= 1.3)
rails (7.0.4.3)
actioncable (= 7.0.4.3)
actionmailbox (= 7.0.4.3)
actionmailer (= 7.0.4.3)
actionpack (= 7.0.4.3)
actiontext (= 7.0.4.3)
actionview (= 7.0.4.3)
activejob (= 7.0.4.3)
activemodel (= 7.0.4.3)
activerecord (= 7.0.4.3)
activestorage (= 7.0.4.3)
activesupport (= 7.0.4.3)
bundler (>= 1.15.0)
railties (= 7.0.4.3)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
rails-html-sanitizer (1.5.0)
loofah (~> 2.19, >= 2.19.1)
railties (7.0.4.3)
actionpack (= 7.0.4.3)
activesupport (= 7.0.4.3)
method_source
rake (>= 12.2)
thor (~> 1.0)
zeitwerk (~> 2.5)
rake (13.0.6)
thor (1.2.1)
timeout (0.3.2)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
websocket-driver (0.7.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
zeitwerk (2.6.7)
PLATFORMS
arm64-darwin-21
DEPENDENCIES
rails
RUBY VERSION
ruby 3.2.1p31
BUNDLED WITH
2.4.8
TCODE
], trim_mode: "<>").result(binding)
file "test/fixtures/basic_rails_app/config/environment.rb", ERB.new(
*[
<<~'TCODE'
# frozen_string_literal: true
# Load the Rails application.
require_relative "application"
# Initialize the Rails application.
Rails.application.initialize!
TCODE
], trim_mode: "<>").result(binding)
file "test/fixtures/basic_rails_app/config/application.rb", ERB.new(
*[
<<~'TCODE'
# frozen_string_literal: true
require_relative "boot"
require "action_controller/railtie"
module Dummy
class Application < Rails::Application
config.logger = ActiveSupport::TaggedLogging.new(Logger.new(nil))
config.log_level = :fatal
config.eager_load = true
end
end
TCODE
], trim_mode: "<>").result(binding)
file "test/fixtures/basic_rails_app/config/environments/development.rb", ERB.new(
*[
<<~'TCODE'
# frozen_string_literal: true
Rails.application.configure do
config.cache_classes = false
# Do not eager load code on boot.
config.eager_load = false
end
TCODE
], trim_mode: "<>").result(binding)
file "test/fixtures/basic_rails_app/config/database.yml", ERB.new(
*[
<<~'TCODE'
development:
adapter: postgresql
database: my_database
host: localhost
TCODE
], trim_mode: "<>").result(binding)
file "test/fixtures/basic_rails_app/.gitignore", ERB.new(
*[
<<~'TCODE'
!Gemfile.lock
TCODE
], trim_mode: "<>").result(binding)
end
needs_ci = yes? "Would you like to configure GitHub Actions to test and publish your template?"
if needs_ci
file ".github/workflows/test.yml", ERB.new(
*[
<<~'TCODE'
name: Build
on:
push:
branches:
- main
pull_request:
jobs:
test:
runs-on: ubuntu-latest
env:
BUNDLE_JOBS: 4
BUNDLE_RETRY: 3
CI: true
strategy:
fail-fast: false
matrix:
ruby: ["3.2"]
steps:
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Run tests
run: |
bundle exec rake test
TCODE
], trim_mode: "<>").result(binding)
file ".github/workflows/publish.yml", ERB.new(
*[
<<~'TCODE'
name: Publish
on:
push:
tags:
- v*
workflow_dispatch:
jobs:
publish:
uses: palkan/rbytes/.github/workflows/railsbytes.yml@master
with:
template: template/<%= name %>.rb
secrets:
RAILS_BYTES_ACCOUNT_ID: "${{ secrets.RAILS_BYTES_ACCOUNT_ID }}"
RAILS_BYTES_TOKEN: "${{ secrets.RAILS_BYTES_TOKEN }}"
RAILS_BYTES_TEMPLATE_ID: "${{ secrets.RAILS_BYTES_TEMPLATE_ID }}"
TCODE
], trim_mode: "<>").result(binding)
end
end