simrpc 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +1 -1
- data/README.rdoc +79 -0
- data/Rakefile +46 -0
- data/lib/simrpc/common.rb +46 -0
- data/lib/simrpc/exceptions.rb +30 -0
- data/lib/simrpc/message.rb +156 -0
- data/lib/simrpc/node.rb +206 -0
- data/lib/simrpc/qpid_adapter.rb +171 -0
- data/lib/simrpc/schema.rb +400 -0
- data/lib/simrpc.rb +9 -802
- data/spec/common_spec.rb +26 -0
- data/spec/message_spec.rb +93 -0
- data/spec/node_spec.rb +99 -0
- data/spec/qpid_spec.rb +92 -0
- data/spec/schema_spec.rb +348 -0
- data/spec/spec_helper.rb +56 -0
- metadata +25 -23
- data/README +0 -25
- /data/lib/{semaphore.rb → simrpc/semaphore.rb} +0 -0
data/spec/common_spec.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# simrpc common module spec
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person
|
6
|
+
# obtaining a copy of this software and associated documentation
|
7
|
+
# files (the "Software"), to deal in the Software without
|
8
|
+
# restriction, including without limitation the rights to use,
|
9
|
+
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the
|
11
|
+
# Software is furnished to do so, subject to the following
|
12
|
+
# conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
19
|
+
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
21
|
+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
22
|
+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# simrpc message spec
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person
|
6
|
+
# obtaining a copy of this software and associated documentation
|
7
|
+
# files (the "Software"), to deal in the Software without
|
8
|
+
# restriction, including without limitation the rights to use,
|
9
|
+
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the
|
11
|
+
# Software is furnished to do so, subject to the following
|
12
|
+
# conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
19
|
+
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
21
|
+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
22
|
+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
27
|
+
|
28
|
+
describe "Simrpc::Message" do
|
29
|
+
|
30
|
+
################## test message module
|
31
|
+
|
32
|
+
it "should format and parse a data field" do
|
33
|
+
formatted = Message::Formatter::format_with_size("foobar")
|
34
|
+
formatted.should == "00000006foobar"
|
35
|
+
assert_equal ["foobar", ""], Message::Formatter::parse_from_formatted(formatted)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should convert a field to and from a string" do
|
39
|
+
field = Message::Field.new(:name => "foo", :value => "bar")
|
40
|
+
field_s = field.to_s
|
41
|
+
field_s.should == "00000003foo00000003bar"
|
42
|
+
|
43
|
+
field_o = Message::Field.from_s(field_s)
|
44
|
+
field_o.name.should == "foo"
|
45
|
+
field_o.value.should == "bar"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should convert a message header to and from a string" do
|
49
|
+
header = Message::Header.new(:target => "footarget")
|
50
|
+
header_s = header.to_s
|
51
|
+
header_s.should == "0000000000000009footarget"
|
52
|
+
|
53
|
+
header_o = Message::Header.from_s(header_s)
|
54
|
+
header.type.should == ""
|
55
|
+
header.target.should == "footarget"
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should convert a message body to and from a string" do
|
59
|
+
body = Message::Body.new
|
60
|
+
body.fields.push Message::Field.new(:name => "foo", :value => "bar")
|
61
|
+
body.fields.push Message::Field.new(:name => "money", :value => "lotsof")
|
62
|
+
body_s = body.to_s
|
63
|
+
body_s.should == "0000002200000003foo00000003bar0000002700000005money00000006lotsof"
|
64
|
+
|
65
|
+
body_o = Message::Body.from_s(body_s)
|
66
|
+
body_o.fields.size.should == 2
|
67
|
+
body_o.fields[0].name.should == "foo"
|
68
|
+
body_o.fields[0].value.should == "bar"
|
69
|
+
body_o.fields[1].name.should == "money"
|
70
|
+
body_o.fields[1].value.should == "lotsof"
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should convert a message to and from a string" do
|
74
|
+
msg = Message::Message.new
|
75
|
+
msg.header.type = 'request'
|
76
|
+
msg.header.target = 'method'
|
77
|
+
msg.body.fields.push Message::Field.new(:name => "foo", :value => "bar")
|
78
|
+
msg.body.fields.push Message::Field.new(:name => "money", :value => "lotsof")
|
79
|
+
msg_s = msg.to_s
|
80
|
+
|
81
|
+
msg_s.should == "0000002900000007request00000006method000000650000002200000003foo00000003bar0000002700000005money00000006lotsof"
|
82
|
+
|
83
|
+
msg_o = Message::Message.from_s(msg_s)
|
84
|
+
msg_o.header.type.should == 'request'
|
85
|
+
msg_o.header.target.should == 'method'
|
86
|
+
msg_o.body.fields.size.should == 2
|
87
|
+
msg_o.body.fields[0].name.should == 'foo'
|
88
|
+
msg_o.body.fields[0].value.should == 'bar'
|
89
|
+
msg_o.body.fields[1].name.should == 'money'
|
90
|
+
msg_o.body.fields[1].value.should == 'lotsof'
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
data/spec/node_spec.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
# simrpc node adapter spec
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person
|
6
|
+
# obtaining a copy of this software and associated documentation
|
7
|
+
# files (the "Software"), to deal in the Software without
|
8
|
+
# restriction, including without limitation the rights to use,
|
9
|
+
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the
|
11
|
+
# Software is furnished to do so, subject to the following
|
12
|
+
# conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
19
|
+
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
21
|
+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
22
|
+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
27
|
+
|
28
|
+
describe "Simrpc::Node" do
|
29
|
+
|
30
|
+
it "should generate and handle simrpc message" do
|
31
|
+
schema_def = Schema::Parser.parse(:schema => TEST_SCHEMA)
|
32
|
+
mmc = MethodMessageController.new(schema_def)
|
33
|
+
msg = mmc.generate('bar_method', [[10,150,20,1130]])
|
34
|
+
|
35
|
+
msg.should_not be_nil
|
36
|
+
msg.header.target.should == 'bar_method'
|
37
|
+
msg.header.type.should == 'request'
|
38
|
+
msg.body.fields.size.should == 1
|
39
|
+
msg.body.fields[0].name.should == 'byte_array'
|
40
|
+
msg.body.fields[0].value.should == '0004000210000315000022000041130'
|
41
|
+
|
42
|
+
finished_lock = Semaphore.new(1)
|
43
|
+
finished_lock.wait()
|
44
|
+
|
45
|
+
schema_def.methods[1].handler = lambda { |byte_array|
|
46
|
+
byte_array.size.should == 4
|
47
|
+
byte_array[0].should == 10
|
48
|
+
byte_array[1].should == 150
|
49
|
+
byte_array[2].should == 20
|
50
|
+
byte_array[3].should == 1130
|
51
|
+
|
52
|
+
x = 0
|
53
|
+
byte_array.each { |b| x += b }
|
54
|
+
return x > 300 ? 1 : 0
|
55
|
+
}
|
56
|
+
|
57
|
+
server = QpidAdapter::Node.new :id => "server"
|
58
|
+
server.async_accept { |node, msg, reply_to|
|
59
|
+
mmc.message_received(node, msg, reply_to)
|
60
|
+
}
|
61
|
+
|
62
|
+
client = QpidAdapter::Node.new :id => 'client'
|
63
|
+
client.async_accept { |node, msg, reply_to|
|
64
|
+
msg = Message::Message.from_s(msg)
|
65
|
+
msg.body.fields.size.should == 1
|
66
|
+
msg.body.fields[0].name.should == 'bool_success'
|
67
|
+
msg.body.fields[0].value.to_i.should == 1
|
68
|
+
finished_lock.signal()
|
69
|
+
}
|
70
|
+
client.send_message("server-queue", msg)
|
71
|
+
finished_lock.wait()
|
72
|
+
|
73
|
+
# FIXME test method that doesn't have any return values
|
74
|
+
end
|
75
|
+
|
76
|
+
it "it should run properly" do
|
77
|
+
server = Node.new(:id => "server3", :schema => TEST_SCHEMA)
|
78
|
+
client = Node.new(:id => "client3", :schema => TEST_SCHEMA, :destination => "server3")
|
79
|
+
|
80
|
+
server.handle_method("foo_method") { |some_int, floating_point_number|
|
81
|
+
some_int.should == 10
|
82
|
+
floating_point_number.should == 15.4
|
83
|
+
|
84
|
+
["work_plz", MyClass.new("foobar", 4.2)] # FIXME have to manually return args in an array
|
85
|
+
# and can't use return 1,2,3,.. shorthand
|
86
|
+
# as currently in ruby, invoking 'return'
|
87
|
+
# from a Proc has undesirable behavior
|
88
|
+
}
|
89
|
+
|
90
|
+
a_str, my_class_instance = client.foo_method(10, 15.4)
|
91
|
+
|
92
|
+
a_str.should == "work_plz"
|
93
|
+
my_class_instance.str_member.should == "foobar"
|
94
|
+
my_class_instance.float_member.should == 4.2
|
95
|
+
|
96
|
+
# FIXME test method that doesn't have any return values
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
data/spec/qpid_spec.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# simrpc qpid adapter spec
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person
|
6
|
+
# obtaining a copy of this software and associated documentation
|
7
|
+
# files (the "Software"), to deal in the Software without
|
8
|
+
# restriction, including without limitation the rights to use,
|
9
|
+
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the
|
11
|
+
# Software is furnished to do so, subject to the following
|
12
|
+
# conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
19
|
+
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
21
|
+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
22
|
+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
27
|
+
|
28
|
+
describe "Simrpc::QpidAdapter" do
|
29
|
+
|
30
|
+
################## test qpid module
|
31
|
+
|
32
|
+
# ensure that we can connect to the qpid broker
|
33
|
+
it "should connect to broker" do
|
34
|
+
# TODO test w/ broker/port & specified conf
|
35
|
+
qpid = QpidAdapter::Node.new :id => "test1"
|
36
|
+
ssn = qpid.instance_variable_get('@ssn')
|
37
|
+
id = qpid.instance_variable_get('@node_id')
|
38
|
+
ssn.error?.should == false
|
39
|
+
#ssn.closed.should == false
|
40
|
+
id.should == "test1"
|
41
|
+
end
|
42
|
+
|
43
|
+
# ensure we define a queue and exchange
|
44
|
+
it "should define a queue and exchange" do
|
45
|
+
node = QpidAdapter::Node.new :id => "test2"
|
46
|
+
|
47
|
+
exchange = node.instance_variable_get("@exchange")
|
48
|
+
exchange.should == "test2-exchange"
|
49
|
+
|
50
|
+
queue = node.instance_variable_get("@queue")
|
51
|
+
queue.should == "test2-queue"
|
52
|
+
|
53
|
+
local_queue = node.instance_variable_get("@local_queue")
|
54
|
+
local_queue.should == "test2-local-queue"
|
55
|
+
|
56
|
+
routing_key = node.instance_variable_get("@routing_key")
|
57
|
+
routing_key.should == "test2-queue"
|
58
|
+
|
59
|
+
ssn = node.instance_variable_get('@ssn')
|
60
|
+
assert !ssn.exchange_query("test2-exchange").not_found
|
61
|
+
assert !ssn.queue_query("test2-queue").queue.nil?
|
62
|
+
|
63
|
+
# TODO how do I get this:
|
64
|
+
# http://www.redhat.com/docs/en-US/Red_Hat_Enterprise_MRG/1.1/html/python/public/qpid.generator.ControlInvoker_0_10-class.html#exchange_bound_result
|
65
|
+
#binding_result = ssn.binding_query("test2-queue")
|
66
|
+
#assert !binding_result.exchange_not_found?
|
67
|
+
#assert !binding_result.queue_not_found?
|
68
|
+
#assert !binding_result.queue_not_matched?
|
69
|
+
#assert !binding_result.key_not_found?
|
70
|
+
end
|
71
|
+
|
72
|
+
# test sending/receiving a message
|
73
|
+
it "should transmit a message" do
|
74
|
+
server = QpidAdapter::Node.new :id => "server1"
|
75
|
+
server.async_accept { |node, msg, reply_to|
|
76
|
+
assert_equal('test-data', msg)
|
77
|
+
node.send_message(reply_to, "test-response")
|
78
|
+
}
|
79
|
+
|
80
|
+
finished_lock = Semaphore.new(1)
|
81
|
+
finished_lock.wait()
|
82
|
+
|
83
|
+
client = QpidAdapter::Node.new :id => 'client1'
|
84
|
+
client.async_accept { |node, msg, reply_to|
|
85
|
+
assert_equal("test-response", msg)
|
86
|
+
finished_lock.signal()
|
87
|
+
}
|
88
|
+
client.send_message("server1-queue", "test-data")
|
89
|
+
finished_lock.wait()
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
data/spec/schema_spec.rb
ADDED
@@ -0,0 +1,348 @@
|
|
1
|
+
# simrpc schema spec
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010 Mohammed Morsi <movitto@yahoo.com>
|
4
|
+
#
|
5
|
+
# Permission is hereby granted, free of charge, to any person
|
6
|
+
# obtaining a copy of this software and associated documentation
|
7
|
+
# files (the "Software"), to deal in the Software without
|
8
|
+
# restriction, including without limitation the rights to use,
|
9
|
+
# copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
# copies of the Software, and to permit persons to whom the
|
11
|
+
# Software is furnished to do so, subject to the following
|
12
|
+
# conditions:
|
13
|
+
#
|
14
|
+
# The above copyright notice and this permission notice shall be
|
15
|
+
# included in all copies or substantial portions of the Software.
|
16
|
+
#
|
17
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
19
|
+
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
21
|
+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
22
|
+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
23
|
+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
24
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
25
|
+
|
26
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
27
|
+
|
28
|
+
describe "Simrpc::Schema" do
|
29
|
+
|
30
|
+
it "should correctly parse a simrpc schema" do
|
31
|
+
schema_def = Schema::Parser.parse(:schema => TEST_SCHEMA)
|
32
|
+
|
33
|
+
schema_def.classes.size.should == 1
|
34
|
+
schema_def.methods.size.should == 2
|
35
|
+
|
36
|
+
schema_def.classes[0].name.should == "MyClass"
|
37
|
+
schema_def.classes[0].members.size.should == 3
|
38
|
+
schema_def.classes[0].members[0].type.should == :str
|
39
|
+
schema_def.classes[0].members[0].name.should == 'str_member'
|
40
|
+
schema_def.classes[0].members[1].type.should == :float
|
41
|
+
schema_def.classes[0].members[1].name.should == 'float_member'
|
42
|
+
schema_def.classes[0].members[2].type.should == :obj
|
43
|
+
schema_def.classes[0].members[2].name.should == 'associated_obj'
|
44
|
+
schema_def.classes[0].members[2].ignore_null.should == true
|
45
|
+
|
46
|
+
schema_def.methods[0].name.should == "foo_method"
|
47
|
+
schema_def.methods[0].parameters.size.should == 2
|
48
|
+
schema_def.methods[0].parameters[0].type.should == :int
|
49
|
+
schema_def.methods[0].parameters[0].name.should == 'some_int'
|
50
|
+
schema_def.methods[0].parameters[1].type.should == :float
|
51
|
+
schema_def.methods[0].parameters[1].name.should == 'floating_point_number'
|
52
|
+
schema_def.methods[0].return_values.size.should == 2
|
53
|
+
schema_def.methods[0].return_values[0].type.should == :str
|
54
|
+
schema_def.methods[0].return_values[0].name.should == 'a_string'
|
55
|
+
schema_def.methods[0].return_values[1].type.should == :obj
|
56
|
+
schema_def.methods[0].return_values[1].name.should == 'my_class_instance'
|
57
|
+
schema_def.methods[0].return_values[1].associated.should == :MyClass
|
58
|
+
schema_def.methods[0].return_values[1].associated_class_def(schema_def).should == schema_def.classes[0]
|
59
|
+
|
60
|
+
schema_def.methods[1].name.should == "bar_method"
|
61
|
+
schema_def.methods[1].parameters.size.should == 1
|
62
|
+
schema_def.methods[1].return_values.size.should == 1
|
63
|
+
schema_def.methods[1].parameters[0].type.should == :array
|
64
|
+
schema_def.methods[1].parameters[0].name.should == 'byte_array'
|
65
|
+
schema_def.methods[1].parameters[0].associated.should == :int
|
66
|
+
schema_def.methods[1].return_values[0].type.should == :int
|
67
|
+
schema_def.methods[1].return_values[0].name.should == 'bool_success'
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should correct parse class" do
|
71
|
+
schema_def = Schema::Parser.parse(:schema =>
|
72
|
+
"<schema><class name='Base'></class><class name='Derived' inherits='Base' /></schema>")
|
73
|
+
|
74
|
+
schema_def.classes.size.should == 2
|
75
|
+
schema_def.classes[0].name.should == "Base"
|
76
|
+
schema_def.classes[0].inherits.should == nil
|
77
|
+
schema_def.classes[1].name.should == "Derived"
|
78
|
+
schema_def.classes[1].inherits.should == "Base"
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should correctly identify primitives" do
|
82
|
+
Schema::is_primitive?(:int).should == true
|
83
|
+
Schema::is_primitive?(:float).should == true
|
84
|
+
Schema::is_primitive?(:str).should == true
|
85
|
+
!Schema::is_primitive?(:obj).should == true
|
86
|
+
!Schema::is_primitive?(:array).should == true
|
87
|
+
|
88
|
+
Schema::primitive_from_str(:str, "yo").should == "yo"
|
89
|
+
Schema::primitive_from_str(:int, "420").should == 420
|
90
|
+
Schema::primitive_from_str(:float, "42.05").should == 42.05
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should convert a data field to / from a string" do
|
94
|
+
schema_def = Schema::Parser.parse(:schema => TEST_SCHEMA)
|
95
|
+
|
96
|
+
data_field = Schema::DataFieldDef.new :name => "foo", :type => :str
|
97
|
+
data_field.to_s("bar").should == "bar"
|
98
|
+
data_field.from_s("bar").should == "bar"
|
99
|
+
data_field.type = :int
|
100
|
+
data_field.to_s(420).should == "420"
|
101
|
+
data_field.from_s("420").should == 420
|
102
|
+
data_field.type = :float
|
103
|
+
data_field.to_s(15.4).should == "15.4"
|
104
|
+
data_field.from_s("15.4").should == 15.4
|
105
|
+
data_field.type = :bool
|
106
|
+
data_field.to_s(true).should == "true"
|
107
|
+
data_field.from_s("true").should == true
|
108
|
+
data_field.to_s(false).should == "false"
|
109
|
+
data_field.from_s("false").should == false
|
110
|
+
|
111
|
+
my_class = MyClass.new("abc", 1.23)
|
112
|
+
my_class2 = MyClass.new("def", 4.56)
|
113
|
+
my_class_s = schema_def.classes[0].to_s(my_class, schema_def)
|
114
|
+
my_class2_s = schema_def.classes[0].to_s(my_class2, schema_def)
|
115
|
+
my_class_o = schema_def.classes[0].from_s(my_class_s, schema_def)
|
116
|
+
my_class2_o = schema_def.classes[0].from_s(my_class2_s, schema_def)
|
117
|
+
|
118
|
+
data_field.type = :obj
|
119
|
+
data_field.associated = "MyClass"
|
120
|
+
data_field.to_s(my_class, schema_def).should == my_class_s
|
121
|
+
#assert_equal my_class_o,
|
122
|
+
# data_field.from_s(my_class_s, schema_def)
|
123
|
+
my_class_o.str_member.should == "abc"
|
124
|
+
my_class_o.float_member.should == 1.23
|
125
|
+
|
126
|
+
data_field.type = :array
|
127
|
+
array_s = data_field.to_s([my_class, my_class2], schema_def)
|
128
|
+
array_s[0...4].should == "0002"
|
129
|
+
array_s[4...8].should == "%04d" % my_class_s.size
|
130
|
+
end_pos = 8+my_class_s.size
|
131
|
+
array_s[8...end_pos].should == my_class_s
|
132
|
+
array_s[end_pos...end_pos+4].should == "%04d" % my_class2_s.size
|
133
|
+
array_s[end_pos+4...end_pos+4+my_class2_s.size].should == my_class2_s
|
134
|
+
|
135
|
+
array_o = data_field.from_s(array_s, schema_def)
|
136
|
+
array_o.size.should == 2
|
137
|
+
#assert_equal my_class_o, array_o[0]
|
138
|
+
#assert_equal my_class2_o, array_o[1]
|
139
|
+
array_o[0].str_member.should == "abc"
|
140
|
+
array_o[1].str_member.should == "def"
|
141
|
+
array_o[0].float_member.should == 1.23
|
142
|
+
array_o[1].float_member.should == 4.56
|
143
|
+
|
144
|
+
data_field.associated = :str
|
145
|
+
array_s = data_field.to_s(['abc', 'def', 'hijklmnopqrstuvwxyz123456789'])
|
146
|
+
array_s[0...4].should == "0003"
|
147
|
+
array_s[4...8].should == "0003"
|
148
|
+
array_s[8...11].should == "abc"
|
149
|
+
array_s[11...15].should == "0003"
|
150
|
+
array_s[15...18].should == "def"
|
151
|
+
array_s[18...22].should == "0028"
|
152
|
+
array_s[22...50].should == "hijklmnopqrstuvwxyz123456789"
|
153
|
+
|
154
|
+
array_o = data_field.from_s(array_s)
|
155
|
+
array_o.size.should == 3
|
156
|
+
array_o[0].should == "abc"
|
157
|
+
array_o[1].should == "def"
|
158
|
+
array_o[2].should == "hijklmnopqrstuvwxyz123456789"
|
159
|
+
|
160
|
+
array_s = data_field.to_s([])
|
161
|
+
array_s[0...4].should == "0000"
|
162
|
+
array_o = data_field.from_s(array_s)
|
163
|
+
array_o.size.should == 0
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should raise an error if a field refers to an invalid class" do
|
167
|
+
schema_def = Schema::Parser.parse(:schema =>
|
168
|
+
"<schema>" +
|
169
|
+
"<class name='SchemaTestSuper'>"+
|
170
|
+
"<member type='obj' name='super_attr' />" +
|
171
|
+
"</class>" +
|
172
|
+
"</schema>")
|
173
|
+
|
174
|
+
lambda {
|
175
|
+
schema_def.classes[0].members[0].to_s(SchemaTestBase.new, schema_def) # try converting a class not defined in schema to_s
|
176
|
+
}.should raise_error(InvalidSchemaClass, "cannot find SchemaTestBase in schema")
|
177
|
+
|
178
|
+
lambda {
|
179
|
+
schema_def.classes[0].members[0].from_s("0014SchemaTestBase", schema_def)
|
180
|
+
}.should raise_error(InvalidSchemaClass, "cannot find SchemaTestBase in schema")
|
181
|
+
|
182
|
+
|
183
|
+
schema_def = Schema::Parser.parse(:schema =>
|
184
|
+
"<schema>" +
|
185
|
+
"<class name='SchemaTestSuper'>"+
|
186
|
+
"<member type='obj' associated='SchemaTestDerived' name='super_attr' />" + # associate w/ class not defined in schema
|
187
|
+
"</class>" +
|
188
|
+
"</schema>")
|
189
|
+
|
190
|
+
lambda {
|
191
|
+
schema_def.classes[0].members[0].to_s(SchemaTestDerived.new, schema_def)
|
192
|
+
}.should raise_error(InvalidSchemaClass, "cannot find SchemaTestDerived in schema")
|
193
|
+
|
194
|
+
lambda {
|
195
|
+
schema_def.classes[0].members[0].from_s("0017SchemaTestDerived", schema_def)
|
196
|
+
}.should raise_error(InvalidSchemaClass, "cannot find SchemaTestDerived in schema")
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should convert a class to / from a string" do
|
200
|
+
schema_def = Schema::Parser.parse(:schema => TEST_SCHEMA)
|
201
|
+
my_class = MyClass.new('abc', 12.3)
|
202
|
+
my_class_s = schema_def.classes[0].to_s(my_class, schema_def)
|
203
|
+
|
204
|
+
my_class_s.size.should == 20
|
205
|
+
my_class_s[0].chr.should == "I" # object defined here, not refrence to elsewhere
|
206
|
+
my_class_s[1...5].should == "0003" # length of the first member
|
207
|
+
my_class_s[5...8].should == "abc" # first member in string format
|
208
|
+
my_class_s[8...12].should == "0004" # length of second member
|
209
|
+
my_class_s[12...16].should == "12.3" # second member in string format
|
210
|
+
my_class_s[16...20].should == "0000" # length of last class member (since we're not setting in this case, 0)
|
211
|
+
|
212
|
+
my_class_o = schema_def.classes[0].from_s(my_class_s, schema_def)
|
213
|
+
my_class_o.class.should == MyClass
|
214
|
+
my_class_o.str_member.should == "abc"
|
215
|
+
my_class_o.float_member.should == 12.3
|
216
|
+
my_class_o.associated_obj.should == nil
|
217
|
+
my_class_o.associated_obj_set.should == false # test ignore_null works
|
218
|
+
|
219
|
+
schema_def.classes[0].to_s(nil).should == "I"
|
220
|
+
schema_def.classes[0].from_s("I").should == nil
|
221
|
+
|
222
|
+
# test MyClass w/ associated_obj member that has no 'associated' attribute
|
223
|
+
my_class = MyClass.new('abc', 12.3, MyClass.new('foo'))
|
224
|
+
my_class_s = schema_def.classes[0].to_s(my_class, schema_def)
|
225
|
+
my_class_s.size.should == 50
|
226
|
+
my_class_s.should == "I" + "0003" + "abc" + "0004" + "12.3" + "0030" + "0007" + "MyClass" + "I" + "0003" + "foo" + "0003" + "0.0" + "0000"
|
227
|
+
|
228
|
+
my_class_o = schema_def.classes[0].from_s(my_class_s, schema_def)
|
229
|
+
my_class_o.str_member.should == "abc"
|
230
|
+
my_class_o.float_member.should == 12.3
|
231
|
+
my_class_o.associated_obj.class.should == MyClass
|
232
|
+
my_class_o.associated_obj.str_member.should == "foo"
|
233
|
+
my_class_o.associated_obj.float_member.should == 0
|
234
|
+
my_class_o.associated_obj_set.should == true # test ignore_null works
|
235
|
+
|
236
|
+
# FIXME test recursion as implemented w/ the converted_classes
|
237
|
+
# parameters to to_s & from_s
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should convert an inherited class to / from a string" do
|
241
|
+
schema_def = Schema::Parser.parse(:schema =>
|
242
|
+
"<schema>" +
|
243
|
+
"<class name='SchemaTestSuper'>"+
|
244
|
+
"<member type='str' name='super_attr' />" +
|
245
|
+
"</class>" +
|
246
|
+
"<class name='SchemaTestBase' inherits='SchemaTestSuper' >"+
|
247
|
+
"<member type='str' name='base_attr' />" +
|
248
|
+
"</class>" +
|
249
|
+
"<class name='SchemaTestDerived' inherits='SchemaTestBase'>" +
|
250
|
+
"<member type='str' name='derived_attr' />" +
|
251
|
+
"</class>" +
|
252
|
+
"</schema>")
|
253
|
+
|
254
|
+
derived = SchemaTestDerived.new
|
255
|
+
derived.derived_attr = "foo"
|
256
|
+
derived.base_attr = "bar"
|
257
|
+
derived.super_attr = "superman"
|
258
|
+
|
259
|
+
ds = schema_def.classes[2].to_s(derived, schema_def)
|
260
|
+
ds.should == "I" + "0003" + "foo" + "0003" + "bar" + "0008" + "superman"
|
261
|
+
|
262
|
+
dso = schema_def.classes[2].from_s(ds, schema_def)
|
263
|
+
dso.derived_attr.should == "foo"
|
264
|
+
dso.base_attr.should == "bar"
|
265
|
+
dso.super_attr.should == "superman"
|
266
|
+
end
|
267
|
+
|
268
|
+
it "should convert a class w/ generic object attribute to / from string" do
|
269
|
+
schema_def = Schema::Parser.parse(:schema =>
|
270
|
+
"<schema>" +
|
271
|
+
"<class name='SchemaTestSuper'>"+
|
272
|
+
"<member type='obj' name='super_attr' />" +
|
273
|
+
"<member type='str' name='another_attr' />" +
|
274
|
+
"</class>" +
|
275
|
+
"</schema>")
|
276
|
+
|
277
|
+
sup1 = SchemaTestSuper.new
|
278
|
+
sup2 = SchemaTestSuper.new
|
279
|
+
sup2.another_attr = "foobar"
|
280
|
+
sup1.super_attr = sup2
|
281
|
+
|
282
|
+
ss = schema_def.classes[0].to_s(sup1, schema_def)
|
283
|
+
ss.should == "I" + "0034" + "0015" + "SchemaTestSuper" + "I" + "0000" + "0006" + "foobar" + "0000"
|
284
|
+
|
285
|
+
sso = schema_def.classes[0].from_s(ss, schema_def)
|
286
|
+
sso.super_attr.class.should == SchemaTestSuper
|
287
|
+
sso.super_attr.another_attr.should == "foobar"
|
288
|
+
|
289
|
+
sup1.super_attr = nil
|
290
|
+
sup1.another_attr = "money"
|
291
|
+
ss = schema_def.classes[0].to_s(sup1, schema_def)
|
292
|
+
ss.should == "I" + "0000" + "0005" + "money"
|
293
|
+
|
294
|
+
sso = schema_def.classes[0].from_s(ss, schema_def)
|
295
|
+
sso.super_attr.should == nil
|
296
|
+
end
|
297
|
+
|
298
|
+
it "should be able to create singleton class from string" do
|
299
|
+
schema_def = Schema::Parser.parse(:schema =>
|
300
|
+
"<schema>" +
|
301
|
+
"<class name='StandardSingletonTest'>"+
|
302
|
+
"<member type='str' name='an_attr' />" +
|
303
|
+
"</class>" +
|
304
|
+
"</schema>")
|
305
|
+
|
306
|
+
lambda {
|
307
|
+
schema_def.classes[0].from_s("I0003foo", schema_def)
|
308
|
+
}.should_not raise_error
|
309
|
+
end
|
310
|
+
|
311
|
+
it "should raise an error if it fails to create a new object from string" do
|
312
|
+
schema_def = Schema::Parser.parse(:schema =>
|
313
|
+
"<schema>" +
|
314
|
+
"<class name='CustomSingletonTest'>"+
|
315
|
+
"<member type='str' name='an_attr' />" +
|
316
|
+
"</class>" +
|
317
|
+
"</schema>")
|
318
|
+
|
319
|
+
lambda {
|
320
|
+
schema_def.classes[0].from_s("I0000", schema_def)
|
321
|
+
}.should raise_error(InvalidSchemaClass, "cannot create schema class CustomSingletonTest")
|
322
|
+
end
|
323
|
+
|
324
|
+
|
325
|
+
end
|
326
|
+
|
327
|
+
class SchemaTestSuper
|
328
|
+
attr_accessor :super_attr
|
329
|
+
attr_accessor :another_attr
|
330
|
+
end
|
331
|
+
|
332
|
+
class SchemaTestBase < SchemaTestSuper
|
333
|
+
attr_accessor :base_attr
|
334
|
+
end
|
335
|
+
|
336
|
+
class SchemaTestDerived < SchemaTestBase
|
337
|
+
attr_accessor :derived_attr
|
338
|
+
end
|
339
|
+
|
340
|
+
class StandardSingletonTest
|
341
|
+
include Singleton
|
342
|
+
end
|
343
|
+
|
344
|
+
# test class where 'new' a 'instance' are not public
|
345
|
+
class CustomSingletonTest
|
346
|
+
attr_accessor :an_attr
|
347
|
+
private_class_method :new
|
348
|
+
end
|