sitepress-core 0.1.21

Sign up to get free protection for your applications and to get access to all the features.
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: