class TheFox::Timr::Model::BasicModel

Basic Class

Models hold data and can be stored to YAML files. Except for Tracks. Tracks are stored to a Task file.

Attributes

file_path[RW]

Path to file.

has_changed[RW]

When calling save_to_file, it will only write the file if @has_changed is true.

Public Class Methods

create_path_by_id(base_path, id) click to toggle source

Converts an SHA1 Hash into a path.

Function IO:

3dd50a2b50eabc84022a23ad2c06d9bb6396f978          <- input
3d/d50a2b50eabc84022a23ad2c06d9bb6396f978
3d/d50a2b50eabc84022a23ad2c06d9bb6396f978
3d/d5/0a2b50eabc84022a23ad2c06d9bb6396f978
3d/d5/0a/2b50eabc84022a23ad2c06d9bb6396f978
3d/d5/0a/2b50eabc84022a23ad2c06d9bb6396f978.yml   <- output
# File lib/timr/model/basic_model.rb, line 187
def create_path_by_id(base_path, id)
    if base_path.is_a?(String)
        base_path = Pathname.new(base_path)
    end
    unless id.is_a?(String)
        raise IdError, "ID is not a String. #{id.class} given."
    end
    if id.length <= 6
        raise IdError, "ID is too short for creating a path. Minimum length: 7"
    end
    
    path_s = '%s/%s/%s/%s.yml' % [id[0, 2], id[2, 2], id[4, 2], id[6..-1]]
    Pathname.new(path_s).expand_path(base_path)
end
find_file_by_id(base_path, id) click to toggle source

Opposite of get_id_from_path.

# File lib/timr/model/basic_model.rb, line 215
def find_file_by_id(base_path, id)
    if base_path.is_a?(String)
        base_path = Pathname.new(base_path)
    end
    unless id.is_a?(String)
        raise IdError, "ID '#{id}' is not a String. #{id.class} given."
    end
    if id.length < 4
        raise IdError, "ID '#{id}' is too short for find. Minimum length: 4"
    end
    
    if id.length == 40
        path = create_path_by_id(base_path, id)
    else
        # 12/34
        search_path = '%s/%s' % [id[0, 2], id[2, 2]]
        if id.length >= 5
            # 12/34/5
            search_path << '/' << id[4]
            
            if id.length >= 6
                # 12/34/56
                search_path << id[5]
                
                if id.length >= 7
                    # 12/34/56/xxxxxx
                    search_path << '/' << id[6..-1]
                end
            end
        end
        search_path << '*'
        
        path = nil
        base_path.find.each do |file|
            # Filter all directories.
            unless file.file?
                next
            end
            
            # Filter all non-yaml files.
            unless file.basename.fnmatch('*.yml')
                next
            end
            
            rel_path = file.relative_path_from(base_path)
            unless rel_path.fnmatch(search_path)
                next
            end
            
            if path
                raise ModelError.new(id), "ID '#{id}' is not a unique identifier."
            else
                path = file
                
                # Do not break the loop here.
                # Iterate all keys to make sure the ID is unique.
            end
        end
    end
    
    if path && path.exist?
        return path
    end
    raise ModelError, "Could not find a file for ID '#{id}' at #{base_path}."
end
get_id_from_path(base_path, path) click to toggle source

Opposite of find_file_by_id.

Function IO:

3d/d5/0a/2b50eabc84022a23ad2c06d9bb6396f978.yml  <- input
3dd50a2b50eabc84022a23ad2c06d9bb6396f978         <- output
# File lib/timr/model/basic_model.rb, line 210
def get_id_from_path(base_path, path)
    path.relative_path_from(base_path).to_s.gsub('/', '')[0..-5]
end
new() click to toggle source
# File lib/timr/model/basic_model.rb, line 27
def initialize
    # id 40 chars long.
    id = Digest::SHA1.hexdigest(UUID.new.generate)
    
    @meta = {
        'id' => id,
        'short_id' => id[0, 6],
        'created' => Time.now.utc.strftime(MODEL_DATETIME_FORMAT),
        'modified' => Time.now.utc.strftime(MODEL_DATETIME_FORMAT),
    }
    @data = nil
    @has_changed = false
    @file_path = nil
