mascot 0.1.12 → 0.1.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1d705fe26af1339418f2018ebeed0af942e6b49e
4
- data.tar.gz: d0fbb9055d2b7214078eb1de963065e8f1949c9b
3
+ metadata.gz: 6be4b1751690e6c59370910168e633fa512d4179
4
+ data.tar.gz: 953289c76d1245c7e037a5a32ededad0321ca8cc
5
5
  SHA512:
6
- metadata.gz: 95f73264703dccec615c082cf1f071237160b94e967903842c7f889da8dae7617c3a5931539ea0be5fbb2fbd070959cfc17bfc800c67eca0c758976249e88f01
7
- data.tar.gz: ab29c4b7f17646864334e14007b1bf27feab5960c9e5fa88a805785dab23421b62727b8b12d813519105a50555c5f116c6a74a82d34e35bbc46786747b9ba457
6
+ metadata.gz: 61471911d2ef4efb0c017e371d4fa563af64116e40360a0eff0062d14f2397343755c9de4930ea01a8470f66408ef29a136994a1d07236e715ec42b136612df9
7
+ data.tar.gz: 2a24c41a5618965f225570b912346fa0d2b9863076c2e1e1d876efe6ea78dc79ee65847297dfcd64dc45f86a26f2868e288bfcb91c5d6e6c6f9658c205eb57d9
data/lib/mascot.rb CHANGED
@@ -1,20 +1,18 @@
1
1
  require "mascot/version"
2
2
 
3
3
  module Mascot
4
- # Raised if a user attempts to access a resource outside of the site path.
5
- UnsafePathAccessError = Class.new(SecurityError)
6
-
7
4
  # Raised by Resources if a path is added that's not a valid path.
8
5
  InvalidRequestPathError = Class.new(RuntimeError)
9
6
 
10
7
  # Raised by Resources if a path is already in its index
11
8
  ExistingRequestPathError = Class.new(InvalidRequestPathError)
12
9
 
13
- autoload :Asset, "mascot/asset"
14
- autoload :Frontmatter, "mascot/frontmatter"
15
- autoload :ResourcesPipeline, "mascot/resources_pipeline"
16
- autoload :Resource, "mascot/resource"
17
- autoload :ResourcesNode, "mascot/resources_node"
18
- autoload :SafeRoot, "mascot/safe_root"
19
- autoload :Site, "mascot/site"
10
+ autoload :Asset, "mascot/asset"
11
+ autoload :DirectoryCollection, "mascot/directory_collection"
12
+ autoload :Formats, "mascot/formats"
13
+ autoload :Frontmatter, "mascot/frontmatter"
14
+ autoload :Resource, "mascot/resource"
15
+ autoload :ResourcesPipeline, "mascot/resources_pipeline"
16
+ autoload :ResourcesNode, "mascot/resources_node"
17
+ autoload :Site, "mascot/site"
20
18
  end
@@ -0,0 +1,24 @@
1
+ module Mascot
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
@@ -13,8 +13,8 @@ module Mascot
13
13
  @rules << Rule.new(layout, block)
14
14
  end
15
15
 
16
- def process_resources(resources)
17
- resources.each do |resource|
16
+ def process_resources(node)
17
+ node.resources.each do |resource|
18
18
  @rules.each do |rule|
19
19
  if rule.processor.call(resource)
20
20
  resource.data["layout"] ||= rule.layout
@@ -5,12 +5,12 @@ module Mascot
5
5
  @block = block
6
6
  end
7
7
 
8
- def process_resources(resources)
9
- resources.each do |resource|
8
+ def process_resources(node)
9
+ node.resources.each do |resource|
10
10
  if @block.arity == 1
11
11
  @block.call resource
12
12
  else # This will blow up if 0 or greater than 2.
13
- @block.call resource, resources
13
+ @block.call resource, node
14
14
  end
15
15
  end
16
16
  end
@@ -0,0 +1,45 @@
1
+ module Mascot
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 Mascot::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
@@ -1,5 +1,4 @@
1
1
  require "forwardable"
2
- require "observer"
3
2
 
4
3
  module Mascot
5
4
  # Represents the request path of an asset. There may be multiple
@@ -70,17 +69,17 @@ module Mascot
70
69
  # below and select, we could call a single map and pull out a resources
71
70
  def filter_resources(type: DEFAULT_FILTER_SCOPE, &block)
72
71
  return [] unless node
73
- resources = block.call.map(&:resources)
72
+ nodes = block.call
74
73
 
75
74
  case type
76
75
  when :all
77
- resources
76
+ nodes.map(&:formats)
78
77
  when :same
