krpc 0.1.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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
|
-
|