proscribe 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY.md CHANGED
@@ -1,3 +1,16 @@
1
+ v0.0.2 - Jul 29, 2011
2
+ ---------------------
3
+
4
+ ### Added:
5
+ * Github link
6
+ * Literate programming style stuff
7
+
8
+ ### Removed:
9
+ * ghpages support
10
+
11
+ ### Misc:
12
+ * Cleaned up extension
13
+
1
14
  v0.0.1 - Jul 24, 2011
2
15
  ---------------------
3
16
 
@@ -7,8 +7,3 @@ tilt_options:
7
7
  haml:
8
8
  ugly: true
9
9
 
10
- # Custom stuff
11
- extractor:
12
- files:
13
- - source: ../lib/**/*.rb
14
- target: api/
@@ -1,4 +1,4 @@
1
- module Hyde::Helpers
1
+ module Proton::Helpers
2
2
  def page_children(page)
3
3
  children = page.children
4
4
  of_type = lambda { |str| children.select { |p| p.html? && p.meta.page_type == str } }
@@ -10,6 +10,15 @@ module Hyde::Helpers
10
10
  type.nil? ? nil : Inflector[type].pluralize.to_sym
11
11
  }
12
12
  end
13
+
14
+ # A link to the source file on GitHub
15
+ def github_source_link
16
+ if project.config.github && project.config.git
17
+ if page.meta.source_file
18
+ "https://github.com/#{project.config.github}/blob/#{project.config.git[0..6]}/#{page.source_file}#L#{page.source_line}".squeeze('/')
19
+ end
20
+ end
21
+ end
13
22
  end
14
23
 
15
24
  # Inflector['hello'].pluralize
@@ -18,6 +18,9 @@
18
18
 
19
19
  %link{:rel => 'stylesheet', :href => rel('/style.css')+"?#{File.mtime(Proton::Page['/style.css'].file).to_i}"}
20
20
 
21
+ %script{src: 'http://cachedcommons.org/cache/prettify/1.0.0/javascripts/prettify-min.js', type: 'text/javascript'}
22
+ %script{src: 'http://cdnjs.cloudflare.com/ajax/libs/jquery/1.6.2/jquery.min.js', type: 'text/javascript'}
23
+ %script{src: rel('/proscribe.js')+"?#{File.mtime(Proton::Page['/proscribe.js'].file).to_i}", type: 'text/javascript'}
21
24
  %body
22
25
  #top
23
26
  %a#logo{href: rel('/')}
@@ -54,6 +57,28 @@
54
57
  - unless method.meta.brief.to_s.empty?
55
58
  %span.brief= method.meta.brief
56
59
 
60
+ -# Source
61
+ %section.footer
62
+ .left
63
+ %strong
64
+ - page.breadcrumbs[1...-1].each do |pp|
65
+ %a{href: rel(page.path)}
66
+ = pp.title
67
+ -# if pp.meta.page_type
68
+ -# %span.type= pp.meta.page_type
69
+
70
+ %span.arrow!= "›"
71
+
72
+ = page.title
73
+ - if page.meta.page_type
74
+ %span.type= page.meta.page_type
75
+
76
+ - if github_source_link
77
+ Defined in
78
+ %a{href: github_source_link}
79
+ %span= page.source_file
80
+ %span.view-source!= "View source ›"
81
+
57
82
  %nav#nav
58
83
  - parent = (page.children.any? ? page : (page.parent || page))
59
84
  - children = parent.children.select { |p| p.html? }
@@ -87,37 +112,3 @@
87
112
  %a{href: rel(pp.path), class: classes.join(' ')}
88
113
  = pp
89
114
 
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
-
@@ -0,0 +1,52 @@
1
+ $(function () {
2
+ $("h4").each(function() {
3
+ var $this = $(this);
4
+
5
+ // Find the next p
6
+ var $p = $this.find('+ p');
7
+ if (!$p.length) { $p = $this; }
8
+
9
+ var $pre = $p.find('+ pre');
10
+ if (!$pre.length) { return; }
11
+
12
+ // Build it
13
+ var $el = $("<section class='literate'>");
14
+ $this.before($el);
15
+
16
+ // Move them
17
+ $el.append($pre);
18
+ $el.append($this);
19
+ $el.append($p);
20
+ });
21
+
22
+ $("pre").each(function() {
23
+ var $this = $(this);
24
+ $this.addClass('prettyprint');
25
+
26
+ // Filename
27
+ var r = /\[(.*?)\s*\((.*?)\)\]\n*/;
28
+ var m = $this.text().match(r);
29
+ if (m) {
30
+ var file = m[1];
31
+ var type = m[2];
32
+ $this.addClass('lang-'+type);
33
+
34
+ if (file.length) {
35
+ $this.addClass('has-caption');
36
+ $this.prepend($("<h5 class='caption'>").text(file));
37
+ }
38
+
39
+ $this.html($this.html().replace(r, ''));
40
+ }
41
+
42
+ // Terminal
43
+ if ($this.text().match(/^\s*([a-zA-Z_~\/]*)\$ /)) {
44
+ $this.addClass('terminal');
45
+ $this.removeClass('prettyprint');
46
+ $this.html($this.html().replace(/([a-zA-Z_~\/]*\$ )(.*?)[\r\n$]/g, "<strong><em>$1</em>$2</strong>\n"));
47
+ }
48
+ });
49
+
50
+ prettyPrint();
51
+ });
52
+
@@ -135,6 +135,7 @@ $pad: 40px;
135
135
  #content {
136
136
  @include border-top-radius(2px);
137
137
  background: white; padding: 40px;
138
+ padding-bottom: 140px;
138
139
  position: relative;
139
140
  @include box-sizing(border-box);
140
141
  @include clearfix;
@@ -195,7 +196,7 @@ $pad: 40px;
195
196
  }
196
197
 
