proscribe 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY.md ADDED
@@ -0,0 +1,4 @@
1
+ v0.0.1 - Jul 24, 2011
2
+ ---------------------
3
+
4
+ Initial.
data/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # ProScribe
2
+ #### Source documentation tool
3
+
4
+ $ gem install proscribe
5
+ $ proscribe
6
+
7
+ See the source for info on how to use it.
data/bin/proscribe ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path('../../lib/proscribe', __FILE__)
4
+
5
+ ProScribe::CLI.run!
@@ -0,0 +1,5 @@
1
+ source :rubygems
2
+
3
+ gem "proton", "~> 0.3.2"
4
+ gem "rack-cache", "~> 1.0.0"
5
+ gem "rdiscount"
@@ -0,0 +1,43 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ RedCloth (4.2.7)
5
+ chunky_png (1.2.0)
6
+ compass (0.11.5)
7
+ chunky_png (~> 1.2)
8
+ fssm (>= 0.2.7)
9
+ sass (~> 3.1)
10
+ cuba (2.0.0)
11
+ rack (~> 1.2)
12
+ tilt (~> 1.2)
13
+ fssm (0.2.7)
14
+ haml (3.1.2)
15
+ hashie (1.0.0)
16
+ maruku (0.6.0)
17
+ syntax (>= 1.0.0)
18
+ proton (0.3.3)
19
+ RedCloth (~> 4.2.3)
20
+ compass (~> 0.11.1)
21
+ cuba (~> 2.0.0)
22
+ haml (~> 3.1.1)
23
+ hashie (~> 1.0.0)
24
+ maruku (~> 0.6.0)
25
+ sass (~> 3.1.1)
26
+ shake (~> 0.1)
27
+ tilt (~> 1.2.2)
28
+ rack (1.3.0)
29
+ rack-cache (1.0.2)
30
+ rack (>= 0.4)
31
+ rdiscount (1.6.8)
32
+ sass (3.1.4)
33
+ shake (0.1.2)
34
+ syntax (1.0.0)
35
+ tilt (1.2.2)
36
+
37
+ PLATFORMS
38
+ ruby
39
+
40
+ DEPENDENCIES
41
+ proton (~> 0.3.2)
42
+ rack-cache (~> 1.0.0)
43
+ rdiscount
@@ -0,0 +1,14 @@
1
+ hyde_requirement: 0.2
2
+ extensions_path: _extensions
3
+ layouts_path: _layouts
4
+ partials_path: _layouts
5
+
6
+ tilt_options:
7
+ haml:
8
+ ugly: true
9
+
10
+ # Custom stuff
11
+ extractor:
12
+ files:
13
+ - source: ../lib/**/*.rb
14
+ target: api/
@@ -0,0 +1,18 @@
1
+ class Proton::CLI
2
+ task :update do
3
+ require File.expand_path('../extractor', __FILE__)
4
+
5
+ Dir.chdir(Hyde.project.root) {
6
+ Proton.project.config.extractor.files.each { |group|
7
+ FileUtils.rm_rf group.target
8
+
9
+ ex = Extractor.new Dir[group.source]
10
+
11
+ ex.write!(group.target) { |b| puts " update * #{File.join(group.target, b.file)}" }
12
+ }
13
+ }
14
+ end
15
+
16
+ task.description = "Extracts inline documentation."
17
+ end
18
+
@@ -0,0 +1,206 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ostruct'
4
+ require 'fileutils'
5
+
6
+ # Extracts comments from list of files.
7
+ # Gets the ones with comment blocks starting with `[...]`
8
+ #
9
+ # == Common usage
10
+ #
11
+ # ex = Extractor.new('.')
12
+ # ex.blocks
13
+ #
14
+ # ex.blocks.map! { |b| b.file = "file: #{b.file}" }
15
+ #
16
+ # ex.write!('manual/') # Writes to manual/
17
+ #
18
+ class Extractor
19
+ def initialize(files, options={})
20
+ @files = files
21
+ end
22
+
23
+ def write!(output_path = '.', &blk)
24
+ blocks.each { |block|
25
+ path = File.join(output_path, block.file)
26
+ FileUtils.mkdir_p File.dirname(path)
27
+ File.open(path, 'w') { |f| f.write block.body }
28
+ yield block if block_given?
29
+ }
30
+ end
31
+
32
+ # Returns an array of Extractor::Blocks.
33
+ def blocks
34
+ @blocks ||= begin
35
+ @files.map { |file|
36
+ if File.file?(file)
37
+ input = File.read(file)
38
+ get_blocks input
39
+ end
40
+ }.compact.flatten
41
+ end
42
+ end
43
+
44
+ private
45
+ # Returns blocks that match a blah.
46
+ def get_blocks(str)
47
+ arr = get_comment_blocks(str)
48
+ arr.map { |block|
49
+ re = /^([A-Za-z ]*?): (.*?)(?: \((.*?)\))?$/
50
+
51
+ if block.last =~ re
52
+ Extractor::Block.new \
53
+ :type => $1,
54
+ :title => $2,
55
+ :parent => $3,
56
+ :body => (block[0..-2].join("\n") + "\n")
57
+ elsif block.first =~ re
58
+ Extractor::Block.new \
59
+ :type => $1,
60
+ :title => $2,
61
+ :parent => $3,
62
+ :body => (block[1..-1].join("\n") + "\n")
63
+ end
64
+ }.compact
65
+ end
66
+
67
+ # Returns contiguous comment blocks.
68
+ def get_comment_blocks(str)
69
+ chunks = Array.new
70
+ i = 0
71
+
72
+ str.split("\n").each { |s|
73
+ if s =~ /^\s*(?:\/\/\/?|##?) ?(.*)$/
74
+ chunks[i] ||= Array.new
75
+ chunks[i] << $1
76
+ else
77
+ i += 1 if chunks[i]
78
+ end
79
+ }
80
+
81
+ chunks
82
+ end
83
+ end
84
+
85
+ class Extractor::Block
86
+ attr_accessor :body
87
+ attr_accessor :file
88
+
89
+ def initialize(options)
90
+ title = options[:title]
91
+ parent = options[:parent]
92
+ body = options[:body]
93
+ type = options[:type].downcase
94
+
95
+ file = to_filename(title, parent)
96
+ brief, *body = body.split("\n\n")
97
+ body = "#{body.join("\n\n")}"
98
+
99
+ heading = "title: #{title}\npage_type: #{type}\nbrief: #{brief}\n"
100
+ heading += "--\n"
101
+
102
+ @file = file
103
+ body = Tilt.new(".md") { body }.render
104
+ body = fix_links(body, from: file)
105
+ @body = heading + body
106
+ end
107
+
108
+ private
109
+ def fix_links(str, options={})
110
+ from = ("/" + options[:from].to_s).squeeze('/')
111
+ depth = from.to_s.count('/')
112
+ indent = (depth > 1 ? '../'*(depth-1) : './')[0..-2]
113
+
114
+ # First pass: {Helpers::content_for} to become links
115
+ str = str.gsub(/{([^}]*?)}/) { |s|
116
+ s = s.gsub(/{|}/, '')
117
+
118
+ m = s.match(/^(.*?)[:\.]+([A-Za-z_\(\)\!\?]+)$/)
119
+ if m
120
+ name, context = $2, $1
121
+ else
122
+ name, context = s, nil
123
+ end
124
+
125
+ s = "<a href='/#{to_filename(s, '', :ext => '.html')}'>#{name}</a>"
126
+ s += " <span class='context'>(#{context})</span>" if context
127
+ s
128
+ }
129
+
130
+ # Second pass: relativize
131
+ re = /href=['"](\/(?:.*?))['"]/
132
+ str.gsub(re) { |s|
133
+ url = s.match(re) && $1
134
+ url = "#{indent}/#{url}".squeeze('/')
135
+ "href=#{url.inspect}"
136
+ }
137
+ end
138
+
139
+ def to_filename(title, parent='', options={})
140
+ extension = options[:ext] || '.erb'
141
+ pathify = lambda { |s|
142
+ s.to_s.scan(/[A-Za-z0-9_\!\?]+/).map { |chunk|
143
+ chunk = chunk.gsub('?', '_question')
144
+ chunk = chunk.gsub('!', '_bang')
145
+
146
+ if chunk[0].upcase == chunk[0]
147
+ chunk
148
+ else
149
+ "#{chunk}_"
150
+ end
151
+ }.join("/")
152
+ }
153
+
154
+ pathify["#{parent}/#{title}"] + extension
155
+ end
156
+ end
157
+
158
+ module Extractor::Command
159
+ module Params
160
+ def extract(what)
161
+ i = index(what) and slice!(i, 2)[1]
162
+ end
163
+
164
+ def extract_all(what)
165
+ re = Array.new
166
+ while true
167
+ x = extract(what) or return re
168
+ re << x
169
+ end
170
+ end
171
+ end
172
+
173
+ def self.show_usage
174
+ puts "Usage: #{$0} <path> [-o <output_path>] [-i <ignore_spec>]"
175
+ puts " Extracts documentation comments from files in <path>, and places"
176
+ puts " them in <output_path>."
177
+ puts ""
178
+ puts "Example:"
179
+ puts " #{$0} **/*.rb -o manual/"
180
+ puts ""
181
+ end
182
+
183
+ def self.run!
184
+ return show_usage if ARGV.empty?
185
+
186
+ ARGV.extend Params
187
+
188
+ glob = lambda { |s| Dir["#{s}/**/*"] + Dir[s] }
189
+
190
+ output = ARGV.extract('--output') || ARGV.extract('-o') || '.'
191
+ ignore = ARGV.extract_all('-i') + ARGV.extract_all('--ignore')
192
+
193
+ files = ARGV.map { |s| glob[s] }.flatten
194
+ files = Dir["**/*"] if ARGV.empty?
195
+
196
+ files -= ignore.map { |s| glob[s] }.flatten
197
+
198
+ ex = Extractor.new(files)
199
+ ex.blocks.map { |b| b.file }
200
+ ex.write!(output) { |blk|
201
+ puts "* #{blk.file}"
202
+ }
203
+ end
204
+ end
205
+
206
+ Extractor::Command.run! if $0 == __FILE__
@@ -0,0 +1,32 @@
1
+ module Hyde::Helpers
2
+ def page_children(page)
3
+ children = page.children
4
+ of_type = lambda { |str| children.select { |p| p.html? && p.meta.page_type == str } }
5
+
6
+ children.
7
+ select { |p| p.html? }.
8
+ group_by { |p|
9
+ type = p.meta.page_type
10
+ type.nil? ? nil : Inflector[type].pluralize.to_sym
11
+ }
12
+ end
13
+ end
14
+
15
+ # Inflector['hello'].pluralize
16
+ class Inflector < String
17
+ def self.[](str)
18
+ new str.to_s
19
+ end
20
+
21
+ def pluralize
22
+ if self[-1] == 's'
23
+ "#{self}es"
24
+ else
25
+ "#{self}s"
26
+ end
27
+ end
28
+
29
+ def sentencize
30
+ self.gsub('_', ' ').capitalize
31
+ end
32
+ end
@@ -0,0 +1,2 @@
1
+ require File.expand_path('../lib/cli', __FILE__)
2
+ require File.expand_path('../lib/helpers', __FILE__)
@@ -0,0 +1,8 @@
1
+ - is_active = from == p
2
+ - if p.html?
3
+ %li
4
+ %a{href: rel(p.path), class: ("active" if is_active)}= p
5
+ %ul{class: "level-#{level}"}
6
+ - p.children.each do |pp|
7
+ != partial :'_nav', from: from, p: pp, level: level+1
8
+
@@ -0,0 +1,123 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %title= page.title
5
+
6
+ %meta(charset='UTF-8')
7
+ -# Use the latest IE engine, or Chrome frame.
8
+ %meta(http-equiv='X-UA-Compatible' content='IE=edge,chrome=1')
9
+
10
+ -# Mobile viewport optimization. j.mp/bplateviewport
11
+ %meta(name='viewport' content='width=device-width, initial-scale=1.0')
12
+
13
+ -# Standard SEO meta
14
+ - if page.meta.keywords
15
+ %meta{:name => 'keywords', :content => page.meta.keywords}
16
+ - if page.meta.description
17
+ %meta{:name => 'description', :content => page.meta.description}
18
+
19
+ %link{:rel => 'stylesheet', :href => rel('/style.css')+"?#{File.mtime(Proton::Page['/style.css'].file).to_i}"}
20
+
21
+ %body
22
+ #top
23
+ %a#logo{href: rel('/')}
24
+ = Hyde::Page['/'].title
25
+
26
+ #area
27
+ #content
28
+ %div.c
29
+ #crumbs
30
+ - page.breadcrumbs[0..-2].each do |p|
31
+ %a{href: rel(p.path)}= p
32
+ %span.gt!= "&rarr;"
33
+
34
+ %strong= page
35
+
36
+ %hgroup
37
+ - if page.meta.layout
38
+ %p.type= page.meta.layout.capitalize
39
+ %h1= page.title
40
+ - if page.meta.brief
41
+ %h5= page.meta.brief
42
+
43
+ .content
44
+ != yield
45
+
46
+ - groups = page_children(page)
47
+ - groups.each do |type, children|
48
+ %h3= type.to_s.gsub('_', ' ').capitalize
49
+
50
+ %ul.section
51
+ - children.each do |method|
52
+ %li
53
+ %a{href: rel(method.path)}= method.title
54
+ - unless method.meta.brief.to_s.empty?
55
+ %span.brief= method.meta.brief
56
+
57
+ %nav#nav
58
+ - parent = (page.children.any? ? page : (page.parent || page))
59
+ - children = parent.children.select { |p| p.html? }
60
+ - groups = children.group_by { |p| p.meta.page_type }
61
+
62
+ - if parent && !parent.root?
63
+ %nav.parents
64
+ %ul
65
+ - parent.breadcrumbs.each do |pp|
66
+ %li
67
+ %a{href: rel(pp.path), class: ('active' if pp.path == page.path)}
68
+ - unless pp.path == page.path
69
+ %span.back!= "&lsaquo;"
70
+
71
+ %em= pp.meta.page_type
72
+ = pp
73
+
74
+ - if groups.any?
75
+ - groups.each do |name, pages|
76
+ - name = name ? Inflector[name].pluralize.capitalize : parent.to_s
77
+ %nav
78
+ - if name
79
+ %h4= name
80
+ %ul
81
+ - pages.each do |pp|
82
+ %li
83
+ - classes = []
84
+ - classes << 'active' if pp.path == page.path
85
+ - classes << 'more' if pp.children.any?
86
+
87
+ %a{href: rel(pp.path), class: classes.join(' ')}
88
+ = pp
89
+
90
+
91
+ %script{src: 'http://cachedcommons.org/cache/prettify/1.0.0/javascripts/prettify-min.js', type: 'text/javascript'}
92
+ %script{src: 'http://cdnjs.cloudflare.com/ajax/libs/jquery/1.6.2/jquery.min.js', type: 'text/javascript'}
93
+ :javascript
94
+ $(function () {
95
+ $("pre").each(function() {
96
+ var r = /\[(.*?)\s*\((.*?)\)\]\n*/;
97
+ var m = $(this).text().match(r);
98
+
99
+ $(this).addClass('prettyprint');
100
+
101
+ if (m) {
102
+ var file = m[1];
103
+ var type = m[2];
104
+ $(this).addClass('lang-'+type);
105
+
106
+ if (file.length) {
107
+ $(this).addClass('has-caption');
108
+ $(this).prepend($("<h5 class='caption'>").text(file));
109
+ }
110
+
111
+ $(this).html($(this).html().replace(r, ''));
112
+ }
113
+
114
+ if ($(this).text().match(/^\s*([a-zA-Z_~\/]*)\$ /)) {
115
+ $(this).addClass('terminal');
116
+ $(this).removeClass('prettyprint');
117
+ $(this).html($(this).html().replace(/([a-zA-Z_~\/]*\$ )(.*?)[\r\n$]/g, "<strong><em>$1</em>$2</strong>\n"));
118
+ }
119
+ });
120
+
121
+ prettyPrint();
122
+ });
123
+