murdoc 0.2.0 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6ef249b35659b0df1ac35d233d7ded20dc29e45b
4
- data.tar.gz: 7017b589cae5baf9511e1aaf065931b32d30c75a
3
+ metadata.gz: 7770978eea693f9a8464f272ed228ecd76fc3493
4
+ data.tar.gz: 9943ca4db60ec141f2088ff9d652f5f2949fbd7c
5
5
  SHA512:
6
- metadata.gz: 935006a39b60ce7b2d6c2d1c4eb8247758f36a60a2ce2174757d8befea55231dc5916b3c167d8d1c26b1f693c7eff06b0a7c7c4ca0f2713c12791b0b9d102e15
7
- data.tar.gz: 87622f60399b0217f751cfa09b60eeea353d2af8a00727293057feaffac8de8ebc25ad3a7a5f5a16c186e57466214cb725911abde2fddaf922697ae7682a6227
6
+ metadata.gz: 858b23df3485164ec8630973857152ee8f90901bf6a1ae903cbadb3ac64548e239fb3d8b8eb6d1a06891d14541a108829c09f853f04932c19178f7f069ee208e
7
+ data.tar.gz: cdc220eb7da112690d7605b0ee4c54db009112dfad38ebf4b7674b627128bd92222085a7121b7b846198eb70d378f5809536e5d665eae5140628f91fbff74aa9
data/README.md CHANGED
@@ -1,12 +1,29 @@
1
- Murdoc -- ruby documenter
1
+ Murdoc a ruby documenter
2
2
  ==============================
3
3
 
4
4
  Murdoc is a doccu-style annotated documentation generator.
5
5
 
6
- You may also want to see:
6
+ Rationale
7
+ ---------
8
+
9
+ Sometimes it makes sense to create a guide, a story told by code and comments side by side. Murdoc generates a pretty html for such a story.
10
+
11
+ Example
12
+ -------
13
+
14
+ Demo at [GH.pages](http://jsus.github.io/murdoc).
15
+
16
+ See also:
17
+
18
+ * [example](http://jsus.github.io/murdoc/docs) of integration with [jsus](http://github.com/jsus/jsus)
19
+
20
+ Usage
21
+ -----
22
+
23
+ * `gem install murdoc`
24
+ * `murdoc <input file> <output html file>` **or**
25
+ * `murdoc <input file 1> <input file 2> ... <output html file>`
7
26
 
8
- * [docco.coffee](http://jashkenas.github.com/docco/)
9
- * [Rocco](http://rtomayko.github.com/rocco/)
10
27
 
11
28
  Dependencies
12
29
  ------------
@@ -14,16 +31,14 @@ Dependencies
14
31
  * Haml
15
32
  * Either RDiscount (for MRI rubies) or Kramdown (for non-mri rubies)
16
33
 
17
- Example
18
- -------
19
-
20
- See example at [GH.pages](http://jsus.github.com/murdoc).
21
-
22
- See also:
23
- * [example](http://jsus.github.com/murdoc/docs) of integration with [jsus](http://github.com/jsus/jsus)
24
- * [LSD documentation guides](https://github.com/lovelyscalabledrawings/lsd-guides/tree/gh-pages/grid)
25
34
 
26
35
  License
27
36
  -------
28
37
 
29
38
  Public domain, see UNLICENSE file.
39
+
40
+ See also
41
+ --------
42
+
43
+ * [docco.coffee](http://jashkenas.github.io/docco/)
44
+ * [Rocco](http://rtomayko.github.io/rocco/)
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+ require "optparse"
3
+ $: << File.expand_path("../../lib/", __FILE__)
4
+ require "murdoc"
5
+
6
+ options = {}
7
+
8
+ option_parser = OptionParser.new do |opts|
9
+ opts.banner = "#{$0} <input dir> <output dir>"
10
+
11
+ opts.on('--index-template [FILENAME]', 'template to use for index files') do |template|
12
+ options[:index_template] = template
13
+ end
14
+
15
+ opts.on('--index-stylesheet [FILENAME]', 'stylesheet to use for index files') do |stylesheet|
16
+ options[:index_stylesheet] = stylesheet
17
+ end
18
+
19
+ opts.on('--template [FILENAME]', 'template to use for other files') do |template|
20
+ options[:template] = template
21
+ end
22
+
23
+ opts.on('--stylesheet [FILENAME]', 'stylesheet to use for other files') do |stylesheet|
24
+ options[:stylesheet] = stylesheet
25
+ end
26
+
27
+ opts.on("--[no-]syntax-highlight", "Highlight syntax using pygments") do |h|
28
+ options[:highlight] = h
29
+ end
30
+
31
+ opts.on("--do-not-count-comment-lines") do |dncl|
32
+ options[:do_not_count_comment_lines] = dncl
33
+ end
34
+
35
+ opts.on_tail("-h", "--help", "Show this message") do
36
+ puts opts
37
+ exit
38
+ end
39
+ end
40
+
41
+ option_parser.parse!
42
+
43
+ if ARGV.size != 2
44
+ puts option_parser
45
+ exit(1)
46
+ else
47
+ Murdoc.generate_tree(ARGV[0], ARGV[1], false, nil, options)
48
+ end
@@ -8,20 +8,29 @@
8
8
  # [ro]: "http://rtomayko.github.com/rocco"
9
9
  #
10
10
 
11
+ require 'fileutils'
12
+ require 'pathname'
11
13
 
12
14
  module Murdoc
13
- AnnotatedFile = Struct.new(:filename, :source, :source_type, :paragraphs, :formatted_paragraphs)
15
+ # `AnnotatedFile` is a struct we pass into our templates
16
+ AnnotatedFile = Struct.new(:filename, :metadata, :source, :source_type, :paragraphs, :formatted_paragraphs)
14
17
 
18
+ # `Murdoc.annotate` arguments are gathered from CLI utility
19
+ #
20
+ # `highlight` regulates syntax highlighting and `do_not_count_comment_lines` flag
21
+ # toggles counting comment lines towards line numbering in the output.
15
22
  def self.annotate(filename, highlight = true, do_not_count_comment_lines = false)
16
23
  filename = File.expand_path(filename)
17
24
  annotator = Annotator.from_file(filename, nil, do_not_count_comment_lines)
18
25
  AnnotatedFile.new(filename,
26
+ annotator.metadata,
19
27
  annotator.source,
20
28
  annotator.source_type,
21
29
  annotator.paragraphs,
22
30
  annotator.paragraphs.map {|p| FormattedParagraph.new(p, highlight) })
23
31
  end
24
32
 
33
+ # Generate a single file story
25
34
  def self.generate_from_file(input, output, options = {})
26
35
  options = default_options.merge(options)
27
36
  annotator = Annotator.from_file(input, nil)
@@ -32,6 +41,7 @@ module Murdoc
32
41
  end
33
42
  end
34
43
 
44
+ # ... or use multiple files
35
45
  def self.generate_from_multiple_files(input_files, output, options = {})
36
46
  options = default_options_for_multiple_files.merge(options)
37
47
  annotated_files = input_files.map {|fn| annotate(fn, options[:highlight], options[:do_not_count_comment_lines]) }
@@ -41,6 +51,69 @@ module Murdoc
41
51
  end
42
52
  end
43
53
 
54
+ # Generate a documentation tree
55
+ def self.generate_tree(input_dir, output_dir, has_parent = false, base_dir = nil, options = {})
56
+ options = default_options_for_tree.merge(options)
57
+ input_dir = Pathname(input_dir).expand_path
58
+ output_dir = Pathname(output_dir).expand_path
59
+ base_dir ||= input_dir
60
+
61
+ FileUtils.mkdir_p(output_dir)
62
+
63
+ entries = input_dir.children
64
+
65
+ directories = entries.select(&:directory?).
66
+ reject {|dir| dir == output_dir }.
67
+ reject {|dir| dir.basename.to_s.start_with?('.')}
68
+ directories.each do |dir|
69
+ next if dir == output_dir
70
+ generate_tree(dir,
71
+ output_dir + dir.relative_path_from(input_dir),
72
+ true,
73
+ base_dir,
74
+ options)
75
+ end
76
+
77
+ files = entries.select(&:file?).select {|file| Languages.detect(file.to_s) }
78
+ files.each do |file|
79
+ relpath = file.relative_path_from(input_dir)
80
+ generate_from_file(file, "#{output_dir}/#{relpath}.html", {
81
+ highlight: options[:highlight],
82
+ template: options[:template],
83
+ stylesheet: options[:stylesheet],
84
+ do_not_count_comment_lines: options[:do_not_count_comment_lines]
85
+ })
86
+ end
87
+
88
+ unless files.empty? && directories.empty?
89
+ generate_index("#{output_dir}/index.html", {
90
+ current_directory: input_dir,
91
+ files: files,
92
+ directories: directories,
93
+ has_parent: has_parent,
94
+ base_dir: base_dir,
95
+ template: options[:index_template],
96
+ stylesheet: options[:index_stylesheet]
97
+ })
98
+ end
99
+ end
100
+
101
+ def self.generate_index(fn, options)
102
+ options = default_options_for_index.merge(options)
103
+ File.open(fn, 'w+') do |f|
104
+ f.puts Renderer.new(options[:template]).render({
105
+ stylesheet: File.read(options[:stylesheet]),
106
+ base_dir: options[:base_dir],
107
+ has_parent: options[:has_parent],
108
+ current_directory: options[:current_directory],
109
+ files: options[:files],
110
+ directories: options[:directories]
111
+ })
112
+ end
113
+ end
114
+
115
+
116
+ # Rest is self-explanatory
44
117
  def self.default_options
45
118
  @options ||= {
46
119
  template: "#{markup_dir}/template.haml",
@@ -50,16 +123,41 @@ module Murdoc
50
123
  end
51
124
 
52
125
  def self.default_options_for_multiple_files
53
- @options ||= {
126
+ @options_multi ||= {
54
127
  template: "#{markup_dir}/template_multifile.haml",
55
128
  stylesheet: "#{markup_dir}/stylesheet.css",
56
129
  highlight: true
57
130
  }
58
131
  end
59
132
 
133
+ def self.default_options_for_tree
134
+ @options_tree ||= {
135
+ index_template: default_options_for_index[:template],
136
+ index_stylesheet: default_options_for_index[:stylesheet],
137
+ template: default_options[:template],
138
+ stylesheet: default_options[:stylesheet],
139
+ highlight: default_options[:highlight],
140
+ do_not_count_comment_lines: false
141
+ }
142
+ end
143
+
144
+ def self.default_options_for_index
145
+ @options_index ||= {
146
+ template: "#{markup_dir}/template_index.haml",
147
+ stylesheet: "#{markup_dir}/stylesheet.css"
148
+ }
149
+ end
150
+
60
151
  def self.markup_dir
61
152
  File.expand_path("../..", __FILE__)+ "/markup"
62
153
  end
154
+
155
+
156
+ def self.try_load_yaml(yaml)
157
+ YAML.load(yaml)
158
+ rescue => e
159
+ nil
160
+ end
63
161
  end
64
162
 
65
163
  require "murdoc/annotator"
@@ -1,7 +1,9 @@
1
+ require 'yaml'
2
+
1
3
  module Murdoc
2
4
  class Annotator
3
5
  attr_accessor :source
4
-
6
+ attr_accessor :metadata
5
7
  # Attribute accessor containing the resulting paragraphs
6
8
  attr_accessor :paragraphs
7
9
 
@@ -11,14 +13,15 @@ module Murdoc
11
13
  # `source` string contains annotated source code
12
14
  # `source_type` is one of supported source types (currently `[:ruby, :javascript]`)
13
15
  def initialize(source, source_type, do_not_count_comment_lines = false)
16
+ self.source = source
14
17
  self.source_type = source_type
15
- self.language = Languages.get(source_type)
16
- self.source = source
17
- self.paragraphs = if !language.annotation_only?
18
- Scanner.new(language).call(source, do_not_count_comment_lines)
18
+ self.language = Languages.get(source_type)
19
+ self.paragraphs = if !language.annotation_only?
20
+ Scanner.new(language).call(self.source, do_not_count_comment_lines)
19
21
  else
20
- [Paragraph.new('', source, 0, nil)]
22
+ [Paragraph.new('', self.source, 0, nil)]
21
23
  end
24
+ extract_metadata!
22
25
  end
23
26
 
24
27
 
@@ -30,6 +33,15 @@ module Murdoc
30
33
  do_not_count_comment_lines)
31
34
  end
32
35
 
36
+ def extract_metadata!
37
+ if paragraphs.count > 0 && paragraphs[0].annotation =~ /\A\s*---\n(.*?)\n\s*---\n?(.*)\z/m
38
+ paragraphs[0].annotation = $2
39
+ self.metadata = Murdoc.try_load_yaml($1)
40
+ else
41
+ self.metadata = {}
42
+ end
43
+ end
44
+
33
45
  def source_type
34
46
  @source_type
35
47
  end
@@ -3,7 +3,7 @@ module Murdoc
3
3
  class FormattedParagraph
4
4
  extend Forwardable
5
5
 
6
- def_delegators :paragraph, :annotation, :source, :starting_line, :source_type
6
+ def_delegators :paragraph, :annotation, :source, :starting_line, :source_type, :metadata
7
7
 
8
8
  attr_reader :paragraph
9
9
  attr_reader :highlight
@@ -6,6 +6,7 @@ rescue LoadError
6
6
  end
7
7
  require "cgi"
8
8
  require "tempfile"
9
+ require "yaml"
9
10
 
10
11
  module Murdoc
11
12
  class Paragraph
@@ -13,12 +14,23 @@ module Murdoc
13
14
  attr_accessor :annotation
14
15
  attr_accessor :source_type
15
16
  attr_accessor :starting_line
17
+ attr_accessor :metadata
16
18
 
17
19
  def initialize(source, annotation, starting_line = 0, source_type = nil)
18
20
  self.source = source
19
21
  self.annotation = annotation
20
22
  self.starting_line = starting_line
21
23
  self.source_type = source_type
24
+ extract_metadata!
25
+ end
26
+
27
+ def extract_metadata!
28
+ if annotation =~ /(.*\n)?^---!([^\n]*)\n?(.*)\z/m
29
+ self.metadata = Murdoc.try_load_yaml($2)
30
+ self.annotation = $1.to_s + $3.to_s
31
+ else
32
+ self.metadata = {}
33
+ end
22
34
  end
23
35
  end
24
36
  end
@@ -1,3 +1,3 @@
1
1
  module Murdoc
2
- VERSION = '0.2.0'
2
+ VERSION = '0.2.1'
3
3
  end
@@ -2,10 +2,16 @@
2
2
  %html
3
3
  %head
4
4
  %title Murdoc
5
- %style=stylesheet
5
+ %style= stylesheet
6
6
  %body
7
7
  .document{:class => annotated_file.source_type}
8
+ /
9
+ metadata:
10
+ = annotated_file.metadata.inspect
8
11
  - annotated_file.formatted_paragraphs.each do |p|
12
+ /
13
+ paragraph metadata:
14
+ = p.metadata.inspect
9
15
  - unless p.annotation.empty?
10
16
  %section= p.formatted_annotation
11
17
  - unless p.source.empty?
@@ -0,0 +1,20 @@
1
+ !!!
2
+ - reldir = current_directory == base_dir ? base_dir.basename : current_directory.relative_path_from(base_dir)
3
+ %html
4
+ %head
5
+ %title Murdoc / #{reldir}
6
+ %style= stylesheet
7
+ %body
8
+ .document.index
9
+ %section
10
+ %h2 #{reldir}
11
+ %ul
12
+ - if has_parent
13
+ %li
14
+ %a{:href => "../index.html"} ..
15
+ - directories.each do |dir|
16
+ %li
17
+ %a{:href => "#{dir.basename}/index.html"}= dir.basename
18
+ - files.each do |file|
19
+ %li
20
+ %a{:href => "#{file.basename}.html"}= file.basename
@@ -6,7 +6,13 @@
6
6
  %body
7
7
  - annotated_files.each do |af|
8
8
  .document{:class => af.source_type}
9
+ /
10
+ metadata:
11
+ = af.metadata.inspect
9
12
  - af.formatted_paragraphs.each_with_index do |p, j|
13
+ /
14
+ paragraph metadata:
15
+ = p.metadata.inspect
10
16
  %section
11
17
  - if j == 0
12
18
  %label= File.basename(af.filename)
@@ -124,4 +124,23 @@ describe Murdoc::Annotator do
124
124
  subject.paragraphs[0].source.should == "def hi\n\nend"
125
125
  end
126
126
  end
127
+
128
+ describe "metadata extraction" do
129
+ subject { described_class.new("# ---\n# foo: 'bar'\n# baz: 'foobar'\n# ---\nhello, world!", :ruby) }
130
+ it "extracts and parses yaml blocks in the beginning of the file" do
131
+ subject.metadata.should == {
132
+ 'foo' => 'bar',
133
+ 'baz' => 'foobar'
134
+ }
135
+ end
136
+
137
+ it "cuts metadata from the paragraph annotation text" do
138
+ subject.paragraphs[0].annotation.should_not include('---')
139
+ end
140
+
141
+ it "doesn't raise on invalid yaml" do
142
+ subject = described_class.new("---\n{\n---\nfoo bar", :ruby)
143
+ subject.metadata.should == {}
144
+ end
145
+ end
127
146
  end
@@ -17,5 +17,17 @@ describe Murdoc::Paragraph do
17
17
  it "should optionally set starting line" do
18
18
  described_class.new("", "", 666, :ruby).starting_line.should == 666
19
19
  end
20
+
21
+ it "extracts metadata" do
22
+ subject = described_class.new("", "---! {'foo': 'bar'}\nbaz")
23
+ subject.metadata.should == {'foo' => 'bar'}
24
+ subject.annotation.should == 'baz'
25
+ end
26
+
27
+ it "extracts metadata from the middle of annotation too" do
28
+ subject = described_class.new("", "foo\n---! {bar: 'baz'}\nfoo2")
29
+ subject.metadata.should == {'bar' => 'baz'}
30
+ subject.annotation.should == "foo\nfoo2"
31
+ end
20
32
  end
21
33
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: murdoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Abramov
@@ -99,6 +99,7 @@ email: markizko@gmail.com
99
99
  executables:
100
100
  - murdoc
101
101
  - murdoc-strip-comments
102
+ - murdoc-tree
102
103
  extensions: []
103
104
  extra_rdoc_files: []
104
105
  files:
@@ -112,6 +113,7 @@ files:
112
113
  - UNLICENSE
113
114
  - bin/murdoc
114
115
  - bin/murdoc-strip-comments
116
+ - bin/murdoc-tree
115
117
  - lib/murdoc.rb
116
118
  - lib/murdoc/annotator.rb
117
119
  - lib/murdoc/formatted_paragraph.rb
@@ -127,6 +129,7 @@ files:
127
129
  - lib/murdoc/version.rb
128
130
  - markup/stylesheet.css
129
131
  - markup/template.haml
132
+ - markup/template_index.haml
130
133
  - markup/template_multifile.haml
131
134
  - murdoc.gemspec
132
135
  - spec/murdoc/annotator_spec.rb