class Ro::Node

Public Class Methods

new(path) click to toggle source
# File lib/ro/node.rb, line 9
def initialize(path)
  @path = Ro.realpath(path.to_s)
  @id   = File.basename(@path)
  @slug = Slug.for(@id)
  @type = File.basename(File.dirname(@path))
  @root = Ro::Root.new(File.dirname(File.dirname(@path)))
  @loaded = false
  @loading = false
  @attributes = Map.new
  @fields = Map.new
end

Public Instance Methods

==(other) click to toggle source
# File lib/ro/node.rb, line 61
def ==(other)
  attributes == other.attributes
end
[](*args) click to toggle source
# File lib/ro/node.rb, line 85
def [](*args)
  attributes.get(*args)
end
_attributes_yml() click to toggle source
# File lib/ro/node.rb, line 474
def _attributes_yml
  File.join(@path, 'attributes.yml')
end
_binding() click to toggle source
# File lib/ro/node.rb, line 441
def _binding
  Kernel.binding
end
_cache_key() click to toggle source
# File lib/ro/node.rb, line 445
def _cache_key
  glob = File.join(@path, '**/**')

  entries = []

  Dir.glob(glob) do |entry|
    stat =
      begin
        File.stat(entry)
      rescue
        next
      end

    timestamp = [stat.ctime, stat.mtime].max
    relative_path = Ro.relative_path(entry, :to => @path)
    entries.push([relative_path, timestamp.iso8601(2)]) 
  end

  fingerprint = entries.map{|pair| pair.join('@')}.join(', ')

  md5 = Ro.md5(fingerprint)

  [@path, md5]
end
_id() click to toggle source
# File lib/ro/node.rb, line 25
def _id
  @id
end
_load(&block) click to toggle source
# File lib/ro/node.rb, line 327
def _load(&block)
  unless @loaded
    if @loading
      return(block ? block.call : :loading)
    end

    @loading = true
    @loaded = _load_from_cache_or_disk
    @loading = false
  end

  block ? block.call : @loaded
end
_load_assets() click to toggle source
# File lib/ro/node.rb, line 423
def _load_assets
  glob = File.join(@path, 'assets/**/**')
  node = self

  Dir.glob(glob) do |path|
    next if test(?d, path)

    relative_path = Ro.relative_path(path, :to => "#{ @path }/assets")

    url = url_for(relative_path)
    key = relative_path.split('/')

    key.unshift('urls')

    @attributes.set(key => url)
  end
end
_load_attribute_files() click to toggle source
# File lib/ro/node.rb, line 384
def _load_attribute_files
  glob = File.join(@path, '**/**')
  node = self

  Dir.glob(glob) do |path|
    next if test(?d, path)

    basename = File.basename(path)
    next if basename == 'attributes.yml'

    relative_path = Ro.relative_path(path, :to => @path)
    next if relative_path =~ /^assets\//

    key = relative_path.split('.', 2).first.split('/')

    html = Ro.render(path, node)
    html = Ro.expand_asset_urls(html, node)

    @attributes.set(key => html)
  end
end
_load_attributes_yml() click to toggle source
# File lib/ro/node.rb, line 368
def _load_attributes_yml
  if test(?s, _attributes_yml)
    buf = IO.binread(_attributes_yml)
    data = YAML.load(buf)
    data = data.is_a?(Hash) ? data : {'_' => data}

    @attributes.update(data)

    %w( assets ).each do |key|
      raise ArgumentError.new("attributes.yml may not contain the key '#{ key }'") if @attributes.has_key?(key)
    end

    @attributes
  end
end
_load_from_cache() click to toggle source
# File lib/ro/node.rb, line 470
def _load_from_cache
  false
end
_load_from_cache_or_disk() click to toggle source
# File lib/ro/node.rb, line 341
def _load_from_cache_or_disk
  cache_key = _cache_key

  cached = Ro.cache.read(cache_key)

  if cached
    Ro.log "loading #{ identifier } from cache"

    @attributes = Map.new.update(cached)

    return :cache
  else
    Ro.log "loading #{ identifier } from disk"

    @attributes = Map.new

    _load_attributes_yml
    _load_attribute_files
    _load_sources
    _load_assets

    Ro.cache.write(cache_key, @attributes)

    return :disk
  end
end
_load_sources() click to toggle source
# File lib/ro/node.rb, line 406
def _load_sources
  glob = File.join(@path, 'assets/source/*')
  node = self

  Dir.glob(glob) do |path|
    next if test(?d, path)

    basename = File.basename(path)
    key, ext = basename.split('.', 2)

    next if basename == 'attributes.yml'

    value = Ro.render_source(path, node)
    @attributes.set([:assets, :source, basename] => value)
  end
end
_path() click to toggle source
# File lib/ro/node.rb, line 41
def _path
  @path
end
_slug() click to toggle source
# File lib/ro/node.rb, line 49
def _slug
  @slug
end
_type() click to toggle source
# File lib/ro/node.rb, line 33
def _type
  @type
end
asset_dir() click to toggle source
# File lib/ro/node.rb, line 93
def asset_dir
  File.join(path, 'assets')
