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 code*: # # 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) # # *pry output*: # # [1] pry(main)> class Network < ZTK::DSL::Base # [1] pry(main)* has_many :servers # [1] pry(main)* # [1] pry(main)* attribute :name # [1] pry(main)* attribute :gw # [1] pry(main)* attribute :network # [1] pry(main)* attribute :netmask # [1] pry(main)* end # => #<Proc:0x0000000121f498@/home/zpatten/Dropbox/code/chef-repo/vendor/checkouts/ztk/lib/ztk/dsl/core/attributes.rb:45 (lambda)> # [2] pry(main)> class Server < ZTK::DSL::Base # [2] pry(main)* belongs_to :network # [2] pry(main)* # [2] pry(main)* attribute :name # [2] pry(main)* end # => #<Proc:0x00000001983108@/home/zpatten/Dropbox/code/chef-repo/vendor/checkouts/ztk/lib/ztk/dsl/core/attributes.rb:45 (lambda)> # [3] pry(main)> Network.new do # [3] pry(main)* id :leet_net # [3] pry(main)* name "leet-net" # [3] pry(main)* gw "7.3.3.1" # [3] pry(main)* network "7.3.3.0" # [3] pry(main)* netmask "255.255.255.0" # [3] pry(main)* # [3] pry(main)* server do # [3] pry(main)* name "leet-server" # [3] pry(main)* end # [3] pry(main)* # [3] pry(main)* server do # [3] pry(main)* id :my_server # [3] pry(main)* name "my-server" # [3] pry(main)* end # [3] pry(main)* # [3] pry(main)* server do # [3] pry(main)* name "dev-server" # [3] pry(main)* end # [3] pry(main)* end # => #<Network id=:leet_net attributes={:id=>:leet_net, :name=>"leet-net", :gw=>"7.3.3.1", :network=>"7.3.3.0", :netmask=>"255.255.255.0"}, has_many_references=1> # [4] pry(main)> Network.count # => 1 # [5] pry(main)> Network.all # => [#<Network id=:leet_net attributes={:id=>:leet_net, :name=>"leet-net", :gw=>"7.3.3.1", :network=>"7.3.3.0", :netmask=>"255.255.255.0"}, has_many_references=1>] # [6] pry(main)> Network.find(:leet_net) # => [#<Network id=:leet_net attributes={:id=>:leet_net, :name=>"leet-net", :gw=>"7.3.3.1", :network=>"7.3.3.0", :netmask=>"255.255.255.0"}, has_many_references=1>] # [7] pry(main)> Server.count # => 3 # [8] pry(main)> Server.all # => [#<Server id=2 attributes={:id=>2, :name=>"leet-server", :network_id=>:leet_net}, belongs_to_references=1>, # #<Server id=:my_server attributes={:id=>:my_server, :name=>"my-server", :network_id=>:leet_net}, belongs_to_references=1>, # #<Server id=4 attributes={:id=>4, :name=>"dev-server", :network_id=>:leet_net}, belongs_to_references=1>] # [9] pry(main)> Server.find(:my_server) # => [#<Server id=:my_server attributes={:id=>:my_server, :name=>"my-server", :network_id=>:leet_net}, belongs_to_references=1>] # # @author Zachary Patten <zachary AT jovelabs DOT com> 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 block_given? and ((block.arity < 1) ? instance_eval(&block) : block.call(self)) primary_key_count = self.class.dataset.count do |d| d.id == self.id end raise StandardError, "Primary key '#{self.id}' already exists!" if (primary_key_count > 1) 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 <zachary AT jovelabs DOT com> 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.class.to_s}:#{self.id} #{details.join(', ')}>" end end end end