= 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.
- BrickHouse[http://github.com/rmcafee/brick_house]

== 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 https://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[http://github.com/markevans/dragonfly] and carrierwave[http://github.com/jnicklas/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/[filename]". 
The uploaded image will be located at: "/media/[app_generated_path]/[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.

== Admin UI Control (CUSTOM DATA SET LINKS)

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.

== Search

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"