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.
@@ -1,47 +1,48 @@
1
1
  require 'krpc/core_extensions'
2
+ require 'nokogiri'
2
3
  require 'colorize'
3
4
 
4
5
  module KRPC
5
6
  module Doc
7
+ @docstr_infos = {}
8
+ @procedure_docstr_infos = {}
6
9
  class << self
7
- @@docstr_infos = {}
8
- @@procedure_docstr_infos = {}
9
10
 
10
- def docstring_for_method(method_owner, method_name)
11
+ def docstring_for_method(method_owner, method_name, is_print_xmldoc_summary = true)
11
12
  is_static, class_cls = method_owner.class == Class ? [true, method_owner] : [false, method_owner.class]
12
13
  service_module_name, class_name = ruby_class_to_pb_module_class_pair(class_cls)
13
14
  key = [service_module_name, is_static, class_name, method_name.to_s].hash
14
- if (@@docstr_infos.has_key? key)
15
- construct_docstring(*@@docstr_infos[key], true)
15
+ if @docstr_infos.has_key? key
16
+ construct_docstring(*@docstr_infos[key], true, is_print_xmldoc_summary)
16
17
  else
17
18
  "No docstring for #{class_cls.name}#{calc_separator(is_static)}#{method_name.to_s} method" +
18
- (method_owner.respond_to?(method_name) ? "" : "\nThere is no such method -- maybe a typo")
19
+ (method_owner.respond_to?(method_name) ? "" : "\nThere is no such method -- maybe a typo")
19
20
  end
20
21
  end
21
22
 
22
- def docstring_for_procedure(service_name, procedure_name)
23
+ def docstring_for_procedure(service_name, procedure_name, is_print_xmldoc_summary = true)
23
24
  key = [service_name, procedure_name].hash
24
- if (@@procedure_docstr_infos.has_key? key)
25
- construct_docstring(service_name, '.', procedure_name, *@@procedure_docstr_infos[key][3..-1], false)
25
+ if @procedure_docstr_infos.has_key? key
26
+ construct_docstring(service_name, '.', procedure_name, *@procedure_docstr_infos[key][3..-1], false, is_print_xmldoc_summary)
26
27
  else
27
28
  "No docstring for #{service_name}.#{procedure_name} procedure"
28
29
  end
29
30
  end
30
31
 
31
- def add_docstring_info(is_static, cls, method_name, service_name="", procedure_name="", param_names=[], param_types=[], param_default=[], return_type: nil)
32
+ def add_docstring_info(is_static, cls, method_name, service_name="", procedure_name="", param_names=[], param_types=[], param_default=[], return_type: nil, xmldoc: "")
32
33
  service_module_name = service_name == cls.class_name ? Services.class_name : service_name
33
34
  key0 = [service_name, procedure_name].hash
34
35
  key1 = [service_module_name, false, cls.class_name, method_name].hash
35
- val = [cls.krpc_name, calc_separator(is_static), method_name, param_names, param_types, param_default, return_type]
36
- @@docstr_infos[key1] = @@procedure_docstr_infos[key0] = val
36
+ val = [cls.krpc_name, calc_separator(is_static), method_name, param_names, param_types, param_default, return_type, xmldoc]
37
+ @docstr_infos[key1] = @procedure_docstr_infos[key0] = val
37
38
  if is_static
38
39
  key2 = [service_module_name, true, cls.class_name, method_name].hash
39
- @@docstr_infos[key2] = val
40
+ @docstr_infos[key2] = val
40
41
  end
41
42
  end
42
43
 
43
44
  def add_special_docstring_info(key, value)
44
- @@docstr_infos[key] = value
45
+ @docstr_infos[key] = value
45
46
  end
46
47
 
47
48
  private #----------------------------------
@@ -56,36 +57,106 @@ module KRPC
56
57
  def calc_separator(is_static)
57
58
  is_static ? '.' : '#'
58
59
  end
