hologram 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,35 +1,57 @@
1
1
  module Hologram
2
2
  class DocumentBlock
3
- attr_accessor :name, :parent, :children, :title, :category, :markdown, :config, :heading
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
- @category = config['category']
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
- :category => @category,
40
+ :categories => @categories,
22
41
  :title => @title
23
42
  }
24
43
  end
25
44
 
26
45
  def is_valid?
27
- !!(@name && @markdown)
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
- @markdown = "\n\n<h#{heading.to_s} id=\"#{@name}\">#{@title}</h#{heading.to_s}>" + @markdown
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, :categories
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
@@ -25,5 +25,5 @@
25
25
  # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
26
 
27
27
  module Hologram
28
- VERSION = "1.0.1"
28
+ VERSION = "1.1.0"
29
29
  end
@@ -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 documenatation assets
23
- # folder instead of specifying this configu.
22
+ # Alternatively, you may have an index.md in the documentation assets
23
+ # folder instead of specifying this config.
24
24
  index: basics
@@ -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
@@ -1,31 +1,18 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Hologram::DocBuilder do
4
- subject(:builder) { Hologram::DocBuilder.new }
4
+ subject(:builder) { Hologram::DocBuilder }
5
5
 
6
- context '#init' do
7
- around do |example|
8
- Hologram::DisplayMessage.quiet!
9
- example.run
10
- Hologram::DisplayMessage.show!
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
- it 'exits the process' do
23
- expect { builder.init(['bad_config.yml']) }.to raise_error SystemExit
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
- builder.init([config_copy_path])
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
- it 'builds a styleguide' do
51
- processed_files = Dir[File.join('.', '**/*.*')]
52
- processed_files.each_with_index do |file, index|
53
- expect(FileUtils.cmp(file, style_files[index])).to be_true
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
- context 'when passed "init" as arg' do
59
- around do |example|
60
- Dir.mktmpdir do |dir|
61
- Dir.chdir(dir) do
62
- example.run
63
- end
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
- before do
68
- builder.init(['init'])
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
- it 'creates a config file' do
72
- expect(File.exists?('hologram_config.yml')).to be_true
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
- it 'creates default assets' do
76
- Dir.chdir('doc_assets') do
77
- ['_header.html', '_footer.html'].each do |asset|
78
- expect(File.exists?(asset)).to be_true
79
- end
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
- context 'when a hologram_config.yml already exists' do
84
- it 'does nothing' do
85
- open('hologram_config.yml', 'w') {|io|io << 'foo'}
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