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.



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

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


59
60
61
62
63
64
65
# File 'lib/builder/model.rb', line 59

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



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

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


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
52
53
54
55
# File 'lib/builder/model.rb', line 24

def build(super_class=::Model, &block)
		@klass = Class.new(super_class)
		if block_given?
 @klass.singleton_class.send :attr_reader, :elements
 @klass.instance_variable_set(:@elements, [])

 @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