sitepress-core 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/sitepress/asset.rb +91 -0
- data/lib/sitepress/directory_collection.rb +24 -0
- data/lib/sitepress/extensions/layouts.rb +27 -0
- data/lib/sitepress/extensions/proc_manipulator.rb +19 -0
- data/lib/sitepress/formats.rb +45 -0
- data/lib/sitepress/frontmatter.rb +22 -0
- data/lib/sitepress/resource.rb +92 -0
- data/lib/sitepress/resource_collection.rb +33 -0
- data/lib/sitepress/resources_node.rb +135 -0
- data/lib/sitepress/resources_pipeline.rb +10 -0
- data/lib/sitepress/site.rb +101 -0
- data/lib/sitepress-core.rb +19 -0
- data/sitepress-core.gemspec +25 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 12fe69efabda302c8e2d3b857fe0b971cd8b8e61
|
4
|
+
data.tar.gz: 72e708c8b2aa30f085a21ba63f410a93c1f5faef
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 509a4ae3e5056c0e39468126130e7e33df92bb15ec93a6f37e3dfe10fb93a91951b1c6ed25adb99d3b0c11a934396d8e180f069efae2d22d23c1cee12140eacb
|
7
|
+
data.tar.gz: 8a47892a53c31f52c528adb022878f80719ce17a8b07c12afdda9b788d4bbd0ecae66c565bed4f70e910fe0dfde31d35a9dc86f23c6535aedfcce07520979c6c
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require "mime/types"
|
2
|
+
require "forwardable"
|
3
|
+
require "pathname"
|
4
|
+
|
5
|
+
module Sitepress
|
6
|
+
# Represents a file on a web server that may be parsed to extract
|
7
|
+
# frontmatter or be renderable via a template. Multiple resources
|
8
|
+
# may point to the same asset. Properties of an asset should be mutable.
|
9
|
+
# The Resource object is immutable and may be modified by the Resources proxy.
|
10
|
+
class Asset
|
11
|
+
# If we can't resolve a mime type for the resource, we'll fall
|
12
|
+
# back to this binary octet-stream type so the client can download
|
13
|
+
# the resource and figure out what to do with it.
|
14
|
+
DEFAULT_MIME_TYPE = MIME::Types["application/octet-stream"].first
|
15
|
+
|
16
|
+
attr_reader :path
|
17
|
+
|
18
|
+
extend Forwardable
|
19
|
+
def_delegators :frontmatter, :data, :body
|
20
|
+
|
21
|
+
def initialize(path: , mime_type: nil)
|
22
|
+
# The MIME::Types gem returns an array when types are looked up.
|
23
|
+
# This grabs the first one, which is likely the intent on these lookups.
|
24
|
+
@mime_type = Array(mime_type).first
|
25
|
+
@path = Pathname.new path
|
26
|
+
end
|
27
|
+
|
28
|
+
# List of all file extensions.
|
29
|
+
def extensions
|
30
|
+
path.basename.to_s.split(".").drop(1)
|
31
|
+
end
|
32
|
+
|
33
|
+
# TODO: This is really a "key" or "leafname".
|
34
|
+
def basename
|
35
|
+
path.basename.to_s.split(".").first
|
36
|
+
end
|
37
|
+
|
38
|
+
def format_basename
|
39
|
+
[basename, format_extension].join(".")
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the format extension.
|
43
|
+
def format_extension
|
44
|
+
extensions.first
|
45
|
+
end
|
46
|
+
|
47
|
+
# The base name with the format extension.
|
48
|
+
def format_name
|
49
|
+
[basename, format_extension].join(".")
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns a list of the rendering extensions.
|
53
|
+
def template_extensions
|
54
|
+
extensions.drop(1)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Treat resources with the same request path as equal.
|
58
|
+
def ==(asset)
|
59
|
+
path == asset.path
|
60
|
+
end
|
61
|
+
|
62
|
+
def mime_type
|
63
|
+
@mime_type ||= inferred_mime_type || DEFAULT_MIME_TYPE
|
64
|
+
end
|
65
|
+
|
66
|
+
def exists?
|
67
|
+
File.exists? path
|
68
|
+
end
|
69
|
+
|
70
|
+
# Spits out a reasonable default request path. This may be changed
|
71
|
+
# via Resource#request_path.
|
72
|
+
def to_request_path
|
73
|
+
if ext = format_extension
|
74
|
+
path.dirname.join(basename).sub_ext(".#{ext}").to_s
|
75
|
+
else
|
76
|
+
path.to_s
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
def frontmatter
|
82
|
+
Frontmatter.new File.read @path
|
83
|
+
end
|
84
|
+
|
85
|
+
# Returns the mime type of the file extension. If a type can't
|
86
|
+
# be resolved then we'll just grab the first type.
|
87
|
+
def inferred_mime_type
|
88
|
+
MIME::Types.type_for(format_extension).first if format_extension
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Sitepress
|
2
|
+
# Maps a directory of assets into a set of routes that correspond with
|
3
|
+
# the `path` root.
|
4
|
+
class DirectoryCollection
|
5
|
+
attr_reader :assets, :path
|
6
|
+
|
7
|
+
def initialize(path: , assets:)
|
8
|
+
@path = path
|
9
|
+
@assets = assets
|
10
|
+
end
|
11
|
+
|
12
|
+
def mount(node)
|
13
|
+
assets.each { |a| node.add path: asset_path_to_request_path(a), asset: a }
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
# Given a @file_path of `/hi`, this method changes `/hi/there/friend.html.erb`
|
18
|
+
# to an absolute `/there/friend` format by removing the file extensions
|
19
|
+
def asset_path_to_request_path(asset)
|
20
|
+
# Relative path of resource to the file_path of this project.
|
21
|
+
asset.path.dirname.join(asset.format_basename).relative_path_from(path).to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Sitepress
|
2
|
+
module Extensions
|
3
|
+
# Register layouts with resources that match certain patterns.
|
4
|
+
class Layouts
|
5
|
+
Rule = Struct.new(:layout, :processor)
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@rules = Array.new
|
9
|
+
end
|
10
|
+
|
11
|
+
# Register a layout for a set of resources.
|
12
|
+
def layout(layout, &block)
|
13
|
+
@rules << Rule.new(layout, block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def process_resources(node)
|
17
|
+
node.flatten.each do |resource|
|
18
|
+
@rules.each do |rule|
|
19
|
+
if rule.processor.call(resource)
|
20
|
+
resource.data["layout"] ||= rule.layout
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Sitepress
|
2
|
+
module Extensions
|
3
|
+
class ProcManipulator
|
4
|
+
def initialize(block)
|
5
|
+
@block = block
|
6
|
+
end
|
7
|
+
|
8
|
+
def process_resources(node)
|
9
|
+
node.flatten.each do |resource|
|
10
|
+
if @block.arity == 1
|
11
|
+
@block.call resource
|
12
|
+
else # This will blow up if 0 or greater than 2.
|
13
|
+
@block.call resource, node
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Sitepress
|
2
|
+
# Manages collections of resources that share the same ResourceNode. Given the files `/a.html` and `/a.gif`,
|
3
|
+
# both of these assets would be stored in the `ResourceNode#name = "a"` under `ResourceNode#formats` with
|
4
|
+
# the extensions `.gif`, and `.html`.
|
5
|
+
class Formats
|
6
|
+
include Enumerable
|
7
|
+
|
8
|
+
extend Forwardable
|
9
|
+
def_delegators :@formats, :size, :clear
|
10
|
+
|
11
|
+
def initialize(node: )
|
12
|
+
@node = node
|
13
|
+
@formats = Hash.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def each(&block)
|
17
|
+
@formats.values.each(&block)
|
18
|
+
end
|
19
|
+
|
20
|
+
def remove(ext)
|
21
|
+
@formats.delete(ext)
|
22
|
+
end
|
23
|
+
|
24
|
+
def ext(ext)
|
25
|
+
@formats[ext]
|
26
|
+
end
|
27
|
+
|
28
|
+
def mime_type(mime_type)
|
29
|
+
find { |f| f.mime_type == mime_type }
|
30
|
+
end
|
31
|
+
|
32
|
+
def add(asset: , ext: )
|
33
|
+
resource = Resource.new(asset: asset, node: @node, ext: ext)
|
34
|
+
if @formats.has_key? ext
|
35
|
+
raise Sitepress::ExistingRequestPathError, "Resource at #{resource.request_path} already set"
|
36
|
+
else
|
37
|
+
@formats[ext] = resource
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def inspect
|
42
|
+
"<#{self.class}: resources=#{map(&:request_path)}>"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module Sitepress
|
4
|
+
# Parses metadata from the header of the page.
|
5
|
+
|
6
|
+
# TODO: Redo this to use File readline and pos to
|
7
|
+
# perform faster
|
8
|
+
class Frontmatter
|
9
|
+
DELIMITER = "---".freeze
|
10
|
+
PATTERN = /\A(#{DELIMITER}\n(.+?)\n#{DELIMITER}\n*)?(.+)\Z/m
|
11
|
+
|
12
|
+
attr_reader :body
|
13
|
+
|
14
|
+
def initialize(content)
|
15
|
+
_, @data, @body = content.match(PATTERN).captures
|
16
|
+
end
|
17
|
+
|
18
|
+
def data
|
19
|
+
@data ? YAML.load(@data) : {}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
module Sitepress
|
4
|
+
# Represents the request path of an asset. There may be multiple
|
5
|
+
# resources that point to the same asset. Resources are immutable
|
6
|
+
# and may be altered by the resource proxy.
|
7
|
+
class Resource
|
8
|
+
extend Forwardable
|
9
|
+
def_delegators :asset, :mime_type
|
10
|
+
|
11
|
+
attr_writer :body, :data
|
12
|
+
attr_reader :node, :asset, :ext
|
13
|
+
|
14
|
+
# Default scope for querying parent/child/sibling resources.
|
15
|
+
DEFAULT_FILTER_SCOPE = :same
|
16
|
+
|
17
|
+
def initialize(asset: , node: , ext: "")
|
18
|
+
@asset = asset
|
19
|
+
@node = node
|
20
|
+
@ext = ext # TODO: Meh, feels dirty but I suppose the thingy has to drop it in.
|
21
|
+
end
|
22
|
+
|
23
|
+
def request_path
|
24
|
+
return unless node
|
25
|
+
# TODO: This `compact` makes me nervous. How can we handle this better?
|
26
|
+
lineage = node.parents.reverse.map(&:name).compact
|
27
|
+
file_name = [node.name, @ext].join
|
28
|
+
File.join("/", *lineage, file_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def data
|
32
|
+
@data ||= asset.data
|
33
|
+
end
|
34
|
+
|
35
|
+
def body
|
36
|
+
@body ||= asset.body
|
37
|
+
end
|
38
|
+
|
39
|
+
def inspect
|
40
|
+
"<#{self.class}:#{object_id} request_path=#{request_path.inspect} asset_path=#{@asset.path.to_s.inspect}>"
|
41
|
+
end
|
42
|
+
|
43
|
+
def parent(**args)
|
44
|
+
parents(**args).first
|
45
|
+
end
|
46
|
+
|
47
|
+
def parents(**args)
|
48
|
+
filter_resources(**args){ node.parents }
|
49
|
+
end
|
50
|
+
|
51
|
+
def siblings(**args)
|
52
|
+
filter_resources(**args){ node.siblings }.compact
|
53
|
+
end
|
54
|
+
|
55
|
+
def children(**args)
|
56
|
+
filter_resources(**args){ node.children }.compact
|
57
|
+
end
|
58
|
+
|
59
|
+
def ==(resource)
|
60
|
+
resource.request_path == request_path
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
# Filters parent/child/sibling resources by a type. The default behavior is to only return
|
65
|
+
# resources of the same type. For example given the pages `/a.html`, `/a.gif`, `/a/b.html`,
|
66
|
+
# if you query the parent from page `/a/b.html` you'd only get `/a.html` by default. If you
|
67
|
+
# query the parents via `parents(type: :all)` you'd get get [`/a.html`, `/a.gif`]
|
68
|
+
#
|
69
|
+
# TODO: When `type: :all` is scoped, some queries will mistakenly return single resources.
|
70
|
+
# :all should return an array of arrays to accurately represention levels.
|
71
|
+
#
|
72
|
+
# TODO: Put a better extension/mime_type handler into resource tree, then instead of faltening
|
73
|
+
# below and select, we could call a single map and pull out a resources
|
74
|
+
def filter_resources(type: DEFAULT_FILTER_SCOPE, &block)
|
75
|
+
return [] unless node
|
76
|
+
nodes = block.call
|
77
|
+
|
78
|
+
case type
|
79
|
+
when :all
|
80
|
+
nodes.map{ |node| node.formats }
|
81
|
+
when :same
|
82
|
+
nodes.map{ |n| n.formats.ext(ext) }.flatten
|
83
|
+
when String
|
84
|
+
nodes.map{ |n| n.formats.ext(type) }.flatten
|
85
|
+
when MIME::Type
|
86
|
+
nodes.map{ |n| n.formats.mime_type(type) }.flatten
|
87
|
+
else
|
88
|
+
raise ArgumentError, "Invalid type argument #{type}. Must be either :same, :all, an extension string, or a Mime::Type"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Sitepress
|
2
|
+
# Represents a collection of resources. Provides interfaces to query
|
3
|
+
# resource via globbing, paths, etc.
|
4
|
+
class ResourceCollection
|
5
|
+
extend Forwardable
|
6
|
+
def_delegators :resources, :each, :size, :index, :[], :last, :length, :fetch, :count, :at
|
7
|
+
|
8
|
+
include Enumerable
|
9
|
+
|
10
|
+
# Used by `#glob` to determine the full path when
|
11
|
+
# given a relative glob pattern.
|
12
|
+
attr_reader :root_path
|
13
|
+
|
14
|
+
def initialize(node: , root_path: ".")
|
15
|
+
@node = node
|
16
|
+
@root_path = Pathname.new(root_path)
|
17
|
+
end
|
18
|
+
|
19
|
+
def glob(pattern)
|
20
|
+
paths = Dir.glob root_path.join(pattern)
|
21
|
+
resources.select { |r| paths.include? r.asset.path.to_s }
|
22
|
+
end
|
23
|
+
|
24
|
+
def get(request_path)
|
25
|
+
@node.get(request_path)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
def resources
|
30
|
+
@node.flatten
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module Sitepress
|
2
|
+
# Resource nodes give resources their parent/sibling/child relationships. The relationship are determined
|
3
|
+
# by the `request_path` given to an asset when its added to a node. Given the `request_path` `/foo/bar/biz/buz.html`,
|
4
|
+
# a tree of resource nodes would be built named `foo`, `bar`, `biz`, `buz`. `foo` would be the "root" node and `buz`
|
5
|
+
# a leaf node. The actual `buz.html` asset is then stored on the leaf node as a resource. This tree structure
|
6
|
+
# makes it possible to reason through path relationships from code to build out elements in a website like tree navigation.
|
7
|
+
class ResourcesNode
|
8
|
+
attr_reader :parent, :name
|
9
|
+
|
10
|
+
DELIMITER = "/".freeze
|
11
|
+
|
12
|
+
def initialize(parent: nil, delimiter: ResourcesNode::DELIMITER, name: nil)
|
13
|
+
@parent = parent
|
14
|
+
@name = name.freeze
|
15
|
+
@delimiter = delimiter.freeze
|
16
|
+
end
|
17
|
+
|
18
|
+
def formats
|
19
|
+
@formats ||= Formats.new(node: self)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the immediate children nodes.
|
23
|
+
def children
|
24
|
+
child_nodes.values
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns sibling nodes.
|
28
|
+
def siblings
|
29
|
+
parent ? parent.children.reject{ |c| c == self } : []
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns all parents up to the root node.
|
33
|
+
def parents
|
34
|
+
parents = []
|
35
|
+
node = parent
|
36
|
+
while node do
|
37
|
+
parents << node
|
38
|
+
node = node.parent
|
39
|
+
end
|
40
|
+
parents
|
41
|
+
end
|
42
|
+
|
43
|
+
def root?
|
44
|
+
parent.nil?
|
45
|
+
end
|
46
|
+
|
47
|
+
def leaf?
|
48
|
+
child_nodes.empty?
|
49
|
+
end
|
50
|
+
|
51
|
+
def flatten(resources: [])
|
52
|
+
formats.each{ |resource| resources << resource }
|
53
|
+
children.each do |child|
|
54
|
+
child.flatten.each{ |resource| resources << resource }
|
55
|
+
end
|
56
|
+
resources
|
57
|
+
end
|
58
|
+
|
59
|
+
def remove
|
60
|
+
if leaf?
|
61
|
+
# TODO: Check the parents to see if they also need to be removed if
|
62
|
+
# this call orphans the tree up to a resource.
|
63
|
+
parent.remove_child(name)
|
64
|
+
else
|
65
|
+
formats.clear
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def add(path: , asset: )
|
70
|
+
head, *path = tokenize(path)
|
71
|
+
if path.empty?
|
72
|
+
# When there's no more paths, we're left with the format (e.g. ".html")
|
73
|
+
formats.add(asset: asset, ext: head)
|
74
|
+
else
|
75
|
+
child_nodes[head].add(path: path, asset: asset)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
alias :[]= :add
|
79
|
+
|
80
|
+
def get(path)
|
81
|
+
*path, ext = tokenize(path)
|
82
|
+
if node = dig(*path)
|
83
|
+
node.formats.ext(ext)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def get_node(path)
|
88
|
+
*path, _ = tokenize(path)
|
89
|
+
dig(*path)
|
90
|
+
end
|
91
|
+
alias :[] :get_node
|
92
|
+
|
93
|
+
def inspect
|
94
|
+
"<#{self.class}: formats=#{formats.map(&:request_path)} children=#{children.map(&:name).inspect}>"
|
95
|
+
end
|
96
|
+
|
97
|
+
# TODO: I don't really like how the path is broken up with the "ext" at the end.
|
98
|
+
# It feels inconsistent. Either make an object/struct that encaspulates this or
|
99
|
+
# just pass `index.html` through to the end.
|
100
|
+
def dig(*args)
|
101
|
+
head, *tail = args
|
102
|
+
if head.nil? and tail.empty?
|
103
|
+
self
|
104
|
+
elsif child_nodes.has_key?(head)
|
105
|
+
child_nodes[head].dig(*tail)
|
106
|
+
else
|
107
|
+
nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
protected
|
112
|
+
def remove_child(path)
|
113
|
+
*_, segment, _ = tokenize(path)
|
114
|
+
child_nodes.delete(segment)
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
def add_child_node(name)
|
119
|
+
ResourcesNode.new(parent: self, delimiter: @delimiter, name: name)
|
120
|
+
end
|
121
|
+
|
122
|
+
def child_nodes
|
123
|
+
@child_nodes ||= Hash.new { |hash, key| hash[key] = add_child_node(key) }
|
124
|
+
end
|
125
|
+
|
126
|
+
# Returns all of the names for the path along with the format, if set.
|
127
|
+
def tokenize(path)
|
128
|
+
return path if path.respond_to? :to_a
|
129
|
+
path, _, file = path.gsub(/^\//, "").rpartition(@delimiter)
|
130
|
+
ext = File.extname(file)
|
131
|
+
file = File.basename(file, ext)
|
132
|
+
path.split(@delimiter).push(file).push(ext)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "sitepress/extensions/proc_manipulator"
|
3
|
+
require "forwardable"
|
4
|
+
|
5
|
+
module Sitepress
|
6
|
+
# A collection of pages from a directory.
|
7
|
+
class Site
|
8
|
+
# Default file pattern to pick up in site
|
9
|
+
DEFAULT_GLOB = "**/**".freeze
|
10
|
+
|
11
|
+
# Default root_path for site.
|
12
|
+
DEFAULT_ROOT_PATH = Pathname.new(".").freeze
|
13
|
+
|
14
|
+
attr_reader :root_path, :resources_pipeline
|
15
|
+
|
16
|
+
# Cache resources for production runs of Sitepress. Development
|
17
|
+
# modes don't cache to optimize for files reloading.
|
18
|
+
attr_accessor :cache_resources
|
19
|
+
alias :cache_resources? :cache_resources
|
20
|
+
|
21
|
+
# TODO: Get rid of these so that folks have ot call site.resources.get ...
|
22
|
+
extend Forwardable
|
23
|
+
def_delegators :resources, :get, :glob
|
24
|
+
|
25
|
+
def initialize(root_path: DEFAULT_ROOT_PATH)
|
26
|
+
self.root_path = root_path
|
27
|
+
end
|
28
|
+
|
29
|
+
# A tree representation of the resourecs wthin the site. The root is a node that's
|
30
|
+
# processed by the `resources_pipeline`.
|
31
|
+
def root
|
32
|
+
ResourcesNode.new.tap do |node|
|
33
|
+
DirectoryCollection.new(assets: pages_assets, path: pages_path).mount(node)
|
34
|
+
resources_pipeline.process node
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns a list of all the resources within #root.
|
39
|
+
def resources
|
40
|
+
@resources = nil unless cache_resources
|
41
|
+
@resources ||= ResourceCollection.new(node: root, root_path: root_path)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Root path to website project. Contains helpers, pages, and more.
|
45
|
+
def root_path=(path)
|
46
|
+
@root_path = Pathname.new(path)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Location of website pages.
|
50
|
+
def pages_path
|
51
|
+
root_path.join("pages")
|
52
|
+
end
|
53
|
+
|
54
|
+
# Quick and dirty way to manipulate resources in the site without
|
55
|
+
# creating classes that implement the #process_resources method.
|
56
|
+
#
|
57
|
+
# A common example may be adding data to a resource if it begins with a
|
58
|
+
# certain path:
|
59
|
+
#
|
60
|
+
# ```ruby
|
61
|
+
# Sitepress.site.manipulate do |resource, root|
|
62
|
+
# if resource.request_path.start_with? "/videos/"
|
63
|
+
# resource.data["layout"] = "video"
|
64
|
+
# end
|
65
|
+
# end
|
66
|
+
# ```
|
67
|
+
#
|
68
|
+
# A more complex, contrived example that sets index.html as the root node
|
69
|
+
# in the site:
|
70
|
+
#
|
71
|
+
# ```ruby
|
72
|
+
# Sitepress.site.manipulate do |resource, root|
|
73
|
+
# if resource.request_path == "/index"
|
74
|
+
# # Remove the HTML format of index from the current resource level
|
75
|
+
# # so we can level it up.
|
76
|
+
# node = resource.node
|
77
|
+
# node.formats.remove ".html"
|
78
|
+
# node.remove
|
79
|
+
# root.add path: "/", asset: resource.asset # Now we can get to this from `/`.
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
# ```
|
83
|
+
def manipulate(&block)
|
84
|
+
resources_pipeline << Extensions::ProcManipulator.new(block)
|
85
|
+
end
|
86
|
+
|
87
|
+
# An array of procs that manipulate the tree and resources from the
|
88
|
+
# ResourceNode returned by #root.
|
89
|
+
def resources_pipeline
|
90
|
+
@resources_pipeline ||= ResourcesPipeline.new
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
# Lazy stream of files that will be rendered by resources.
|
95
|
+
def pages_assets(glob = DEFAULT_GLOB)
|
96
|
+
Dir.glob(pages_path.join(glob)).select(&File.method(:file?)).lazy.map do |path|
|
97
|
+
Asset.new(path: path)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "sitepress/version"
|
2
|
+
|
3
|
+
module Sitepress
|
4
|
+
# Raised by Resources if a path is added that's not a valid path.
|
5
|
+
InvalidRequestPathError = Class.new(RuntimeError)
|
6
|
+
|
7
|
+
# Raised by Resources if a path is already in its index
|
8
|
+
ExistingRequestPathError = Class.new(InvalidRequestPathError)
|
9
|
+
|
10
|
+
autoload :Asset, "sitepress/asset"
|
11
|
+
autoload :DirectoryCollection, "sitepress/directory_collection"
|
12
|
+
autoload :Formats, "sitepress/formats"
|
13
|
+
autoload :Frontmatter, "sitepress/frontmatter"
|
14
|
+
autoload :Resource, "sitepress/resource"
|
15
|
+
autoload :ResourceCollection, "sitepress/resource_collection"
|
16
|
+
autoload :ResourcesPipeline, "sitepress/resources_pipeline"
|
17
|
+
autoload :ResourcesNode, "sitepress/resources_node"
|
18
|
+
autoload :Site, "sitepress/site"
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require File.expand_path('../../sitepress/lib/sitepress/version', __FILE__)
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "sitepress-core"
|
8
|
+
spec.version = Sitepress::VERSION
|
9
|
+
spec.authors = ["Brad Gessler"]
|
10
|
+
spec.email = ["bradgessler@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{An embeddable file-backed content management system.}
|
13
|
+
spec.homepage = "https://github.com/sitepress/sitepress"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
|
+
spec.bindir = "exe"
|
17
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
21
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
22
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
23
|
+
|
24
|
+
spec.add_runtime_dependency "mime-types", ">= 2.99"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sitepress-core
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.21
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Brad Gessler
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: mime-types
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.99'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.99'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- bradgessler@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- lib/sitepress-core.rb
|
77
|
+
- lib/sitepress/asset.rb
|
78
|
+
- lib/sitepress/directory_collection.rb
|
79
|
+
- lib/sitepress/extensions/layouts.rb
|
80
|
+
- lib/sitepress/extensions/proc_manipulator.rb
|
81
|
+
- lib/sitepress/formats.rb
|
82
|
+
- lib/sitepress/frontmatter.rb
|
83
|
+
- lib/sitepress/resource.rb
|
84
|
+
- lib/sitepress/resource_collection.rb
|
85
|
+
- lib/sitepress/resources_node.rb
|
86
|
+
- lib/sitepress/resources_pipeline.rb
|
87
|
+
- lib/sitepress/site.rb
|
88
|
+
- sitepress-core.gemspec
|
89
|
+
homepage: https://github.com/sitepress/sitepress
|
90
|
+
licenses: []
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.5.1
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: An embeddable file-backed content management system.
|
112
|
+
test_files: []
|
113
|
+
has_rdoc:
|