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 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,10 @@
1
+ require "forwardable"
2
+
3
+ module Sitepress
4
+ # Processes a collection of resources
5
+ class ResourcesPipeline < Array
6
+ def process(resources)
7
+ each{ |processor| processor.process_resources resources }
8
+ end
9
+ end
10
+ 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: