Añadiendo métodos a las clases Array y ActiveRecord::Base

Hola a todos, a ver si podéis ayudarme un poco (no tengo mucha idea):

En lib/temp_module.rb he creado:

#module TempModule

class Array
def all
map {|x| [x.name, x.id] }
end
def names
map {|x| x.name }
end
end

class ActiveRecord::Base
def self.names
self.find(:all,:select => “name”).map{|g| g.name}
end
def all
self.find(:all).map {|x| [x.name, x.id] } #¿aqui no funciona .all?
end
end
#end

He comentado la declaración de TempModule porque con ella los métodos no
eran visibles (no se si porque creaba un nuevo espacio de nombres y
realmente no llegaba a modificar las clases Array y ActiveRecord). Tal y
cómo está el código ahora funciona, aunque puede que hayan demasiados
accesos a la base de datos. Sin embargo, falla al cargar la libreria
(temp_module) cuando también llamo a otras para exportar datos a csv y
xls:
require ‘temp_module’
require ‘csv’
require ‘spreadsheet/excel’
include Spreadsheet

Es decir, parece que todo funciona perfectamente de forma independiente
pero al utilizarse conjuntamente pasa pasa algo con las dependencias y
no encuentra el método de un módulo (no de una librería, sino de un
módulo):

class HomeController < ApplicationController
require ‘temp_module’
require ‘csv’
require ‘spreadsheet/excel’
include Spreadsheet

private
def rows
rs=Mtask.find(params[:id]).results
rowss=rs.map{|r| [r.mtask_id, r.ident ,r.author]} # falla al hacer
r.author
#…
rowss
end

end

Por otra parte, en log/development.log aparece:

NoMethodError (undefined method author' for #<Result:0xb77ca134>): /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.2/lib/active_record/base.rb:1861:inmethod_missing’
.//app/controllers/home_controller.rb:144:in rows' /usr/lib/ruby/gems/1.8/gems/activerecord-1.15.2/lib/active_record/associations/association_proxy.rb:123:inmethod_missing’
/usr/lib/ruby/gems/1.8/gems/activerecord-1.15.2/lib/active_record/associations/has_many_association.rb:98:in
`method_missing’

Y he probado rs.map{|r| [r.mtask_id, r.ident ,r.author]} desde la
consola y funciona.

¿Alguien puede darme una pista? Gracias.

On 5/2/07, Héctor [email protected] wrote:

def names
  self.find(:all).map {|x| [x.name, x.id] } #¿aqui no funciona .all?
end

end
#end

Hola Héctor,

  1. Para agregar métodos a las clases de la manera fácil:

class Array
def all
# …
end
end

  1. Para agregar métodos de una manera más linda:

module ArrayExtension
def all
# …
end
end

Esto podría ir en environment.rb o en el init.rb de un plugin tuyo.

Array.send(:include, ArrayExtension)

  1. Para agregar métodos de clase:

class ActiveRecord::Base
class << self
def all
find(…) # self en este contexto es la clase, no la instancia
end
end

o bien, que es lo mismo

def self.all
find(…) # no hace falta self, porque self en ese contexto es la
clase
end
end

  1. Sobre require e include:

require es simplemente para que Ruby cargue tu archivo y lo puedas
tener disponible.
include es algo totalmente distinto: lo que haces es “inyectar” el
módulo en el contexto en donde lo incluís (no es un copy & paste del
código sino una referencia al módulo). si lo hacés en una clase,
entonces los métodos del módulo están disponibles en la clase. si lo
hacés afuera, se extiende Kernel.

No te puedo ayudar con lo de Excel porque no lo usé, pero lo correcto sería:

require ‘temp_module’

class HomeController < …
include TempModule
end

Probablemente el require no sea necesario porque Rails lo
encontraríacuando intentes incluir el módulo y se dispare todo el mecanismo de
autoloading.

Espero haber aclarado algo!

Saludos!

On 5/2/07, Héctor [email protected] wrote:

  1. Al incluir Array.send(:include, ArrayExtension) en environment.rb,
    supongo que también hay que hacer un require ‘array_extension’ antes,
    pero ¿va dentro de

Rails::Initializer.run do |config|

end

o fuera?

Es lo mismo, por un tema de orden yo lo pondría afuera.

El require no es necesario; Rails lo encuentra solo…

  1. ¿Para los métodos de clase también debería hacer un .send en
    environment o con hacer un require basta?

Al incluir el módulo se agregan tanto los métodos de clase como los de
instancia.

Saludos!

Gracias por tu respuesta Janowski, aunque hay un par de cosas que
comentabas que no me han quedado muy claras:

  1. Al incluir Array.send(:include, ArrayExtension) en environment.rb,
    supongo que también hay que hacer un require ‘array_extension’ antes,
    pero ¿va dentro de

Rails::Initializer.run do |config|

end

o fuera?

  1. ¿Para los métodos de clase también debería hacer un .send en
    environment o con hacer un require basta?

¿Incluir un módulo es hacer el .send?
¿ActiveRecord::Base.send(:include,‘modulo’)?

Gracias de nuevo!

Entonces, ¿debería hacer ActiveRecord::Base.send(:include,‘modulo’)?

Aggg, quería decir extend:
Entonces, ¿debería hacer ActiveRecord::Base.send(:extend,‘modulo’)?

On May 2, 2007, at 7:24 PM, Damian J. wrote:

Al incluir el módulo se agregan tanto los métodos de clase como los de
instancia.

Ojo, los primeros no van. Por eso en muchos plugins se separan
modulos InstanceMethods y ClassMethods, para hacer un include del
primero y un extend con el segundo:

http://blog.grayproductions.net/articles/2006/02/22/class-level-
mixins

– fxn

On 5/2/07, Héctor [email protected] wrote:

Aggg, quería decir extend:
Entonces, ¿debería hacer ActiveRecord::Base.send(:extend,‘modulo’)?

Yay!

Estuve viendo #extend en un artículo [1] y sigue pareciéndome que lo
más usual en Ruby es hacer lo siguiente (que es la forma que usa
acts_as_* en ActiveRecord)

module ArrayExtension
module ClassMethods
def all

end
end

def self.included(base)
base.extend(ClassMethods)
end

def foo

este ya es un método de instancia

end
end

Ahí va? :slight_smile: