protip 0.9.7 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/protip/converter.rb +22 -0
- data/lib/protip/messages/types.pb.rb +26 -0
- data/lib/protip/messages/wrappers.pb.rb +64 -0
- data/lib/protip/resource.rb +29 -14
- data/lib/protip/standard_converter.rb +75 -0
- data/lib/protip/wrapper.rb +81 -0
- data/test/functional/protip/resource_test.rb +20 -14
- data/test/unit/protip/resource_test.rb +46 -4
- data/test/unit/protip/standard_converter_test.rb +78 -0
- data/test/unit/protip/wrapper_test.rb +101 -0
- metadata +12 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c1e044c1437371db39928f0e1ea08104e1a15554
|
4
|
+
data.tar.gz: 5672c792162be0304a20520d89d4f0a4a8cf1f4a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85bcbbde46db8f52a0b6e75f05b38192cbf1ca8be0c2c9ccd4eed63c3782cae17f09d290fd33a95590ce843c22c9ecd664b7965e07eee213b82a2015a251ca30
|
7
|
+
data.tar.gz: 0f7b09c74b2c57c16502083be6c0f5e5eeaffcb99fae9e474c6a785359ca8cdfbcc42ce80c052f1a46c566adf6ec97fb21a47d9f2de5fd93b023fed94c5f02e4
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
|
3
|
+
|
4
|
+
module Protip
|
5
|
+
module Converter
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def convertible?(message_class)
|
9
|
+
raise NotImplementedError.new(
|
10
|
+
'Must specify whether a message of a given type can be converted to/from a Ruby object'
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_object(message)
|
15
|
+
raise NotImplementedError.new('Must convert a message into a Ruby object')
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_message(object, message_class)
|
19
|
+
raise NotImplementedError.new('Must convert a Ruby object into a message of the given type')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
##
|
4
|
+
# This file is auto-generated. DO NOT EDIT!
|
5
|
+
#
|
6
|
+
require 'protobuf/message'
|
7
|
+
|
8
|
+
module Protip
|
9
|
+
|
10
|
+
##
|
11
|
+
# Message Classes
|
12
|
+
#
|
13
|
+
class Date < ::Protobuf::Message; end
|
14
|
+
|
15
|
+
|
16
|
+
##
|
17
|
+
# Message Fields
|
18
|
+
#
|
19
|
+
class Date
|
20
|
+
required :int64, :year, 1
|
21
|
+
required :uint32, :month, 2
|
22
|
+
required :uint32, :day, 3
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
##
|
4
|
+
# This file is auto-generated. DO NOT EDIT!
|
5
|
+
#
|
6
|
+
require 'protobuf/message'
|
7
|
+
|
8
|
+
module Protip
|
9
|
+
|
10
|
+
##
|
11
|
+
# Message Classes
|
12
|
+
#
|
13
|
+
class DoubleValue < ::Protobuf::Message; end
|
14
|
+
class FloatValue < ::Protobuf::Message; end
|
15
|
+
class Int64Value < ::Protobuf::Message; end
|
16
|
+
class UInt64Value < ::Protobuf::Message; end
|
17
|
+
class Int32Value < ::Protobuf::Message; end
|
18
|
+
class UInt32Value < ::Protobuf::Message; end
|
19
|
+
class BoolValue < ::Protobuf::Message; end
|
20
|
+
class StringValue < ::Protobuf::Message; end
|
21
|
+
class BytesValue < ::Protobuf::Message; end
|
22
|
+
|
23
|
+
|
24
|
+
##
|
25
|
+
# Message Fields
|
26
|
+
#
|
27
|
+
class DoubleValue
|
28
|
+
required :double, :value, 1
|
29
|
+
end
|
30
|
+
|
31
|
+
class FloatValue
|
32
|
+
required :float, :value, 1
|
33
|
+
end
|
34
|
+
|
35
|
+
class Int64Value
|
36
|
+
required :int64, :value, 1
|
37
|
+
end
|
38
|
+
|
39
|
+
class UInt64Value
|
40
|
+
required :uint64, :value, 1
|
41
|
+
end
|
42
|
+
|
43
|
+
class Int32Value
|
44
|
+
required :int32, :value, 1
|
45
|
+
end
|
46
|
+
|
47
|
+
class UInt32Value
|
48
|
+
required :uint32, :value, 1
|
49
|
+
end
|
50
|
+
|
51
|
+
class BoolValue
|
52
|
+
required :bool, :value, 1
|
53
|
+
end
|
54
|
+
|
55
|
+
class StringValue
|
56
|
+
required :string, :value, 1
|
57
|
+
end
|
58
|
+
|
59
|
+
class BytesValue
|
60
|
+
required :bytes, :value, 1
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
data/lib/protip/resource.rb
CHANGED
@@ -13,7 +13,11 @@ require 'active_model/naming'
|
|
13
13
|
require 'active_model/translation'
|
14
14
|
require 'active_model/errors'
|
15
15
|
|
16
|
+
require 'forwardable'
|
17
|
+
|
16
18
|
require 'protip/error'
|
19
|
+
require 'protip/standard_converter'
|
20
|
+
require 'protip/wrapper'
|
17
21
|
|
18
22
|
require 'protip/messages/array.pb'
|
19
23
|
|
@@ -65,7 +69,7 @@ module Protip
|
|
65
69
|
# we should generally do this through the `save` method.
|
66
70
|
def create!
|
67
71
|
raise RuntimeError.new("Can't re-create a persisted object") if persisted?
|
68
|
-
|
72
|
+
self.message = self.class.client.request path: self.class.base_path,
|
69
73
|
method: Net::HTTP::Post,
|
70
74
|
message: message,
|
71
75
|
response_type: self.class.message
|
@@ -80,7 +84,7 @@ module Protip
|
|
80
84
|
# we should generally do this through the `save` method.
|
81
85
|
def update!
|
82
86
|
raise RuntimeError.new("Can't update a non-persisted object") if !persisted?
|
83
|
-
|
87
|
+
self.message = self.class.client.request path: "#{self.class.base_path}/#{id}",
|
84
88
|
method: Net::HTTP::Put,
|
85
89
|
message: message,
|
86
90
|
response_type: self.class.message
|
@@ -92,7 +96,7 @@ module Protip
|
|
92
96
|
module Destroyable
|
93
97
|
def destroy
|
94
98
|
raise RuntimeError.new("Can't destroy a non-persisted object") if !persisted?
|
95
|
-
|
99
|
+
self.message = self.class.client.request path: "#{self.class.base_path}/#{id}",
|
96
100
|
method: Net::HTTP::Delete,
|
97
101
|
message: nil,
|
98
102
|
response_type: self.class.message
|
@@ -126,11 +130,17 @@ module Protip
|
|
126
130
|
included do
|
127
131
|
extend ActiveModel::Naming
|
128
132
|
extend ActiveModel::Translation
|
133
|
+
extend Forwardable
|
134
|
+
|
135
|
+
def_delegator :@wrapper, :message
|
136
|
+
def_delegator :@wrapper, :as_json
|
129
137
|
end
|
130
138
|
module ClassMethods
|
131
139
|
|
132
140
|
attr_accessor :client
|
141
|
+
|
133
142
|
attr_reader :message
|
143
|
+
attr_reader :converter
|
134
144
|
|
135
145
|
attr_writer :base_path
|
136
146
|
def base_path
|
@@ -140,20 +150,18 @@ module Protip
|
|
140
150
|
private
|
141
151
|
|
142
152
|
# Primary entry point for defining resourceful behavior.
|
143
|
-
def resource(actions:, message:, query: nil)
|
153
|
+
def resource(actions:, message:, query: nil, converter: Protip::StandardConverter.new)
|
144
154
|
if @message
|
145
155
|
raise RuntimeError.new('Only one call to `resource` is allowed')
|
146
156
|
end
|
147
157
|
|
158
|
+
@converter = converter
|
159
|
+
|
148
160
|
# Define attribute readers/writers
|
149
161
|
@message = message
|
150
162
|
@message.all_fields.each do |field|
|
151
|
-
|
152
|
-
|
153
|
-
end
|
154
|
-
define_method :"#{field.name}=" do |value|
|
155
|
-
@message.public_send :"#{field.name}=", value
|
156
|
-
end
|
163
|
+
def_delegator :@wrapper, :"#{field.name}"
|
164
|
+
def_delegator :@wrapper, :"#{field.name}="
|
157
165
|
end
|
158
166
|
|
159
167
|
# Validate arguments
|
@@ -220,20 +228,26 @@ module Protip
|
|
220
228
|
end
|
221
229
|
end
|
222
230
|
|
223
|
-
attr_reader :message
|
224
231
|
def initialize(message_or_params = {})
|
225
232
|
if self.class.message == nil
|
226
233
|
raise RuntimeError.new('Must define a message class using `resource`')
|
227
234
|
end
|
228
235
|
if message_or_params.is_a?(self.class.message)
|
229
|
-
|
236
|
+
self.message = message_or_params
|
230
237
|
else
|
231
|
-
|
238
|
+
self.message = self.class.message.new
|
239
|
+
message_or_params.each do |field, value|
|
240
|
+
public_send :"#{field}=", value
|
241
|
+
end
|
232
242
|
end
|
233
243
|
|
234
244
|
super()
|
235
245
|
end
|
236
246
|
|
247
|
+
def message=(message)
|
248
|
+
@wrapper = Protip::Wrapper.new(message, self.class.converter)
|
249
|
+
end
|
250
|
+
|
237
251
|
def save
|
238
252
|
success = true
|
239
253
|
begin
|
@@ -260,8 +274,9 @@ module Protip
|
|
260
274
|
end
|
261
275
|
|
262
276
|
def attributes
|
277
|
+
# Like `.as_json`, but includes nil fields to match ActiveRecord behavior.
|
263
278
|
self.class.message.all_fields.map{|field| field.name}.inject({}) do |hash, attribute_name|
|
264
|
-
hash[attribute_name] = message.field?(attribute_name) ? public_send(attribute_name) : nil
|
279
|
+
hash[attribute_name] = message.field?(attribute_name) ? public_send(attribute_name).as_json : nil
|
265
280
|
hash
|
266
281
|
end
|
267
282
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'protip/converter'
|
2
|
+
|
3
|
+
require 'protobuf'
|
4
|
+
require 'protip/messages/types.pb'
|
5
|
+
require 'protip/messages/wrappers.pb'
|
6
|
+
|
7
|
+
module Protip
|
8
|
+
class StandardConverter
|
9
|
+
include Protip::Converter
|
10
|
+
|
11
|
+
class << self
|
12
|
+
attr_reader :conversions
|
13
|
+
end
|
14
|
+
@conversions = {}
|
15
|
+
|
16
|
+
## Protip types
|
17
|
+
@conversions[Protip::Date] = {
|
18
|
+
to_object: ->(message) { ::Date.new(message.year, message.month, message.day) },
|
19
|
+
to_message: lambda do |date|
|
20
|
+
raise ArgumentError unless date.is_a?(::Date)
|
21
|
+
Protip::Date.new year: date.year, month: date.month, day: date.day
|
22
|
+
end
|
23
|
+
}
|
24
|
+
|
25
|
+
## Standard wrappers
|
26
|
+
[Protip::Int64Value, Protip::Int32Value, Protip::UInt64Value, Protip::UInt32Value].each do |message_class|
|
27
|
+
@conversions[message_class] = {
|
28
|
+
to_object: ->(message) { message.value },
|
29
|
+
to_message: lambda do |integer|
|
30
|
+
raise ArgumentError unless integer.is_a?(Integer)
|
31
|
+
message_class.new value: integer
|
32
|
+
end
|
33
|
+
}
|
34
|
+
end
|
35
|
+
[Protip::DoubleValue, Protip::FloatValue].each do |message_class|
|
36
|
+
@conversions[message_class] = {
|
37
|
+
to_object: ->(message) { message.value },
|
38
|
+
to_message: lambda do |float|
|
39
|
+
raise ArgumentError unless float.is_a?(Float)
|
40
|
+
message_class.new value: float
|
41
|
+
end
|
42
|
+
}
|
43
|
+
end
|
44
|
+
[Protip::BoolValue].each do |message_class|
|
45
|
+
@conversions[message_class] = {
|
46
|
+
to_object: ->(message) { message.value },
|
47
|
+
to_message: lambda do |bool|
|
48
|
+
# Protobuf throws a type error if this isn't the correct type, so we don't need to check
|
49
|
+
message_class.new value: bool
|
50
|
+
end
|
51
|
+
}
|
52
|
+
end
|
53
|
+
[Protip::StringValue, Protip::BytesValue].each do |message_class|
|
54
|
+
@conversions[message_class] = {
|
55
|
+
to_object: ->(message) { message.value },
|
56
|
+
to_message: lambda do |string|
|
57
|
+
# Protobuf throws a type error if this isn't the correct type, so we don't need to check
|
58
|
+
message_class.new value: string
|
59
|
+
end
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def convertible?(message_class)
|
64
|
+
self.class.conversions.has_key?(message_class)
|
65
|
+
end
|
66
|
+
|
67
|
+
def to_object(message)
|
68
|
+
self.class.conversions[message.class][:to_object].call(message)
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_message(object, message_class)
|
72
|
+
self.class.conversions[message_class][:to_message].call(object)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'active_support/concern'
|
2
|
+
require 'protobuf'
|
3
|
+
|
4
|
+
module Protip
|
5
|
+
class Wrapper
|
6
|
+
attr_reader :message, :converter
|
7
|
+
def initialize(message, converter)
|
8
|
+
@message = message
|
9
|
+
@converter = converter
|
10
|
+
end
|
11
|
+
|
12
|
+
def respond_to?(name)
|
13
|
+
if super
|
14
|
+
true
|
15
|
+
else
|
16
|
+
if name =~ /=$/
|
17
|
+
message.class.fields.any?{|field| :"#{field.name}=" == name.to_sym}
|
18
|
+
else
|
19
|
+
message.class.fields.any?{|field| field.name == name.to_sym}
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(name, *args)
|
25
|
+
if (name =~ /=$/ && field = message.class.fields.detect{|field| :"#{field.name}=" == name})
|
26
|
+
raise ArgumentError unless args.length == 1
|
27
|
+
set field, args[0]
|
28
|
+
elsif (field = message.class.fields.detect{|field| field.name == name})
|
29
|
+
raise ArgumentError unless args.length == 0
|
30
|
+
get field
|
31
|
+
else
|
32
|
+
super
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def as_json
|
37
|
+
json = {}
|
38
|
+
message.class.fields.each do |name|
|
39
|
+
value = public_send(name)
|
40
|
+
json[name.to_s] = value.respond_to?(:as_json) ? value.as_json : value
|
41
|
+
end
|
42
|
+
json
|
43
|
+
end
|
44
|
+
|
45
|
+
def ==(wrapper)
|
46
|
+
message == wrapper.message && converter == wrapper.converter
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def get(field)
|
52
|
+
if field.is_a?(Protobuf::Field::MessageField)
|
53
|
+
if message[field.name].nil?
|
54
|
+
nil
|
55
|
+
else
|
56
|
+
if converter.convertible?(field.type_class)
|
57
|
+
converter.to_object message[field.name]
|
58
|
+
else
|
59
|
+
self.class.new message[field.name], converter
|
60
|
+
end
|
61
|
+
end
|
62
|
+
else
|
63
|
+
message[field.name]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def set(field, value)
|
68
|
+
if field.is_a?(Protobuf::Field::MessageField)
|
69
|
+
if value.is_a? Protobuf::Message
|
70
|
+
message[field.name] = value
|
71
|
+
elsif converter.convertible?(field.type_class)
|
72
|
+
message[field.name] = converter.to_message value, field.type_class
|
73
|
+
else
|
74
|
+
raise ArgumentError.new "Cannot convert from Ruby object: \"#{field}\""
|
75
|
+
end
|
76
|
+
else
|
77
|
+
message[field.name] = value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -11,31 +11,35 @@ module Protip::ResourceTestFunctional # Namespace for internal constants
|
|
11
11
|
|
12
12
|
# Make sure none of these are structurally identical (e.g. give fields
|
13
13
|
# different positions), to avoid potential errors where a message is
|
14
|
-
# incorrectly
|
15
|
-
|
14
|
+
# incorrectly encoded but still accidentally correctly decoded.
|
15
|
+
class NestedMessage < ::Protobuf::Message
|
16
|
+
optional :string, :inconvertible_value, 1
|
17
|
+
end
|
16
18
|
class ResourceMessage < ::Protobuf::Message
|
17
|
-
optional :int64, :id,
|
18
|
-
optional :string, :ordered_tests,
|
19
|
+
optional :int64, :id, 2
|
20
|
+
optional :string, :ordered_tests, 3
|
21
|
+
optional NestedMessage, :nested_message, 4
|
22
|
+
optional Protip::Int64Value, :nested_int, 5
|
19
23
|
end
|
20
24
|
|
21
25
|
class ResourceQuery < ::Protobuf::Message
|
22
|
-
optional :string, :param,
|
26
|
+
optional :string, :param, 6
|
23
27
|
end
|
24
28
|
|
25
29
|
class NameResponse < ::Protobuf::Message
|
26
|
-
optional :string, :name,
|
30
|
+
optional :string, :name, 7
|
27
31
|
end
|
28
32
|
|
29
33
|
class SearchRequest < ::Protobuf::Message
|
30
|
-
optional :string, :term,
|
34
|
+
optional :string, :term, 8
|
31
35
|
end
|
32
36
|
|
33
37
|
class SearchResponse < ::Protobuf::Message
|
34
|
-
repeated :string, :results,
|
38
|
+
repeated :string, :results, 9
|
35
39
|
end
|
36
40
|
|
37
41
|
class FetchRequest < ::Protobuf::Message
|
38
|
-
repeated :string, :names,
|
42
|
+
repeated :string, :names, 10
|
39
43
|
end
|
40
44
|
|
41
45
|
class Client
|
@@ -64,7 +68,7 @@ module Protip::ResourceTestFunctional # Namespace for internal constants
|
|
64
68
|
describe 'with a successful server response' do
|
65
69
|
before do
|
66
70
|
response = Protip::Messages::Array.new(messages: ['bilbo', 'baggins'].each_with_index.map do |name, index|
|
67
|
-
ResourceMessage.new(id: index, ordered_tests: name).encode
|
71
|
+
ResourceMessage.new(id: index, ordered_tests: name, nested_int: {value: index + 42}).encode
|
68
72
|
end)
|
69
73
|
stub_request(:get, 'https://external.service/resources')
|
70
74
|
.to_return body: response.encode
|
@@ -79,8 +83,10 @@ module Protip::ResourceTestFunctional # Namespace for internal constants
|
|
79
83
|
assert_equal 2, results.length, 'incorrect number of resources were returned'
|
80
84
|
results.each { |result| assert_instance_of Resource, result, 'incorrect type was parsed'}
|
81
85
|
|
82
|
-
assert_equal({ordered_tests: 'bilbo', id: 0},
|
83
|
-
|
86
|
+
assert_equal({ordered_tests: 'bilbo', id: 0, nested_message: nil, nested_int: 42},
|
87
|
+
results[0].attributes)
|
88
|
+
assert_equal({ordered_tests: 'baggins', id: 1, nested_message: nil, nested_int: 43},
|
89
|
+
results[1].attributes)
|
84
90
|
end
|
85
91
|
|
86
92
|
it 'allows requests without parameters' do
|
@@ -138,7 +144,7 @@ module Protip::ResourceTestFunctional # Namespace for internal constants
|
|
138
144
|
].each do |id, method, uri|
|
139
145
|
describe "with a #{id ? 'persisted' : 'non-persisted'} resource" do
|
140
146
|
before do
|
141
|
-
@resource = Resource.new id: id
|
147
|
+
@resource = Resource.new id: id, nested_int: 100
|
142
148
|
end
|
143
149
|
|
144
150
|
describe 'with a successful server response' do
|
@@ -155,7 +161,7 @@ module Protip::ResourceTestFunctional # Namespace for internal constants
|
|
155
161
|
@resource.save
|
156
162
|
|
157
163
|
assert_requested method, uri,
|
158
|
-
times: 1, body: ResourceMessage.new(id: id, ordered_tests: 'no').encode
|
164
|
+
times: 1, body: ResourceMessage.new(id: id, ordered_tests: 'no', nested_int: {value: 100}).encode
|
159
165
|
assert_equal 'yes', @resource.ordered_tests
|
160
166
|
end
|
161
167
|
end
|
@@ -1,15 +1,23 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
3
|
require 'protip/client'
|
4
|
+
require 'protip/converter'
|
4
5
|
require 'protip/resource'
|
5
6
|
|
6
7
|
module Protip::ResourceTest # Namespace for internal constants
|
7
8
|
describe Protip::Resource do
|
9
|
+
|
10
|
+
class NestedMessage < ::Protobuf::Message
|
11
|
+
optional :int64, :number, 1
|
12
|
+
end
|
13
|
+
|
8
14
|
class ResourceMessage < ::Protobuf::Message
|
9
15
|
optional :int64, :id, 1
|
10
16
|
optional :string, :string, 2
|
11
17
|
optional :string, :string2, 3
|
18
|
+
optional NestedMessage, :nested_message, 4
|
12
19
|
end
|
20
|
+
|
13
21
|
class ResourceQuery < ::Protobuf::Message
|
14
22
|
optional :string, :param, 1
|
15
23
|
end
|
@@ -43,11 +51,19 @@ module Protip::ResourceTest # Namespace for internal constants
|
|
43
51
|
end
|
44
52
|
|
45
53
|
describe '.resource' do
|
54
|
+
|
55
|
+
let :converter do
|
56
|
+
Class.new do
|
57
|
+
include Protip::Converter
|
58
|
+
end.new
|
59
|
+
end
|
60
|
+
|
46
61
|
before do
|
47
|
-
resource_class.
|
48
|
-
resource actions: [], message: ResourceMessage
|
62
|
+
resource_class.class_exec(converter) do |converter|
|
63
|
+
resource actions: [], message: ResourceMessage, converter: converter
|
49
64
|
end
|
50
65
|
end
|
66
|
+
|
51
67
|
it 'can only be invoked once' do
|
52
68
|
assert_raises RuntimeError do
|
53
69
|
resource_class.class_eval do
|
@@ -58,18 +74,44 @@ module Protip::ResourceTest # Namespace for internal constants
|
|
58
74
|
|
59
75
|
it 'defines accessors for the fields on its message' do
|
60
76
|
resource = resource_class.new
|
61
|
-
[:id, :
|
77
|
+
[:id, :string].each do |method|
|
62
78
|
assert_respond_to resource, method
|
63
79
|
end
|
64
80
|
refute_respond_to resource, :foo
|
65
81
|
end
|
66
82
|
|
67
|
-
it 'sets fields on the underlying message when setters are called' do
|
83
|
+
it 'sets fields on the underlying message when simple setters are called' do
|
68
84
|
resource = resource_class.new
|
69
85
|
resource.string = 'intern'
|
70
86
|
assert_equal 'intern', resource.message.string
|
71
87
|
assert_equal 'intern', resource.string
|
72
88
|
end
|
89
|
+
|
90
|
+
it 'never checks with the converter when setting simple types' do
|
91
|
+
converter.expects(:convertible?).never
|
92
|
+
resource = resource_class.new
|
93
|
+
resource.string = 'intern'
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'checks with the converter when setting message types' do
|
97
|
+
converter.expects(:convertible?).once.with(NestedMessage).returns(false)
|
98
|
+
resource = resource_class.new
|
99
|
+
assert_raises(ArgumentError) do
|
100
|
+
resource.nested_message = 5
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'converts message types to and from their Ruby values when the converter allows' do
|
105
|
+
converter.expects(:convertible?).times(2).with(NestedMessage).returns(true)
|
106
|
+
converter.expects(:to_message).once.with(6, NestedMessage).returns(NestedMessage.new number: 100)
|
107
|
+
converter.expects(:to_object).once.with(NestedMessage.new number: 100).returns 'intern'
|
108
|
+
|
109
|
+
resource = resource_class.new
|
110
|
+
resource.nested_message = 6
|
111
|
+
|
112
|
+
assert_equal NestedMessage.new(number: 100), resource.message.nested_message, 'object was not converted'
|
113
|
+
assert_equal 'intern', resource.nested_message, 'message was not converted'
|
114
|
+
end
|
73
115
|
end
|
74
116
|
|
75
117
|
describe '.all' do
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'protip/standard_converter'
|
4
|
+
|
5
|
+
describe Protip::StandardConverter do
|
6
|
+
let(:converter) { Protip::StandardConverter.new }
|
7
|
+
|
8
|
+
let(:integer_types) do
|
9
|
+
[Protip::Int64Value, Protip::Int32Value, Protip::UInt64Value, Protip::UInt32Value]
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:float_types) do
|
13
|
+
[Protip::FloatValue, Protip::DoubleValue]
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:bool_types) do
|
17
|
+
[Protip::BoolValue]
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:string_types) do
|
21
|
+
[Protip::StringValue, Protip::BytesValue]
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
describe '#convertible?' do
|
26
|
+
it 'converts all standard types' do
|
27
|
+
(integer_types + float_types + string_types + bool_types + [Protip::Date]).each do |message_class|
|
28
|
+
assert converter.convertible?(message_class), 'expected type not convertible'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'does not convert other message types' do
|
33
|
+
refute converter.convertible?(Class.new(::Protobuf::Message))
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#to_object' do
|
38
|
+
it 'converts wrapper types' do
|
39
|
+
{
|
40
|
+
6 => integer_types,
|
41
|
+
5.5 => float_types,
|
42
|
+
false => bool_types,
|
43
|
+
'asdf' => string_types,
|
44
|
+
}.each do |value, message_types|
|
45
|
+
message_types.each do |message_class|
|
46
|
+
assert_equal value, converter.to_object(message_class.new value: value)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'converts dates' do
|
52
|
+
date = converter.to_object(Protip::Date.new year: 2015, month: 2, day: 9)
|
53
|
+
assert_instance_of Date, date
|
54
|
+
assert_equal 'Mon, 09 Feb 2015', date.inspect
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe '#to_message' do
|
59
|
+
it 'converts wrapper types' do
|
60
|
+
{
|
61
|
+
6 => integer_types,
|
62
|
+
5.5 => float_types,
|
63
|
+
false => bool_types,
|
64
|
+
'asdf' => string_types,
|
65
|
+
}.each do |value, message_types|
|
66
|
+
message_types.each do |message_class|
|
67
|
+
assert_equal message_class.new(value: value), converter.to_message(value, message_class)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'converts dates' do
|
73
|
+
date = ::Date.new(2012, 5, 7)
|
74
|
+
assert_equal 7, date.day # Sanity check argument order
|
75
|
+
assert_equal Protip::Date.new(year: 2012, month: 5, day: 7), converter.to_message(date, Protip::Date)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
require 'protip/wrapper'
|
4
|
+
|
5
|
+
module Protip::WrapperTest # namespace for internal constants
|
6
|
+
describe Protip::Wrapper do
|
7
|
+
let(:converter) do
|
8
|
+
Class.new do
|
9
|
+
include Protip::Converter
|
10
|
+
end.new
|
11
|
+
end
|
12
|
+
|
13
|
+
class InnerMessage < ::Protobuf::Message
|
14
|
+
required :int64, :value, 1
|
15
|
+
end
|
16
|
+
class Message < ::Protobuf::Message
|
17
|
+
optional InnerMessage, :inner, 1
|
18
|
+
optional :string, :string, 2
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:wrapped_message) do
|
22
|
+
Message.new(inner: {value: 25}, string: 'test')
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:wrapper) do
|
26
|
+
Protip::Wrapper.new(wrapped_message, converter)
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#respond_to?' do
|
30
|
+
it 'adds setters for message fields' do
|
31
|
+
assert_respond_to wrapper, :string=
|
32
|
+
assert_respond_to wrapper, :inner=
|
33
|
+
end
|
34
|
+
it 'adds getters for message fields' do
|
35
|
+
assert_respond_to wrapper, :string
|
36
|
+
assert_respond_to wrapper, :inner
|
37
|
+
end
|
38
|
+
it 'responds to standard defined methods' do
|
39
|
+
assert_respond_to wrapper, :as_json
|
40
|
+
end
|
41
|
+
it 'does not add other setters/getters' do
|
42
|
+
refute_respond_to wrapper, :foo=
|
43
|
+
refute_respond_to wrapper, :foo
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#get' do
|
48
|
+
it 'does not convert simple fields' do
|
49
|
+
converter.expects(:convertible?).never
|
50
|
+
converter.expects(:to_object).never
|
51
|
+
assert_equal 'test', wrapper.string
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'converts convertible messages' do
|
55
|
+
converter.expects(:convertible?).with(InnerMessage).once.returns(true)
|
56
|
+
converter.expects(:to_object).with(InnerMessage.new(value: 25)).returns 40
|
57
|
+
assert_equal 40, wrapper.inner
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'wraps inconvertible messages' do
|
61
|
+
converter.expects(:convertible?).with(InnerMessage).once.returns(false)
|
62
|
+
converter.expects(:to_object).never
|
63
|
+
assert_equal Protip::Wrapper.new(InnerMessage.new(value: 25), converter), wrapper.inner
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#set' do
|
68
|
+
it 'does not convert simple fields' do
|
69
|
+
converter.expects(:convertible?).never
|
70
|
+
converter.expects(:to_message).never
|
71
|
+
|
72
|
+
wrapper.string = 'test2'
|
73
|
+
assert_equal 'test2', wrapper.message.string
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'converts convertible messages' do
|
77
|
+
converter.expects(:convertible?).with(InnerMessage).once.returns(true)
|
78
|
+
converter.expects(:to_message).with(40, InnerMessage).returns(InnerMessage.new(value: 30))
|
79
|
+
|
80
|
+
wrapper.inner = 40
|
81
|
+
assert_equal InnerMessage.new(value: 30), wrapper.message.inner
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'raises an error when setting inconvertible messages' do
|
85
|
+
converter.expects(:convertible?).with(InnerMessage).once.returns(false)
|
86
|
+
converter.expects(:to_message).never
|
87
|
+
assert_raises ArgumentError do
|
88
|
+
wrapper.inner = 'cannot convert me'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'passes through messages without checking whether they are convertible' do
|
93
|
+
converter.expects(:convertible?).never
|
94
|
+
converter.expects(:to_message).never
|
95
|
+
|
96
|
+
wrapper.inner = InnerMessage.new(value: 50)
|
97
|
+
assert_equal InnerMessage.new(value: 50), wrapper.message.inner
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: protip
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- AngelList
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-05-
|
11
|
+
date: 2015-05-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -144,15 +144,23 @@ extra_rdoc_files: []
|
|
144
144
|
files:
|
145
145
|
- lib/protip.rb
|
146
146
|
- lib/protip/client.rb
|
147
|
+
- lib/protip/converter.rb
|
147
148
|
- lib/protip/error.rb
|
148
149
|
- lib/protip/messages/array.pb.rb
|
149
150
|
- lib/protip/messages/errors.pb.rb
|
151
|
+
- lib/protip/messages/types.pb.rb
|
152
|
+
- lib/protip/messages/wrappers.pb.rb
|
150
153
|
- lib/protip/resource.rb
|
154
|
+
- lib/protip/standard_converter.rb
|
155
|
+
- lib/protip/wrapper.rb
|
151
156
|
- test/functional/protip/resource_test.rb
|
152
157
|
- test/test_helper.rb
|
153
158
|
- test/unit/protip/resource_test.rb
|
154
|
-
|
155
|
-
|
159
|
+
- test/unit/protip/standard_converter_test.rb
|
160
|
+
- test/unit/protip/wrapper_test.rb
|
161
|
+
homepage: https://github.com/AngelList/protip
|
162
|
+
licenses:
|
163
|
+
- MIT
|
156
164
|
metadata: {}
|
157
165
|
post_install_message:
|
158
166
|
rdoc_options: []
|