edge
Mais em rubyonrails.org: Mais Ruby on Rails

Os Conceitos Básicos de Criação de Plugins Rails

Um plugin Rails é uma extensão ou modificação do framework principal. Os plugins fornecem:

Após ler este guia, você saberá:

Este guia descreve como construir um plugin orientado a testes que irá:

Para fins deste guia, finja por um momento que você é um observador de pássaros ávido. Sua ave favorita é o Yaffle, e você deseja criar um plugin que permita que outros desenvolvedores compartilhem a maravilha do Yaffle.

1 Configuração

Atualmente, os plugins Rails são construídos como gems, gemified plugins. Eles podem ser compartilhados entre diferentes aplicações Rails usando o RubyGems e o Bundler, se desejado.

1.1 Gerar um Plugin Gemificado

O Rails vem com um comando rails plugin new que cria um esqueleto para desenvolver qualquer tipo de extensão Rails com a capacidade de executar testes de integração usando uma aplicação Rails fictícia. Crie o seu plugin com o comando:

$ rails plugin new yaffle

Veja o uso e as opções solicitando ajuda:

$ rails plugin new --help

2 Testando seu Plugin Recém-gerado

Navegue até o diretório que contém o plugin e edite yaffle.gemspec para substituir quaisquer linhas que tenham valores TODO:

spec.homepage    = "http://example.com"
spec.summary     = "Resumo do Yaffle."
spec.description = "Descrição do Yaffle."

...

spec.metadata["source_code_uri"] = "http://example.com"
spec.metadata["changelog_uri"] = "http://example.com"

Em seguida, execute o comando bundle install.

Agora você pode executar os testes usando o comando bin/test e você deverá ver:

$ bin/test
...
1 execução, 1 asserção, 0 falhas, 0 erros, 0 pulos

Isso indicará que tudo foi gerado corretamente e você está pronto para começar a adicionar funcionalidades.

3 Estendendo Classes Principais

Esta seção explicará como adicionar um método à String que estará disponível em qualquer lugar de sua aplicação Rails.

Neste exemplo, você adicionará um método chamado to_squawk à String. Para começar, crie um novo arquivo de teste com algumas asserções:

# yaffle/test/core_ext_test.rb

require "test_helper"

class CoreExtTest < ActiveSupport::TestCase
  def test_to_squawk_prepends_the_word_squawk
    assert_equal "squawk! Hello World", "Hello World".to_squawk
  end
end

Execute bin/test para executar o teste. Este teste deve falhar porque ainda não implementamos o método to_squawk:

$ bin/test
E

