miércoles, 11 de septiembre de 2013

La reproducción de los objetos

La reproducción de los objetos


En el colegio me enseñaron que “los seres vivos nacen, crecen, se reproducen y mueren”. Por lo visto los HTMLDoc son seres vivos. Nacen cuando los crea una llamada a “new”. Crecen cuando les añadimos código HTML con cosas como “insertAfter” o “insertBefore”. Mueren cuando se les hace una llamada a “clear” cuando eliminamos nodos.

Y se reproducen, porque un objeto puede generar otros de su misma clase mediante “getElement”. Que es el único método que me queda por pagaros. Así que ahí va:

 
def getElement(start)
  if not (0..(@nodes.length - 1)).include?(start)
   raise 'Element out of range'
  end

  ending = findEndOfTag(start)

  new_element = HtmlDoc.new
  new_element.type = :node
  new_element.nodes = @nodes
  new_element.starts_at = start
  new_element.ends_at = ending
  new_element.document = typeOf == :document ? self : @document

  new_element.document.nodes_created.push(new_element)
  return new_element
end

 
Le pasamos un número con la posición en que comienza el código HTML del objeto que queremos retornar y “getElement” hace el resto. Primero comprobamos que la posición que nos dan es correcta. Si todo va bien, seguiremos con

ending = findEndOfTag(start)

… que asigna a “ending” la posición en que acaba el código HTML del objeto. Y para ello llamamos al método “findEndOfTag”... que aún no hemos definido. ¡Jolín! ¡Ahora que estábamos casi acabando y nos aparece otro método que dejaremos para más adelante!

Tranquis, que tampoco es para tanto, y sigamos. Creamos un nuevo objeto de la clase HTMLDoc para representar el elemento:

new_element = HtmlDoc.new

… e inicializamos sus valores, par decir que es un nodo (no un documento completo), que comparte los nodos de su padre (que en algo se tenía que parecer a él), indicar donde comienza y acaba y señalar al documento al que pertenece:

new_element.type = :node
new_element.nodes = @nodes
new_element.starts_at = start
new_element.ends_at = ending
new_element.document = typeOf == :document ? self : @document

Fijaos en que las invocaciones a “self” o “typeOf” se refieren al objeto padre, sobre el que estamos invocando el método, no al hijo que estamos creando. Y recordad que podemos modificar un objeto desde otro porque definimos los accesores como “protegidos”.

Y otra cosa: en la asignación "new_element.nodes = @nodes" hay que tener en cuenta que cuando asignamos a una variable un objeto en Ruby NO creamos una nueva copia del objeto. De modo que el atributo @nodes del nodo que acabamos de crear y el de su padre harán referencia a un mismo objeto.

Como prueba valga el siguiente ejemplo:

$ irb
irb(main):001:0> a = [1,2,3]
=> [1, 2, 3]
irb(main):002:0> b = a
=> [1, 2, 3]
irb(main):003:0> a[2] = 1     # Modificamos el array original
=> 1
irb(main):004:0> b
=> [1, 2, 1]
irb(main):005:0> # ... y b también ha sido modificado

Para crear copias de un objeto tenemos el método clone

irb(main):006:0* c = a.clone    # c es ahora una copia de a
=> [1, 2, 1]
irb(main):007:0> a[1]=100    # Si modificamos a...
=> 100
irb(main):008:0> c 
=> [1, 2, 1]
irb(main):009:0>  # c sigue igual

Sigamos. Al documento original hay que decirle que hemos creado un nuevo elemento a partir de él, para que vaya llevando la cuenta:

new_element.document.nodes_created.push(new_element)

Y... listo. Sólo queda retornar el nuevo elemento creado:

 
return new_element

 

No hay comentarios:

Publicar un comentario