Caribou avoids holding any global state and elects rather to store state particular to the application in a configuration map that is owned by the user. This has a number of advantages, the first being that no code is tied to a particular configuration. Configurations can be swapped in and out and Caribou will pick up and run with that configuration as if it had been using it the whole time.
That given, there are a fair number of options and state that Caribou keeps track of and requires to run, so not just any map will work. In the caribou.config
and caribou.app.config
namespace there are a number of functions which facilitate the construction, modification and reading of these configuration maps.
Once you have a configuration map, you can call any Caribou methods inside of a caribou.core/with-caribou
block.
(let [config (pull-config-map-from-somewhere)] (caribou.core/with-caribou config (... )) ;; block of code that assumes a caribou config
As we progress we will illuminate a number of Caribou calls that work in this manner.
Also, in order to access a value that lives inside a Caribou configuration, use caribou.config/draw
:
(caribou.config/draw :models :model :id) ---> The id of the Model model.
In general, we will refer to namespaces inside a Caribou project as {project}.foo
, since we don't know what you named your project. So if you named your project "taiga" and we are talking about the {project}.core
namespace, that means taiga.core
.
Caribou configuration is done by passing in a configuration map to the caribou.core/init
call in the main {project}.boot
namespace. By convention, this map is obtained as a result of calling the caribou.config/config-from-environment
method on a default configuration map obtained from caribou.app.config/default-config
.
(let [default (caribou.app.config/default-config) local (config/merge-config default local-config) config (caribou.config/config-from-environment local)] (caribou.core/init config))
caribou.core/init
sets up all the state that Caribou needs to run and stores it in the config object passed into it. Once a config map has been through caribou.core/init
it is ready to be used for any Caribou related operation that needs to be performed.
caribou.config/config-from-environment
just reads the result of whatever file in resources/config/{environment}.clj
matches the current environment setting and merges that map into the default map you provide. By default the environment is "development", but it can be set as a java option (which can be done in a number of ways). One of the easiest is to set it in your env like so:
% export _JAVA_OPTIONS=-Denvironment=production
This is a standard method for setting JVM options from the command line. (For other methods check the java documentation).
There is also a local-config
map that is defined in boot.clj
. It contains the defaults for every config option that is possible in Caribou, and will be applied to all environments before the config for that environment. This provides a way to set up sane defaults but still be able to modulate the settings on an environment by environment basis.
Even though this is the default method for Caribou configuration, you can configure Caribou in any way that gets a configuration map with the right options into caribou.core/init
in {project}.boot
. Your {project}.core
will call {project}.boot/boot
to obtain this map when setting up the initial handler.
Caribou is highly configurable in a number of ways. Caribou configuration is meant to work out of the box, while still allowing for any changes that might be desired along the way.
There are a variety of options for configuring a Caribou site. Most of these you will not need immediately, but they are documented here for when they do become necessary.
Here is a map of all default configuration options:
{:app {:use-database true :public-dir "public" :default-locale "global" :localize-routes ""} :assets {:dir "app/" :prefix nil :root ""} :aws {:bucket nil :credentials nil} :cljs {:root "resources/cljs" :reload false :options {:output-to "resources/public/js/app/taiga.js" :output-dir "resources/public/js/app/out" :pretty-print true}} :controller {:namespace "taiga.controllers" :reload true} :database {:classname "org.postgresql.Driver" :subprotocol "postgresql" :ssl true :sslfactory "org.postgresql.ssl.NonValidatingFactory"} :error {:show-stacktrace false :catch-exceptions true} :field {:namespace "taiga.fields" :slug-transform [[#"['\"]+" ""] [#"[_ \\/?%:#^\[\]<>@!|$&*+;,.()]+" "-"] [#"^-+|-+$" ""]]} :hooks {:namespace "taiga.hooks"} :index {:path "caribou-index" :default-limit 1000} :logging {:loggers [{:type :stdout :level :debug}]} :nrepl {:port nil} :query {:enable-query-cache false :query-defaults {}} :template {:cache-strategy :never}}
As you can see, there is a whole rainbow of options to choose from. Let's take them one by one.
Here is where we hold the most general configuration level options.
true
.Anything having to do with uploaded files is configured in this map. The available keys in the assets map are:
Information about how to connect to amazon is stored here. Because the configuration can be different for different environments, you could have one amazon bucket or account for one environment, and a different account or bucket for another environment.
{:access-key "YOUR-ACCESS-KEY" :secret-key "YOUR-SECRET-KEY"}
This is where you can trigger clojurescript to build automatically on page load.
The various options for configuring controllers.
{project}.controllers
, which means that any controller namespace you want to reference must start with {project}.controllers.{controller}
. Actions are functions inside your controller namespace, so the index
action inside your home
controller in the taiga
project would be found at taiga.controllers.home/index
.Any and all information for connecting to a database go in this map. Usually the main feature of each environment's config file, it holds a variety of options, some of which are relevant only to certain databases:
Current possible values: postgresql, mysql, h2
localhost
, but in many situations this is a remote server.Here are a couple of examples of database configurations to get you started:
{:database {:classname "org.postgresql.Driver" :subprotocol "postgresql" :host "127.0.0.1" :database "caribou_test" :user "caribou" :password "TUNDRA"}}
{:database {:classname "com.mysql.jdbc.Driver" :subprotocol "mysql" :host "localhost" :database "caribou_test" :user "caribou" :password "TUNDRA"}}
H2 requires a couple more fields to identify that you are using a file based database and to specify the path. (notice :protocol
and :path
are both present, but not :host
)
{:database {:classname "org.h2.Driver" :subprotocol "h2" :protocol "file" :path "./" :database "caribou_development" :user "h2" :password ""}}
When errors occur, these options governs how they are handled.
Hooks are run at specific point during every piece of content's lifecycle. The various hook points are:
:before-save :before-create :after-create :after-save
:before-save :before-update :after-update :after-save
:before-destroy :after-destroy
The index options control how content is indexed in the built in Lucene search engine. This is used in the Admin but you can also use it in your own site. http://lucene.apache.org/
Caribou uses the clucy library to abstract over the raw Java Lucene interface: https://github.com/weavejester/clucy
Logging contains a list of logger specifications under the :loggers key. These specifications are a map containing two keys: :type
and :level
. :type
indicates what endpoint the logger will output to (the default logger writes to :stdout), and :level
indicates what level of Caribou events to pay attention to.
The different types currently supported are :stdout
, :file
and :remote
. :stdout
simply outputs to stdout, and is the default logger type. If :file
is chosen, you must also add a :file
key to the map pointing at the file to log to. If the logger type is :remote
then you must also include a :host
key which indicates what remote host to log to. In the case of a remote host, it uses UDP to send packets to the host, so the host must be running syslog and must be configured to allow access from the server sending the packets.
The levels in order from most critical to least critical are:
:emergency 0 :alert 1 :critical 2 :error 3 :warning 4 :warn 4 :notice 5 :informational 6 :info 6 :debug 7 :trace 7
If you set a logger to watch at :warn
level for instance, it will ignore any event below :warn
, but output all messages from :warn
level up to :emergency
. :emergency
level events are always output.
Nrepl provides a repl running inside the Caribou process that can be connected to from the command line or from inside an editor with nrepl support. This is a great way to interact with a running Caribou process and inspect or alter state using a given configuration.
If a :port
is provided, then an nrepl server will be booted at that port when Caribou is initialized. In that case, a reference to the running server will be stored in the atom under :server
. If no :port
option is present, nrepl will not be booted.
The query
option is the domain of the query cache. Turned off by default, the query cache will cache the results of every query map that caribou.model/gather
sees. There are a variety of entries in this map that play different roles in the inner workings of the query cache.
true
in your config. Not necessary for development, but a good thing to do in production if you know that your content is not necessarily changing often. Even if it does change, the cache will be invalidated on any update to that model, so your site will remain current.caribou.model/gather
. Want to restrict your content to only those "enabled"? This is the place to do it.The various options pertaining to the built-in template rendering live here.
:never
or :always
.