60
+
61
+ def construct_docstring(namespace, separator, name, param_names, param_types, param_default, return_type, xmldoc, is_hide_this_param, is_print_xmldoc_summary)
62
+ xmldoc = Nokogiri::XML(xmldoc)
63
+ xmldoc_summary = xmlElements2str(xmldoc.xpath("doc/summary").children, :light_blue, :light_green, :light_red)
64
+
65
+ xmldoc_returns = xmlElements2str(xmldoc.xpath("doc/returns").children, :blue, :green, :light_red)
66
+ xmldoc_returns = "- ".blue + xmldoc_returns unless xmldoc_returns.empty?
59
67
 
60
- def construct_docstring(namespace, separator, name, param_names, param_types, param_default, return_type, is_hide_this_param)
61
- def type2str(type)
62
- return "nil" if type.nil?
63
- return type.class_name if type.class == Class
64
- rt = type.ruby_type
65
- if type.is_a?(Types::EnumType) then "Enum" + rt.keys.to_s
66
- elsif type.is_a?(Types::ListType) ||
67
- type.is_a?(Types::SetType)
68
- "#{rt.class_name}[#{type2str(type.value_type)}]"
69
- elsif type.is_a?(Types::DictionaryType)
70
- %Q{#{rt.class_name}[#{type2str(type.key_type)} => #{type2str(type.value_type)}]}
71
- elsif type.is_a?(Types::TupleType)
72
- %Q{#{rt.class_name}[#{type.value_types.map{|x| type2str(x)}.join(", ")}]}
73
- else rt.class_name end
68
+ xmldoc_params = {}
69
+ xmldoc.xpath("doc/param").each do |e|
70
+ param_name = e.attr("name")
71
+ if param_name
72
+ unless e.text.strip.empty?
73
+ xmldoc_params[param_name.underscore] = " - ".blue + xmlElements2str(e.children, :blue, :green, :yellow)
74
+ else
75
+ xmldoc_params[param_name.underscore] = ""
76
+ end
77
+ end
74
78
  end
79
+
75
80
  param_infos = param_names.zip(param_types.map{|x| type2str(x)}, param_default)
76
81
  param_infos.shift if is_hide_this_param && param_names[0] == "this"
77
82
  if param_infos.empty?
78
83
  params = ""
79
84
  else
80
- params = "\n" + param_infos.map do |pi|
81
- "\t#{pi[0].light_green} :#{pi[1]}" + (pi[2].nil? ? "" : " = #{pi[2]}".magenta)
82
- end.join(",\n") + "\n"
85
+ params = "\n" + param_infos.map.with_index do |pi, i|
86
+ "\t#{pi[0].light_green} :#{pi[1]}" + (pi[2].nil? ? "" : " = #{pi[2]}".magenta) \
87
+ + (param_infos.size == i+1 ? "" : ",") \
88
+ + xmldoc_params[pi[0]].to_s
89
+ end.join("\n") + "\n"
90
+ end
91
+ "#{namespace.cyan}#{separator.cyan}#{name.bold}(#{params}) :#{type2str(return_type).light_red} #{xmldoc_returns}" \
92
+ + (is_print_xmldoc_summary ? "\n\n#{xmldoc_summary}" : "")
93
+ end
94
+
95
+ def type2str(type)
96
+ return "nil" if type.nil?
97
+ return type.class_name if type.class == Class
98
+ rt = type.ruby_type
99
+ if type.is_a?(Types::EnumType) then "Enum" + rt.keys.to_s
100
+ elsif type.is_a?(Types::ListType) ||
101
+ type.is_a?(Types::SetType)
102
+ "#{rt.class_name}[#{type2str(type.value_type)}]"
103
+ elsif type.is_a?(Types::DictionaryType)
104
+ %Q{#{rt.class_name}[#{type2str(type.key_type)} => #{type2str(type.value_type)}]}
105
+ elsif type.is_a?(Types::TupleType)
106
+ %Q{#{rt.class_name}[#{type.value_types.map{|x| type2str(x)}.join(", ")}]}
107
+ else rt.class_name end
108
+ end
109
+
110
+ def xmlElements2str(elements, main_color, paramref_color, value_color, error_color = :red)
111
+ elements.map do |elem|
112
+ if elem.is_a?(Nokogiri::XML::Text)
113
+ elem.text.colorize(main_color)
114
+ else
115
+ begin
116
+ case elem.name
117
+ when "paramref" then elem.attr("name").underscore.colorize(paramref_color)
118
+ when "a", "math" then elem.text.colorize(main_color)
119
+ when "see"
120
+ type, _, cref = elem.attr("cref").rpartition(':')
121
+ cref = cref.split('.')
122
+ if type == 'T'
123
+ cref.join("::").colorize(value_color)
124
+ elsif type == 'M'
125
+ begin
126
+ val = cref[2].underscore.to_sym
127
+ raise RuntimeException unless ::KRPC::Gen.const_get(cref[0], false).const_get(cref[1], false)[val].is_a?(Integer)
128
+ (':' + val.to_s).colorize(value_color)
129
+ rescue
130
+ raise RuntimeException unless is_method_defined(["Services"] + cref) || is_method_defined(["Gen"] + cref)
131
+ (cref[0..-2].join("::") + "#").cyan + cref[-1].underscore
132
+ end
133
+ else raise RuntimeException end
134
+ when "list"
135
+ "!n!" + elem.children.map{|ch| "!s! * ".bold + ch.text}.join("!n!").colorize(main_color)
136
+ when "c"
137
+ elem.text.gsub("null","nil").colorize(value_color)
138
+ else elem.to_s.colorize(error_color)
139
+ end
140
+ rescue RuntimeException => exc
141
+ elem.to_s.colorize(error_color)
142
+ end
143
+ end
144
+ end.join.gsub("\n"," ").gsub("!n!","\n").squeeze(' ').strip.gsub("!s!"," ")
145
+ end
146
+
147
+ def is_method_defined(path_array)
148
+ method_name = path_array[-1].underscore
149
+ begin
150
+ cls = path_array[0..-2].inject(::KRPC){|a,e| a.const_get(e, false) }
151
+ cls.method_defined?(method_name)
152
+ rescue NameError
153
+ false
83
154
  end
84
- "#{namespace.cyan}#{separator.cyan}#{name.bold}(#{params}) :#{type2str(return_type).light_red}"
85
155
  end
86
156
 
87
157
  end
88
158
 
159
+
89
160
  module SuffixMethods
90
161
  DOCSTRING_SUFFIX = "_doc"
91
162
  DOCSTRING_SUFFIX_REGEX = /^(.+)(?:#{DOCSTRING_SUFFIX}(=)?)$/
@@ -119,4 +190,3 @@ module KRPC
119
190
 
120
191
  end
121
192
  end
122
-
@@ -9,36 +9,36 @@ module KRPC
9
9
  class << self
10
10
 
11
11
  # Given a type object, and ruby object, encode the ruby object
12
- def encode(obj, type, type_store)
12
+ def encode(obj, type)
13
13
  if type.is_a?(Types::MessageType) then obj.serialize_to_string
14
14
  elsif type.is_a?(Types::ValueType) then encode_value(obj, type)
15
15
  elsif type.is_a?(Types::EnumType)
16
16
  enum_value = type.ruby_type[obj]
17
- encode_value(enum_value, type_store.as_type("int32"))
17
+ encode_value(enum_value, TypeStore["int32"])
18
18
  elsif type.is_a?(Types::ClassType)
19
19
  remote_oid = if obj == nil then 0 else obj.remote_oid end
20
- encode_value(remote_oid, type_store.as_type("uint64"))
20
+ encode_value(remote_oid, TypeStore["uint64"])
21
21
  elsif type.is_a?(Types::ListType)
22
- msg = type_store.as_type("KRPC.List").ruby_type.new
23
- msg.items = obj.map{|x| encode(x, type.value_type, type_store)}.to_a
22
+ msg = TypeStore["KRPC.List"].ruby_type.new
23
+ msg.items = obj.map{|x| encode(x, type.value_type)}.to_a
24
24
  msg.serialize_to_string
25
25
  elsif type.is_a?(Types::DictionaryType)
26
- entry_type = type_store.as_type("KRPC.DictionaryEntry").ruby_type
27
- msg = type_store.as_type("KRPC.Dictionary").ruby_type.new
26
+ entry_type = TypeStore["KRPC.DictionaryEntry"].ruby_type
27
+ msg = TypeStore["KRPC.Dictionary"].ruby_type.new
28
28
  msg.entries = obj.map do |k,v|
29
29
  entry = entry_type.new
30
- entry.key = encode(k, type.key_type, type_store)
31
- entry.value = encode(v, type.value_type, type_store)
30
+ entry.key = encode(k, type.key_type)
31
+ entry.value = encode(v, type.value_type)
32
32
  entry
33
33
  end
34
34
  msg.serialize_to_string
35
35
  elsif type.is_a?(Types::SetType)
36
- msg = type_store.as_type("KRPC.Set").ruby_type.new
37
- msg.items = obj.map{|x| encode(x, type.value_type, type_store)}.to_a
36
+ msg = TypeStore["KRPC.Set"].ruby_type.new
37
+ msg.items = obj.map{|x| encode(x, type.value_type)}.to_a
38
38
  msg.serialize_to_string
39
39
  elsif type.is_a?(Types::TupleType)
40
- msg = type_store.as_type("KRPC.Tuple").ruby_type.new
41
- msg.items = obj.zip(type.value_types).map{|x,t| encode(x, t, type_store)}.to_a
40
+ msg = TypeStore["KRPC.Tuple"].ruby_type.new
41
+ msg.items = obj.zip(type.value_types).map{|x,t| encode(x, t)}.to_a
42
42
  msg.serialize_to_string
43
43
  else raise(RuntimeError, "Cannot encode object #{obj} of type #{type}")
44
44
  end
@@ -50,11 +50,10 @@ module KRPC
50
50
 
51
51
  def encode_request(req)
52
52
  data = req.serialize_to_string
53
- length = ProtobufUtils::Encoder.encode_nonnegative_varint(data.length)
53
+ length = ProtobufUtils::Encoder.encode_nonnegative_varint(data.length)
54
54
  length + data
55
55
  end
56
56
 
57
57
  end
58
58
  end
59
59
  end
60
-
@@ -8,10 +8,12 @@ module KRPC
8
8
 
9
9
  class ArgumentErrorSig < ArgumentError
10
10
  attr_reader :message_without_signature, :signature
11
+
11
12
  def initialize(msg = nil, sig = nil)
12
13
  @message_without_signature, @signature = msg, sig
13
14
  super(signature.nil? ? msg : msg + "\n" + signature.to_s)
14
15
  end
16
+
15
17
  def with_signature(sig)
16
18
  self.class.new(message_without_signature, sig)
17
19
  end
@@ -19,15 +21,16 @@ module KRPC
19
21
 
20
22
  class ArgumentsNumberErrorSig < ArgumentErrorSig
21
23
  attr_reader :args_count, :valid_params_count_range
24
+
22
25
  def initialize(args_count, valid_params_count_range, sig = nil)
23
26
  @args_count, @valid_params_count_range = args_count, valid_params_count_range
24
27
  valid_params_str = (valid_params_count_range.min == valid_params_count_range.max ? valid_params_count_range.min : valid_params_count_range).to_s
25
28
  super("wrong number of arguments (#{args_count} for #{valid_params_str})", sig)
26
29
  end
30
+
27
31
  def with_signature(sig)
28
32
  self.class.new(args_count, valid_params_count_range, sig)
29
33
  end
30
34
  end
31
35
 
32
36
  end
33
-
@@ -1,10 +1,12 @@
1
1
  require 'krpc/doc'
2
+ require 'krpc/streaming'
2
3
  require 'krpc/core_extensions'
4
+ require 'colorize'
3
5
 
4
6
  module KRPC
5
7
  module Gen
6
8
  AvailableToClassAndInstanceModuleName = "AvailableToClassAndInstance"
7
-
9
+
8
10
  class << self
9
11
  def service_gen_module(service_name)
10
12
  const_get_or_create(service_name, Module.new)
@@ -14,8 +16,8 @@ module KRPC
14
16
  mod = service_gen_module(service_name)
15
17
  mod.const_get_or_create(class_name) do
16
18
  Class.new(ClassBase) do
17
- attr_reader :service_name
18
19
  @service_name = service_name
20
+ class << self; attr_reader :service_name end
19
21
  end
20
22
  end
21
23
  end
@@ -27,56 +29,72 @@ module KRPC
27
29
  end
28
30
  end
29
31
 
30
- def add_rpc_method(cls, method_name, service_name, proc, client, *options)
32
+ def add_rpc_method(cls, method_name, service_name, proc, *options)
31
33
  is_static = options.include? :static
32
34
  prepend_self_to_args = options.include? :prepend_self_to_args
33
35
  target_module = is_static ? cls.const_get_or_create(AvailableToClassAndInstanceModuleName, Module.new) : cls
34
- param_names, param_types, required_params_count, param_default, return_type = parse_procedure(proc, client)
36
+ param_names, param_types, param_default, return_type = parse_procedure(proc)
35
37
  method_name = method_name.underscore
36
-
38
+
39
+ transform_exceptions = Proc.new do |method_owner, &block|
40
+ begin
41
+ block.call
42
+ rescue ArgumentsNumberErrorSig => err
43
+ sig = Doc.docstring_for_method(method_owner, method_name, false)
44
+ if prepend_self_to_args then raise ArgumentsNumberErrorSig.new(err.args_count - 1, (err.valid_params_count_range.min-1)..(err.valid_params_count_range.max-1), sig)
45
+ else raise err.with_signature(sig) end
46
+ rescue ArgumentErrorSig => err
47
+ raise err.with_signature(Doc.docstring_for_method(method_owner, method_name, false))
48
+ end
49
+ end
50
+
51
+ # Define method
37
52
  target_module.instance_eval do
38
- define_method method_name do |*args, **kwargs|
39
- begin
53
+ define_method method_name do |*args|
54
+ transform_exceptions.call(self) do
55
+ kwargs = args.extract_kwargs!
40
56
  args = [self] + args if prepend_self_to_args
41
- client.rpc(service_name, proc.name, args, kwargs, param_names, param_types, required_params_count, param_default, return_type: return_type)
42
- rescue ArgumentsNumberErrorSig => err
43
- sig = Doc.docstring_for_method(self, method_name)
44
- if prepend_self_to_args then raise ArgumentsNumberErrorSig.new(err.args_count - 1, (err.valid_params_count_range.min-1)..(err.valid_params_count_range.max-1), sig)
45
- else raise err.with_signature(sig) end
46
- rescue ArgumentErrorSig => err
47
- raise err.with_signature(Doc.docstring_for_method(self, method_name))
57
+ self.client.rpc(service_name, proc.name, args, kwargs, param_names, param_types, param_default, return_type: return_type)
48
58
  end
49
59
  end
50
60
  end
51
- Doc.add_docstring_info(is_static, cls, method_name, service_name, proc, param_names, param_types, param_default, return_type: return_type)
61
+ # Add stream-constructing Proc
62
+ unless options.include? :no_stream
63
+ cls.stream_constructors[method_name] = Proc.new do |this, *args, **kwargs|
64
+ transform_exceptions.call(this) do
65
+ req_args = prepend_self_to_args ? [this] + args : args
66
+ request = this.client.build_request(service_name, proc.name, req_args, kwargs, param_names, param_types, param_default)
67
+ this.client.streams_manager.create_stream(request, return_type, this.method(method_name), *args, **kwargs)
68
+ end
69
+ end
70
+ end
71
+ # Add docstring info
72
+ Doc.add_docstring_info(is_static, cls, method_name, service_name, proc.name, param_names, param_types, param_default, return_type: return_type, xmldoc: proc.documentation)
52
73
  end
53
74
 
54
75
  private #----------------------------------
55
76
 
56
- def parse_procedure(proc, client)
77
+ def parse_procedure(proc)
57
78
  param_names = proc.parameters.map{|p| p.name.underscore}
58
79
  param_types = proc.parameters.map.with_index do |p,i|
59
- client.type_store.get_parameter_type(i, p.type, proc.attributes)
80
+ TypeStore.get_parameter_type(i, p.type, proc.attributes)
60
81
  end
61
- param_required = proc.parameters.map{|p| not p.has_field?("default_argument")}
62
- required_params_count = param_required.take_while{|x| x}.size
63
82
  param_default = proc.parameters.zip(param_types).map do |param, type|
64
83
  if param.has_field?("default_argument")
65
- Decoder.decode(param.default_argument, type, client.type_store)
84
+ Decoder.decode(param.default_argument, type, :clientless)
66
85
  else nil
67
86
  end
68
87
  end
69
88
  return_type = if proc.has_field?("return_type")
70
- client.type_store.get_return_type(proc.return_type, proc.attributes)
71
- else nil
72
- end
73
- [param_names, param_types, required_params_count, param_default, return_type]
89
+ TypeStore.get_return_type(proc.return_type, proc.attributes)
90
+ else nil end
91
+ [param_names, param_types, param_default, return_type]
74
92
  end
75
93
  end
76
94
 
77
95
  module RPCMethodGenerator
78
- def include_rpc_method(method_name, service_name, procedure_name, return_type: nil)
79
- Gen.add_rpc_method(self.class, method_name, service_name, PB::Procedure.new(name: procedure_name, return_type: return_type), client)
96
+ def include_rpc_method(method_name, service_name, procedure_name, params: [], return_type: nil, xmldoc: "", options: [])
97
+ Gen.add_rpc_method(self.class, method_name, service_name, PB::Procedure.new(name: procedure_name, parameters: params, return_type: return_type, documentation: xmldoc), options)
80
98
  end
81
99
  end
82
100
 
@@ -94,11 +112,16 @@ module KRPC
94
112
  class ClassBase
95
113
  extend AvailableToClassAndInstanceMethodsHandler
96
114
  include Doc::SuffixMethods
115
+ include Streaming::StreamConstructors
97
116
 
98
- attr_reader :remote_oid
117
+ attr_reader :client, :remote_oid
99
118
 
100
- def initialize(remote_oid)
101
- @remote_oid = remote_oid
119
+ def self.krpc_name
120
+ name[11..-1]
121
+ end
122
+
123
+ def initialize(client, remote_oid)
124
+ @client, @remote_oid = client, remote_oid
102
125
  end
103
126
 
104
127
  alias_method :eql?, :==
@@ -109,11 +132,14 @@ module KRPC
109
132
  remote_oid.hash
110
133
  end
111
134
 
112
- def self.krpc_name
113
- name[11..-1]
135
+ def to_s
136
+ "#<#{self.class} @remote_oid=#{remote_oid}>"
137
+ end
138
+
139
+ def inspect
140
+ "#<#{self.class} ".green + "@remote_oid" + "=".green + remote_oid.to_s.bold.blue + ">".green
114
141
  end
115
142
  end
116
143
 
117
144
  end
118
145
  end
119
-
@@ -4,11 +4,11 @@ module KRPC
4
4
  module ProtobufUtils
5
5
  class << self
6
6
  def create_PB_to_PB_message_class_hash(package)
7
- protobuf_module = Kernel.const_get(package.gsub(".","::") + "::PB")
8
- msg_classes_names = protobuf_module.constants.select{|c| protobuf_module.const_get(c,false).superclass == ::Protobuf::Message}
9
- msg_classes_names.map do |name|
10
- [package + "." + name.to_s, protobuf_module.const_get(name,false)]
11
- end.to_h
7
+ protobuf_module = Kernel.const_get(package.gsub(".","::") + "::PB")
8
+ msg_classes_names = protobuf_module.constants.select{|c| protobuf_module.const_get(c,false).superclass == ::Protobuf::Message}
9
+ msg_classes_names.map do |name|
10
+ [package + "." + name.to_s, protobuf_module.const_get(name,false)]
11
+ end.to_h
12
12
  end
13
13
  end
14
14
 
@@ -29,7 +29,7 @@ module KRPC
29
29
  result = 0
30
30
  shift = 0
31
31
  loop do
32
- byte = bytes[pos].ord # <-- replace ".ord" with ".bytes[0]" if ".ord" start to cause problems again
32
+ byte = bytes[pos].ord
33
33
  pos += 1
34
34
  result |= (byte & 0b0111_1111) << shift
35
35
  return [result, pos] if (byte & 0b1000_0000) == 0
@@ -129,5 +129,3 @@ module KRPC
129
129
 
130
130
  end
131
131
  end
132
-
133
-