mascot 0.1.11 → 0.1.12
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 +10 -0
- data/lib/mascot/resource.rb +59 -26
- data/lib/mascot/resources_node.rb +148 -0
- data/lib/mascot/site.rb +16 -4
- data/lib/mascot/version.rb +1 -1
- data/lib/mascot.rb +2 -2
- metadata +3 -3
- data/lib/mascot/resources.rb +0 -103
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1d705fe26af1339418f2018ebeed0af942e6b49e
|
4
|
+
data.tar.gz: d0fbb9055d2b7214078eb1de963065e8f1949c9b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 95f73264703dccec615c082cf1f071237160b94e967903842c7f889da8dae7617c3a5931539ea0be5fbb2fbd070959cfc17bfc800c67eca0c758976249e88f01
|
7
|
+
data.tar.gz: ab29c4b7f17646864334e14007b1bf27feab5960c9e5fa88a805785dab23421b62727b8b12d813519105a50555c5f116c6a74a82d34e35bbc46786747b9ba457
|
data/lib/mascot/asset.rb
CHANGED
@@ -30,15 +30,25 @@ module Mascot
|
|
30
30
|
path.basename.to_s.split(".").drop(1)
|
31
31
|
end
|
32
32
|
|
33
|
+
# TODO: This is really a "key" or "leafname".
|
33
34
|
def basename
|
34
35
|
path.basename.to_s.split(".").first
|
35
36
|
end
|
36
37
|
|
38
|
+
def format_basename
|
39
|
+
[basename, format_extension].join(".")
|
40
|
+
end
|
41
|
+
|
37
42
|
# Returns the format extension.
|
38
43
|
def format_extension
|
39
44
|
extensions.first
|
40
45
|
end
|
41
46
|
|
47
|
+
# The base name with the format extension.
|
48
|
+
def format_name
|
49
|
+
[basename, format_extension].join(".")
|
50
|
+
end
|
51
|
+
|
42
52
|
# Returns a list of the rendering extensions.
|
43
53
|
def template_extensions
|
44
54
|
extensions.drop(1)
|
data/lib/mascot/resource.rb
CHANGED
@@ -6,51 +6,84 @@ module Mascot
|
|
6
6
|
# resources that point to the same asset. Resources are immutable
|
7
7
|
# and may be altered by the resource proxy.
|
8
8
|
class Resource
|
9
|
-
include Observable
|
10
|
-
|
11
9
|
extend Forwardable
|
12
10
|
def_delegators :asset, :mime_type
|
13
11
|
|
14
|
-
attr_accessor :request_path, :asset
|
15
12
|
attr_writer :body, :data
|
13
|
+
attr_reader :node, :asset, :ext
|
14
|
+
|
15
|
+
# Default scope for querying parent/child/sibling resources.
|
16
|
+
DEFAULT_FILTER_SCOPE = :same
|
16
17
|
|
17
|
-
def initialize(asset: ,
|
18
|
-
self.request_path = request_path || asset.to_request_path
|
18
|
+
def initialize(asset: , node: , ext: "")
|
19
19
|
@asset = asset
|
20
|
+
@node = node
|
21
|
+
@ext = ext # TODO: Meh, feels dirty but I suppose the thingy has to drop it in.
|
20
22
|
end
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
24
|
+
def request_path
|
25
|
+
return unless node
|
26
|
+
# TODO: This `compact` makes me nervous. How can we handle this better?
|
27
|
+
lineage = node.parents.reverse.map(&:name).compact
|
28
|
+
file_name = [node.name, @ext].join
|
29
|
+
File.join("/", *lineage, file_name)
|
28
30
|
end
|
29
31
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
@
|
36
|
-
changed
|
37
|
-
notify_observers self, old_request_path
|
32
|
+
def data
|
33
|
+
@data ||= asset.data
|
34
|
+
end
|
35
|
+
|
36
|
+
def body
|
37
|
+
@body ||= asset.body
|
38
38
|
end
|
39
39
|
|
40
40
|
def inspect
|
41
|
-
"
|
41
|
+
"<#{self.class}:#{object_id} request_path=#{request_path.inspect} asset_path=#{@asset.path.to_s.inspect}>"
|
42
42
|
end
|
43
43
|
|
44
|
-
def
|
45
|
-
|
44
|
+
def parents(**args)
|
45
|
+
filter_resources(**args){ node.parents }
|
46
46
|
end
|
47
47
|
|
48
|
-
def
|
49
|
-
|
48
|
+
def siblings(**args)
|
49
|
+
filter_resources(**args){ node.siblings }
|
50
|
+
end
|
51
|
+
|
52
|
+
def children(**args)
|
53
|
+
filter_resources(**args){ node.children }
|
54
|
+
end
|
55
|
+
|
56
|
+
def ==(resource)
|
57
|
+
resource.request_path == request_path
|
50
58
|
end
|
51
59
|
|
52
|
-
|
53
|
-
|
60
|
+
private
|
61
|
+
# Filters parent/child/sibling resources by a type. The default behavior is to only return
|
62
|
+
# resources of the same type. For example given the pages `/a.html`, `/a.gif`, `/a/b.html`,
|
63
|
+
# if you query the parent from page `/a/b.html` you'd only get `/a.html` by default. If you
|
64
|
+
# query the parents via `parents(type: :all)` you'd get get [`/a.html`, `/a.gif`]
|
65
|
+
#
|
66
|
+
# TODO: When `type: :all` is scoped, some queries will mistakenly return single resources.
|
67
|
+
# :all should return an array of arrays to accurately represention levels.
|
68
|
+
#
|
69
|
+
# TODO: Put a better extension/mime_type handler into resource tree, then instead of faltening
|
70
|
+
# below and select, we could call a single map and pull out a resources
|
71
|
+
def filter_resources(type: DEFAULT_FILTER_SCOPE, &block)
|
72
|
+
return [] unless node
|
73
|
+
resources = block.call.map(&:resources)
|
74
|
+
|
75
|
+
case type
|
76
|
+
when :all
|
77
|
+
resources
|
78
|
+
when :same
|
79
|
+
resources.flatten.select { |r| r.ext == ext }
|
80
|
+
when String
|
81
|
+
resources.flatten.select { |r| r.ext == type }
|
82
|
+
when MIME::Type
|
83
|
+
resources.flatten.select { |r| r.mime_type == type }
|
84
|
+
else
|
85
|
+
raise ArgumentError, "Invalid type argument #{type}. Must be either :same, :all, an extension string, or a Mime::Type"
|
86
|
+
end
|
54
87
|
end
|
55
88
|
end
|
56
89
|
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
module Mascot
|
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
|
+
# TODO: Should it be called "formats" or "extensions"? Probably the latter, but that could conflict with Resource Manipulators.
|
9
|
+
attr_reader :parent, :name, :formats
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
DELIMITER = "/".freeze
|
13
|
+
|
14
|
+
def initialize(parent: nil, delimiter: ResourcesNode::DELIMITER, name: nil)
|
15
|
+
@parent = parent
|
16
|
+
@name = name.freeze
|
17
|
+
@delimiter = delimiter.freeze
|
18
|
+
@children = Hash.new { |hash, key| hash[key] = ResourcesNode.new(parent: self, delimiter: delimiter, name: key) }
|
19
|
+
@resources = Hash.new
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the immediate children nodes.
|
23
|
+
def children
|
24
|
+
@children.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
|
+
# Iterates through resources in the current node and all child resources.
|
44
|
+
# TODO: Should this include all children? Perhaps I should move this method
|
45
|
+
# into an eumerator that more clearly states it returns an entire sub-tree.
|
46
|
+
def each(&block)
|
47
|
+
@resources.values.each { |resource| block.call(resource) }
|
48
|
+
children.each{ |c| c.each(&block) }
|
49
|
+
end
|
50
|
+
|
51
|
+
def root?
|
52
|
+
parent.nil?
|
53
|
+
end
|
54
|
+
|
55
|
+
def leaf?
|
56
|
+
@children.empty?
|
57
|
+
end
|
58
|
+
|
59
|
+
def resources
|
60
|
+
@resources.values
|
61
|
+
end
|
62
|
+
|
63
|
+
def remove_resource(resource)
|
64
|
+
@resources.delete resource.ext #if @resources[resource.ext] == resource
|
65
|
+
end
|
66
|
+
|
67
|
+
def remove
|
68
|
+
if leaf?
|
69
|
+
# TODO: Check the parents to see if they also need to be removed if
|
70
|
+
# this call orphans the tree up to a resource.
|
71
|
+
parent.remove_child(name)
|
72
|
+
else
|
73
|
+
@resources.clear
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def add(path: , asset: )
|
78
|
+
head, *path = tokenize(path)
|
79
|
+
if path.empty?
|
80
|
+
# When there's no more paths, we're left with the format (e.g. ".html")
|
81
|
+
add_format(asset: asset, ext: head)
|
82
|
+
else
|
83
|
+
@children[head].add(path: path, asset: asset)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
alias :[]= :add
|
87
|
+
|
88
|
+
def get_resource(path)
|
89
|
+
*path, ext = tokenize(path)
|
90
|
+
if node = dig(*path)
|
91
|
+
node.get_format(ext: ext)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def inspect
|
96
|
+
"<#{self.class}: resources=#{resources.map(&:request_path)} children=#{children.map(&:name).inspect}>"
|
97
|
+
end
|
98
|
+
|
99
|
+
def get(path)
|
100
|
+
*path, ext = tokenize(path)
|
101
|
+
dig(*path)
|
102
|
+
end
|
103
|
+
alias :[] :get
|
104
|
+
|
105
|
+
protected
|
106
|
+
def get_format(ext: "")
|
107
|
+
@resources[ext]
|
108
|
+
end
|
109
|
+
|
110
|
+
def remove_child(path)
|
111
|
+
*_, segment, _ = tokenize(path)
|
112
|
+
@children.delete(segment)
|
113
|
+
end
|
114
|
+
|
115
|
+
def add_format(asset: , ext: )
|
116
|
+
resource = Resource.new(asset: asset, node: self, ext: ext)
|
117
|
+
if @resources.has_key? ext
|
118
|
+
raise Mascot::ExistingRequestPathError, "Resource at #{resource.request_path} already set"
|
119
|
+
else
|
120
|
+
@resources[ext] = resource
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# TODO: I don't really like how the path is broken up with the "ext" at the end.
|
125
|
+
# It feels inconsistent. Either make an object/struct that encaspulates this or
|
126
|
+
# just pass `index.html` through to the end.
|
127
|
+
def dig(*args)
|
128
|
+
head, *tail = args
|
129
|
+
if head.nil? and tail.empty?
|
130
|
+
self
|
131
|
+
elsif @children.has_key?(head)
|
132
|
+
@children[head].dig(*tail)
|
133
|
+
else
|
134
|
+
nil
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
# Returns all of the names for the path along with the format, if set.
|
140
|
+
def tokenize(path)
|
141
|
+
return path if path.respond_to? :to_a
|
142
|
+
path, _, file = path.gsub(/^\//, "").rpartition(@delimiter)
|
143
|
+
ext = File.extname(file)
|
144
|
+
file = File.basename(file, ext)
|
145
|
+
path.split(@delimiter).push(file).push(ext)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
data/lib/mascot/site.rb
CHANGED
@@ -22,11 +22,16 @@ module Mascot
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
def glob(glob)
|
26
|
+
paths = safe_root.glob(root.join(glob))
|
27
|
+
resources.select{ |r| paths.include? r.asset.path.to_s }
|
28
|
+
end
|
29
|
+
|
25
30
|
# Returns a list of resources.
|
26
31
|
def resources
|
27
|
-
|
28
|
-
assets.each { |a|
|
29
|
-
resources_pipeline.process
|
32
|
+
ResourcesNode.new.tap do |root_node|
|
33
|
+
assets.each { |a| root_node.add path: asset_path_to_request_path(a), asset: a }
|
34
|
+
resources_pipeline.process root_node
|
30
35
|
end
|
31
36
|
end
|
32
37
|
|
@@ -38,7 +43,7 @@ module Mascot
|
|
38
43
|
|
39
44
|
# Find the page with a path.
|
40
45
|
def get(request_path)
|
41
|
-
resources.
|
46
|
+
resources.get_resource(request_path)
|
42
47
|
end
|
43
48
|
|
44
49
|
def root=(path)
|
@@ -50,6 +55,13 @@ module Mascot
|
|
50
55
|
end
|
51
56
|
|
52
57
|
private
|
58
|
+
# Given a @file_path of `/hi`, this method changes `/hi/there/friend.html.erb`
|
59
|
+
# to an absolute `/there/friend` format by removing the file extensions
|
60
|
+
def asset_path_to_request_path(asset)
|
61
|
+
# Relative path of resource to the file_path of this project.
|
62
|
+
asset.path.dirname.join(asset.format_basename).relative_path_from(root).to_s
|
63
|
+
end
|
64
|
+
|
53
65
|
def safe_root
|
54
66
|
SafeRoot.new(path: root)
|
55
67
|
end
|
data/lib/mascot/version.rb
CHANGED
data/lib/mascot.rb
CHANGED
@@ -13,8 +13,8 @@ module Mascot
|
|
13
13
|
autoload :Asset, "mascot/asset"
|
14
14
|
autoload :Frontmatter, "mascot/frontmatter"
|
15
15
|
autoload :ResourcesPipeline, "mascot/resources_pipeline"
|
16
|
-
autoload :Resources, "mascot/resources"
|
17
16
|
autoload :Resource, "mascot/resource"
|
17
|
+
autoload :ResourcesNode, "mascot/resources_node"
|
18
18
|
autoload :SafeRoot, "mascot/safe_root"
|
19
|
-
autoload :Site,
|
19
|
+
autoload :Site, "mascot/site"
|
20
20
|
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.12
|
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-08-
|
11
|
+
date: 2016-08-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -79,7 +79,7 @@ files:
|
|
79
79
|
- lib/mascot/extensions/proc_manipulator.rb
|
80
80
|
- lib/mascot/frontmatter.rb
|
81
81
|
- lib/mascot/resource.rb
|
82
|
-
- lib/mascot/
|
82
|
+
- lib/mascot/resources_node.rb
|
83
83
|
- lib/mascot/resources_pipeline.rb
|
84
84
|
- lib/mascot/safe_root.rb
|
85
85
|
- lib/mascot/site.rb
|
data/lib/mascot/resources.rb
DELETED
@@ -1,103 +0,0 @@
|
|
1
|
-
require "forwardable"
|
2
|
-
require "pathname"
|
3
|
-
|
4
|
-
module Mascot
|
5
|
-
class Resources
|
6
|
-
include Enumerable
|
7
|
-
|
8
|
-
extend Forwardable
|
9
|
-
def_delegators :@routes, :size, :empty?, :any?, :clear
|
10
|
-
|
11
|
-
def initialize(root_file_path: )
|
12
|
-
@routes = Hash.new
|
13
|
-
@root_file_path = Pathname.new(root_file_path)
|
14
|
-
end
|
15
|
-
|
16
|
-
def each(&block)
|
17
|
-
@routes.values.each(&block)
|
18
|
-
end
|
19
|
-
|
20
|
-
def last
|
21
|
-
@routes.values.last
|
22
|
-
end
|
23
|
-
|
24
|
-
def request_paths
|
25
|
-
@routes.keys
|
26
|
-
end
|
27
|
-
|
28
|
-
def glob(pattern = "**/**")
|
29
|
-
paths = safe_root.glob @root_file_path.join(pattern)
|
30
|
-
select { |r| paths.include? r.asset.path.to_s}
|
31
|
-
end
|
32
|
-
|
33
|
-
def get(request_path)
|
34
|
-
return if request_path.nil?
|
35
|
-
@routes[key(request_path)]
|
36
|
-
end
|
37
|
-
|
38
|
-
def add(resource)
|
39
|
-
validate_request_path resource
|
40
|
-
validate_uniqueness resource
|
41
|
-
|
42
|
-
resource.add_observer self
|
43
|
-
@routes[key(resource)] = resource
|
44
|
-
end
|
45
|
-
|
46
|
-
def update(resource, old_request_path)
|
47
|
-
validate_request_path old_request_path
|
48
|
-
validate_request_path resource
|
49
|
-
validate_uniqueness resource
|
50
|
-
|
51
|
-
@routes.delete key(old_request_path)
|
52
|
-
@routes[key(resource)] = resource
|
53
|
-
end
|
54
|
-
|
55
|
-
def remove(resource)
|
56
|
-
validate_request_path resource
|
57
|
-
resource.delete_observer self
|
58
|
-
@routes.delete key(resource)
|
59
|
-
resource
|
60
|
-
end
|
61
|
-
|
62
|
-
def add_asset(asset, request_path: nil)
|
63
|
-
add Resource.new asset: asset, request_path: asset_path_to_request_path(request_path || asset.to_request_path)
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
def key(path)
|
68
|
-
# TODO: Conslidate this into SafeRoot.
|
69
|
-
File.join "/", validate_request_path(coerce_request_path(path))
|
70
|
-
end
|
71
|
-
|
72
|
-
def coerce_request_path(resource)
|
73
|
-
resource.respond_to?(:request_path) ? resource.request_path : resource
|
74
|
-
end
|
75
|
-
|
76
|
-
def validate_request_path(path)
|
77
|
-
path = coerce_request_path(path)
|
78
|
-
raise InvalidRequestPathError, "path can't be nil" if path.nil?
|
79
|
-
path
|
80
|
-
end
|
81
|
-
|
82
|
-
# Raise an exception if the user tries to add a Resource with an existing request path.
|
83
|
-
def validate_uniqueness(resource)
|
84
|
-
path = coerce_request_path(resource)
|
85
|
-
if existing_resource = get(path)
|
86
|
-
raise ExistingRequestPathError, "Resource #{existing_resource} already exists at #{path}"
|
87
|
-
else
|
88
|
-
resource
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# Given a @file_path of `/hi`, this method changes `/hi/there/friend.html.erb`
|
93
|
-
# to an absolute `/there/friend` format by removing the file extensions
|
94
|
-
def asset_path_to_request_path(path)
|
95
|
-
# Relative path of resource to the file_path of this project.
|
96
|
-
relative_path = Pathname.new(path).relative_path_from(@root_file_path)
|
97
|
-
end
|
98
|
-
|
99
|
-
def safe_root
|
100
|
-
@safe_root ||= SafeRoot.new(path: @root_file_path)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|