Serilisation, Preloading and SimpleCov

Alisdair McDiarmid has a great write up on solving the problems of serialising and deserializing custom objects to yaml in rails whilst in development and getting around the ArgumentError: undefined class/module UnknownClass error that are thrown due to classes not being loaded.

The solution is to eager load classes and reload them each request to make sure that when YAML deserialization occurs the classes are present as usually this process doesnt trigger the default lazy loading of classes in development.

The solution looks like thisSource
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Eager load all value objects, as they may be instantiated from
# YAML before the symbol is referenced
config.before_initialize do |app|
    app.config.paths.add 'app/values', :eager_load => true
end

# Reload cached/serialized classes before every request (in development
# mode) or on startup (in production mode)
config.to_prepare do
    Dir[ File.expand_path(Rails.root.join("app/values/*.rb")) ].each do |file|
        require_dependency file
    end
    require_dependency 'article_cache'
end

Testing, SimpleCov and Missed Coverage

This solution was great, it solves the loading issue but during development I noticed the lines of code covered by SimpleCov was reduced, looking into know issues on the SimpleCov github one explained the reason why files where missing. The above solution of preloading the classes for deserialization means that the files are already loaded in memory before SimpleCov initialises and thus cant be tracked by Ruby’s built in coverage mechanism.

Solution

As of rails 3.1, objects can be serialised to JSON as well as YAML. While it requires the addition of two methods to the class in question it does mean the class doesn’t need to be preloaded and thus allows the code coverage to be tracked.

Adding the following two methods in your class and the additional JSON option to serialize in the model file allows for objects to be reinitialised based on the stored data parameters saved as JSON in the database.

in the model file
1
serialize :column_name, JSON
Serializing and Deserializing with JSONsource
1
2
3
4
5
6
7
def self.json_create(o)
  new(*o['data'])
end

def to_json(*a)
  { 'json_class' => self.class.name, 'data' => [id, name, next_step] }.to_json(*a)
end

Comments