edge
Más en rubyonrails.org: Más Ruby on Rails

Los fundamentos de la creación de plugins en Rails

Un plugin de Rails es una extensión o modificación del marco principal. Los plugins proporcionan:

Después de leer esta guía, sabrá:

Esta guía describe cómo construir un plugin impulsado por pruebas que:

Con el propósito de esta guía, finja por un momento que es un ávido observador de aves. Su ave favorita es el Yaffle, y desea crear un plugin que permita a otros desarrolladores compartir la bondad de Yaffle.

1 Configuración

Actualmente, los plugins de Rails se construyen como gemas, gemified plugins. Se pueden compartir entre diferentes aplicaciones de Rails utilizando RubyGems y Bundler si se desea.

1.1 Generar un Plugin como Gema

Rails incluye un comando rails plugin new que crea un esqueleto para desarrollar cualquier tipo de extensión de Rails con la capacidad de ejecutar pruebas de integración utilizando una aplicación Rails ficticia. Cree su plugin con el siguiente comando:

$ rails plugin new yaffle

Consulte el uso y las opciones solicitando ayuda:

$ rails plugin new --help

2 Pruebas de su Plugin Recién Generado

Navegue hasta el directorio que contiene el plugin y edite yaffle.gemspec para reemplazar cualquier línea que tenga valores TODO:

spec.homepage    = "http://example.com"
spec.summary     = "Resumen de Yaffle."
spec.description = "Descripción de Yaffle."

...

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

Luego ejecute el comando bundle install.

Ahora puede ejecutar las pruebas utilizando el comando bin/test y debería ver:

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

Esto le indicará que todo se generó correctamente y está listo para comenzar a agregar funcionalidad.

3 Extender Clases Principales

Esta sección explicará cómo agregar un método a String que estará disponible en cualquier lugar de su aplicación Rails.

En este ejemplo, agregará un método a String llamado to_squawk. Para comenzar, cree un nuevo archivo de prueba con algunas afirmaciones:

# 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

Ejecute bin/test para ejecutar la prueba. Esta prueba debería fallar porque no hemos implementado el método to_squawk:

$ bin/test
E

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


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

.

Finished in 0.003358s, 595.6483 runs/s, 297.8242 assertions/s.
2 runs, 1 assertions, 0 failures, 1 errors, 0 skips

Genial, ahora estás listo para comenzar el desarrollo.

En lib/yaffle.rb, agregue require "yaffle/core_ext":

# yaffle/lib/yaffle.rb

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

module Yaffle
  # Your code goes here...
end

Finalmente, cree el archivo core_ext.rb y agregue el método to_squawk:

# yaffle/lib/yaffle/core_ext.rb

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

Para probar que su método hace lo que dice que hace, ejecute las pruebas unitarias con bin/test desde el directorio de su plugin.

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

Para ver esto en acción, cambie al directorio test/dummy, inicie bin/rails console y comience a hacer squawk:

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

4 Agregar un Método "acts_as" a Active Record

Un patrón común en los plugins es agregar un método llamado acts_as_algo a los modelos. En este caso, usted quiere escribir un método llamado acts_as_yaffle que agregue un método squawk a sus modelos de Active Record.

Para comenzar, configure sus archivos de manera que tenga:

# 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
  # Your code goes here...
end
# yaffle/lib/yaffle/acts_as_yaffle.rb

module Yaffle
  module ActsAsYaffle
  end
end

4.1 Agregar un Método de Clase

Este complemento espera que hayas agregado un método a tu modelo llamado last_squawk. Sin embargo, los usuarios del complemento podrían haber definido un método en su modelo llamado last_squawk que utilizan para otra cosa. Este complemento permitirá cambiar el nombre agregando un método de clase llamado yaffle_text_field.

Para comenzar, escribe una prueba fallida que muestre el comportamiento que deseas:

# 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

Cuando ejecutes bin/test, deberías ver lo siguiente:

$ 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

Esto nos dice que no tenemos los modelos necesarios (Hickwall y Wickwall) que estamos tratando de probar. Podemos generar fácilmente estos modelos en nuestra aplicación Rails "dummy" ejecutando los siguientes comandos desde el directorio test/dummy:

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

Ahora puedes crear las tablas de base de datos necesarias en tu base de datos de prueba navegando hasta tu aplicación dummy y migrando la base de datos. Primero, ejecuta:

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

