Skip to content

Instantly share code, notes, and snippets.

@lamp
Created April 13, 2010 16:10
Show Gist options
  • Save lamp/364776 to your computer and use it in GitHub Desktop.
Save lamp/364776 to your computer and use it in GitHub Desktop.
Jambler
Overall design:
1. Gather a 'master list' of application dependencies via the Jamfile. A Jamfile provides mapping between a dependency name and a client-side dependency file (JS or CSS), with the following metadata:
a version number (optional - multiple versions of a file e.g. multiple jquery versions can be registered with the same dependency name
a path (optional - Jambler will attempt to resolve this using a default naming and path convention if not defined)
a source url (optional - Jambler can attempt to download libraries in-place during jambling)
a browser condition, allowing you to nest dependencies in icky IE conditional comments
The Jamfile:
is parsed using an instance of Jambler::DSL::Jamfile using instance_eval. Jambler will use the DSL instance as the master dependency list.
The Jamfile registers a master list of Jambler::Dependency instances (Jambler::Dependency::JS, Jambler::Dependency::CSS)
2. Allow individual dependency files to announce their dependencies by dependency name (required) and version (optional, may use a comparator mask such as ">= 0.9")
3. Provide a document rendering cycle which caters to both dynamically-generated applications and statically-compiled HTML applications. An individual document render is represented by an instance of Jambler::Document.
For dynamic applications:
The document may announce dependencies at runtime (during the inner template render or any other time before the main layout is rendered)
The document may declare an insertion point for all registered dependencies of a particular type associated with the Jambler::Document instance.
For static applications:
The Jambler build task must be invoked in order to compile the HTML
The document may announce dependencies in a similar manner to dynamic applications by including HTML comments containing the dependency declarations.
The comment structure uses a <!-- /Jambler --> ending block. This is to allow non-destructive in-place compilation of any HTML file.
In all cases:
Jambler accrues per-document dependencies during the render/compilation cycle
Dependency instances are always sourced from the master dependency list so that they can act as stateful objects
When registered dependency instances are read for the first time, they are parsed using Jambler::DSL::JS, Jambler::DSL::CSS, Jambler::DSL::HTML::Comments, Jambler::DSL::HTML::InlineRuby
During parsing, the JS and CSS DSLs again source their dependency objects from the master list as pointered references.
When asked to render the dependencies, Jambler will calculate the dependency graph and possibly raise an exception:
Jambler::Exceptions::CyclicDependencyError - this document appears to be turtles all the way down
Jambler::Exceptions::MissingDependencyError - this document refers to a dependency that has not been registered in the jamfile
Jambler::Exceptions::VersionConflictError - multiple versions of the same file are depended upon by this document
The document uses an instance of Jambler::Resolver which calculates the graph of dependencies and acts as a reduce and sort operation against the master dependency list
Object structure during a document render:
Jambler singleton
Jambler::DSL::Jamfile # master dependency list
#dependencies method
Jambler::Dependency::JS instance
#dependencies method
array of pointers to dependency instances from the Jambler::DSL::Jamfile instance
Jambler::Dependency::CSS instance
#dependencies method
array of pointers to dependency instances from the Jambler::DSL::Jamfile instance
Jambler::Document instance
Jambler::DSL::HTML instance # list of document's dependencies
#dependencies method
array of pointers to dependency instances from the Jambler::DSL::Jamfile instance
Jambler::Resolver instance
#list method, returns a flat list of dependencies for this document
#sort method, returns a topologically-sorted (we may even get away with stdlib's TSort module) list of dependencies
#render method, runs the sorted list through the compilation strategy.
4. Provide compilation strategies to allow for optimisation. Which strategies are available depends on the nature of your app, but there should be a framework for registering new strategies. The registration API allows developers to:
Create subclasses of Jambler::Strategy::Compile which implement a single public-facing method:
run!(document, dependency_type) e.g. run!(@document, :js)
the run! method should return the output to be rendered into the document
Register those subclasses with Jambler:
Jambler::use_strategy(:js, MyApp::JavascriptCompileStrategy)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment