Brick Layer

The concept behind Brick Layer is to have a site manager that includes CMS as a feature. It should be easy to plug into any Rails 3.1 application. It provides the bare minimum support to get up and rolling quickly with a basic CRUD data management system that easily outputs JSON as an alternative format by default.

Features

  • CRUD and RESTful DataSet Support

    • If you just need to have a system of any kind of data that outputs to json

  • Search Support

    • Searching across a certain set of data or all data sets should be easy

  • Routing Support

    • Easily tie a route to a certain data set endpoint

  • Auto UI Generation

    • Allow the dataset definitions to generate your user interface for you

  • User Authentication

    • Basic User Authentication Setup

  • Custom Config API

    • Easily ping the app to get JSON format delivery of existing Routes and other configuration

Requirements

  • Right now this requires Mongo which currently provides the maximum flexibility in what the use cases are.

Installation

Add the gem to your Rails 3.1 application Gemfile and that’s it!

gem 'brick_layer'

Also in your ‘app/assets/stylesheets’ directory add:

//= require brick_layer

Associated Gems

This gem can work with other code that can server as clients.

Data Sets

To define a data set:

# You can name your data set whatever you want, but you probably want to try and end it with “DataSet” as a convention

class ShipDataSet < BrickLayer::DataSet
  field :name,  type: String
  field :size,   type: String
end

This will include the timestamps and text search abilities (if you need them) by default. After creating a data set or a few, it should be accessible via:

http://<host>:<port>/brick_layer/data-sets/ship_data_sets

OR EVEN BETTER

http://<host>:<port>/brick_layer/data-sets/ship_data_sets.json

If you want to make a method accessible in the data set json you can just:

class ShipDataSet < BrickLayer::DataSet
  field :name,   type: String
  field :size,   type: String

  expose_data :my_method

  def my_method(options={})
    "result from my method"
  end
end

This will make the method’s result available in the json in the format:

{ my_method: "result from method" }

The options variable is there to support changing up the data if you want for some reason. The default html view on a page data set will pass { :html => true } as an option to the exposed data method. So you can format the display of exposed data methods if you want.

class ShipDataSet < BrickLayer::DataSet
  field :name,   type: String
  field :size,   type: String

  expose_data :my_method

  def my_method(options={})
    if options[:html]
      "something that is readable in an html view"
    else
      "some other format for json display"
    end
  end
end

CRUD and Auto Form Builder

There is a built in auto-crud system with simple styles to handle quickly adding data to the database.

Brick Layer is using simple form to help support the auto building for HTML5 forms for custom field types. You can find out more about simple form at github.com/plataformatec/simple_form

Using the UI form builder is fairly easy. It’ll automatically read the fields you define on a model and display them correctly.

Let’s say we define a standalone data set like:

class Product < BrickLayer::DataSet
  field :name,          type: String
  field :release_date,  type: DateTime
  field :publish,       type: Boolean
end

Then we head over to:

http://<host>:<port>/brick_layer/data-sets/products

From there will be able to create a new product and the fields will be formatted to match the data types we’ve described here.

Now, I know what you are thinking - what about when I want to have a file field or something similar. That’s easy to:

class Product < BrickLayer::DataSet
  field :name,          type: String
  field :release_date,  type: DateTime
  field :publish,       type: Boolean

  custom_field :description,    :text
  custom_field :website,        :url
  custom_field :phone,          :phone
  custom_field :avatar,         :image # This requires you have imagemagick installed
end

All you have to do is say ‘custom_field_type’ with whatever mapping that is supported by simple form.

File and Image Uploads

Brick Layer is using a combo of dragonfly and carrierwave to allow resizing support of images by passing dimension variables into your data set for custom_fields with the ‘image’ type.

class Employee < BrickLayer::DataSet
  custom_field :avatar, :image, { :sizes => { :small => "50x50>", :large => "400x400#" } }
  custom_field :badge,  :image, { :sizes => { :small => "50x50>", :large => "400x400#" } }

  custom_field :resume, :file
end

The uploaded file will be located at: “/files/”. The uploaded image will be located at: “/media//[image_name]”.

Note this data will then be available in your JSON data load and that the fields “avatar_small” and “avatar_large” will be in different paths.

Enable Supported WYSIWYG for Text Fields

You can pass simple form supported options into your custom field to enable the built in WYSIWYG editor or whatever else you want to pass to the view.

class Product < BrickLayer::DataSet
  custom_field :description, :text, { :input_html => { :class => "withEditor" } }
end

