Hooker¶ ↑
Description¶ ↑
Hooker provides a simple registry of hooks or extension points, enabling decoupled run time configuration in terse but straight ruby syntax. Inspiration includes Emacs Hook Functions and other ruby-based configuration files.
Features¶ ↑
-
Hook Procs may be added by the configuration source in any order.
-
Hook Procs may be chained and combined via various conventions: Hooker.apply, Hooker.inject, Hooker.merge.
-
Hook Procs are executed only when applied by the extended application. Thus a single configuration source may include hook Procs that are left un-executed in certain contexts. This is useful when configuring across several different (contextually loaded) modules from one source.
-
Optional or implicit scoping of keys, providing a two level Hook
[:scope, :key]
hierarchy. The default scope is:default
. -
Optional Hooker.log_with extension point
-
Can check and list hooks that were not applied, including the call site of where added, allowing you to test your configurations and avoid typos.
-
Thread safe (though you should strive to complete all configurable bootstrapping in the main thread).
Synopsis¶ ↑
Lets say an application has a ‘Job’ it would like to support configuration hooks on. First arrange for any number of configuration sources to be loaded via Hooker.load_file. Then apply any loaded hook procs to an instance of your Job, like so:
require 'hooker' Hooker.load_file( "config.rb" ) if File.exist?( "config.rb" ) job = Hooker.apply( :job, Job.new )
You could also use Hooker.register_config with an OptionParser to support a -c/--config FILE
flag for specifying the configuration source.
If Job is to be configured via setters, then the configuration source might look like this:
Hooker.with do |h| h.setup_job do |j| j.workers = 3 j.timeout = 10 * 60 #seconds end end
Alternatively, if Job takes a Hash on construction for configuration, use Hooker.merge like so:
opts = Hooker.merge( :job, { workers: 2 } ) #defaults job = Job.new( opts )
…and Hash syntax in the configuration source. Note that unlike JSON or YAML, the configuration remains fully interpreted for greater expressiveness, for example sharing variables across different configured objects:
Hooker.with do |h| cpus = 3 h.setup_job do { workers: cpus + 1, timeout: 10 * 60 } end h.setup_connection_pool do { size: cpus } end end
Hooker can be yielded to the configuration source via an alternative Module.method name, to fully encapsulate its use:
class Chaplain def self.configure( &block ) Hooker.with( :church, &block ) end end
…supports the configuration source:
Chaplain.configure do |c| c.setup_job do |j| j.workers = 3 j.timeout = 10 * 60 #seconds end end
License¶ ↑
Copyright © 2011-2015 David Kellum
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at:
www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.