Erro:
CoreExtTest#test_to_squawk_prepends_the_word_squawk:
NoMethodError: undefined method `to_squawk' for "Hello World":String


bin/test /caminho/para/yaffle/test/core_ext_test.rb:4

.

Concluído em 0.003358s, 595.6483 execuções/s, 297.8242 asserções/s.
2 execuções, 1 asserção, 0 falhas, 1 erro, 0 pulos

Ótimo - agora você está pronto para começar o desenvolvimento.

Em lib/yaffle.rb, adicione require "yaffle/core_ext":

# yaffle/lib/yaffle.rb

require "yaffle/version"
require "yaffle/railtie"
require "yaffle/core_ext"

module Yaffle
  # Seu código vai aqui...
end

Finalmente, crie o arquivo core_ext.rb e adicione o método to_squawk:

# yaffle/lib/yaffle/core_ext.rb

class String
  def to_squawk
    "squawk! #{self}".strip
  end
end

Para testar se seu método faz o que diz que faz, execute os testes unitários com bin/test a partir do diretório do seu plugin.

$ bin/test
...
2 execuções, 2 asserções, 0 falhas, 0 erros, 0 pulos

Para ver isso em ação, mude para o diretório test/dummy, inicie bin/rails console e comece a cacarejar:

irb> "Hello World".to_squawk
=> "squawk! Hello World"

4 Adicionar um Método "acts_as" ao Active Record

Um padrão comum em plugins é adicionar um método chamado acts_as_alguma_coisa aos modelos. Neste caso, você deseja escrever um método chamado acts_as_yaffle que adiciona um método squawk aos seus modelos Active Record.

Para começar, configure seus arquivos para que você tenha:

# yaffle/test/acts_as_yaffle_test.rb

require "test_helper"

class ActsAsYaffleTest < ActiveSupport::TestCase
end
# yaffle/lib/yaffle.rb

require "yaffle/version"
require "yaffle/railtie"
require "yaffle/core_ext"
require "yaffle/acts_as_yaffle"

module Yaffle
  # Seu código vai aqui...
end
# yaffle/lib/yaffle/acts_as_yaffle.rb

module Yaffle
  module ActsAsYaffle
  end
end

4.1 Adicionar um Método de Classe

Este plugin espera que você tenha adicionado um método ao seu modelo chamado last_squawk. No entanto, os usuários do plugin podem já ter definido um método em seu modelo chamado last_squawk que eles usam para outra coisa. Este plugin permitirá que o nome seja alterado adicionando um método de classe chamado yaffle_text_field.

Para começar, escreva um teste que falhe e mostre o comportamento desejado:

# yaffle/test/acts_as_yaffle_test.rb

require "test_helper"

class ActsAsYaffleTest < ActiveSupport::TestCase
  def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
    assert_equal "last_squawk", Hickwall.yaffle_text_field
  end

  def test_a_wickwalls_yaffle_text_field_should_be_last_tweet
    assert_equal "last_tweet", Wickwall.yaffle_text_field
  end
end

Quando você executar bin/test, você deverá ver o seguinte:

$ bin/test
# Running:

..E

Error:
ActsAsYaffleTest#test_a_wickwalls_yaffle_text_field_should_be_last_tweet:
NameError: uninitialized constant ActsAsYaffleTest::Wickwall


bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:8

E

Error:
ActsAsYaffleTest#test_a_hickwalls_yaffle_text_field_should_be_last_squawk:
NameError: uninitialized constant ActsAsYaffleTest::Hickwall


bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:4



Finished in 0.004812s, 831.2949 runs/s, 415.6475 assertions/s.
4 runs, 2 assertions, 0 failures, 2 errors, 0 skips

Isso nos diz que não temos os modelos necessários (Hickwall e Wickwall) que estamos tentando testar. Podemos gerar facilmente esses modelos em nossa aplicação Rails "dummy" executando os seguintes comandos a partir do diretório test/dummy:

$ cd test/dummy
$ bin/rails generate model Hickwall last_squawk:string
$ bin/rails generate model Wickwall last_squawk:string last_tweet:string

Agora você pode criar as tabelas de banco de dados necessárias em seu banco de dados de teste navegando até sua aplicação dummy e migrando o banco de dados. Primeiro, execute:

$ cd test/dummy
$ bin/rails db:migrate

Enquanto estiver aqui, altere os modelos Hickwall e Wickwall para que eles saibam que devem agir como yaffles.

# test/dummy/app/models/hickwall.rb

class Hickwall < ApplicationRecord
  acts_as_yaffle
end
# test/dummy/app/models/wickwall.rb

class Wickwall < ApplicationRecord
  acts_as_yaffle yaffle_text_field: :last_tweet
end

Também adicionaremos código para definir o método acts_as_yaffle.

# yaffle/lib/yaffle/acts_as_yaffle.rb

module Yaffle
  module ActsAsYaffle
    extend ActiveSupport::Concern

    class_methods do
      def acts_as_yaffle(options = {})
      end
    end
  end
end
# test/dummy/app/models/application_record.rb

class ApplicationRecord < ActiveRecord::Base
  include Yaffle::ActsAsYaffle

  self.abstract_class = true
end

Você pode então retornar ao diretório raiz (cd ../..) do seu plugin e executar os testes novamente usando bin/test.

$ bin/test
# Running:

.E

Error:
ActsAsYaffleTest#test_a_hickwalls_yaffle_text_field_should_be_last_squawk:
NoMethodError: undefined method `yaffle_text_field' for #<Class:0x0055974ebbe9d8>


bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:4

E

