class TheFox::Timr::Model::BasicModel

Basic Class

Models hold data and can be stored to YAML files. Except for [Tracks](Track). 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](ruby-doc.org/stdlib-2.4.1/libdoc/digest/rdoc/Digest/SHA1.html) 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