ruby-on-rails - form_with - select rails helper
¿Cuál es la forma más limpia de anular el hallazgo de ActiveRecord tanto para modelos como para colecciones? (3)
Antes que nada, asegúrate de conocer bien la estructura de herencia de llamadas al método de Ruby, ya que sin esto puedes terminar apuñalando en la oscuridad.
La forma más directa de hacer esto dentro de una clase ActiveRecord es:
def self.find(*args)
super
end
Esto se aplicará también a las asociaciones, ya que usan el buscador de bases ellos mismos. Ahora solo necesitas hacer tu personalización. La complejidad de eso puede variar ampliamente, y no sé lo que estás haciendo, así que no puedo ofrecer ninguna recomendación.
También definir esto dinámicamente será un ejercicio en sí mismo, pero esto debería hacer que apuntes en la dirección correcta.
Tengo un código de biblioteca que anula el método de búsqueda de Ar. También incluyo el módulo para todas las clases de la Asociación, de modo que tanto MyModel.find como @ parent.my_models.find funcionen y apliquen el alcance correcto.
Basé mi código fuera de will_paginate:
a = ActiveRecord::Associations
returning([ a::AssociationCollection ]) { |classes|
# detect http://dev.rubyonrails.org/changeset/9230
unless a::HasManyThroughAssociation.superclass == a::HasManyAssociation
classes << a::HasManyThroughAssociation
end
}.each do |klass|
klass.send :include, Finder::ClassMethods
klass.class_eval { alias_method_chain :method_missing, :paginate }
end
Mi problema es que solo quiero anular los buscadores para algunos modelos. Actualmente necesito extender todas las clases de colección de asociaciones que comparten todos los modelos. Sé que puedo extender las asociaciones por modelo pasando un módulo:
has_many :things, :extend => SomeCustomMethods
Pero mi biblioteca es básicamente un complemento de ActiveRecord, por lo que me gustaría una convención limpia para las extensiones de buscador enchufables que se apliquen tanto al modelo como a las colecciones de ámbito sin afectar a todos los modelos en la aplicación.
Desea anular find_every
, que es el método AR que finalmente ejecutará find_by_sql
con la consulta correspondiente. Anular el find
no funcionará para los buscadores personalizados, y es más frágil.
Pero para ser compatible con otros complementos, no puedes simplemente sobrecargar este método. En cambio, alias y llama a la implementación original después de hacer lo que quieras:
module MyPlugin
def self.included(base)
class << base
alias_method :find_every_without_my_plugin, :find_every
def find_every(*args)
# do whatever you need ...
find_every_without_my_plugin(*args)
end
end
end
end
ActiveRecord::Base.send :include, MyPlugin
Esto habilitará su complemento para todas las clases. ¿Cómo desea controlar qué modelos están habilitados? Tal vez un accesorio de complemento estándar?
class User < ActiveRecord::Base
my_plugin
end
Para apoyar esto, necesitas mover la class << base
a un método de clase (por lo tanto, base
debe ser self
). Me gusta:
module MyPlugin
def self.included(base)
class << base
base.extend ClassMethods
end
end
module ClassMethods
def my_plugin
class << self
alias_method :find_every_without_my_plugin, :find_every
# ...
end
end
end
end
''La respuesta de Pedro es correcta, pero hay un pequeño error.
def self.included(base)
class << base
base.extend ClassMethods
end
end
debiera ser
def self.included(base)
base.extend ClassMethods
end
El uso de la clase << base ... final tiene el efecto de invocar ''extender'' en ''base'' en el ámbito de método de clase, pero no hay ningún método ''base'' en ActiveRecord :: Base, por lo que se genera un error. Usar base.extend por sí mismo llamará al método ''extender'' de ActiveRecord :: Base.