source portable para opencobol open ruby require circular-dependency

ruby - portable - opencobol para windows



Dependencias circulares en Ruby (3)

Si necesita acceder a una subclase de una superclase, existe una buena posibilidad de que su modelo se rompa (es decir, debería ser una clase).

Dicho esto, hay un par de soluciones obvias:

1) solo crea un archivo que requiera los archivos foo:

all_foos.rb:

require "foo.rb" require "foo_sub.rb"

y elimine los requisitos de foo.rb y foo_sub.rb.

2) eliminar el requerimiento de foo.rb

3) eliminar el require de foo_sub.rb y poner el require en foo.rb después de la definición de la clase.

Ruby no es C ++, no se quejará de FooSub.SOME_CONSTANT hasta que llame a Foo # foo ();)

Digamos que tenemos dos clases, Foo y Foo Sub, cada una en un archivo diferente, foo.rb y foo_sub.rb respectivamente.

foo.rb:

require "foo_sub" class Foo def foo FooSub.SOME_CONSTANT end end

foo_sub.rb:

require "foo" class FooSub < Foo SOME_CONSTANT = 1 end

Esto no va a funcionar debido a la dependencia circular; no podemos definir ninguna clase sin la otra. Hay varias soluciones que he visto. Dos de ellos quiero evitarlos: ponerlos en el mismo archivo y eliminar la dependencia circular. Entonces, la única otra solución que he encontrado es una declaración a futuro:

foo.rb:

class Foo end require "foo_sub" class Foo def foo FooSub.SOME_CONSTANT end end

foo_sub.rb

require "foo" class FooSub < Foo SOME_CONSTANT = 1 end

Lamentablemente, no puedo hacer que funcione lo mismo si tengo tres archivos:

foo.rb:

class Foo end require "foo_sub_sub" class Foo def foo FooSubSub.SOME_CONSTANT end end

foo_sub.rb:

require "foo" class FooSub < Foo end

foo_sub_sub.rb:

require "foo_sub" class FooSubSub < FooSub SOME_CONSTANT = 1 end

Si requiero foo_sub.rb, entonces FooSub es una constante no inicializada en foo_sub_sub.rb. ¿Alguna idea de cómo evitar esto sin ponerlos en el mismo archivo ni eliminar la dependencia circular?


Otra opción decente es usar la función de autocarga de Ruby.

Funciona así:

module MyModule autoload :Class1, File.join(File.dirname(__FILE__), *%w[my_module class1.rb]) autoload :Class2, File.join(File.dirname(__FILE__), *%w[my_module class2.rb]) # Code for MyModule here end

y se describe bien aquí:

http://talklikeaduck.denhaven2.com/2009/04/06/all-that-y-might-require


Sandi Metz explica una solución a este problema y cómo resolverlo muy bien en su libro Diseño Práctico Orientado a Objetos en Ruby (POODA).

Lo que ella sugiere (y me inclino a aceptar porque funcionó lo mejor para mí hasta ahora), es inyectar la subclase FooSub en la clase magistral Foo .

Esto se haría en foo.rb con:

1 class Foo 2 def initialize(foo_sub:) 3 end 4 end

para mantener el código limpio y mantenerlo fácilmente modificable, debe envolver el foo_sub en un método contenedor para que su clase se vea así:

1 class Foo 2 3 attr_reader :foo_sub 4 5 def initialize(foo_sub:) 6 @foo_sub = foo_sub 7 end 8 end

(Aquí, attr_reader está configurando un método llamado foo_sub y luego lo que se pasa al valor del hash de inicialización es una instancia de foo_sub, por @foo_sub tanto @foo_sub (línea 6), se puede establecer en el valor del método foo_sub ).

Ahora puede tener su clase FooSub sin requisitos, por lo que es independiente de todo:

1 class FooSub 2 SOME_CONSTANT = 1 3 end

y puede agregar un método a su clase Foo que tenga acceso a #SOME_CONSTANT:

1 class Foo 2 3 attr_reader :foo_sub 4 5 def initialize(foo_sub:) 6 @foo_sub = foo_sub 7 end 8 9 def foo 10 foo_sub.SOME_CONSTANT 11 end 12 end

En realidad, con esto, está configurando un método que devuelve la instancia de foo_sub @foo_sub (que se inyecta en la inicialización), con el método #SOME_CONSTANT agregado en él. Tu clase solo espera que todo lo que se inyecte en la inicialización responda a #SOME_CONSTANT. Para que funcione, tendría que inyectar su clase FooSub al configurar Foo en un REPL (por ejemplo, IRB o PRY):

PRY [1]> require ''foo'' [2]> => true [3]> require ''foo_sub'' [4]> => true [5]> foo_sub = FooSub.new [6]> => #<FooSub:0x007feb91157140> [7]> foo = Foo.new(foo_sub: foo_sub) [8]> => #<Foo:0x007feb91157735 @foo_sub=FooSub:0x007feb91157140> [9]> foo.foo [10]> => 1

si, sin embargo, le inyectaste algo más, terminarías con:

PRY [1]> require ''foo'' [2]> => true [3]> require ''foo_sub'' [4]> => true [5]> foo_sub = FooSub.new [6]> => #<FooSub:0x007feb91157140> [7]> foo = Foo.new(foo_sub: ''something else as a string'') [8]> => #<Foo:0x007feb91157735 @foo_sub=''something else as a string''> [9]> foo.foo [10]> => UNDEFINED CONSTANT #SOME_CONSTANT ERROR MESSAGE

No sé qué leería el mensaje de error real en la línea 10, pero piense en esas líneas. Este error podría ocurrir porque de hecho habrías intentado ejecutar el método #SOME_CONSTANT en la cadena ''algo más como una cadena'' o ''something else as a string''.SOME_CONSTANT cadena''. ''something else as a string''.SOME_CONSTANT que obviamente no funcionaría.