inversion 0.0.1 → 0.0.2

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.
Files changed (50) hide show
  1. data.tar.gz.sig +0 -0
  2. data/Manifest.txt +45 -0
  3. data/lib/inversion.rb +2 -2
  4. data/lib/inversion/template/begintag.rb +122 -0
  5. data/lib/inversion/template/defaulttag.rb +135 -0
  6. data/lib/inversion/template/rescuetag.rb +92 -0
  7. data/lib/inversion/template/timedeltatag.rb +93 -0
  8. data/manual/layouts/default.erb +87 -0
  9. data/manual/lib/api-filter.rb +96 -0
  10. data/manual/lib/editorial-filter.rb +59 -0
  11. data/manual/lib/examples-filter.rb +238 -0
  12. data/manual/lib/links-filter.rb +111 -0
  13. data/manual/resources/css/manual.css +764 -0
  14. data/manual/resources/fonts/GraublauWeb.otf +0 -0
  15. data/manual/resources/fonts/GraublauWebBold.otf +0 -0
  16. data/manual/resources/fonts/Inconsolata.otf +0 -0
  17. data/manual/resources/images/arrow_225_small.png +0 -0
  18. data/manual/resources/images/arrow_315_small.png +0 -0
  19. data/manual/resources/images/arrow_skip.png +0 -0
  20. data/manual/resources/images/cc-by.png +0 -0
  21. data/manual/resources/images/dialog-error.png +0 -0
  22. data/manual/resources/images/dialog-information.png +0 -0
  23. data/manual/resources/images/dialog-warning.png +0 -0
  24. data/manual/resources/images/emblem-important.png +0 -0
  25. data/manual/resources/images/help.png +0 -0
  26. data/manual/resources/images/information.png +0 -0
  27. data/manual/resources/images/magnifier.png +0 -0
  28. data/manual/resources/images/magnifier_left.png +0 -0
  29. data/manual/resources/images/page_white_code.png +0 -0
  30. data/manual/resources/images/page_white_copy.png +0 -0
  31. data/manual/resources/images/printer.png +0 -0
  32. data/manual/resources/images/question.png +0 -0
  33. data/manual/resources/images/scripts_code.png +0 -0
  34. data/manual/resources/images/wrap.png +0 -0
  35. data/manual/resources/images/wrapping.png +0 -0
  36. data/manual/resources/js/jquery-1.4.4.min.js +167 -0
  37. data/manual/resources/js/manual.js +30 -0
  38. data/manual/resources/js/sh.js +580 -0
  39. data/manual/resources/swf/clipboard.swf +0 -0
  40. data/manual/src/examples.page +154 -0
  41. data/manual/src/gettingstarted.page +64 -0
  42. data/manual/src/index.page +97 -0
  43. data/manual/src/tags.page +501 -0
  44. data/manual/src/templates.page +74 -0
  45. data/spec/inversion/template/begintag_spec.rb +217 -0
  46. data/spec/inversion/template/defaulttag_spec.rb +60 -0
  47. data/spec/inversion/template/rescuetag_spec.rb +109 -0
  48. data/spec/inversion/template/timedeltatag_spec.rb +215 -0
  49. metadata +72 -27
  50. metadata.gz.sig +0 -0
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+ # vim: set noet nosta sw=4 ts=4 :
3
+
4
+ require 'uri'
5
+ require 'time'
6
+ require 'date'
7
+ require 'inversion/template/attrtag'
8
+
9
+ # Inversion time delta tag.
10
+ #
11
+ # This tag is a derivative of the 'attr' tag that transforms the results of its method call
12
+ # to a Time object (if it isn't already), and then generates an English description of
13
+ # the different between it and the current time.
14
+ #
15
+ # == Syntax
16
+ #
17
+ # Updated <?timedelta entry.update_date ?>.
18
+ #
19
+ class Inversion::Template::TimeDeltaTag < Inversion::Template::AttrTag
20
+
21
+ # Approximate Time Constants (in seconds)
22
+ MINUTES = 60
23
+ HOURS = 60 * MINUTES
24
+ DAYS = 24 * HOURS
25
+ WEEKS = 7 * DAYS
26
+ MONTHS = 30 * DAYS
27
+ YEARS = 365.25 * DAYS
28
+
29
+
30
+ ######
31
+ public
32
+ ######
33
+
34
+ ### Render the tag.
35
+ def render( renderstate )
36
+ val = super( renderstate )
37
+ time = nil
38
+
39
+ if val.respond_to?( :to_time )
40
+ time = val.to_time
41
+ elsif val.is_a?( Numeric )
42
+ time = Time.at( val )
43
+ else
44
+ time = Time.parse( val.to_s )
45
+ end
46
+
47
+ now = Time.now
48
+ if now > time
49
+ seconds = now - time
50
+ return "%s ago" % [ timeperiod(seconds) ]
51
+ else
52
+ seconds = time - now
53
+ return "%s from now" % [ timeperiod(seconds) ]
54
+ end
55
+ end
56
+
57
+
58
+ #######
59
+ private
60
+ #######
61
+
62
+ ### Return a string describing +seconds+ as an approximate interval of time.
63
+ def timeperiod( seconds )
64
+ return case
65
+ when seconds < MINUTES - 5
66
+ 'less than a minute'
67
+ when seconds < 50 * MINUTES
68
+ if seconds <= 89
69
+ "a minute"
70
+ else
71
+ "%d minutes" % [ (seconds.to_f / MINUTES).ceil ]
72
+ end
73
+ when seconds < 90 * MINUTES
74
+ 'about an hour'
75
+ when seconds < 18 * HOURS
76
+ "%d hours" % [ (seconds.to_f / HOURS).ceil ]
77
+ when seconds < 30 * HOURS
78
+ 'about a day'
79
+ when seconds < WEEKS
80
+ "%d days" % [ (seconds.to_f / DAYS).ceil ]
81
+ when seconds < 2 * WEEKS
82
+ 'about a week'
83
+ when seconds < 3 * MONTHS
84
+ "%d weeks" % [ (seconds.to_f / WEEKS).ceil ]
85
+ when seconds < 18 * MONTHS
86
+ "%d months" % [ (seconds.to_f / MONTHS).ceil ]
87
+ else
88
+ "%d years" % [ (seconds.to_f / YEARS).ceil ]
89
+ end
90
+ end
91
+
92
+ end # class Inversion::Template::TimeDeltaTag
93
+
@@ -0,0 +1,87 @@
1
+ <!DOCTYPE html>
2
+ <!--
3
+
4
+ Main Manual Layout
5
+ $Id: default.erb,v 8d85088f0601 2011/08/15 16:36:48 mahlon $
6
+
7
+ Author: Michael Granger <ged@FaerieMUD.org>
8
+ Two column fluid layout adapted from Matthew James Taylor's "Perfect 'Left Menu'
9
+ 2 Column Liquid Layout":
10
+ http://matthewjamestaylor.com/blog/perfect-2-column-left-menu.htm
11
+
12
+ -->
13
+ <html lang="en">
14
+ <head>
15
+ <meta charset="utf-8">
16
+ <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
17
+
18
+ <title>inversion Manual — <%= page.config['title'] || "untitled" %></title>
19
+
20
+ <link rel="stylesheet" href="css/manual.css">
21
+
22
+ <script src="js/jquery-1.4.4.min.js"></script>
23
+ <script src="js/sh.js"></script>
24
+ <script src="js/manual.js"></script>
25
+
26
+ </head>
27
+ <body>
28
+ <div id="page">
29
+
30
+ <header>
31
+ <hgroup>
32
+ <a href="/">
33
+ <h1>inversion</h1>
34
+ </a>
35
+ <% if metadata.version %><h2 class="version"><%= metadata.version %></h2><% end %>
36
+ <% if page.config['tagline'] %><h2 class="tagline"><%= page.config['tagline'] %></h2><% end %>
37
+ </hgroup>
38
+ </header>
39
+
40
+ <div class="colmask leftmenu">
41
+ <div class="colleft">
42
+
43
+ <section id="content" class="col1">
44
+ <!-- Generated content -->
45
+ <%= content %>
46
+ <!-- end of generated content -->
47
+ </section>
48
+
49
+ <aside id="sidebar" class="col2">
50
+ <nav>
51
+ <%
52
+ @catalog.traverse_page_hierarchy( self ) do |type, title, path|
53
+ case type
54
+ when :section
55
+ %>
56
+ <div class="section">
57
+ <h3><a href="<%= self.basepath + path %>/"><%= title %></a></h3>
58
+ <ul class="index-section">
59
+ <% when :current_section %>
60
+ <div class="section current-section">
61
+ <h3><a href="<%= self.basepath + path %>/"><%= title %></a></h3>
62
+ <ul class="index-section current-index-section">
63
+ <% when :section_end, :current_section_end %>
64
+ </ul>
65
+ </div>
66
+ <% when :entry %>
67
+ <li><a href="<%= self.basepath + path %>.html"><%= title %></a></li>
68
+ <% when :current_entry %>
69
+ <li class="current-entry"><%= title %></li>
70
+ <% end
71
+ end
72
+ %>
73
+ </nav>
74
+ </aside>
75
+
76
+ </div>
77
+ </div>
78
+
79
+ <footer>
80
+ <div class="copyright">Copyright &copy; 2008-<%= Time.now.year %> Michael Granger, Mahlon E. Smith.</div>
81
+ <div class="vcsrev">Rev: $Revision: 8d85088f0601 $</div>
82
+ <div class="timestamp">Built: <%= Time.now.strftime("%Y%m%d %H:%M:%S %Z") %></div>
83
+ </footer>
84
+
85
+ </body>
86
+ </html>
87
+
@@ -0,0 +1,96 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # A manual filter to generate links from the Darkfish API.
4
+ #
5
+ # Authors:
6
+ # * Michael Granger <ged@FaerieMUD.org>
7
+ # * Mahlon E. Smith <mahlon@martini.nu>
8
+ #
9
+
10
+
11
+ ### A filter for generating links from the generated API documentation. This allows you to refer
12
+ ### to class documentation by simply referencing a class name.
13
+ ###
14
+ ### Links are XML processing instructions. Pages can be referenced as such:
15
+ ###
16
+ ### <?api Class::Name ?>
17
+ ### <?api Class::Name#instance_method ?>
18
+ ### <?api Class::Name.class_method ?>
19
+ ###
20
+ ### Link text can be overridden, too:
21
+ ###
22
+ ### <?api "click here":Class::Name ?>
23
+ ### <?api "click here":Class::Name#instance_method ?>
24
+ ###
25
+ class Hoe::ManualGen::APIFilter < Hoe::ManualGen::PageFilter
26
+
27
+ # PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
28
+ ApiPI = %r{
29
+ <\?
30
+ api # Instruction Target
31
+ \s+
32
+ (?:"
33
+ (.*?) # Optional link text [$1]
34
+ ":)?
35
+ (.*?) # Class name [$2]
36
+ ([\.#].*?)? # Method anchor [$3]
37
+ \s+
38
+ \?>
39
+ }x
40
+
41
+
42
+ ######
43
+ public
44
+ ######
45
+
46
+ ### Process the given +source+ for <?api ... ?> processing-instructions, calling out
47
+ def process( source, page, metadata )
48
+
49
+ apipath = metadata.api_dir or
50
+ raise "The API output directory is not defined in the manual task."
51
+
52
+ return source.gsub( ApiPI ) do |match|
53
+ # Grab the tag values
54
+ link_text = $1
55
+ classname = $2
56
+ methodname = $3
57
+
58
+ self.generate_link( page, apipath, classname, methodname, link_text )
59
+ end
60
+ end
61
+
62
+
63
+ ### Create an HTML link fragment from the parsed ApiPI.
64
+ def generate_link( current_page, apipath, classname, methodname=nil, link_text=nil )
65
+
66
+ classpath = "%s.html" % [ classname.gsub('::', '/') ]
67
+ classfile = apipath + classpath
68
+ classuri = current_page.basepath + 'api' + classpath
69
+
70
+ if classfile.exist?
71
+ return %{<a href="%s%s">%s</a>} % [
72
+ classuri,
73
+ make_anchor( methodname ),
74
+ link_text || (classname + methodname || '')
75
+ ]
76
+ else
77
+ link_text ||= classname
78
+ error_message = "Could not find a link for class '%s'" % [ classname ]
79
+ $stderr.puts( error_message )
80
+ return %{<a href="#" title="#{error_message}" class="broken-link">#{link_text}</a>}
81
+ end
82
+ end
83
+
84
+
85
+ ### Attach a method anchor.
86
+ def make_anchor( methodname )
87
+ return '' unless methodname
88
+
89
+ method_type = methodname[ 0, 1 ]
90
+ return "#method-%s-%s" % [
91
+ ( method_type == '#' ? 'i' : 'c' ),
92
+ methodname[ 1..-1 ]
93
+ ]
94
+ end
95
+ end
96
+
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # A manual filter to highlight content that needs editorial help.
4
+ #
5
+ # Authors:
6
+ # * Michael Granger <ged@FaerieMUD.org>
7
+ #
8
+ #
9
+
10
+
11
+
12
+ ### A filter for making editorial marks in manual content.
13
+ ###
14
+ ### Editorial marks are XML processing instructions. There are several available types of
15
+ ### marks:
16
+ ###
17
+ ### <?ed "This is an editor's note." ?>
18
+ ### <?ed verify:"this content needs checking or verification" ?>
19
+ ###
20
+ class Hoe::ManualGen::EditorialFilter < Hoe::ManualGen::PageFilter
21
+
22
+ # PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
23
+ LinkPI = %r{
24
+ <\?
25
+ ed # Instruction Target
26
+ \s+
27
+ (\w+?) # type of editorial mark [$1]
28
+ :? # optional colon
29
+ "
30
+ (.*?) # content that should be edited [$2]
31
+ "
32
+ \s*
33
+ \?>
34
+ }x
35
+
36
+
37
+ ######
38
+ public
39
+ ######
40
+
41
+ ### Process the given +source+ for <?ed ... ?> processing-instructions
42
+ def process( source, page, metadata )
43
+ return source.gsub( LinkPI ) do |match|
44
+ # Grab the tag values
45
+ mark_type = $1
46
+ content = $2
47
+
48
+ self.generate_mark( page, mark_type, content )
49
+ end
50
+ end
51
+
52
+
53
+ ### Create an HTML fragment from the parsed LinkPI.
54
+ def generate_mark( current_page, mark_type, content )
55
+ return "%%(editorial %s-mark)%s%%" % [ mark_type, content ]
56
+ end
57
+
58
+
59
+ end
@@ -0,0 +1,238 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # A collection of standard filters for the manual generation tasklib.
4
+ #
5
+ # Authors:
6
+ # Michael Granger <ged@FaerieMUD.org>
7
+ #
8
+ #
9
+
10
+ # Dependencies deferred until #initialize
11
+
12
+
13
+
14
+ ### A filter for inline example code or command-line sessions -- does
15
+ ### syntax-checking for some languages and captioning.
16
+ ###
17
+ ### Examples are enclosed in XML processing instructions like so:
18
+ ###
19
+ ### <?example {language: ruby, testable: true, caption: "A fine example"} ?>
20
+ ### a = 1
21
+ ### puts a
22
+ ### <?end example ?>
23
+ ###
24
+ ### This will be pulled out into a preformatted section in the HTML,
25
+ ### highlighted as Ruby source, checked for valid syntax, and annotated with
26
+ ### the specified caption. Valid keys in the example PI are:
27
+ ###
28
+ ### language::
29
+ ### Specifies which (machine) language the example is in.
30
+ ### testable::
31
+ ### If set and there is a testing function for the given language, run it and append
32
+ ### any errors to the output.
33
+ ### caption::
34
+ ### A small blurb to put below the pulled-out example in the HTML.
35
+ class Hoe::ManualGen::ExamplesFilter < Hoe::ManualGen::PageFilter
36
+
37
+ DEFAULTS = {
38
+ :language => :ruby,
39
+ :line_numbers => :inline,
40
+ :tab_width => 4,
41
+ :hint => :debug,
42
+ :testable => false,
43
+ }
44
+
45
+ # PI ::= '<?' PITarget (S (Char* - (Char* '?>' Char*)))? '?>'
46
+ ExamplePI = %r{
47
+ <\?
48
+ example # Instruction Target
49
+ (?: # Optional instruction body
50
+ \s+
51
+ ((?: # [$1]
52
+ [^?]* # Run of anything but a question mark
53
+ | # -or-
54
+ \?(?!>) # question mark not followed by a closing angle bracket
55
+ )*)
56
+ )?
57
+ \?>
58
+ }x
59
+
60
+ EndPI = %r{ <\? end (?: \s+ example )? \s* \?> }x
61
+
62
+
63
+ ### Defer loading of dependenies until the filter is loaded
64
+ def initialize( *args )
65
+ begin
66
+ require 'pathname'
67
+ require 'strscan'
68
+ require 'yaml'
69
+ require 'rcodetools/xmpfilter'
70
+ require 'digest/md5'
71
+ require 'tmpdir'
72
+ require 'erb'
73
+ rescue LoadError => err
74
+ unless Object.const_defined?( :Gem )
75
+ require 'rubygems'
76
+ retry
77
+ end
78
+
79
+ raise
80
+ end
81
+ end
82
+
83
+
84
+ ######
85
+ public
86
+ ######
87
+
88
+ ### Process the given +source+ for <?example ... ?> processing-instructions, calling out
89
+ def process( source, page, metadata )
90
+ scanner = StringScanner.new( source )
91
+
92
+ buffer = ''
93
+ until scanner.eos?
94
+ startpos = scanner.pos
95
+
96
+ # If we find an example
97
+ if scanner.skip_until( ExamplePI )
98
+ contents = ''
99
+
100
+ # Append the interstitial content to the buffer
101
+ if ( scanner.pos - startpos > scanner.matched.length )
102
+ offset = scanner.pos - scanner.matched.length - 1
103
+ buffer << scanner.string[ startpos..offset ]
104
+ end
105
+
106
+ # Append everything up to it to the buffer and save the contents of
107
+ # the tag
108
+ params = scanner[1]
109
+
110
+ # Now find the end of the example or complain
111
+ contentpos = scanner.pos
112
+ scanner.skip_until( EndPI ) or
113
+ raise "Unterminated example at line %d" %
114
+ [ scanner.string[0..scanner.pos].count("\n") ]
115
+
116
+ # Now build the example and append to the buffer
117
+ if ( scanner.pos - contentpos > scanner.matched.length )
118
+ offset = scanner.pos - scanner.matched.length - 1
119
+ contents = scanner.string[ contentpos..offset ]
120
+ end
121
+
122
+ trace "Processing with params: %p, contents: %p" % [ params, contents ]
123
+ buffer << self.process_example( params, contents, page )
124
+ else
125
+ break
126
+ end
127
+
128
+ end
129
+ buffer << scanner.rest
130
+ scanner.terminate
131
+
132
+ return buffer
133
+ end
134
+
135
+
136
+ ### Filter out 'example' macros, doing syntax highlighting, and running
137
+ ### 'testable' examples through a validation process appropriate to the
138
+ ### language the example is in.
139
+ def process_example( params, body, page )
140
+ options = self.parse_options( params )
141
+ caption = options.delete( :caption )
142
+ content = ''
143
+ lang = options.delete( :language ).to_s
144
+
145
+ # Test it if it's testable
146
+ if options[:testable]
147
+ content = test_content( body, lang, page )
148
+ else
149
+ content = body
150
+ end
151
+
152
+ # Strip trailing blank lines and syntax-highlight
153
+ content = highlight( content.strip, options, lang )
154
+ caption = %{<div class="caption">} + caption.to_s + %{</div>} if caption
155
+
156
+ return %{<notextile><div class="example #{lang}-example">%s%s</div></notextile>} %
157
+ [content, caption || '']
158
+ end
159
+
160
+
161
+ ### Parse an options hash for filtering from the given +args+, which can either
162
+ ### be a plain String, in which case it is assumed to be the name of the language the example
163
+ ### is in, or a Hash of configuration options.
164
+ def parse_options( args )
165
+ args = "{ #{args} }" unless args.strip[0] == ?{
166
+ args = YAML.load( args )
167
+
168
+ # Convert to Symbol keys and value
169
+ args.keys.each do |k|
170
+ newval = args.delete( k )
171
+ next if newval.nil? || (newval.respond_to?(:size) && newval.size == 0)
172
+ args[ k.to_sym ] = newval.respond_to?( :to_sym ) ? newval.to_sym : newval
173
+ end
174
+ return DEFAULTS.merge( args )
175
+ end
176
+
177
+
178
+ ### Test the given +content+ with a rule specific to the given +language+.
179
+ def test_content( body, language, page )
180
+ case language.to_sym
181
+ when :ruby
182
+ return self.test_ruby_content( body, page )
183
+
184
+ when :yaml
185
+ return self.test_yaml_content( body, page )
186
+
187
+ else
188
+ return body
189
+ end
190
+ end
191
+
192
+
193
+ ### Test the specified Ruby content for valid syntax
194
+ def test_ruby_content( source, page )
195
+ # $stderr.puts "Testing ruby content..."
196
+ libdir = Pathname.new( __FILE__ ).dirname.parent.parent.parent + 'lib'
197
+ extdir = Pathname.new( __FILE__ ).dirname.parent.parent.parent + 'ext'
198
+
199
+ options = Rcodetools::XMPFilter::INITIALIZE_OPTS.dup
200
+ options[:include_paths] |= [ libdir.to_s, extdir.to_s ]
201
+ options[:width] = 60
202
+
203
+ if page.config['example_prelude']
204
+ prelude = page.config['example_prelude']
205
+ trace " prepending prelude:\n#{prelude}"
206
+ source = prelude.strip + "\n" + source.strip
207
+ else
208
+ trace " no prelude; page config is: %p" % [ page.config ]
209
+ end
210
+
211
+ rval = Rcodetools::XMPFilter.run( source, options )
212
+
213
+ trace "test output: ", rval
214
+ return rval.join
215
+ rescue Exception => err
216
+ return "%s while testing: %s\n %s" %
217
+ [ err.class.name, err.message, err.backtrace.join("\n ") ]
218
+ end
219
+
220
+
221
+ ### Test the specified YAML content for valid syntax
222
+ def test_yaml_content( source, metadata )
223
+ YAML.load( source )
224
+ rescue YAML::Error => err
225
+ return "# Invalid YAML: " + err.message + "\n" + source
226
+ else
227
+ return source
228
+ end
229
+
230
+
231
+ ### Highlights the given +content+ in language +lang+.
232
+ def highlight( content, options, lang )
233
+ source = ERB::Util.html_escape( content )
234
+ return %Q{\n\n<pre class="brush:#{lang}">#{source}</pre>\n\n}
235
+ end
236
+
237
+ end
238
+