2008-10-18 14 views
9

Sé por diseño que no puede controlar lo que sucede cuando se destruye un objeto. También conozco la definición de algún método de clase como finalizador.RAII en Ruby (O, Cómo administrar recursos en Ruby)

Sin embargo, ¿es la expresión de ruby ​​para RAII de C++ (los recursos se inicializan en constructor, cerrado en destructor)? ¿Cómo administran las personas los recursos utilizados dentro de los objetos incluso cuando ocurren errores o excepciones?

Usando asegurar obras:

f = File.open("testfile") 
begin 
    # .. process 
rescue 
    # .. handle error 
ensure 
    f.close unless f.nil? 
end 

pero los usuarios de la clase tener que acordarse de hacer toda la chacha cada comenzará-rescate-asegurar el método abierto tiene que ser llamado.

Así, por ejemplo, voy a tener la clase siguiente:

class SomeResource 
def initialize(connection_string) 
    @resource_handle = ...some mojo here... 
end 

def do_something() 
    begin 
    @resource_handle.do_that() 
    ... 
    rescue 
    ... 
    ensure 
end 

def close 
    @resource_handle.close 
end 

end 

El resource_handle no se cerrará si la excepción es causado por alguna otra clase y el script finaliza.

¿O es el problema más de que sigo haciendo esto también C++ - como?

Respuesta

15

para que los usuarios no "tienen que acordarse de hacer el conjunto comenzará-rescate-chacha garantizar" combinar rescue/ensure con yield.

class SomeResource 
    ... 
    def SomeResource.use(*resource_args) 
    # create resource 
    resource = SomeResource.new(*resource_args) # pass args direct to constructor 
    # export it 
    yield resource 
    rescue 
    # known error processing 
    ... 
    ensure 
    # close up when done even if unhandled exception thrown from block 
    resource.close 
    end 
    ... 
end 

El código de cliente se puede utilizar como sigue:

SomeResource.use(connection_string) do | resource | 
    resource.do_something 
    ... # whatever else 
end 
# after this point resource has been .close()d 

De hecho, esta es la forma en File.open opera - haciendo que la primera respuesta confusa en el mejor (era a mis compañeros de trabajo).

File.open("testfile") do |f| 
    # .. process - may include throwing exceptions 
end 
# f is guaranteed closed after this point even if exceptions are 
# thrown during processing 
-1

Ver http://www.rubycentral.com/pickaxe/tut_exceptions.html

En Rubí, se utilizaría un ensure declaración:

f = File.open("testfile") 
begin 
    # .. process 
rescue 
    # .. handle error 
ensure 
    f.close unless f.nil? 
end 

Ésta será familiar para los usuarios de Python, Java o C#, ya que funciona como try/catch/finally .

+3

Mientras esto funciona, tiene que depender de alguien que esté usando su clase para hacer lo correcto. Deben recordar hacer todo el chacha de inicio-rescate-asegurar cada vez que se necesite llamar al método abierto. Estoy editando la pregunta para aclarar esto, pero gracias por la respuesta :) – moogs

8

¿Qué hay de yield ing a resource to a block? Ejemplo:

File.open("testfile") do |f| 
    begin 
    # .. process 
    rescue 
    # .. handle error 
    end 
end 
+0

De hecho, el archivo # abierto llamado con un bloque tiene un bloque de 'asegura', que cierra el archivo correctamente, pase lo que pase. Entonces, sí, puedes tener bloques de rescate/aseguramiento, pero son opcionales. – webmat

+0

Exactamente. Con este modismo, solo tiene que implementar el bloque de seguridad una vez por tipo de recurso, en lugar de implementarlo en todos los códigos que usan el recurso. – bk1e

2

O es el problema más todavía estoy haciendo esto también C++ - como?

Sí, es porque en C++ la desasignación de recursos ocurre implícitamente para todo en la pila. Pila desenrollada = recurso destruido = destructores llamados y desde allí se pueden liberar cosas. Como Ruby no tiene destructores, no hay "hacer eso cuando todo lo demás está terminado", ya que la colección de grabación puede demorarse varios ciclos desde donde se encuentre. Tienes finalizadores pero se llaman "en el limbo" (no todo está disponible para ellos) y se les llama en GC.

Por lo tanto, si tiene algún control sobre algún recurso que se libere mejor, debe liberarlo explícitamente. De hecho, la expresión correcta para manejar este tipo de situación es

def with_shmoo 
    handle = allocate_shmoo 
    yield(handle) 
ensure 
    handle.close 
end