Skip to content
Adam Venturella edited this page Oct 28, 2011 · 15 revisions

Configuration

The Basics

Your CrazyHorse application is configured by creating a JSON file at the root of your application called crazyhorse.config

The crazyhorse.config file is composed of sections:

  • Your application specific section
  • The CrazyHorse specific section
  • Any number of custom sections you would like to define.

Focusing on the first 2 for a moment, a stub of a config would look like this:

{
	"application"
	{
		...
	},
	"crazyhorse":
	{
		...
	}
}

Lets start by looking at the application specific section.

Application Configuration

Your application configuration within the crazyhorse.config is composed of 3 sub-sections:

  • system
  • settings
  • custom_errors

The system section is required, settings and custom_errors are optional.

{
	"application"
	{
		"route_digest":true/false
		"system":{…},
		"settings":{…},
		"custom_errors":[…],
		"authorization_providers":[]
	},
	"crazyhorse":
	{
		...
	}
}

route_digest

As noted in the routing section, if you would like CrazyHorse to build a JSON reflection of your defined routes, you specify this here. route_digest is not a required key in the application section of the configuration.

system

The system portion of the application configuration is where you define your default_view handler and the path to your controllers

{
	"application":
	{
		"system":
		{
			"default_view":"myapp.views.MyViewHandler",
			"controllers":["myapp.controllers", "myapp.controllers.admin"]
		}
	}
}

So, what do these 2 options give us?

default_view

If you have looked at the routing section already, you may recall a method call in one of our actions:

return self.view("homepage", model)

If you have not viewed that section yet, we set up a route for our homepage that looks like this:

	@route(name = "welcome",
            path = "/")
	def hello_world(self):
		model = {"message":"Hello World"}
		return self.view("homepage", model)

If this is your first time reviewing this documentation, you may be confused by the @route decorator, not to worry you can learn more about CrazyHorse routing by reading the routing section.

All that we care about right now is that last line in the method:

return self.view("homepage", model)

Whatever you define as default_view in the crazyhorse.config file is what will be initialized for you when you call self.view().

"default_view":"myapp.views.MyViewHandler"

You can therefore configure any kind of view system you like. Any arguments you pass into view will be handed off to the __init__ of your view handler. There are 3 rules for creating a view handler:

  1. Your view handler MUST extend CrazyHorseResult
  2. Your view handler class MUST be callable. In other words your view handler needs to define __call__(self) this method MUST return the output you of your view, e.g. html, json, xml, etc.
  3. Your view handler MUST expose a content_type property whose value is a valid mime type.

Below is a sample view handler using Jinja2. You don't have to use Jinja2, you are welcome to use whatever meets your needs, it is provided here for example purposes only.

from crazyhorse.web.results import CrazyHorseResult
from jinja2 import Environment, FileSystemLoader

jinja2 = Environment(loader=FileSystemLoader(['/Path/to/your/app/views']))

class Jinja2View(CrazyHorseResult):

def __init__(self, name, model):
    self.model        = model;
    self.name         = name

@property
def content_type(self):
    return "text/html; charset=utf-8"

def __call__(self):
	template = jinja2.get_template(self.name + ".html")
	return template.render(self.model)

Please be aware, CrazyHorse DOES NOT provide a default view system. It is left as an exercise to the developer to choose their view engine of choice and implement a view handler to their liking that follows the afore mentioned 3 rules.

controllers

The controllers setting within our application system configuration is an array of the packages that contain the controllers for your application.

"controllers":["myapp.controllers", "myapp.controllers.admin"]

When CrazyHorse starts up, it will scan these locations for your routes, you can learn more about routing in the routing section.

settings

The settings section of the application configuration is where your can set things like API keys, or SQL credentials, or some other setting of your choosing.

{
	"application":
	{
		"settings":
		{
			"twitterApiKey":"12345",
			"twitterApiSecret":"67890",
            "sqlServerUsername":"test",
            "sqlServerPassword":"pass",
			"numbers": [1,2,3,4],
			"aDict":{"foo":"bar"}
		}
	}
}

You can access any of these settings from within your application in the following way:

from crazyhorse.configuration.manager import Configuration

Configuration.APP_SETTINGS["twitterApiKey"]
Configuration.APP_SETTINGS["twitterApiSecret"]
Configuration.APP_SETTINGS["sqlServerUsername"]
Configuration.APP_SETTINGS["numbers"][0]