Mientras estás aquí, cambia los modelos Hickwall y Wickwall para que sepan que deben actuar 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

También agregaremos código para definir el 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

Luego puedes volver al directorio raíz (cd ../..) de tu complemento y volver a ejecutar las pruebas 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

Nos estamos acercando... Ahora implementaremos el código del método acts_as_yaffle para que las pruebas pasen.

# 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

Cuando ejecutes bin/test, deberías ver que todas las pruebas pasan:

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

4.2 Agregar un Método de Instancia

Este complemento agregará un método llamado 'squawk' a cualquier objeto Active Record que llame a acts_as_yaffle. El método 'squawk' simplemente establecerá el valor de uno de los campos en la base de datos.

Para comenzar, escribe una prueba fallida que muestre el comportamiento que deseas:

# 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

Ejecuta la prueba para asegurarte de que las dos últimas pruebas fallen con un error que contenga "NoMethodError: undefined method `squawk'", luego actualiza acts_as_yaffle.rb para que se vea así:

# 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

Ejecuta bin/test una última vez, y deberías ver:

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

NOTA: El uso de write_attribute para escribir en el campo del modelo es solo un ejemplo de cómo un complemento puede interactuar con el modelo, y no siempre será el método correcto para usar. Por ejemplo, también podrías usar: ruby send("#{self.class.yaffle_text_field}=", string.to_squawk)

5 Generadores

Los generadores se pueden incluir en tu gema simplemente creándolos en un directorio lib/generators de tu plugin. Más información sobre la creación de generadores se puede encontrar en la Guía de Generadores.

6 Publicando tu Gema

Las gemas en desarrollo se pueden compartir fácilmente desde cualquier repositorio Git. Para compartir la gema Yaffle con otros, simplemente haz un commit del código a un repositorio Git (como GitHub) y agrega una línea al Gemfile de la aplicación en cuestión:

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

Después de ejecutar bundle install, la funcionalidad de tu gema estará disponible para la aplicación.

Cuando la gema esté lista para ser compartida como una versión formal, se puede publicar en RubyGems.

Alternativamente, puedes beneficiarte de las tareas Rake de Bundler. Puedes ver una lista completa con el siguiente comando:

$ bundle exec rake -T

$ bundle exec rake build
# Construye yaffle-0.1.0.gem en el directorio pkg

$ bundle exec rake install
# Construye e instala yaffle-0.1.0.gem en las gemas del sistema

$ bundle exec rake release
# Crea la etiqueta v0.1.0 y construye y empuja yaffle-0.1.0.gem a Rubygems

Para obtener más información sobre cómo publicar gemas en RubyGems, consulta: Publicando tu gema.

7 Documentación RDoc

Una vez que tu plugin sea estable y estés listo para implementarlo, hazle un favor a los demás y documenta. Afortunadamente, escribir documentación para tu plugin es fácil.

El primer paso es actualizar el archivo README con información detallada sobre cómo usar tu plugin. Algunas cosas clave para incluir son:

  • Tu nombre
  • Cómo instalar
  • Cómo agregar la funcionalidad a la aplicación (varios ejemplos de casos de uso comunes)
  • Advertencias, problemas o consejos que puedan ayudar a los usuarios y ahorrarles tiempo

Una vez que tu README esté sólido, revisa y agrega comentarios RDoc a todos los métodos que los desarrolladores usarán. También es costumbre agregar comentarios # :nodoc: a aquellas partes del código que no están incluidas en la API pública.

Una vez que tus comentarios estén listos, navega hasta el directorio de tu plugin y ejecuta:

$ bundle exec rake rdoc

7.1 Referencias

Comentarios

Se te anima a ayudar a mejorar la calidad de esta guía.

Por favor, contribuye si encuentras algún error tipográfico o factual. Para empezar, puedes leer nuestra contribución a la documentación sección.

También puedes encontrar contenido incompleto o desactualizado. Por favor, añade cualquier documentación faltante para main. Asegúrate de revisar Edge Guides primero para verificar si los problemas ya están resueltos o no en la rama principal. Consulta las Directrices de las Guías de Ruby on Rails para el estilo y las convenciones.

Si por alguna razón encuentras algo que corregir pero no puedes solucionarlo tú mismo, por favor abre un problema.

Y por último, cualquier tipo de discusión sobre la documentación de Ruby on Rails es muy bienvenida en el Foro oficial de Ruby on Rails.