class FoxPage::Model

Constants

DEFAULT_STORAGE_TYPE
VALID_STORAGE_TYPES

Public Class Methods

[](storage_type = DEFAULT_STORAGE_TYPE, storage_type_opts = {}) click to toggle source
# File lib/fox_page/model.rb, line 20
def self.[](storage_type = DEFAULT_STORAGE_TYPE, storage_type_opts = {})
  unless VALID_STORAGE_TYPES.include?(storage_type)
    raise ArgumentError,
          "type must be one of #{VALID_STORAGE_TYPES.join(',')}"
  end

  @__tmp_storage_type = storage_type
  @__tmp_storage_type_opts = storage_type_opts

  self
end
all() click to toggle source
# File lib/fox_page/model.rb, line 133
def self.all
  @__data
end
belongs_to(what, referenced_by: :name) click to toggle source

e.g. in Project: belongs_to :project_category, referenced_by: :name

# File lib/fox_page/model.rb, line 96
def self.belongs_to(what, referenced_by: :name)
  association_class = Kernel.const_get(what.to_s.camelize)

  define_method("__#{what}_value") do
    @__ostruct[what]
  end

  define_method(what) do
    association_class.find(referenced_by => public_send("__#{what}_value"))
  end
end
def_parser(attribute, &parser) click to toggle source

define a parser method for attributes @example

require "time"

class BlogPost < FoxPage::Model[:dir]
  def_parser :date do |date|
    Time.parse(date)
  end
end

# in the blog posts' front matter:
# ---
# title: foo
# date: Sat 13 Jul 13:38:43 CEST 2019
# ---

# then, anywhere else:
blog_post.date        # => 2019-07-13 13:38:43 +0200
blog_post.date.class  # => Time
# File lib/fox_page/model.rb, line 127
def self.def_parser(attribute, &parser)
  define_method(attribute) do
    parser.call(@__ostruct[attribute])
  end
end
each(&block) click to toggle source
# File lib/fox_page/model.rb, line 137
def self.each(&block)
  @__data.each(&block)
end
find(filter) click to toggle source
# File lib/fox_page/model.rb, line 141
def self.find(filter)
  @__data.find do |object|
    filter.all? do |key, value|
      value_key_name = "__#{key}_value"
      key = value_key_name if object.respond_to?(value_key_name)

      object.public_send(key) == value
    end
  end
end
has_many(what, referenced_by: nil) click to toggle source

e.g. in ProjectCategory: has_many :projects, referenced_by: nil

nil = default of self.class.name underscored

# File lib/fox_page/model.rb, line 86
def self.has_many(what, referenced_by: nil) # rubocop:disable Naming/PredicateName, Metrics/LineLength
  referenced_by ||= name.to_s.underscore
  association_class = Kernel.const_get(what.to_s.singularize.camelize)

  define_method(what.to_s) do
    association_class.where(referenced_by => name.to_s)
  end
end
inherited(subclass) click to toggle source
# File lib/fox_page/model.rb, line 32
def self.inherited(subclass)
  set_ivar_if_unset subclass, :storage_type, DEFAULT_STORAGE_TYPE
  set_ivar_if_unset subclass, :storage_type_opts, {}

  subclass.reload_all(@__app)
end
new(ostruct) click to toggle source
# File lib/fox_page/model.rb, line 169
def initialize(ostruct)
  @__ostruct = ostruct
end
reload_all(app) click to toggle source
# File lib/fox_page/model.rb, line 51
def self.reload_all(app)
  data_name = name.to_s.underscore.pluralize
  puts "MODEL\t#{data_name}"

  case @__storage_type
  when :yaml
    @__data = YAML.load_file(
      app.root.join("data", data_name + ".yml")
    ).to_deep_ostruct.map { |ostruct| new(ostruct) }
  when :dir
    default_opts = { extension: :md }
    opts = default_opts.merge(@__storage_type_opts)
    base_data_dir = app.root.join("data", data_name)
    files = Dir[File.join(base_data_dir, "**", "*.#{opts.fetch(:extension)}")]

    @__data = files.map do |fn|
      id = File.join(File.dirname(fn).sub(/\A#{base_data_dir}/, ""),
                     File.basename(fn, ".#{opts.fetch(:extension)}"))
               .sub(/\A(?:#{File::SEPARATOR})/, "")
      content = IO.read(fn)

      front_matter = {}
      if content =~ /\A(---\n.*\n)^(?:---)\s*$\n?/m
        content = Regexp.last_match.post_match
        front_matter = YAML.safe_load(Regexp.last_match[1], [Time])
      end

      new front_matter.merge(id: id, content: content).to_deep_ostruct
    end
  end
end
where(filter) click to toggle source
# File lib/fox_page/model.rb, line 152
def self.where(filter)
  @__data.select do |object|
    filter.all? do |key, value|
      value_key_name = "__#{key}_value"
      key = value_key_name if object.respond_to?(value_key_name)

      returned_value = object.public_send(key)
      case returned_value
      when Array
        returned_value.include?(value)
      else
        returned_value == value
      end
    end
  end
end

Private Class Methods

set_ivar_if_unset(subclass, ivar, default) click to toggle source
# File lib/fox_page/model.rb, line 39
def self.set_ivar_if_unset(subclass, ivar, default)
  target_ivar = :"@__#{ivar}"
  return if subclass.instance_variable_get(target_ivar)

  tmp_ivar = :"@__tmp_#{ivar}"
  value = instance_variable_get tmp_ivar
  instance_variable_set tmp_ivar, nil

  subclass.instance_variable_set target_ivar, value || default
end

Public Instance Methods

method_missing(method, *args, &block) click to toggle source
Calls superclass method
# File lib/fox_page/model.rb, line 173
def method_missing(method, *args, &block)
  hash_ostruct = @__ostruct.to_h
  return super unless hash_ostruct.key?(method.to_sym)

  hash_ostruct[method.to_sym]
end
respond_to_missing?(method, *) click to toggle source
Calls superclass method
# File lib/fox_page/model.rb, line 180
def respond_to_missing?(method, *)
  return true if @__ostruct.to_h.key?(method.to_sym)

  super
end