class Utopia::Content

A middleware which serves dynamically generated content based on markup files.

Constants

CACHE_CONTROL
CONTENT_NAMESPACE
CONTENT_TAG_NAME
CONTENT_TYPE
DEFERRED_TAG_NAME
EXPIRES

Compatibility with older versions of rack:

INDEX
NO_CACHE
Tag
UTOPIA_NAMESPACE
XNODE_EXTENSION

The file extension for markup nodes on disk.

Attributes

root[R]

Public Class Methods

new(app, root: Utopia::default_root, namespaces: {}) click to toggle source

@param root [String] The content root where pages will be generated from. @param namespaces [Hash<String,Library>] Tag namespaces for dynamic tag lookup.

# File lib/utopia/content.rb, line 45
def initialize(app, root: Utopia::default_root, namespaces: {})
        @app = app
        @root = root
        
        @template_cache = Concurrent::Map.new
        @node_cache = Concurrent::Map.new
        
        @links = Links.new(@root)
        
        @namespaces = namespaces
        
        # Default content namespace for dynamic path based lookup:
        @namespaces[CONTENT_NAMESPACE] ||= self.method(:content_tag)
        
        # The core namespace for utopia specific functionality:
        @namespaces[UTOPIA_NAMESPACE] ||= Tags
end

Public Instance Methods

call(env) click to toggle source
# File lib/utopia/content.rb, line 110
def call(env)
        request = Rack::Request.new(env)
        path = Path.create(request.path_info)
        
        # Check if the request is to a non-specific index. This only works for requests with a given name:
        basename = path.basename
        directory_path = File.join(@root, path.dirname.components, basename)
        
        # If the request for /foo/bar is actually a directory, rewrite it to /foo/bar/index:
        if File.directory? directory_path
                index_path = [basename, INDEX]
                
                return [307, {HTTP::LOCATION => path.dirname.join(index_path).to_s}, []]
        end
        
        locale = env[Localization::CURRENT_LOCALE_KEY]
        if link = @links.for(path, locale)
                if node = resolve_link(link)
                        attributes = request.env.fetch(VARIABLES_KEY, {}).to_hash
                        
                        return node.process!(request, attributes)
                elsif redirect_uri = link[:uri]
                        return [307, {HTTP::LOCATION => redirect_uri}, []]
                end
        end
        
        return @app.call(env)
end
fetch_template(path) click to toggle source
# File lib/utopia/content.rb, line 80
def fetch_template(path)
        @template_cache.fetch_or_store(path.to_s) do
                Trenni::MarkupTemplate.load_file(path)
        end
end
freeze() click to toggle source
Calls superclass method
# File lib/utopia/content.rb, line 63
def freeze
        return self if frozen?
        
        @root.freeze
        @namespaces.values.each(&:freeze)
        @namespaces.freeze
        
        super
end
lookup_node(path, locale = nil) click to toggle source

@param path [Path] the request path is an absolute uri path, e.g. `/foo/bar`. If an xnode file exists on disk for this exact path, it is instantiated, otherwise nil.

# File lib/utopia/content.rb, line 96
def lookup_node(path, locale = nil)
        resolve_link(
                @links.for(path, locale)
        )
end
lookup_tag(qualified_name, node) click to toggle source

Look up a named tag such as `<entry />` or `<content:page>…`

# File lib/utopia/content.rb, line 87
def lookup_tag(qualified_name, node)
        namespace, name = Trenni::Tag.split(qualified_name)
        
        if library = @namespaces[namespace]
                library.call(name, node)
        end
end

Private Instance Methods

content_tag(name, node) click to toggle source
# File lib/utopia/content.rb, line 177
def content_tag(name, node)
        full_path = node.parent_path + name
        
        name = full_path.pop
        
        # If the current node is called 'foo', we can't lookup 'foo' in the current directory or we will have infinite recursion.
        while full_path.last == name
                full_path.pop
        end
        
        cache_key = full_path + name
        
        @node_cache.fetch_or_store(cache_key) do
                lookup_content(name, full_path)
        end
end
lookup_content(name, parent_path) click to toggle source
# File lib/utopia/content.rb, line 141
def lookup_content(name, parent_path)
        if String === name && name.index('/')
                name = Path.create(name)
        end
        
        if Path === name
                name = parent_path + name
                name_path = name.components.dup
                name_path[-1] += XNODE_EXTENSION
        else
                name_path = name + XNODE_EXTENSION
        end
        
        components = parent_path.components.dup
        
        while components.any?
                tag_path = File.join(@root, components, name_path)
                
                if File.exist? tag_path
                        return Node.new(self, Path[components] + name, parent_path + name, tag_path)
                end
                
                if String === name_path
                        tag_path = File.join(@root, components, '_' + name_path)
                        
                        if File.exist? tag_path
                                return Node.new(self, Path[components] + name, parent_path + name, tag_path)
                        end
                end
                
                components.pop
        end
        
        return nil
end