krpc 0.3.2 → 0.4.0.beta1
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/README.md +2 -4
- data/krpc.gemspec +6 -6
- data/lib/krpc.rb +2 -2
- data/lib/krpc/client.rb +54 -48
- data/lib/krpc/connection.rb +38 -33
- data/lib/krpc/core_extensions.rb +3 -3
- data/lib/krpc/decoder.rb +13 -20
- data/lib/krpc/doc.rb +29 -30
- data/lib/krpc/encoder.rb +19 -31
- data/lib/krpc/error.rb +2 -1
- data/lib/krpc/gen.rb +36 -37
- data/lib/krpc/krpc.pb.rb +87 -26
- data/lib/krpc/procedure_name_parser.rb +47 -0
- data/lib/krpc/protobuf_extensions.rb +22 -1
- data/lib/krpc/protobuf_utils.rb +28 -31
- data/lib/krpc/service.rb +44 -67
- data/lib/krpc/streaming.rb +30 -30
- data/lib/krpc/types.rb +73 -154
- data/lib/krpc/version.rb +8 -1
- metadata +17 -16
- data/lib/krpc/attributes.rb +0 -80
data/lib/krpc/streaming.rb
CHANGED
@@ -6,19 +6,20 @@ module KRPC
|
|
6
6
|
|
7
7
|
class StreamsManager
|
8
8
|
attr_reader :client
|
9
|
-
|
9
|
+
|
10
10
|
def initialize(client)
|
11
11
|
@client = client
|
12
12
|
@streams = {}
|
13
13
|
@streams_mutex = Mutex.new
|
14
14
|
@streaming_thread = Thread.new {}
|
15
15
|
end
|
16
|
-
|
17
|
-
# Send
|
16
|
+
|
17
|
+
# Send the streaming request, create related Stream object and return it. If identical Stream
|
18
18
|
# already exists, doesn't create new Stream and return the existing one.
|
19
|
-
def create_stream(
|
19
|
+
def create_stream(call, return_type, method, *args, **kwargs)
|
20
20
|
raise RuntimeError("Cannot stream a property setter") if method.name.to_s.end_with? '='
|
21
|
-
|
21
|
+
stream_msg = client.core.add_stream(call)
|
22
|
+
id = stream_msg.id
|
22
23
|
@streams_mutex.synchronize do
|
23
24
|
if @streams.include? id
|
24
25
|
@streams[id]
|
@@ -28,9 +29,9 @@ module KRPC
|
|
28
29
|
end
|
29
30
|
end
|
30
31
|
end
|
31
|
-
|
32
|
-
# Remove
|
33
|
-
# streaming request
|
32
|
+
|
33
|
+
# Remove the streaming request and deactivate the Stream object. Returns `true` if the
|
34
|
+
# streaming request has been removed or `false` if passed Stream object is already inactive.
|
34
35
|
def remove_stream(stream)
|
35
36
|
return false unless stream.active?
|
36
37
|
@streams_mutex.synchronize do
|
@@ -47,42 +48,41 @@ module KRPC
|
|
47
48
|
def remove_all_streams
|
48
49
|
@streams.each {|_,stream| remove_stream(stream)}
|
49
50
|
end
|
50
|
-
|
51
|
+
|
51
52
|
# Start streaming thread. It receives stream data, and updates Stream object's `value` attribute.
|
52
53
|
def start_streaming_thread
|
53
54
|
stop_streaming_thread
|
54
55
|
@streaming_thread = Thread.new do
|
55
56
|
connection = client.stream_connection
|
56
|
-
stream_message_type = TypeStore["KRPC.StreamMessage"]
|
57
57
|
loop do
|
58
58
|
size = connection.recv_varint
|
59
59
|
data = connection.recv(size)
|
60
|
-
stream_msg =
|
60
|
+
stream_msg = PB::StreamUpdate.decode(data)
|
61
61
|
@streams_mutex.synchronize do
|
62
|
-
stream_msg.
|
63
|
-
next unless @streams.include?
|
64
|
-
stream = @streams[
|
65
|
-
if
|
66
|
-
stream.value =
|
62
|
+
stream_msg.results.each do |result|
|
63
|
+
next unless @streams.include? result.id
|
64
|
+
stream = @streams[result.id]
|
65
|
+
if result.result.field_empty? :error
|
66
|
+
stream.value = Decoder.decode(result.result.value, stream.return_type, client)
|
67
67
|
else
|
68
|
-
stream.value =
|
68
|
+
stream.value = RPCError.new(result.result.error)
|
69
69
|
end
|
70
70
|
end
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
74
74
|
end
|
75
|
-
|
75
|
+
|
76
76
|
# Stop streaming thread.
|
77
77
|
def stop_streaming_thread
|
78
78
|
@streaming_thread.terminate
|
79
79
|
end
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
class Stream
|
83
83
|
attr_reader :id, :method, :args, :kwargs, :return_type, :manager
|
84
84
|
attr_writer :value
|
85
|
-
|
85
|
+
|
86
86
|
def initialize(manager, id, return_type, value, method, *args, **kwargs)
|
87
87
|
@manager = manager
|
88
88
|
@id = id
|
@@ -90,7 +90,7 @@ module KRPC
|
|
90
90
|
@method, @args, @kwargs = method, args, kwargs
|
91
91
|
@active = true
|
92
92
|
end
|
93
|
-
|
93
|
+
|
94
94
|
# Get the current stream value. Has alias method `value`.
|
95
95
|
def get
|
96
96
|
raise @value if @value.is_a?(Exception)
|
@@ -103,18 +103,18 @@ module KRPC
|
|
103
103
|
manager.remove_stream self
|
104
104
|
end
|
105
105
|
alias_method :close, :remove
|
106
|
-
|
106
|
+
|
107
107
|
# Check if stream is active (i.e. not removed).
|
108
108
|
def active?; @active end
|
109
109
|
|
110
110
|
# Mark stream as inactive.
|
111
111
|
# WARNING: This method does not remove the stream. To remove the stream call Stream#remove instead.
|
112
112
|
def mark_as_inactive; @active = false end
|
113
|
-
|
113
|
+
|
114
114
|
def to_s
|
115
115
|
inspect.gsub(/\n|\t/," ").squeeze(" ").uncolorize
|
116
116
|
end
|
117
|
-
|
117
|
+
|
118
118
|
def inspect
|
119
119
|
def coderay(x)
|
120
120
|
require 'coderay'
|
@@ -134,11 +134,11 @@ module KRPC
|
|
134
134
|
">".green
|
135
135
|
end
|
136
136
|
end
|
137
|
-
|
137
|
+
|
138
138
|
module StreamConstructors
|
139
139
|
STREAM_METHOD_SUFFIX = "_stream"
|
140
140
|
STREAM_METHOD_REGEX = /^(.+)(?:#{STREAM_METHOD_SUFFIX})$/
|
141
|
-
|
141
|
+
|
142
142
|
module ClassMethods
|
143
143
|
def stream_constructors
|
144
144
|
@stream_constructors ||= {}
|
@@ -149,7 +149,7 @@ module KRPC
|
|
149
149
|
base.extend ClassMethods
|
150
150
|
base.extend self
|
151
151
|
end
|
152
|
-
|
152
|
+
|
153
153
|
def method_missing(method, *args, **kwargs, &block)
|
154
154
|
if STREAM_METHOD_REGEX =~ method.to_s
|
155
155
|
if respond_to? $1.to_sym
|
@@ -159,7 +159,7 @@ module KRPC
|
|
159
159
|
end
|
160
160
|
super
|
161
161
|
end
|
162
|
-
|
162
|
+
|
163
163
|
def respond_to_missing?(method, *)
|
164
164
|
if STREAM_METHOD_REGEX =~ method.to_s
|
165
165
|
if respond_to? $1.to_sym
|
@@ -169,8 +169,8 @@ module KRPC
|
|
169
169
|
end
|
170
170
|
super
|
171
171
|
end
|
172
|
-
|
172
|
+
|
173
173
|
end
|
174
|
-
|
174
|
+
|
175
175
|
end
|
176
176
|
end
|
data/lib/krpc/types.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'krpc/gen'
|
2
|
-
require 'krpc/attributes'
|
3
2
|
require 'krpc/protobuf_utils'
|
4
3
|
require 'krpc/error'
|
5
4
|
require 'krpc/core_extensions'
|
@@ -7,78 +6,40 @@ require 'set'
|
|
7
6
|
|
8
7
|
module KRPC
|
9
8
|
module Types
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
"string" => String,
|
21
|
-
"bytes" => Array
|
9
|
+
PROTOBUF_TO_RUBY_VALUE_TYPES = {
|
10
|
+
DOUBLE: Float,
|
11
|
+
FLOAT: Float,
|
12
|
+
SINT32: Integer,
|
13
|
+
SINT64: Integer,
|
14
|
+
UINT32: Integer,
|
15
|
+
UINT64: Integer,
|
16
|
+
BOOL: Boolean,
|
17
|
+
STRING: String,
|
18
|
+
BYTES: Array
|
22
19
|
}
|
23
|
-
|
24
|
-
|
20
|
+
PROTOBUF_TO_RUBY_MESSAGE_TYPES = {
|
21
|
+
PROCEDURE_CALL: PB::ProcedureCall,
|
22
|
+
STREAM: PB::Stream,
|
23
|
+
STATUS: PB::Status,
|
24
|
+
SERVICES: PB::Services
|
25
|
+
}
|
26
|
+
|
27
|
+
|
25
28
|
class TypeStore
|
26
29
|
@cache = {}
|
27
30
|
class << self
|
28
|
-
|
29
|
-
def [](
|
30
|
-
|
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
|
52
|
-
|
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
|
59
|
-
end
|
60
|
-
end
|
61
|
-
self[type]
|
62
|
-
end
|
63
|
-
|
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
|
71
|
-
end
|
72
|
-
self[type]
|
31
|
+
|
32
|
+
def [](protobuf_type)
|
33
|
+
@cache[protobuf_type.to_proto.hash] ||= PROTOBUF_TYPE_CODE_TO_TYPE_TYPE[protobuf_type.code].new(protobuf_type)
|
73
34
|
end
|
74
|
-
|
35
|
+
|
75
36
|
def coerce_to(value, type)
|
76
37
|
return value if type.is_a?(EnumType) && value.class == Symbol # Enum handling
|
77
38
|
return value if value.is_a?(type.ruby_type)
|
78
39
|
# A NilClass can be coerced to a ClassType
|
79
40
|
return nil if type.is_a?(ClassType) && value.nil?
|
80
41
|
# Handle service' class instance
|
81
|
-
if type.is_a?(ClassType) && value.is_a?(Gen::ClassBase) &&
|
42
|
+
if type.is_a?(ClassType) && value.is_a?(Gen::ClassBase) &&
|
82
43
|
type.ruby_type == value.class
|
83
44
|
return value
|
84
45
|
end
|
@@ -103,138 +64,96 @@ module KRPC
|
|
103
64
|
return value.to_i
|
104
65
|
end
|
105
66
|
# Convert value type to string
|
106
|
-
if type.
|
67
|
+
if type.ruby_type == String
|
107
68
|
return value.to_s
|
108
69
|
end
|
109
70
|
raise(ValueError, "Failed to coerce value #{value.to_s} of type #{value.class} to type #{type}")
|
110
71
|
end
|
111
|
-
|
72
|
+
|
112
73
|
end
|
113
74
|
end
|
114
|
-
|
115
|
-
|
75
|
+
|
76
|
+
|
116
77
|
class TypeBase
|
117
78
|
attr_reader :protobuf_type, :ruby_type
|
118
79
|
def initialize(protobuf_type, ruby_type)
|
119
80
|
@protobuf_type = protobuf_type
|
120
81
|
@ruby_type = ruby_type
|
121
82
|
end
|
122
|
-
|
123
|
-
protected
|
124
|
-
|
125
|
-
def parse_type_string(type)
|
126
|
-
raise ValueError.new if type.nil?
|
127
|
-
result = ""
|
128
|
-
level = 0
|
129
|
-
type.each_char do |x|
|
130
|
-
break if level == 0 && x == ','
|
131
|
-
level += 1 if x == '('
|
132
|
-
level -= 1 if x == ')'
|
133
|
-
result += x
|
134
|
-
end
|
135
|
-
raise ValueError.new if level != 0
|
136
|
-
return [result, nil] if result == type
|
137
|
-
raise ValueError.new if type[result.length] != ','
|
138
|
-
[result, type[(result.length+1)..-1]]
|
139
|
-
end
|
140
83
|
end
|
141
|
-
|
84
|
+
|
142
85
|
class ValueType < TypeBase
|
143
|
-
def initialize(
|
144
|
-
raise(ValueError, "
|
145
|
-
super(type_string, PROTOBUF_TO_RUBY_VALUE_TYPE[type_string])
|
86
|
+
def initialize(pb_type)
|
87
|
+
super(pb_type, PROTOBUF_TO_RUBY_VALUE_TYPES[pb_type.code] || raise(ValueError, "#{pb_type.code} is not a valid type code for a value type"))
|
146
88
|
end
|
147
89
|
end
|
148
|
-
|
149
|
-
class MessageType < TypeBase
|
150
|
-
def initialize(type_string)
|
151
|
-
if PROTOBUF_TO_MESSAGE_TYPE.has_key? type_string
|
152
|
-
super(type_string, PROTOBUF_TO_MESSAGE_TYPE[type_string])
|
153
|
-
else
|
154
|
-
raise(ValueError, "\"#{type_string}\" is not a valid type string for a message type")
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
90
|
+
|
159
91
|
class ClassType < TypeBase
|
160
92
|
attr_reader :service_name, :class_name
|
161
|
-
def initialize(
|
162
|
-
|
163
|
-
|
164
|
-
@service_name, @class_name = m[1..2]
|
165
|
-
super(type_string, Gen.generate_class(service_name, class_name))
|
93
|
+
def initialize(pb_type)
|
94
|
+
@service_name, @class_name = pb_type.service, pb_type.name
|
95
|
+
super(pb_type, Gen.generate_class(service_name, class_name))
|
166
96
|
end
|
167
97
|
end
|
168
|
-
|
98
|
+
|
169
99
|
class EnumType < TypeBase
|
170
100
|
attr_reader :service_name, :enum_name
|
171
|
-
def initialize(
|
172
|
-
|
173
|
-
raise(ValueError, "\"#{type_string}\" is not a valid type string for a enumeration type") unless m
|
174
|
-
@service_name, @enum_name = m[1..2]
|
101
|
+
def initialize(pb_type)
|
102
|
+
@service_name, @enum_name = pb_type.service, pb_type.name
|
175
103
|
# Sets ruby_type to nil, set_values must be called to set the ruby_type
|
176
|
-
super(
|
104
|
+
super(pb_type, nil)
|
177
105
|
end
|
178
|
-
|
106
|
+
|
179
107
|
def set_values(values)
|
180
108
|
@ruby_type = Gen.generate_enum(service_name, enum_name, values)
|
181
109
|
end
|
182
110
|
end
|
183
|
-
|
111
|
+
|
184
112
|
class ListType < TypeBase
|
185
113
|
attr_reader :value_type
|
186
|
-
def initialize(
|
187
|
-
|
188
|
-
|
189
|
-
@value_type = TypeStore[m[1]]
|
190
|
-
super(type_string, Array)
|
114
|
+
def initialize(pb_type)
|
115
|
+
@value_type = TypeStore[pb_type.types.first]
|
116
|
+
super(pb_type, Array)
|
191
117
|
end
|
192
118
|
end
|
193
|
-
|
194
|
-
class DictionaryType < TypeBase
|
195
|
-
attr_reader :key_type, :value_type
|
196
|
-
def initialize(type_string)
|
197
|
-
m = /^Dictionary\((.+)\)$/.match type_string
|
198
|
-
raise(ValueError, "\"#{type_string}\" is not a valid type string for a dictionary type") unless m
|
199
|
-
|
200
|
-
key_string, type = parse_type_string(m[1])
|
201
|
-
value_string, type = parse_type_string(type)
|
202
|
-
raise(ValueError, "\"#{type_string}\" is not a valid type string for a dictionary type") unless type.nil?
|
203
|
-
@key_type = TypeStore[key_string]
|
204
|
-
@value_type = TypeStore[value_string]
|
205
|
-
|
206
|
-
super(type_string, Hash)
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
119
|
+
|
210
120
|
class SetType < TypeBase
|
211
121
|
attr_reader :value_type
|
212
|
-
def initialize(
|
213
|
-
|
214
|
-
|
215
|
-
@value_type = TypeStore[m[1]]
|
216
|
-
super(type_string, Set)
|
122
|
+
def initialize(pb_type)
|
123
|
+
@value_type = TypeStore[pb_type.types.first]
|
124
|
+
super(pb_type, Set)
|
217
125
|
end
|
218
126
|
end
|
219
|
-
|
127
|
+
|
220
128
|
class TupleType < TypeBase
|
221
129
|
attr_reader :value_types
|
222
|
-
def initialize(
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
@value_types = []
|
227
|
-
type = m[1]
|
228
|
-
until type.nil?
|
229
|
-
value_type, type = parse_type_string(type)
|
230
|
-
@value_types << TypeStore[value_type]
|
231
|
-
end
|
232
|
-
|
233
|
-
super(type_string, Array)
|
130
|
+
def initialize(pb_type)
|
131
|
+
@value_types = pb_type.types.map {|t| TypeStore[t] }
|
132
|
+
super(pb_type, Array)
|
234
133
|
end
|
235
134
|
end
|
236
|
-
|
135
|
+
|
136
|
+
class DictionaryType < TypeBase
|
137
|
+
attr_reader :key_type, :value_type
|
138
|
+
def initialize(pb_type)
|
139
|
+
@key_type, @value_type = pb_type.types.map {|t| TypeStore[t] }
|
140
|
+
super(pb_type, Hash)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class MessageType < TypeBase
|
145
|
+
def initialize(pb_type)
|
146
|
+
super(pb_type, PROTOBUF_TO_RUBY_MESSAGE_TYPES[pb_type.code] || raise(ValueError, "\"#{pb_type.code}\" is not a valid type code for a message type"))
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
PROTOBUF_TYPE_CODE_TO_TYPE_TYPE =
|
151
|
+
PROTOBUF_TO_RUBY_VALUE_TYPES.keys.inject({}) {|a,e| a[e] = ValueType; a }.merge(
|
152
|
+
PROTOBUF_TO_RUBY_MESSAGE_TYPES.keys.inject({}) {|a,e| a[e] = MessageType; a }).merge(
|
153
|
+
CLASS: ClassType, ENUMERATION: EnumType,
|
154
|
+
LIST: ListType, SET: SetType, TUPLE: TupleType, DICTIONARY: DictionaryType
|
155
|
+
)
|
237
156
|
end
|
238
|
-
|
157
|
+
|
239
158
|
TypeStore = Types::TypeStore
|
240
159
|
end
|