197
198
  >h2, >h3 {
198
- & + pre {
199
+ & + pre, & + section {
199
200
  margin-top: 20px !important; }
200
201
 
201
202
  font-size: 2em;
@@ -360,11 +361,14 @@ pre.has-caption {
360
361
  }
361
362
 
362
363
  pre.terminal {
363
- background: #222;
364
+ background: #444;
365
+ @include box-shadow(inset 2px 2px 8px rgba(black, 0.4));
364
366
 
365
- code {
367
+ &, code {
366
368
  color: #ccc;
367
- text-shadow: 1px 1px 0 rgba(black, 0.3);
369
+ text-shadow: 1px 1px 0 rgba(black, 0.3); }
370
+
371
+ code {
368
372
  background: transparent; }
369
373
 
370
374
  strong {
@@ -376,6 +380,118 @@ pre.terminal {
376
380
  color: #8d8 * 0.5; }
377
381
  }
378
382
 
383
+ section.literate {
384
+ overflow: hidden;
385
+ padding: 20px 40px;
386
+ border-top: solid 1px #eee;
387
+ margin: 0 -40px;
388
+
389
+ font-size: 9pt;
390
+
391
+ pre + * { margin-top: 0; }
392
+ &:first-of-type { margin-top: 30px; }
393
+ &:last-of-type { margin-bottom: 30px; }
394
+
395
+ p { width: 33%; }
396
+ pre { float: right; width: 60%; }
397
+ pre + pre { margin-top: 20px; }
398
+ pre::after { clear: both; }
399
+
400
+ h4 { font-size: 1.2em; margin-bottom: 10px; color: #456 * 1.7; }
401
+ h4 + * { margin-top: 0; }
402
+ }
403
+
404
+ a.github-link, a[title="Source code"] {
405
+ position: absolute;
406
+ top: 0;
407
+ right: 0;
408
+ display: block;
409
+ background: url('https://d3nwyuy0nl342s.cloudfront.net/img/e6bef7a091f5f3138b8cd40bc3e114258dd68ddf/687474703a2f2f73332e616d617a6f6e6177732e636f6d2f6769746875622f726962626f6e732f666f726b6d655f72696768745f7265645f6161303030302e706e67') top right no-repeat;
410
+ text-indent: -9999px;
411
+ width: 149px; height: 149px;
412
+ z-index: 1;
413
+ }
414
+
415
+ section.aux-links {
416
+ position: absolute;
417
+ top: 40px;
418
+ right: 0px;
419
+
420
+ ul, li { list-style-type: none; margin: 0; padding: 0; }
421
+
422
+ a {
423
+ overflow: hidden;
424
+ display: block;
425
+ margin-bottom: 5px;
426
+ font-size: 8pt;
427
+ padding: 5px 10px;
428
+ width: 140px;
429
+ font-size: 8pt;
430
+ line-height: 12pt;
431
+ font-weight: bold;
432
+ @include border-left-radius(4px);
433
+ background: #eee; }
434
+
435
+ a::after {
436
+ content: '›'; color: #aaa;
437
+ font-size: 1.5em;
438
+ float: right; }
439
+
440
+ a:hover {
441
+ background: #888; color: #ddd;
442
+ text-shadow: 1px 1px 0 rgba(black, 0.1); }
443
+ }
444
+
445
+ section.footer {
446
+ background: #eee;
447
+ border-top: solid 1px #eee;
448
+
449
+ position: absolute;
450
+ @include box-sizing(border-box);
451
+ bottom: 0; left: 0; width: 100%;
452
+ padding: 20px;
453
+
454
+
455
+ font-size: 0.75em;
456
+
457
+ color: #aaa;
458
+ text-shadow: 1px 1px 0 rgba(white, 0.5);
459
+
460
+ @include box-shadow(inset 0 3px 3px rgba(black, 0.1));
461
+ text-align: left;
462
+
463
+ a, strong {
464
+ color: #888; }
465
+
466
+ strong {
467
+ font-size: 1.1em; }
468
+
469
+ span.type {
470
+ opacity: 0.7;
471
+ font-weight: normal; font-size: 0.9em;
472
+ &::before { content: '['; }
473
+ &::after { content: ']'; } }
474
+
475
+ span.arrow {
476
+ margin: 0 5px; }
477
+
478
+ .left {
479
+ strong { display: block; } }
480
+
481
+ .line {
482
+ font-style: normal; }
483
+
484
+ .view-source {
485
+ background: rgba(black, 0.1);
486
+ color: #888;
487
+ padding: 1px 8px;
488
+ font-size: 0.9em;
489
+ font-weight: normal;
490
+ margin-left: 5px;
491
+ @include border-radius(7px);
492
+ font-style: normal; }
493
+ }
494
+
379
495
  //
380
496
  // Prettify
381
497
  //
data/data/rack/config.ru CHANGED
@@ -1,8 +1,4 @@
1
- begin
2
- require 'bundler'
3
- Bundler.setup
4
- rescue LoadError => e
5
- end
6
-
1
+ gem 'proscribe', '~> 0.0.1'
7
2
  require 'proscribe'
3
+
8
4
  run ProScribe.rack_app
data/lib/proscribe/cli.rb CHANGED
@@ -41,5 +41,17 @@ class ProScribe::CLI < Shake
41
41
  copy_files ProScribe.root('data/rack'), dir
42
42
  end
43
43
  task.description = "Makes a project Rack-compatible"
44
+
45
+ invalid do
46
+ if task(command)
47
+ usage = task(command).usage || command
48
+
49
+ err "Invalid usage."
50
+ err "Usage: #{executable} #{usage}"
51
+ err "See `#{executable} help` for more info."
52
+ else
53
+ err "Unknown command: #{command}"
54
+ err "See `#{executable} help` for a list of commands."
55
+ end
56
+ end
44
57
  end
45
-
@@ -17,8 +17,9 @@ module ProScribe
17
17
  # ex.write!('manual/') # Writes to manual/
18
18
  #
19
19
  class Extractor
20
- def initialize(files, options={})
20
+ def initialize(files, root, options={})
21
21
  @files = files
22
+ @root = File.realpath(root)
22
23
  end
23
24
 
24
25
  def write!(output_path = '.', &blk)
@@ -36,17 +37,22 @@ module ProScribe
36
37
  @files.map { |file|
37
38
  if File.file?(file)
38
39
  input = File.read(file)
39
- get_blocks input
40
+ get_blocks(input, unroot(file))
40
41
  end
41
42
  }.compact.flatten
42
43
  end
43
44
  end
44
45
 
45
46
  private
47
+ def unroot(fn)
48
+ (File.realpath(fn))[@root.size..-1]
49
+ end
50
+
46
51
  # Returns blocks that match a blah.
47
- def get_blocks(str)
52
+ def get_blocks(str, filename)
48
53
  arr = get_comment_blocks(str)
49
- arr.map { |block|
54
+ arr.map { |hash|
55
+ block = hash[:block]
50
56
  re = /^([A-Za-z ]*?): (.*?)(?: \((.*?)\))?$/
51
57
 
52
58
  if block.last =~ re
@@ -54,32 +60,37 @@ module ProScribe
54
60
  :type => $1,
55
61
  :title => $2,
56
62
  :parent => $3,
63
+ :line => hash[:line] + block.size + 1,
64
+ :source => filename,
57
65
  :body => (block[0..-2].join("\n") + "\n")
58
66
  elsif block.first =~ re
59
67
  Extractor::Block.new \
60
68
  :type => $1,
61
69
  :title => $2,
62
70
  :parent => $3,
71
+ :line => hash[:line] + block.size + 1,
72
+ :source => filename,
63
73
  :body => (block[1..-1].join("\n") + "\n")
64
74
  end
65
75
  }.compact
66
76
  end
67
77
 
68
78
  # Returns contiguous comment blocks.
79
+ # Returns an array of hashes (:block => [line1,line2...], :line => n)
69
80
  def get_comment_blocks(str)
70
81
  chunks = Array.new
71
82
  i = 0
72
83
 
73
- str.split("\n").each { |s|
84
+ str.split("\n").each_with_index { |s, line|
74
85
  if s =~ /^\s*(?:\/\/\/?|##?) ?(.*)$/
75
- chunks[i] ||= Array.new
76
- chunks[i] << $1
86
+ chunks[i] ||= { :block => Array.new, :line => line }
87
+ chunks[i][:block] << $1
77
88
  else
78
89
  i += 1 if chunks[i]
79
90
  end
80
91
  }
81
92
 
82
- chunks
93
+ chunks.compact
83
94
  end
84
95
  end
85
96
 
@@ -93,11 +104,14 @@ module ProScribe
93
104
  body = options[:body]
94
105
  type = options[:type].downcase
95
106
 
107
+ source = options[:source]
108
+ line = options[:line]
109
+
96
110
  file = to_filename(title, parent)
97
111
  brief, *body = body.split("\n\n")
98
112
  body = "#{body.join("\n\n")}"
99
113
 
100
- heading = "title: #{title}\npage_type: #{type}\nbrief: #{brief}\n"
114
+ heading = "title: #{title}\npage_type: #{type}\nsource_file: #{source}\nsource_line: #{line}\nbrief: #{brief}\n"
101
115
  heading += "--\n"
102
116
 
103
117
  @file = file
@@ -52,19 +52,39 @@ module ProScribe
52
52
  # Copy manual files over
53
53
  copy_files manual_path, dir, :except => ['Gemfile', 'Gemfile.lock', 'config.ru']
54
54
 
55
+ # Merge Scribefile into Protonfile
56
+ File.open(File.join(dir, 'Protonfile'), 'w') { |f| f.write protonfile }
57
+
55
58
  # Extract block comments
56
59
  config.files.each do |group|
57
- ex = ProScribe::Extractor.new Dir[root(group.source)]
58
- ex.write! File.join(dir, group.target)
60
+ ex = ProScribe::Extractor.new(Dir[root(group.source)], root)
61
+ ex.write! File.join(dir, group.prefix || '')
59
62
  end
60
63
  end
61
64
 
65
+ def protonfile
66
+ yaml = YAML::load_file(ProScribe.root('data/default/Protonfile'))
67
+
68
+ # Copy from Scribefile
69
+ c = config.to_hash.dup
70
+ c.delete 'manual'
71
+ c.delete 'output'
72
+ c.delete 'files'
73
+
74
+ # Add some things
75
+ c['git'] = `git rev-parse HEAD`.strip
76
+
77
+ yaml.merge! c
78
+
79
+ YAML::dump yaml
80
+ end
81
+
62
82
  # Attribute: dir (ProScribe::Project)
63
83
  # Returns the path to the temporary Proton project.
64
84
  #
65
85
  def dir(*a)
66
86
  @dir ||= begin
67
- dir = File.join(Dir.tmpdir, File.basename(root))
87
+ dir = File.join(Dir.tmpdir, File.basename(root) + Time.now.to_i.to_s)
68
88
  FileUtils.rm_rf dir
69
89
  FileUtils.mkdir_p dir
70
90
  dir
@@ -2,7 +2,7 @@ require 'proton'
2
2
  require 'proton/server'
3
3
  require 'shake'
4
4
 
5
- # Module: RackApp
5
+ # Module: RackApp (ProScribe)
6
6
  # Provides a Rack app.
7
7
  #
8
8
  # ## Usage
@@ -3,9 +3,9 @@ module ProScribe
3
3
  # Returns the version of the ProScribe gem.
4
4
  #
5
5
  # ## Example
6
- # ProScribe.version #=> "0.0.1"
6
+ # ProScribe.version #=> "0.0.2"
7
7
  #
8
8
  def self.version
9
- "0.0.1"
9
+ "0.0.3"
10
10
  end
11
11
  end
data/lib/proscribe.rb CHANGED
@@ -8,6 +8,17 @@ require 'tilt'
8
8
  # Module: ProScribe
9
9
  # The main module.
10
10
  #
11
+ # #### Hello s1
12
+ #
13
+ # yes s1
14
+ #
15
+ # foo s1
16
+ # foo s1
17
+ #
18
+ # #### Creating something
19
+ #
20
+ # s2
21
+ #
11
22
  module ProScribe
12
23
  def self.root(*a)
13
24
  File.join(File.expand_path('../../', __FILE__), *a)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: proscribe
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-07-24 00:00:00.000000000 +08:00
12
+ date: 2011-07-29 00:00:00.000000000 +08:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: hashie
17
- requirement: &2153048280 !ruby/object:Gem::Requirement
17
+ requirement: &2158295020 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ~>
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: 1.0.0
23
23
  type: :runtime
24
24
  prerelease: false
25
- version_requirements: *2153048280
25
+ version_requirements: *2158295020
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: shake
28
- requirement: &2153047780 !ruby/object:Gem::Requirement
28
+ requirement: &2158294520 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ~>
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: 0.1.2
34
34
  type: :runtime
35
35
  prerelease: false
36
- version_requirements: *2153047780
36
+ version_requirements: *2158294520
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: tilt
39
- requirement: &2153047320 !ruby/object:Gem::Requirement
39
+ requirement: &2158294060 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ~>
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: 1.2.2
45
45
  type: :runtime
46
46
  prerelease: false
47
- version_requirements: *2153047320
47
+ version_requirements: *2158294060
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: proton
50
- requirement: &2153046860 !ruby/object:Gem::Requirement
50
+ requirement: &2158293600 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ~>
@@ -55,10 +55,10 @@ dependencies:
55
55
  version: 0.3.3
56
56
  type: :runtime
57
57
  prerelease: false
58
- version_requirements: *2153046860
58
+ version_requirements: *2158293600
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: fssm
61
- requirement: &2153046400 !ruby/object:Gem::Requirement
61
+ requirement: &2158293140 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
64
  - - ~>
@@ -66,10 +66,10 @@ dependencies:
66
66
  version: 0.2.7
67
67
  type: :runtime
68
68
  prerelease: false
69
- version_requirements: *2153046400
69
+ version_requirements: *2158293140
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: rdiscount
72
- requirement: &2153045940 !ruby/object:Gem::Requirement
72
+ requirement: &2158292680 !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
75
  - - ~>
@@ -77,7 +77,7 @@ dependencies:
77
77
  version: 1.6.8
78
78
  type: :runtime
79
79
  prerelease: false
80
- version_requirements: *2153045940
80
+ version_requirements: *2158292680
81
81
  description: Build some documentation for your projects of any language.
82
82
  email:
83
83
  - rico@sinefunc.com
@@ -86,18 +86,15 @@ executables:
86
86
  extensions: []
87
87
  extra_rdoc_files: []
88
88
  files:
89
- - data/default/_extensions/manual/lib/cli.rb
90
- - data/default/_extensions/manual/lib/extractor.rb
91
- - data/default/_extensions/manual/lib/helpers.rb
92
- - data/default/_extensions/manual/manual.rb
89
+ - data/default/_extensions/helpers.rb
93
90
  - data/default/_layouts/_nav.haml
94
91
  - data/default/_layouts/default.haml
95
92
  - data/default/Gemfile
96
93
  - data/default/Gemfile.lock
94
+ - data/default/proscribe.js
97
95
  - data/default/Protonfile
98
96
  - data/default/style.scss
99
97
  - data/rack/config.ru
100
- - data/rack/Gemfile
101
98
  - bin/proscribe
102
99
  - lib/proscribe/cli.rb
103
100
  - lib/proscribe/extractor.rb
@@ -1,18 +0,0 @@
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
-
@@ -1,206 +0,0 @@
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__
@@ -1,2 +0,0 @@
1
- require File.expand_path('../lib/cli', __FILE__)
2
- require File.expand_path('../lib/helpers', __FILE__)
data/data/rack/Gemfile DELETED
@@ -1,2 +0,0 @@
1
- source :rubygems
2
- gem 'proscribe', '~> 0.0.1'