Class: Utopia::Content::Middleware

Inherits:
Protocol::HTTP::Middleware
  • Object
show all
Includes:
Localization::Resolver
Defined in:
lib/utopia/content/middleware.rb

Overview

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

Constant Summary collapse

CONTENT_NAMESPACE =
"content".freeze
RELATIVE_NAMESPACE =
"relative".freeze
UTOPIA_NAMESPACE =
"utopia".freeze
CONTENT_TAG_NAME =
"utopia:content".freeze

Constants included from Localization::Resolver

Localization::Resolver::CONTENT_LANGUAGE, Localization::Resolver::CONTENT_LOCATION

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, root: Utopia::default_root, namespaces: {}) ⇒ Middleware

Returns a new instance of Middleware.

Parameters:

  • root (String) (defaults to: Utopia::default_root)

    The content root where pages will be generated from.

  • namespaces (Hash<String,Library>) (defaults to: {})

    Tag namespaces for dynamic tag lookup.



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/utopia/content/middleware.rb', line 34

def initialize(app, root: Utopia::default_root, namespaces: {})
	super(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)
	
	# Resolve content relative to the logical invocation path:
	@namespaces[RELATIVE_NAMESPACE] ||= self.method(:relative_tag)
	
	# The core namespace for utopia specific functionality:
	@namespaces[UTOPIA_NAMESPACE] ||= Tags
end

Instance Attribute Details

#rootObject (readonly)

Returns the value of attribute root.



68
69
70
# File 'lib/utopia/content/middleware.rb', line 68

def root
  @root
end

Instance Method Details

#call(request) ⇒ Object

Serve or redirect filesystem-backed content, otherwise invoke the application.



129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/utopia/content/middleware.rb', line 129

def call(request)
	path = request.path
	
	# 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]
		location = path.dirname.join(index_path).to_url_path.encoded
		
		return Utopia::Response[307, {HTTP::LOCATION => location}, []]
	end
	
	response = resolve_localized(request) do |localization|
		locale = localization&.locale
		
		if link = @links.for(path, locale, fallback: false)
			self.respond(link, request, localization: localization)
		end
	end
	
	if response
		return response
	end
	
	return @delegate.call(request)
end

#fetch_template(path) ⇒ Object

Load and cache a content template.



78
79
80
81
82
# File 'lib/utopia/content/middleware.rb', line 78

def fetch_template(path)
	@template_cache.fetch_or_store(path.to_s) do
		XRB::Template.load_file(path)
	end
end

#freezeObject

Freeze this object and its internal state.



58
59
60
61
62
63
64
65
66
# File 'lib/utopia/content/middleware.rb', line 58

def freeze
	return self if frozen?
	
	@root.freeze
	@namespaces.values.each(&:freeze)
	@namespaces.freeze
	
	super
end

TODO we should remove this method and expose @links directly.



71
72
73
# File 'lib/utopia/content/middleware.rb', line 71

def links(path, **options)
	@links.index(path, **options)
end

#lookup_node(path, locale = nil) ⇒ Object

Parameters:

  • 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.



94
95
96
97
98
# File 'lib/utopia/content/middleware.rb', line 94

def lookup_node(path, locale = nil)
	resolve_link(
		@links.for(path, locale)
	)
end

#lookup_tag(qualified_name, node) ⇒ Object

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



85
86
87
88
89
90
91
# File 'lib/utopia/content/middleware.rb', line 85

def lookup_tag(qualified_name, node)
	namespace, name = XRB::Tag.split(qualified_name)
	
	if library = @namespaces[namespace]
		library.call(name, node)
	end
end

Resolve a link to an existing content node.



103
104
105
106
107
108
109
# File 'lib/utopia/content/middleware.rb', line 103

def resolve_link(link)
	if full_path = link&.full_path(@root)
		if File.exist?(full_path)
			return Node.new(self, link.path, link.path, full_path)
		end
	end
end

#respond(link, request, localization: request.localization) ⇒ Object

Respond.



116
117
118
119
120
121
122
123
124
# File 'lib/utopia/content/middleware.rb', line 116

def respond(link, request, localization: request.localization)
	if node = resolve_link(link)
		attributes = request.variables&.to_hash || {}
		
		return node.process!(request, attributes, localization: localization)
	elsif redirect_uri = link[:uri]
		return Utopia::Response[307, {HTTP::LOCATION => redirect_uri}, []]
	end
end