protip 0.19.2 → 0.20.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7bfa2572b20c366e72093d7c071ae2cbf26302a1
4
- data.tar.gz: 282b0e395f8cc7038f8fc923d3857fd936155fb9
3
+ metadata.gz: 4e9e8d262a3ac9f0a1164c3da1c73895c23eb868
4
+ data.tar.gz: 6898418d57886241026534c0a04174944a2092c2
5
5
  SHA512:
6
- metadata.gz: 86dbb93f457cef7f024e954929299aaec41714db6c9966228d4c499738d16dd6347cfe07ba389c7f52af2fb3d8148ba8896154c1f037d1c1c08dfc27bbfb8a0d
7
- data.tar.gz: 131be596c91854dd6e504be790c4905139f2bae872997bff9ce36f0de539266613b3f2e41f767314475c1a7e852ed138aa3b153a71fa7c9a9dce600f969ecb2e
6
+ metadata.gz: 58bfca0184504abdc837dc766fde21eaa164f02fc22b1b0b3318ffb654bcc909474d0c660234145b8df69de866fc7da86c4c56f8dce71d3dc0769f15d85dbc13
7
+ data.tar.gz: 84502680643b0aac55ffda5e833381e0f20bae12415977b1a0991e0fa564aa16bf0ec4326870d1495e451e16c0de3d511260cb6b6f8568d396f4c12e0057d1db
@@ -0,0 +1,7 @@
1
+ syntax = "proto3";
2
+
3
+ import "google/protobuf/descriptor.proto";
4
+
5
+ extend google.protobuf.FieldOptions {
6
+ string protip_enum = 14383889;
7
+ }
@@ -1,5 +1,7 @@
1
1
  syntax = "proto3";
2
2
 
3
+ import public "protip/extensions.proto";
4
+
3
5
  package protip.messages;
4
6
 
