The chameleon Reference Manual

Table of Contents

Next: , Previous: , Up: (dir)   [Contents][Index]

The chameleon Reference Manual

This is the chameleon Reference Manual, version 1.0.1, generated automatically by Declt version 3.0 "Montgomery Scott" on Sun May 15 03:31:16 2022 GMT+0.


Next: , Previous: , Up: Top   [Contents][Index]

1 Introduction

* Chameleon

** Introduction

Chameleon is a configuration management library shipped with profile support. It can help you:

1. Define configuration items with default value.
2. Define profiles reflecting different scenarios, e.g. development, testing, production etc.
3. Switch between several profiles just like a chameleon switching its colors!
4. Access configuration item via functions (instead of bare-bone keywords or strings) to avoid invalid names.
5. Extend its behavior by providing ~:before~, ~:around~ or ~:after~ methods.

*Compatibility Note*

- Since version 2.0, Chameleon no longer defines ~active-profile~ or ~profiles~. Use ~*profile*~ as the "current" profile and ~*config*~ holds the "current" configuration instance. Also, there will be no default ~NIL~ profile. You cannot access any configuration value without first defining a profile.

- Since version 1.2, Chameleon no longer exports generated symbols (including those configuration accessors) by default. This is to avoid compiler complaints when a configuration item is removed. Make it explicit!

** Installation

#+BEGIN_SRC lisp
  ;; Install Chameleon.
  (ql:quickload :chameleon)
  
  ;; Test if it works on your machine.
  (asdf:test-system :chameleon)
#+END_SRC

** Usage

The main entry points of Chameleon are 2 macros: ~defconfig~ and ~defprofile~, which defines the configuration set schema and profiles.

*** Defining a Configuration Set with ~defconfig~