Error:
ActsAsYaffleTest#test_a_wickwalls_yaffle_text_field_should_be_last_tweet:
NoMethodError: undefined method `yaffle_text_field' for #<Class:0x0055974eb8cfc8>


bin/test /path/to/yaffle/test/acts_as_yaffle_test.rb:8

.

Finished in 0.008263s, 484.0999 runs/s, 242.0500 assertions/s.
4 runs, 2 assertions, 0 failures, 2 errors, 0 skips

Estamos chegando lá... Agora implementaremos o código do método acts_as_yaffle para fazer os testes passarem.

# yaffle/lib/yaffle/acts_as_yaffle.rb

module Yaffle
  module ActsAsYaffle
    extend ActiveSupport::Concern

    class_methods do
      def acts_as_yaffle(options = {})
        cattr_accessor :yaffle_text_field, default: (options[:yaffle_text_field] || :last_squawk).to_s
      end
    end
  end
end
# test/dummy/app/models/application_record.rb

class ApplicationRecord < ActiveRecord::Base
  include Yaffle::ActsAsYaffle

  self.abstract_class = true
end

Quando você executar bin/test, você verá que todos os testes passam:

$ bin/test
...
4 runs, 4 assertions, 0 failures, 0 errors, 0 skips

4.2 Adicionar um Método de Instância

Este plugin adicionará um método chamado 'squawk' a qualquer objeto Active Record que chame acts_as_yaffle. O método 'squawk' simplesmente definirá o valor de um dos campos no banco de dados.

Para começar, escreva um teste que falhe e mostre o comportamento desejado:

# yaffle/test/acts_as_yaffle_test.rb
require "test_helper"

class ActsAsYaffleTest < ActiveSupport::TestCase
  def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
    assert_equal "last_squawk", Hickwall.yaffle_text_field
  end

  def test_a_wickwalls_yaffle_text_field_should_be_last_tweet
    assert_equal "last_tweet", Wickwall.yaffle_text_field
  end

  def test_hickwalls_squawk_should_populate_last_squawk
    hickwall = Hickwall.new
    hickwall.squawk("Hello World")
    assert_equal "squawk! Hello World", hickwall.last_squawk
  end

  def test_wickwalls_squawk_should_populate_last_tweet
    wickwall = Wickwall.new
    wickwall.squawk("Hello World")
    assert_equal "squawk! Hello World", wickwall.last_tweet
  end
end

Execute o teste para garantir que os dois últimos testes falhem com um erro que contenha "NoMethodError: undefined method `squawk'", em seguida, atualize acts_as_yaffle.rb para ficar assim:

# yaffle/lib/yaffle/acts_as_yaffle.rb

module Yaffle
  module ActsAsYaffle
    extend ActiveSupport::Concern

    included do
      def squawk(string)
        write_attribute(self.class.yaffle_text_field, string.to_squawk)
      end
    end

    class_methods do
      def acts_as_yaffle(options = {})
        cattr_accessor :yaffle_text_field, default: (options[:yaffle_text_field] || :last_squawk).to_s
      end
    end
  end
end
# test/dummy/app/models/application_record.rb

class ApplicationRecord < ActiveRecord::Base
  include Yaffle::ActsAsYaffle

  self.abstract_class = true
end

Execute bin/test mais uma vez, e você verá:

$ bin/test
...
6 runs, 6 assertions, 0 failures, 0 errors, 0 skips

NOTA: O uso de write_attribute para escrever no campo no modelo é apenas um exemplo de como um plugin pode interagir com o modelo e nem sempre será o método correto a ser usado. Por exemplo, você também pode usar: ruby send("#{self.class.yaffle_text_field}=", string.to_squawk)

5 Geradores

Geradores podem ser incluídos em sua gem simplesmente criando-os em um diretório lib/generators do seu plugin. Mais informações sobre a criação de geradores podem ser encontradas no Guia de Geradores.

6 Publicando sua Gem

Plugins de gem atualmente em desenvolvimento podem ser facilmente compartilhados de qualquer repositório Git. Para compartilhar a gem Yaffle com outros, simplesmente faça o commit do código para um repositório Git (como o GitHub) e adicione uma linha ao Gemfile da aplicação em questão:

gem "yaffle", git: "https://github.com/rails/yaffle.git"

Após executar bundle install, a funcionalidade da sua gem estará disponível para a aplicação.

Quando a gem estiver pronta para ser compartilhada como um lançamento formal, ela pode ser publicada no RubyGems.

Alternativamente, você pode se beneficiar das tarefas Rake do Bundler. Você pode ver uma lista completa com o seguinte:

$ bundle exec rake -T

$ bundle exec rake build
# Constrói yaffle-0.1.0.gem no diretório pkg

$ bundle exec rake install
# Constrói e instala yaffle-0.1.0.gem nas gems do sistema

$ bundle exec rake release
# Cria a tag v0.1.0 e constrói e envia yaffle-0.1.0.gem para o Rubygems

Para obter mais informações sobre a publicação de gems no RubyGems, consulte: Publicando sua gem.

7 Documentação RDoc

Quando seu plugin estiver estável e você estiver pronto para implantá-lo, faça um favor a todos e documente-o! Felizmente, escrever documentação para seu plugin é fácil.

O primeiro passo é atualizar o arquivo README com informações detalhadas sobre como usar seu plugin. Algumas coisas importantes a incluir são:

  • Seu nome
  • Como instalar
  • Como adicionar a funcionalidade ao aplicativo (vários exemplos de casos de uso comuns)
  • Avisos, problemas ou dicas que possam ajudar os usuários e economizar tempo

Depois de ter um README sólido, adicione comentários RDoc a todos os métodos que os desenvolvedores usarão. Também é costume adicionar comentários # :nodoc: às partes do código que não estão incluídas na API pública.

Depois que seus comentários estiverem prontos, navegue até o diretório do seu plugin e execute:

$ bundle exec rake rdoc

7.1 Referências

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.