79
- resources.flatten.select { |r| r.ext == ext }
78
+ nodes.map{ |n| n.formats.ext(ext) }.flatten.compact
80
79
  when String
81
- resources.flatten.select { |r| r.ext == type }
80
+ nodes.map{ |n| n.formats.ext(type) }.flatten.compact
82
81
  when MIME::Type
83
- resources.flatten.select { |r| r.mime_type == type }
82
+ nodes.map{ |n| n.formats.mime_type(type) }.flatten.compact
84
83
  else
85
84
  raise ArgumentError, "Invalid type argument #{type}. Must be either :same, :all, an extension string, or a Mime::Type"
86
85
  end
@@ -5,9 +5,7 @@ module Mascot
5
5
  # a leaf node. The actual `buz.html` asset is then stored on the leaf node as a resource. This tree structure
6
6
  # makes it possible to reason through path relationships from code to build out elements in a website like tree navigation.
7
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
8
+ attr_reader :parent, :name
11
9
 
12
10
  DELIMITER = "/".freeze
13
11
 
@@ -15,13 +13,15 @@ module Mascot
15
13
  @parent = parent
16
14
  @name = name.freeze
17
15
  @delimiter = delimiter.freeze
18
- @children = Hash.new { |hash, key| hash[key] = ResourcesNode.new(parent: self, delimiter: delimiter, name: key) }
19
- @resources = Hash.new
16
+ end
17
+
18
+ def formats
19
+ @formats ||= Formats.new(node: self)
20
20
  end
21
21
 
22
22
  # Returns the immediate children nodes.
23
23
  def children
24
- @children.values
24
+ child_nodes.values
25
25
  end
26
26
 
27
27
  # Returns sibling nodes.
@@ -40,28 +40,34 @@ module Mascot
40
40
  parents
41
41
  end
42
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
43
  def root?
52
44
  parent.nil?
53
45
  end
54
46
 
55
47
  def leaf?
56
- @children.empty?
48
+ child_nodes.empty?
57
49
  end
58
50
 
59
- def resources
60
- @resources.values
51
+ class Resources
52
+ include Enumerable
53
+
54
+ def initialize(node: )
55
+ @node = node
56
+ end
57
+
58
+ def each(&block)
59
+ @node.formats.each(&block)
60
+ @node.children.each { |child| child.resources.each(&block) }
61
+ end
62
+
63
+ def glob(pattern)
64
+ paths = Dir.glob(pattern)
65
+ select { |r| paths.include? r.asset.path.to_s }
66
+ end
61
67
  end
62
68
 
63
- def remove_resource(resource)
64
- @resources.delete resource.ext #if @resources[resource.ext] == resource
69
+ def resources
70
+ Resources.new(node: self)
65
71
  end
66
72
 
67
73
  def remove
@@ -70,7 +76,7 @@ module Mascot
70
76
  # this call orphans the tree up to a resource.
71
77
  parent.remove_child(name)
72
78
  else
73
- @resources.clear
79
+ formats.clear
74
80
  end
75
81
  end
76
82
 
@@ -78,9 +84,9 @@ module Mascot
78
84
  head, *path = tokenize(path)
79
85
  if path.empty?
80
86
  # When there's no more paths, we're left with the format (e.g. ".html")
81
- add_format(asset: asset, ext: head)
87
+ formats.add(asset: asset, ext: head)
82
88
  else
83
- @children[head].add(path: path, asset: asset)
89
+ child_nodes[head].add(path: path, asset: asset)
84
90
  end
85
91
  end
86
92
  alias :[]= :add
@@ -88,37 +94,18 @@ module Mascot
88
94
  def get_resource(path)
89
95
  *path, ext = tokenize(path)
90
96
  if node = dig(*path)
91
- node.get_format(ext: ext)
97
+ node.formats.ext(ext)
92
98
  end
93
99
  end
94
100
 
95
- def inspect
96
- "<#{self.class}: resources=#{resources.map(&:request_path)} children=#{children.map(&:name).inspect}>"
97
- end
98
-
99
101
  def get(path)
100
102
  *path, ext = tokenize(path)
101
103
  dig(*path)
102
104
  end
103
105
  alias :[] :get
104
106
 
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
107
+ def inspect
108
+ "<#{self.class}: formats=#{formats.map(&:request_path)} children=#{children.map(&:name).inspect}>"
122
109
  end
123
110
 
124
111
  # TODO: I don't really like how the path is broken up with the "ext" at the end.
@@ -128,14 +115,28 @@ module Mascot
128
115
  head, *tail = args
129
116
  if head.nil? and tail.empty?
130
117
  self
131
- elsif @children.has_key?(head)
132
- @children[head].dig(*tail)
118
+ elsif child_nodes.has_key?(head)
119
+ child_nodes[head].dig(*tail)
133
120
  else