Relationships

Relationships are also supported so you can add those and the fields should automatically show up.

class Owner < BrickLayer::DataSet
  field :name

  has_one :company, autosave: true

  def to_s
    self.name
  end
end

class Company < BrickLayer::DataSet
  field :name

  belongs_to :owner
  has_many   :employees, autosave: true

  def to_s
    self.name
  end
end

class Employee < BrickLayer::DataSet
  belongs_to :company

  field :first_name,        type: String
  field :last_name,         type: String
  field :dob,               type: DateTime
end

The defined “to_s” method will be used when displaying item information in the form fields.

If you choose to use the view navigation outside of just using URLs you can add the standalone classes you want to show in an initializer file.

# config/initializers/brick_layer_init.rb (or whatever you want)

BrickLayer.data_sets_standalones = [:companies, :employees]

This will add these classes to the “Custom Data Sets Link” in the admin navigation and also initialize the navigation link to show all together.

Admin UI Control (ROUTES and PAGES)

The concept behind the admin of brick layer when it comes to pages is that you don’t have to use them unless you want to. In order to get a page (which is basically a route with a data set) requires little work. There is a conventional way of using pages.

First you have to enable page support by adding this configuration to your initializer file:

# config/initializers/brick_layer_init.rb (or whatever you want)

BrickLayer.enable_pages = true

Once that is done you’ll get a pages link that’ll allow you to create new routes and attach data sets to them. So the way this works is that you define your dataset for page just like any other dataset. Except you put it in your models/page_data_sets/directory (this is a convention)

# app/models/page_data_sets/home_page

class PageDataSets::HomePage < BrickLayer::DataSet
  belongs_to :company

  field :welcome_message,   type: String
  field :blurb,             type: String

  custom_field :welcome_message, :text
end

Once you do this and you go to create a route you’ll get the option to select the data set associated with the route. Once you create the route it’ll redirect you to the edit page for that route which will show the custom form fields associated with that page. That’s it!

So all you have to do at this point is create data sets that you want to associate with a page that’ll be made available when you request the page. Don’t forget that you can aggregate more data on a page as well.

For example, let’s say you have a product custom data set already and you want to display the 10 most recent on the homepage. You can do this:

# app/models/page_data_sets/home_page

class PageDataSets::HomePage < BrickLayer::DataSet
  belongs_to :company

  field :welcome_message,   type: String
  field :blurb,             type: String

  custom_field :welcome_message, :text

  expose_data :most_recent_products

  def most_recent_products(options={})
    DummyCompany.order_by(:created_at,"DESC").limit(10)
  end
end

That data will be made available for that page attached to that route!

Also, let’s say that you want to give the administrator control over what the limit to display on that page is. No sweat, you would:

# app/models/page_data_sets/home_page

class PageDataSets::HomePage < BrickLayer::DataSet
  belongs_to :company

  field :welcome_message,   type: String
  field :blurb,             type: String
  field :limit,             type: Integer

  custom_field :welcome_message, :text

  expose_data :most_recent_products

  def most_recent_productss(options={})
    product_limit = 10 || self.limit
    DummyCompany.order_by(:created_at,"DESC").limit(product_limit)
  end
end

You can access this data at:

http://<host>:<port>/brick_layer/your-route-here

AND OF COURSE in JSON!

http://<host>:<port>/brick_layer/your-route-here.json

Authentication for Admin Access

By default there isn’t an administrator set in Brick Layer. Once you create 1 the brick layer admin will automatically lock and you will be able to login using the correct username and password.

Authentication of Content with Token

By default all requests for endpoints using the json format on “index”(list) and “show” actions will be exposed.

However, if you set a token in your “initializers/bricklayer_init.rb” file or similar to:

Bricklayer.token = "your-token-here"

It will force require any access to the brick layer data to have the header “X-AUTH-TOKEN” set in the request for the data to match that token.

To search all data sets you can ping the url with the path to search and pass it params. The search supports most mongo queries.

So you can:

http://<host>:<port>/brick_layer/data-sets/search?title_in="document title"

If you want to search on a specific data set just pass it in as a model param:

http://<host>:<port>/brick_layer/data-sets/search?title_in="document title"&models="ship_data_set"

You can also chain queries together:

http://<host>:<port>/brick_layer/data-sets/search?queries[title_in]="document title"&queries[meta_description_in] = "meta description"

Just add .json to the end of ‘search’ and you get results in json format:

http://<host>:<port>/brick_layer/data-sets/search.json?title_in="document title"