The following example demonstrates a real-world scenario taken from my [[https://github.com/sheepduke/silver-brain][Silver Brain]] repository.

#+BEGIN_SRC lisp
  (defpackage config
    (:use #:cl)
    (:export #:*profile*
             #:switch-profile
             #:server-port))
  
  (in-package config)
  
  (defconfig
    (data-dir)
    (log-level :info)
    (server-port 5000 "The port of running server.")
  #+END_SRC

The code above defines a configuration set with 3 items. Each item briefly follows the pattern of ~defvar~, i.e. ~(name [initial-value [documentation]])~:

- ~name~ is a symbol not evaluated.
- ~initial-value~ is a form and always evaluated. Unlike ~defvar~, it is evaluated during every macro expansion.
- ~documentation~ is a string, not evaluated.

It will generate:

- A variable ~*profile*~ indicating current profile name. Defaults to ~NIL~.
- A class ~config~ containing configuration items as slots.
- A variable ~*config*~ indicating current configuration instance. Defaults to ~NIL~.
- A generic function ~switch-profile~ that is used to switch the profile.
- 3 zero-arity access functions ~data-dir~, ~log-leve~ and ~server-port~ and their ~setf~ version.

You need to manually export these symbols in order to use them outside the current package.

**Note**

The access functions will check the value of configuration item. If the value is a function, i.e. ~functionp~ returns ~T~, it will be invoked and the value is returned. Otherwise, the value is directly returned. It is useful in scenarios where you want to dynamically compute the value of a configuration item.

*** Defining Some Profiles with ~defprofile~

A profile consists of values for each configuration item. If an item is missing, the default value will be used.

Profiles are isolated. Switching to a profile does not *modify* anything, it just sets the "current profile" to it. Also changing values defined in one profile does not affect other profiles.

Given the ~defconfig~ code above, we may write:

#+BEGIN_SRC lisp
  ;; Define a profile with default values.
  (defprofile :default)
  
  ;; Define a profile with name :DEV.
  (defprofile :dev
    (server-port 5001)
    (data-dir (truename "~/temp/silver-brain/"))
    (log-level (lambda ()
                 (print "Evaluated on every access")
                 (print "Definitely useless for simply T")
                 :debug))))
  
  ;; Port equals to the default value, i.e. 5000.
  (defprofile :prod
    (data-dir (truename "~/.silver-brain/")))
  
  ;; When running integration tests, the port is randomly picked.
  ;; 
  ;; Macro EVAL-ONCE is no more than a let-over-lambda that caches the
  ;; evaluation result. The function FIND-PORT:FIND-PORT is invoked only
  ;; once.
  (defprofile :test
    (server-port (eval-once (find-port:find-port))))
#+END_SRC

#+BEGIN_SRC lisp
  (defpackage config-user
    (:use #:cl))
  
  (in-package config-user)
  
  ;; Set profile to :DEFAULT.
  (config:switch-profile :default)
  
  (config:server-port) ; => 5000 (13 bits, #x1388)
  #+END_SRC

*** Generated Helper Functions/Macros

These helper functions and/or macros will be generated into the caller package. The following example assumes that these has been exported.

#+BEGIN_SRC lisp
  ;; Temporally set profile to :DEFAULT and evaluate body.
  (config:with-profile :default
    (server-port)) ; => 5000 (13 bits, #x1388)
#+END_SRC

*** Extending Behavior with ~defmethod~

The ~switch-profile~ generated by ~defconfig~ is a generic function. Each ~defprofile~ generates a implementation method that sets ~*profile*~ and ~*config*~. You may implement your own method to extend its behavior.

#+BEGIN_SRC lisp
  (in-package config-user)
  
  (defmethod switch-profile :after (profile)
    "Reset the log level of log4cl."
    (log4cl:configure (log-level)))
#+END_SRC

Then, every time you call ~switch-profile~ to change the current profile, this method is called *after* the profile is set, thus the log4cl get reconfigured by picking up value ~log-level~ defined in target profile.


Next: , Previous: , Up: Top   [Contents][Index]

2 Systems

The main system appears first, followed by any subsystem dependency.


Previous: , Up: Systems   [Contents][Index]

2.1 chameleon

Author

YUE Daian

License

MIT

Description

Configuration management facilities for Common Lisp with multiple profile support.

Version

1.0.1

Dependency

alexandria

Source

chameleon.asd (file)

Component

src (module)


Next: , Previous: , Up: Top   [Contents][Index]

3 Modules

Modules are listed depth-first from the system components tree.


Previous: , Up: Modules   [Contents][Index]

3.1 chameleon/src

Parent

chameleon (system)

Location

src/

Component

chameleon.lisp (file)


Next: , Previous: , Up: Top   [Contents][Index]

4 Files

Files are sorted by type and then listed depth-first from the systems components trees.


Previous: , Up: Files   [Contents][Index]

4.1 Lisp


Next: , Previous: , Up: Lisp files   [Contents][Index]

4.1.1 chameleon.asd

Location

chameleon.asd

Systems

chameleon (system)


Previous: , Up: Lisp files   [Contents][Index]

4.1.2 chameleon/src/chameleon.lisp

Parent

src (module)

Location

src/chameleon.lisp

Packages

chameleon

Exported Definitions
Internal Definitions

Next: , Previous: , Up: Top   [Contents][Index]

5 Packages

Packages are listed by definition order.


Previous: , Up: Packages   [Contents][Index]

5.1 chameleon

Source

chameleon.lisp (file)

Use List

common-lisp

Exported Definitions
Internal Definitions

Next: , Previous: , Up: Top   [Contents][Index]

6 Definitions

Definitions are sorted by export status, category, package, and then by lexicographic order.


Next: , Previous: , Up: Definitions   [Contents][Index]

6.1 Exported definitions


Previous: , Up: Exported definitions   [Contents][Index]

6.1.1 Macros

Macro: defconfig &body CONFIGS

Defines a configuration. The body CONFIGS contains multiple items, with each following this pattern:
(name initial-value &optional docstring)

While calling this macro, all the given names, initial-values and docstrings are evaluated during macro expansion.

Beneath the surface, DEFCONFIG actually generates the following stuff:

1. A variable *PROFILE*. It stores the current profile which is desired to be a keyword.

2. A variable *CONFIG*. It stores the current configuration instance.

3. A class named CONFIG. Each item maps to a slot definition:
- name maps to slot name.
- initial-value maps to :initform argument.
- docstring maps to :documentation property of the slot.

4. A zero-arity inline function (and its setf version) for each name. The function returns configuration value of current profile. When the value itself is a function, it is called every time and the value is returned. Otherwise, the value is directly returned.

5. Some other helper functions and macros:
- with-profile

A typical example is:
(defconfig
(server-port 5001 "The server port.")
(app-dir "/tmp"))

Package

chameleon

Source

chameleon.lisp (file)

Macro: defprofile NAME &body CONFIGS

Defines a profile with given NAME. CONFIGS is one or more lists, with each following this pattern: (name value).

The evaluation rule of value follows DEFCONFIG.

It generates a variable *CONFIG-<NAME>* and a method SWITCH-PROFILE.

You may provide :before, :around or :after methods to SWITCH-PROFILE to insert some code.

Package

chameleon

Source

chameleon.lisp (file)

Macro: eval-once &body BODY

Defines a closure to evaluate BODY for only once.

Package

chameleon

Source

chameleon.lisp (file)


Previous: , Up: Definitions   [Contents][Index]

6.2 Internal definitions


Next: , Previous: , Up: Internal definitions   [Contents][Index]

6.2.1 Functions

Function: config-item-to-slot ITEM

Convert given ITEM to CLOS slot. ITEM should be the a 2 or 3 length pair in config definition.

Package

chameleon

Source

chameleon.lisp (file)

Function: eval-config-var PROFILE PACKAGE
Package

chameleon

Source

chameleon.lisp (file)

Function: make-config-var-name NAME
Package

chameleon

Source

chameleon.lisp (file)

Function: make-keyword THING

Make a keyword out of given string THING. The keyword is guaranteed to be upper case.

Package

chameleon

Source

chameleon.lisp (file)


Previous: , Up: Internal definitions   [Contents][Index]

6.2.2 Conditions

Condition: null-profile-error ()
Package

chameleon

Source

chameleon.lisp (file)

Direct superclasses

error (condition)


Previous: , Up: Top   [Contents][Index]

Appendix A Indexes


Next: , Previous: , Up: Indexes   [Contents][Index]

A.1 Concepts

Jump to:   C   F   L   M  
Index Entry  Section

C
chameleon.asd: The chameleon․asd file
chameleon/src: The chameleon/src module
chameleon/src/chameleon.lisp: The chameleon/src/chameleon․lisp file

F
File, Lisp, chameleon.asd: The chameleon․asd file
File, Lisp, chameleon/src/chameleon.lisp: The chameleon/src/chameleon․lisp file

L
Lisp File, chameleon.asd: The chameleon․asd file
Lisp File, chameleon/src/chameleon.lisp: The chameleon/src/chameleon․lisp file

M
Module, chameleon/src: The chameleon/src module

Jump to:   C   F   L   M  

Next: , Previous: , Up: Indexes   [Contents][Index]

A.2 Functions

Jump to:   C   D   E   F   M  
Index Entry  Section

C
config-item-to-slot: Internal functions

D
defconfig: Exported macros
defprofile: Exported macros

E
eval-config-var: Internal functions
eval-once: Exported macros

F
Function, config-item-to-slot: Internal functions
Function, eval-config-var: Internal functions
Function, make-config-var-name: Internal functions
Function, make-keyword: Internal functions

M
Macro, defconfig: Exported macros
Macro, defprofile: Exported macros
Macro, eval-once: Exported macros
make-config-var-name: Internal functions
make-keyword: Internal functions

Jump to:   C   D   E   F   M  

Next: , Previous: , Up: Indexes   [Contents][Index]

A.3 Variables


Previous: , Up: Indexes   [Contents][Index]

A.4 Data types

Jump to:   C   N   P   S  
Index Entry  Section

C
chameleon: The chameleon system
chameleon: The chameleon package
Condition, null-profile-error: Internal conditions

N
null-profile-error: Internal conditions

P
Package, chameleon: The chameleon package

S
System, chameleon: The chameleon system

Jump to:   C   N   P   S