protip 0.9.7 → 0.10.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/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: []
|