hologram 1.0.1 → 1.1.0
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/.travis.yml +2 -2
- data/README.md +169 -82
- data/{lib/hologram_markdown_renderer.rb → example_markdown_renderer.rb.example} +6 -2
- data/hologram.gemspec +3 -3
- data/lib/hologram.rb +11 -1
- data/lib/hologram/cli.rb +45 -0
- data/lib/hologram/doc_block_collection.rb +23 -20
- data/lib/hologram/doc_builder.rb +160 -155
- data/lib/hologram/doc_parser.rb +26 -25
- data/lib/hologram/document_block.rb +27 -5
- data/lib/hologram/errors.rb +12 -0
- data/lib/hologram/markdown_renderer.rb +49 -0
- data/lib/hologram/template_variables.rb +1 -1
- data/lib/hologram/utils.rb +16 -0
- data/lib/hologram/version.rb +1 -1
- data/lib/template/hologram_config.yml +2 -2
- data/spec/cli_spec.rb +37 -0
- data/spec/display_message_spec.rb +0 -17
- data/spec/doc_block_collection_spec.rb +4 -4
- data/spec/doc_builder_spec.rb +131 -48
- data/spec/doc_parser_spec.rb +64 -3
- data/spec/document_block_spec.rb +65 -4
- data/spec/fixtures/renderer/invalid_renderer.rb +2 -0
- data/spec/fixtures/renderer/valid_renderer.rb +2 -0
- data/spec/hologram_markdown_renderer_spec.rb +14 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/utils_spec.rb +52 -0
- metadata +36 -22
@@ -1,35 +1,57 @@
|
|
1
1
|
module Hologram
|
2
2
|
class DocumentBlock
|
3
|
-
|
3
|
+
COMMENT_REGEX = /^\s*---\s(.*?)\s---$/m
|
4
|
+
|
5
|
+
attr_accessor :name, :parent, :children, :title, :categories, :markdown, :config, :heading, :errors
|
4
6
|
|
5
7
|
def initialize(config = nil, markdown = nil)
|
6
8
|
@children = {}
|
9
|
+
@errors = []
|
7
10
|
set_members(config, markdown) if config and markdown
|
8
11
|
end
|
9
12
|
|
13
|
+
def self.from_comment(comment)
|
14
|
+
comment_match = COMMENT_REGEX.match(comment)
|
15
|
+
return if !comment_match
|
16
|
+
|
17
|
+
markdown = comment.sub(comment_match[0], '')
|
18
|
+
config = YAML::load(comment_match[1])
|
19
|
+
|
20
|
+
self.new(config, markdown)
|
21
|
+
rescue ArgumentError, Psych::SyntaxError
|
22
|
+
raise CommentLoadError, "Could not parse comment:\n#{comment}"
|
23
|
+
end
|
24
|
+
|
10
25
|
def set_members(config, markdown)
|
11
26
|
@name = config['name']
|
12
|
-
@
|
27
|
+
@categories = Array(config['category'] || config['categories'])
|
13
28
|
@title = config['title']
|
14
29
|
@parent = config['parent']
|
15
30
|
@markdown = markdown
|
31
|
+
|
32
|
+
if @name.nil?
|
33
|
+
@name = @title.gsub(' ', '_')
|
34
|
+
end
|
16
35
|
end
|
17
36
|
|
18
37
|
def get_hash
|
19
38
|
{:name => @name,
|
20
39
|
:parent => @parent,
|
21
|
-
:
|
40
|
+
:categories => @categories,
|
22
41
|
:title => @title
|
23
42
|
}
|
24
43
|
end
|
25
44
|
|
26
45
|
def is_valid?
|
27
|
-
|
46
|
+
errors << 'Missing required category and/or parent config value' if !categories and !parent
|
47
|
+
errors << 'Missing name or title config value' if !name and !title
|
48
|
+
errors << 'Missing required markdown' if !markdown
|
49
|
+
errors.empty?
|
28
50
|
end
|
29
51
|
|
30
52
|
# sets the header tag based on how deep your nesting is
|
31
53
|
def markdown_with_heading(heading = 1)
|
32
|
-
|
54
|
+
"\n\n<h#{heading.to_s} id=\"#{@name}\">#{@title}</h#{heading.to_s}>" + @markdown
|
33
55
|
end
|
34
56
|
end
|
35
57
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Hologram
|
2
|
+
class CommentLoadError < StandardError
|
3
|
+
end
|
4
|
+
end
|
5
|
+
|
6
|
+
module Hologram
|
7
|
+
class NoCategoryError < StandardError
|
8
|
+
def message
|
9
|
+
"Hologram comments found with no defined category. Are there other warnings/errors that need to be resolved?"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Hologram
|
2
|
+
class MarkdownRenderer < Redcarpet::Render::HTML
|
3
|
+
def block_code(code, language)
|
4
|
+
if language and language.include?('example')
|
5
|
+
if language.include?('js')
|
6
|
+
# first actually insert the code in the docs so that it will run and make our example work.
|
7
|
+
'<script>' + code + '</script>
|
8
|
+
<div class="codeBlock jsExample">' + Pygments.highlight(code) + '</div>'
|
9
|
+
else
|
10
|
+
'<div class="codeExample">' + '<div class="exampleOutput">' + render_html(code, language) + '</div>' + '<div class="codeBlock">' + Pygments.highlight(code, :lexer => get_lexer(language)) + '</div>' + '</div>'
|
11
|
+
end
|
12
|
+
else
|
13
|
+
'<div class="codeBlock">' + Pygments.highlight(code) + '</div>'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def render_html(code, language)
|
19
|
+
case language
|
20
|
+
when 'haml_example'
|
21
|
+
safe_require('haml', language)
|
22
|
+
return Haml::Engine.new(code.strip).render(template_rendering_scope, {})
|
23
|
+
else
|
24
|
+
code
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def template_rendering_scope
|
29
|
+
Object.new
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_lexer(language)
|
33
|
+
case language
|
34
|
+
when 'haml_example'
|
35
|
+
'haml'
|
36
|
+
else
|
37
|
+
'html'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def safe_require(templating_library, language)
|
42
|
+
begin
|
43
|
+
require templating_library
|
44
|
+
rescue LoadError
|
45
|
+
raise "#{templating_library} must be present for you to use #{language}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -2,7 +2,7 @@ module Hologram
|
|
2
2
|
|
3
3
|
#Helper class for binding things for ERB
|
4
4
|
class TemplateVariables
|
5
|
-
attr_accessor :title, :file_name, :blocks, :
|
5
|
+
attr_accessor :title, :file_name, :blocks, :output_files_by_category, :config, :pages
|
6
6
|
|
7
7
|
def initialize(args)
|
8
8
|
set_args(args)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Hologram
|
2
|
+
module Utils
|
3
|
+
def self.get_markdown_renderer(custom_markdown = nil)
|
4
|
+
return MarkdownRenderer if custom_markdown.nil?
|
5
|
+
|
6
|
+
load custom_markdown
|
7
|
+
renderer_class = File.basename(custom_markdown, '.rb').split(/_/).map(&:capitalize).join
|
8
|
+
DisplayMessage.info("Custom markdown renderer #{renderer_class} loaded.")
|
9
|
+
Module.const_get(renderer_class)
|
10
|
+
rescue LoadError => e
|
11
|
+
DisplayMessage.error("Could not load #{custom_markdown}.")
|
12
|
+
rescue NameError => e
|
13
|
+
DisplayMessage.error("Class #{renderer_class} not found in #{custom_markdown}.")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/hologram/version.rb
CHANGED
@@ -19,6 +19,6 @@ dependencies:
|
|
19
19
|
- ./build
|
20
20
|
|
21
21
|
# Mark which category should be the index page
|
22
|
-
# Alternatively, you may have an index.md in the
|
23
|
-
# folder instead of specifying this
|
22
|
+
# Alternatively, you may have an index.md in the documentation assets
|
23
|
+
# folder instead of specifying this config.
|
24
24
|
index: basics
|
data/spec/cli_spec.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'hologram/cli'
|
3
|
+
|
4
|
+
describe Hologram::CLI do
|
5
|
+
let(:argv) { [] }
|
6
|
+
subject(:cli) { Hologram::CLI.new(argv) }
|
7
|
+
|
8
|
+
context '#run' do
|
9
|
+
context 'when arg is "init"' do
|
10
|
+
let(:argv) { ['init'] }
|
11
|
+
|
12
|
+
it 'setups the dir' do
|
13
|
+
expect(Hologram::DocBuilder).to receive(:setup_dir)
|
14
|
+
cli.run
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when arg is empty' do
|
19
|
+
subject(:builder) { double(Hologram::DocBuilder, is_valid?: true, build: true) }
|
20
|
+
|
21
|
+
it 'builds the documentation' do
|
22
|
+
expect(Hologram::DocBuilder).to receive(:from_yaml).and_return(builder)
|
23
|
+
cli.run
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when a config file is passed' do
|
28
|
+
let(:argv) { ['test.yml'] }
|
29
|
+
subject(:builder) { double(Hologram::DocBuilder, is_valid?: true, build: true) }
|
30
|
+
|
31
|
+
it 'builds the documentation' do
|
32
|
+
expect(Hologram::DocBuilder).to receive(:from_yaml).with('test.yml').and_return(builder)
|
33
|
+
cli.run
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -4,23 +4,6 @@ require 'tempfile'
|
|
4
4
|
describe Hologram::DisplayMessage do
|
5
5
|
subject(:display) { Hologram::DisplayMessage }
|
6
6
|
|
7
|
-
#Rails kernerl helper
|
8
|
-
def capture(stream)
|
9
|
-
stream = stream.to_s
|
10
|
-
captured_stream = Tempfile.new(stream)
|
11
|
-
stream_io = eval("$#{stream}")
|
12
|
-
origin_stream = stream_io.dup
|
13
|
-
stream_io.reopen(captured_stream)
|
14
|
-
|
15
|
-
yield
|
16
|
-
|
17
|
-
stream_io.rewind
|
18
|
-
return captured_stream.read
|
19
|
-
ensure
|
20
|
-
captured_stream.unlink
|
21
|
-
stream_io.reopen(origin_stream)
|
22
|
-
end
|
23
|
-
|
24
7
|
context '.quiet!' do
|
25
8
|
around do |example|
|
26
9
|
display.quiet!
|
@@ -32,7 +32,7 @@ comment
|
|
32
32
|
context '#add_doc_block' do
|
33
33
|
context 'when the comment is valid' do
|
34
34
|
before do
|
35
|
-
collection.add_doc_block(comment)
|
35
|
+
collection.add_doc_block(comment, 'fake_file.sass')
|
36
36
|
end
|
37
37
|
|
38
38
|
it 'adds a doc block to the collection' do
|
@@ -42,7 +42,7 @@ comment
|
|
42
42
|
|
43
43
|
context 'when no yaml is provided' do
|
44
44
|
before do
|
45
|
-
collection.add_doc_block('')
|
45
|
+
collection.add_doc_block('', 'fake_file.sass')
|
46
46
|
end
|
47
47
|
|
48
48
|
it 'does not add a new block' do
|
@@ -54,7 +54,7 @@ comment
|
|
54
54
|
context '#create_nested_structure' do
|
55
55
|
context 'when the collection has blocks with parents' do
|
56
56
|
before do
|
57
|
-
collection.add_doc_block(comment)
|
57
|
+
collection.add_doc_block(comment, 'fake_file.sass')
|
58
58
|
collection.add_doc_block(%q{
|
59
59
|
/*doc
|
60
60
|
---
|
@@ -63,7 +63,7 @@ comment
|
|
63
63
|
parent: button
|
64
64
|
---
|
65
65
|
some other button style
|
66
|
-
*/})
|
66
|
+
*/}, 'fake_file.sass')
|
67
67
|
|
68
68
|
collection.create_nested_structure
|
69
69
|
end
|
data/spec/doc_builder_spec.rb
CHANGED
@@ -1,31 +1,18 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Hologram::DocBuilder do
|
4
|
-
subject(:builder) { Hologram::DocBuilder
|
4
|
+
subject(:builder) { Hologram::DocBuilder }
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
context 'when passed an invalid config' do
|
14
|
-
before do
|
15
|
-
File.open('bad_config.yml', 'w'){ |io| io << '%' }
|
16
|
-
end
|
17
|
-
|
18
|
-
after do
|
19
|
-
FileUtils.rm('bad_config.yml')
|
20
|
-
end
|
6
|
+
around do |example|
|
7
|
+
Hologram::DisplayMessage.quiet!
|
8
|
+
example.run
|
9
|
+
Hologram::DisplayMessage.show!
|
10
|
+
end
|
21
11
|
|
22
|
-
|
23
|
-
|
24
|
-
end
|
25
|
-
end
|
12
|
+
context '.from_yaml' do
|
13
|
+
subject(:builder) { Hologram::DocBuilder }
|
26
14
|
|
27
|
-
context 'when passed a config file' do
|
28
|
-
let(:style_files) { Dir[File.expand_path('../fixtures/styleguide/**/*.*', __FILE__)] }
|
15
|
+
context 'when passed a valid config file' do
|
29
16
|
let(:config_path) { File.join(Dir.pwd, 'spec/fixtures/source/config.yml') }
|
30
17
|
let(:config_copy_path) { File.join(Dir.pwd, 'spec/fixtures/source/config.yml.copy') }
|
31
18
|
|
@@ -43,50 +30,146 @@ describe Hologram::DocBuilder do
|
|
43
30
|
end
|
44
31
|
end
|
45
32
|
|
33
|
+
it 'returns a DocBuilder instance' do
|
34
|
+
expect(subject.from_yaml(config_copy_path)).to be_a Hologram::DocBuilder
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when passed an invalid config' do
|
46
39
|
before do
|
47
|
-
|
40
|
+
File.open('bad_config.yml', 'w'){ |io| io << '%' }
|
41
|
+
end
|
42
|
+
|
43
|
+
after do
|
44
|
+
FileUtils.rm('bad_config.yml')
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'exits the process' do
|
48
|
+
expect { subject.from_yaml('bad_config.yml') }.to raise_error SyntaxError
|
48
49
|
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context '.setup_dir' do
|
54
|
+
subject(:builder) { Hologram::DocBuilder }
|
49
55
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
56
|
+
around do |example|
|
57
|
+
Dir.mktmpdir do |dir|
|
58
|
+
Dir.chdir(dir) do
|
59
|
+
example.run
|
54
60
|
end
|
55
61
|
end
|
56
62
|
end
|
57
63
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
+
before do
|
65
|
+
builder.setup_dir
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'creates a config file' do
|
69
|
+
expect(File.exists?('hologram_config.yml')).to be_true
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'creates default assets' do
|
73
|
+
Dir.chdir('doc_assets') do
|
74
|
+
['_header.html', '_footer.html'].each do |asset|
|
75
|
+
expect(File.exists?(asset)).to be_true
|
64
76
|
end
|
65
77
|
end
|
78
|
+
end
|
66
79
|
|
67
|
-
|
68
|
-
|
80
|
+
context 'when a hologram_config.yml already exists' do
|
81
|
+
it 'does nothing' do
|
82
|
+
open('hologram_config.yml', 'w') {|io|io << 'foo'}
|
83
|
+
builder.setup_dir
|
84
|
+
expect(IO.read('hologram_config.yml')).to eql('foo')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context '#is_valid?' do
|
90
|
+
|
91
|
+
let(:config) do
|
92
|
+
{
|
93
|
+
'source' => 'spec/fixtures/source/components',
|
94
|
+
'documentation_assets' => 'spec/fixtures/source/templates',
|
95
|
+
'base_path' => 'spec/fixtures/source/'
|
96
|
+
}
|
97
|
+
end
|
98
|
+
|
99
|
+
let(:builder) { Hologram::DocBuilder.new(config) }
|
100
|
+
|
101
|
+
around do |example|
|
102
|
+
Dir.mktmpdir do |tmpdir|
|
103
|
+
config['destination'] = tmpdir
|
104
|
+
example.run
|
69
105
|
end
|
106
|
+
end
|
70
107
|
|
71
|
-
|
72
|
-
|
108
|
+
context 'when config vars are present and directories exists' do
|
109
|
+
it 'returns true' do
|
110
|
+
expect(builder.is_valid?).to be_true
|
73
111
|
end
|
112
|
+
end
|
74
113
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
114
|
+
['source', 'destination', 'documentation_assets'].each do |config_var|
|
115
|
+
context "when the required #{config_var} parameter is missing" do
|
116
|
+
before do
|
117
|
+
config.delete(config_var)
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'returns false' do
|
121
|
+
expect(builder.is_valid?).to be_false
|
80
122
|
end
|
81
|
-
end
|
82
123
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
builder.init(['init'])
|
87
|
-
expect(IO.read('hologram_config.yml')).to eql('foo')
|
124
|
+
it 'populates errors' do
|
125
|
+
builder.is_valid?
|
126
|
+
expect(builder.errors.size).to eql 1
|
88
127
|
end
|
89
128
|
end
|
90
129
|
end
|
130
|
+
|
131
|
+
context "when the source directory does not exist" do
|
132
|
+
before do
|
133
|
+
config['source'] = './foo'
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'returns false' do
|
137
|
+
expect(builder.is_valid?).to be_false
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'populates errors' do
|
141
|
+
builder.is_valid?
|
142
|
+
expect(builder.errors.size).to eql 1
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context '#build' do
|
148
|
+
let(:style_files) { Dir[File.expand_path('../fixtures/styleguide/**/*.*', __FILE__)] }
|
149
|
+
let(:processed_files) { Dir[File.join(builder.destination, '.', '**/*.*')] }
|
150
|
+
let(:config_path) { File.join(Dir.pwd, 'spec/fixtures/source/config.yml') }
|
151
|
+
let(:config_copy_path) { File.join(Dir.pwd, 'spec/fixtures/source/config.yml.copy') }
|
152
|
+
let(:builder) { Hologram::DocBuilder.from_yaml(config_copy_path) }
|
153
|
+
|
154
|
+
around do |example|
|
155
|
+
Dir.mktmpdir do |tmpdir|
|
156
|
+
FileUtils.cp(config_path, config_copy_path)
|
157
|
+
File.open(config_copy_path, 'a'){ |io| io << "destination: #{tmpdir}" }
|
158
|
+
current_dir = Dir.pwd
|
159
|
+
Dir.chdir('spec/fixtures/source')
|
160
|
+
|
161
|
+
example.run
|
162
|
+
|
163
|
+
Dir.chdir(current_dir)
|
164
|
+
FileUtils.rm(config_copy_path)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'builds a styleguide' do
|
169
|
+
builder.build
|
170
|
+
style_files.each_with_index do |file, index|
|
171
|
+
expect(FileUtils.cmp(file, processed_files[index])).to be_true
|
172
|
+
end
|
173
|
+
end
|
91
174
|
end
|
92
175
|
end
|