homeblogfaces actions options and facades oh my

Faces, Actions, Options, and Facades, Oh My!

This follows on from my previous post on Faces and starts to break down the components that make up the Faces system. The fundamental components here are:
  1. Facades – the ways you can interact with your Faces and Actions.
  2. Faces – these represent a coherent subsystem, or modelled object or entity.
  3. Actions – these represent things you can do to the modelled object or entity.
  4. Options – these modify the way that actions work
Over the next few posts I will be breaking down these components and talking about what they mean, how they are used, and where we expect them to go in the future. Please keep in mind, though, that Faces are a very new feature. We love the idea of having a good, accessible API in Ruby, as well as through the command line, and we are really looking forward to see what the creative community around Puppet can do with them. When you are working through the issues, let us know what works, what doesn't, and what we could do better by e-mailing the puppet-users list with "Faces" somewhere in your subject line. We are really looking forward to turning this into something super-useful, but we need to know what makes it useful for you for that to work.

Facades: Ways of Using Faces

Faces, and their Actions, provide a standard Ruby API over our models of the network, and the things you can command Puppet to do. That, alone, would provide a huge amount of value, but it isn't the end of the story. As part of Faces we have developed a Facade: every Face can be accessed, and every action invoked, from the command line. We map the arguments and options for the Face and Action down into command line options you can pass. We see the Facades as one of the greatest strengths of Puppet Faces. They provide a huge amount of functionality for free today, and we expect them to do a whole lot more tomorrow. Adding a Facade that integration with MCollective, or that provides a generic HTTP RPC mechanism, would add huge value without needing to change the individual Faces. The use of Facades also forces our Faces to support introspection: enough that you can write code that reasons about, and influences, the contained code. That also means that we can expect Faces to be able to effectively interact with each other using the same introspection mechanisms—code that manages code is easy, and routine, within Faces.

An Example of Face Introspection

One of the best examples of the power of Face introspection is the puppet help Face. This is just a regular Face that loads up other Faces and Actions, then uses introspection to fill out a documentation template with the Options, Actions, Faces, and other relevant details. The help Face is 124 lines long, but a lot of that is backwards compatibility support for our older model applications, or documentation on the Face itself. (Fun tip: puppet help help help will tell you all about the help Action of the help Face. ;) Once we have dealt with all the legacy support, the actual meat of the function to get access to the face and action (less error handling) is only:
version ||= :current            # a sensible default...
face      = Puppet::Face[facename, version]
action    = face.get_action(actionname) if actionname
Once we have that we pass it off to the appropriate template for displaying global, Face, or Action help. The template uses some basic introspection to build up the help:
% action.options.map(&:get_option).each do |option|
 *  - <%= option.name %> - <%= option.summary %>
% end
In addition to the name and summary we can pull out details like a generated synopsis of the Action or Face, required arguments to each action, and other details required to invoke the system... Stay tuned for the next installment, "About Faces, until we go in the right direction."