front_matter_parser 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +3 -0
- data/.yardopts +2 -0
- data/COPYING.LESSER +165 -0
- data/COPYING.txt +674 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +123 -0
- data/Rakefile +6 -0
- data/bin/autospec +16 -0
- data/bin/rake +16 -0
- data/bin/rspec +16 -0
- data/front_matter_parser.gemspec +24 -0
- data/lib/front_matter_parser.rb +103 -0
- data/lib/front_matter_parser/parsed.rb +18 -0
- data/lib/front_matter_parser/version.rb +3 -0
- data/spec/fixtures/example.coffee +4 -0
- data/spec/fixtures/example.foo +0 -0
- data/spec/fixtures/example.haml +5 -0
- data/spec/fixtures/example.html +6 -0
- data/spec/fixtures/example.liquid +6 -0
- data/spec/fixtures/example.md +4 -0
- data/spec/fixtures/example.sass +4 -0
- data/spec/fixtures/example.scss +4 -0
- data/spec/fixtures/example.slim +5 -0
- data/spec/front_matter_parser/parsed_spec.rb +28 -0
- data/spec/front_matter_parser_spec.rb +192 -0
- data/spec/spec_helper.rb +2 -0
- metadata +141 -0
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 marc
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# FrontMatterParser
|
2
|
+
|
3
|
+
FrontMatterParser is a library to parse files or strings with YAML front matters. When working with files, it can automatically detect the syntax of a file from its extension and it supposes that the front matter is marked as that syntax comments.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'front_matter_parser'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install front_matter_parser
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Front matters must be between two lines with three dashes `---`.
|
22
|
+
|
23
|
+
Given a file `example.md`:
|
24
|
+
|
25
|
+
---
|
26
|
+
title: Hello World
|
27
|
+
category: Greetings
|
28
|
+
---
|
29
|
+
Some actual content
|
30
|
+
|
31
|
+
You can parse it:
|
32
|
+
|
33
|
+
parsed = FrontMatterParser.parse_file('example.md')
|
34
|
+
parsed.front_matter #=> {'title' => 'Hello World'}
|
35
|
+
parsed.content #=> 'Some actual content'
|
36
|
+
|
37
|
+
You can apply directly `[]` method to get a front matter value:
|
38
|
+
|
39
|
+
parsed['category'] #=> 'Greetings'
|
40
|
+
|
41
|
+
### Syntax autodetection
|
42
|
+
|
43
|
+
`FrontMatterParser` detects the syntax of a file by its extension and suppose that the front matter is within that syntax comments delimiters.
|
44
|
+
|
45
|
+
Given a file `example.haml`:
|
46
|
+
|
47
|
+
-#
|
48
|
+
---
|
49
|
+
title: Hello
|
50
|
+
---
|
51
|
+
Content
|
52
|
+
|
53
|
+
The `-#` and the indentation enclose the front matter as a comment. `FrontMatterParser` is aware of it and you can simply do:
|
54
|
+
|
55
|
+
title = FrontMatterParser.parse_file('example.haml')['title'] #=> 'Hello'
|
56
|
+
|
57
|
+
Following there is a relation of known syntaxes and their known comment delimiters:
|
58
|
+
|
59
|
+
<pre>
|
60
|
+
| Syntax | Single line comment | Start multiline comment | End multiline comment |
|
61
|
+
| ------ | ------------------- | ----------------------- | ---------------------- |
|
62
|
+
| haml | | -# | (indentation) |
|
63
|
+
| slim | | / | (indentation) |
|
64
|
+
| liquid | | <% comment %> | <% endcomment %> |
|
65
|
+
| md | | | |
|
66
|
+
| html | | <!-- | --> |
|
67
|
+
| coffee | # | | |
|
68
|
+
| sass | // | | |
|
69
|
+
| scss | // | | |
|
70
|
+
</pre>
|
71
|
+
|
72
|
+
You can provide your own by passing `autodetect: false` and options for single line comment delimiter (`:comment`) or start multiline comment (`:start_comment`) and end multiline comment (`:end_comment`) delimiters. If `:start_comment` is provided but it isn't `:end_comment`, then it is supposed that the multiline comment is ended by indentation.
|
73
|
+
|
74
|
+
FrontMatterParser.parse_file('example.haml', autodetect: false, start_comment: '<!--', end_comment: '-->') # start and end multiline comment delimiters
|
75
|
+
FrontMatterParser.parse_file('example.slim', autodetect: false, start_comment: '/!') # multiline comment closed by indentation
|
76
|
+
FrontMatterParser.parse_file('example.foo', autodetect: false, comment: '#') # single line comments
|
77
|
+
|
78
|
+
### Parsing a string
|
79
|
+
|
80
|
+
You can as well parse a string, providing manually its comment delimiters if needed:
|
81
|
+
|
82
|
+
string = File.read('example.slim')
|
83
|
+
FrontMatterParser.parse(string, start_comment: '/')
|
84
|
+
|
85
|
+
## Contributing
|
86
|
+
|
87
|
+
1. Fork it
|
88
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
89
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
90
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
91
|
+
5. Create new Pull Request
|
92
|
+
|
93
|
+
## Release Policy
|
94
|
+
|
95
|
+
`front_matter_parser` follows the principles of [semantic versioning](http://semver.org/).
|
96
|
+
|
97
|
+
## To Do
|
98
|
+
|
99
|
+
* Add more known syntaxes.
|
100
|
+
* Allow configuration of global front matter delimiters. It would be easy, but I'm not sure if too useful.
|
101
|
+
* Allow different formats (as JSON). Again, I'm not sure if it would be very useful.
|
102
|
+
|
103
|
+
## Other ruby front matter parsers
|
104
|
+
|
105
|
+
* [front-matter](https://github.com/zhaocai/front-matter.rb) Can parse YAML front matters with single line comments delimiters. YAML must be correctly indented.
|
106
|
+
* [ruby_front_matter](https://github.com/F-3r/ruby_front_matter) Can parse JSON front matters and can configure front matter global delimiters, but does not accept comment delimiters.
|
107
|
+
|
108
|
+
## LICENSE
|
109
|
+
|
110
|
+
Copyright 2013 Marc Busqué - <marc@lamarciana.com>
|
111
|
+
|
112
|
+
This program is free software: you can redistribute it and/or modify
|
113
|
+
it under the terms of the GNU General Public License as published by
|
114
|
+
the Free Software Foundation, either version 3 of the License, or
|
115
|
+
(at your option) any later version.
|
116
|
+
|
117
|
+
This program is distributed in the hope that it will be useful,
|
118
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
119
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
120
|
+
GNU General Public License for more details.
|
121
|
+
|
122
|
+
You should have received a copy of the GNU General Public License
|
123
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
data/Rakefile
ADDED
data/bin/autospec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'autospec' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rspec-core', 'autospec')
|
data/bin/rake
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rake' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rake', 'rake')
|
data/bin/rspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rspec' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rspec-core', 'rspec')
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'front_matter_parser/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "front_matter_parser"
|
8
|
+
spec.version = FrontMatterParser::VERSION
|
9
|
+
spec.authors = ["marc"]
|
10
|
+
spec.email = ["marc@lamarciana.com"]
|
11
|
+
spec.description = %q{Library to parse files or strings with YAML front matters with syntax autodetection.}
|
12
|
+
spec.summary = %q{FrontMatterParser is a library to parse files or strings with YAML front matters. When working with files, it can automatically detect the syntax of a file from its extension and it supposes that the front matter is marked as that syntax comments.}
|
13
|
+
spec.homepage = "https://github.com/laMarciana/front_matter_parser"
|
14
|
+
spec.license = "LGPL3"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.5", "<1.6"
|
22
|
+
spec.add_development_dependency "rake", "~>10.1"
|
23
|
+
spec.add_development_dependency "rspec", "~> 2.14"
|
24
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require "front_matter_parser/version"
|
3
|
+
require "front_matter_parser/parsed"
|
4
|
+
|
5
|
+
# FrontMatterParser module is the entry point to parse strings or files with YAML front matters. When working with files, it can automatically detect the syntax of a file from its extension and it supposes that the front matter is marked as that syntax comments.
|
6
|
+
module FrontMatterParser
|
7
|
+
# {Hash {Symbol => Array}} Comments delimiters used in FrontMatterParser known syntaxes. Keys are file extensions, and values are three elements array:
|
8
|
+
#
|
9
|
+
# * First element is single line comment delimiter.
|
10
|
+
# * Second element is the start multiline comment delimiter.
|
11
|
+
# * Third element is the end multiline comment delimiter. If it is `nil` and start multiline comment delimiter isn't, it means that the comment is closed by indentation.
|
12
|
+
COMMENT_DELIMITERS = {
|
13
|
+
slim: [nil, '/', nil],
|
14
|
+
html: [nil, '<!--', '-->'],
|
15
|
+
coffee: ['#', nil, nil],
|
16
|
+
haml: [nil, '-#', nil],
|
17
|
+
liquid: [nil, '<% comment %>', '<% endcomment %>'],
|
18
|
+
sass: ['//', nil, nil],
|
19
|
+
scss: ['//', nil, nil],
|
20
|
+
md: [nil, nil, nil],
|
21
|
+
}
|
22
|
+
|
23
|
+
# Parses a string into a {Parsed} instance. For comment options, see {COMMENT_DELIMITERS} values (but they are not limited to those for the known syntaxes).
|
24
|
+
#
|
25
|
+
# @param string [String] The string to parse
|
26
|
+
# @param opts [Hash] Options
|
27
|
+
# @option opts [String, nil] :comment Single line comment delimiter
|
28
|
+
# @option opts [String, nil] :start_comment Start multiline comment delimiter
|
29
|
+
# @option opts [String, nil] :end_comment End multiline comment delimiter
|
30
|
+
# @return [Parsed]
|
31
|
+
# @raise [ArgumentError] If end_comment option is provided but not start_comment
|
32
|
+
# @see COMMENT_DELIMITERS
|
33
|
+
def self.parse(string, opts = {})
|
34
|
+
opts = {
|
35
|
+
comment: nil,
|
36
|
+
start_comment: nil,
|
37
|
+
end_comment: nil,
|
38
|
+
}.merge(opts)
|
39
|
+
raise(ArgumentError, "If you provide the `end_comment` option, you must provide also the `start_comment` option") if (opts[:end_comment] != nil and opts[:start_comment] == nil)
|
40
|
+
parsed = Parsed.new
|
41
|
+
if matches = (string.match(/
|
42
|
+
# Start of string
|
43
|
+
\A
|
44
|
+
# Zero or more space characters
|
45
|
+
([[:space:]]*)
|
46
|
+
# Start multiline comment
|
47
|
+
#{'(?-x:(?<multiline_comment_indentation>^[[:blank:]]*)'+opts[:start_comment]+'[[:blank:]]*[\n\r][[:space:]]*)' unless opts[:start_comment].nil?}
|
48
|
+
# Begin front matter
|
49
|
+
(?-x:^[[:blank:]]*#{opts[:comment]}[[:blank:]]*---[[:blank:]]*$[\n\r])
|
50
|
+
# The front matter
|
51
|
+
(?<front_matter>.*)
|
52
|
+
# End front matter
|
53
|
+
(?-x:^[[:blank:]]*#{opts[:comment]}[[:blank:]]*---[[:blank:]]*$[\n\r])
|
54
|
+
# End multiline comment
|
55
|
+
#{'(?-x:\k<multiline_comment_indentation>)' if opts[:end_comment].nil? and not opts[:start_comment].nil?}
|
56
|
+
#{'(?-x:[[:space:]]*^[[:blank:]]*'+opts[:end_comment]+'[[:blank:]]*[\n\r])' if not opts[:end_comment].nil?}
|
57
|
+
# The content
|
58
|
+
(?<content>.*)
|
59
|
+
# End of string
|
60
|
+
\z
|
61
|
+
/mx))
|
62
|
+
front_matter = matches[:front_matter].gsub(/^#{opts[:comment]}/, '')
|
63
|
+
parsed.front_matter = YAML.load(front_matter)
|
64
|
+
parsed.content = matches[:content]
|
65
|
+
else
|
66
|
+
parsed.front_matter = {}
|
67
|
+
parsed.content = string
|
68
|
+
end
|
69
|
+
parsed
|
70
|
+
end
|
71
|
+
|
72
|
+
# Parses a file into a {Parsed} instance. If autodetect option is `true`, comment delimiters are guessed from the file extension. If it is `false` comment options are taken into consideration. See {COMMENT_DELIMITERS} for a list of known syntaxes and the comment delimiters values.
|
73
|
+
#
|
74
|
+
# @param pathname [String] The path to the file
|
75
|
+
# @param opts [Hash] Options
|
76
|
+
# @option opts [Boolean] :autodetect If it is true, FrontMatterParser uses the comment delimiters known for the syntax of the file and comment options are ignored. If it is false, comment options are taken into consideration.
|
77
|
+
# @option opts [String, nil] :comment Single line comment delimiter
|
78
|
+
# @option opts [String, nil] :start_comment Start multiline comment delimiter
|
79
|
+
# @option opts [String, nil] :end_comment End multiline comment delimiter
|
80
|
+
# @return [Parsed]
|
81
|
+
# @raise [ArgumentError] If autodetect option is false, and start_comment option is provided but not end_comment
|
82
|
+
# @raise [RuntimeError] If the syntax of the file (the extension) is not within the keys of {COMMENT_DELIMITERS}
|
83
|
+
# @see COMMENT_DELIMITERS
|
84
|
+
def self.parse_file(pathname, opts={})
|
85
|
+
opts = {
|
86
|
+
autodetect: true,
|
87
|
+
comment: nil,
|
88
|
+
start_comment: nil,
|
89
|
+
end_comment: nil,
|
90
|
+
}.merge(opts)
|
91
|
+
if opts[:autodetect]
|
92
|
+
ext = File.extname(pathname)[1 .. -1].to_sym
|
93
|
+
raise(RuntimeError, "Comment delimiters for extension #{ext.to_s} not known. Please, call #parse_file without autodetect option and provide comment delimiters.") unless COMMENT_DELIMITERS.has_key?(ext)
|
94
|
+
comment_delimiters = COMMENT_DELIMITERS[ext]
|
95
|
+
opts[:comment] = comment_delimiters[0]
|
96
|
+
opts[:start_comment] = comment_delimiters[1]
|
97
|
+
opts[:end_comment] = comment_delimiters[2]
|
98
|
+
end
|
99
|
+
File.open(pathname) do |file|
|
100
|
+
parse(file.read, comment: opts[:comment], start_comment: opts[:start_comment], end_comment: opts[:end_comment])
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Parsed is the result of calling {FrontMatterParser.parse} or {FrontMatterParser.parse_file} in {FrontMatterParser}. It keeps the front matter and the content parsed and it has some useful methods to work with them.
|
2
|
+
class FrontMatterParser::Parsed
|
3
|
+
# @!attribute [rw] front_matter
|
4
|
+
# @return [Hash{String => String, Array, Hash}] the parsed front matter
|
5
|
+
# @!attribute [rw] content
|
6
|
+
# @return [String] the parsed content
|
7
|
+
attr_accessor :front_matter, :content
|
8
|
+
|
9
|
+
alias_method :to_hash, :front_matter
|
10
|
+
|
11
|
+
# Returns the front matter value for the given key
|
12
|
+
#
|
13
|
+
# @param key [String] The key of the front matter
|
14
|
+
# @return [String, Array, Hash] The value of the front matter for the given key
|
15
|
+
def [](key)
|
16
|
+
@front_matter[key]
|
17
|
+
end
|
18
|
+
end
|
File without changes
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module FrontMatterParser
|
4
|
+
describe Parsed do
|
5
|
+
|
6
|
+
let(:sample) { {'title' => 'hello'} }
|
7
|
+
|
8
|
+
let(:string) { %Q(
|
9
|
+
---
|
10
|
+
title: hello
|
11
|
+
---
|
12
|
+
Content) }
|
13
|
+
|
14
|
+
let(:parsed) { FrontMatterParser.parse(string) }
|
15
|
+
|
16
|
+
describe "#to_hash" do
|
17
|
+
it "returns @front_matter" do
|
18
|
+
expect(parsed.to_hash).to eq(sample)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#[]" do
|
23
|
+
it "returns the front matter value for the given key" do
|
24
|
+
expect(parsed['title']).to eq('hello')
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|