1 Primeiro Contato
Quando você cria uma aplicação usando o comando rails
, na verdade está usando um gerador Rails. Depois disso, você pode obter uma lista de todos os geradores disponíveis invocando bin/rails generate
:
$ rails new myapp
$ cd myapp
$ bin/rails generate
NOTA: Para criar uma aplicação Rails, usamos o comando global rails
, que usa a versão do Rails instalada via gem install rails
. Quando dentro do diretório da sua aplicação, usamos o comando bin/rails
, que usa a versão do Rails incluída na aplicação.
Você obterá uma lista de todos os geradores que vêm com o Rails. Para ver uma descrição detalhada de um gerador específico, invoque o gerador com a opção --help
. Por exemplo:
$ bin/rails generate scaffold --help
2 Criando Seu Primeiro Gerador
Os geradores são construídos em cima do Thor, que fornece opções poderosas para análise e uma ótima API para manipulação de arquivos.
Vamos construir um gerador que cria um arquivo de inicialização chamado initializer.rb
dentro de config/initializers
. O primeiro passo é criar um arquivo em lib/generators/initializer_generator.rb
com o seguinte conteúdo:
class InitializerGenerator < Rails::Generators::Base
def create_initializer_file
create_file "config/initializers/initializer.rb", <<~RUBY
# Adicione o conteúdo de inicialização aqui
RUBY
end
end
Nosso novo gerador é bastante simples: ele herda de Rails::Generators::Base
e tem uma definição de método. Quando um gerador é invocado, cada método público no gerador é executado sequencialmente na ordem em que é definido. Nosso método invoca create_file
, que criará um arquivo no destino fornecido com o conteúdo fornecido.
Para invocar nosso novo gerador, executamos:
$ bin/rails generate initializer
Antes de prosseguirmos, vamos ver a descrição do nosso novo gerador:
$ bin/rails generate initializer --help
O Rails geralmente é capaz de derivar uma boa descrição se um gerador estiver em um namespace, como ActiveRecord::Generators::ModelGenerator
, mas não neste caso. Podemos resolver esse problema de duas maneiras. A primeira maneira de adicionar uma descrição é chamando desc
dentro do nosso gerador:
class InitializerGenerator < Rails::Generators::Base
desc "Este gerador cria um arquivo de inicialização em config/initializers"
def create_initializer_file
create_file "config/initializers/initializer.rb", <<~RUBY
# Adicione o conteúdo de inicialização aqui
RUBY
end
end
Agora podemos ver a nova descrição invocando --help
no novo gerador.
A segunda maneira de adicionar uma descrição é criando um arquivo chamado USAGE
no mesmo diretório do nosso gerador. Faremos isso no próximo passo.
3 Criando Geradores com Geradores
Os próprios geradores têm um gerador. Vamos remover nosso InitializerGenerator
e usar bin/rails generate generator
para gerar um novo:
$ rm lib/generators/initializer_generator.rb
$ bin/rails generate generator initializer
create lib/generators/initializer
create lib/generators/initializer/initializer_generator.rb
create lib/generators/initializer/USAGE
create lib/generators/initializer/templates
invoke test_unit
create test/lib/generators/initializer_generator_test.rb
Este é o gerador acabamos de criar:
class InitializerGenerator < Rails::Generators::NamedBase
source_root File.expand_path("templates", __dir__)
end
Primeiro, observe que o gerador herda de Rails::Generators::NamedBase
em vez de Rails::Generators::Base
. Isso significa que nosso gerador espera pelo menos um argumento, que será o nome do inicializador e estará disponível para nosso código via name
.
Podemos ver isso verificando a descrição do novo gerador:
$ bin/rails generate initializer --help
Uso:
bin/rails generate initializer NAME [options]
Além disso, observe que o gerador tem um método de classe chamado source_root
. Este método aponta para a localização dos nossos modelos, se houver. Por padrão, ele aponta para o diretório lib/generators/initializer/templates
que acabamos de criar.
Para entender como os modelos de gerador funcionam, vamos criar o arquivo lib/generators/initializer/templates/initializer.rb
com o seguinte conteúdo:
# Adicione o conteúdo de inicialização aqui
E vamos alterar o gerador para copiar este modelo quando invocado: ```ruby class InitializerGenerator < Rails::Generators::NamedBase source_root File.expand_path("templates", dir)
def copy_initializer_file copy_file "initializer.rb", "config/initializers/#{file_name}.rb" end end ```
Agora vamos executar nosso gerador:
$ bin/rails generate initializer core_extensions
create config/initializers/core_extensions.rb
$ cat config/initializers/core_extensions.rb
# Adicione o conteúdo de inicialização aqui
Vemos que copy_file
criou config/initializers/core_extensions.rb
com o conteúdo do nosso modelo. (O método file_name
usado no
caminho de destino é herdado de Rails::Generators::NamedBase
.)
4 Opções de Linha de Comando do Gerador
Os geradores podem suportar opções de linha de comando usando class_option
. Por
exemplo:
class InitializerGenerator < Rails::Generators::NamedBase
class_option :scope, type: :string, default: "app"
end
Agora nosso gerador pode ser invocado com a opção --scope
:
$ bin/rails generate initializer theme --scope dashboard
Os valores das opções são acessíveis nos métodos do gerador através de options
:
def copy_initializer_file
@scope = options["scope"]
end
5 Resolução de Geradores
Ao resolver o nome de um gerador, o Rails procura o gerador usando vários
nomes de arquivo. Por exemplo, quando você executa bin/rails generate initializer core_extensions
,
o Rails tenta carregar cada um dos seguintes arquivos, em ordem, até encontrar um:
rails/generators/initializer/initializer_generator.rb
generators/initializer/initializer_generator.rb
rails/generators/initializer_generator.rb
generators/initializer_generator.rb
Se nenhum desses for encontrado, um erro será gerado.
Colocamos nosso gerador no diretório lib/
da aplicação porque esse
diretório está em $LOAD_PATH
, permitindo assim que o Rails encontre e carregue o arquivo.
6 Substituindo Modelos de Geradores do Rails
O Rails também procura em vários lugares ao resolver arquivos de modelos de geradores.
Um desses lugares é o diretório lib/templates/
da aplicação. Esse
comportamento nos permite substituir os modelos usados pelos geradores internos do Rails.
Por exemplo, poderíamos substituir o modelo de controlador de scaffold ou os
modelos de visualização de scaffold.
Para ver isso em ação, vamos criar um arquivo lib/templates/erb/scaffold/index.html.erb.tt
com o seguinte conteúdo:
<%% @<%= plural_table_name %>.count %> <%= human_name.pluralize %>
Observe que o modelo é um modelo ERB que renderiza outro modelo ERB.
Portanto, qualquer <%
que deve aparecer no modelo resultante deve ser escapado como
<%%
no modelo do gerador.
Agora vamos executar o gerador de scaffold interno do Rails:
$ bin/rails generate scaffold Post title:string
...
create app/views/posts/index.html.erb
...
O conteúdo de app/views/posts/index.html.erb
é:
<% @posts.count %> Posts
7 Substituindo Geradores do Rails
Os geradores internos do Rails podem ser configurados via config.generators
,
incluindo a substituição de alguns geradores completamente.
Primeiro, vamos dar uma olhada mais de perto em como o gerador de scaffold funciona.
$ bin/rails generate scaffold User name:string
invoke active_record
create db/migrate/20230518000000_create_users.rb
create app/models/user.rb
invoke test_unit
create test/models/user_test.rb
create test/fixtures/users.yml
invoke resource_route
route resources :users
invoke scaffold_controller
create app/controllers/users_controller.rb
invoke erb
create app/views/users
create app/views/users/index.html.erb
create app/views/users/edit.html.erb
create app/views/users/show.html.erb
create app/views/users/new.html.erb
create app/views/users/_form.html.erb
create app/views/users/_user.html.erb
invoke resource_route
invoke test_unit
create test/controllers/users_controller_test.rb
create test/system/users_test.rb
invoke helper
create app/helpers/users_helper.rb
invoke test_unit
invoke jbuilder
create app/views/users/index.json.jbuilder
create app/views/users/show.json.jbuilder
A partir da saída, podemos ver que o gerador de scaffold invoca outros
geradores, como o gerador scaffold_controller
. E alguns desses
geradores também invocam outros geradores. Em particular, o gerador scaffold_controller
invoca vários outros geradores, incluindo o gerador helper
.
Vamos substituir o gerador interno helper
por um novo gerador. Vamos nomear
o gerador como my_helper
:
$ bin/rails generate generator rails/my_helper
create lib/generators/rails/my_helper
create lib/generators/rails/my_helper/my_helper_generator.rb
create lib/generators/rails/my_helper/USAGE
create lib/generators/rails/my_helper/templates
invoke test_unit
create test/lib/generators/rails/my_helper_generator_test.rb
E em lib/generators/rails/my_helper/my_helper_generator.rb
vamos definir
o gerador como:
class Rails::MyHelperGenerator < Rails::Generators::NamedBase
def create_helper_file
create_file "app/helpers/#{file_name}_helper.rb", <<~RUBY
module #{class_name}Helper
# Estou ajudando!
end
RUBY
end
end
Por fim, precisamos informar ao Rails para usar o gerador my_helper
em vez do
gerador interno helper
. Para isso, usamos config.generators
. Em
config/application.rb
, vamos adicionar:
config.generators do |g|
g.helper :my_helper
end
Agora, se executarmos o gerador de scaffold novamente, veremos o gerador my_helper
em
ação:
$ bin/rails generate scaffold Article body:text
...
invoke scaffold_controller
...
invoke my_helper
create app/helpers/articles_helper.rb
...
NOTA: Você pode notar que a saída para o gerador interno helper
inclui "invoke test_unit", enquanto a saída para my_helper
não inclui.
Embora o gerador helper
não gere testes por padrão, ele fornece um gancho para fazê-lo usando hook_for
.
Podemos fazer o mesmo incluindo hook_for :test_framework, as: :helper
na classe MyHelperGenerator
.
Consulte a documentação do hook_for
para obter mais informações.
7.1 Fallbacks de Geradores
Outra maneira de substituir geradores específicos é usando fallbacks. Um fallback
permite que um namespace de gerador delegue para outro namespace de gerador.
Por exemplo, digamos que queremos substituir o gerador test_unit:model
pelo nosso próprio gerador my_test_unit:model
, mas não queremos substituir todos os outros geradores test_unit:*
, como test_unit:controller
.
Primeiro, criamos o gerador my_test_unit:model
em lib/generators/my_test_unit/model/model_generator.rb
:
module MyTestUnit
class ModelGenerator < Rails::Generators::NamedBase
source_root File.expand_path("templates", __dir__)
def do_different_stuff
say "Fazendo coisas diferentes..."
end
end
end
Em seguida, usamos config.generators
para configurar o gerador test_framework
como my_test_unit
, mas também configuramos uma alternativa para que quaisquer geradores my_test_unit:*
ausentes sejam resolvidos como test_unit:*
:
config.generators do |g|
g.test_framework :my_test_unit, fixture: false
g.fallbacks[:my_test_unit] = :test_unit
end
Agora, quando executamos o gerador de scaffold, vemos que my_test_unit
substituiu test_unit
, mas apenas os testes de modelo foram afetados:
$ bin/rails generate scaffold Comment body:text
invoke active_record
create db/migrate/20230518000000_create_comments.rb
create app/models/comment.rb
invoke my_test_unit
Fazendo coisas diferentes...
invoke resource_route
route resources :comments
invoke scaffold_controller
create app/controllers/comments_controller.rb
invoke erb
create app/views/comments
create app/views/comments/index.html.erb
create app/views/comments/edit.html.erb
create app/views/comments/show.html.erb
create app/views/comments/new.html.erb
create app/views/comments/_form.html.erb
create app/views/comments/_comment.html.erb
invoke resource_route
invoke my_test_unit
create test/controllers/comments_controller_test.rb
create test/system/comments_test.rb
invoke helper
create app/helpers/comments_helper.rb
invoke my_test_unit
invoke jbuilder
create app/views/comments/index.json.jbuilder
create app/views/comments/show.json.jbuilder
8 Modelos de Aplicação
Modelos de aplicação são um tipo especial de gerador. Eles podem usar todos os métodos auxiliares do gerador, mas são escritos como um script Ruby em vez de uma classe Ruby. Aqui está um exemplo:
# template.rb
if yes?("Gostaria de instalar o Devise?")
gem "devise"
devise_model = ask("Como você gostaria que o modelo de usuário fosse chamado?", default: "User")
end
after_bundle do
if devise_model
generate "devise:install"
generate "devise", devise_model
rails_command "db:migrate"
end
git add: ".", commit: %(-m 'Commit inicial')
end
Primeiro, o modelo pergunta ao usuário se ele gostaria de instalar o Devise.
Se o usuário responder "sim" (ou "s"), o modelo adiciona o Devise ao Gemfile
e pergunta ao usuário o nome do modelo de usuário do Devise (com o padrão User
).
Mais tarde, depois que o bundle install
for executado, o modelo executará os geradores do Devise e rails db:migrate
se um modelo do Devise for especificado. Por fim, o modelo fará git add
e git commit
em todo o diretório do aplicativo.
Podemos executar nosso modelo ao gerar um novo aplicativo Rails passando a opção -m
para o comando rails new
:
$ rails new my_cool_app -m path/to/template.rb
Alternativamente, podemos executar nosso modelo dentro de um aplicativo existente com o bin/rails app:template
:
$ bin/rails app:template LOCATION=path/to/template.rb
Os modelos também não precisam ser armazenados localmente - você pode especificar uma URL em vez de um caminho:
$ rails new my_cool_app -m http://example.com/template.rb
$ bin/rails app:template LOCATION=http://example.com/template.rb
9 Métodos Auxiliares do Gerador
O Thor fornece muitos métodos auxiliares do gerador por meio de Thor::Actions
, como:
Além desses, o Rails também fornece muitos métodos auxiliares por meio de Rails::Generators::Actions
, como:
Feedback
Você é incentivado a ajudar a melhorar a qualidade deste guia.
Por favor, contribua se encontrar algum erro de digitação ou factual. Para começar, você pode ler nossa contribuição à documentação seção.
Você também pode encontrar conteúdo incompleto ou desatualizado. Por favor, adicione qualquer documentação ausente para o principal. Certifique-se de verificar Guias Edge primeiro para verificar se os problemas já foram corrigidos ou não no branch principal. Verifique as Diretrizes dos Guias do Ruby on Rails para estilo e convenções.
Se por algum motivo você encontrar algo para corrigir, mas não puder corrigi-lo você mesmo, por favor abra uma issue.
E por último, mas não menos importante, qualquer tipo de discussão sobre a documentação do Ruby on Rails é muito bem-vinda no Fórum oficial do Ruby on Rails.