Code Monkey home page Code Monkey logo

oolong's Introduction

oolong

A config-based loader for stuartsierra's component library

Aims:

  • Be really simple to use
  • Be machine manipulable
  • Keep your app's config in one configuration file
  • Be secure (future plans and all that...)

Background

From Wikipedia:

Oolong is a traditional Chinese tea produced through a unique process including withering the plant under the strong sun and oxidation before curling and twisting. ... It can be sweet and fruity with honey aromas, or woody and thick with roasted aromas, or green and fresh with bouquet aromas, all depending on the horticulture and style of production

oolong is a config-based loader for stuartsierra's component library It treats config files through a unique process including withering the amount of code you have to write and providing for automated manipulation of your configuration

oolong's main purpose is to take a descriptor of your application and give you something you can run. It does this by ascribing meaning to a structured configuration (we favour edn stored as a file).

Complex systems cannot run without code being written and we make no attempt to prevent you from plugging in custom code - we only aim to provide a simple means of declaring the structure of your application - how your code fits together.

Usage

project.clj:

Clojars Project

Sample loader code:

(ns myapp.core
  (:require [oolong :refer [brew-master-file start-system]]))
(defn go
  "Given a filename, reads the file as edn and loads the services
   named under the `:app` key", passing in the entire config
  [filename]
  (-> filename brew-master-file start-system))

Sample component code:

(ns myapp.cpt.foo
  (:require [oolong :refer [Lifecycle]]))

; This is the component record itself
(defrecord Foo [a bar]
  ; The Lifecycle protocol is what a component obeys
  Lifecycle

  ; The start method is called when you start the whole system up
  ; Dependencies will be resolved automatically and merged into self
  ; self will thus contain all dependencies and its configuration
  (start [{:keys [a bar] :as self}]
    ; Here 'a' is from the config, 'bar' is a component we need
    ; And you'll almost certainly want to do something more useful here
    ; like connect to a database or something
    (assoc-in self [:a2] a))
    
  ; The stop method is called when you stop the whole system
  ; Here we'd disconnect any resources we've acquired and assoc nil to them
  (stop [self]
    (assoc-in self [:a2] nil)))

(defn cpt
  "Constructor function for Foo record.
  Args: [config] config map
  Returns: new Foo record
  [config]
  (map->Foo config))

Sample configuration (edn):

; `:app` points at a service descriptor. Every entry in the map names:
; - A key in the configuration to restrict scope to
; - A key in the service that names the system or component following
; The `cpt` form is used to load a named component
{:app {:foo (cpt myapp.cpt.foo/cpt :bar)
       :bar (cpt myapp.cpt.bar/cpt)}
 :foo {:a 123}
 :bar {}}

Documentation

Quickstart

There are services and components. Both are structured as records. Services provide a way of grouping components together.

In the general case, services do not need to do anything special to initialise their components so we specify them simply as a map of name to descriptor

{:foo (cpt myapp.cpt.foo/cpt :bar)
 :bar (cpt myapp.cpt.bar/cpt)}

Here the service will have two keys, :foo and :bar, each pointing to a component. We declare a component with a list form. The simplest example of which can be seen for the :bar key, where we name myapp.cpt.bar/cpt. This expects the symbol myapp.cpt.bar/cpt to exist and be a function that takes a single argument (which will be the configuration under the key :bar) and returns a component record.

The component form for :foo specifies a second argument: a dependency descriptor. In this case we use :bar, which declares a dependency on :bar. Also valid are a vector (for multiple dependencies) and a map (to support renaming of keys). Please see the README.md in the component repository for more information about this form.

You can also nest systems like this:

{:foo {:bar (cpt myapp.cpt.bar/cpt)}}

In this case, since we traverse the :foo and :bar keys, the component's config will be found in the path [:foo :bar] in the configuration file, like so:

{:foo {:bar {:is-nested? true}}}

Sometimes you will want systems that depend on other systems. You can do that with the sys form

{:foo (sys {:bar myapp.cpt.bar} :baz)
 :baz myapp.cpt.baz}

Inserting the sys form here means we have introduced another map, so in the above, you will provide configuration that looks like this:

{:foo {:bar {:is-nested? true}}
 :baz {:is-top-level? true}}

Anywhere you can provide a map in your system configuration, you can also just provide a namespace-qualified symbol pointing to a function. This function will be loaded and called with the system configuration attached. If we take this system:

{:foo `myapp.sys.bar}

Then we expect to load configuration from the :foo key in the configuration. We do not negate the configuration-nested effects of using a map to describe a system.

We recommend embedding your service configuration in your main (and only) configuration file.

Further information about how to use @stuartsierra's excellent `component](https://github.com/stuartsierra/component/) library can be found in the README.md of the component repository

API Documentation

Codox docs can be found in the 'doc' directory of this repo or on github: https://github.com/jjl/oolong/tree/master/doc/index.html

They can be regenerated with lein doc

Future

A few things to do:

  • Docs need prettifying
  • Spit out line and column information on error
  • Consider inline testing

License

Copyright © 2015 James Laver

Distributed under the MIT License (see file LICENSE in this directory)

oolong's People

Contributors

jjl avatar

Watchers

James Cloos avatar Sebastian Bensusan avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.