lib/ztk/dsl/base.rb
module ZTK::DSL
# Generic Domain-specific Language Interface
#
# This module allows you to easily add attributes and relationships to classes
# to create a custom DSL in no time.
#
# You can then access these classes in manners similar to what *ActiveRecord*
# provides for relationships. You can easily link classes together; load
# stored objects from Ruby rb files (think Opscode Chef DSL).
#
# I intend the interface to act like ActiveRecord for the programmer and a
# nice DSL for the end user. It's not meant to be a database; more like
# a soft dataset in memory; extremely fast but highly volitale. As always
# you can never have your cake and eat it too.
#
# You specify the "schema" in the classes itself; there is no data storage at
# this time, but I do plan to add support for loading/saving *datasets* to
# disk. Keep in mind since you do not specify type constrants in Ruby, one
# can assign any object to an attribute.
#
# At this time if you do not specify an ID; one is auto generated.
#
# If you wish to create objects in a nested fashion the outer most object must
# be started using the class name initializer. Once inside the block you can
# start using the relationship names and do not need to call any further
# class initializers.
#
# You can also instantiate classes separately and associate them after the
# fact. That is not shown in this example.
#
# @example Building infrastructure DSL objects
#
# class Network < ZTK::DSL::Base
# has_many :servers
#
# attribute :name
# attribute :gw
# attribute :network
# attribute :netmask
# end
#
# class Server < ZTK::DSL::Base
# belongs_to :network
#
# attribute :name
# end
#
# Network.new do
# id :leet_net
# name "leet-net"
# gw "7.3.3.1"
# network "7.3.3.0"
# netmask "255.255.255.0"
#
# server do
# name "leet-server"
# end
#
# server do
# id :my_server
# name "my-server"
# end
#
# server do
# name "dev-server"
# end
# end
#
# Network.count
# Network.all
# Network.find(:leet_net)
#
# Server.count
# Server.all
# Server.find(:my_server)
#
# @author Zachary Patten <zpatten AT jovelabs DOT io>
class Base
include(ZTK::DSL::Core)
# @api private
def self.inherited(base)
base.send(:extend, ZTK::DSL::Base::ClassMethods)
base.instance_eval do
attribute :id
end
end
# @api private
def self.included(base)
# NOOP
end
# @api private
def self.extended(base)
# NOOP
end
def initialize(id=nil, &block)
self.id = (id || self.class.next_id)
self.class.dataset << self
do_block(&block)
end
def do_block(&block)
if block_given?
if (block.arity < 1)
instance_eval(&block)
else
block.call(self)
end
end
if (self.class.dataset.count{ |data| (data.id == self.id) } > 1)
raise StandardError, "Primary key '#{self.id}' already exists!"
end
end
# Instance Inspect
#
# Inspect the DSL object's instance, returning a concise string
# representation of the instance.
#
# @return [String] A concise string representation of the instance.
def inspect
details = Array.new
details << "attributes=#{attributes.inspect}" if attributes.count > 0
details << "has_many_references=#{@has_many_references.count}" if @has_many_references
details << "belongs_to_references=#{@belongs_to_references.count}" if @belongs_to_references
"#<#{self.class.to_s} id=#{self.id.inspect} #{details.join(', ')}>"
end
# @author Zachary Patten <zpatten AT jovelabs DOT io>
module ClassMethods
# Class Inspect
#
# Inspect the DSL object's class, returning a concise string
# representation of the class.
#
# @return [String] A concise string representation of the class.
def inspect
details = Array.new
details << "count=#{self.all.count}" if self.all.count > 0
"#<#{self.to_s} #{details.join(', ')}>"
end
end
end
end