end
asset_for(*args, &block) click to toggle source
# File lib/ro/node.rb, line 114
def asset_for(*args, &block)
  options = Map.options_for!(args)

  path_info = Ro.relative_path_for(args)

  path = File.join(@path.to_s, 'assets', path_info)

  glob = path_info.gsub(/[_-]/, '[_-]')

  globs = 
    [
      File.join(@path.to_s, 'assets', "#{ glob }"),
      File.join(@path.to_s, 'assets', "#{ glob }*"),
      File.join(@path.to_s, 'assets', "**/#{ glob }*")
    ]

  candidates = globs.map{|glob| Dir.glob(glob, ::File::FNM_CASEFOLD)}.flatten.compact.uniq

  case candidates.size
    when 0
      raise ArgumentError.new("no asset matching #{ globs.inspect }")
    else
      path = candidates.first
      name = path.sub(asset_dir + "/", "")
      path_info = path.gsub(/^#{ Regexp.escape(Ro.root) }/, '')
      url = File.join(Ro.route, path_info)
  end

  Asset.new(name, :path => path, :url => url)
end
asset_for?(*args, &block) click to toggle source
# File lib/ro/node.rb, line 145
def asset_for?(*args, &block)
  begin
    asset_for(*args, &block)
  rescue
    nil
  end
end
asset_path(*args, &block) click to toggle source
# File lib/ro/node.rb, line 89
def asset_path(*args, &block)
  File.join(relative_path, 'assets')
end
asset_paths() click to toggle source
# File lib/ro/node.rb, line 97
def asset_paths
  Dir.glob("#{ asset_dir }/**/**").select{|entry| test(?f, entry)}
end
asset_urls() click to toggle source
# File lib/ro/node.rb, line 110
def asset_urls
  assets.map(&:url)
end
assets() click to toggle source
# File lib/ro/node.rb, line 101
def assets
  asset_paths.map do |path|
    name = path.sub(asset_dir + "/", "")
    path_info = path.gsub(/^#{ Regexp.escape(Ro.root) }/, '')
    url = File.join(Ro.route, path_info)
    Asset.new(name, :path => path, :url => url)
  end
end
attributes() click to toggle source
# File lib/ro/node.rb, line 263
def attributes
  _load{ @attributes }
end
attributes=(attributes) click to toggle source
# File lib/ro/node.rb, line 267
def attributes=(attributes)
  @attributes = attributes
end
basename() click to toggle source
# File lib/ro/node.rb, line 73
def basename
  name
end
get(*args) click to toggle source
# File lib/ro/node.rb, line 81
def get(*args)
  attributes.get(*args)
end
hash() click to toggle source
# File lib/ro/node.rb, line 57
def hash
  identifier.hash
end
id() click to toggle source
# File lib/ro/node.rb, line 21
def id
  @id
end
identifier() click to toggle source
# File lib/ro/node.rb, line 53
def identifier
  "#{ _type }/#{ _id }"
end
inspect() click to toggle source
# File lib/ro/node.rb, line 65
def inspect
  identifier
end
instance_eval(*args, &block) click to toggle source
Calls superclass method
# File lib/ro/node.rb, line 271
def instance_eval(*args, &block)
  _load{ super }
end
load!(&block) click to toggle source
# File lib/ro/node.rb, line 323
def load!(&block)
  _load(&block)
end
loaded() click to toggle source
# File lib/ro/node.rb, line 319
def loaded
  attributes
end
method_missing(method, *args, &block) click to toggle source
Calls superclass method
# File lib/ro/node.rb, line 241
def method_missing(method, *args, &block)
  super if method.to_s == 'to_ary'

  Ro.log "Ro::Node(#{ identifier })#method_missing(#{ method.inspect }, #{ args.inspect })"

  key = method.to_s

  if @attributes.has_key?(key)
    return @attributes[key]
  end

  _load do
    return(
      if @attributes.has_key?(key)
        @attributes[key]
      else
        super
      end
    )
  end
end
node() click to toggle source
# File lib/ro/node.rb, line 77
def node
  self
end
path() click to toggle source
# File lib/ro/node.rb, line 37
def path
  attributes[:path] || @path
end
relative_path() click to toggle source
# File lib/ro/node.rb, line 236
def relative_path
  re = /^#{ Regexp.escape(Ro.root) }/
  @path.to_s.gsub(re, '')
end
reload(&block) click to toggle source
# File lib/ro/node.rb, line 314
def reload(&block)
  @loaded = false
  _load(&block)
end
route(*args) click to toggle source
# File lib/ro/node.rb, line 231
def route(*args)
  path_info = Ro.absolute_path_for(Ro.route, relative_path)
  [Ro.asset_host, path_info].compact.join('/')
end
slug() click to toggle source
# File lib/ro/node.rb, line 45
def slug
  attributes[:slug] || @slug
end
source_for(*args) click to toggle source
# File lib/ro/node.rb, line 226
def source_for(*args)
  key = Ro.relative_path_for(:assets, :source, args).split('/')
  get(key)
end
to_s() click to toggle source
# File lib/ro/node.rb, line 69
def to_s
  inspect
end
type() click to toggle source
# File lib/ro/node.rb, line 29
def type
  attributes[:type] || @type
end
url_for(*args, &block) click to toggle source
# File lib/ro/node.rb, line 191
def url_for(*args, &block)
  options = Map.options_for!(args)

  opts = Map.new

  opts[:timestamp] = options.delete(:timestamp)

  args.push(options)

  asset = asset_for(*args, &block)

  if ts = opts.delete(:timestamp)
    if ts == true
      opts[:_] = File.stat(asset.path).mtime.utc.to_i
    else
      opts[:_] = ts
    end
  end

  if opts.empty?
    asset.url
  else
    query_string = Ro.query_string_for(opts)
    "#{ asset.url }?#{ query_string }"
  end
end
url_for?(*args, &block) click to toggle source
# File lib/ro/node.rb, line 218
def url_for?(*args, &block)
  begin
    url_for(*args, &block)
  rescue
    nil
  end
end