universitario una por planificar planificacion plan modelos modelo hacer estructura elementos ejemplos ejemplo docente diaria competencias como clases clase ruby class namespaces module

ruby - una - plan de clase por competencias pdf



¿Es posible dar a un submódulo el mismo nombre que una clase de nivel superior? (3)

Aquí hay otro ejemplo divertido:

module SomeName class Client end end module Integrations::SomeName::Importer def perform ... client = ::SomeName::Client.new(...) ... end end

Eso produce:

block in load_missing_constant'': uninitialized constant Integrations::SomeName::Importer::SomeName (NameError)

Ruby (2.3.4) solo va a la primera aparición de "SomeName" que puede encontrar, no al nivel superior.

Una forma de Kernel.const_get(''SomeName'') es usar un mejor anidamiento de módulos / clases (!!), o usar Kernel.const_get(''SomeName'')

Fondo:

Aquí está el problema, reducido a un ejemplo mínimo:

# bar.rb class Bar end # foo/bar.rb module Foo::Bar end # foo.rb class Foo include Foo::Bar end # runner.rb require ''bar'' require ''foo''

➔ ruby runner.rb ./foo.rb:2: warning: toplevel constant Bar referenced by Foo::Bar ./foo.rb:2:in `include'': wrong argument type Class (expected Module) (TypeError) from ./foo.rb:2 from runner.rb:2:in `require'' from runner.rb:2


Aquí hay un ejemplo más mínimo para demostrar este comportamiento:

class Bar; end class Foo include Foo::Bar end

Salida:

warning: toplevel constant Bar referenced by Foo::Bar TypeError: wrong argument type Class (expected Module)

Y aquí es aún más mínimo:

Bar = 0 class Foo; end Foo::Bar

Salida:

warning: toplevel constant Bar referenced by Foo::Bar

La explicación es simple, no hay ningún error: no hay una Bar en Foo , y Foo::Bar aún no está definido. Para que se defina Foo::Bar , Foo debe definirse primero. El siguiente código funciona bien:

class Bar; end class Foo module ::Foo::Bar; end include Foo::Bar end

Sin embargo, hay algo que es inesperado para mí. Los siguientes dos bloques se comportan de manera diferente:

Bar = 0 class Foo; end Foo::Bar

produce una advertencia:

warning: toplevel constant Bar referenced by Foo::Bar

pero

Bar = 0 module Foo; end Foo::Bar

produce un error:

uninitialized constant Foo::Bar (NameError)


Excelente; Su ejemplo de código es muy claro. Lo que tiene allí es una dependencia circular de jardín, oscurecida por las peculiaridades del operador de resolución de alcance de Ruby.

Cuando ejecutas el código de Ruby require ''foo'' , ruby ​​encuentra foo.rb y lo ejecuta, y luego encuentra foo/bar.rb y lo ejecuta. Así que cuando Ruby encuentra su clase de Foo y ejecuta include Foo::Bar , busca una constante llamada Bar en la clase de Foo , porque eso es lo que Foo::Bar denota. Cuando no encuentra uno, busca constantes denominadas Bar otros ámbitos que lo encierran y, finalmente, lo encuentra en el nivel superior. Pero ese Bar es una clase, y por eso no se puede include d.

Incluso si pudieras persuadirte de que foo/bar.rb antes de foo.rb , no sería útil; module Foo::Bar significa "encuentra la constante Foo , y si es una clase o un módulo, comienza a definir un módulo dentro de él llamado Bar ". Foo no se habrá creado aún, por lo que la demanda seguirá fallando.

Cambiar el nombre de Foo::Bar a Foo::UserBar tampoco servirá de nada, ya que el choque de nombres no tiene la culpa definitiva.

Entonces, ¿qué puedes hacer? En un nivel alto, tienes que romper el ciclo de alguna manera. Lo más simple es definir Foo en dos partes, así:

# bar.rb class Bar A = 4 end # foo.rb class Foo # Stuff that doesn''t depend on Foo::Bar goes here. end # foo/bar.rb module Foo::Bar A = 5 end class Foo # Yep, we re-open class Foo inside foo/bar.rb include Bar # Note that you don''t need Foo:: as we automatically search Foo first. end Bar::A # => 4 Foo::Bar::A # => 5

Espero que esto ayude.