engineering 0.2 → 0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +11 -0
- data/Gemfile +6 -2
- data/Rakefile +2 -0
- data/engineering.gemspec +8 -6
- data/lib/builder/extrusion.rb +189 -0
- data/lib/builder/model.rb +120 -0
- data/lib/builder/sketch.rb +119 -0
- data/lib/engineering.rb +28 -54
- data/lib/model/dsl.rb +26 -0
- data/lib/sketchup.rb +25 -5
- data/test/builder/extrusion.rb +143 -0
- data/test/builder/model.rb +191 -0
- data/test/builder/sketch.rb +161 -0
- data/test/engineering.rb +95 -30
- data/test/fixtures/sketchup/sketch_group_group.su +4 -0
- data/test/fixtures/sketchup/sketch_group_group_group.su +4 -0
- data/test/sketchup/builder.rb +43 -9
- metadata +42 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 77f2d000480b81c235ca318065fa10b03ae24c53
|
4
|
+
data.tar.gz: 7079726edaaa3a2c4861f91170a2522b298a0882
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 27e226858c03ed4213db6d169386886f6492aec85c8ca60d9d9ad70d0148391af909741443359ef3481ba816e9cc36c4a1c3dae21030e3e68126fe022b839fe1
|
7
|
+
data.tar.gz: dd254bd2ede34af0ab21f9635c3e4c92b4e1ac32be27423736f401641df720bad14484e3a4dd4857d20a4a369e23da3876897a77568cbab3ad0e5743e6d8452a
|
data/.travis.yml
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
language: ruby
|
2
|
+
rvm:
|
3
|
+
- 2.0.0
|
4
|
+
deploy:
|
5
|
+
provider: rubygems
|
6
|
+
api_key:
|
7
|
+
secure: VCPQ9R1RYDhW68CT/P84RvNFbMzI3PsU8K0dFThhr5D2QcpRZTZTW6OeQIpToMdpvFny6Dpu7xa9I+wNFEuMYGIXXlLNirPkhNpu8iBCgarP1QoW173sNNhT8gCjhJrY2QmWFMjH//smMSZHfoJ0cO06/7246/9qK9171C8Lo0E=
|
8
|
+
gem: engineering
|
9
|
+
on:
|
10
|
+
tags: true
|
11
|
+
repo: bfoz/engineering
|
data/Gemfile
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
source "http://rubygems.org"
|
2
2
|
|
3
|
-
# Specify your gem's dependencies in engineering.gemspec
|
4
3
|
gemspec
|
5
4
|
|
6
5
|
gem 'model', github: 'bfoz/model'
|
7
|
-
gem '
|
6
|
+
gem 'sketch', github: 'bfoz/sketch'
|
7
|
+
gem 'units', github: 'bfoz/units-ruby'
|
8
|
+
|
9
|
+
group :test do
|
10
|
+
gem 'rake'
|
11
|
+
end
|
data/Rakefile
CHANGED
data/engineering.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "engineering"
|
6
|
-
s.version = '0.
|
6
|
+
s.version = '0.3'
|
7
7
|
s.authors = ["Brandon Fosdick"]
|
8
8
|
s.email = ["bfoz@bfoz.net"]
|
9
9
|
s.homepage = "http://github.com/bfoz/engineering"
|
@@ -17,9 +17,11 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
18
|
s.require_paths = ["lib"]
|
19
19
|
|
20
|
-
s.add_dependency 'dxf', '~> 0.
|
21
|
-
s.add_dependency 'geometry', '~> 6.
|
22
|
-
s.add_dependency 'model', '~> 0.
|
23
|
-
s.add_dependency 'sketch', '~> 0.
|
24
|
-
s.add_dependency 'units', '~> 2.
|
20
|
+
s.add_dependency 'dxf', '~> 0.3'
|
21
|
+
s.add_dependency 'geometry', '~> 6.4'
|
22
|
+
s.add_dependency 'model', '~> 0.2'
|
23
|
+
s.add_dependency 'sketch', '~> 0.4'
|
24
|
+
s.add_dependency 'units', '~> 2.4'
|
25
|
+
|
26
|
+
s.required_ruby_version = '>= 2.0'
|
25
27
|
end
|
@@ -0,0 +1,189 @@
|
|
1
|
+
require 'model/extrusion'
|
2
|
+
|
3
|
+
require_relative 'sketch'
|
4
|
+
|
5
|
+
module Engineering
|
6
|
+
module Builder
|
7
|
+
# Build an {Extrusion} subclass
|
8
|
+
class Extrusion
|
9
|
+
include ::Sketch::DSL
|
10
|
+
|
11
|
+
# Convenience method for creating a new builder and evaluating a block
|
12
|
+
def self.build(&block)
|
13
|
+
self.new.build(&block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@attribute_defaults = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
# Evaluate a block in the context of an {Extrusion} and a {Skecth}
|
21
|
+
# Use the trick found here http://www.dan-manges.com/blog/ruby-dsls-instance-eval-with-delegation
|
22
|
+
# to allow the DSL block to call methods in the enclosing *lexical* scope
|
23
|
+
def build(&block)
|
24
|
+
@klass = Class.new(::Model::Extrusion)
|
25
|
+
@sketch_klass = Class.new(::Sketch)
|
26
|
+
|
27
|
+
@klass.singleton_class.send :attr_accessor, :sketch
|
28
|
+
@klass.instance_variable_set('@sketch', @sketch_klass)
|
29
|
+
|
30
|
+
if block_given?
|
31
|
+
# So that #push has something to append to
|
32
|
+
@sketch_klass.singleton_class.send :attr_accessor, :elements
|
33
|
+
@sketch_klass.instance_variable_set('@elements', [])
|
34
|
+
|
35
|
+
@self_before_instance_eval = block.binding.eval('self')
|
36
|
+
self.instance_eval(&block)
|
37
|
+
|
38
|
+
# Instance variable values for read-only attributes need special handling
|
39
|
+
setter_defaults = @attribute_defaults.select {|k,v| @sketch_klass.respond_to? k.to_s + '=' } # Find the ones that can be set normally
|
40
|
+
instance_variable_defaults = @attribute_defaults.reject {|k,v| @sketch_klass.respond_to? k.to_s + '=' } # These must be set directly
|
41
|
+
|
42
|
+
# The new Sketch subclass needs an initializer too
|
43
|
+
@sketch_klass.send :define_method, :initialize do |*args, &block|
|
44
|
+
# Directly set the read-only instance variables
|
45
|
+
instance_variable_defaults.each {|k,v| instance_variable_set('@' + k.to_s, v) }
|
46
|
+
|
47
|
+
super(*args, &block)
|
48
|
+
|
49
|
+
# Push the default geometry
|
50
|
+
self.class.instance_variable_get(:@elements).each do |a|
|
51
|
+
if a.is_a? Array
|
52
|
+
push a.first.new(*a.last)
|
53
|
+
else
|
54
|
+
push a
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
@klass.send :define_method, :initialize do |options={}, &block|
|
60
|
+
raise ArgumentError, "Can't initialize with a length when #{self} already has a length attribute" if self.class.length and options.key?(:length)
|
61
|
+
raise ArgumentError, "Can't initialize with a Sketch when #{self} already has a Sketch attribute" if self.class.sketch and options.key?(:sketch)
|
62
|
+
|
63
|
+
# Sketch doesn't support any Transformation options
|
64
|
+
sketch_options = options.reject {|k,v| [:angle, :origin, :translate, :x, :y].include? k }
|
65
|
+
# More things that Sketch can't handle
|
66
|
+
sketch_options.reject! {|k,v| [:length, :sketch].include? k }
|
67
|
+
|
68
|
+
# Evaluate any blocks in the passed arguments and dupe the options
|
69
|
+
# hash as a side effect so that the caller's hash isn't mutated
|
70
|
+
options = (options.map {|k,v| { k => (v.respond_to?(:call) ? v.call : v) } }).reduce(:merge) || {}
|
71
|
+
|
72
|
+
# Create a new instance of the Sketch subclass
|
73
|
+
options[:sketch] = self.class.sketch.new(setter_defaults.merge(sketch_options)) if self.class.sketch
|
74
|
+
options[:length] = self.class.length if self.class.length
|
75
|
+
|
76
|
+
super options
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
@klass.singleton_class.send :attr_accessor, :length
|
81
|
+
@klass.instance_variable_set('@length', @length)
|
82
|
+
|
83
|
+
@klass
|
84
|
+
end
|
85
|
+
|
86
|
+
# The second half of the instance_eval delegation trick mentioned at
|
87
|
+
# http://www.dan-manges.com/blog/ruby-dsls-instance-eval-with-delegation
|
88
|
+
def method_missing(method, *args, &block)
|
89
|
+
if @klass.respond_to? method
|
90
|
+
@klass.send method, *args, &block
|
91
|
+
elsif @sketch_klass.respond_to? method
|
92
|
+
@sketch_klass.send method, *args, &block
|
93
|
+
else
|
94
|
+
@self_before_instance_eval.send method, *args, &block
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# @group DSL support methods
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
# Set the length attribute of the {Extrusion}
|
103
|
+
# @param length [Number] the new length
|
104
|
+
def length(length=nil)
|
105
|
+
@length = length if length
|
106
|
+
@length
|
107
|
+
end
|
108
|
+
|
109
|
+
# Create a {Group} with an optional transformation
|
110
|
+
def build_group(*args, &block)
|
111
|
+
[Builder::Sketch.new.build(::Sketch::Group, &block), args]
|
112
|
+
end
|
113
|
+
|
114
|
+
# Create a {Layout}
|
115
|
+
# @param direction [Symbol] The layout direction (either :horizontal or :vertical)
|
116
|
+
# @option options [Symbol] alignment :top, :bottom, :left, or :right
|
117
|
+
# @option options [Number] spacing The spacing between each element
|
118
|
+
def build_layout(direction, alignment, spacing, *args, &block)
|
119
|
+
[Builder::Sketch.new.build(::Sketch::Layout, &block), args]
|
120
|
+
end
|
121
|
+
|
122
|
+
# Use the given block to build a {Polyline}
|
123
|
+
def build_polyline(**options, &block)
|
124
|
+
::Sketch::Builder::Polyline.new(**options).evaluate(&block)
|
125
|
+
end
|
126
|
+
|
127
|
+
# Build a {Polygon} from a block
|
128
|
+
# @return [Polygon]
|
129
|
+
def build_polygon(**options, &block)
|
130
|
+
::Sketch::Builder::Polygon.new(**options).evaluate(&block)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Define an attribute with the given name and optional default value (or block)
|
134
|
+
# @param name [String] The attribute's name
|
135
|
+
# @param value An optional default value
|
136
|
+
def define_attribute_reader(name, value=nil, &block)
|
137
|
+
name, value = name.flatten if name.is_a?(Hash)
|
138
|
+
name = name.to_sym
|
139
|
+
|
140
|
+
# Class accessor that forwards to the Sketch
|
141
|
+
@klass.class_eval "class << self; def #{name}; sketch.#{name}; end; end"
|
142
|
+
|
143
|
+
# Instance accessor that forwards to the Sketch
|
144
|
+
@klass.send :define_method, name.to_sym do
|
145
|
+
sketch.send name
|
146
|
+
end
|
147
|
+
|
148
|
+
# Instance accessor on the new Sketch
|
149
|
+
@sketch_klass.send :attr_reader, name
|
150
|
+
|
151
|
+
if value || block_given?
|
152
|
+
# Class accessor on the new Sketch subclass
|
153
|
+
@sketch_klass.singleton_class.send :attr_reader, name
|
154
|
+
|
155
|
+
# Set the ivar on the Sketch subclass
|
156
|
+
@sketch_klass.instance_variable_set('@' + name.to_s, value || instance_eval(&block))
|
157
|
+
@attribute_defaults[name] = value || block
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Define an attribute with the given name
|
162
|
+
# @param name [String,Symbol] the name of the attribute
|
163
|
+
def define_attribute_writer(name)
|
164
|
+
method_name = name.to_s + '='
|
165
|
+
|
166
|
+
# Class accessor that forwards to the Sketch
|
167
|
+
@klass.class_eval "class << self; def #{method_name}(value); sketch.#{method_name} value; end; end"
|
168
|
+
|
169
|
+
# Instance accessor that forwards to the Sketch
|
170
|
+
@klass.send :define_method, method_name.to_sym do |value|
|
171
|
+
sketch.send method_name, value
|
172
|
+
end
|
173
|
+
|
174
|
+
# Instance accessor on the new Sketch
|
175
|
+
@sketch_klass.send :attr_writer, name.to_sym
|
176
|
+
end
|
177
|
+
|
178
|
+
# Append a new object (with optional transformation) to the {Sketch}
|
179
|
+
def push(element, *args)
|
180
|
+
if element.is_a? Class
|
181
|
+
@sketch_klass.instance_variable_get(:@elements).push [element, args]
|
182
|
+
else
|
183
|
+
@sketch_klass.instance_variable_get(:@elements).push element
|
184
|
+
end
|
185
|
+
end
|
186
|
+
# @endgroup
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'model'
|
2
|
+
|
3
|
+
require_relative '../model/dsl'
|
4
|
+
require_relative 'extrusion'
|
5
|
+
|
6
|
+
module Engineering
|
7
|
+
module Builder
|
8
|
+
# Build a {Model} subclass
|
9
|
+
class Model
|
10
|
+
include ::Model::DSL
|
11
|
+
|
12
|
+
# Convenience method for creating a new builder and evaluating a block
|
13
|
+
def self.build(&block)
|
14
|
+
self.new.build(&block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize
|
18
|
+
@attribute_defaults = {}
|
19
|
+
end
|
20
|
+
|
21
|
+
# Evaluate a block and return a new {Model} subclass
|
22
|
+
# Use the trick found here http://www.dan-manges.com/blog/ruby-dsls-instance-eval-with-delegation
|
23
|
+
# to allow the DSL block to call methods in the enclosing *lexical* scope
|
24
|
+
def build(super_class=::Model, &block)
|
25
|
+
@klass = Class.new(super_class)
|
26
|
+
if block_given?
|
27
|
+
@klass.singleton_class.send :attr_reader, :elements
|
28
|
+
@klass.instance_variable_set(:@elements, [])
|
29
|
+
|
30
|
+
@self_before_instance_eval = block.binding.eval('self')
|
31
|
+
self.instance_eval(&block)
|
32
|
+
|
33
|
+
# Instance variable values for read-only attributes need special handling
|
34
|
+
options = @attribute_defaults.select {|k,v| @klass.respond_to? k.to_s + '=' } # Find the ones that can be set normally
|
35
|
+
instance_variable_defaults = @attribute_defaults.reject {|k,v| @klass.respond_to? k.to_s + '=' } # These must be set directly
|
36
|
+
|
37
|
+
@klass.send :define_method, :initialize do |*args, &block|
|
38
|
+
# Directly set the read-only instance variables
|
39
|
+
instance_variable_defaults.each {|k,v| instance_variable_set('@' + k.to_s, v) }
|
40
|
+
|
41
|
+
# Handle the others normally, while evaluating any blocks
|
42
|
+
super(*(options.map {|k,v| { k => (v.respond_to?(:call) ? v.call : v) } }), *args, &block)
|
43
|
+
|
44
|
+
# Push the default geometry
|
45
|
+
self.class.instance_variable_get(:@elements).each do |a|
|
46
|
+
if a.is_a? Array
|
47
|
+
push a.first.new(*a.last)
|
48
|
+
else
|
49
|
+
push a
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
@klass
|
55
|
+
end
|
56
|
+
|
57
|
+
# The second half of the instance_eval delegation trick mentioned at
|
58
|
+
# http://www.dan-manges.com/blog/ruby-dsls-instance-eval-with-delegation
|
59
|
+
def method_missing(method, *args, &block)
|
60
|
+
if @klass.respond_to? method
|
61
|
+
@klass.send method, *args, &block
|
62
|
+
else
|
63
|
+
@self_before_instance_eval.send method, *args, &block
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# @group DSL support methods
|
68
|
+
private
|
69
|
+
|
70
|
+
# Build a new {Extrusion} subclass
|
71
|
+
# @param length [Number] the length of the extrusion
|
72
|
+
# @param sketch [Sketch] a {Sketch} subclass to extrude (or nil)
|
73
|
+
# @param parent [Object] a parent context to use while building
|
74
|
+
# @param options [Hash] anything that needs to be passed to the new {Extrusion} instance
|
75
|
+
def build_extrusion(length, sketch, parent, options={}, &block)
|
76
|
+
[Builder::Extrusion.build(&block), [options.merge(length:length)]]
|
77
|
+
end
|
78
|
+
|
79
|
+
# Build a new {Group} subclass
|
80
|
+
def build_group(*args, &block)
|
81
|
+
[self.class.new.build(::Model::Group, &block), args]
|
82
|
+
end
|
83
|
+
|
84
|
+
# Define an attribute with the given name and optional default value (or block)
|
85
|
+
# @param name [String] The attribute's name
|
86
|
+
# @param value An optional default value
|
87
|
+
def define_attribute_reader(name, value=nil, &block)
|
88
|
+
name, value = name.flatten if name.is_a?(Hash)
|
89
|
+
ivar_name = '@' + name.to_s
|
90
|
+
name = name.to_sym
|
91
|
+
|
92
|
+
# Class accessor
|
93
|
+
@klass.singleton_class.send :attr_reader, name
|
94
|
+
|
95
|
+
@klass.send :attr_reader, name.to_sym # Instance accessor
|
96
|
+
if value || block_given?
|
97
|
+
@klass.instance_variable_set(ivar_name, value || instance_eval(&block))
|
98
|
+
@attribute_defaults[name] = value || block
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Define an attribute with the given name
|
103
|
+
# @param name [String,Symbol] the name of the attribute
|
104
|
+
def define_attribute_writer(name)
|
105
|
+
@klass.send :attr_writer, name.to_sym # Instance accessor
|
106
|
+
end
|
107
|
+
|
108
|
+
def push(element, *args)
|
109
|
+
if element.is_a? Class
|
110
|
+
@klass.instance_variable_get(:@elements).push [element, args]
|
111
|
+
elsif element.is_a?(Array) and element.first.is_a?(Class)
|
112
|
+
@klass.instance_variable_get(:@elements).push element
|
113
|
+
else
|
114
|
+
raise ArgumentError, "Can't push instances while building a class"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
# @endgroup
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'sketch'
|
2
|
+
|
3
|
+
module Engineering
|
4
|
+
module Builder
|
5
|
+
class Sketch
|
6
|
+
include ::Sketch::DSL
|
7
|
+
|
8
|
+
# Convenience method for creating a new builder and evaluating a block
|
9
|
+
def self.build(&block)
|
10
|
+
self.new.build(&block)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@attribute_defaults = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# Evaluate a block and return a new {Model} subclass
|
18
|
+
# Use the trick found here http://www.dan-manges.com/blog/ruby-dsls-instance-eval-with-delegation
|
19
|
+
# to allow the DSL block to call methods in the enclosing *lexical* scope
|
20
|
+
def build(super_class=::Sketch, &block)
|
21
|
+
@klass = Class.new(super_class)
|
22
|
+
if block_given?
|
23
|
+
@klass.singleton_class.send :attr_accessor, :elements
|
24
|
+
@klass.instance_variable_set('@elements', [])
|
25
|
+
|
26
|
+
@self_before_instance_eval = block.binding.eval('self')
|
27
|
+
self.instance_eval(&block)
|
28
|
+
|
29
|
+
# Instance variable values for read-only attributes need special handling
|
30
|
+
setter_defaults = @attribute_defaults.select {|k,v| @klass.respond_to? k.to_s + '=' } # Find the ones that can be set normally
|
31
|
+
instance_variable_defaults = @attribute_defaults.reject {|k,v| @klass.respond_to? k.to_s + '=' } # These must be set directly
|
32
|
+
|
33
|
+
@klass.send :define_method, :initialize do |*args, &block|
|
34
|
+
# Directly set the read-only instance variables
|
35
|
+
instance_variable_defaults.each {|k,v| instance_variable_set('@' + k.to_s, v) }
|
36
|
+
|
37
|
+
super(setter_defaults, *args, &block)
|
38
|
+
|
39
|
+
# Push the default geometry
|
40
|
+
self.class.instance_variable_get(:@elements).each do |a|
|
41
|
+
if a.is_a? Array
|
42
|
+
push a.first.new(*a.last)
|
43
|
+
else
|
44
|
+
push a
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
@klass
|
50
|
+
end
|
51
|
+
|
52
|
+
# The second half of the instance_eval delegation trick mentioned at
|
53
|
+
# http://www.dan-manges.com/blog/ruby-dsls-instance-eval-with-delegation
|
54
|
+
def method_missing(method, *args, &block)
|
55
|
+
@self_before_instance_eval.send method, *args, &block
|
56
|
+
end
|
57
|
+
|
58
|
+
# @group DSL support methods
|
59
|
+
private
|
60
|
+
|
61
|
+
# Create a {Group} with an optional transformation
|
62
|
+
def build_group(*args, &block)
|
63
|
+
[self.class.new.build(::Sketch::Group, &block), args]
|
64
|
+
end
|
65
|
+
|
66
|
+
# Create a {Layout}
|
67
|
+
# @param direction [Symbol] The layout direction (either :horizontal or :vertical)
|
68
|
+
# @option options [Symbol] alignment :top, :bottom, :left, or :right
|
69
|
+
# @option options [Number] spacing The spacing between each element
|
70
|
+
def build_layout(direction, alignment, spacing, *args, &block)
|
71
|
+
[Builder::Sketch.new.build(::Sketch::Layout, &block), args]
|
72
|
+
end
|
73
|
+
|
74
|
+
# Use the given block to build a {Polyline}
|
75
|
+
def build_polyline(**options, &block)
|
76
|
+
::Sketch::Builder::Polyline.new(**options).evaluate(&block)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Build a {Polygon} from a block
|
80
|
+
# @return [Polygon]
|
81
|
+
def build_polygon(**options, &block)
|
82
|
+
::Sketch::Builder::Polygon.new(**options).evaluate(&block)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Define an attribute with the given name and optional default value (or block)
|
86
|
+
# @param name [String] The attribute's name
|
87
|
+
# @param value An optional default value
|
88
|
+
def define_attribute_reader(name, value=nil, &block)
|
89
|
+
name, value = name.flatten if name.is_a?(Hash)
|
90
|
+
ivar_name = '@' + name.to_s
|
91
|
+
name = name.to_sym
|
92
|
+
|
93
|
+
# Class accessor
|
94
|
+
@klass.class_eval "class << self; attr_reader :#{name}; end"
|
95
|
+
|
96
|
+
@klass.send :attr_reader, name.to_sym # Instance accessor
|
97
|
+
if value || block_given?
|
98
|
+
@klass.instance_variable_set(ivar_name, value || instance_eval(&block))
|
99
|
+
@attribute_defaults[name] = value || block
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Define an attribute with the given name
|
104
|
+
# @param name [String,Symbol] the name of the attribute
|
105
|
+
def define_attribute_writer(name)
|
106
|
+
@klass.send :attr_writer, name.to_sym # Instance accessor
|
107
|
+
end
|
108
|
+
|
109
|
+
def push(element, *args)
|
110
|
+
if element.is_a? Class
|
111
|
+
@klass.instance_variable_get(:@elements).push [element, args]
|
112
|
+
else
|
113
|
+
@klass.instance_variable_get(:@elements).push element
|
114
|
+
end
|
115
|
+
end
|
116
|
+
# @endgroup
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|