custom_errors

The custom_errors section of the application is where you can define the array of your 404 and 500 error handling. Be advised, at this time, not all 500 type errors may get fed into this handler.

{
	"application":
	{
		"custom_errors":
		[
			{"code":404, "method":"*", "controller":"myapp.controllers.errors.ServerErrorController", "action":"error_404"},
		    {"code":500, "method":"*", "controller":"myapp.controllers.errors.ServerErrorController", "action":"error_500"}
		]
	}
}

For all intents and purposes these definitions are effectively routes that will get called when a clients requests a url that is not currently present in your router, or a catchable error is raised. You must set the following 4 options:

  • code the http response code that will handle this error. As of this writing 404 and 500 are supported
  • method the http method for this error. You scan define separate error handlers for GET, POST, PUT etc. You can also set the method to * which will handle all methods.
  • controller the controller that will be initialized for this handler
  • action the method/action on the controller to be called.

authorization_providers

The authorization_providers section of the application is where you can define your your authorization actions for your CrazyHorse application.

"authorization_providers":[
{"name":"default",
"provider":"myapp.security.authorization.DefaultAuthorizationProvider" },
{"name":"other",
"provider":"myapp.security.authorization.OtherAuthorizationProvider" }
	]

Here we have defined two authorization providers, yes, you may define more than one if needed. An authorization provider requires the following attributes:

  • name the name of your provider. If you only have 1 authorization provider, you can leave the name blank and a name of "default" will be assumed. The name of the provider is important as you will see, it is how you can assign a specific authorization provider to one of your actions. In addition, it is also the name you will use to define your error handler should authorization fail.

  • provider the fully qualified name of the class that will be handling your authorization. This class is required to contain 1 method:

    def is_authorized(self, httpcontext): pass

This method must return true or false.

Adding a custom error handler

As noted in the Execution Path, if authorization fails, that is if is_authorized returns false, CrazyHorse will attempt to execute a custom error handler for you. So how does CrazyHorse know what to execute? Lets look back at our custom_errors section for the answer.

First lets assume we have defined an authorization handler that looks like this:

{"name":"default",
"provider":"myapp.security.authorization.DefaultAuthorizationProvider" }

If we would like an opportunity to handle this, rather than just have CrazyHorse return a 403 Forbidden, we would need to add the following to our custom_errors config section:

{"code":"authorization.default", "method":"*", "controller":"myapp.controllers.errors.AuthErrorController", "action":"auth_failed_default"}

Here, both controller and action can be anything you like, the choice of names here is for example only. The only important thing to note is the code attribute.

Taking a closer look, we can see that code here is not a number, but a string authorization.default.

The prefix "authorization." is required. It's how we know by looking at the config that this error handler is used for authorization purposes.

The remainder of the code, default corresponds directly to the name of our authorization provider. Had we named our authorization provider foo:

{"name":"foo",
"provider":"myapp.security.authorization.DefaultAuthorizationProvider" }

Then our custom error handler would need to be:

{"code":"authorization.foo", "method":"*", "controller":"myapp.controllers.errors.AuthErrorController", "action":"auth_failed_default"}

Note that the code attribute here is authorization.foo. So you can see how the name attribute of our authorization provider directly corresponds to the code attribute of our custom error handler.

To learn more about applying authorization to your routes check out the Authorization docs.

Summary

Putting it all together, your application configuration section of your crazyhorse.config would look like this, based on the examples above

{
	"application"
	{
		"route_digest":true,
		"system":
		{
			"default_view":"myapp.views.MyViewHandler",
			"controllers":["myapp.controllers", "myapp.controllers.admin"]
		},
		"settings":
		{
			"twitterApiKey":"12345",
			"twitterApiSecret":"67890",
            "sqlServerUsername":"test",
            "sqlServerPassword":"pass",
			"numbers": [1,2,3,4],
			"aDict":{"foo":"bar"}
		},
		"custom_errors":
		[
			{"code":404, "method":"*", "controller":"myapp.controllers.errors.ServerErrorController", "action":"error_404"},
		    {"code":500, "method":"*", "controller":"myapp.controllers.errors.ServerErrorController", "action":"error_500"}
		],
		"authorization_providers":
		[ 
			{"name":"default", "provider":"myapp.security.authorization.DefaultAuthorizationProvider" }
		]
	},
	"crazyhorse":
	{
		...
	}
}

