krpc 0.1.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -2
- data/README.md +45 -19
- data/krpc.gemspec +5 -2
- data/lib/krpc.rb +2 -7
- data/lib/krpc/KRPC.pb.rb +0 -1
- data/lib/krpc/attributes.rb +0 -1
- data/lib/krpc/client.rb +36 -28
- data/lib/krpc/connection.rb +4 -7
- data/lib/krpc/core_extensions.rb +5 -0
- data/lib/krpc/decoder.rb +13 -14
- data/lib/krpc/doc.rb +103 -33
- data/lib/krpc/encoder.rb +14 -15
- data/lib/krpc/error.rb +4 -1
- data/lib/krpc/gen.rb +58 -32
- data/lib/krpc/protobuf_utils.rb +6 -8
- data/lib/krpc/repl_tools.rb +0 -1
- data/lib/krpc/service.rb +20 -20
- data/lib/krpc/streaming.rb +176 -0
- data/lib/krpc/types.rb +84 -85
- metadata +48 -3
data/lib/krpc/repl_tools.rb
CHANGED
data/lib/krpc/service.rb
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
require 'krpc/gen'
|
2
2
|
require 'krpc/attributes'
|
3
3
|
require 'krpc/doc'
|
4
|
+
require 'krpc/streaming'
|
4
5
|
|
5
6
|
module KRPC
|
6
7
|
module Services
|
7
8
|
class << self
|
8
9
|
|
9
10
|
# Generate classes and methods for the service - see documentation for Client#generate_services_api!
|
10
|
-
def create_service(service_msg
|
11
|
+
def create_service(service_msg)
|
11
12
|
service_name = service_msg.name
|
12
13
|
|
13
14
|
# Create service class
|
@@ -16,12 +17,12 @@ module KRPC
|
|
16
17
|
|
17
18
|
# Create service' classes
|
18
19
|
service_msg.classes.map(&:name).each do |sc_name|
|
19
|
-
|
20
|
+
TypeStore["Class(#{service_name}.#{sc_name})"]
|
20
21
|
end
|
21
22
|
|
22
23
|
# Create service' enums
|
23
24
|
service_msg.enumerations.each do |enum|
|
24
|
-
enum_type =
|
25
|
+
enum_type = TypeStore["Enum(#{service_name}.#{enum.name})"]
|
25
26
|
enum_type.set_values(enum.values)
|
26
27
|
end
|
27
28
|
|
@@ -29,28 +30,28 @@ module KRPC
|
|
29
30
|
service_msg.procedures.each do |proc|
|
30
31
|
if Attributes.is_a_class_method_or_property_accessor(proc.attributes)
|
31
32
|
class_name = Attributes.get_class_name(proc.attributes)
|
32
|
-
class_cls =
|
33
|
+
class_cls = TypeStore["Class(#{service_name}.#{class_name})"].ruby_type
|
33
34
|
method_name = Attributes.get_class_method_or_property_name(proc.attributes)
|
34
35
|
if Attributes.is_a_class_property_accessor(proc.attributes) # service' class property
|
35
36
|
if Attributes.is_a_class_property_getter(proc.attributes)
|
36
|
-
Gen.add_rpc_method(class_cls, method_name, service_name, proc,
|
37
|
+
Gen.add_rpc_method(class_cls, method_name, service_name, proc, :prepend_self_to_args)
|
37
38
|
else
|
38
|
-
Gen.add_rpc_method(class_cls, method_name + '=', service_name, proc,
|
39
|
+
Gen.add_rpc_method(class_cls, method_name + '=', service_name, proc, :prepend_self_to_args, :no_stream)
|
39
40
|
end
|
40
41
|
elsif Attributes.is_a_class_method(proc.attributes) # service' class method
|
41
|
-
Gen.add_rpc_method(class_cls, method_name, service_name, proc,
|
42
|
+
Gen.add_rpc_method(class_cls, method_name, service_name, proc, :prepend_self_to_args)
|
42
43
|
else # service' static class method
|
43
|
-
Gen.add_rpc_method(class_cls, method_name, service_name, proc,
|
44
|
+
Gen.add_rpc_method(class_cls, method_name, service_name, proc, :static)
|
44
45
|
end
|
45
46
|
elsif Attributes.is_a_property_accessor(proc.attributes) # service' property
|
46
47
|
property_name = Attributes.get_property_name(proc.attributes)
|
47
48
|
if Attributes.is_a_property_getter(proc.attributes)
|
48
|
-
Gen.add_rpc_method(service_class, property_name, service_name, proc
|
49
|
+
Gen.add_rpc_method(service_class, property_name, service_name, proc)
|
49
50
|
elsif Attributes.is_a_property_setter(proc.attributes)
|
50
|
-
Gen.add_rpc_method(service_class, property_name + '=', service_name, proc,
|
51
|
+
Gen.add_rpc_method(service_class, property_name + '=', service_name, proc, :no_stream)
|
51
52
|
end
|
52
53
|
else # plain procedure = method available to service class and its instance
|
53
|
-
Gen.add_rpc_method(service_class, proc.name, service_name, proc,
|
54
|
+
Gen.add_rpc_method(service_class, proc.name, service_name, proc, :static)
|
54
55
|
end
|
55
56
|
end
|
56
57
|
|
@@ -60,8 +61,8 @@ module KRPC
|
|
60
61
|
service_classes = mod.constants.map{|c| mod.const_get(c)}.select {|c| c.is_a? Class}
|
61
62
|
service_classes.each(&:add_methods_available_to_class_and_instance)
|
62
63
|
|
63
|
-
# Return service class
|
64
|
-
service_class
|
64
|
+
# Return service class
|
65
|
+
service_class
|
65
66
|
end
|
66
67
|
|
67
68
|
end
|
@@ -71,6 +72,7 @@ module KRPC
|
|
71
72
|
class ServiceBase
|
72
73
|
extend Gen::AvailableToClassAndInstanceMethodsHandler
|
73
74
|
include Doc::SuffixMethods
|
75
|
+
include Streaming::StreamConstructors
|
74
76
|
|
75
77
|
attr_reader :client
|
76
78
|
|
@@ -83,19 +85,17 @@ module KRPC
|
|
83
85
|
# Core kRPC service, e.g. for querying for the available services.
|
84
86
|
class KRPC < ServiceBase
|
85
87
|
include Gen::RPCMethodGenerator
|
86
|
-
|
88
|
+
|
87
89
|
def initialize(client)
|
88
90
|
super(client)
|
89
91
|
unless respond_to? :get_status
|
90
|
-
include_rpc_method("get_status", "KRPC", "GetStatus", return_type: "KRPC.Status")
|
91
|
-
include_rpc_method("get_services", "KRPC", "GetServices", return_type: "KRPC.Services")
|
92
|
-
|
93
|
-
|
94
|
-
# include_rpc_method("remove_stream", "KRPC", "RemoveStream", ...)
|
92
|
+
include_rpc_method("get_status", "KRPC", "GetStatus", return_type: "KRPC.Status", xmldoc: "<doc><summary>Gets a status message from the server containing information including the server’s version string and performance statistics.</summary></doc>", options: :no_stream)
|
93
|
+
include_rpc_method("get_services", "KRPC", "GetServices", return_type: "KRPC.Services", xmldoc: "<doc><summary>Gets available services and procedures.</summary></doc>", options: :no_stream)
|
94
|
+
include_rpc_method("add_stream", "KRPC", "AddStream", params: [PB::Parameter.new(name: "request", type: "KRPC.Request")], return_type: "uint32", xmldoc: "<doc><summary>Add a streaming request. Returns it's identifier.</summary></doc>", options: :no_stream)
|
95
|
+
include_rpc_method("remove_stream", "KRPC", "RemoveStream", params: [PB::Parameter.new(name: "id", type: "uint32")], xmldoc: "<doc><summary>Remove a streaming request</summary></doc>", options: :no_stream)
|
95
96
|
end
|
96
97
|
end
|
97
98
|
end
|
98
99
|
|
99
100
|
end
|
100
101
|
end
|
101
|
-
|
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'colorize'
|
3
|
+
|
4
|
+
module KRPC
|
5
|
+
module Streaming
|
6
|
+
|
7
|
+
class StreamsManager
|
8
|
+
attr_reader :client
|
9
|
+
|
10
|
+
def initialize(client)
|
11
|
+
@client = client
|
12
|
+
@streams = {}
|
13
|
+
@streams_mutex = Mutex.new
|
14
|
+
@streaming_thread = Thread.new {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# Send a streaming request, create related Stream object and return it. If identical Stream
|
18
|
+
# already exists, doesn't create new Stream and return the existing one.
|
19
|
+
def create_stream(request, return_type, method, *args, **kwargs)
|
20
|
+
raise RuntimeError("Cannot stream a property setter") if method.name.to_s.end_with? '='
|
21
|
+
id = client.krpc.add_stream(request)
|
22
|
+
@streams_mutex.synchronize do
|
23
|
+
if @streams.include? id
|
24
|
+
@streams[id]
|
25
|
+
else
|
26
|
+
value = method.call(*args, **kwargs)
|
27
|
+
@streams[id] = Stream.new(self, id, return_type, value, method, *args, **kwargs)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Remove a streaming request and deactivate the Stream object. Returns `true` if
|
33
|
+
# streaming request is removed or `false` if passed Stream object is already inactive.
|
34
|
+
def remove_stream(stream)
|
35
|
+
return false unless stream.active?
|
36
|
+
@streams_mutex.synchronize do
|
37
|
+
return false unless @streams.include? stream.id
|
38
|
+
client.krpc.remove_stream stream.id
|
39
|
+
@streams.delete stream.id
|
40
|
+
end
|
41
|
+
stream.value = RuntimeError.new("Stream has been removed")
|
42
|
+
stream.mark_as_inactive
|
43
|
+
true
|
44
|
+
end
|
45
|
+
|
46
|
+
# Remove all streams created by this streams manager.
|
47
|
+
def remove_all_streams
|
48
|
+
@streams.each {|_,stream| remove_stream(stream)}
|
49
|
+
end
|
50
|
+
|
51
|
+
# Start streaming thread. It receives stream data, and updates Stream object's `value` attribute.
|
52
|
+
def start_streaming_thread
|
53
|
+
stop_streaming_thread
|
54
|
+
@streaming_thread = Thread.new do
|
55
|
+
connection = client.stream_connection
|
56
|
+
stream_message_type = TypeStore["KRPC.StreamMessage"]
|
57
|
+
loop do
|
58
|
+
size = connection.recv_varint
|
59
|
+
data = connection.recv(size)
|
60
|
+
stream_msg = Decoder.decode(data, stream_message_type, client)
|
61
|
+
@streams_mutex.synchronize do
|
62
|
+
stream_msg.responses.each do |stream_resp|
|
63
|
+
next unless @streams.include? stream_resp.id
|
64
|
+
stream = @streams[stream_resp.id]
|
65
|
+
if stream_resp.response.has_field?("error")
|
66
|
+
stream.value = RPCError.new(stream_resp.response.error)
|
67
|
+
else
|
68
|
+
stream.value = Decoder.decode(stream_resp.response.return_value, stream.return_type, client)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Stop streaming thread.
|
77
|
+
def stop_streaming_thread
|
78
|
+
@streaming_thread.terminate
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
class Stream
|
83
|
+
attr_reader :id, :method, :args, :kwargs, :return_type, :manager
|
84
|
+
attr_writer :value
|
85
|
+
|
86
|
+
def initialize(manager, id, return_type, value, method, *args, **kwargs)
|
87
|
+
@manager = manager
|
88
|
+
@id = id
|
89
|
+
@return_type, @value = return_type, value
|
90
|
+
@method, @args, @kwargs = method, args, kwargs
|
91
|
+
@active = true
|
92
|
+
end
|
93
|
+
|
94
|
+
# Get the current stream value. Has alias method `value`.
|
95
|
+
def get
|
96
|
+
raise @value if @value.is_a?(Exception)
|
97
|
+
@value
|
98
|
+
end
|
99
|
+
alias_method :value, :get
|
100
|
+
|
101
|
+
# Remove stream. Has alias method `close`.
|
102
|
+
def remove
|
103
|
+
manager.remove_stream self
|
104
|
+
end
|
105
|
+
alias_method :close, :remove
|
106
|
+
|
107
|
+
# Check if stream is active (i.e. not removed).
|
108
|
+
def active?; @active end
|
109
|
+
|
110
|
+
# Mark stream as inactive.
|
111
|
+
# WARNING: This method does not remove the stream. To remove the stream call Stream#remove instead.
|
112
|
+
def mark_as_inactive; @active = false end
|
113
|
+
|
114
|
+
def to_s
|
115
|
+
inspect.gsub(/\n|\t/," ").squeeze(" ").uncolorize
|
116
|
+
end
|
117
|
+
|
118
|
+
def inspect
|
119
|
+
def coderay(x)
|
120
|
+
require 'coderay'
|
121
|
+
if x.is_a?(Array) then "[" + x.map{|e| e.is_a?(Gen::ClassBase) ? e.inspect : coderay(e.inspect)}.join(", ") + "]"
|
122
|
+
elsif x.is_a?(Hash) then "{" + x.map{|k,v| coderay(k.inspect) + "=>" + (v.is_a?(Gen::ClassBase) ? v.inspect : coderay(v.inspect))}.join(", ") + "}"
|
123
|
+
else CodeRay.scan(x, :ruby).term end
|
124
|
+
rescue Exception
|
125
|
+
x.inspect
|
126
|
+
end
|
127
|
+
"#<#{self.class}".green +
|
128
|
+
" @id" + "=".green + id.to_s.bold.blue +
|
129
|
+
" @active" + "=".green + @active.to_s.bold.light_cyan +
|
130
|
+
"\n\t@method" + "=".green + method.inspect.green +
|
131
|
+
(args.empty? ? "" : "\n\t@args" + "=".green + coderay(args)) +
|
132
|
+
(kwargs.empty? ? "" : "\n\t@kwargs" + "=".green + coderay(kwargs)) +
|
133
|
+
"\n\treturn_ruby_type" + "=".green + coderay(return_type.ruby_type) +
|
134
|
+
">".green
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
module StreamConstructors
|
139
|
+
STREAM_METHOD_SUFFIX = "_stream"
|
140
|
+
STREAM_METHOD_REGEX = /^(.+)(?:#{STREAM_METHOD_SUFFIX})$/
|
141
|
+
|
142
|
+
module ClassMethods
|
143
|
+
def stream_constructors
|
144
|
+
@stream_constructors ||= {}
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def self.included(base)
|
149
|
+
base.extend ClassMethods
|
150
|
+
base.extend self
|
151
|
+
end
|
152
|
+
|
153
|
+
def method_missing(method, *args, **kwargs, &block)
|
154
|
+
if STREAM_METHOD_REGEX =~ method.to_s
|
155
|
+
if respond_to? $1.to_sym
|
156
|
+
ctors = self.is_a?(Module) ? stream_constructors : self.class.stream_constructors
|
157
|
+
return ctors[$1].call(self, *args, **kwargs) if ctors.include? $1
|
158
|
+
end
|
159
|
+
end
|
160
|
+
super
|
161
|
+
end
|
162
|
+
|
163
|
+
def respond_to_missing?(method, *)
|
164
|
+
if STREAM_METHOD_REGEX =~ method.to_s
|
165
|
+
if respond_to? $1.to_sym
|
166
|
+
ctors = self.is_a?(Module) ? stream_constructors : self.class.stream_constructors
|
167
|
+
return true if ctors.include? $1
|
168
|
+
end
|
169
|
+
end
|
170
|
+
super
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|
data/lib/krpc/types.rb
CHANGED
@@ -21,93 +21,91 @@ module KRPC
|
|
21
21
|
"bytes" => String
|
22
22
|
}
|
23
23
|
PROTOBUF_TO_MESSAGE_TYPE = ProtobufUtils.create_PB_to_PB_message_class_hash("KRPC")
|
24
|
-
|
25
|
-
class TypeStore
|
26
24
|
|
27
|
-
|
28
|
-
|
29
|
-
|
25
|
+
class TypeStore
|
26
|
+
@cache = {}
|
27
|
+
class << self
|
30
28
|
|
31
|
-
|
32
|
-
|
29
|
+
def [](type_string)
|
30
|
+
return @cache[type_string] if @cache.include? type_string
|
31
|
+
|
32
|
+
type =
|
33
|
+
if PROTOBUF_VALUE_TYPES.include? type_string then ValueType.new(type_string)
|
34
|
+
elsif type_string.start_with? "Class(" || type_string == "Class" then ClassType.new(type_string)
|
35
|
+
elsif type_string.start_with? "Enum(" || type_string == "Enum" then EnumType.new(type_string)
|
36
|
+
elsif type_string.start_with? "List(" || type_string == "List" then ListType.new(type_string)
|
37
|
+
elsif type_string.start_with? "Dictionary(" || type_string == "Dictionary" then DictionaryType.new(type_string)
|
38
|
+
elsif type_string.start_with? "Set(" || type_string == "Set" then SetType.new(type_string)
|
39
|
+
elsif type_string.start_with? "Tuple(" || type_string == "Tuple" then TupleType.new(type_string)
|
40
|
+
else # A message type (eg. type_string = "KRPC.List" or "KRPC.Services")
|
41
|
+
raise(ValueError, "\"#{type_string}\" is not a valid type string") unless /^[A-Za-z0-9_\.]+$/ =~ type_string
|
42
|
+
if PROTOBUF_TO_MESSAGE_TYPE.has_key? type_string
|
43
|
+
MessageType.new(type_string)
|
44
|
+
else
|
45
|
+
raise(ValueError, "\"#{type_string}\" is not a valid type string")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
@cache[type_string] = type
|
50
|
+
type
|
51
|
+
end
|
33
52
|
|
34
|
-
type
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
elsif type_string.start_with? "Set(" || type_string == "Set" then SetType.new(type_string, self)
|
41
|
-
elsif type_string.start_with? "Tuple(" || type_string == "Tuple" then TupleType.new(type_string, self)
|
42
|
-
else # A message type (eg. type_string = "KRPC.List" or "KRPC.Services")
|
43
|
-
raise(ValueError, "\"#{type_string}\" is not a valid type string") unless /^[A-Za-z0-9_\.]+$/ =~ type_string
|
44
|
-
if PROTOBUF_TO_MESSAGE_TYPE.has_key? type_string
|
45
|
-
MessageType.new(type_string)
|
46
|
-
else
|
47
|
-
raise(ValueError, "\"#{type_string}\" is not a valid type string")
|
53
|
+
def get_parameter_type(pos, type, attrs)
|
54
|
+
type_attrs = Attributes.get_parameter_type_attrs(pos, attrs)
|
55
|
+
type_attrs.each do |ta|
|
56
|
+
begin
|
57
|
+
return self[ta]
|
58
|
+
rescue ValueError
|
48
59
|
end
|
49
60
|
end
|
61
|
+
self[type]
|
62
|
+
end
|
50
63
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
begin
|
59
|
-
return as_type(ta)
|
60
|
-
rescue ValueError
|
64
|
+
def get_return_type(type, attrs)
|
65
|
+
type_attrs = Attributes.get_return_type_attrs(attrs)
|
66
|
+
type_attrs.each do |ta|
|
67
|
+
begin
|
68
|
+
return self[ta]
|
69
|
+
rescue ValueError
|
70
|
+
end
|
61
71
|
end
|
72
|
+
self[type]
|
62
73
|
end
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
74
|
+
|
75
|
+
def coerce_to(value, type)
|
76
|
+
return value if type.is_a?(EnumType) && value.class == Symbol # Enum handling
|
77
|
+
return value if value.is_a?(type.ruby_type)
|
78
|
+
# A NilClass can be coerced to a ClassType
|
79
|
+
return nil if type.is_a?(ClassType) && value.nil?
|
80
|
+
# Handle service' class instance
|
81
|
+
if type.is_a?(ClassType) && value.is_a?(Gen::ClassBase) &&
|
82
|
+
type.ruby_type == value.class
|
83
|
+
return value
|
84
|
+
end
|
85
|
+
# -- Collection types --
|
69
86
|
begin
|
70
|
-
|
87
|
+
# coerce "list" to array
|
88
|
+
if type.is_a?(ListType) && value.respond_to?(:map) && value.respond_to?(:to_a)
|
89
|
+
return type.ruby_type.new(value.map{|x| coerce_to(x, type.value_type) }.to_a)
|
90
|
+
end
|
91
|
+
# coerce "tuple" to array + check elements count
|
92
|
+
if type.is_a?(TupleType) && value.respond_to?(:map) && value.respond_to?(:to_a) && value.respond_to?(:size)
|
93
|
+
raise ValueError if value.size != type.value_types.size
|
94
|
+
return type.ruby_type.new(value.map.with_index{|x,i| coerce_to(x, type.value_types[i]) }.to_a)
|
95
|
+
end
|
71
96
|
rescue ValueError
|
97
|
+
raise(ValueError, "Failed to coerce value #{value.to_s} of type #{value.class} to type #{type}")
|
72
98
|
end
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
return value if type.is_a?(EnumType) && value.class == Symbol # Enum handling
|
79
|
-
return value if value.is_a?(type.ruby_type)
|
80
|
-
# A NilClass can be coerced to a ClassType
|
81
|
-
return nil if type.is_a?(ClassType) && value == nil
|
82
|
-
# Coerce identical class types from different client connections
|
83
|
-
if type.is_a?(ClassType) && value.is_a?(Gen::ClassBase) &&
|
84
|
-
type.ruby_type.service_name == value.class.service_name &&
|
85
|
-
type.ruby_type.name == value.class.name
|
86
|
-
return type.ruby_type.new(value.remote_oid)
|
87
|
-
end
|
88
|
-
# -- Collection types --
|
89
|
-
begin
|
90
|
-
# coerce "list" to array
|
91
|
-
if type.is_a?(ListType) && value.respond_to?(:map) && value.respond_to?(:to_a)
|
92
|
-
return type.ruby_type.new(value.map{|x| coerce_to(x, type.value_type) }.to_a)
|
93
|
-
end
|
94
|
-
# coerce "tuple" to array + check elements count
|
95
|
-
if type.is_a?(TupleType) && value.respond_to?(:map) && value.respond_to?(:to_a) && value.respond_to?(:size)
|
96
|
-
raise ValueError if value.size != type.value_types.size
|
97
|
-
return type.ruby_type.new(value.map.with_index{|x,i| coerce_to(x, type.value_types[i]) }.to_a)
|
99
|
+
# Numeric types
|
100
|
+
if type.ruby_type == Float && value.respond_to?(:to_f)
|
101
|
+
return value.to_f
|
102
|
+
elsif type.ruby_type == Integer && value.respond_to?(:to_i)
|
103
|
+
return value.to_i
|
98
104
|
end
|
99
|
-
rescue ValueError
|
100
105
|
raise(ValueError, "Failed to coerce value #{value.to_s} of type #{value.class} to type #{type}")
|
101
106
|
end
|
102
|
-
# Numeric types
|
103
|
-
if type.ruby_type == Float && value.respond_to?(:to_f)
|
104
|
-
return value.to_f
|
105
|
-
elsif type.ruby_type == Integer && value.respond_to?(:to_i)
|
106
|
-
return value.to_i
|
107
|
-
end
|
108
|
-
raise(ValueError, "Failed to coerce value #{value.to_s} of type #{value.class} to type #{type}")
|
109
|
-
end
|
110
107
|
|
108
|
+
end
|
111
109
|
end
|
112
110
|
|
113
111
|
|
@@ -121,7 +119,7 @@ module KRPC
|
|
121
119
|
protected
|
122
120
|
|
123
121
|
def parse_type_string(type)
|
124
|
-
raise ValueError.new if type
|
122
|
+
raise ValueError.new if type.nil?
|
125
123
|
result = ""
|
126
124
|
level = 0
|
127
125
|
type.each_char do |x|
|
@@ -181,25 +179,25 @@ module KRPC
|
|
181
179
|
|
182
180
|
class ListType < TypeBase
|
183
181
|
attr_reader :value_type
|
184
|
-
def initialize(type_string
|
182
|
+
def initialize(type_string)
|
185
183
|
m = /^List\((.+)\)$/.match type_string
|
186
184
|
raise(ValueError, "\"#{type_string}\" is not a valid type string for a list type") unless m
|
187
|
-
@value_type =
|
185
|
+
@value_type = TypeStore[m[1]]
|
188
186
|
super(type_string, Array)
|
189
187
|
end
|
190
188
|
end
|
191
189
|
|
192
190
|
class DictionaryType < TypeBase
|
193
191
|
attr_reader :key_type, :value_type
|
194
|
-
def initialize(type_string
|
192
|
+
def initialize(type_string)
|
195
193
|
m = /^Dictionary\((.+)\)$/.match type_string
|
196
194
|
raise(ValueError, "\"#{type_string}\" is not a valid type string for a dictionary type") unless m
|
197
195
|
|
198
196
|
key_string, type = parse_type_string(m[1])
|
199
197
|
value_string, type = parse_type_string(type)
|
200
|
-
raise(ValueError, "\"#{type_string}\" is not a valid type string for a dictionary type")
|
201
|
-
@key_type =
|
202
|
-
@value_type =
|
198
|
+
raise(ValueError, "\"#{type_string}\" is not a valid type string for a dictionary type") unless type.nil?
|
199
|
+
@key_type = TypeStore[key_string]
|
200
|
+
@value_type = TypeStore[value_string]
|
203
201
|
|
204
202
|
super(type_string, Hash)
|
205
203
|
end
|
@@ -207,25 +205,25 @@ module KRPC
|
|
207
205
|
|
208
206
|
class SetType < TypeBase
|
209
207
|
attr_reader :value_type
|
210
|
-
def initialize(type_string
|
208
|
+
def initialize(type_string)
|
211
209
|
m = /^Set\((.+)\)$/.match type_string
|
212
210
|
raise(ValueError, "\"#{type_string}\" is not a valid type string for a set type") unless m
|
213
|
-
@value_type =
|
211
|
+
@value_type = TypeStore[m[1]]
|
214
212
|
super(type_string, Set)
|
215
213
|
end
|
216
214
|
end
|
217
215
|
|
218
216
|
class TupleType < TypeBase
|
219
217
|
attr_reader :value_types
|
220
|
-
def initialize(type_string
|
218
|
+
def initialize(type_string)
|
221
219
|
m = /^Tuple\((.+)\)$/.match type_string
|
222
220
|
raise(ValueError, "\"#{type_string}\" is not a valid type string for a tuple type") unless m
|
223
221
|
|
224
222
|
@value_types = []
|
225
223
|
type = m[1]
|
226
|
-
|
224
|
+
until type.nil?
|
227
225
|
value_type, type = parse_type_string(type)
|
228
|
-
@value_types <<
|
226
|
+
@value_types << TypeStore[value_type]
|
229
227
|
end
|
230
228
|
|
231
229
|
super(type_string, Array)
|
@@ -233,5 +231,6 @@ module KRPC
|
|
233
231
|
end
|
234
232
|
|
235
233
|
end
|
234
|
+
|
235
|
+
TypeStore = Types::TypeStore
|
236
236
|
end
|
237
|
-
|