134
121
  nil
135
122
  end
136
123
  end
137
124
 
125
+ protected
126
+ def remove_child(path)
127
+ *_, segment, _ = tokenize(path)
128
+ child_nodes.delete(segment)
129
+ end
130
+
138
131
  private
132
+ def add_child_node(name)
133
+ ResourcesNode.new(parent: self, delimiter: @delimiter, name: name)
134
+ end
135
+
136
+ def child_nodes
137
+ @child_nodes ||= Hash.new { |hash, key| hash[key] = add_child_node(key) }
138
+ end
139
+
139
140
  # Returns all of the names for the path along with the format, if set.
140
141
  def tokenize(path)
141
142
  return path if path.respond_to? :to_a
data/lib/mascot/site.rb CHANGED
@@ -6,31 +6,24 @@ module Mascot
6
6
  class Site
7
7
  # Default file pattern to pick up in site
8
8
  DEFAULT_GLOB = "**/**".freeze
9
- # Default root path for site.
10
- DEFAULT_ROOT_PATH = Pathname.new(".").freeze
11
9
 
12
- attr_reader :root, :resources_pipeline
10
+ # Default root_path for site.
11
+ DEFAULT_ROOT_PATH = Pathname.new(".").freeze
13
12
 
14
- def initialize(root: DEFAULT_ROOT_PATH)
15
- self.root = root
16
- end
13
+ attr_reader :root_path, :resources_pipeline
17
14
 
18
- # Lazy stream of files that will be rendered by resources.
19
- def assets(glob = DEFAULT_GLOB)
20
- safe_root.glob(root.join(glob)).select(&File.method(:file?)).lazy.map do |path|
21
- Asset.new(path: path)
22
- end
15
+ def initialize(root_path: DEFAULT_ROOT_PATH)
16
+ self.root_path = root_path
23
17
  end
24
18
 
25
19
  def glob(glob)
26
- paths = safe_root.glob(root.join(glob))
27
- resources.select{ |r| paths.include? r.asset.path.to_s }
20
+ root.resources.glob(root_path.join(glob))
28
21
  end
29
22
 
30
23
  # Returns a list of resources.
31
- def resources
24
+ def root
32
25
  ResourcesNode.new.tap do |root_node|
33
- assets.each { |a| root_node.add path: asset_path_to_request_path(a), asset: a }
26
+ DirectoryCollection.new(assets: assets, path: root_path).mount(root_node)
34
27
  resources_pipeline.process root_node
35
28
  end
36
29
  end
@@ -43,11 +36,11 @@ module Mascot
43
36
 
44
37
  # Find the page with a path.
45
38
  def get(request_path)
46
- resources.get_resource(request_path)
39
+ root.get_resource(request_path)
47
40
  end
48
41
 
49
- def root=(path)
50
- @root = Pathname.new(path)
42
+ def root_path=(path)
43
+ @root_path = Pathname.new(path)
51
44
  end
52
45
 
53
46
  def resources_pipeline
@@ -55,15 +48,11 @@ module Mascot
55
48
  end
56
49
 
57
50
  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
-
65
- def safe_root
66
- SafeRoot.new(path: root)
51
+ # Lazy stream of files that will be rendered by resources.
52
+ def assets(glob = DEFAULT_GLOB)
53
+ Dir.glob(root_path.join(glob)).select(&File.method(:file?)).lazy.map do |path|
54
+ Asset.new(path: path)
55
+ end
67
56
  end
68
57
  end
69
58
  end
@@ -1,3 +1,3 @@
1
1
  module Mascot
2
- VERSION = "0.1.12"
2
+ VERSION = "0.1.14"
3
3
  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.12
4
+ version: 0.1.14
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-18 00:00:00.000000000 Z
11
+ date: 2016-09-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -75,13 +75,14 @@ extra_rdoc_files: []
75
75
  files:
76
76
  - lib/mascot.rb
77
77
  - lib/mascot/asset.rb
78
+ - lib/mascot/directory_collection.rb
78
79
  - lib/mascot/extensions/layouts.rb
79
80
  - lib/mascot/extensions/proc_manipulator.rb
81
+ - lib/mascot/formats.rb
80
82
  - lib/mascot/frontmatter.rb
81
83
  - lib/mascot/resource.rb
82
84
  - lib/mascot/resources_node.rb
83
85
  - lib/mascot/resources_pipeline.rb
84
- - lib/mascot/safe_root.rb
85
86
  - lib/mascot/site.rb
86
87
  - lib/mascot/version.rb
87
88
  - mascot.gemspec
@@ -1,42 +0,0 @@
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 site,
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