Project

gazebo

0.0
No commit activity in last 3 years
No release in over 3 years
A lightweight MVC framework
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

~> 1.1
~> 10.0

Runtime

~> 0.8.1
~> 0.20.0
>= 2.0.1, ~> 2.0
>= 0.19.1, ~> 0.19
 Project Readme

Gazebo

A light-weight MVC framework inspired by Rails. Check out my beat-making app built to demonstrate using this gem. Github | Live

Installation

gem install gazebo

Project Setup

You will need to create a project directory with the following structure (No gazebo new command yet):

  • /app_name
    • /app
      • /controllers
      • /models
      • /views
    • /config
    • /db
      • /migrations

Add gem gazebo to your Gemfile.

Additionally, at the root of the project you will need a file named config.ru with the following code:

require 'gazebo'

Gazebo.root = File.expand_path(File.dirname(__FILE__))

run Gazebo.app

Migrations

Files in db/migrations/ will be read and executed as raw sql. Specify the order in which the files will be executed by prefixing each filename with a two digit number (01, 02, 03, ...). This is necessary for migrations to work as expected.

gazebo migrate runs any new migrations. Migrations are deemed to be new based on the presence of the filename in a 'migrations' table. At the moment there is no command to rollback migrations. Simply add new ones to reverse the changes you made.

#lib/activeleopard/db_connection.rb

def self.run_migration_file(file_name)
  migration_name = file_name.match(/\w+/).to_s

  return if migration_name.empty? || already_run?(migration_name)

  file = File.join(Gazebo::ROOT, "db/migrations", file_name)
  migration_sql = File.read(file)
  execute(migration_sql)

  record_migration!(migration_name)
end

Models and ActiveLeopard

Model files should be named the singular version of their associated table name. Add model files in app/models/. All model classes need to inherit from ActiveLeopard::Base. Additionally, users need to call ::finalize! at the end of the model class definition.

Validations

Gazebo supports presence and uniqueness validators. The ::validates method can be invoked inside of the class definition, accepting a column_name and an options hash.

Example: validates :name, presence: true, uniqueness: true

Validations are checked for upon saving. If any validations fail, #save will return false. Additionally, the errors will be accessible with the #errors method and can be displayed by #show_errors.

Associations

All association creators use sensible defaults generated based on the model class name when explicit primary keys or class names are not given.

::belongs_to(association_name, options) example: belongs_to :human, foreign_key: :owner_id

::has_many(association_name, options) example: has_many :cats, :foreign_key => :owner_id

has_many_through(association_name, options) example: has_many_through :cats, :humans, :cats

has_one_through(association_name, options)

ActiveLeopard Query Methods

The following query methods are available after inheriting from ActiveLeopard::Base. They return Relation objects and can be chained on to each other. The query that is built is only triggered when the actually query result is needed. Relation objects have access to the Enumerables module.

  • ::all

  • ::joins(association symbol or string)

  • ::select(string)

  • ::group(string)

  • ::limit(number)

  • ::from(string)

  • ::order(string)

  • ::where(string or hash)

#lib/activeleopard/modules/searchable.rb

def where(*params)
  Relation.new(
    {where: WhereClause.new(params)},
    self
  )
end
  • ::distinct

The Relation object holds onto the query parameters inside of the query instance variable. Before executing the query, or when requested by the user for debugging, the Relation object calls #as_sql on each of the clause objects inside of the query hash to construct the sql query.

#lib/activeleopard/relation.rb

def as_sql
  Relation.ordered_clauses.map do |clause|
    query[clause].as_sql
  end.join(" \n ")
end

Rather than returning a relation object, the following methods return the found records as model instance(s).

  • ::first
  • ::last
  • ::find(id)
  • ::find_by(string or hash)

CRUD Methods

  • ::new(params_hash)
  • save ( returns true or false )
  • #valid?
  • #destroy
  • ::destroy_all

Lifecycle Callback Methods

  • ::after_initialize(callback_method) (callback method is invoked in last line of initialize)

Seeding

Add seeds.rb in /db. You will have access to all the model classes you've defined when you run bundle exec gazebo seed.

Routes

Routes can be defined in config/routes.rb. You will need to make this file yourself. Define routes within a block passed to the Gazebo::Router.draw method. You need to write out the path as a regular expression, as the router will use regular expression to match wildcards in the path.

Example:

Gazebo::Router.draw do
  get Regexp.new("^/beats"), BeatsController, :index
  delete Regexp.new("^/beats/(?<id>\\d+)$"), BeatsController, :destroy
end

With the second route, any number after '/beats' will show up in params under a key of 'id'.

Controllers and ActionCondor

Controller file names should be the constantized form of the folder names in '/views'. Otherwise, ActionCondor will not be able to guess which template to render by default.

Session

#session exposes a Session object which provides an interface for setting keys in an application session cookie. This could be used to implement a basic auth pattern (see demo app for example of auth implementation).

CSRF Protection

When ::protect_from_forgery is invoked inside of a controller class definition, any non-get requests will be denied unless they carry the correct authenticity_token. authenticity_token can be sent up in forms by including a hidden input with the value given by #form_authenticity_token.

Rendering and Redirecting

The main purpose of the controller base is to build up a response. You can do this easily with the following methods.

Note: a params hash will be populated from wildcards in the url, or data from request. Use #params to access this information.

render(template_name) (only renders html views at this point)

redirect_to(url) (paths relative to project root work fine)

render_content(content, content_type) (with this you can render json or text/html)

Flash

Use #flash to expose a flash object. Anything set in the flash object using []= will be available for the current and next req/res cycle. Key value pairs set in #flash.now will only be available in the current cycle.

Serving Static Assets

Place any static assets in app/assets/. Most MIME types are supported by the static asset server. You will need to provide the path relative to the root of the project directory. (e.g. app/assets/images/some_image.jpg)

Next Steps

  • RSpec test suite
  • More commands, namely gazebo new
  • Method to eagerly load associated data (i.e. #includes)
  • Have association methods return Association Collection objects that can take query/CRUD methods