Earlier this week I wrote about Oldskool which is a Gem extendable search tool. Today I want to show how to create a plugin for it to query some custom source.
We’ll build a plugin that shows Puppet Type references, you can see how it will look in the image, click for a larger version.
The end result is that I can just search for “type exec” to get the correct documentation for my Puppet install. I’ll go quite quick through all the various bits here, the complete working plugin is in my GitHub.
The nice thing about rendering the type references locally is that you can choose exactly which version to render the docs for and you could possibly also render docs for locally written types that are not part of Puppet – not tried to see how you might render custom types though.
Plugins are made up of a few things that should have predictable names, in the case of our Puppet plugin I decided to call it puppet which means we need a class called Oldskool::PuppetHandler
that does the work for that plugin. You can see the one here and it goes in lib/oldskool/puppet_handler.rb
in your gem:
module Oldskool class PuppetHandler def initialize(params, keyword, config) @params = params @keyword = keyword @config = config self end def plugin_template(template) File.read(File.expand_path("../../../views/#{template}.erb", __FILE__)) end def handle_request(keyword, query) type = Puppetdoc.new(query) menu = [{:title => "Type Reference", :url => "http://docs.puppetlabs.com/references/stable/type.html"}, {:title => "Function Reference", :url => "http://docs.puppetlabs.com/references/stable/function.html"}, {:title => "Language Guide", :url => "http://docs.puppetlabs.com/guides/language_guide.html"}] {:template => plugin_template(:type), :type => type.doc, :topmenu => menu} end end end |
The initialize
and plugin_template
methods will rarely change, the handle_request
is where the magic happens. It gets called with the keyword and the query, so I set this up to respond to searched like type exec
. If you needed any kind of configuration data from the main Oldskool config file you’d just add data to that YAML file and the data would be available in @config
.
The keyword
would be type
and the query
would be exec
. The idea is that we could route for example type
as well as function
keywords into the plugin and then do different things with the query string.
I wrote a class called Puppetdoc
that takes care of the Puppet introspection, I won’t go into the details but you can see it here, it just returns a hash with all the Markdown for each parameter, meta parameter and the type itself.
We then create a simple menu that’s just an array of title and url pairs that will be used to build the top menu that you see in the screenshot.
And finally we just return a hash. The hash that you return must include a template key the rest is optional, I override the meaning of the word template a bit – probably should have chosen a better name:
- If it’s a string it’s assumed the string is a ERB template, Sinatra will just render that
- When it’s the symbol :redirect then your hash must have a :url item in it, this will just redirect the user to another url
- When it’s the symbol :error or just nil you can optionally add a :error key that will be shown to the user
You can see in the code above I passed the menu in as :topmenu you could also pass it back as :sidemenu which will create buttons down the side of the page, you can use both menus and buttons at the same time.
This takes care of creating the data to display but not yet the displaying aspect. The call to plugin_template(:type) will read the contents of the type.erb in the plugins view directory and return the contents. The Oldskool framework will then render that template making your returned hash available in @result
Here’s the first part of the view in question, you can see the whole thing here:
<% unless @error %> <h2><%= @result[:type][:name].to_s.capitalize %> version <%= @result[:type][:version] %></h2> <% end %> |
Your view can check if @error
is set to show some text to the user in the case of exceptions etc otherwise just display the results. You can see here the @result
variable is the data the handle_request
returned.
Finally there’s a convention for gem names – this one would be oldskool-puppet so you should create a similarly named Ruby file to load the various bits, place this in lib/oldskool-puppet.rb
:
require 'oldskool/puppetdoc' require 'oldskool/puppet_handler' |
From there you just need to build the gem, the Rakefile below does that:
require 'rubygems' require 'rake/gempackagetask' spec = Gem::Specification::new do |spec| spec.name = "oldskool-puppet" spec.version = "0.0.3" spec.platform = Gem::Platform::RUBY spec.summary = "oldskool-1assword" spec.description = "description: Generate documentation for Puppet types" spec.files = FileList["lib/**/*.rb", "views/*.erb"] spec.executables = [] spec.require_path = "lib" spec.has_rdoc = false spec.test_files = nil spec.add_dependency 'puppet' spec.add_dependency 'redcarpet' spec.extensions.push(*[]) spec.author = "R.I.Pienaar" spec.email = "rip@devco.net" spec.homepage = "http://devco.net/" end Rake::GemPackageTask.new(spec) do |pkg| pkg.need_zip = false pkg.need_tar = false end |
% rake gem mkdir -p pkg WARNING: no rubyforge_project specified Successfully built RubyGem Name: oldskool-puppet Version: 0.0.3 File: oldskool-puppet-0.0.3.gem mv oldskool-puppet-0.0.3.gem pkg/oldskool-puppet-0.0.3.gem % gem push pkg/oldskool-puppet-0.0.3.gem |
If your gem command is setup this will publish the gem to Github ready for use. In this case all I did was add it to my Gemfile for my webapp:
gem 'puppet', '2.6.9' gem 'facter' gem 'oldskool-puppet', '>= 0.0.3' |
And used bundler to update my site after that everything worked.