Investigando un poco el comportamiento de acts_as_scribe, me surgió la necesidad de instanciar un clase conociendo el nombre de la misma. Algo que en java conocía como se realizaba:
Object o = Class.forName("className").newInstance()
lo curioso es que vi que no necesitaba esta solución, que mi problema era otro
, sin embargo la curiosidad pudo más que la necesidad.
Esperaba utilizar para ello la misma clase: Class, sin embargo descubrí que no podía hacerlo así. En Ruby el camino es distinto. Un post en stackoverflow me llevó a encontrar una solución múltiple, aquí las distintas posibilidades:
Object::const_get('String').new()
class_name = 'String'
eval(class_name).new
Module.const_get('Array').new
Y un helper:
"String".constantize.new
Module.const_get no funciona con clases anidadas en módulos, digamos que no admite la sintaxis Admin::Folder, debido a que sólo reconoce sintaxis válidas para dar nombre a una constante.
eval, por el contrario sí permite nombres de clases con namespaces, aunque sufrimos un decrimento en el rendimiento.
Podríamos comprobar el rendimiento de todas ellas con un simple script:
#class_instanciation.rb
#!/usr/bin/env ruby
#
# Created by on 2008-11-24.
require 'benchmark'
require 'qualified_const_get'
n = 1000000
Benchmark.bmbm(10) do |rpt|
rpt.report("simple") do
n.times {Array.new}
end
rpt.report("Object::const_get") do
n.times {Object::const_get('Array').new()}
end
rpt.report("Kernel.const_get") do
n.times {Kernel.const_get('Array').new}
end
rpt.report("Kernel.qualified_const_get") do
n.times {Kernel.qualified_const_get('Array').new}
end
rpt.report("eval") do
n.times {eval('Array').new}
end
# no funciona!!
#rpt.report("eval") do
# n.times {"String".constantize.new}
#end
end
#qualified_const_get.rb
module Kernel
def qualified_const_get(str)
path = str.to_s.split('::')
from_root = path[0].empty?
if from_root
from_root = []
path = path[1..-1]
else
start_ns = ((Class === self)||(Module === self)) ? self : self.class
from_root = start_ns.to_s.split('::')
end
until from_root.empty?
begin
return (from_root+path).inject(Object) { |ns,name| ns.const_get(name) }
rescue NameError
from_root.delete_at(-1)
end
end
path.inject(Object) { |ns,name| ns.const_get(name) }
end
end
Los resultados obtenidos los podéis ver en la siguiente gráfica:

en ella podemos apreciar que si utilizamos const_get se tarda aproximadamente 2 veces más que con la creación normal; que el uso de eval nos hace emplear 5 veces más tiempo y que sin duda la implementación de qualified_const_get no es nada buena, dado que nos hace emplear unos 10sg! osea unas 15 veces más.
La recomendación sería utilizar const_get siempre que se pueda, es decir mientras nuestra clase no tenga namespaces y eval cuando no quede más remedio. El caso de qualified_const_get, me parece poco apropiado para cualquier caso.
Por otro lado, el caso de constantize, no terminó de funcionar, por lo que es imposible compararlo.

0 Responses
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.