hayde 0.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.
- data/.document +12 -0
- data/.gitignore +24 -0
- data/Gemfile +15 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +65 -0
- data/VERSION +1 -0
- data/hayde.gemspec +66 -0
- data/lib/hayde/generator.rb +244 -0
- data/lib/hayde/helpers.rb +34 -0
- data/lib/hayde/indexer.rb +68 -0
- data/lib/hayde/levenshtein.rb +31 -0
- data/lib/hayde/textile_extensions.rb +41 -0
- data/lib/hayde.rb +33 -0
- data/test/helper.rb +10 -0
- data/test/test_hayde.rb +7 -0
- metadata +128 -0
data/.document
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# .document is used by rdoc and yard to know how to generate documentation
|
2
|
+
# for example, it can be used to control how rdoc gets built when you do `gem install foo`
|
3
|
+
|
4
|
+
README.rdoc
|
5
|
+
lib/**/*.rb
|
6
|
+
bin/*
|
7
|
+
|
8
|
+
# Files below the line with - are treated as 'extra files', and aren't parsed for ruby code
|
9
|
+
-
|
10
|
+
README.markdown
|
11
|
+
features/**/*.feature
|
12
|
+
LICENSE
|
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'http://gemcutter.org'
|
2
|
+
source 'http://gems.github.com'
|
3
|
+
|
4
|
+
group :runtime do
|
5
|
+
gem 'RedCloth', '>= 4.1.1'
|
6
|
+
gem 'actionpack', '= 3.0.0.beta3'
|
7
|
+
end
|
8
|
+
|
9
|
+
group :development do
|
10
|
+
gem 'rake'
|
11
|
+
gem 'jeweler'
|
12
|
+
gem 'shoulda'
|
13
|
+
gem 'yard'
|
14
|
+
gem 'rcov'
|
15
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Roman Zaharenkov
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= hayde
|
2
|
+
|
3
|
+
Hayde is a helper for generating guides articles from textile source. Extracted from railties-3.0.0.beta3 project.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2010 Roman Zaharenkov. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require "bundler"
|
4
|
+
Bundler.setup
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'jeweler'
|
8
|
+
Jeweler::Tasks.new do |gem|
|
9
|
+
gem.name = "hayde"
|
10
|
+
gem.summary = %Q{Textile guides generator like rails-guides.}
|
11
|
+
gem.description = %Q{Helper for generating guides articles from textile source. Extracted from railties-3.0.0.beta3 project. }
|
12
|
+
gem.email = "ZaharenkovRoman@gmail.com"
|
13
|
+
gem.homepage = "http://github.com/Romantic/hayde"
|
14
|
+
gem.authors = ["Roman Zaharenkov"]
|
15
|
+
gem.add_dependency('RedCloth', '>= 4.1.1')
|
16
|
+
gem.add_dependency('actionpack', '>= 2.9')
|
17
|
+
gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
|
18
|
+
|
19
|
+
gem.files.include %w(lib/hayde/**/*)
|
20
|
+
|
21
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
22
|
+
end
|
23
|
+
Jeweler::GemcutterTasks.new
|
24
|
+
rescue LoadError
|
25
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
26
|
+
end
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
begin
|
36
|
+
require 'rcov/rcovtask'
|
37
|
+
Rcov::RcovTask.new do |test|
|
38
|
+
test.libs << 'test'
|
39
|
+
test.pattern = 'test/**/test_*.rb'
|
40
|
+
test.verbose = true
|
41
|
+
end
|
42
|
+
rescue LoadError
|
43
|
+
task :rcov do
|
44
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
task :test => :check_dependencies
|
49
|
+
|
50
|
+
task :default => :test
|
51
|
+
|
52
|
+
require 'yard'
|
53
|
+
YARD::Rake::YardocTask.new(:yardoc) do |t|
|
54
|
+
t.files = FileList['lib/**/*.rb'].exclude('lib/hayde/templates/**/*.rb')
|
55
|
+
end
|
56
|
+
|
57
|
+
require 'rake/rdoctask'
|
58
|
+
Rake::RDocTask.new do |rdoc|
|
59
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
60
|
+
|
61
|
+
rdoc.rdoc_dir = 'rdoc'
|
62
|
+
rdoc.title = "hayde #{version}"
|
63
|
+
rdoc.rdoc_files.include('README*')
|
64
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
65
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/hayde.gemspec
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{hayde}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Roman Zaharenkov"]
|
12
|
+
s.date = %q{2010-05-14}
|
13
|
+
s.description = %q{Helper for generating guides articles from textile source. Extracted from railties-3.0.0.beta3 project. }
|
14
|
+
s.email = %q{ZaharenkovRoman@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"Gemfile",
|
23
|
+
"LICENSE",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"VERSION",
|
27
|
+
"hayde.gemspec",
|
28
|
+
"lib/hayde.rb",
|
29
|
+
"lib/hayde/generator.rb",
|
30
|
+
"lib/hayde/helpers.rb",
|
31
|
+
"lib/hayde/indexer.rb",
|
32
|
+
"lib/hayde/levenshtein.rb",
|
33
|
+
"lib/hayde/textile_extensions.rb",
|
34
|
+
"test/helper.rb",
|
35
|
+
"test/test_hayde.rb"
|
36
|
+
]
|
37
|
+
s.homepage = %q{http://github.com/Romantic/hayde}
|
38
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
39
|
+
s.require_paths = ["lib"]
|
40
|
+
s.rubygems_version = %q{1.3.7}
|
41
|
+
s.summary = %q{Textile guides generator like rails-guides.}
|
42
|
+
s.test_files = [
|
43
|
+
"test/helper.rb",
|
44
|
+
"test/test_hayde.rb"
|
45
|
+
]
|
46
|
+
|
47
|
+
if s.respond_to? :specification_version then
|
48
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
49
|
+
s.specification_version = 3
|
50
|
+
|
51
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
52
|
+
s.add_runtime_dependency(%q<RedCloth>, [">= 4.1.1"])
|
53
|
+
s.add_runtime_dependency(%q<actionpack>, [">= 2.9"])
|
54
|
+
s.add_development_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
55
|
+
else
|
56
|
+
s.add_dependency(%q<RedCloth>, [">= 4.1.1"])
|
57
|
+
s.add_dependency(%q<actionpack>, [">= 2.9"])
|
58
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
59
|
+
end
|
60
|
+
else
|
61
|
+
s.add_dependency(%q<RedCloth>, [">= 4.1.1"])
|
62
|
+
s.add_dependency(%q<actionpack>, [">= 2.9"])
|
63
|
+
s.add_dependency(%q<thoughtbot-shoulda>, [">= 0"])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
@@ -0,0 +1,244 @@
|
|
1
|
+
# ---------------------------------------------------------------------------
|
2
|
+
#
|
3
|
+
# This script generates the guides. It can be invoked either directly or via the
|
4
|
+
# generate_guides rake task within the railties directory.
|
5
|
+
#
|
6
|
+
# Guides are taken from the source directory, and the resulting HTML goes into the
|
7
|
+
# output directory. Assets are stored under files, and copied to output/files as
|
8
|
+
# part of the generation process.
|
9
|
+
#
|
10
|
+
# Options:
|
11
|
+
#
|
12
|
+
# :warnings
|
13
|
+
# If you are writing a guide, please work always with :warnings = true.
|
14
|
+
# Users can generate the guides, and thus this flag is off by default.
|
15
|
+
#
|
16
|
+
# Internal links (anchors) are checked. If a reference is broken levenshtein
|
17
|
+
# distance is used to suggest an existing one. This is useful since IDs are
|
18
|
+
# generated by Textile from headers and thus edits alter them.
|
19
|
+
#
|
20
|
+
# Also detects duplicated IDs. They happen if there are headers with the same
|
21
|
+
# text. Please do resolve them, if any, so guides are valid XHTML.
|
22
|
+
#
|
23
|
+
# :force
|
24
|
+
# Set to true to force the generation of all guides.
|
25
|
+
#
|
26
|
+
# :edge
|
27
|
+
# Set to true to indicate generated guides should be marked as edge. This
|
28
|
+
# inserts a badge and changes the preamble of the home page.
|
29
|
+
#
|
30
|
+
# :layout
|
31
|
+
# Use to customize layout. Layout must be placed to the same directory with guides sources.
|
32
|
+
#
|
33
|
+
# ---------------------------------------------------------------------------
|
34
|
+
|
35
|
+
require 'set'
|
36
|
+
require 'fileutils'
|
37
|
+
|
38
|
+
require 'active_support/core_ext/string/output_safety'
|
39
|
+
require 'active_support/core_ext/object/blank'
|
40
|
+
require 'action_controller'
|
41
|
+
require 'action_view'
|
42
|
+
|
43
|
+
require 'hayde/indexer'
|
44
|
+
require 'hayde/helpers'
|
45
|
+
require 'hayde/levenshtein'
|
46
|
+
require 'hayde/utils'
|
47
|
+
|
48
|
+
module Hayde
|
49
|
+
class Generator
|
50
|
+
# TODO: Move method to utils mudule.
|
51
|
+
def self.filelist_attribute(*names)
|
52
|
+
names.each do |name|
|
53
|
+
define_method "#{name}" do
|
54
|
+
files = instance_variable_get("@#{name}_files")
|
55
|
+
if !files
|
56
|
+
files = FileList.new()
|
57
|
+
instance_variable_set("@#{name}_files", files)
|
58
|
+
end
|
59
|
+
files
|
60
|
+
end
|
61
|
+
|
62
|
+
define_method "#{name}=" do |files|
|
63
|
+
instance_variable_set("@#{name}_files", FileList[files])
|
64
|
+
if files && files.class != FileList
|
65
|
+
files = FileList.new(files)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
attr_accessor :output_dir, :assets_dir, :warnings, :edge, :force, :layout
|
72
|
+
filelist_attribute :sources
|
73
|
+
|
74
|
+
GUIDES_RE = /\.(?:textile|html\.erb)$/
|
75
|
+
|
76
|
+
def initialize(output = nil)
|
77
|
+
initialize_output_dir(output)
|
78
|
+
yield self if block_given?
|
79
|
+
end
|
80
|
+
|
81
|
+
def generate
|
82
|
+
generate_guides
|
83
|
+
copy_assets
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
def initialize_output_dir(output)
|
89
|
+
@output_dir = output || File.join(File.dirname(__FILE__), "docs", "guides")
|
90
|
+
FileUtils.mkdir_p(@output_dir)
|
91
|
+
end
|
92
|
+
|
93
|
+
def generate_guides
|
94
|
+
sources.each do |source|
|
95
|
+
output = output_file_for(source)
|
96
|
+
generate_guide(source, output) if generate?(source, output)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def copy_assets
|
101
|
+
FileUtils.cp_r(Dir.glob("#{assets_dir}/*"), output_dir)
|
102
|
+
end
|
103
|
+
|
104
|
+
def output_file_for(source)
|
105
|
+
output = File.basename(source).sub(GUIDES_RE, '.html')
|
106
|
+
File.join(output_dir, output)
|
107
|
+
end
|
108
|
+
|
109
|
+
def generate?(source, output)
|
110
|
+
force || !File.exists?(output) || File.mtime(output) < File.mtime(source)
|
111
|
+
end
|
112
|
+
|
113
|
+
def generate_guide(source, output)
|
114
|
+
puts "Generating #{output}"
|
115
|
+
File.open(output, 'w') do |f|
|
116
|
+
view = ActionView::Base.new(File.dirname(source), :edge => edge)
|
117
|
+
view.extend(Helpers)
|
118
|
+
|
119
|
+
if source =~ /\.html\.erb$/
|
120
|
+
# Generate the special pages like the home.
|
121
|
+
result = view.render(:layout => 'layout', :file => source)
|
122
|
+
else
|
123
|
+
body = File.read(source)
|
124
|
+
body = set_header_section(body, view)
|
125
|
+
body = set_index(body, view)
|
126
|
+
|
127
|
+
result = view.render(:layout => 'layout', :text => textile(body))
|
128
|
+
|
129
|
+
warn_about_broken_links(result) if warnings
|
130
|
+
end
|
131
|
+
|
132
|
+
f.write result
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def set_header_section(body, view)
|
137
|
+
new_body = body.gsub(/(.*?)endprologue\./m, '').strip
|
138
|
+
header = $1
|
139
|
+
|
140
|
+
header =~ /h2\.(.*)/
|
141
|
+
page_title = "Framework Guides: #{$1.strip}"
|
142
|
+
|
143
|
+
header = textile(header)
|
144
|
+
|
145
|
+
view.content_for(:page_title) { page_title.html_safe }
|
146
|
+
view.content_for(:header_section) { header.html_safe }
|
147
|
+
new_body
|
148
|
+
end
|
149
|
+
|
150
|
+
def set_index(body, view)
|
151
|
+
index = <<-INDEX
|
152
|
+
<div id="subCol">
|
153
|
+
<h3 class="chapter"><img src="images/chapters_icon.gif" alt="" />Chapters</h3>
|
154
|
+
<ol class="chapters">
|
155
|
+
INDEX
|
156
|
+
|
157
|
+
i = Indexer.new(body, warnings)
|
158
|
+
i.index
|
159
|
+
|
160
|
+
# Set index for 2 levels
|
161
|
+
i.level_hash.each do |key, value|
|
162
|
+
link = view.content_tag(:a, :href => key[:id]) { textile(key[:title], true).html_safe }
|
163
|
+
|
164
|
+
children = value.keys.map do |k|
|
165
|
+
view.content_tag(:li,
|
166
|
+
view.content_tag(:a, :href => k[:id]) { textile(k[:title], true).html_safe })
|
167
|
+
end
|
168
|
+
|
169
|
+
children_ul = children.empty? ? "" : view.content_tag(:ul, children.join(" ").html_safe)
|
170
|
+
|
171
|
+
index << view.content_tag(:li, link.html_safe + children_ul.html_safe)
|
172
|
+
end
|
173
|
+
|
174
|
+
index << '</ol>'
|
175
|
+
index << '</div>'
|
176
|
+
|
177
|
+
view.content_for(:index_section) { index.html_safe }
|
178
|
+
|
179
|
+
i.result
|
180
|
+
end
|
181
|
+
|
182
|
+
def textile(body, lite_mode=false)
|
183
|
+
# If the issue with notextile is fixed just remove the wrapper.
|
184
|
+
with_workaround_for_notextile(body) do |body|
|
185
|
+
t = RedCloth.new(body)
|
186
|
+
t.hard_breaks = false
|
187
|
+
t.lite_mode = lite_mode
|
188
|
+
t.to_html(:notestuff, :plusplus, :code, :tip)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# For some reason the notextile tag does not always turn off textile. See
|
193
|
+
# LH ticket of the security guide (#7). As a temporary workaround we deal
|
194
|
+
# with code blocks by hand.
|
195
|
+
def with_workaround_for_notextile(body)
|
196
|
+
code_blocks = []
|
197
|
+
body.gsub!(%r{<(yaml|shell|ruby|erb|html|sql|plain)>(.*?)</\1>}m) do |m|
|
198
|
+
es = ERB::Util.h($2)
|
199
|
+
css_class = ['erb', 'shell'].include?($1) ? 'html' : $1
|
200
|
+
code_blocks << %{<div class="code_container"><code class="#{css_class}">#{es}</code></div>}
|
201
|
+
"\ndirty_workaround_for_notextile_#{code_blocks.size - 1}\n"
|
202
|
+
end
|
203
|
+
|
204
|
+
body = yield body
|
205
|
+
|
206
|
+
body.gsub(%r{<p>dirty_workaround_for_notextile_(\d+)</p>}) do |_|
|
207
|
+
code_blocks[$1.to_i]
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def warn_about_broken_links(html)
|
212
|
+
anchors = extract_anchors(html)
|
213
|
+
check_fragment_identifiers(html, anchors)
|
214
|
+
end
|
215
|
+
|
216
|
+
def extract_anchors(html)
|
217
|
+
# Textile generates headers with IDs computed from titles.
|
218
|
+
anchors = Set.new
|
219
|
+
html.scan(/<h\d\s+id="([^"]+)/).flatten.each do |anchor|
|
220
|
+
if anchors.member?(anchor)
|
221
|
+
puts "*** DUPLICATE ID: #{anchor}, please put and explicit ID, e.g. h4(#explicit-id), or consider rewording"
|
222
|
+
else
|
223
|
+
anchors << anchor
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Also, footnotes are rendered as paragraphs this way.
|
228
|
+
anchors += Set.new(html.scan(/<p\s+class="footnote"\s+id="([^"]+)/).flatten)
|
229
|
+
return anchors
|
230
|
+
end
|
231
|
+
|
232
|
+
def check_fragment_identifiers(html, anchors)
|
233
|
+
html.scan(/<a\s+href="#([^"]+)/).flatten.each do |fragment_identifier|
|
234
|
+
next if fragment_identifier == 'mainCol' # in layout, jumps to some DIV
|
235
|
+
unless anchors.member?(fragment_identifier)
|
236
|
+
guess = anchors.min { |a, b|
|
237
|
+
Levenshtein.distance(fragment_identifier, a) <=> Levenshtein.distance(fragment_identifier, b)
|
238
|
+
}
|
239
|
+
puts "*** BROKEN LINK: ##{fragment_identifier}, perhaps you meant ##{guess}."
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Hayde
|
2
|
+
module Helpers
|
3
|
+
def guide(name, url, options = {}, &block)
|
4
|
+
link = content_tag(:a, :href => url) { name }
|
5
|
+
result = content_tag(:dt, link)
|
6
|
+
|
7
|
+
if ticket = options[:ticket]
|
8
|
+
result << content_tag(:dd, lh(ticket), :class => 'ticket')
|
9
|
+
end
|
10
|
+
|
11
|
+
result << content_tag(:dd, capture(&block))
|
12
|
+
result
|
13
|
+
end
|
14
|
+
|
15
|
+
def lh(id, label = "Lighthouse Ticket")
|
16
|
+
url = "http://rails.lighthouseapp.com/projects/16213/tickets/#{id}"
|
17
|
+
content_tag(:a, label, :href => url)
|
18
|
+
end
|
19
|
+
|
20
|
+
def author(name, nick, image = 'credits_pic_blank.gif', &block)
|
21
|
+
image = "images/#{image}"
|
22
|
+
|
23
|
+
result = content_tag(:img, nil, :src => image, :class => 'left pic', :alt => name)
|
24
|
+
result << content_tag(:h3, name)
|
25
|
+
result << content_tag(:p, capture(&block))
|
26
|
+
content_tag(:div, result, :class => 'clearfix', :id => nick)
|
27
|
+
end
|
28
|
+
|
29
|
+
def code(&block)
|
30
|
+
c = capture(&block)
|
31
|
+
content_tag(:code, c)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
require 'active_support/ordered_hash'
|
3
|
+
|
4
|
+
module Hayde
|
5
|
+
class Indexer
|
6
|
+
attr_reader :body, :result, :warnings, :level_hash
|
7
|
+
|
8
|
+
def initialize(body, warnings)
|
9
|
+
@body = body
|
10
|
+
@result = @body.dup
|
11
|
+
@warnings = warnings
|
12
|
+
end
|
13
|
+
|
14
|
+
def index
|
15
|
+
@level_hash = process(body)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def process(string, current_level=3, counters=[1])
|
21
|
+
s = StringScanner.new(string)
|
22
|
+
|
23
|
+
level_hash = ActiveSupport::OrderedHash.new
|
24
|
+
|
25
|
+
while !s.eos?
|
26
|
+
re = %r{^h(\d)(?:\((#.*?)\))?\s*\.\s*(.*)$}
|
27
|
+
s.match?(re)
|
28
|
+
if matched = s.matched
|
29
|
+
matched =~ re
|
30
|
+
level, idx, title = $1.to_i, $2, $3.strip
|
31
|
+
|
32
|
+
if level < current_level
|
33
|
+
# This is needed. Go figure.
|
34
|
+
return level_hash
|
35
|
+
elsif level == current_level
|
36
|
+
index = counters.join(".")
|
37
|
+
idx ||= '#' + title_to_idx(title)
|
38
|
+
|
39
|
+
raise "Parsing Fail" unless @result.sub!(matched, "h#{level}(#{idx}). #{index} #{title}")
|
40
|
+
|
41
|
+
key = {
|
42
|
+
:title => title,
|
43
|
+
:id => idx
|
44
|
+
}
|
45
|
+
# Recurse
|
46
|
+
counters << 1
|
47
|
+
level_hash[key] = process(s.post_match, current_level + 1, counters)
|
48
|
+
counters.pop
|
49
|
+
|
50
|
+
# Increment the current level
|
51
|
+
last = counters.pop
|
52
|
+
counters << last + 1
|
53
|
+
end
|
54
|
+
end
|
55
|
+
s.getch
|
56
|
+
end
|
57
|
+
level_hash
|
58
|
+
end
|
59
|
+
|
60
|
+
def title_to_idx(title)
|
61
|
+
idx = title.strip.downcase.gsub(/\s+|_/, '-').delete('^a-z0-9-').sub(/^[^a-z]*/, '')
|
62
|
+
if warnings && idx.blank?
|
63
|
+
puts "BLANK ID: please put an explicit ID for section #{title}, as in h5(#my-id)"
|
64
|
+
end
|
65
|
+
idx
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Hayde
|
2
|
+
module Levenshtein
|
3
|
+
# Based on the pseudocode in http://en.wikipedia.org/wiki/Levenshtein_distance.
|
4
|
+
def self.distance(s1, s2)
|
5
|
+
s = s1.unpack('U*')
|
6
|
+
t = s2.unpack('U*')
|
7
|
+
m = s.length
|
8
|
+
n = t.length
|
9
|
+
|
10
|
+
# matrix initialization
|
11
|
+
d = []
|
12
|
+
0.upto(m) { |i| d << [i] }
|
13
|
+
0.upto(n) { |j| d[0][j] = j }
|
14
|
+
|
15
|
+
# distance computation
|
16
|
+
1.upto(m) do |i|
|
17
|
+
1.upto(n) do |j|
|
18
|
+
cost = s[i] == t[j] ? 0 : 1
|
19
|
+
d[i][j] = [
|
20
|
+
d[i-1][j] + 1, # deletion
|
21
|
+
d[i][j-1] + 1, # insertion
|
22
|
+
d[i-1][j-1] + cost, # substitution
|
23
|
+
].min
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# all done
|
28
|
+
return d[m][n]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Hayde
|
2
|
+
module TextileExtensions
|
3
|
+
def notestuff(body)
|
4
|
+
body.gsub!(/^(IMPORTANT|CAUTION|WARNING|NOTE|INFO)[.:](.*)$/) do |m|
|
5
|
+
css_class = $1.downcase
|
6
|
+
css_class = 'warning' if ['caution', 'important'].include?(css_class)
|
7
|
+
|
8
|
+
result = "<div class='#{css_class}'><p>"
|
9
|
+
result << $2.strip
|
10
|
+
result << '</p></div>'
|
11
|
+
result
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def tip(body)
|
16
|
+
body.gsub!(/^TIP[.:](.*)$/) do |m|
|
17
|
+
result = "<div class='info'><p>"
|
18
|
+
result << $1.strip
|
19
|
+
result << '</p></div>'
|
20
|
+
result
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def plusplus(body)
|
25
|
+
body.gsub!(/\+(.*?)\+/) do |m|
|
26
|
+
"<notextile><tt>#{$1}</tt></notextile>"
|
27
|
+
end
|
28
|
+
|
29
|
+
# The real plus sign
|
30
|
+
body.gsub!('<plus>', '+')
|
31
|
+
end
|
32
|
+
|
33
|
+
def code(body)
|
34
|
+
body.gsub!(%r{<(yaml|shell|ruby|erb|html|sql|plain)>(.*?)</\1>}m) do |m|
|
35
|
+
es = ERB::Util.h($2)
|
36
|
+
css_class = ['erb', 'shell'].include?($1) ? 'html' : $1
|
37
|
+
%{<notextile><div class="code_container"><code class="#{css_class}">#{es}</code></div></notextile>}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/hayde.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
pwd = File.dirname(__FILE__)
|
2
|
+
$:.unshift pwd
|
3
|
+
|
4
|
+
# Loading Action Pack requires rack and erubis.
|
5
|
+
require 'rubygems'
|
6
|
+
|
7
|
+
begin
|
8
|
+
# Guides generation in the Rails repo.
|
9
|
+
as_lib = File.join(pwd, "../../activesupport/lib")
|
10
|
+
ap_lib = File.join(pwd, "../../actionpack/lib")
|
11
|
+
|
12
|
+
$:.unshift as_lib if File.directory?(as_lib)
|
13
|
+
$:.unshift ap_lib if File.directory?(ap_lib)
|
14
|
+
rescue LoadError
|
15
|
+
# Guides generation from gems.
|
16
|
+
gem "actionpack", '>= 3.0'
|
17
|
+
end
|
18
|
+
|
19
|
+
begin
|
20
|
+
gem 'RedCloth', '>= 4.1.1'
|
21
|
+
require 'redcloth'
|
22
|
+
rescue Gem::LoadError
|
23
|
+
$stderr.puts %(Generating Guides requires RedCloth 4.1.1+)
|
24
|
+
exit 1
|
25
|
+
end
|
26
|
+
|
27
|
+
module Hayde
|
28
|
+
require 'hayde/textile_extensions'
|
29
|
+
require 'hayde/generator'
|
30
|
+
|
31
|
+
RedCloth.send(:include, Hayde::TextileExtensions)
|
32
|
+
#Hayde::Generator.new.generate
|
33
|
+
end
|
data/test/helper.rb
ADDED
data/test/test_hayde.rb
ADDED
metadata
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: hayde
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Roman Zaharenkov
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-05-14 00:00:00 +03:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
name: RedCloth
|
25
|
+
version_requirements: &id001 !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 57
|
31
|
+
segments:
|
32
|
+
- 4
|
33
|
+
- 1
|
34
|
+
- 1
|
35
|
+
version: 4.1.1
|
36
|
+
requirement: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
name: actionpack
|
41
|
+
version_requirements: &id002 !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
hash: 17
|
47
|
+
segments:
|
48
|
+
- 2
|
49
|
+
- 9
|
50
|
+
version: "2.9"
|
51
|
+
requirement: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
type: :development
|
54
|
+
prerelease: false
|
55
|
+
name: thoughtbot-shoulda
|
56
|
+
version_requirements: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 3
|
62
|
+
segments:
|
63
|
+
- 0
|
64
|
+
version: "0"
|
65
|
+
requirement: *id003
|
66
|
+
description: "Helper for generating guides articles from textile source. Extracted from railties-3.0.0.beta3 project. "
|
67
|
+
email: ZaharenkovRoman@gmail.com
|
68
|
+
executables: []
|
69
|
+
|
70
|
+
extensions: []
|
71
|
+
|
72
|
+
extra_rdoc_files:
|
73
|
+
- LICENSE
|
74
|
+
- README.rdoc
|
75
|
+
files:
|
76
|
+
- .document
|
77
|
+
- .gitignore
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE
|
80
|
+
- README.rdoc
|
81
|
+
- Rakefile
|
82
|
+
- VERSION
|
83
|
+
- hayde.gemspec
|
84
|
+
- lib/hayde.rb
|
85
|
+
- lib/hayde/generator.rb
|
86
|
+
- lib/hayde/helpers.rb
|
87
|
+
- lib/hayde/indexer.rb
|
88
|
+
- lib/hayde/levenshtein.rb
|
89
|
+
- lib/hayde/textile_extensions.rb
|
90
|
+
- test/helper.rb
|
91
|
+
- test/test_hayde.rb
|
92
|
+
has_rdoc: true
|
93
|
+
homepage: http://github.com/Romantic/hayde
|
94
|
+
licenses: []
|
95
|
+
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options:
|
98
|
+
- --charset=UTF-8
|
99
|
+
require_paths:
|
100
|
+
- lib
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
102
|
+
none: false
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
hash: 3
|
107
|
+
segments:
|
108
|
+
- 0
|
109
|
+
version: "0"
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
none: false
|
112
|
+
requirements:
|
113
|
+
- - ">="
|
114
|
+
- !ruby/object:Gem::Version
|
115
|
+
hash: 3
|
116
|
+
segments:
|
117
|
+
- 0
|
118
|
+
version: "0"
|
119
|
+
requirements: []
|
120
|
+
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 1.3.7
|
123
|
+
signing_key:
|
124
|
+
specification_version: 3
|
125
|
+
summary: Textile guides generator like rails-guides.
|
126
|
+
test_files:
|
127
|
+
- test/helper.rb
|
128
|
+
- test/test_hayde.rb
|