minilab 1.0.0-mswin32

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.
Files changed (128) hide show
  1. data/.document +2 -0
  2. data/CHANGES +2 -0
  3. data/LICENSE +19 -0
  4. data/README +107 -0
  5. data/Rakefile +145 -0
  6. data/config/environment.rb +15 -0
  7. data/config/objects.yml +22 -0
  8. data/lib/analog_io.rb +30 -0
  9. data/lib/digital_auxport_io.rb +49 -0
  10. data/lib/digital_configuration.rb +66 -0
  11. data/lib/digital_port_io.rb +68 -0
  12. data/lib/extension/extconf.rb +4 -0
  13. data/lib/extension/minilab_hardware.c +235 -0
  14. data/lib/extension/minilab_hardware.so +0 -0
  15. data/lib/library_translator.rb +48 -0
  16. data/lib/minilab.rb +149 -0
  17. data/lib/minilab_context.rb +39 -0
  18. data/lib/result_verifier.rb +14 -0
  19. data/test/integration/analog_input_output_test.rb +43 -0
  20. data/test/integration/connect_to_hardware_test.rb +13 -0
  21. data/test/integration/digital_input_output_test.rb +114 -0
  22. data/test/integration/integration_test.rb +53 -0
  23. data/test/integration/require_minilab_test.rb +9 -0
  24. data/test/system/analog_input.test +3 -0
  25. data/test/system/analog_output.test +37 -0
  26. data/test/system/digital_port_input.test +5 -0
  27. data/test/system/digital_port_output.test +39 -0
  28. data/test/system/digital_port_read_byte.test +26 -0
  29. data/test/system/digital_screw_terminals_input.test +2 -0
  30. data/test/system/digital_screw_terminals_output.test +11 -0
  31. data/test/system/minilab_driver.rb +85 -0
  32. data/test/test_helper.rb +11 -0
  33. data/test/unit/analog_io_test.rb +87 -0
  34. data/test/unit/digital_auxport_io_test.rb +114 -0
  35. data/test/unit/digital_configuration_test.rb +136 -0
  36. data/test/unit/digital_port_io_test.rb +117 -0
  37. data/test/unit/library_translator_test.rb +100 -0
  38. data/test/unit/minilab_context_test.rb +82 -0
  39. data/test/unit/minilab_hardware_test.rb +83 -0
  40. data/test/unit/minilab_test.rb +131 -0
  41. data/test/unit/result_verifier_test.rb +33 -0
  42. data/vendor/behaviors/lib/behaviors.rb +50 -0
  43. data/vendor/behaviors/tasks/behaviors_tasks.rake +140 -0
  44. data/vendor/behaviors/test/behaviors_tasks_test.rb +71 -0
  45. data/vendor/behaviors/test/behaviors_test.rb +50 -0
  46. data/vendor/behaviors/test/tasks_test/Rakefile +16 -0
  47. data/vendor/behaviors/test/tasks_test/doc/behaviors.html +55 -0
  48. data/vendor/behaviors/test/tasks_test/lib/user.rb +2 -0
  49. data/vendor/behaviors/test/tasks_test/test/user_test.rb +17 -0
  50. data/vendor/constructor/Rakefile +44 -0
  51. data/vendor/constructor/config/environment.rb +12 -0
  52. data/vendor/constructor/lib/constructor.rb +132 -0
  53. data/vendor/constructor/test/constructor_test.rb +366 -0
  54. data/vendor/constructor/test/helper.rb +3 -0
  55. data/vendor/diy/README +26 -0
  56. data/vendor/diy/Rakefile +18 -0
  57. data/vendor/diy/lib/constructor.rb +114 -0
  58. data/vendor/diy/lib/diy.rb +329 -0
  59. data/vendor/diy/proto/context.rb +117 -0
  60. data/vendor/diy/proto/context.yml +20 -0
  61. data/vendor/diy/test/diy_test.rb +370 -0
  62. data/vendor/diy/test/files/broken_construction.yml +7 -0
  63. data/vendor/diy/test/files/cat/cat.rb +4 -0
  64. data/vendor/diy/test/files/cat/extra_conflict.yml +5 -0
  65. data/vendor/diy/test/files/cat/heritage.rb +2 -0
  66. data/vendor/diy/test/files/cat/needs_input.yml +3 -0
  67. data/vendor/diy/test/files/cat/the_cat_lineage.rb +1 -0
  68. data/vendor/diy/test/files/dog/dog_model.rb +4 -0
  69. data/vendor/diy/test/files/dog/dog_presenter.rb +4 -0
  70. data/vendor/diy/test/files/dog/dog_view.rb +2 -0
  71. data/vendor/diy/test/files/dog/file_resolver.rb +2 -0
  72. data/vendor/diy/test/files/dog/other_thing.rb +2 -0
  73. data/vendor/diy/test/files/dog/simple.yml +11 -0
  74. data/vendor/diy/test/files/donkey/foo.rb +8 -0
  75. data/vendor/diy/test/files/donkey/foo/bar/qux.rb +7 -0
  76. data/vendor/diy/test/files/fud/objects.yml +13 -0
  77. data/vendor/diy/test/files/fud/toy.rb +15 -0
  78. data/vendor/diy/test/files/gnu/objects.yml +14 -0
  79. data/vendor/diy/test/files/gnu/thinger.rb +8 -0
  80. data/vendor/diy/test/files/goat/base.rb +8 -0
  81. data/vendor/diy/test/files/goat/can.rb +6 -0
  82. data/vendor/diy/test/files/goat/goat.rb +6 -0
  83. data/vendor/diy/test/files/goat/objects.yml +12 -0
  84. data/vendor/diy/test/files/goat/paper.rb +6 -0
  85. data/vendor/diy/test/files/goat/plane.rb +8 -0
  86. data/vendor/diy/test/files/goat/shirt.rb +6 -0
  87. data/vendor/diy/test/files/goat/wings.rb +8 -0
  88. data/vendor/diy/test/files/horse/holder_thing.rb +4 -0
  89. data/vendor/diy/test/files/horse/objects.yml +7 -0
  90. data/vendor/diy/test/files/yak/core_model.rb +4 -0
  91. data/vendor/diy/test/files/yak/core_presenter.rb +4 -0
  92. data/vendor/diy/test/files/yak/core_view.rb +1 -0
  93. data/vendor/diy/test/files/yak/data_source.rb +1 -0
  94. data/vendor/diy/test/files/yak/fringe_model.rb +4 -0
  95. data/vendor/diy/test/files/yak/fringe_presenter.rb +4 -0
  96. data/vendor/diy/test/files/yak/fringe_view.rb +1 -0
  97. data/vendor/diy/test/files/yak/my_objects.yml +21 -0
  98. data/vendor/diy/test/test_helper.rb +40 -0
  99. data/vendor/hardmock/CHANGES +8 -0
  100. data/vendor/hardmock/LICENSE +7 -0
  101. data/vendor/hardmock/README +48 -0
  102. data/vendor/hardmock/Rakefile +100 -0
  103. data/vendor/hardmock/TODO +7 -0
  104. data/vendor/hardmock/config/environment.rb +12 -0
  105. data/vendor/hardmock/homepage/demo.rb +21 -0
  106. data/vendor/hardmock/homepage/hardmock_sample.png +0 -0
  107. data/vendor/hardmock/homepage/index.html +65 -0
  108. data/vendor/hardmock/init.rb +3 -0
  109. data/vendor/hardmock/lib/hardmock.rb +634 -0
  110. data/vendor/hardmock/lib/method_cleanout.rb +14 -0
  111. data/vendor/hardmock/rcov.rake +18 -0
  112. data/vendor/hardmock/test/functional/assert_error_test.rb +52 -0
  113. data/vendor/hardmock/test/functional/auto_verify_test.rb +192 -0
  114. data/vendor/hardmock/test/functional/direct_mock_usage_test.rb +396 -0
  115. data/vendor/hardmock/test/functional/hardmock_test.rb +380 -0
  116. data/vendor/hardmock/test/test_helper.rb +23 -0
  117. data/vendor/hardmock/test/unit/expectation_builder_test.rb +18 -0
  118. data/vendor/hardmock/test/unit/expector_test.rb +56 -0
  119. data/vendor/hardmock/test/unit/method_cleanout_test.rb +35 -0
  120. data/vendor/hardmock/test/unit/mock_control_test.rb +172 -0
  121. data/vendor/hardmock/test/unit/mock_test.rb +273 -0
  122. data/vendor/hardmock/test/unit/simple_expectation_test.rb +345 -0
  123. data/vendor/hardmock/test/unit/trapper_test.rb +60 -0
  124. data/vendor/hardmock/test/unit/verify_error_test.rb +34 -0
  125. data/vendor/systir/systir.rb +403 -0
  126. data/vendor/systir/test/unit/ui/xml/testrunner.rb +192 -0
  127. data/vendor/systir/test/unit/ui/xml/xmltestrunner.xslt +109 -0
  128. metadata +235 -0
