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.
- data/.document +2 -0
- data/CHANGES +2 -0
- data/LICENSE +19 -0
- data/README +107 -0
- data/Rakefile +145 -0
- data/config/environment.rb +15 -0
- data/config/objects.yml +22 -0
- data/lib/analog_io.rb +30 -0
- data/lib/digital_auxport_io.rb +49 -0
- data/lib/digital_configuration.rb +66 -0
- data/lib/digital_port_io.rb +68 -0
- data/lib/extension/extconf.rb +4 -0
- data/lib/extension/minilab_hardware.c +235 -0
- data/lib/extension/minilab_hardware.so +0 -0
- data/lib/library_translator.rb +48 -0
- data/lib/minilab.rb +149 -0
- data/lib/minilab_context.rb +39 -0
- data/lib/result_verifier.rb +14 -0
- data/test/integration/analog_input_output_test.rb +43 -0
- data/test/integration/connect_to_hardware_test.rb +13 -0
- data/test/integration/digital_input_output_test.rb +114 -0
- data/test/integration/integration_test.rb +53 -0
- data/test/integration/require_minilab_test.rb +9 -0
- data/test/system/analog_input.test +3 -0
- data/test/system/analog_output.test +37 -0
- data/test/system/digital_port_input.test +5 -0
- data/test/system/digital_port_output.test +39 -0
- data/test/system/digital_port_read_byte.test +26 -0
- data/test/system/digital_screw_terminals_input.test +2 -0
- data/test/system/digital_screw_terminals_output.test +11 -0
- data/test/system/minilab_driver.rb +85 -0
- data/test/test_helper.rb +11 -0
- data/test/unit/analog_io_test.rb +87 -0
- data/test/unit/digital_auxport_io_test.rb +114 -0
- data/test/unit/digital_configuration_test.rb +136 -0
- data/test/unit/digital_port_io_test.rb +117 -0
- data/test/unit/library_translator_test.rb +100 -0
- data/test/unit/minilab_context_test.rb +82 -0
- data/test/unit/minilab_hardware_test.rb +83 -0
- data/test/unit/minilab_test.rb +131 -0
- data/test/unit/result_verifier_test.rb +33 -0
- data/vendor/behaviors/lib/behaviors.rb +50 -0
- data/vendor/behaviors/tasks/behaviors_tasks.rake +140 -0
- data/vendor/behaviors/test/behaviors_tasks_test.rb +71 -0
- data/vendor/behaviors/test/behaviors_test.rb +50 -0
- data/vendor/behaviors/test/tasks_test/Rakefile +16 -0
- data/vendor/behaviors/test/tasks_test/doc/behaviors.html +55 -0
- data/vendor/behaviors/test/tasks_test/lib/user.rb +2 -0
- data/vendor/behaviors/test/tasks_test/test/user_test.rb +17 -0
- data/vendor/constructor/Rakefile +44 -0
- data/vendor/constructor/config/environment.rb +12 -0
- data/vendor/constructor/lib/constructor.rb +132 -0
- data/vendor/constructor/test/constructor_test.rb +366 -0
- data/vendor/constructor/test/helper.rb +3 -0
- data/vendor/diy/README +26 -0
- data/vendor/diy/Rakefile +18 -0
- data/vendor/diy/lib/constructor.rb +114 -0
- data/vendor/diy/lib/diy.rb +329 -0
- data/vendor/diy/proto/context.rb +117 -0
- data/vendor/diy/proto/context.yml +20 -0
- data/vendor/diy/test/diy_test.rb +370 -0
- data/vendor/diy/test/files/broken_construction.yml +7 -0
- data/vendor/diy/test/files/cat/cat.rb +4 -0
- data/vendor/diy/test/files/cat/extra_conflict.yml +5 -0
- data/vendor/diy/test/files/cat/heritage.rb +2 -0
- data/vendor/diy/test/files/cat/needs_input.yml +3 -0
- data/vendor/diy/test/files/cat/the_cat_lineage.rb +1 -0
- data/vendor/diy/test/files/dog/dog_model.rb +4 -0
- data/vendor/diy/test/files/dog/dog_presenter.rb +4 -0
- data/vendor/diy/test/files/dog/dog_view.rb +2 -0
- data/vendor/diy/test/files/dog/file_resolver.rb +2 -0
- data/vendor/diy/test/files/dog/other_thing.rb +2 -0
- data/vendor/diy/test/files/dog/simple.yml +11 -0
- data/vendor/diy/test/files/donkey/foo.rb +8 -0
- data/vendor/diy/test/files/donkey/foo/bar/qux.rb +7 -0
- data/vendor/diy/test/files/fud/objects.yml +13 -0
- data/vendor/diy/test/files/fud/toy.rb +15 -0
- data/vendor/diy/test/files/gnu/objects.yml +14 -0
- data/vendor/diy/test/files/gnu/thinger.rb +8 -0
- data/vendor/diy/test/files/goat/base.rb +8 -0
- data/vendor/diy/test/files/goat/can.rb +6 -0
- data/vendor/diy/test/files/goat/goat.rb +6 -0
- data/vendor/diy/test/files/goat/objects.yml +12 -0
- data/vendor/diy/test/files/goat/paper.rb +6 -0
- data/vendor/diy/test/files/goat/plane.rb +8 -0
- data/vendor/diy/test/files/goat/shirt.rb +6 -0
- data/vendor/diy/test/files/goat/wings.rb +8 -0
- data/vendor/diy/test/files/horse/holder_thing.rb +4 -0
- data/vendor/diy/test/files/horse/objects.yml +7 -0
- data/vendor/diy/test/files/yak/core_model.rb +4 -0
- data/vendor/diy/test/files/yak/core_presenter.rb +4 -0
- data/vendor/diy/test/files/yak/core_view.rb +1 -0
- data/vendor/diy/test/files/yak/data_source.rb +1 -0
- data/vendor/diy/test/files/yak/fringe_model.rb +4 -0
- data/vendor/diy/test/files/yak/fringe_presenter.rb +4 -0
- data/vendor/diy/test/files/yak/fringe_view.rb +1 -0
- data/vendor/diy/test/files/yak/my_objects.yml +21 -0
- data/vendor/diy/test/test_helper.rb +40 -0
- data/vendor/hardmock/CHANGES +8 -0
- data/vendor/hardmock/LICENSE +7 -0
- data/vendor/hardmock/README +48 -0
- data/vendor/hardmock/Rakefile +100 -0
- data/vendor/hardmock/TODO +7 -0
- data/vendor/hardmock/config/environment.rb +12 -0
- data/vendor/hardmock/homepage/demo.rb +21 -0
- data/vendor/hardmock/homepage/hardmock_sample.png +0 -0
- data/vendor/hardmock/homepage/index.html +65 -0
- data/vendor/hardmock/init.rb +3 -0
- data/vendor/hardmock/lib/hardmock.rb +634 -0
- data/vendor/hardmock/lib/method_cleanout.rb +14 -0
- data/vendor/hardmock/rcov.rake +18 -0
- data/vendor/hardmock/test/functional/assert_error_test.rb +52 -0
- data/vendor/hardmock/test/functional/auto_verify_test.rb +192 -0
- data/vendor/hardmock/test/functional/direct_mock_usage_test.rb +396 -0
- data/vendor/hardmock/test/functional/hardmock_test.rb +380 -0
- data/vendor/hardmock/test/test_helper.rb +23 -0
- data/vendor/hardmock/test/unit/expectation_builder_test.rb +18 -0
- data/vendor/hardmock/test/unit/expector_test.rb +56 -0
- data/vendor/hardmock/test/unit/method_cleanout_test.rb +35 -0
- data/vendor/hardmock/test/unit/mock_control_test.rb +172 -0
- data/vendor/hardmock/test/unit/mock_test.rb +273 -0
- data/vendor/hardmock/test/unit/simple_expectation_test.rb +345 -0
- data/vendor/hardmock/test/unit/trapper_test.rb +60 -0
- data/vendor/hardmock/test/unit/verify_error_test.rb +34 -0
- data/vendor/systir/systir.rb +403 -0
- data/vendor/systir/test/unit/ui/xml/testrunner.rb +192 -0
- data/vendor/systir/test/unit/ui/xml/xmltestrunner.xslt +109 -0
- metadata +235 -0
@@ -0,0 +1,140 @@
|
|
1
|
+
desc "List behavioral definitions for the classes under test (use for=<term> also)"
|
2
|
+
task :behaviors do
|
3
|
+
specifications.each do |spec|
|
4
|
+
puts "#{spec.name} should:\n"
|
5
|
+
spec.requirements.each do |req|
|
6
|
+
puts " - #{req}"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "List behavioral definitions for the classes under test (HTML output)"
|
12
|
+
task :behaviors_html do
|
13
|
+
require 'erb'
|
14
|
+
txt =<<-EOS
|
15
|
+
<html>
|
16
|
+
<head>
|
17
|
+
<style>
|
18
|
+
|
19
|
+
div.title
|
20
|
+
{
|
21
|
+
width: 600px;
|
22
|
+
font: bold 14pt trebuchet ms;
|
23
|
+
}
|
24
|
+
|
25
|
+
div.specification
|
26
|
+
{
|
27
|
+
font: bold 12pt trebuchet ms;
|
28
|
+
border: solid 1px black;
|
29
|
+
width: 600px;
|
30
|
+
padding: 5px;
|
31
|
+
margin: 5px;
|
32
|
+
}
|
33
|
+
|
34
|
+
ul.requirements
|
35
|
+
{
|
36
|
+
font: normal 11pt verdana;
|
37
|
+
padding-left: 0;
|
38
|
+
margin-left: 0;
|
39
|
+
border-bottom: 1px solid gray;
|
40
|
+
width: 600px;
|
41
|
+
}
|
42
|
+
|
43
|
+
ul.requirements li
|
44
|
+
{
|
45
|
+
list-style: none;
|
46
|
+
margin: 0;
|
47
|
+
padding: 0.25em;
|
48
|
+
border-top: 1px solid gray;
|
49
|
+
}
|
50
|
+
</style>
|
51
|
+
</head>
|
52
|
+
<body>
|
53
|
+
<div class="title">Specifications for <%= title %></div>
|
54
|
+
<% specifications.each do |spec| %>
|
55
|
+
<div class="specification">
|
56
|
+
<%= spec.name %> should:
|
57
|
+
<ul class="requirements">
|
58
|
+
<% spec.requirements.each do |req| %>
|
59
|
+
<li><%= req %></li>
|
60
|
+
<% end %>
|
61
|
+
</ul>
|
62
|
+
</div>
|
63
|
+
<% end %>
|
64
|
+
</body>
|
65
|
+
</html>
|
66
|
+
EOS
|
67
|
+
output_dir = File.expand_path("#{APP_ROOT}/doc")
|
68
|
+
mkdir_p output_dir
|
69
|
+
output_filename = output_dir + "/behaviors.html"
|
70
|
+
File.open(output_filename,"w") do |f|
|
71
|
+
f.write ERB.new(txt).result(binding)
|
72
|
+
end
|
73
|
+
puts "(Wrote #{output_filename})"
|
74
|
+
end
|
75
|
+
|
76
|
+
def title
|
77
|
+
File.basename(File.expand_path(APP_ROOT))
|
78
|
+
end
|
79
|
+
|
80
|
+
def specifications
|
81
|
+
test_files.map do |file|
|
82
|
+
spec = OpenStruct.new
|
83
|
+
m = %r".*/([^/].*)_test.rb".match(file)
|
84
|
+
class_name = titleize(m[1]) if m[1]
|
85
|
+
spec.name = class_name
|
86
|
+
spec.requirements = []
|
87
|
+
File::readlines(file).each do |line|
|
88
|
+
if line =~ /^\s*should\s+\(?\s*["'](.*)["']/
|
89
|
+
spec.requirements << $1
|
90
|
+
end
|
91
|
+
end
|
92
|
+
spec
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_files
|
97
|
+
test_list = FileList["#{APP_ROOT}/test/**/*_test.rb"]
|
98
|
+
if ENV['for']
|
99
|
+
test_list = test_list.grep(/#{ENV['for']}/i)
|
100
|
+
end
|
101
|
+
test_list
|
102
|
+
end
|
103
|
+
|
104
|
+
|
105
|
+
############################################################
|
106
|
+
# STOLEN FROM inflector.rb
|
107
|
+
############################################################
|
108
|
+
#--
|
109
|
+
# Copyright (c) 2005 David Heinemeier Hansson
|
110
|
+
#
|
111
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
112
|
+
# a copy of this software and associated documentation files (the
|
113
|
+
# "Software"), to deal in the Software without restriction, including
|
114
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
115
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
116
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
117
|
+
# the following conditions:
|
118
|
+
#
|
119
|
+
# The above copyright notice and this permission notice shall be
|
120
|
+
# included in all copies or substantial portions of the Software.
|
121
|
+
#
|
122
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
123
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
124
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
125
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
126
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
127
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
128
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
129
|
+
#++
|
130
|
+
def titleize(word)
|
131
|
+
humanize(underscore(word)).gsub(/\b([a-z])/) { $1.capitalize }
|
132
|
+
end
|
133
|
+
|
134
|
+
def underscore(camel_cased_word) camel_cased_word.to_s.gsub(/::/, '/').
|
135
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
|
136
|
+
end
|
137
|
+
|
138
|
+
def humanize(lower_case_and_underscored_word)
|
139
|
+
lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
|
140
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
APP_ROOT = File.expand_path(File.dirname(__FILE__) + '/../')
|
4
|
+
|
5
|
+
class BehaviorsTasksTest < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
here = File.expand_path(File.dirname(__FILE__))
|
9
|
+
cmd = RUBY_PLATFORM[/mswin/] ? 'rake.cmd' : 'rake'
|
10
|
+
@base_cmd = "#{cmd} -f \"#{here}/tasks_test/Rakefile\" "
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# HELPERS
|
15
|
+
#
|
16
|
+
def run_behaviors_task
|
17
|
+
run_cmd "behaviors"
|
18
|
+
end
|
19
|
+
|
20
|
+
def run_behaviors_html_task
|
21
|
+
run_cmd "behaviors_html"
|
22
|
+
end
|
23
|
+
|
24
|
+
def run_cmd(cmd)
|
25
|
+
@report = %x[ #{@base_cmd} #{cmd} ]
|
26
|
+
end
|
27
|
+
|
28
|
+
def see_html_task_output_message
|
29
|
+
@html_output_filename = "#{APP_ROOT}/test/tasks_test/doc/behaviors.html"
|
30
|
+
assert_match /Wrote #{@html_output_filename}/, @report
|
31
|
+
end
|
32
|
+
|
33
|
+
def see_that_html_report_file_exits
|
34
|
+
assert File.exists?(@html_output_filename), "html output file should exist"
|
35
|
+
end
|
36
|
+
|
37
|
+
def html_report_file_should_contain(user_behaviors)
|
38
|
+
file_contents = File.read(@html_output_filename)
|
39
|
+
user_behaviors.each do |line|
|
40
|
+
assert_match /#{line}/, file_contents
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# TESTS
|
46
|
+
#
|
47
|
+
def test_that_behaviors_tasks_should_list_behavioral_definitions_for_the_classes_under_test
|
48
|
+
run_behaviors_task
|
49
|
+
user_behaviors = [
|
50
|
+
"User should:",
|
51
|
+
" - be able set user name and age during construction",
|
52
|
+
" - be able to get user name and age",
|
53
|
+
" - be able to ask if a user is an adult"
|
54
|
+
]
|
55
|
+
assert_match /#{user_behaviors.join("\n")}/, @report
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_that_behaviors_tasks_should_list_behavioral_definitions_for_the_classes_under_test_in_html_output
|
59
|
+
run_behaviors_html_task
|
60
|
+
see_html_task_output_message
|
61
|
+
see_that_html_report_file_exits
|
62
|
+
user_behaviors = [
|
63
|
+
"User should:",
|
64
|
+
"be able set user name and age during construction",
|
65
|
+
"be able to get user name and age",
|
66
|
+
"be able to ask if a user is an adult"
|
67
|
+
]
|
68
|
+
html_report_file_should_contain user_behaviors
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require File.expand_path(File.dirname(__FILE__)) + '/../lib/behaviors'
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
loading_developer_test_class_stdout = StringIO.new
|
6
|
+
saved_stdout = $stdout.dup
|
7
|
+
$stdout = loading_developer_test_class_stdout
|
8
|
+
|
9
|
+
class DeveloperTest
|
10
|
+
extend Behaviors
|
11
|
+
attr_accessor :flunk_msg, :tested_code
|
12
|
+
|
13
|
+
should "test their code" do
|
14
|
+
@tested_code = true
|
15
|
+
end
|
16
|
+
should "go to meetings"
|
17
|
+
end
|
18
|
+
|
19
|
+
$stdout = saved_stdout
|
20
|
+
loading_developer_test_class_stdout.rewind
|
21
|
+
$loading_developer_test_class_output = loading_developer_test_class_stdout.read
|
22
|
+
|
23
|
+
class BehaviorsTest < Test::Unit::TestCase
|
24
|
+
|
25
|
+
|
26
|
+
def setup
|
27
|
+
@target = DeveloperTest.new
|
28
|
+
assert_nil @target.tested_code, "block called too early"
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# TESTS
|
33
|
+
#
|
34
|
+
def test_should_called_with_a_block_defines_a_test
|
35
|
+
assert @target.methods.include?("test_should_test their code"), "Missing test method"
|
36
|
+
|
37
|
+
@target.send("test_should_test their code")
|
38
|
+
|
39
|
+
assert @target.tested_code, "block not called"
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_should_called_without_a_block_does_not_create_a_test_method
|
43
|
+
assert !@target.methods.include?("test_should_go to meetings"), "Should not have method"
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_should_called_without_a_block_will_give_unimplemented_output_when_class_loads
|
47
|
+
unimplemented_output = "UNIMPLEMENTED CASE: Developer should go to meetings"
|
48
|
+
assert_match /#{unimplemented_output}/, $loading_developer_test_class_output
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
APP_ROOT = File.expand_path(File.dirname(__FILE__))
|
5
|
+
load "#{APP_ROOT}/../../tasks/behaviors_tasks.rake"
|
6
|
+
|
7
|
+
cd APP_ROOT
|
8
|
+
|
9
|
+
desc 'Default: run unit tests.'
|
10
|
+
task :default => :test
|
11
|
+
|
12
|
+
Rake::TestTask.new(:test) do |t|
|
13
|
+
t.libs << "#{APP_ROOT}/../../lib"
|
14
|
+
t.pattern = 'test/**/*_test.rb'
|
15
|
+
t.verbose = true
|
16
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
<html>
|
2
|
+
<head>
|
3
|
+
<style>
|
4
|
+
|
5
|
+
div.title
|
6
|
+
{
|
7
|
+
width: 600px;
|
8
|
+
font: bold 14pt trebuchet ms;
|
9
|
+
}
|
10
|
+
|
11
|
+
div.specification
|
12
|
+
{
|
13
|
+
font: bold 12pt trebuchet ms;
|
14
|
+
border: solid 1px black;
|
15
|
+
width: 600px;
|
16
|
+
padding: 5px;
|
17
|
+
margin: 5px;
|
18
|
+
}
|
19
|
+
|
20
|
+
ul.requirements
|
21
|
+
{
|
22
|
+
font: normal 11pt verdana;
|
23
|
+
padding-left: 0;
|
24
|
+
margin-left: 0;
|
25
|
+
border-bottom: 1px solid gray;
|
26
|
+
width: 600px;
|
27
|
+
}
|
28
|
+
|
29
|
+
ul.requirements li
|
30
|
+
{
|
31
|
+
list-style: none;
|
32
|
+
margin: 0;
|
33
|
+
padding: 0.25em;
|
34
|
+
border-top: 1px solid gray;
|
35
|
+
}
|
36
|
+
</style>
|
37
|
+
</head>
|
38
|
+
<body>
|
39
|
+
<div class="title">Specifications for tasks_test</div>
|
40
|
+
|
41
|
+
<div class="specification">
|
42
|
+
User should:
|
43
|
+
<ul class="requirements">
|
44
|
+
|
45
|
+
<li>be able set user name and age during construction</li>
|
46
|
+
|
47
|
+
<li>be able to get user name and age</li>
|
48
|
+
|
49
|
+
<li>be able to ask if a user is an adult</li>
|
50
|
+
|
51
|
+
</ul>
|
52
|
+
</div>
|
53
|
+
|
54
|
+
</body>
|
55
|
+
</html>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'behaviors'
|
3
|
+
|
4
|
+
require 'user'
|
5
|
+
|
6
|
+
class UserTest < Test::Unit::TestCase
|
7
|
+
extend Behaviors
|
8
|
+
|
9
|
+
def setup
|
10
|
+
end
|
11
|
+
|
12
|
+
should "be able set user name and age during construction"
|
13
|
+
should "be able to get user name and age"
|
14
|
+
should "be able to ask if a user is an adult"
|
15
|
+
def test_DELETEME
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'rubygems'
|
5
|
+
Gem::manage_gems
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
|
8
|
+
desc 'Default: run unit tests.'
|
9
|
+
task :default => :test
|
10
|
+
|
11
|
+
GEM_VERSION = '1.0.0'
|
12
|
+
GEM_NAME = 'constructor'
|
13
|
+
|
14
|
+
gem_spec = Gem::Specification.new do |s|
|
15
|
+
s.name = GEM_NAME
|
16
|
+
s.version = GEM_VERSION
|
17
|
+
s.author = "Atomic Object LLC"
|
18
|
+
s.email = "dev@atomicobject.com"
|
19
|
+
s.homepage = "http://atomicobject.com"
|
20
|
+
s.platform = Gem::Platform::RUBY
|
21
|
+
s.summary = "Declarative, named constructor arguments"
|
22
|
+
s.files = FileList["{test,lib}/**/*"].exclude("rdoc").to_a
|
23
|
+
s.has_rdoc = true
|
24
|
+
s.add_dependency "behaviors", ">= 1.0.0"
|
25
|
+
end
|
26
|
+
|
27
|
+
Rake::GemPackageTask.new(gem_spec) do |pkg|
|
28
|
+
pkg.need_tar = true
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'Run all tests.'
|
32
|
+
Rake::TestTask.new(:test) do |t|
|
33
|
+
t.libs << 'lib'
|
34
|
+
t.pattern = 'test/*_test.rb'
|
35
|
+
t.verbose = true
|
36
|
+
end
|
37
|
+
|
38
|
+
desc 'Generate documentation.'
|
39
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
40
|
+
rdoc.rdoc_dir = 'rdoc'
|
41
|
+
rdoc.title = 'Constructor'
|
42
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
43
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
44
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# The path to the root directory of your application.
|
2
|
+
APP_ROOT = File.join(File.dirname(__FILE__), '..')
|
3
|
+
|
4
|
+
ADDITIONAL_LOAD_PATHS = []
|
5
|
+
ADDITIONAL_LOAD_PATHS.concat %w(
|
6
|
+
src
|
7
|
+
).map { |dir| "#{APP_ROOT}/#{dir}" }.select { |dir| File.directory?(dir) }
|
8
|
+
|
9
|
+
# Prepend to $LOAD_PATH
|
10
|
+
ADDITIONAL_LOAD_PATHS.reverse.each { |dir| $:.unshift(dir) if File.directory?(dir) }
|
11
|
+
|
12
|
+
# Require any additional libraries needed
|
@@ -0,0 +1,132 @@
|
|
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
|
+
# Arguments specified are required by default. You can disable
|
20
|
+
# strict argument checking with :strict option. This means that
|
21
|
+
# the constructor will not raise an error if you pass more or
|
22
|
+
# fewer arguments than declared.
|
23
|
+
# Eg,
|
24
|
+
# class Donkey
|
25
|
+
# constructor :age, :odor, :strict => false
|
26
|
+
# end
|
27
|
+
# ... this allows you to pass either an age or odor key (or neither) to
|
28
|
+
# the Donkey constructor.
|
29
|
+
#
|
30
|
+
def constructor(*attrs)
|
31
|
+
# Look for embedded options in the listing:
|
32
|
+
opts = attrs.find { |a| a.kind_of?(Hash) and attrs.delete(a) }
|
33
|
+
do_acc = opts.nil? ? false : opts[:accessors] == true
|
34
|
+
require_args = opts.nil? ? true : opts[:strict] != false
|
35
|
+
super_args = opts.nil? ? nil : opts[:super]
|
36
|
+
|
37
|
+
# Incorporate superclass's constructor keys, if our superclass
|
38
|
+
if superclass.constructor_keys
|
39
|
+
attrs = [attrs,superclass.constructor_keys].flatten
|
40
|
+
end
|
41
|
+
# Generate ivar assigner code lines
|
42
|
+
assigns = ''
|
43
|
+
attrs.each do |k|
|
44
|
+
assigns += "@#{k.to_s} = args[:#{k.to_s}]\n"
|
45
|
+
end
|
46
|
+
|
47
|
+
# If accessors option is on, declare accessors for the attributes:
|
48
|
+
if do_acc
|
49
|
+
self.class_eval "attr_accessor " + attrs.map {|x| ":#{x.to_s}"}.join(',')
|
50
|
+
end
|
51
|
+
|
52
|
+
# If user supplied super-constructor hints:
|
53
|
+
super_call = ''
|
54
|
+
if super_args
|
55
|
+
list = super_args.map do |a|
|
56
|
+
case a
|
57
|
+
when String
|
58
|
+
%|"#{a}"|
|
59
|
+
when Symbol
|
60
|
+
%|:#{a}|
|
61
|
+
end
|
62
|
+
end
|
63
|
+
super_call = %|super(#{list.join(',')})|
|
64
|
+
end
|
65
|
+
|
66
|
+
# If strict is on, define the constructor argument validator method,
|
67
|
+
# and setup the initializer to invoke the validator method.
|
68
|
+
# Otherwise, insert lax code into the initializer.
|
69
|
+
validation_code = "return if args.nil?"
|
70
|
+
if require_args
|
71
|
+
self.class_eval do
|
72
|
+
def _validate_constructor_args(args)
|
73
|
+
# First, make sure we've got args of some kind
|
74
|
+
unless args and args.keys and args.keys.size > 0
|
75
|
+
raise ConstructorArgumentError.new(self.class.constructor_keys)
|
76
|
+
end
|
77
|
+
# Scan for missing keys in the argument hash
|
78
|
+
a_keys = args.keys
|
79
|
+
missing = []
|
80
|
+
self.class.constructor_keys.each do |ck|
|
81
|
+
unless a_keys.member?(ck)
|
82
|
+
missing << ck
|
83
|
+
end
|
84
|
+
a_keys.delete(ck) # Delete inbound keys as we address them
|
85
|
+
end
|
86
|
+
if missing.size > 0 || a_keys.size > 0
|
87
|
+
raise ConstructorArgumentError.new(missing,a_keys)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
# Setup the code to insert into the initializer:
|
92
|
+
validation_code = "_validate_constructor_args args "
|
93
|
+
end
|
94
|
+
|
95
|
+
# Generate the initializer code
|
96
|
+
self.class_eval %{
|
97
|
+
def initialize(args=nil)
|
98
|
+
#{super_call}
|
99
|
+
#{validation_code}
|
100
|
+
#{assigns}
|
101
|
+
setup if respond_to?(:setup)
|
102
|
+
end
|
103
|
+
}
|
104
|
+
|
105
|
+
# Remember our constructor keys
|
106
|
+
@_ctor_keys = attrs
|
107
|
+
end
|
108
|
+
|
109
|
+
# Access the constructor keys for this class
|
110
|
+
def constructor_keys; @_ctor_keys; end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Fancy validation exception, based on missing and extraneous keys.
|
114
|
+
class ConstructorArgumentError < RuntimeError
|
115
|
+
def initialize(missing,rejected=[])
|
116
|
+
err_msg = ''
|
117
|
+
if missing.size > 0
|
118
|
+
err_msg = "Missing constructor args [#{missing.join(',')}]"
|
119
|
+
end
|
120
|
+
if rejected.size > 0
|
121
|
+
# Some inbound keys were not addressed earlier; this means they're unwanted
|
122
|
+
if err_msg
|
123
|
+
err_msg << "; " # Appending to earlier message about missing items
|
124
|
+
else
|
125
|
+
err_msg = ''
|
126
|
+
end
|
127
|
+
# Enumerate the rejected key names
|
128
|
+
err_msg << "Rejected constructor args [#{rejected.join(',')}]"
|
129
|
+
end
|
130
|
+
super err_msg
|
131
|
+
end
|
132
|
+
end
|