Class: Engineering::Builder::Model

Inherits:
Object
  • Object
show all
Includes:
Model::DSL
Defined in:
lib/builder/model.rb

Overview

Build a Model subclass

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Model::DSL

#attr_accessor, #attr_reader, #attr_writer

Constructor Details

#initializeModel

Returns a new instance of Model.



16
17
18
# File 'lib/builder/model.rb', line 16

def initialize
		@attribute_defaults = {}
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args, &block) ⇒ Object

The second half of the instance_eval delegation trick mentioned at

http://www.dan-manges.com/blog/ruby-dsls-instance-eval-with-delegation


55
56
57
58
59
60
61
# File 'lib/builder/model.rb', line 55

def method_missing(method, *args, &block)
		if @klass.respond_to? method
 @klass.send method, *args, &block
		else
 @self_before_instance_eval.send method, *args, &block
		end
end

Class Method Details

.build(&block) ⇒ Object

Convenience method for creating a new builder and evaluating a block



12
13
14
# File 'lib/builder/model.rb', line 12

def self.build(&block)
		self.new.build(&block)
end

Instance Method Details

#build(super_class = ::Model, &block) ⇒ Object

Evaluate a block and return a new Engineering::Builder::Model subclass

Use the trick found here http://www.dan-manges.com/blog/ruby-dsls-instance-eval-with-delegation
to allow the DSL block to call methods in the enclosing *lexical* scope


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/builder/model.rb', line 23

def build(super_class=::Model, &block)
		@klass = Class.new(super_class)
		if block_given?
 @self_before_instance_eval = block.binding.eval('self')
 self.instance_eval(&block)

 # Instance variable values for read-only attributes need special handling
 options = @attribute_defaults.select {|k,v| @klass.respond_to? k.to_s + '=' } # Find the ones that can be set normally
 instance_variable_defaults = @attribute_defaults.reject {|k,v| @klass.respond_to? k.to_s + '=' }	# These must be set directly

 @klass.send :define_method, :initialize do |*args, &block|
			# Directly set the read-only instance variables
			instance_variable_defaults.each {|k,v| instance_variable_set('@' + k.to_s, v) }

			# Handle the others normally, while evaluating any blocks
			super(*(options.map {|k,v| { k => (v.respond_to?(:call) ? v.call : v) } }), *args, &block)

			# Push the default geometry
			self.class.instance_variable_get(:@elements).each do |a|
  if a.is_a? Array
				push a.first.new(*a.last)
  else
				push a
  end
			end
 end
		end
		@klass
end