mascot 0.1.5 → 0.1.6
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 +4 -4
- data/lib/mascot/asset.rb +67 -0
- data/lib/mascot/frontmatter.rb +19 -0
- data/lib/mascot/resource.rb +56 -0
- data/lib/mascot/resources.rb +97 -0
- data/lib/mascot/safe_root.rb +42 -0
- data/lib/mascot/sitemap.rb +52 -0
- data/lib/mascot/version.rb +1 -1
- data/lib/mascot.rb +11 -137
- data/mascot.gemspec +0 -1
- metadata +8 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 121fe032defb71fbc2265ccf6a0f850a91e2f47d
|
4
|
+
data.tar.gz: 3afef4d165793f1727c3dc8c43390419e6b2ceb3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a674481e7d739c26f2517579808cc60494c64872865e45a496a7b8e144e2ecd053a11f4048c39d2d735bf413ebd5a118c4feda6c7d4ad4f57e640759c7cf17f9
|
7
|
+
data.tar.gz: 8a77fb0441652d75b55524207bbcbff4f11ebd780d646444318b1b7ba2c974a8a6c68207221aaa77d587e032327a5053f319d1449f2d733389b8f216cb1c0fde
|
data/lib/mascot/asset.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
require "mime/types"
|
2
|
+
require "forwardable"
|
3
|
+
require "pathname"
|
4
|
+
|
5
|
+
module Mascot
|
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
|
+
# Returns the format extension.
|
34
|
+
def format_extension
|
35
|
+
extensions.first
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns a list of the rendering extensions.
|
39
|
+
def template_extensions
|
40
|
+
extensions.drop(1)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Treat resources with the same request path as equal.
|
44
|
+
def ==(asset)
|
45
|
+
path == asset.path
|
46
|
+
end
|
47
|
+
|
48
|
+
def mime_type
|
49
|
+
@mime_type ||= Array(inferred_mime_type).first || DEFAULT_MIME_TYPE
|
50
|
+
end
|
51
|
+
|
52
|
+
def exists?
|
53
|
+
File.exists? path
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
def frontmatter
|
58
|
+
Frontmatter.new File.read @path
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns the mime type of the file extension. If a type can't
|
62
|
+
# be resolved then we'll just grab the first type.
|
63
|
+
def inferred_mime_type
|
64
|
+
MIME::Types.type_for(format_extension) if format_extension
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module Mascot
|
4
|
+
# Parses metadata from the header of the page.
|
5
|
+
class Frontmatter
|
6
|
+
DELIMITER = "---".freeze
|
7
|
+
PATTERN = /\A(#{DELIMITER}\n(.+)\n#{DELIMITER}\n)?(.+)\Z/m
|
8
|
+
|
9
|
+
attr_reader :body
|
10
|
+
|
11
|
+
def initialize(content)
|
12
|
+
_, @data, @body = content.match(PATTERN).captures
|
13
|
+
end
|
14
|
+
|
15
|
+
def data
|
16
|
+
@data ? YAML.load(@data) : {}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require "observer"
|
3
|
+
|
4
|
+
module Mascot
|
5
|
+
# Represents the request path of an asset. There may be multiple
|
6
|
+
# resources that point to the same asset. Resources are immutable
|
7
|
+
# and may be altered by the resource proxy.
|
8
|
+
class Resource
|
9
|
+
include Observable
|
10
|
+
|
11
|
+
extend Forwardable
|
12
|
+
def_delegators :asset, :mime_type
|
13
|
+
|
14
|
+
attr_accessor :request_path, :asset
|
15
|
+
attr_writer :body, :data
|
16
|
+
|
17
|
+
def initialize(request_path: , asset: )
|
18
|
+
self.request_path = request_path
|
19
|
+
@asset = asset
|
20
|
+
end
|
21
|
+
|
22
|
+
# When #dup or #clone is copied, the Resource
|
23
|
+
# collection observer must be removed so there's
|
24
|
+
# not duplicate resources.
|
25
|
+
def initialize_copy(instance)
|
26
|
+
instance.delete_observers
|
27
|
+
super instance
|
28
|
+
end
|
29
|
+
|
30
|
+
def request_path=(request_path)
|
31
|
+
old_request_path = @request_path
|
32
|
+
# We freeze the value to ensure users can't modify
|
33
|
+
# the request_path string in place (e.g. Resource#request_path.capitalize!)
|
34
|
+
# and throw the resource out of sync with the Resources collection.
|
35
|
+
@request_path = request_path.dup.freeze
|
36
|
+
changed
|
37
|
+
notify_observers self, old_request_path
|
38
|
+
end
|
39
|
+
|
40
|
+
def inspect
|
41
|
+
"#<#{self.class}:0x#{(object_id << 1).to_s(16)} @request_path=#{@request_path.inspect} @asset=#{@asset.inspect}>"
|
42
|
+
end
|
43
|
+
|
44
|
+
def data
|
45
|
+
@data ||= asset.data
|
46
|
+
end
|
47
|
+
|
48
|
+
def body
|
49
|
+
@body ||= asset.body
|
50
|
+
end
|
51
|
+
|
52
|
+
def ==(asset)
|
53
|
+
request_path == asset.request_path
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Mascot
|
2
|
+
class Resources
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
extend Forwardable
|
6
|
+
def_delegators :@routes, :size, :empty?, :any?, :clear
|
7
|
+
|
8
|
+
def initialize(root_file_path: )
|
9
|
+
@routes = Hash.new
|
10
|
+
@root_file_path = Pathname.new(root_file_path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def each(&block)
|
14
|
+
@routes.values.each(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def last
|
18
|
+
@routes.values.last
|
19
|
+
end
|
20
|
+
|
21
|
+
def glob(pattern = "**/**")
|
22
|
+
paths = safe_root.glob @root_file_path.join(pattern)
|
23
|
+
select { |r| paths.include? r.asset.path.to_s}
|
24
|
+
end
|
25
|
+
|
26
|
+
def get(request_path)
|
27
|
+
return if request_path.nil?
|
28
|
+
@routes[key(request_path)]
|
29
|
+
end
|
30
|
+
|
31
|
+
def add(resource)
|
32
|
+
validate_request_path resource
|
33
|
+
validate_uniqueness resource
|
34
|
+
|
35
|
+
resource.add_observer self
|
36
|
+
@routes[key(resource)] = resource
|
37
|
+
end
|
38
|
+
|
39
|
+
def update(resource, old_request_path)
|
40
|
+
validate_request_path old_request_path
|
41
|
+
validate_request_path resource
|
42
|
+
validate_uniqueness resource
|
43
|
+
|
44
|
+
@routes.delete key(old_request_path)
|
45
|
+
@routes[key(resource)] = resource
|
46
|
+
end
|
47
|
+
|
48
|
+
def remove(resource)
|
49
|
+
validate_request_path resource
|
50
|
+
resource.delete_observer self
|
51
|
+
@routes.delete key(resource)
|
52
|
+
resource
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_asset(asset, request_path: nil)
|
56
|
+
add Resource.new asset: asset, request_path: asset_path_to_request_path(request_path || asset.path)
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
def key(path)
|
61
|
+
File.join "/", validate_request_path(coerce_request_path(path))
|
62
|
+
end
|
63
|
+
|
64
|
+
def coerce_request_path(resource)
|
65
|
+
resource.respond_to?(:request_path) ? resource.request_path : resource
|
66
|
+
end
|
67
|
+
|
68
|
+
def validate_request_path(path)
|
69
|
+
path = coerce_request_path(path)
|
70
|
+
raise InvalidRequestPathError, "path can't be nil" if path.nil?
|
71
|
+
path
|
72
|
+
end
|
73
|
+
|
74
|
+
# Raise an exception if the user tries to add a Resource with an existing request path.
|
75
|
+
def validate_uniqueness(resource)
|
76
|
+
path = coerce_request_path(resource)
|
77
|
+
if existing_resource = get(path)
|
78
|
+
raise ExistingRequestPathError, "Resource #{existing_resource} already exists at #{path}"
|
79
|
+
else
|
80
|
+
resource
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Given a @file_path of `/hi`, this method changes `/hi/there/friend.html.erb`
|
85
|
+
# to an absolute `/there/friend` format by removing the file extensions
|
86
|
+
def asset_path_to_request_path(path)
|
87
|
+
# Relative path of resource to the file_path of this project.
|
88
|
+
relative_path = Pathname.new(path).relative_path_from(@root_file_path)
|
89
|
+
# Removes the .fooz.baz
|
90
|
+
File.join("/", relative_path).to_s.sub(/\..*/, '')
|
91
|
+
end
|
92
|
+
|
93
|
+
def safe_root
|
94
|
+
@safe_root ||= SafeRoot.new(path: @root_file_path)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module Mascot
|
4
|
+
# Validates if a path is within another path. This prevents
|
5
|
+
# users from accidentally selecting a file outside of their sitemap,
|
6
|
+
# which could be insured.
|
7
|
+
class SafeRoot
|
8
|
+
def initialize(path: )
|
9
|
+
@path = Pathname.new(path)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Validates if a path is safe by checking if its within a folder.
|
13
|
+
def safe?(path)
|
14
|
+
root_path = File.expand_path(@path)
|
15
|
+
resource_path = File.expand_path(path)
|
16
|
+
|
17
|
+
if resource_path.start_with? root_path
|
18
|
+
path
|
19
|
+
else
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def glob(pattern)
|
24
|
+
Dir[validate(pattern)]
|
25
|
+
end
|
26
|
+
|
27
|
+
def unsafe?(path)
|
28
|
+
not safe? path
|
29
|
+
end
|
30
|
+
|
31
|
+
def path
|
32
|
+
end
|
33
|
+
|
34
|
+
def validate(path)
|
35
|
+
if unsafe? path
|
36
|
+
raise Mascot::UnsafePathAccessError, "Unsafe attempt to access #{path} outside of #{@path}"
|
37
|
+
else
|
38
|
+
path
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module Mascot
|
4
|
+
# A collection of pages from a directory.
|
5
|
+
class Sitemap
|
6
|
+
# Default file pattern to pick up in sitemap
|
7
|
+
DEFAULT_GLOB = "**/**".freeze
|
8
|
+
# Default root path for sitemap.
|
9
|
+
DEFAULT_ROOT_PATH = Pathname.new(".").freeze
|
10
|
+
# Default root request path
|
11
|
+
DEFAULT_ROOT_REQUEST_PATH = Pathname.new("/").freeze
|
12
|
+
|
13
|
+
attr_reader :root, :request_path
|
14
|
+
|
15
|
+
def initialize(root: DEFAULT_ROOT_PATH, request_path: DEFAULT_ROOT_REQUEST_PATH)
|
16
|
+
self.root = root
|
17
|
+
self.request_path = request_path
|
18
|
+
end
|
19
|
+
|
20
|
+
# Lazy stream of files that will be rendered by resources.
|
21
|
+
def assets(glob = DEFAULT_GLOB)
|
22
|
+
safe_root.glob(root.join(glob)).select(&File.method(:file?)).lazy.map do |path|
|
23
|
+
Asset.new(path: path)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns a list of resources.
|
28
|
+
def resources
|
29
|
+
Resources.new(root_file_path: root).tap do |resources|
|
30
|
+
assets.each { |a| resources.add_asset a }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Find the page with a path.
|
35
|
+
def get(request_path)
|
36
|
+
resources.get(request_path)
|
37
|
+
end
|
38
|
+
|
39
|
+
def root=(path)
|
40
|
+
@root = Pathname.new(path)
|
41
|
+
end
|
42
|
+
|
43
|
+
def request_path=(path)
|
44
|
+
@request_path = Pathname.new(path)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def safe_root
|
49
|
+
@safe_root ||= SafeRoot.new(path: root)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/mascot/version.rb
CHANGED
data/lib/mascot.rb
CHANGED
@@ -1,145 +1,19 @@
|
|
1
1
|
require "mascot/version"
|
2
2
|
|
3
|
-
require "forwardable"
|
4
|
-
require "pathname"
|
5
|
-
require "yaml"
|
6
|
-
require "mime/types"
|
7
|
-
|
8
3
|
module Mascot
|
9
4
|
# Raised if a user attempts to access a resource outside of the sitemap path.
|
10
|
-
|
11
|
-
|
12
|
-
# Parses metadata from the header of the page.
|
13
|
-
class Frontmatter
|
14
|
-
DELIMITER = "---".freeze
|
15
|
-
PATTERN = /\A(#{DELIMITER}\n(.+)\n#{DELIMITER}\n)?(.+)\Z/m
|
16
|
-
|
17
|
-
attr_reader :body
|
18
|
-
|
19
|
-
def initialize(content)
|
20
|
-
_, @data, @body = content.match(PATTERN).captures
|
21
|
-
end
|
22
|
-
|
23
|
-
def data
|
24
|
-
@data ? YAML.load(@data) : {}
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
def parse
|
29
|
-
@content
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# Represents a page in a web server context.
|
34
|
-
class Resource
|
35
|
-
# If we can't resolve a mime type for the resource, we'll fall
|
36
|
-
# back to this binary octet-stream type so the client can download
|
37
|
-
# the resource and figure out what to do with it.
|
38
|
-
DEFAULT_MIME_TYPE = MIME::Types["application/octet-stream"].first
|
39
|
-
|
40
|
-
attr_reader :request_path, :file_path
|
41
|
-
|
42
|
-
extend Forwardable
|
43
|
-
def_delegators :@frontmatter, :data, :body
|
44
|
-
|
45
|
-
def initialize(request_path: , file_path: , mime_type: nil)
|
46
|
-
@request_path = request_path
|
47
|
-
@file_path = Pathname.new file_path
|
48
|
-
@frontmatter = Frontmatter.new File.read @file_path
|
49
|
-
@mime_types = Array(mime_type) if mime_type
|
50
|
-
end
|
51
|
-
|
52
|
-
# List of all file extensions.
|
53
|
-
def extensions
|
54
|
-
@file_path.basename.to_s.split(".").drop(1)
|
55
|
-
end
|
56
|
-
|
57
|
-
# Returns the format extension.
|
58
|
-
def format_extension
|
59
|
-
extensions.first
|
60
|
-
end
|
61
|
-
|
62
|
-
# Returns a list of the rendering extensions.
|
63
|
-
def template_extensions
|
64
|
-
extensions.drop(1)
|
65
|
-
end
|
66
|
-
|
67
|
-
def mime_type
|
68
|
-
(@mime_types ||= Array(resolve_mime_type)).push(DEFAULT_MIME_TYPE).first
|
69
|
-
end
|
70
|
-
|
71
|
-
# Treat resources with the same request path as equal.
|
72
|
-
def ==(resource)
|
73
|
-
request_path == resource.request_path
|
74
|
-
end
|
75
|
-
|
76
|
-
private
|
77
|
-
# Returns the mime type of the file extension. If a type can't
|
78
|
-
# be resolved then we'll just grab the first type.
|
79
|
-
def resolve_mime_type
|
80
|
-
MIME::Types.type_for(format_extension) if format_extension
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
# A collection of pages from a directory.
|
85
|
-
class Sitemap
|
86
|
-
# Default file pattern to pick up in sitemap
|
87
|
-
DEFAULT_GLOB = "**/**".freeze
|
88
|
-
# Default root path for sitemap.
|
89
|
-
DEFAULT_ROOT_DIR = Pathname.new(".").freeze
|
90
|
-
# Default root request path
|
91
|
-
DEFAULT_ROOT_REQUEST_PATH = Pathname.new("/").freeze
|
92
|
-
|
93
|
-
attr_reader :file_path, :request_path
|
94
|
-
|
95
|
-
def initialize(file_path: DEFAULT_ROOT_DIR, request_path: DEFAULT_ROOT_REQUEST_PATH)
|
96
|
-
self.file_path = file_path
|
97
|
-
self.request_path = request_path
|
98
|
-
end
|
99
|
-
|
100
|
-
# Lazy stream of resources.
|
101
|
-
def resources(glob = DEFAULT_GLOB)
|
102
|
-
Dir[validate_path(@file_path.join(glob))].select(&File.method(:file?)).lazy.map do |path|
|
103
|
-
Resource.new request_path: request_path(path), file_path: path
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
# Find the page with a path.
|
108
|
-
def find_by_request_path(request_path)
|
109
|
-
return if request_path.nil?
|
110
|
-
resources.find { |r| r.request_path == File.join("/", request_path) }
|
111
|
-
end
|
112
|
-
|
113
|
-
def file_path=(path)
|
114
|
-
@file_path = Pathname.new(path)
|
115
|
-
end
|
116
|
-
|
117
|
-
def request_path=(path)
|
118
|
-
@request_path = Pathname.new(path)
|
119
|
-
end
|
120
|
-
|
121
|
-
private
|
5
|
+
UnsafePathAccessError = Class.new(SecurityError)
|
122
6
|
|
123
|
-
|
124
|
-
|
125
|
-
def validate_path(path)
|
126
|
-
root_path = @file_path.expand_path.to_s
|
127
|
-
resource_path = path.expand_path.to_s
|
7
|
+
# Raised by Resources if a path is added that's not a valid path.
|
8
|
+
InvalidRequestPathError = Class.new(RuntimeError)
|
128
9
|
|
129
|
-
|
130
|
-
|
131
|
-
else
|
132
|
-
raise Mascot::InsecurePathAccessError, "#{resource_path} outside sitemap #{root_path} directory"
|
133
|
-
end
|
134
|
-
end
|
10
|
+
# Raised by Resources if a path is already in its index
|
11
|
+
ExistingRequestPathError = Class.new(InvalidRequestPathError)
|
135
12
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
@request_path.join(relative_path).to_s.sub(/\..*/, '')
|
143
|
-
end
|
144
|
-
end
|
13
|
+
autoload :Asset, "mascot/asset"
|
14
|
+
autoload :Frontmatter, "mascot/frontmatter"
|
15
|
+
autoload :SafeRoot, "mascot/safe_root"
|
16
|
+
autoload :Resources, "mascot/resources"
|
17
|
+
autoload :Resource, "mascot/resource"
|
18
|
+
autoload :Sitemap, "mascot/sitemap"
|
145
19
|
end
|
data/mascot.gemspec
CHANGED
@@ -20,7 +20,6 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.add_development_dependency "bundler", "~> 1.11"
|
21
21
|
spec.add_development_dependency "rake", "~> 10.0"
|
22
22
|
spec.add_development_dependency "rspec", "~> 3.0"
|
23
|
-
spec.add_development_dependency "pry"
|
24
23
|
|
25
24
|
spec.add_runtime_dependency "mime-types", ">= 2.99"
|
26
25
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mascot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brad Gessler
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-07-
|
11
|
+
date: 2016-07-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -52,20 +52,6 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: pry
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ">="
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: '0'
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ">="
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: '0'
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
56
|
name: mime-types
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -88,6 +74,12 @@ extensions: []
|
|
88
74
|
extra_rdoc_files: []
|
89
75
|
files:
|
90
76
|
- lib/mascot.rb
|
77
|
+
- lib/mascot/asset.rb
|
78
|
+
- lib/mascot/frontmatter.rb
|
79
|
+
- lib/mascot/resource.rb
|
80
|
+
- lib/mascot/resources.rb
|
81
|
+
- lib/mascot/safe_root.rb
|
82
|
+
- lib/mascot/sitemap.rb
|
91
83
|
- lib/mascot/version.rb
|
92
84
|
- mascot.gemspec
|
93
85
|
homepage: https://github.com/bradgessler/mascot
|