Write O'Reilly-style code snippets inside your Asciidoc or markdown files.
Add this line to your application's Gemfile:
gem 'oreilly-snippets'
And then execute:
$ bundle
Or install it yourself as:
$ gem install oreilly-snippets
Send a string with proper snippet code into the process method:
Oreilly::Snippets.process( asciidoc_string )
Check out Code Snippets from O'Reilly.
A snippet looks like this inside your markup.
[filename="coffeetech.js", language="js" identifier="IDENTIFIER"]
snippet~~~~
Put any descriptive text you want here. It will be replaced with the
specified code snippet when you build ebook outputs
snippet~~~~
Then, inside your coffeetech.js
file you have something like this:
// BEGIN IDENTIFIER
var mod = angular.module( 'coffeetech', [] );
mod.factory( 'Github', function() {
return new Github({});
});
// END IDENTIFIER
mod.factory( 'Geo', [ '$window', function( $window ) {
return $window.navigator.geolocation;
} ] );
Putting that snippet into your markdown makes the markdown eventually end up like this:
var mod = angular.module( 'coffeetech', [] );
mod.factory( 'Github', function() {
return new Github({});
});
You can also use syntax like this to pull from a specific commit in your git repository:
[filename="../../github.js.test", language="js", sha="8e05a916fe0b1a9d3e:coffeetech.js"]
This will look in the git repository in the ../../github.js.test
directory, then grab the file at the specific SHA hash. This means you
can write code inside a repository, and add a snippet pointing to that
exact revision in the repository.
Also, you can specify line numbers and use just certain lines within the file retrieved:
[filename="../../github.js.test", language="js", sha="8e05a916fe0b1a9d3e:coffeetech.js, lines="1..5"]
This is equivalent to a range in ruby like [0..4]
. So, we use human indexes, which are converted to zero-based numbering.
If you want to use a placeholder to remind you to put the correct content in later once you have made the correct commit, use "xxx" as the sha hash.
[filename="../../github.js.test", language="js", sha="xxx:coffeetech.js, lines="1..5"]
This will get replaced with PLACEHOLDER TEXT, UPDATE WITH CORRECT SHA HASH
.
You can specify flatten=true
and oreilly-snippets will flatten out
indentation. For example, if you are including a snippet of python
content, you might not want to keep the indentation level as it is in
the file, but display the content "flattened" to the smallest
indentation level.
For example, imagine this content:
def barfoo():
print "barfoo"
if someVar == "someVar"
if anotherVar == "anotherVar"
if thirdVar == "thirdVar"
print( "all of them" )
Then, imagine if you take the snippet from lines 4-6. You probably don't want to display the snippet like this:
if anotherVar == "anotherVar"
if thirdVar == "thirdVar"
print( "all of them" )
You probably want it like this:
if anotherVar == "anotherVar"
if thirdVar == "thirdVar"
print( "all of them" )
If you want the default to be to flatten (avoiding setting it each
snippet declaration), you can set that using the config method: Oreilly::Snippets.config( flatten: true )
At the moment, flattening does not work perfectly for Java files. You can ignore java with Oreilly::Snippets.config( flatten: true, skip_flattening: { java: true } )
NB: This format of snippets is not currently compatible with Atlas from O'Reilly. However, you can always process the snippet and write out a normal Asciidoc file, a file which will be compatible with Atlas. See below for an example using Guard.
If you use callouts, you might run into a situation where you write 10
of them inside a snippet, and then you break that snippet into
two. Your second snippet will have callouts 6-10. If you specify
normcallouts="true"
in your snippet, this module will rewrite those to
start from 1.
One nice workflow is to edit files in your editor, then have guard running to process those files, and open them in the browser so that when you make changes the live-reload plugin will automatically reload the processed HTML.
The file structure I use for this:
- Store my pre-processed files in the /pre directory. These are the files which have the snippets. I need to have a special directory because this means Atlas will ignore them, and guard will process the files into the Asciidoc files stored in the root of the directory, the files which Atlas will process.
- In the directory root I have a bunch of .asciidoc files which are the post-processed files, the files which have the snippets resolved into real code.
- Guard watches both directories (the root and the /pre directory). When I make a change to the files in /pre, Guard regenerates the asciidoc files at the top level. When Guard sees one of those files has been changed, it processes them through Asciidoctor and then builds the HTML. You could combine these into a single watch if you want.
- I serve the HTML file using a combination of pow (pow.cx) and config.ru.
- Use the live-reload plugin for Chrome: https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei
It looks like this:
# Files which have been processed by oreilly-snippets and
# have regulard [source,javascript] style code blocks
chapter1.asciidoc
chapter2.asciidoc
# Files with the exact same name which have the oreilly-snippets
# code includes. These are the files I edit.
/pre/chapter1.asciidoc
/pre/chapter2.asciidoc
# The rendered HTML files. I open these in my browser and the live-reload
# plugin reloads once I edit anything in the /pre directory.
chapter1.asciidoc.html
chapter2.asciidoc.html
Here is the Guardfile
require 'asciidoctor'
require 'oreilly/snippets'
# Add a bit of JS to each file before writing to HTML
init_script = '<script type="text/javascript" src="init.js"></script>';
guard 'shell' do
watch( /^pre\/[^\/]*\.asciidoc$/) {|m|
contents = File.read( m[0] )
snippetized = Oreilly::Snippets.process( contents )
snippet_out = m[0].gsub( "pre/", "" )
File.open( snippet_out, "w+" ) do |f|
f.write snippetized
puts "Wrote new snippet: #{snippet_out}"
end
}
end
guard 'shell' do
watch( /^[^\/]*\.asciidoc$/ ) { |m|
puts "File: #{m.inspect}"
asciidoc = File.read( m[0] )
out = Asciidoctor.render( asciidoc,
:header_footer => true,
:safe => Asciidoctor::SafeMode::SAFE,
:attributes => {'linkcss!' => ''})
File.open( m[0]+ ".html", "w+" ) do |f|
out.gsub!( '</body>', "</body>\n#{init_script}\n" )
f.write out
puts "Wrote: #{m[0]+'.html'}"
end
}
end
guard 'livereload' do
watch(%r{^.+\.(css|js|html)$})
end
My config.ru
app = proc do |env|
Rack::Directory.new('.').call(env)
end
run app
Install the rspec
gem.
$ rspec
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Write tests (see the tests in the spec/ directory)
- Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request