0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Rails generator, Thor

Last updated at Posted at 2023-06-11
1 / 18

Rails Guideのコマンドラインのところを見ているとgeneratorを作成できることに気づいた

  # こんな感じでファイルをテンプレートから作れるジェネレータを作れる
  be rails g my_generator hogehoge

config/defsは作成するのが面倒だと前から感じていた


generatorを作ってみた


defsに定数を追加したいときは大体以下のファイルを生成する必要がある

  • config/defs/**.yml
  • app/model/defs/**.rb
  • frontend/src/Defs/**.jsx

こんな感じでテンプレートを生成したい

  be rails g defs hoge_fuga

  Running via Spring preloader in process 175
    create  app/models/defs/hoge_fuga.rb
    create  config/defs/hoge_fuga.yml
    create  frontend/src/Defs/HogeFuga.jsx

generatorをgenerateする


generatorをコマンドから作成する

  # testも作成されるが削除した
  be rails g generator defs

    create    lib/generators/defs/defs_generator.rb
    create    lib/generators/defs/USAGE
    create    lib/generators/defs/templates

各ファイルの説明

  • defs_generator.rb

generatorの本体。

  • USAGE

helpの中身be rails g defs --helpで中身を表示する
説明は省略する

  • templates

template用のerbを置くディレクトリ


generator本体

こいつのパブリックメソッドが定義順に実行される
Thorというgemを使っている
Rails::Generators::NamedBaseを継承するとデフォルトでnameに引数が入る

class DefsGenerator < Rails::Generators::NamedBase
  source_root File.expand_path('templates', __dir__)

  def create_model_file
    template 'model.rb.erb', "app/models/defs/#{name.underscore}.rb"
  end

  def create_config_file
    template 'config.yml.erb', "config/defs/#{name.underscore}.yml"
  end

  def create_jsx_file
    template 'front_defs.jsx.erb', "frontend/src/Defs/#{class_name}.jsx"
  end

  private

  def class_name
    name.classify
  end
end


templates

普通のerbで書ける

model.rb.erb

class Defs::<%= class_name %> < ActiveYaml::Base
  include ActiveHash::Enum

  set_root_path Rails.root.join('config/defs')
  set_filename '<%= name.underscore %>'

  enum_accessor :key
end

config.yml.erb

#
# id <Int>
# key: <String>
# name <String>
#
hoge:
  id: 1
  key: hoge
  name: fuga

front_defs.jsx.erb

import constantizeJSONDef from 'ConstantizeJSONDef';
import Def from 'RailsConfig/defs/<%= name.underscore %>.yml';

export default constantizeJSONDef(Def, {
  key: 'key',
  name: '<%= class_name %>',
});

Usage

> be rails g defs --help
Usage:
  rails generate defs NAME [options]

Options:
  [--skip-namespace], [--no-skip-namespace]              # Skip namespace (affects only isolated engines)
  [--skip-collision-check], [--no-skip-collision-check]  # Skip collision check

Runtime options:
  -f, [--force]                    # Overwrite files that already exist
  -p, [--pretend], [--no-pretend]  # Run but do not make any changes
  -q, [--quiet], [--no-quiet]      # Suppress status output
  -s, [--skip], [--no-skip]        # Skip files that already exist

Description:
    Defsを生成する

Example:
    be rails g defs hoge

    This will create:
        app/models/defs/hoge.rb
        config/defs/hoge.yml
        frontend/src/Defs/Hoge.jsx

    消したくなったら
    be rails destroy defs hoge


Railsのgeneratorをもう少し詳しく


Thor


Railsのgeneratorの内部で使われているgem
コマンドラインツールの作成のためのgem
Rake-likeなシンタックスで書ける

Rails内部で使われているのでrequireすれば使える


引数の扱いがrakeと比べて楽

class HogeGenerator < Rails::Generators::Base
  source_root File.expand_path('templates', __dir__)

  # documentには :string, :numeric, :array, :hashが使えるとある
  # :optionalと:requiredは逆の関係にある
  argument :arg1, type: :string, default: 'str', desc: 'essential argment'
  argument :arg3, type: :hash, optional: true
  argument :arg4, type: :array, required: false
  # 何故かバグった
  # argument :arg2, type: :numeric

  def test_args
    puts arg1
    # puts arg2
    puts arg3
    puts arg4
  end

  class_option :skip_option, type: :boolean, default: false, desc: 'this is description'
  class_option :put_option2, type: :boolean, default: false, desc: 'this is description', aliases: [:x, :y, :z]

  def test_class_options
    say 'not skip option' unless options[:skip_option]
    puts 'put option2' if options[:put_option2]
  end
end

RailsのgeneratorではThor::GroupとThor::Actionsを拡張する形で使っている
Thor::Groupはpublicメソッドを定義順に呼び出す処理を主に行っている
Actionsはrailsのものと合わせてdocを探しながら実装するのが良さそう

thor_doc:
https://rdoc.info/github/erikhuda/thor/master/Thor/Actions

rails_doc:
https://api.rubyonrails.org/classes/Rails/Generators/Actions.html

module Rails
  module Generators
    class Error < Thor::Error # :nodoc:
    end

    class Base < Thor::Group
      include Thor::Actions
      include Rails::Generators::Actions

  
 # 以下略
end

使えそうなメソッド

Thor::Actions

  • directory(source, *args, &block)
    ディレクトリごと再帰的にコピーする

  • gsub_file(path, flag, *args, &block)
    ファイル内のテキストを置き換える

  • inject_into_class(path, klass, *args, &block) ⇒ Object

  • inject_into_module(path, module_name, *args, &block) ⇒ Object

  • insert_into_file(destination, *args, &block) ⇒ Object (also: #inject_into_file)
    読んで字の如くコードの内容を挿入できる

  • inside(dir = "", config = {}, &block)
    ブロック実行中のみ一時的に他ディレクトリをルートにする

  • run(command, config = {})
    シェルのコマンドが実行できる

  • run_ruby_script(command, config = {})
    rubyのスクリプトが実行できる

  • template(source, *args, &block)
    ERBのテンプレートからファイルを生成できる

Rails::Generators::Actions

  • rake(command, options = {})
    rakeタスクが実行できる

  • git(commands = {})
    gitコマンドが実行できる


0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?