Class: Strelka::CMS::Page

Inherits:
Object
  • Object
show all
Extended by:
Loggability
Defined in:
lib/strelka/cms/page.rb

Overview

An abstraction of a page with markup. This class provides rendering and introspection facilities for page files on disk. A page file is a plain file with a .page extension that contains some content to be rendered for the docs site, and is of the form:

---                                     | - YAML document separator
title: My Brilliant Document            | - An optional YAML header, see
filters:                                |
  - apilinks                            |
  - links                               |
  - syntax                              |
  - editorial                           |
  - textile                             |
                                        |
---                                     | - Document separator
h1. My Brilliant Document               | - Document contents which will
                                        |   will be transformed by the
This is a document with some stuff in   |   configured filters.
it. It rules you. Like my teeth.        |
                                        |

Constant Summary collapse

PAGE_WITH_YAML_HEADER =

Pattern to match a source page with a YAML header

/
		\A---\s*$	# It should should start with three hyphens
		(.*?)		# ...have some YAML stuff
		^---\s*$	# then have another three-hyphen line,
		(.*)\Z		# then the rest of the document
/xm
DEFAULT_OPTIONS =

The default page options.

{
	'title'    => '(Untitled)',
	'filters'  => %w[strip textile],
	'tags'     => [],
}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(content, catalog = nil, options = {}) ⇒ Page

Create a new Page with the specified content and options.



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/strelka/cms/page.rb', line 106

def initialize( content, catalog=nil, options={} )
	# If there's no catalog, the options might be in its place
	if catalog.is_a?( Hash )
		options = catalog
		catalog = nil
	end

	@content  = content
	@catalog  = catalog

	options   = DEFAULT_OPTIONS.merge( options )
	@tags     = options.delete( 'tags' ) || options.delete( 'keywords' )
	@title    = options.delete( 'title' )
	@filters  = options.delete( 'filters' )
	@template = options.delete( 'template' )
	@options  = options
	@created  = Time.now

	@article  = nil

	@path     = nil
end

Instance Attribute Details

#catalogObject

The page’s catalog (if it was loaded via one)



138
139
140
# File 'lib/strelka/cms/page.rb', line 138

def catalog
  @catalog
end

#contentObject

The page’s content



135
136
137
# File 'lib/strelka/cms/page.rb', line 135

def content
  @content
end

#createdObject (readonly)

The Time the page object was instantiated



157
158
159
# File 'lib/strelka/cms/page.rb', line 157

def created
  @created
end

#filtersObject (readonly)

The Array of filter names that will be applied to the rendered output, in the order they will be applied.



145
146
147
# File 'lib/strelka/cms/page.rb', line 145

def filters
  @filters
end

#optionsObject (readonly)

Any additional options in the header



154
155
156
# File 'lib/strelka/cms/page.rb', line 154

def options
  @options
end

#pathObject

The path to the page (if loaded from a file, nil otherwise)



151
152
153
# File 'lib/strelka/cms/page.rb', line 151

def path
  @path
end

#templateObject (readonly)

The path to the Inversion template to wrap the page content in



148
149
150
# File 'lib/strelka/cms/page.rb', line 148

def template
  @template
end

#titleObject

The page title given in the page config



141
142
143
# File 'lib/strelka/cms/page.rb', line 141

def title
  @title
end

Class Method Details

.load(path, catalog = nil) ⇒ Object

Read page source from the file at the given path and return a new Page object.



64
65
66
67
68
69
70
71
72
# File 'lib/strelka/cms/page.rb', line 64

def self::load( path, catalog=nil )
	page = File.open( path, 'r', encoding: 'utf-8' ) do |io|
		self.read( io, catalog )
	end

	page.path = path

	return page
end

.parse(source, catalog = nil) ⇒ Object

Parse the given source and return a new Page object.



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/strelka/cms/page.rb', line 84

def self::parse( source, catalog=nil )
	options = nil

	if source =~ PAGE_WITH_YAML_HEADER
		self.log.debug "Parsing page with YAML header..."
		options = YAML.load( $1 )
		source = $2
		self.log.debug "  YAML header options: %p" % [ options ]
	else
		self.log.debug "Parsing page with no header."
		options = {}
	end

	return new( source, catalog, options )
end

.read(io, catalog = nil) ⇒ Object

Read page source from the given io and return a new Page object.



76
77
78
79
80
# File 'lib/strelka/cms/page.rb', line 76

def self::read( io, catalog=nil )
	source = io.read
	self.log.debug "Read %d bytes from %p" % [ source.length, io ]
	page = self.parse( source, catalog )
end

Instance Method Details

#apply_filters(content) ⇒ Object

Apply the filters corresponding to the given filter_names and return the modified content.



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/strelka/cms/page.rb', line 184