@@ -0,0 +1,366 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+ require 'behaviors'
3
+ require 'constructor'
4
+
5
+ class ConstructorTest < Test::Unit::TestCase
6
+ extend Behaviors
7
+
8
+ class TestingClass
9
+ attr_accessor :foo, :bar, :why, :qux
10
+ constructor :foo, :bar, :why, :qux, :strict => false
11
+
12
+ def to_pretty_pretty
13
+ "#{@foo} #{@bar}"
14
+ end
15
+
16
+ end
17
+
18
+ class Mama
19
+ attr_accessor :fat, :age
20
+ constructor :age, :strict => false
21
+ def setup
22
+ @fat = "much"
23
+ end
24
+ end
25
+
26
+ class Sissy < Mama
27
+ attr_accessor :friends, :beauty
28
+ constructor :beauty, :strict => false
29
+ def setup
30
+ super #IMPORTANT!
31
+ @friends = "many"
32
+ end
33
+ end
34
+
35
+ class TestingStrictArgsDefault
36
+ constructor :foo, :bar
37
+ def to_pretty_pretty
38
+ "#{@foo} #{@bar}"
39
+ end
40
+ end
41
+
42
+ class TestingStrictArgs
43
+ constructor :foo, :bar, :strict => true
44
+ def to_pretty_pretty
45
+ "#{@foo} #{@bar}"
46
+ end
47
+ end
48
+
49
+ class TestingStrictArgs2
50
+ constructor :foo, :bar, :accessors => true
51
+ end
52
+
53
+ class SubclassOfTestingClass < TestingClass
54
+ end
55
+
56
+ class SubclassOfTestingClass2 < TestingClass
57
+ def initialize; end
58
+ end
59
+
60
+ class SubclassOfTestingClass3 < TestingClass
61
+ attr_reader :my_new_var
62
+ def initialize(hash = nil)
63
+ super
64
+ @my_new_var = "something"
65
+ end
66
+ end
67
+
68
+ class TestingAutoAccessors
69
+ constructor :foo, :bar, :why, :qux, :accessors => true, :strict => false
70
+ def to_pretty_pretty
71
+ "#{@foo} #{@bar}"
72
+ end
73
+ end
74
+
75
+ class TestingSuperConstructorBase
76
+ attr_reader :a, :b
77
+ def initialize(a,b)
78
+ @a = a
79
+ @b = b
80
+ end
81
+ end
82
+
83
+ class TestingSuperConstructor < TestingSuperConstructorBase
84
+ constructor :far, :away, :accessors => true, :super => ["once", :twice], :strict => false
85
+ end
86
+
87
+ class TestingSuperConstructorBase2
88
+ attr_reader :c, :d
89
+ def initialize
90
+ @c = 'what a'
91
+ @d = 'day for'
92
+ end
93
+ end
94
+
95
+ class TestingSuperConstructor2 < TestingSuperConstructorBase2
96
+ constructor :some, :accessors => true, :super => [], :strict => false
97
+ end
98
+
99
+ class TestingBlockedAccessors
100
+ constructor :foo, :bar, :accessors => false
101
+ def to_pretty_pretty
102
+ "#{@foo} #{@bar}"
103
+ end
104
+ end
105
+
106
+ class Papa
107
+ constructor :car, :saw
108
+ end
109
+
110
+ class Sonny < Papa
111
+ constructor :computer, :accessors => true
112
+ end
113
+
114
+ class Llamma
115
+ attr_accessor :hungry, :hair
116
+ constructor :hair
117
+ def setup
118
+ @hungry = true
119
+ end
120
+ end
121
+
122
+ #
123
+ # TESTS
124
+ #
125
+
126
+ should "construct an object using a hash of named constructor arguments" do
127
+ fuh = TestingClass.new(
128
+ :foo => 'my foo',
129
+ :bar => 'my bar',
130
+ :qux => 'my qux',
131
+ :why => 'lucky'
132
+ )
133
+
134
+ assert_equal 'my foo', fuh.foo
135
+ assert_equal 'my bar', fuh.bar
136
+ assert_equal 'my qux', fuh.qux
137
+ assert_equal 'lucky', fuh.why
138
+ assert_equal 'my foo my bar', fuh.to_pretty_pretty
139
+ end
140
+
141
+ should "allow omission of arguments when strict is off" do
142
+ fuh = TestingClass.new( :foo => 'my foo' )
143
+ assert_equal 'my foo', fuh.foo
144
+ assert_nil fuh.bar
145
+ assert_nil fuh.qux
146
+ assert_nil fuh.why
147
+ end
148
+
149
+ should "allow no arguments to a constructor when strict is off" do
150
+ fuh = TestingClass.new
151
+ assert_nil fuh.foo
152
+ assert_nil fuh.bar
153
+ assert_nil fuh.qux
154
+ assert_nil fuh.why
155
+ end
156
+
157
+ should "that objects that do not use constructor work normally" do
158
+ require 'rexml/document'
159
+ d = REXML::Document.new("<base/>")
160
+ assert_not_nil d, 'nil document'
161
+ assert_equal "base", d.root.name
162
+ end
163
+
164
+ should "inherit constructor arguments" do
165
+ fuh = SubclassOfTestingClass.new(:foo => 'whu?')
166
+ assert_equal "whu?", fuh.foo
167
+ assert_nil fuh.bar
168
+ assert_nil fuh.qux
169
+ assert_nil fuh.why
170
+ end
171
+
172
+ should "still be able to use a standard constructor in a subclass" do
173
+ fuh = SubclassOfTestingClass2.new
174
+ assert_nil fuh.foo
175
+ end
176
+
177
+ should "call subclass constructor" do
178
+ fuh = SubclassOfTestingClass3.new
179
+ assert_equal "something", fuh.my_new_var
180
+ end
181
+
182
+ should "be able to use named constructor args when a subclass defines a constructor and calls super" do
183
+ fuh = SubclassOfTestingClass3.new(:foo => 12)
184
+ assert_equal "something", fuh.my_new_var
185
+ assert_equal 12, fuh.foo
186
+ end
187
+
188
+ should "provide accessors for constructor arguments when the accessor option is used" do
189
+ fuh = TestingAutoAccessors.new(
190
+ :foo => 'my foo',
191
+ :bar => 'my bar',
192
+ :qux => 'my qux',
193
+ :why => 'lucky'
194
+ )
195
+ assert_equal 'my foo', fuh.foo
196
+ assert_equal 'my bar', fuh.bar
197
+ assert_equal 'my qux', fuh.qux
198
+ assert_equal 'lucky', fuh.why
199
+ assert_equal 'my foo my bar', fuh.to_pretty_pretty
200
+ end
201
+
202
+ should "not provide accessors for constructor arguments when the accessor option is false" do
203
+ fuh = TestingBlockedAccessors.new(
204
+ :foo => 'my foo',
205
+ :bar => 'my bar'
206
+ )
207
+ [:foo, :bar].each do |accessor_name|
208
+ assert_raise NoMethodError do
209
+ fuh.send accessor_name
210
+ end
211
+ end
212
+ assert_equal 'my foo my bar', fuh.to_pretty_pretty
213
+ end
214
+
215
+ should "inherit constructor arguments" do
216
+ s = Sonny.new(:car => "Nissan", :saw => "Dewalt", :computer => "Dell")
217
+ assert_equal "Dell", s.computer
218
+ assert_equal "Dewalt", s.saw
219
+ assert_equal "Nissan", s.car
220
+ end
221
+
222
+ should "call setup method if defined" do
223
+ ralph = Llamma.new(:hair => "red")
224
+ assert ralph.hungry
225
+ assert_equal "red", ralph.hair
226
+ end
227
+
228
+ class Baby < Mama
229
+ constructor :cuteness
230
+ end
231
+
232
+ should "call setup on superclass if subclass does not define a setup method" do
233
+ baby = Baby.new(:cuteness => 'little', :age => 1)
234
+ assert_equal "much", baby.fat
235
+ end
236
+
237
+ should "let parent class setup affect subclass setup" do
238
+ m = Mama.new(:age => 55)
239
+ assert_equal 55, m.age
240
+ assert_equal "much", m.fat
241
+
242
+ s = Sissy.new(:age => 19, :beauty => "medium")
243
+ assert_equal 19, s.age
244
+ assert_equal "medium", s.beauty
245
+ assert_equal "much", s.fat
246
+ assert_equal "many", s.friends
247
+ end
248
+
249
+ should "default to strict argument enforcement" do
250
+ fuh = TestingStrictArgsDefault.new(
251
+ :foo => 'my foo',
252
+ :bar => 'my bar')
253
+ assert_equal 'my foo my bar', fuh.to_pretty_pretty
254
+
255
+ # Omit foo
256
+ err = assert_raise ConstructorArgumentError do
257
+ TestingStrictArgsDefault.new :bar => 'ok,yeah'
258
+ end
259
+ assert_match(/foo/, err.message)
260
+
261
+ # Omit bar
262
+ err = assert_raise ConstructorArgumentError do
263
+ TestingStrictArgsDefault.new :foo => 'ok,yeah'
264
+ end
265
+ assert_match(/bar/, err.message)
266
+ end
267
+
268
+ should "enforce strict arguments when the strict option is true" do
269
+ fuh = TestingStrictArgs.new(
270
+ :foo => 'my foo',
271
+ :bar => 'my bar')
272
+ assert_equal 'my foo my bar', fuh.to_pretty_pretty
273
+
274
+ # Omit foo
275
+ err = assert_raise ConstructorArgumentError do
276
+ TestingStrictArgs.new :bar => 'ok,yeah'
277
+ end
278
+ assert_match(/foo/, err.message)
279
+
280
+ # Omit bar
281
+ err = assert_raise ConstructorArgumentError do
282
+ TestingStrictArgs.new :foo => 'ok,yeah'
283
+ end
284
+ assert_match(/bar/, err.message)
285
+ end
286
+
287
+
288
+ should "not allow an empty construction when using strict arguments" do
289
+ # Omit foo and bar
290
+ err = assert_raise ConstructorArgumentError do
291
+ TestingStrictArgs.new {}
292
+ end
293
+ assert_match(/bar/, err.message)
294
+ assert_match(/foo/, err.message)
295
+
296
+ # Nil
297
+ err = assert_raise ConstructorArgumentError do
298
+ TestingStrictArgs.new
299
+ end
300
+ assert_match(/bar/, err.message)
301
+ assert_match(/foo/, err.message)
302
+
303
+ # Nil explicitly
304
+ err = assert_raise ConstructorArgumentError do
305
+ TestingStrictArgs.new nil
306
+ end
307
+ assert_match(/bar/, err.message)
308
+ assert_match(/foo/, err.message)
309
+ end
310
+
311
+ should "not allow extraneous arguments in strict mode" do
312
+ err = assert_raise ConstructorArgumentError do
313
+ TestingStrictArgs.new(:foo => 1, :bar => 2, :other => 3, :thing => 4)
314
+ end
315
+ assert_match(/other/, err.message)
316
+ assert_match(/thing/, err.message)
317
+
318
+ end
319
+
320
+ should "raise an exception mentioning missing or extra arguments in strict mode" do
321
+ err = assert_raise ConstructorArgumentError do
322
+ TestingStrictArgs.new(:other => 3, :thing => 4)
323
+ end
324
+ assert_match(/foo/, err.message)
325
+ assert_match(/bar/, err.message)
326
+ assert_match(/thing/, err.message)
327
+ assert_match(/thing/, err.message)
328
+ end
329
+
330
+
331
+ should "provide accessors for constructor arguments in strict mode" do
332
+ t2 = TestingStrictArgs2.new(:foo => 1, :bar => 2)
333
+ # See that accessors work
334
+ assert_equal 1, t2.foo
335
+ assert_equal 2, t2.bar
336
+
337
+ # See that strictness still applies
338
+ err = assert_raise ConstructorArgumentError do
339
+ TestingStrictArgs2.new(:no => 'good')
340
+ end
341
+ end
342
+
343
+ should "allow generic rescue of constructor argument catching" do
344
+ begin
345
+ TestingStrictArgs.new(:broken => 'yoobetcha')
346
+ rescue => oops
347
+ assert oops.kind_of?(ConstructorArgumentError)
348
+ end
349
+ end
350
+
351
+ should "pass arguments given in the super option to the initializer of a parent class" do
352
+ tsc = TestingSuperConstructor.new(:far => 'oo', :away => 'kk')
353
+ assert_equal 'oo', tsc.far
354
+ assert_equal 'kk', tsc.away
355
+ assert_equal "once", tsc.a
356
+ assert_equal :twice, tsc.b
357
+ end
358
+
359
+ should "call a super constructor when the super option is an empty array" do
360
+ tsc = TestingSuperConstructor2.new(:some => 'thing')
361
+ assert_equal 'thing', tsc.some
362
+ assert_equal 'what a', tsc.c
363
+ assert_equal 'day for', tsc.d
364
+ end
365
+
366
+ end
@@ -0,0 +1,3 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/../config/environment")
2
+ require 'test/unit'
3
+
@@ -0,0 +1,26 @@
1
+ Tue Oct 25 01:48:45 EDT 2005
2
+ crosby
3
+
4
+ DIY - Dependency Injection with YAML
5
+
6
+ Constructor-based dependency injection container using YAML input.
7
+
8
+ Currently, all objects that get components put into them must have a
9
+ constructor that gets a hash with symbols as keys.
10
+ Best used with constructor.rb
11
+
12
+ Auto-naming and auto-library support is done.
13
+
14
+ TEST
15
+ rake
16
+ ...or ruby test/diy_context_test.rb
17
+
18
+ INSTALL
19
+ Just grab src/diy_context.rb and put it somewhere.
20
+ You might want lib/constructor.rb too, but best get it from svn/devtools/ruby/constructor, its official home.
21
+
22
+ DOC
23
+ Read SVN log
24
+ Read diy_context_test.rb
25
+ See test/files/dog/simple.yml for sample.
26
+ "rdoc src/"
@@ -0,0 +1,18 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ def header(s)
5
+ puts "---\n\n#{s}\n\n---\n"
6
+ end
7
+
8
+
9
+ task :default => [ :testall ]
10
+
11
+ desc "Run the unit tests in test/unit"
12
+ Rake::TestTask.new("testall") { |t|
13
+ header(t.name)
14
+ t.libs << "test"
15
+ t.pattern = 'test/**/*_test.rb'
16
+ t.verbose = true
17
+ }
18
+
@@ -0,0 +1,114 @@
1
+ class Class
2
+ #
3
+ # Declarative means to define object properties by passing a hash
4
+ # to the constructor, which will set the corresponding ivars.
5
+ # Eg,
6
+ # class Horse
7
+ # constructor :name, :breed, :weight
8
+ # end
9
+ # Horse.new :name => 'Ed', :breed => 'Mustang', :weight => 342
10
+ #
11
+ # By default the ivars do not get accessors defined.
12
+ # But you can get them auto-made if you want:
13
+ # class Horse
14
+ # constructor :name, :breed, :weight, :accessors => true
15
+ # end
16
+ # ...
17
+ # puts my_horse.weight
18
+ #
19
+ # You can enforce strict argument checking with :strict option.
20
+ # This means that the constructor will raise an error if you pass
21
+ # more or fewer arguments than declared.
22
+ # Eg,
23
+ # class Donkey
24
+ # constructor :age, :odor, :strict => true
25
+ # end
26
+ # ... this forces you to pass both an age and odor key to the Donkey constructor.
27
+ #
28
+ def constructor(*attrs)
29
+ # Look for embedded options in the listing:
30
+ opts = attrs.find { |a| a.kind_of?(Hash) and attrs.delete(a) }
31
+ do_acc = opts.nil? ? false : opts[:accessors] == true
32
+ require_args = opts.nil? ? false : opts[:strict] == true
33
+
34
+ # Incorporate superclass's constructor keys
35
+ if superclass.constructor_keys
36
+ attrs = [attrs,superclass.constructor_keys].flatten
37
+ end
38
+ # Generate ivar assigner code lines
39
+ assigns = ''
40
+ attrs.each do |k|
41
+ assigns += "@#{k.to_s} = args[:#{k.to_s}]\n"
42
+ end
43
+
44
+ # If accessors option is on, declare accessors for the attributes:
45
+ if do_acc
46
+ self.class_eval "attr_accessor " + attrs.map {|x| ":#{x.to_s}"}.join(',')
47
+ end
48
+
49
+ # If strict is on, define the constructor argument validator method,
50
+ # and setup the initializer to invoke the validator method.
51
+ # Otherwise, insert lax code into the initializer.
52
+ validation_code = "return if args.nil?"
53
+ if require_args
54
+ self.class_eval do
55
+ def _validate_constructor_args(args)
56
+ # First, make sure we've got args of some kind
57
+ unless args and args.keys and args.keys.size > 0
58
+ raise ConstructorArgumentError.new(self.class.constructor_keys)
59
+ end
60
+ # Scan for missing keys in the argument hash
61
+ a_keys = args.keys
62
+ missing = []
63
+ self.class.constructor_keys.each do |ck|
64
+ unless a_keys.member?(ck)
65
+ missing << ck
66
+ end
67
+ a_keys.delete(ck) # Delete inbound keys as we address them
68
+ end
69
+ if missing.size > 0 || a_keys.size > 0
70
+ raise ConstructorArgumentError.new(missing,a_keys)
71
+ end
72
+ end
73
+ end
74
+ # Setup the code to insert into the initializer:
75
+ validation_code = "_validate_constructor_args args "
76
+ end
77
+
78
+ # Generate the initializer code
79
+ self.class_eval %{
80
+ def initialize(args=nil)
81
+ #{validation_code}
82
+ #{assigns}
83
+ setup if respond_to?(:setup)
84
+ end
85
+ }
86
+
87
+ # Remember our constructor keys
88
+ @_ctor_keys = attrs
89
+ end
90
+
91
+ # Access the constructor keys for this class
92
+ def constructor_keys; @_ctor_keys; end
93
+ end
94
+
95
+ # Fancy validation exception, based on missing and extraneous keys.
96
+ class ConstructorArgumentError < RuntimeError
97
+ def initialize(missing,rejected=[])
98
+ err_msg = ''
99
+ if missing.size > 0
100
+ err_msg = "Missing constructor args [#{missing.join(',')}]"
101
+ end
102
+ if rejected.size > 0
103
+ # Some inbound keys were not addressed earlier; this means they're unwanted
104
+ if err_msg
105
+ err_msg << "; " # Appending to earlier message about missing items
106
+ else
107
+ err_msg = ''
108
+ end
109
+ # Enumerate the rejected key names
110
+ err_msg << "Rejected constructor args [#{rejected.join(',')}]"
111
+ end
112
+ super err_msg
113
+ end
114
+ end