Robot Has No Heart

Xavier Shay blogs here

A robot that does not have a heart

YAML persistence

Fixed up my persistence code to not have to specify variables as an array, and committed my changes to CVS. Funny that on the day I got developer access to clxmlserial, I switched it out of my project in favour of YAML. Of course, I need to add a persistent attribute to that also, but it works a little different from XML:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class Object
  def self._persist klass
    begin
      @@persist
    rescue
      @@persist = {}
    end
    @@persist[klass] = [] if !@@persist[klass]
    @@persist[klass]
  end

  def self._persist_with_parent klass
    begin
      @@persist
    rescue
      @@persist = {}
    end
    p = nil
    while (!p) && klass
      p = @@persist[klass.to_s]      
      klass = klass.superclass
    end
    p
  end

  def self.persistent *var
    p = self._persist(self.to_s)
    for i in (0..var.length-1)
      var[i] = var[i].to_s
    end
    p.concat(var)
  end

  def to_yaml ( opts = {} )       
    p = self.class._persist_with_parent(self.class)
   
    if p.size > 0
      YAML::quick_emit( object_id, opts ) do |out|
        out.map( taguri, to_yaml_style ) do |map|
          p.each do |m|
            map.add( m, instance_variable_get( '@' + m ) )
          end
        end
      end
    else
      YAML::quick_emit( object_id, opts ) do |out|
        out.map( taguri, to_yaml_style ) do |map|
                                  to_yaml_properties.each do |m|
            map.add( m[1..-1], instance_variable_get( m ) )
          end
        end
      end
    end
  end

  def save(filename)
    File.open( filename + '.yaml', 'w' ) do |out|
      YAML.dump( self, out )
    end
  end
end

XML Serialization and Persistence

I’ve been using cl/xmlserial to save/load my levels. Unfortunately, it doesn’t have a good mechanism for making variables transient – it just dumps every instance variable you’ve got. UNACCEPTABLE. So i patched it a bit. Now we can do something like this:

1
2
3
4
5
6
class Actor
  include XmlSerialization
  attr_accessor :name, :location, :last_location

  persistent [:name, :location]
end

I needed a bit of metaprogramming to get that persistent attribute to work properly. It basically adds a class method ‘persistent’ to any class that includes XmlSerialization, and then provides an accessor for use in the instance_data_to_xml method:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def XmlSerialization.append_features(includingClass)
  includingClass.class_eval <<-EOS
      def self.persistent var
        var = [var] if !var.kind_of?(Array)
          
        @@persist = var
        for i in (0..@@persist.length-1)
          @@persist[i] = @@persist[i].to_s
        end
      end
  
      def self.persist
        @@persist
      end
    EOS

# Rest snipped

I’d like to get rid of the array in the call (so you can just keep adding on parameters like attr_accessor), but I’m not sure how to do it. Unfortunately I couldn’t figure out a good way to ensure that @@persist was defined for all classes, so I’ve currently just wrapped the access in a begin/rescue (thinking now … I could do this in persistent to remove the array thing … hmmmm)

1
2
3
4
5
6
7
8
9
10
11
12
module XmlSerialization
def instance_data_to_xml(element)    
  begin    
    p = self.class.persist
  rescue
    p = nil
  end

  instance_variables.each do |instanceVarName|
    if !p || p.include?(instanceVarName[1..instanceVarName.length])

# Rest snipped

One other small addition is the calling of a post_from_xml instance method (if it exists) after deserialization, to allow the object to do extra initialization, since the constructor has already been called and the instance vars are populated directly (doesn’t use accessor methods).

At some point I’ll have to write up some proper tests and submit it back to the author. I think it’s a worthwhile addition to the code, at least in idea if not implementation.

This morning I add moving platforms, coins (collectibles) that can be attached to those moving platforms, fixed up the XML code as detailed above, and fixed up the collision response to feel a bit nicer.

Link of the day goes to DWEMTHY_S ARRAY, a fun ruby adventure. I’ve linked to the poignant guide before, but I feel it’s worth another mention.

A pretty flower Another pretty flower