end

Public Instance Methods

changed() click to toggle source

Mark an object as changed. Only changed objects are stored to files on #save_to_file().

# File lib/timr/model/basic_model.rb, line 70
def changed
    @meta['modified'] = Time.now.utc.strftime(MODEL_DATETIME_FORMAT)
    @has_changed = true
end
created=(created) click to toggle source

Set created Time String.

# File lib/timr/model/basic_model.rb, line 60
def created=(created)
    @meta['created'] = created
end
delete_file(path = nil) click to toggle source

Delete the file.

# File lib/timr/model/basic_model.rb, line 161
def delete_file(path = nil)
    path ||= @file_path
    if path.nil?
        raise ModelError, 'Path cannot be nil.'
    end
    
    path.delete
end
id() click to toggle source

Get ID.

# File lib/timr/model/basic_model.rb, line 50
def id
    @meta['id']
end
id=(id) click to toggle source

Set ID.

# File lib/timr/model/basic_model.rb, line 43
def id=(id)
    @meta['id'] = id
    
    changed
end
load_from_file(path = nil) click to toggle source

Load an entity into the current instance.

If path is not given @file_path will be taken. If @file_path is also not given throw ModelError exception.

# File lib/timr/model/basic_model.rb, line 78
def load_from_file(path = nil)
    load = pre_load_from_file
    
    if path.nil?
        path = @file_path
        if path.nil?
            raise ModelError, 'Path cannot be nil.'
        end
    else
        @file_path = path
    end
    
    if load
        content = YAML::load_file(path)
        @meta = content['meta']
        @data = content['data']
        @has_changed = false
    end
    
    post_load_from_file
end
modified=(modified) click to toggle source

Set modified Time String.

# File lib/timr/model/basic_model.rb, line 65
def modified=(modified)
    @meta['modified'] = modified
end
post_load_from_file() click to toggle source

Hook function for subclass called after load_from_file payload was executed.

Subclasses can access @meta and @data to write values into instance variables, or to convert data to other formats.

See pre_save_to_file.

# File lib/timr/model/basic_model.rb, line 110
def post_load_from_file
end
post_save_to_file() click to toggle source

Hook function for subclass called after save_to_file payload was executed.

# File lib/timr/model/basic_model.rb, line 157
def post_save_to_file
end
pre_load_from_file() click to toggle source

Hook function for subclass called before load_from_file payload will be executed.

# File lib/timr/model/basic_model.rb, line 101
def pre_load_from_file
    true
end
pre_save_to_file() click to toggle source

Hook function for subclass called before save_to_file payload will be executed.

Subclasses can modify @meta and @data in this method to store more informations to the meta Hash, or to convert data to other formats that can be better written to file.

For example, it's probably better to convert a floating point number to a %.2f formatted String and convert it back to float on post_load_from_file.
See Floating Point Math 0.30000000000000004.com.

# File lib/timr/model/basic_model.rb, line 152
def pre_save_to_file
    true
end
save_to_file(path = nil, force = false) click to toggle source

Save an entity to a YAML file.

# File lib/timr/model/basic_model.rb, line 114
def save_to_file(path = nil, force = false)
    store = pre_save_to_file
    
    if path.nil?
        path = @file_path
        if path.nil?
            raise ModelError, 'Path cannot be nil.'
        end
    else
        @file_path = path
    end
    
    if force || (store && @has_changed)
        @meta['modified'] = Time.now.utc.strftime(MODEL_DATETIME_FORMAT)
        
        # Create underlying directories.
        unless path.dirname.exist?
            path.dirname.mkpath
        end
        
        store = YAML::Store.new(path)
        store.transaction do
            store['meta'] = @meta
            store['data'] = @data
        end
        
        @has_changed = false
    end
    
    post_save_to_file
end
short_id() click to toggle source

Get Short ID. Only 6 chars long.

# File lib/timr/model/basic_model.rb, line 55
def short_id
    @meta['id'][0, 6]
end