def apply_filters( content )
	filters = self.filter_objects
	self.log.debug "Applying %d filters to %d bytes of content: %p" %
		[ filters.length, content.length, content[0,100] ]

	return filters.inject( self.content.dup ) do |filtered_content, filter|
		begin
			filter.process( filtered_content, self )
		rescue => err
			self.log.error "%s while applying the %s filter: %s" %
				[ err.class.name, filter.class.factory_type, err.message ]
			self.log.debug "  " + err.backtrace.join("\n  ")
			filtered_content
		end
	end
end

#filter_objectsObject

Return the page’s filters as instances of Strelka::CMS::PageFilter.



203
204
205
206
207
208
# File 'lib/strelka/cms/page.rb', line 203

def filter_objects
	return self.filters.map do |filter_name|
		filter_name.untaint
		Strelka::CMS::PageFilter.create( filter_name )
	end
end

#first_headingObject

Return the contents of the first heading from the rendered page.



212
213
214
215
216
217
218
219
220
221
222
# File 'lib/strelka/cms/page.rb', line 212

def first_heading
	self.log.debug "Fetching page title from rendered page"
	doc = Nokogiri::HTML( self.to_s )
	if heading = doc.css( 'h1,h2,h3' ).first
		self.log.debug "  got first heading: %s" % [ heading ]
		return heading.content
	else
		self.log.debug "  couldn't find a heading."
		return nil
	end
end

#index_fieldsObject

Extract a Hash of fields to be indexed from the page and return it.



254
255
256
257
258
259
260
261
262
263
# File 'lib/strelka/cms/page.rb', line 254

def index_fields
	return {
	    :title         => self.title,
	    :first_heading => self.first_heading,
		:tags          => self.tags.join( ', ' ),
	    :content       => self.stripped_content,
		:modified      => self.modified,
		:created       => self.created,
	}
end

#inspectObject

Return a human-readable representation of the receiving object.



306
307
308
309
310
311
312
313
314
315
# File 'lib/strelka/cms/page.rb', line 306

def inspect
	return %|#<%s:%#0x %p %p [%s] {filters: %s}>| % [
		self.class.name,
		self.object_id / 2,
		self.path,
		self.title,
		self.tags.join(', '),
		self.filters.join('+'),
	]
end

#modifiedObject Also known as: date

Return the Time the page was last modified. This is derived from its ‘date’ header if it has one, or its mtime if it was loaded from a file, or its #created date if neither of those are available.



269
270
271
272
273
274
275
276
277
278
279
# File 'lib/strelka/cms/page.rb', line 269

def modified
	if headerdate = self.options['date']
		headerdate = headerdate.to_time if headerdate.respond_to?( :to_time )
		headerdate = Time.parse( headerdate ) unless headerdate.is_a?( Time )
		return headerdate
	elsif self.path
		return self.path.mtime
	else
		return self.created
	end
end

#relative_html_pathObject

Return the page’s HTML path.



176
177
178
179
# File 'lib/strelka/cms/page.rb', line 176

def relative_html_path
	pagepath = self.relative_path or return nil
	return pagepath.to_s.sub(/\.page$/, '.html')
end

#relative_pathObject

Return the page’s path relative to its catalog.



167
168
169
170
171
172
# File 'lib/strelka/cms/page.rb', line 167

def relative_path
	catalog = self.catalog or return nil
	path = self.path or return nil

	return path.relative_path_from( catalog.basedir )
end

#renderObject

Return the page rendered as HTML after applying filters and wrapping it in its configured template if it has one.



227
228
229
230
231
232
233
234
235
236
# File 'lib/strelka/cms/page.rb', line 227

def render( * )
	if path = self.template
		self.log.debug "Wrapping %p in template at %p" % [ self, path ]
		tmpl = Inversion::Template.load( path )
		tmpl.page = self
		return tmpl
	else
		return self.to_s
	end
end

#stripped_contentObject

Return the page content with tags stripped.



248
249
250
# File 'lib/strelka/cms/page.rb', line 248

def stripped_content
	return self.content.gsub( /<.*?>/, '' )
end

#summaryObject

Return a summary of the page.



284
285
286
287
# File 'lib/strelka/cms/page.rb', line 284

def summary
	summary = self.article.summarize( sentences: 1 ).first
	return summary[:sentence].strip
end

#summary_topicsObject

Return an Array of topics for the page.



291
292
293
# File 'lib/strelka/cms/page.rb', line 291

def summary_topics
	return self.article.topics
end

#tagsObject Also known as: keywords

Return the Array of tags/keywords set in the header, or derived from the content if not specified explicitly.



298
299
300
301
# File 'lib/strelka/cms/page.rb', line 298

def tags
	@tags ||= self.article.keywords
	return @tags
end

#to_sObject Also known as: to_html

Render the page content, filtering it with its configured filters. Note that this does not wrap it in its template.



241
242
243
# File 'lib/strelka/cms/page.rb', line 241

def to_s
	return self.apply_filters( self.content )
end