5
7
  message RepeatedDouble {
@@ -36,4 +38,10 @@ message RepeatedString {
36
38
 
37
39
  message RepeatedBytes {
38
40
  repeated bytes values = 1;
41
+ }
42
+
43
+ // Should generally be used with the "protip_enum" option
44
+ // for serialization to/from Ruby.
45
+ message RepeatedEnum {
46
+ repeated int32 values = 1;
39
47
  }
@@ -0,0 +1,18 @@
1
+ // Should be temporary - enables testing our hacky enum option handling with an actual compiled file.
2
+ // Do not depend on anything in this file.
3
+
4
+ syntax = "proto3";
5
+
6
+ import "protip/messages/repeated_wrappers.proto";
7
+ import "protip/messages/wrappers.proto";
8
+
9
+ package protip.messages;
10
+
11
+ message EnumTest {
12
+ enum Enum {
13
+ ZERO = 0;
14
+ ONE = 1;
15
+ }
16
+ protip.messages.EnumValue enum = 1 [(protip_enum) = "protip.messages.EnumTest.Enum"];
17
+ protip.messages.RepeatedEnum enums = 2 [(protip_enum) = "protip.messages.EnumTest.Enum"];
18
+ }
@@ -0,0 +1,9 @@
1
+ syntax = "proto3";
2
+
3
+ import public "protip/extensions.proto";
4
+
5
+ package protip.messages;
6
+
7
+ message EnumValue {
8
+ int32 value = 1;
9
+ }
@@ -0,0 +1 @@
1
+ # Placeholder file until Google rolls google/protobuf/descriptor into the gem
@@ -11,11 +11,11 @@ module Protip
11
11
  )
12
12
  end
13
13
 
14
- def to_object(message)
14
+ def to_object(message, field)
15
15
  raise NotImplementedError.new('Must convert a message into a Ruby object')
16
16
  end
17
17
 
18
- def to_message(object, message_class)
18
+ def to_message(object, message_class, field)
19
19
  raise NotImplementedError.new('Must convert a Ruby object into a message of the given type')
20
20
  end
21
21
  end
@@ -0,0 +1,9 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # source: protip/extensions.proto
3
+
4
+ require 'google/protobuf'
5
+
6
+ require 'google/protobuf/descriptor'
7
+ Google::Protobuf::DescriptorPool.generated_pool.build do
8
+ end
9
+
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'google/protobuf'
5
5
 
6
+ require 'protip/extensions'
6
7
  Google::Protobuf::DescriptorPool.generated_pool.build do
7
8
  add_message "protip.messages.RepeatedDouble" do
8
9
  repeated :values, :double, 1
@@ -31,6 +32,9 @@ Google::Protobuf::DescriptorPool.generated_pool.build do
31
32
  add_message "protip.messages.RepeatedBytes" do
32
33
  repeated :values, :bytes, 1
33
34
  end
35
+ add_message "protip.messages.RepeatedEnum" do
36
+ repeated :values, :int32, 1
37
+ end
34
38
  end
35
39
 
36
40
  module Protip
@@ -44,5 +48,6 @@ module Protip
44
48
  RepeatedBool = Google::Protobuf::DescriptorPool.generated_pool.lookup("protip.messages.RepeatedBool").msgclass
45
49
  RepeatedString = Google::Protobuf::DescriptorPool.generated_pool.lookup("protip.messages.RepeatedString").msgclass
46
50
  RepeatedBytes = Google::Protobuf::DescriptorPool.generated_pool.lookup("protip.messages.RepeatedBytes").msgclass
51
+ RepeatedEnum = Google::Protobuf::DescriptorPool.generated_pool.lookup("protip.messages.RepeatedEnum").msgclass
47
52
  end
48
53
  end
@@ -0,0 +1,28 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # source: protip/messages/test.proto
3
+
4
+ require 'google/protobuf'
5
+
6
+ require 'protip/messages/repeated_wrappers'
7
+ require 'protip/messages/wrappers'
8
+ Google::Protobuf::DescriptorPool.generated_pool.build do
9
+ add_message "protip.messages.EnumTest" do
10
+ optional :enum, :message, 1, "protip.messages.EnumValue"
11
+ optional :enums, :message, 2, "protip.messages.RepeatedEnum"
12
+ end
13
+ add_enum "protip.messages.EnumTest.Enum" do
14
+ value :ZERO, 0
15
+ value :ONE, 1
16
+ end
17
+ end
18
+
19
+ module Protip
20
+ module Messages
21
+ EnumTest = Google::Protobuf::DescriptorPool.generated_pool.lookup("protip.messages.EnumTest").msgclass
22
+ EnumTest::Enum = Google::Protobuf::DescriptorPool.generated_pool.lookup("protip.messages.EnumTest.Enum").enummodule
23
+ end
24
+ end
25
+
26
+ # -- Protip hack until https://github.com/google/protobuf/issues/1198 is resolved
27
+ Google::Protobuf::DescriptorPool.generated_pool.lookup("protip.messages.EnumTest").lookup("enum").instance_variable_set(:"@_protip_enum_value", "protip.messages.EnumTest.Enum")
28
+ Google::Protobuf::DescriptorPool.generated_pool.lookup("protip.messages.EnumTest").lookup("enums").instance_variable_set(:"@_protip_enum_value", "protip.messages.EnumTest.Enum")
@@ -0,0 +1,17 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # source: protip/messages/wrappers.proto
3
+
4
+ require 'google/protobuf'
5
+
6
+ require 'protip/extensions'
7
+ Google::Protobuf::DescriptorPool.generated_pool.build do
8
+ add_message "protip.messages.EnumValue" do
9
+ optional :value, :int32, 1
10
+ end
11
+ end
12
+
13
+ module Protip
14
+ module Messages
15
+ EnumValue = Google::Protobuf::DescriptorPool.generated_pool.lookup("protip.messages.EnumValue").msgclass
16
+ end
17
+ end
@@ -9,6 +9,7 @@ require 'protip/messages/money'
9
9
  require 'protip/messages/range'
10
10
  require 'protip/messages/repeated_wrappers'
11
11
  require 'protip/messages/types'
12
+ require 'protip/messages/wrappers'
12
13
  require 'google/protobuf'
13
14
  module Protip
14
15
  class StandardConverter
@@ -24,15 +25,15 @@ module Protip
24
25
 
25
26
  ## Protip types
26
27
  @conversions['protip.messages.Currency'] = {
27
- to_object: ->(message) { message.currency_code },
28
- to_message: ->(currency_code, message_class) { message_class.new currency_code: currency_code }
28
+ to_object: ->(message, field) { message.currency_code },
29
+ to_message: ->(currency_code, message_class, field) { message_class.new currency_code: currency_code }
29
30
  }
30
31
 
31
32
  @conversions['protip.messages.Money'] = {
32
- to_object: ->(message) do
33
+ to_object: ->(message, field) do
33
34
  ::Money.new(message.amount_cents, message.currency.currency_code)
34
35
  end,
35
- to_message: ->(money, message_class) do
36
+ to_message: ->(money, message_class, field) do
36
37
  raise ArgumentError unless money.is_a?(::Money)
37
38
  currency = ::Protip::Messages::Currency.new(currency_code: money.currency.iso_code.to_sym)
38
39
  message_class.new(
@@ -43,16 +44,16 @@ module Protip
43
44
  }
44
45
 
45
46
  @conversions['protip.messages.Date'] = {
46
- to_object: ->(message) { ::Date.new(message.year, message.month, message.day) },
47
- to_message: lambda do |date, message_class|
47
+ to_object: ->(message, field) { ::Date.new(message.year, message.month, message.day) },
48
+ to_message: lambda do |date, message_class, field|
48
49
  raise ArgumentError unless date.is_a?(::Date)
49
50
  message_class.new year: date.year, month: date.month, day: date.day
50
51
  end
51
52
  }
52
53
 
53
54
  @conversions['protip.messages.Range'] = {
54
- to_object: ->(message) { message.begin..message.end },
55
- to_message: ->(range, message_class) do
55
+ to_object: ->(message, field) { message.begin..message.end },
56
+ to_message: ->(range, message_class, field) do
56
57
  message_class.new(begin: range.begin.to_i, end: range.end.to_i)
57
58
  end
58
59
  }
@@ -60,43 +61,76 @@ module Protip
60
61
  ## Standard wrappers
61
62
  %w(Int64 Int32 UInt64 UInt32 Double Float String Bytes).each do |type|
62
63
  @conversions["google.protobuf.#{type}Value"] = {
63
- to_object: ->(message) { message.value },
64
- to_message: lambda do |value, message_class|
64
+ to_object: ->(message, field) { message.value },
65
+ to_message: lambda do |value, message_class, field|
65
66
  message_class.new value: value
66
67
  end
67
68
  }
68
69
  @conversions["protip.messages.Repeated#{type}"] = {
69
- to_object: ->(message) { message.values.to_a.freeze },
70
- to_message: lambda do |value, message_class|
70
+ to_object: ->(message, field) { message.values.to_a.freeze },
71
+ to_message: lambda do |value, message_class, field|
71
72
  message_class.new values: (value.is_a?(Enumerable) ? value : [value])
72
73
  end
73
74
  }
74
75
  end
75
76
 
76
77
  conversions['google.protobuf.BoolValue'] = {
77
- to_object: ->(message) { message.value },
78
- to_message: lambda do |value, message_class|
78
+ to_object: ->(message, field) { message.value },
79
+ to_message: lambda do |value, message_class, field|
79
80
  message_class.new value: value_to_boolean(value)
80
81
  end
81
82
  }
82
83
 
83
84
  conversions['protip.messages.RepeatedBool'] = {
84
- to_object: ->(message) { message.values.to_a.freeze },
85
- to_message: lambda do |value, message_class|
85
+ to_object: ->(message, field) { message.values.to_a.freeze },
86
+ to_message: lambda do |value, message_class, field|
86
87
  message_class.new values: (value.is_a?(Enumerable) ? value : [value]).map{|v| value_to_boolean(v)}
87
88
  end
88
89
 
89
90
  }
90
91
 
92
+ conversions['protip.messages.EnumValue'] = {
93
+ to_object: lambda do |message, field|
94
+ enum_for_field(field).lookup_value(message.value) || message.value
95
+ end,
96
+ to_message: lambda do |value, message_class, field|
97
+ enum_value = (value.is_a?(Fixnum) ? value : enum_for_field(field).lookup_name(value.to_sym))
98
+ if nil == enum_value
99
+ raise RangeError.new('Unknown symbol value for an EnumValue field.')
100
+ end
101
+ message_class.new(value: enum_value)
102
+ end
103
+ }
104
+
105
+ conversions['protip.messages.RepeatedEnum'] = {
106
+ to_object: lambda do |message, field|
107
+ enum = enum_for_field(field)
108
+ message.values.map{|v| enum.lookup_value(v) || v}
109
+ end,
110
+ to_message: lambda do |values, message_class, field|
111
+ enum = nil # Don't look this up unless we have to
112
+ (values = [values]) unless values.is_a?(Enumerable) # Convert to an array if a scalar was given
113
+ # Convert values in the same way we do for +protip.messages.EnumValue+
114
+ message_class.new values: (values.map do |value|
115
+ if value.is_a?(Fixnum)
116
+ value
117
+ else
118
+ enum ||= enum_for_field(field)
119
+ enum.lookup_name(value.to_sym) || (raise RangeError.new('Unknown symbol value for a RepeatedEnum field.'))
120
+ end
121
+ end)
122
+ end
123
+ }
124
+
91
125
  ## ActiveSupport objects
92
126
  conversions['protip.messages.ActiveSupport.TimeWithZone'] = {
93
- to_object: ->(message) {
127
+ to_object: ->(message, field) {
94
128
  ActiveSupport::TimeWithZone.new(
95
129
  Time.at(message.utc_timestamp).utc,
96
130
  ActiveSupport::TimeZone.new(message.time_zone_name)
97
131
  )
98
132
  },
99
- to_message: ->(value, message_class) {
133
+ to_message: ->(value, message_class, field) {
100
134
  if !value.is_a?(::ActiveSupport::TimeWithZone) && (value.is_a?(Time) || value.is_a?(DateTime))
101
135
  value = ::ActiveSupport::TimeWithZone.new(value.to_time.utc, ::ActiveSupport::TimeZone.new('UTC'))
102
136
  end
@@ -113,12 +147,12 @@ module Protip
113
147
  self.class.conversions.has_key?(message_class.descriptor.name)
114
148
  end
115
149
 
116
- def to_object(message)
117
- self.class.conversions[message.class.descriptor.name][:to_object].call(message)
150
+ def to_object(message, field)
151
+ self.class.conversions[message.class.descriptor.name][:to_object].call(message, field)
118
152
  end
119
153
 
120
- def to_message(object, message_class)
121
- self.class.conversions[message_class.descriptor.name][:to_message].call(object, message_class)
154
+ def to_message(object, message_class, field)
155
+ self.class.conversions[message_class.descriptor.name][:to_message].call(object, message_class, field)
122
156
  end
123
157
 
124
158
  class << self
@@ -130,6 +164,11 @@ module Protip
130
164
  # exception)
131
165
  value
132
166
  end
167
+
168
+ private
169
+ def enum_for_field(field)
170
+ Google::Protobuf::DescriptorPool.generated_pool.lookup(field.instance_variable_get(:'@_protip_enum_value'))
171
+ end
133
172
  end
134
173
  end
135
174
  end
@@ -187,7 +187,7 @@ module Protip
187
187
  if nil == value
188
188
  nil
189
189
  elsif converter.convertible?(field.subtype.msgclass)
190
- converter.to_object value
190
+ converter.to_object value, field
191
191
  elsif nested_resources.has_key?(field_name_sym)
192
192
  resource_klass = nested_resources[field_name_sym]
193
193
  resource_klass.new value
@@ -217,7 +217,7 @@ module Protip
217
217
  elsif value.is_a?(field.subtype.msgclass)
218
218
  value
219
219
  elsif converter.convertible?(field.subtype.msgclass)
220
- converter.to_message value, field.subtype.msgclass
220
+ converter.to_message value, field.subtype.msgclass, field
221
221
  elsif nested_resources.has_key?(field.name.to_sym)
222
222
  value.message
223
223
  else
data/test/test_helper.rb CHANGED
@@ -2,6 +2,5 @@ require 'minitest/autorun'
2
2
  require 'mocha/mini_test'
3
3
  require 'webmock/minitest'
4
4
  require 'minitest/stub/const'
5
- require 'minitest/debugger' if ENV['DEBUG']
6
5
 
7
6
  require 'minitest/pride'
@@ -63,6 +63,7 @@ module Protip::ResourceTest # Namespace for internal constants
63
63
  pool.lookup(name).msgclass
64
64
  end
65
65
  end
66
+ let(:nested_message_field) { resource_message_class.descriptor.lookup('nested_message') }
66
67
  # Stubbed API client
67
68
  let :client do
68
69
  mock.responds_like_instance_of(Class.new { include Protip::Client })
@@ -158,8 +159,8 @@ module Protip::ResourceTest # Namespace for internal constants
158
159
 
159
160
  it 'converts message types to and from their Ruby values when the converter allows' do
160
161
  converter.expects(:convertible?).at_least_once.with(nested_message_class).returns(true)
161
- converter.expects(:to_message).once.with(6, nested_message_class).returns(nested_message_class.new number: 100)
162
- converter.expects(:to_object).at_least_once.with(nested_message_class.new number: 100).returns 'intern'
162
+ converter.expects(:to_message).once.with(6, nested_message_class, nested_message_field).returns(nested_message_class.new number: 100)
163
+ converter.expects(:to_object).at_least_once.with(nested_message_class.new(number: 100), nested_message_field).returns 'intern'
163
164
 
164
165
  resource = resource_class.new
165
166
  resource.nested_message = 6
@@ -243,8 +244,8 @@ module Protip::ResourceTest # Namespace for internal constants
243
244
  before do
244
245
  # Sanity check - the user should specify all these variables in "let" statements
245
246
  # http_method, path, query_class, and response specify the expected call to the client
246
- # nested_message_field specifies the field on the query class that may or may not be convertible, and should
247
- # refer to a submessage field of type nested_message_class
247
+ # nested_message_field_name specifies the field on the query class that may or may not be convertible, and
248
+ # should refer to a submessage field of type nested_message_class
248
249
  # invoke_method! should call the desired method, assuming that +parameters+ contains the query parameters to
249
250
  # pass in (e.g. `resource_class.all(parameters)` or `resource_class.find('id', parameters)`)
250
251
  %i(
@@ -252,7 +253,7 @@ module Protip::ResourceTest # Namespace for internal constants
252
253
  path
253
254
  query_class
254
255
  response
255
- nested_message_field
256
+ nested_message_field_name
256
257
  invoke_method!
257
258
  ).each do |name|
258
259
  raise "Must define #{name} before invoking `it_converts_query_parameters`" unless respond_to?(name)
@@ -262,7 +263,7 @@ module Protip::ResourceTest # Namespace for internal constants
262
263
  client.expects(:request)
263
264
  .once
264
265
  .with(method: http_method, path: path,
265
- message: query_class.new(:"#{nested_message_field}" => nested_message_class.new(number: 43)),
266
+ message: query_class.new(:"#{nested_message_field_name}" => nested_message_class.new(number: 43)),
266
267
  response_type: (nil == response ? nil : response.class),
267
268
  ).returns(response)
268
269
  end
@@ -272,10 +273,12 @@ module Protip::ResourceTest # Namespace for internal constants
272
273
  describe 'with a convertible message' do
273
274
  before do
274
275
  resource_class.converter.stubs(:convertible?).with(nested_message_class).returns(true)
275
- resource_class.converter.stubs(:to_message).with(42, nested_message_class).returns(nested_message_class.new(number: 43))
276
+ resource_class.converter.stubs(:to_message)
277
+ .with(42, nested_message_class, query_class.descriptor.lookup(nested_message_field_name.to_s))
278
+ .returns(nested_message_class.new(number: 43))
276
279
  end
277
280
 
278
- let(:parameters) { {"#{nested_message_field}" => 42} }
281
+ let(:parameters) { {"#{nested_message_field_name}" => 42} }
279
282
  it 'converts query parameters' do
280
283
  invoke_method!
281
284
  end
@@ -288,14 +291,14 @@ module Protip::ResourceTest # Namespace for internal constants
288
291
  end
289
292
 
290
293
  describe 'with a hash' do
291
- let(:parameters) { {"#{nested_message_field}" => {number: 43}} }
294
+ let(:parameters) { {"#{nested_message_field_name}" => {number: 43}} }
292
295
  it 'allows a hash to be provided for the nested message' do
293
296
  invoke_method!
294
297
  end
295
298
  end
296
299
 
297
300
  describe 'with a submessage' do
298
- let(:parameters) { {"#{nested_message_field}" => nested_message_class.new(number: 43)} }
301
+ let(:parameters) { {"#{nested_message_field_name}" => nested_message_class.new(number: 43)} }
299
302
  it 'allows a submessage to be provided directly' do
300
303
  invoke_method!
301
304
  end
@@ -398,7 +401,7 @@ module Protip::ResourceTest # Namespace for internal constants
398
401
  let(:http_method) { Net::HTTP::Get }
399
402
  let(:path) { 'base_path' }
400
403
  let(:query_class) { resource_query_class }
401
- let(:nested_message_field) { :nested_message }
404
+ let(:nested_message_field_name) { :nested_message }
402
405
  let(:invoke_method!) { resource_class.all(parameters) }
403
406
  it_converts_query_parameters
404
407
  end
@@ -482,7 +485,7 @@ module Protip::ResourceTest # Namespace for internal constants
482
485
  let(:http_method) { Net::HTTP::Get }
483
486
  let(:path) { 'base_path/5' }
484
487
  let(:query_class) { resource_query_class }
485
- let(:nested_message_field) { :nested_message }
488
+ let(:nested_message_field_name) { :nested_message }
486
489
  let(:invoke_method!) { resource_class.find 5, parameters }
487
490
  it_converts_query_parameters
488
491
  end
@@ -571,9 +574,9 @@ module Protip::ResourceTest # Namespace for internal constants
571
574
  describe '(message attributes)' do
572
575
  before do
573
576
  converter.stubs(:convertible?).with(nested_message_class).returns(true)
574
- converter.stubs(:to_message).with(42, nested_message_class).returns(nested_message_class.new(number: 52))
575
- converter.stubs(:to_object).with(nested_message_class.new(number: 52)).returns(42)
576
- converter.stubs(:to_object).with(nested_message_class.new(number: 62)).returns(72)
577
+ converter.stubs(:to_message).with(42, nested_message_class, nested_message_field).returns(nested_message_class.new(number: 52))
578
+ converter.stubs(:to_object).with(nested_message_class.new(number: 52), nested_message_field).returns(42)
579
+ converter.stubs(:to_object).with(nested_message_class.new(number: 62), nested_message_field).returns(72)
577
580
  end
578
581
  it 'marks convertible messages as changed if they are changed as Ruby values' do
579
582
  setter.set nested_message: 42
@@ -937,7 +940,7 @@ module Protip::ResourceTest # Namespace for internal constants
937
940
  let(:http_method) { Net::HTTP::Post }
938
941
  let(:path) { path }
939
942
  let(:query_class) { action_query_class }
940
- let(:nested_message_field) { :nested_message }
943
+ let(:nested_message_field_name) { :nested_message }
941
944
  let(:invoke_method!) { target.action(parameters) }
942
945
  it_converts_query_parameters
943
946
  end
@@ -4,8 +4,23 @@ require 'money'
4
4
  require 'google/protobuf/wrappers'
5
5
  require 'protip/messages/active_support/time_with_zone'
6
6
  require 'protip/standard_converter'
7
+ require 'protip/messages/test'
7
8
 
8
9
  describe Protip::StandardConverter do
10
+ let :pool do
11
+ # See https://github.com/google/protobuf/blob/master/ruby/tests/generated_code.rb for
12
+ # examples of field types you can add here
13
+ pool = Google::Protobuf::DescriptorPool.new
14
+ pool.build do
15
+ add_enum 'number' do
16
+ value :ZERO, 0
17
+ value :ONE, 1
18
+ end
19
+ end
20
+ pool
21
+ end
22
+ let(:enum) { pool.lookup 'number' }
23
+
9
24
  let(:converter) { Protip::StandardConverter.new }
10
25
 
11
26
  let(:integer_types) do
@@ -94,6 +109,8 @@ describe Protip::StandardConverter do
94
109
  end
95
110
  end
96
111
 
112
+ let(:field) { mock.responds_like_instance_of Google::Protobuf::FieldDescriptor }
113
+
97
114
  describe '#to_object' do
98
115
  it 'converts wrapper types' do
99
116
  {
@@ -104,7 +121,7 @@ describe Protip::StandardConverter do
104
121
  bytes_value => bytes_types,
105
122
  }.each do |value, message_types|
106
123
  message_types.each do |message_class|
107
- assert_equal value, converter.to_object(message_class.new value: value)
124
+ assert_equal value, converter.to_object(message_class.new(value: value), field)
108
125
  end
109
126
  end
110
127
  end
@@ -118,7 +135,7 @@ describe Protip::StandardConverter do
118
135
  bytes_value => repeated_bytes_types,
119
136
  }.each do |value, message_types|
120
137
  message_types.each do |message_class|
121
- result = converter.to_object(message_class.new values: [value])
138
+ result = converter.to_object(message_class.new(values: [value]), field)
122
139
  assert_equal [value], result
123
140
  exception = assert_raises RuntimeError do
124
141
  result << value
@@ -129,26 +146,26 @@ describe Protip::StandardConverter do
129
146
  end
130
147
 
131
148
  it 'converts dates' do
132
- date = converter.to_object(::Protip::Messages::Date.new year: 2015, month: 2, day: 9)
149
+ date = converter.to_object(::Protip::Messages::Date.new(year: 2015, month: 2, day: 9), field)
133
150
  assert_instance_of ::Date, date
134
151
  assert_equal '2015-02-09', date.strftime
135
152
  end
136
153
 
137
154
  it 'converts ranges' do
138
- range = converter.to_object(::Protip::Messages::Range.new begin: 1, end: 4)
155
+ range = converter.to_object(::Protip::Messages::Range.new(begin: 1, end: 4), field)
139
156
  assert_instance_of ::Range, range
140
157
  assert_equal 1..4, range
141
158
  end
142
159
 
143
160
  it 'converts currency' do
144
- currency = converter.to_object(::Protip::Messages::Currency.new currency_code: :GBP)
161
+ currency = converter.to_object(::Protip::Messages::Currency.new(currency_code: :GBP), field)
145
162
  assert_equal :GBP, currency
146
163
  end
147
164
 
148
165
  it 'converts money' do
149
166
  message = ::Protip::Messages::Money.new amount_cents: 250,
150
167
  currency: (::Protip::Messages::Currency.new currency_code: :CAD)
151
- money = converter.to_object(message)
168
+ money = converter.to_object(message, field)
152
169
  assert_instance_of ::Money, money
153
170
  assert_equal Money::Currency.new(:CAD), money.currency
154
171
  assert_equal 250, money.fractional
@@ -158,11 +175,42 @@ describe Protip::StandardConverter do
158
175
  it 'converts times with zones' do
159
176
  message = ::Protip::Messages::ActiveSupport::TimeWithZone.new utc_timestamp: 1451610000,
160
177
  time_zone_name: 'America/Los_Angeles'
161
- time = converter.to_object(message)
178
+ time = converter.to_object(message, field)
162
179
  assert_instance_of ::ActiveSupport::TimeWithZone, time
163
180
  assert_equal 1451610000, time.to_i
164
181
  assert_equal '2015-12-31T17:00:00-08:00', time.iso8601
165
182
  end
183
+
184
+ describe 'enums' do
185
+ before do
186
+ ::Protip::StandardConverter.stubs(:enum_for_field).with(field).returns(enum)
187
+ end
188
+
189
+ # Make sure we mirror the behavior of an actual enum field on the message.
190
+ it 'converts enum values in range to symbols' do
191
+ message = ::Protip::Messages::EnumValue.new value: 1
192
+ assert_equal :ONE, converter.to_object(message, field)
193
+ end
194
+
195
+ it 'converts enum values out of range to integers' do
196
+ message = ::Protip::Messages::EnumValue.new value: 5
197
+ assert_equal 5, converter.to_object(message, field)
198
+ end
199
+
200
+ it 'converts repeated enum values in range to symbols' do
201
+ message = ::Protip::Messages::RepeatedEnum.new values: [0, 1]
202
+ assert_equal [:ZERO, :ONE], converter.to_object(message, field)
203
+ end
204
+
205
+ it 'converts repeated enum values out of range to integers' do
206
+ message = ::Protip::Messages::RepeatedEnum.new values: [3, 1, 5]
207
+ assert_equal [3, :ONE, 5], converter.to_object(message, field)
208
+ end
209
+ end
210
+ end
211
+
212
+ describe '.enum_for_field' do
213
+ # TODO pending https://github.com/google/protobuf/issues/1198
166
214
  end
167
215
 
168
216
  describe '#to_message' do
@@ -175,7 +223,7 @@ describe Protip::StandardConverter do
175
223
  bytes_value => bytes_types,
176
224
  }.each do |value, message_types|
177
225
  message_types.each do |message_class|
178
- assert_equal message_class.new(value: value), converter.to_message(value, message_class)
226
+ assert_equal message_class.new(value: value), converter.to_message(value, message_class, field)
179
227
  end
180
228
  end
181
229
  end
@@ -189,7 +237,7 @@ describe Protip::StandardConverter do
189
237
  bytes_value => repeated_bytes_types,
190
238
  }.each do |value, message_types|
191
239
  message_types.each do |message_class|
192
- assert_equal message_class.new(values: [value]), converter.to_message(value, message_class)
240
+ assert_equal message_class.new(values: [value]), converter.to_message(value, message_class, field)
193
241
  end
194
242
  end
195
243
  end
@@ -203,7 +251,9 @@ describe Protip::StandardConverter do
203
251
  bytes_value => repeated_bytes_types,
204
252
  }.each do |value, message_types|
205
253
  message_types.each do |message_class|
206
- assert_equal message_class.new(values: [value, value]), converter.to_message([value, value], message_class)
254
+ assert_equal message_class.new(values: [value, value]), converter.to_message(
255
+ [value, value], message_class, field
256
+ )
207
257
  end
208
258
  end
209
259
  end
@@ -211,23 +261,23 @@ describe Protip::StandardConverter do
211
261
  it 'converts dates' do
212
262
  date = ::Date.new(2012, 5, 7)
213
263
  assert_equal 7, date.day # Sanity check argument order
214
- assert_equal ::Protip::Messages::Date.new(year: 2012, month: 5, day: 7), converter.to_message(date, ::Protip::Messages::Date)
264
+ assert_equal ::Protip::Messages::Date.new(year: 2012, month: 5, day: 7), converter.to_message(date, ::Protip::Messages::Date, field)
215
265
  end
216
266
 
217
267
  it 'converts ranges' do
218
268
  range = -1..34
219
- assert_equal ::Protip::Messages::Range.new(begin: -1, end: 34), converter.to_message(range, ::Protip::Messages::Range)
269
+ assert_equal ::Protip::Messages::Range.new(begin: -1, end: 34), converter.to_message(range, ::Protip::Messages::Range, field)
220
270
  end
221
271
 
222
272
  it 'converts currency' do
223
273
  currency = :HKD
224
- message = converter.to_message(currency, ::Protip::Messages::Currency)
274
+ message = converter.to_message(currency, ::Protip::Messages::Currency, field)
225
275
  assert_equal ::Protip::Messages::Currency.new(currency_code: currency), message
226
276
  end
227
277
 
228
278
  it 'converts money' do
229
279
  money = ::Money.new(250, 'CAD')
230
- message = converter.to_message(money, ::Protip::Messages::Money)
280
+ message = converter.to_message(money, ::Protip::Messages::Money, field)
231
281
  assert_instance_of ::Protip::Messages::Money, message
232
282
  assert_equal ::Protip::Messages::Money.new(
233
283
  amount_cents: money.cents,
@@ -240,21 +290,21 @@ describe Protip::StandardConverter do
240
290
  it 'converts truthy values to booleans' do
241
291
  [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].each do |truth_value|
242
292
  assert_equal Google::Protobuf::BoolValue.new(value: true),
243
- converter.to_message(truth_value, Google::Protobuf::BoolValue)
293
+ converter.to_message(truth_value, Google::Protobuf::BoolValue, field)
244
294
  end
245
295
  end
246
296
 
247
297
  it 'converts falsey values to booleans' do
248
298
  [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].each do |false_value|
249
299
  assert_equal Google::Protobuf::BoolValue.new(value: false),
250
- converter.to_message(false_value, Google::Protobuf::BoolValue)
300
+ converter.to_message(false_value, Google::Protobuf::BoolValue, field)
251
301
  end
252
302
  end
253
303
 
254
304
  it 'raises an exception if non-boolean values passed to boolean field' do
255
305
  [nil, 'test', Object.new, 2, {}, []].each do |bad_value|
256
306
  assert_raises TypeError do
257
- converter.to_message(bad_value, Google::Protobuf::BoolValue)
307
+ converter.to_message(bad_value, Google::Protobuf::BoolValue, field)
258
308
  end
259
309
  end
260
310
  end
@@ -262,24 +312,119 @@ describe Protip::StandardConverter do
262
312
  it 'converts times with zones' do
263
313
  time_with_zone = ::ActiveSupport::TimeWithZone.new(Time.new(2016, 1, 1, 0, 0, 0, 0),
264
314
  ::ActiveSupport::TimeZone.new('America/New_York'))
265
- message = converter.to_message(time_with_zone, ::Protip::Messages::ActiveSupport::TimeWithZone)
315
+ message = converter.to_message(time_with_zone, ::Protip::Messages::ActiveSupport::TimeWithZone, field)
266
316
  assert_equal 1451606400, message.utc_timestamp
267
317
  assert_equal 'America/New_York', message.time_zone_name
268
318
  end
269
319
 
270
320
  it 'converts times without zones' do
271
321
  time = Time.new(2016, 1, 1, 0, 0, 0, -3600)
272
- message = converter.to_message(time, ::Protip::Messages::ActiveSupport::TimeWithZone)
322
+ message = converter.to_message(time, ::Protip::Messages::ActiveSupport::TimeWithZone, field)
273
323
  assert_equal 1451610000, message.utc_timestamp
274
324
  assert_equal 'UTC', message.time_zone_name
275
325
  end
276
326
 
277
327
  it 'converts datetimes without zones' do
278
328
  datetime = DateTime.new(2016, 1, 1, 0, 0, 0, '-1')
279
- message = converter.to_message(datetime, ::Protip::Messages::ActiveSupport::TimeWithZone)
329
+ message = converter.to_message(datetime, ::Protip::Messages::ActiveSupport::TimeWithZone, field)
280
330
  assert_equal 1451610000, message.utc_timestamp
281
331
  assert_equal 'UTC', message.time_zone_name
282
332
 
283
333
  end
334
+
335
+ describe 'enums' do
336
+ before do
337
+ ::Protip::StandardConverter.stubs(:enum_for_field).with(field).returns(enum)
338
+ end
339
+ def convert(value)
340
+ converter.to_message value, message_class, field
341
+ end
342
+ %w(zero one two).each do |number| # values symbolizing as :ZERO, :ONE, :TWO
343
+ let number do
344
+ value = mock
345
+ value.stubs(:to_sym).returns(number.upcase.to_sym)
346
+ value
347
+ end
348
+ end
349
+
350
+ describe '::Protip::Messages::EnumValue' do
351
+ let(:message_class) { ::Protip::Messages::EnumValue }
352
+ # Ensure identical behavior to setting a standard enum field
353
+ it 'converts integers' do
354
+ assert_equal 1, convert(1).value, 'improper conversion of an in-range integer'
355
+ assert_equal 4, convert(4).value, 'improper conversion of an out-of-range integer'
356
+ end
357
+ it 'converts non-integers via to_sym' do
358
+ assert_equal 1, convert(one).value, 'improper conversion of non-integer value'
359
+ end
360
+ it 'throws an error when a non-existent symbol is given' do
361
+ assert_raises RangeError do
362
+ convert(two)
363
+ end
364
+ end
365
+ end
366
+
367
+ describe '::Protip::Messages::RepeatedEnum' do
368
+ let(:message_class) { ::Protip::Messages::RepeatedEnum }
369
+ it 'converts integers' do
370
+ assert_equal [1, 4], convert([1, 4]).values
371
+ end
372
+ it 'converts non-integers via to_sym' do
373
+ assert_equal [0, 2, 1], convert([zero, 2, one]).values
374
+ end
375
+ it 'throws an error when a non-existent symbol is given' do
376
+ assert_raises RangeError do
377
+ convert([0, two])
378
+ end
379
+ end
380
+ it 'allows assigning a scalar value' do
381
+ assert_equal [1], convert(one).values
382
+ end
383
+ end
384
+ end
284
385
  end
386
+
387
+ describe '(enums - functional)' do # Temp - test an actual compiled file to make sure our options hack is working
388
+ let(:wrapped_message) { Protip::Messages::EnumTest.new }
389
+ let(:wrapper) { Protip::Wrapper.new wrapped_message, converter }
390
+
391
+ let(:value_map) do
392
+ {
393
+ :ONE => :ONE,
394
+ 1 => :ONE,
395
+ 2 => 2,
396
+ }
397
+ end
398
+
399
+ it 'allows setting and getting a scalar field by Ruby value' do
400
+ value_map.each do |value, expected|
401
+ wrapper.enum = value
402
+ assert_equal expected, wrapper.enum
403
+ end
404
+ assert_raises RangeError do
405
+ wrapper.enum = :TWO
406
+ end
407
+ end
408
+ it 'allows setting and getting a scalar field by message' do
409
+ wrapper.enum = ::Protip::Messages::EnumValue.new(value: 1)
410
+ assert_equal :ONE, wrapper.enum
411
+ end
412
+
413
+ it 'allows setting and getting a repeated field by Ruby value' do
414
+ value_map.each do |value, expected|
415
+ wrapper.enums = [value]
416
+ assert_equal [expected], wrapper.enums
417
+ end
418
+ assert_raises RangeError do
419
+ wrapper.enums = [:TWO]
420
+ end
421
+ end
422
+ it 'allows setting and geting a repeated field by message' do
423
+ wrapper.enums = ::Protip::Messages::RepeatedEnum.new(values: [2])
424
+ assert_equal [2], wrapper.enums
425
+ end
426
+
427
+
428
+ end
429
+
285
430
  end
@@ -59,6 +59,7 @@ module Protip::WrapperTest # namespace for internal constants
59
59
  pool.lookup(name).msgclass
60
60
  end
61
61
  end
62
+ let (:inner_message_field) { message_class.descriptor.lookup('inner') }
62
63
 
63
64
  # Stubbed API client
64
65
  let :client do
@@ -209,7 +210,7 @@ module Protip::WrapperTest # namespace for internal constants
209
210
  end
210
211
 
211
212
  it 'converts scalar Ruby values to protobuf messages' do
212
- converter.expects(:to_message).once.with(45, inner_message_class).returns(inner_message_class.new(value: 43))
213
+ converter.expects(:to_message).once.with(45, inner_message_class, inner_message_field).returns(inner_message_class.new(value: 43))
213
214
  wrapper.assign_attributes inner: 45
214
215
  assert_equal inner_message_class.new(value: 43), wrapped_message.inner
215
216
  end
@@ -329,7 +330,7 @@ module Protip::WrapperTest # namespace for internal constants
329
330
  describe 'with a nested convertible message' do
330
331
  before do
331
332
  converter.stubs(:convertible?).with(inner_message_class).returns true
332
- [1, 2, 3].each{|i| converter.stubs(:to_object).with(inner_message_class.new(value: i)).returns(i)}
333
+ [1, 2, 3].each{|i| converter.stubs(:to_object).with(inner_message_class.new(value: i), anything).returns(i)}
333
334
  end
334
335
  it 'returns a hash with the nested message converted' do
335
336
  assert_equal 1, wrapper.to_h[:inner]
@@ -385,7 +386,7 @@ module Protip::WrapperTest # namespace for internal constants
385
386
 
386
387
  it 'converts convertible messages' do
387
388
  converter.expects(:convertible?).with(inner_message_class).once.returns(true)
388
- converter.expects(:to_object).once.with(inner_message_class.new(value: 25)).returns 40
389
+ converter.expects(:to_object).once.with(inner_message_class.new(value: 25), inner_message_field).returns 40
389
390
  assert_equal 40, wrapper.inner
390
391
  end
391
392
 
@@ -445,7 +446,7 @@ module Protip::WrapperTest # namespace for internal constants
445
446
 
446
447
  it 'converts convertible messages' do
447
448
  converter.expects(:convertible?).at_least_once.with(inner_message_class).returns(true)
448
- converter.expects(:to_message).with(40, inner_message_class).returns(inner_message_class.new(value: 30))
449
+ converter.expects(:to_message).with(40, inner_message_class, inner_message_field).returns(inner_message_class.new(value: 30))
449
450
 
450
451
  wrapper.inner = 40
451
452
  assert_equal inner_message_class.new(value: 30), wrapper.message.inner
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.19.2
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - AngelList
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-08 00:00:00.000000000 Z
11
+ date: 2016-03-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -98,20 +98,6 @@ dependencies:
98
98
  - - "~>"
99
99
  - !ruby/object:Gem::Version
100
100
  version: '5.0'
101
- - !ruby/object:Gem::Dependency
102
- name: minitest-debugger
103
- requirement: !ruby/object:Gem::Requirement
104
- requirements:
105
- - - "~>"
106
- - !ruby/object:Gem::Version
107
- version: '1.0'
108
- type: :development
109
- prerelease: false
110
- version_requirements: !ruby/object:Gem::Requirement
111
- requirements:
112
- - - "~>"
113
- - !ruby/object:Gem::Version
114
- version: '1.0'
115
101
  - !ruby/object:Gem::Dependency
116
102
  name: minitest-stub-const
117
103
  requirement: !ruby/object:Gem::Requirement
@@ -193,6 +179,7 @@ extensions: []
193
179
  extra_rdoc_files: []
194
180
  files:
195
181
  - definitions/google/protobuf/wrappers.proto
182
+ - definitions/protip/extensions.proto
196
183
  - definitions/protip/messages/active_support/time_with_zone.proto
197
184
  - definitions/protip/messages/array.proto
198
185
  - definitions/protip/messages/currency.proto
@@ -200,12 +187,16 @@ files:
200
187
  - definitions/protip/messages/money.proto
201
188
  - definitions/protip/messages/range.proto
202
189
  - definitions/protip/messages/repeated_wrappers.proto
190
+ - definitions/protip/messages/test.proto
203
191
  - definitions/protip/messages/types.proto
192
+ - definitions/protip/messages/wrappers.proto
193
+ - lib/google/protobuf/descriptor.rb
204
194
  - lib/google/protobuf/wrappers.rb
205
195
  - lib/protip.rb
206
196
  - lib/protip/client.rb
207
197
  - lib/protip/converter.rb
208
198
  - lib/protip/error.rb
199
+ - lib/protip/extensions.rb
209
200
  - lib/protip/messages/active_support/time_with_zone.rb
210
201
  - lib/protip/messages/array.rb
211
202
  - lib/protip/messages/currency.rb
@@ -213,7 +204,9 @@ files:
213
204
  - lib/protip/messages/money.rb
214
205
  - lib/protip/messages/range.rb
215
206
  - lib/protip/messages/repeated_wrappers.rb
207
+ - lib/protip/messages/test.rb
216
208
  - lib/protip/messages/types.rb
209
+ - lib/protip/messages/wrappers.rb
217
210
  - lib/protip/resource.rb
218
211
  - lib/protip/resource/associations/association.rb
219
212
  - lib/protip/resource/associations/belongs_to_association.rb