Next we will go over the crazyhorse section of the crazyhorse.config file.

CrazyHorse Configuration

The crazy horse configuration section currently supports 1 property, features.

"crazyhorse":
{
	"features": {…}
}

Within the features object, however, you have some choices you can make.

features

If you choose to leave this section empty, all you will get from CrazyHorse if the router. The router only would be perfectly fine if your goal was to serve up pages that would never be POSTed to, use query strings, sessions or cookies. This should tell you something about what you can do with features.

As of this writing the available features are:

  • request_body (parsing for POST/PUT, etc)
  • querystrings
  • cookies
  • sessions

CrazyHorse currently ships with default implementations for these features. Should you desire, you can always replace CrazyHorses features with your own. a feature is simply defined as the fully qualified name to a callable. Given the corresponding features above, the following would wire you up to use the default CrazyHorse implementations: "crazyhorse": { "features": { "request_body" : "crazyhorse.features.requests.forms.feature_forms", "querystrings" : "crazyhorse.features.querystrings.feature_querystrings", "cookies" : "crazyhorse.features.cookies.feature_cookies", "sessions" : "crazyhorse.features.sessions.feature_sessions" } }

Again, these are just the paths to a callable. You can replace these with your own versions should you need something special. For example, the default request_body features will deal with parsing application/x-www-form-urlencoded and multipart/form-data. Perhaps your application needs to deal with JSON being submitted instead, this is where you could write your own implementation if needed. See the docs on custom features for more information.

Custom Configuration Sections

Aside from the application and crazyhorse sections, CrazyHorse also allows you to define any number of custom configuration sections.

{
	"application"
	{
		… 
	},
	"crazyhorse":
	{
		… 
	}
	"my-section": {…}
}

Custom configuration sections can contain any information you like. If present in the crazyhorse.config CrazyHorse will had that decoded JSON object to your custom configuration handler for you to process.

Custom Configuration Handlers

The handlers for you custom configurations sections are also defined in the crazyhorse.config. In the example above, we just defined a custom section, at this point however, nothing would happen, as we have not defined any handlers for those sections. Building on the above example, our configuration would look like this if we would like to actually enable those custom sections:

{
	"application"
	{
		… 
	},
	"crazyhorse":
	{
		… 
	}
	"custom_sections":
	[{"name":"my-section", "type":"myapp.configuration.sections.MySection", "notes":"Will be available as CUSTOM_MY_SECTION"}],
	
	"my-section": {…}
}

Note that we have added a "custom_sections" collection into our config. This collection is filled with objects containing the following properties:

  • name
  • type
  • notes

Of the 3 properties outlined above, name and type are required, notes is completely optional.

name

The name property tells CrazyHorse what section in the crazyhorse.config it should look for. Your name property and the name of your custom section MUST be the same.

type

The type property tells CrazyHorse what handler to initialize to process your custom section.

notes

The notes property is only available for documentation purposes. It will not be handed off to any of your custom configuration handlers.

You are not required to have have a corresponding section of JSON to use a custom configuration handler. If you define a handler in the custom_sections collection and no corresponding JSON for that section, your custom handler will still be initialized and assigned into Configuration object. If you do add some JSON for your custom config section, that decoded JSON object will be passed to your handler's __call__(self, section)

To access your custom configuration sections at runtime is as simple as accessing the Configuration object.

In the sample above, we named our custom configuration section my-section CrazyHorse will initialize our custom handler, pass in the JSON object if present, and the assign our custom handler to the Configuration object, which we can then access within our application:

from crazyhorse.configuration.manager import Configuration
Configuration.CUSTOM_MY_SECTION.some_ivar
Configuration.CUSTOM_MY_SECTION.other_ivar

Note how the - are converted to _. Also note that our section name gets prefixed with CUSTOM_

If we had named our section lucy then we would be able to access it in the following way:

from crazyhorse.configuration.manager import Configuration
Configuration.CUSTOM_LUCY.some_ivar
Configuration.CUSTOM_LUCY.other_ivar

If you would like more information on creating custom configuration sections, see the *custom configuration section portion of this documentation. A full sample will be provided.

Clone this wiki locally