parent
ba52f2b678
commit
e3094a8d80
3 changed files with 305 additions and 0 deletions
@ -0,0 +1,206 @@ |
||||
#!/usr/bin/ruby |
||||
# Protocol Buffers - Google's data interchange format |
||||
# Copyright 2008 Google Inc. All rights reserved. |
||||
# https://developers.google.com/protocol-buffers/ |
||||
# |
||||
# Redistribution and use in source and binary forms, with or without |
||||
# modification, are permitted provided that the following conditions are |
||||
# met: |
||||
# |
||||
# * Redistributions of source code must retain the above copyright |
||||
# notice, this list of conditions and the following disclaimer. |
||||
# * Redistributions in binary form must reproduce the above |
||||
# copyright notice, this list of conditions and the following disclaimer |
||||
# in the documentation and/or other materials provided with the |
||||
# distribution. |
||||
# * Neither the name of Google Inc. nor the names of its |
||||
# contributors may be used to endorse or promote products derived from |
||||
# this software without specific prior written permission. |
||||
# |
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||
|
||||
require 'google/protobuf/any_pb' |
||||
require 'google/protobuf/duration_pb' |
||||
require 'google/protobuf/field_mask_pb' |
||||
require 'google/protobuf/struct_pb' |
||||
require 'google/protobuf/timestamp_pb' |
||||
|
||||
module Google |
||||
module Protobuf |
||||
|
||||
Any.class_eval do |
||||
def pack(msg, type_url_prefix='type.googleapis.com/') |
||||
if type_url_prefix.empty? or type_url_prefix[-1] != '/' then |
||||
self.type_url = "#{type_url_prefix}/#{msg.class.descriptor.name}" |
||||
else |
||||
self.type_url = "#{type_url_prefix}#{msg.class.descriptor.name}" |
||||
end |
||||
self.value = msg.to_proto |
||||
end |
||||
|
||||
def unpack(klass) |
||||
if self.is(klass) then |
||||
klass.decode(self.value) |
||||
else |
||||
nil |
||||
end |
||||
end |
||||
|
||||
def type_name |
||||
return self.type_url.split("/")[-1] |
||||
end |
||||
|
||||
def is(klass) |
||||
return self.type_name == klass.descriptor.name |
||||
end |
||||
end |
||||
|
||||
Timestamp.class_eval do |
||||
def to_time |
||||
Time.at(self.to_f) |
||||
end |
||||
|
||||
def from_time(time) |
||||
self.seconds = time.to_i |
||||
self.nanos = time.nsec |
||||
end |
||||
|
||||
def to_i |
||||
self.seconds |
||||
end |
||||
|
||||
def to_f |
||||
self.seconds + (self.nanos.to_f / 1_000_000_000) |
||||
end |
||||
end |
||||
|
||||
Duration.class_eval do |
||||
def to_f |
||||
self.seconds + (self.nanos.to_f / 1_000_000_000) |
||||
end |
||||
end |
||||
|
||||
Value.class_eval do |
||||
def to_ruby(recursive = false) |
||||
case self.kind |
||||
when :struct_value |
||||
if recursive |
||||
self.struct_value.to_h |
||||
else |
||||
self.struct_value |
||||
end |
||||
when :list_value |
||||
if recursive |
||||
self.list_value.to_a |
||||
else |
||||
self.list_value |
||||
end |
||||
when :null_value |
||||
nil |
||||
when :number_value |
||||
self.number_value |
||||
when :string_value |
||||
self.string_value |
||||
when :bool_value |
||||
self.bool_value |
||||
else |
||||
raise "Value not set" |
||||
end |
||||
end |
||||
|
||||
def from_ruby(value) |
||||
if value.nil? |
||||
self.null_value = 0 |
||||
elsif value.is_a?(Numeric) |
||||
self.number_value = value |
||||
elsif value.is_a?(String) |
||||
self.string_value = value |
||||
elsif value.is_a?(TrueClass) |
||||
self.bool_value = true |
||||
elsif value.is_a?(FalseClass) |
||||
self.bool_value = false |
||||
elsif value.is_a?(Struct) |
||||
self.struct_value = value |
||||
elsif value.is_a?(Hash) |
||||
self.struct_value = Struct.from_hash(value) |
||||
elsif value.is_a?(ListValue) |
||||
self.list_value = value |
||||
elsif value.is_a?(Array) |
||||
self.list_value = ListValue.from_a(value) |
||||
else |
||||
raise "Unexpected type" |
||||
end |
||||
end |
||||
end |
||||
|
||||
Struct.class_eval do |
||||
def [](key) |
||||
self.fields[key].to_ruby |
||||
end |
||||
|
||||
def []=(key, value) |
||||
self.fields[key] ||= Google::Protobuf::Value.new |
||||
self.fields[key].from_ruby(value) |
||||
end |
||||
|
||||
def to_h |
||||
ret = {} |
||||
self.fields.each { |key, val| ret[key] = val.to_ruby(true) } |
||||
ret |
||||
end |
||||
|
||||
def self.from_hash(hash) |
||||
ret = Struct.new |
||||
hash.each { |key, val| ret[key] = val } |
||||
ret |
||||
end |
||||
end |
||||
|
||||
ListValue.class_eval do |
||||
include Enumerable |
||||
|
||||
def length |
||||
self.values.length |
||||
end |
||||
|
||||
def [](index) |
||||
self.values[index].to_ruby |
||||
end |
||||
|
||||
def []=(index, value) |
||||
self.values[index].from_ruby(value) |
||||
end |
||||
|
||||
def <<(value) |
||||
wrapper = Google::Protobuf::Value.new |
||||
wrapper.from_ruby(value) |
||||
self.values << wrapper |
||||
end |
||||
|
||||
def each |
||||
self.values.each { |x| yield(x.to_ruby) } |
||||
end |
||||
|
||||
def to_a |
||||
self.values.map { |x| x.to_ruby(true) } |
||||
end |
||||
|
||||
def self.from_a(arr) |
||||
ret = ListValue.new |
||||
arr.each { |val| ret << val } |
||||
ret |
||||
end |
||||
end |
||||
|
||||
end |
||||
end |
@ -0,0 +1,98 @@ |
||||
#!/usr/bin/ruby |
||||
|
||||
require 'test/unit' |
||||
require 'google/protobuf/well_known_types' |
||||
|
||||
class TestWellKnownTypes < Test::Unit::TestCase |
||||
def test_timestamp |
||||
ts = Google::Protobuf::Timestamp.new |
||||
|
||||
assert_equal Time.at(0), ts.to_time |
||||
|
||||
ts.seconds = 12345 |
||||
assert_equal Time.at(12345), ts.to_time |
||||
assert_equal 12345, ts.to_i |
||||
|
||||
ts.from_time(Time.at(123456, 654321)) |
||||
assert_equal 123456, ts.seconds |
||||
assert_equal 654321000, ts.nanos |
||||
assert_equal Time.at(123456.654321), ts.to_time |
||||
end |
||||
|
||||
def test_duration |
||||
duration = Google::Protobuf::Duration.new(seconds: 123, nanos: 456) |
||||
assert_equal 123.000000456, duration.to_f |
||||
end |
||||
|
||||
def test_struct |
||||
struct = Google::Protobuf::Struct.new |
||||
|
||||
substruct = { |
||||
"subkey" => 999, |
||||
"subkey2" => false |
||||
} |
||||
|
||||
sublist = ["abc", 123, {"deepkey" => "deepval"}] |
||||
|
||||
struct["number"] = 12345 |
||||
struct["boolean-true"] = true |
||||
struct["boolean-false"] = false |
||||
struct["null"] = nil |
||||
struct["string"] = "abcdef" |
||||
struct["substruct"] = substruct |
||||
struct["sublist"] = sublist |
||||
|
||||
assert_equal 12345, struct["number"] |
||||
assert_equal true, struct["boolean-true"] |
||||
assert_equal false, struct["boolean-false"] |
||||
assert_equal nil, struct["null"] |
||||
assert_equal "abcdef", struct["string"] |
||||
assert_equal(Google::Protobuf::Struct.from_hash(substruct), |
||||
struct["substruct"]) |
||||
assert_equal(Google::Protobuf::ListValue.from_a(sublist), |
||||
struct["sublist"]) |
||||
|
||||
should_equal = { |
||||
"number" => 12345, |
||||
"boolean-true" => true, |
||||
"boolean-false" => false, |
||||
"null" => nil, |
||||
"string" => "abcdef", |
||||
"substruct" => { |
||||
"subkey" => 999, |
||||
"subkey2" => false |
||||
}, |
||||
"sublist" => ["abc", 123, {"deepkey" => "deepval"}] |
||||
} |
||||
|
||||
list = struct["sublist"] |
||||
list.is_a?(Google::Protobuf::ListValue) |
||||
assert_equal "abc", list[0] |
||||
assert_equal 123, list[1] |
||||
assert_equal({"deepkey" => "deepval"}, list[2].to_h) |
||||
|
||||
# to_h returns a fully-flattened Ruby structure (Hash and Array). |
||||
assert_equal(should_equal, struct.to_h) |
||||
|
||||
# Test that we can assign Struct and ListValue directly. |
||||
struct["substruct"] = Google::Protobuf::Struct.from_hash(substruct) |
||||
struct["sublist"] = Google::Protobuf::ListValue.from_a(sublist) |
||||
|
||||
assert_equal(should_equal, struct.to_h) |
||||
|
||||
struct["sublist"] << nil |
||||
should_equal["sublist"] << nil |
||||
|
||||
assert_equal(should_equal, struct.to_h) |
||||
assert_equal(should_equal["sublist"].length, struct["sublist"].length) |
||||
end |
||||
|
||||
def test_any |
||||
any = Google::Protobuf::Any.new |
||||
ts = Google::Protobuf::Timestamp.new(seconds: 12345, nanos: 6789) |
||||
any.pack(ts) |
||||
|
||||
assert_true any.is(Google::Protobuf::Timestamp) |
||||
assert_equal ts, any.unpack(Google::Protobuf::Timestamp) |
||||
end |
||||
end |
Loading…
Reference in new issue