Class: Strelka::CMS::PageFilter::Example
- Inherits:
-
Strelka::CMS::PageFilter
- Object
- Strelka::CMS::PageFilter
- Strelka::CMS::PageFilter::Example
- Defined in:
- lib/strelka/cms/pagefilter/example.rb
Overview
A filter for inline example code or command-line sessions – does syntax-highlighting (via CodeRay), syntax-checking for some languages, and captioning.
Examples are enclosed in XML processing instructions like so:
<?example {language: ruby, testable: true, caption: "A fine example"} ?>
a = 1
puts a
<?end example ?>
This will be pulled out into a preformatted section in the HTML, highlighted as Ruby source, checked for valid syntax, and annotated with the specified caption. Valid keys in the example PI are:
- language
-
Specifies which (machine) language the example is in.
- testable
-
If set and there is a testing function for the given language, run it and append any errors to the output.
- caption
-
A small blurb to put below the pulled-out example in the HTML.
Constant Summary collapse
- DEFAULTS =
{ :language => :shell, :line_numbers => :inline, :tab_width => 4, :hint => :debug, :testable => false, }
- EXAMPLE_PI =
PI ::= ‘<?’ PITarget (S (Char* - (Char* ‘?>’ Char*)))? ‘?>’
%r{ <\? example # Instruction Target (?: # Optional instruction body \s+ ((?: # [$1] [^?]* # Run of anything but a question mark | # -or- \?(?!>) # question mark not followed by a closing angle bracket )*) )? \?> }x
- END_PI =
%r{ <\? end (?: \s+ example )? \s* \?> }x
Instance Method Summary collapse
-
#highlight(content, lang) ⇒ Object
Highlights the given
content
in languagelang
. -
#parse_options(args) ⇒ Object
Parse an options hash for filtering from the given
args
, which can either be a plain String, in which case it is assumed to be the name of the language the example is in, or a Hash of configuration options. -
#process(source, page) ⇒ Object
Process the given
source
for <?example … -
#process_example(params, body, page) ⇒ Object
Filter out ‘example’ macros, doing syntax highlighting, and running ‘testable’ examples through a validation process appropriate to the language the example is in.
-
#test_content(body, language, page) ⇒ Object
Test the given
content
with a rule specific to the givenlanguage
. -
#test_ruby_content(source, page) ⇒ Object
Test the specified Ruby content for valid syntax.
-
#test_yaml_content(source, metadata) ⇒ Object
Test the specified YAML content for valid syntax.
Instance Method Details
#highlight(content, lang) ⇒ Object
Highlights the given content
in language lang
.
226 227 228 229 |
# File 'lib/strelka/cms/pagefilter/example.rb', line 226 def highlight( content, lang ) source = ERB::Util.html_escape( content ) return %Q{\n\n<pre><code class="#{lang}">#{source}</code></pre>\n\n} end |
#parse_options(args) ⇒ Object
Parse an options hash for filtering from the given args
, which can either be a plain String, in which case it is assumed to be the name of the language the example is in, or a Hash of configuration options.
156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/strelka/cms/pagefilter/example.rb', line 156 def ( args ) args = "{ #{args} }" unless args && args.strip[0] == ?{ args = YAML.load( args ) # Convert to Symbol keys and value args.keys.each do |k| newval = args.delete( k ) next if newval.nil? || (newval.respond_to?(:size) && newval.size == 0) args[ k.to_sym ] = newval.respond_to?( :to_sym ) ? newval.to_sym : newval end return DEFAULTS.merge( args ) end |
#process(source, page) ⇒ Object
Process the given source
for <?example … ?> processing-instructions, calling out
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/strelka/cms/pagefilter/example.rb', line 68 def process( source, page ) scanner = StringScanner.new( source ) buffer = '' until scanner.eos? startpos = scanner.pos # If we find an example if scanner.skip_until( EXAMPLE_PI ) contents = '' # Append the interstitial content to the buffer if ( scanner.pos - startpos > scanner.matched.length ) offset = scanner.pos - scanner.matched.length - 1 buffer << scanner.string[ startpos..offset ] end # Append everything up to it to the buffer and save the contents of # the tag params = scanner[1] # Now find the end of the example or complain contentpos = scanner.pos scanner.skip_until( END_PI ) or raise "Unterminated example at line %d" % [ scanner.string[0..scanner.pos].count("\n") ] # Now build the example and append to the buffer if ( scanner.pos - contentpos > scanner.matched.length ) offset = scanner.pos - scanner.matched.length - 1 contents = scanner.string[ contentpos..offset ] end self.log.debug "Processing with params: %p, contents: %p" % [ params, contents ] buffer << self.process_example( params, contents, page ) else break end end buffer << scanner.rest scanner.terminate return buffer end |
#process_example(params, body, page) ⇒ Object
Filter out ‘example’ macros, doing syntax highlighting, and running ‘testable’ examples through a validation process appropriate to the language the example is in.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/strelka/cms/pagefilter/example.rb', line 118 def process_example( params, body, page ) = self.( params ) = .delete( :caption ) content = '' lang = .delete( :language ).to_s self.log.debug "Processing a %p example..." % [ lang ] # Test it if it's testable if [:testable] content = test_content( body, lang, page ) else content = body end # If the language is something that can itself include PIs, look for the # special '<??end ?>' token and strip the extra '?' if %w[xml html textile xhtml].include?( lang ) content.gsub!( /<\?\?(.*?)\?>/ ) do tag_content = $1 self.log.debug "Unescaping escaped PI %p in example." % [ tag_content ] "<?#{tag_content}?>" end self.log.debug " example is now: %p" % [ content ] end # Strip trailing blank lines and syntax-highlight content = highlight( content.strip, lang ) = %{<div class="caption">} + .to_s + %{</div>} if return %{<notextile><div class="example %s-example">%s%s</div></notextile>} % [lang, content, || ''] end |
#test_content(body, language, page) ⇒ Object
Test the given content
with a rule specific to the given language
.
171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/strelka/cms/pagefilter/example.rb', line 171 def test_content( body, language, page ) self.log.debug "Running a testable %p example..." % [ language ] case language.to_sym when :ruby return self.test_ruby_content( body, page ) when :yaml return self.test_yaml_content( body, page ) else self.log.error "...oops, I don't know how to test %p examples." % [ language ] return body end end |
#test_ruby_content(source, page) ⇒ Object
Test the specified Ruby content for valid syntax
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/strelka/cms/pagefilter/example.rb', line 188 def test_ruby_content( source, page ) # $stderr.puts "Testing ruby content..." libdir = Pathname.new( __FILE__ ).dirname.parent.parent.parent + 'lib' extdir = Pathname.new( __FILE__ ).dirname.parent.parent.parent + 'ext' = Rcodetools::XMPFilter::INITIALIZE_OPTS.dup [:include_paths] |= [ libdir.to_s, extdir.to_s ] [:width] = 80 if page.config['example_prelude'] prelude = page.config['example_prelude'] self.log.debug " prepending prelude:\n#{prelude}" source = prelude.strip + "\n\n" + source.strip else self.log.debug " no prelude; page config is: %p" % [ page.config ] end rval = Rcodetools::XMPFilter.run( source, ) self.log.debug "test output: %p" % [ rval ] return rval.join rescue Exception => err return "%s while testing: %s\n %s" % [ err.class.name, err., err.backtrace.join("\n ") ] end |
#test_yaml_content(source, metadata) ⇒ Object
Test the specified YAML content for valid syntax
216 217 218 219 220 221 222 |
# File 'lib/strelka/cms/pagefilter/example.rb', line 216 def test_yaml_content( source, ) YAML.load( source ) rescue YAML::Error => err return "# Invalid YAML: " + err. + "\n" + source else return source end |