Class: Engineering::Builder::Extrusion

Inherits:
Object
  • Object
show all
Includes:
Sketch::DSL
Defined in:
lib/builder/extrusion.rb

Overview

Build an Extrusion subclass

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeExtrusion

Returns a new instance of Extrusion.



16
17
18
# File 'lib/builder/extrusion.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


88
89
90
91
92
93
94
95
96
# File 'lib/builder/extrusion.rb', line 88

def method_missing(method, *args, &block)
		if @klass.respond_to? method
 @klass.send method, *args, &block
		elsif @sketch_klass.respond_to? method
 @sketch_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/extrusion.rb', line 12

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

Instance Method Details

#build(&block) ⇒ Object

Evaluate a block in the context of an Engineering::Builder::Extrusion and a Skecth

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/builder/extrusion.rb', line 23

def build(&block)
		@klass = Class.new(::Model::Extrusion)
		@sketch_klass = Class.new(::Sketch)

		@klass.singleton_class.send :attr_accessor, :sketch
		@klass.instance_variable_set('@sketch', @sketch_klass)

		if block_given?
 # So that #push has something to append to
 @sketch_klass.singleton_class.send :attr_accessor, :elements
 @sketch_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
 setter_defaults = @attribute_defaults.select {|k,v| @sketch_klass.respond_to? k.to_s + '=' } # Find the ones that can be set normally
 instance_variable_defaults = @attribute_defaults.reject {|k,v| @sketch_klass.respond_to? k.to_s + '=' }	# These must be set directly

 # The new Sketch subclass needs an initializer too
 @sketch_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) }

			super(*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

 @klass.send :define_method, :initialize do |options={}, &block|
			raise ArgumentError, "Can't initialize with a length when #{self} already has a length attribute" if self.class.length and options.key?(:length)
			raise ArgumentError, "Can't initialize with a Sketch when #{self} already has a Sketch attribute" if self.class.sketch and options.key?(:sketch)

			# Sketch doesn't support any Transformation options
			sketch_options = options.reject {|k,v| [:angle, :origin, :translate, :x, :y].include? k }
			# More things that Sketch can't handle
			sketch_options.reject! {|k,v| [:length, :sketch].include? k }

			# Evaluate any blocks in the passed arguments and dupe the options
			#  hash as a side effect so that the caller's hash isn't mutated
			options = (options.map {|k,v| { k => (v.respond_to?(:call) ? v.call : v) } }).reduce(:merge) || {}

			# Create a new instance of the Sketch subclass
			options[:sketch] = self.class.sketch.new(setter_defaults.merge(sketch_options)) if self.class.sketch
			options[:length] = self.class.length if self.class.length

			super options
 end
		end

		@klass.singleton_class.send :attr_accessor, :length
		@klass.instance_variable_set('@length', @length)

		@klass
end