protip 0.20.7 → 0.30.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/protip.rb +10 -0
  3. data/lib/protip/{wrapper.rb → decorator.rb} +98 -82
  4. data/lib/protip/extensions.rb +0 -1
  5. data/lib/protip/resource.rb +56 -32
  6. data/lib/protip/resource/extra_methods.rb +5 -3
  7. data/lib/protip/tasks/compile.rake +2 -2
  8. data/lib/protip/transformer.rb +14 -0
  9. data/lib/protip/transformers/abstract_transformer.rb +11 -0
  10. data/lib/protip/transformers/active_support/time_with_zone_transformer.rb +35 -0
  11. data/lib/protip/transformers/decorating_transformer.rb +33 -0
  12. data/lib/protip/transformers/default_transformer.rb +22 -0
  13. data/lib/protip/transformers/delegating_transformer.rb +44 -0
  14. data/lib/protip/transformers/deprecated_transformer.rb +90 -0
  15. data/lib/protip/transformers/enum_transformer.rb +93 -0
  16. data/lib/protip/transformers/primitives_transformer.rb +78 -0
  17. data/lib/protip/transformers/timestamp_transformer.rb +31 -0
  18. data/test/functional/protip/decorator_test.rb +204 -0
  19. data/test/unit/protip/decorator_test.rb +665 -0
  20. data/test/unit/protip/resource_test.rb +76 -92
  21. data/test/unit/protip/transformers/decorating_transformer_test.rb +92 -0
  22. data/test/unit/protip/transformers/delegating_transformer_test.rb +83 -0
  23. data/test/unit/protip/transformers/deprecated_transformer_test.rb +139 -0
  24. data/test/unit/protip/transformers/enum_transformer_test.rb +157 -0
  25. data/test/unit/protip/transformers/primitives_transformer_test.rb +139 -0
  26. data/test/unit/protip/transformers/timestamp_transformer_test.rb +46 -0
  27. metadata +23 -12
  28. data/definitions/google/protobuf/wrappers.proto +0 -99
  29. data/lib/google/protobuf/descriptor.rb +0 -1
  30. data/lib/google/protobuf/wrappers.rb +0 -48
  31. data/lib/protip/converter.rb +0 -22
  32. data/lib/protip/standard_converter.rb +0 -185
  33. data/test/unit/protip/standard_converter_test.rb +0 -502
  34. data/test/unit/protip/wrapper_test.rb +0 -646
@@ -1,7 +1,6 @@
1
1
  require 'test_helper'
2
2
 
3
3
  require 'protip/client'
4
- require 'protip/converter'
5
4
  require 'protip/resource'
6
5
 
7
6
  module Protip::ResourceTest # Namespace for internal constants
@@ -84,16 +83,16 @@ module Protip::ResourceTest # Namespace for internal constants
84
83
 
85
84
  describe '.resource' do
86
85
 
87
- let :converter do
86
+ let :transformer do
88
87
  Class.new do
89
- include Protip::Converter
88
+ include Protip::Transformer
90
89
  end.new
91
90
  end
92
91
  describe 'with basic resource' do
93
92
  before do
94
- resource_class.class_exec(converter, resource_message_class) do |converter, message|
93
+ resource_class.class_exec(transformer, resource_message_class) do |transformer, message|
95
94
  resource actions: [], message: message
96
- self.converter = converter
95
+ self.transformer = transformer
97
96
  end
98
97
  end
99
98
 
@@ -143,24 +142,21 @@ module Protip::ResourceTest # Namespace for internal constants
143
142
  assert_equal 'intern', resource.string
144
143
  end
145
144
 
146
- it 'never checks with the converter when setting simple types' do
147
- converter.expects(:convertible?).never
145
+ it 'never checks with the transformer when setting simple types' do
146
+ transformer.expects(:to_message).never
148
147
  resource = resource_class.new
149
148
  resource.string = 'intern'
150
149
  end
151
150
 
152
- it 'checks with the converter when setting message types' do
153
- converter.expects(:convertible?).at_least_once.with(nested_message_class).returns(false)
154
- resource = resource_class.new
155
- assert_raises(ArgumentError) do
156
- resource.nested_message = 5
157
- end
158
- end
159
-
160
- it 'converts message types to and from their Ruby values when the converter allows' do
161
- converter.expects(:convertible?).at_least_once.with(nested_message_class).returns(true)
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'
151
+ it 'transforms message types to and from their Ruby values' do
152
+ transformer.expects(:to_message).
153
+ once.
154
+ with(6, nested_message_field).
155
+ returns(nested_message_class.new number: 100)
156
+ transformer.expects(:to_object).
157
+ at_least_once.
158
+ with(nested_message_class.new(number: 100), nested_message_field).
159
+ returns 'intern'
164
160
 
165
161
  resource = resource_class.new
166
162
  resource.nested_message = 6
@@ -177,37 +173,27 @@ module Protip::ResourceTest # Namespace for internal constants
177
173
  refute resource.number?(:ONE)
178
174
  end
179
175
 
180
- it 'defines query methods for the booleans on its message' do
176
+ it 'defines query methods for the primitives on its message' do
181
177
  resource.boolean = true
182
178
  assert_respond_to resource, :boolean?
183
179
  assert_equal true, resource.boolean?
184
180
  end
185
181
 
186
- it 'defines query methods for the google.protobuf.BoolValues on its message' do
182
+ it 'defines query methods for the submessages on its message' do
187
183
  assert_respond_to resource, :google_bool_value?
188
184
  assert_equal false, resource.google_bool_value?
189
185
  end
190
186
 
191
- it 'does not define query methods for repeated enums' do
192
- refute_respond_to resource, :numbers?
193
- assert_raises NoMethodError do
194
- resource.numbers?(:ZERO)
195
- end
196
- end
197
-
198
- it 'does not define query methods for non-enum fields' do
199
- refute_respond_to resource, :inner?
200
- assert_raises NoMethodError do
201
- resource.inner?(:ZERO)
202
- end
187
+ it 'defines query methods for repeated fields' do
188
+ assert_respond_to resource, :numbers?
189
+ assert_equal false, resource.numbers?
203
190
  end
204
191
  end
205
192
  end
206
193
  describe 'with empty nested resources' do
207
194
  it 'does not throw an error' do
208
- resource_class.class_exec(converter, resource_message_class) do |converter, message|
195
+ resource_class.class_exec(resource_message_class) do |message|
209
196
  resource actions: [], message: message, nested_resources: {}
210
- self.converter = converter
211
197
  end
212
198
  end
213
199
  end
@@ -215,11 +201,10 @@ module Protip::ResourceTest # Namespace for internal constants
215
201
  describe 'with invalid nested resource key' do
216
202
  it 'throws an error' do
217
203
  assert_raises RuntimeError do
218
- resource_class.class_exec(converter, resource_message_class) do |converter, message|
204
+ resource_class.class_exec(resource_message_class) do |message|
219
205
  resource actions: [],
220
206
  message: message,
221
207
  nested_resources: {'snoop' => Protip::Resource}
222
- self.converter = converter
223
208
  end
224
209
  end
225
210
  end
@@ -228,9 +213,8 @@ module Protip::ResourceTest # Namespace for internal constants
228
213
  describe 'with invalid nested resource class' do
229
214
  it 'throws an error' do
230
215
  assert_raises RuntimeError do
231
- resource_class.class_exec(converter, resource_message_class) do |converter, message|
216
+ resource_class.class_exec(resource_message_class) do |message|
232
217
  resource actions: [], message: message, nested_resources: {dogg: Object}
233
- self.converter = converter
234
218
  end
235
219
  end
236
220
  end
@@ -242,12 +226,16 @@ module Protip::ResourceTest # Namespace for internal constants
242
226
  # queries
243
227
  def self.it_converts_query_parameters
244
228
  before do
245
- # Sanity check - the user should specify all these variables in "let" statements
246
- # http_method, path, query_class, and response specify the expected call to the client
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
249
- # invoke_method! should call the desired method, assuming that +parameters+ contains the query parameters to
250
- # pass in (e.g. `resource_class.all(parameters)` or `resource_class.find('id', parameters)`)
229
+ # Sanity check - the user should specify all these variables
230
+ # in "let" statements http_method, path, query_class, and
231
+ # response specify the expected call to the client
232
+ # nested_message_field_name specifies the field on the query
233
+ # class that may or may not be convertible, and should refer
234
+ # to a submessage field of type nested_message_class
235
+ # invoke_method! should call the desired method, assuming that
236
+ # +parameters+ contains the query parameters to pass in
237
+ # (e.g. `resource_class.all(parameters)` or
238
+ # `resource_class.find('id', parameters)`)
251
239
  %i(
252
240
  http_method
253
241
  path
@@ -268,40 +256,34 @@ module Protip::ResourceTest # Namespace for internal constants
268
256
  ).returns(response)
269
257
  end
270
258
 
271
-
272
-
273
- describe 'with a convertible message' do
259
+ describe 'with a transformable object as one of the attributes' do
274
260
  before do
275
- resource_class.converter.stubs(:convertible?).with(nested_message_class).returns(true)
276
- resource_class.converter.stubs(:to_message)
277
- .with(42, nested_message_class, query_class.descriptor.lookup(nested_message_field_name.to_s))
261
+ resource_class.transformer.stubs(:to_message)
262
+ .with(42, query_class.descriptor.lookup(nested_message_field_name.to_s))
278
263
  .returns(nested_message_class.new(number: 43))
279
264
  end
280
265
 
281
266
  let(:parameters) { {"#{nested_message_field_name}" => 42} }
282
- it 'converts query parameters' do
267
+ it 'transforms query parameters' do
283
268
  invoke_method!
284
269
  end
285
270
  end
286
271
 
287
- describe 'with an inconvertible message' do
288
- before do
289
- resource_class.converter.stubs(:convertible?).with(nested_message_class).returns(false)
290
- resource_class.converter.expects(:to_message).never
272
+ describe 'with a submessage as one of the attributes' do
273
+ let(:parameters) { {"#{nested_message_field_name}" => nested_message_class.new(number: 43)} }
274
+ it 'allows a submessage to be provided directly' do
275
+ invoke_method!
291
276
  end
277
+ end
292
278
 
293
- describe 'with a hash' do
294
- let(:parameters) { {"#{nested_message_field_name}" => {number: 43}} }
295
- it 'allows a hash to be provided for the nested message' do
296
- invoke_method!
297
- end
279
+ describe 'with a message as the main argument' do
280
+ let(:parameters) do
281
+ query_class.new(
282
+ :"#{nested_message_field_name}" => nested_message_class.new(number: 43)
283
+ )
298
284
  end
299
-
300
- describe 'with a submessage' do
301
- let(:parameters) { {"#{nested_message_field_name}" => nested_message_class.new(number: 43)} }
302
- it 'allows a submessage to be provided directly' do
303
- invoke_method!
304
- end
285
+ it 'allows a complete message to be provided directly' do
286
+ invoke_method!
305
287
  end
306
288
  end
307
289
  end
@@ -537,17 +519,14 @@ module Protip::ResourceTest # Namespace for internal constants
537
519
  def self.describe_dirty_attributes_setter(setter_class)
538
520
 
539
521
  describe 'dirty attributes' do
540
- let :converter do
541
- Class.new do
542
- include Protip::Converter
543
- end.new
522
+ let(:transformer) do
523
+ mock.responds_like_instance_of(Class.new { include Protip::Transformer })
544
524
  end
545
-
546
525
  let :resource do
547
526
  resource_class.new resource_message_class.new({
548
- string: 'foo',
549
- nested_message: nested_message_class.new(number: 32)
550
- })
527
+ string: 'foo',
528
+ nested_message: nested_message_class.new(number: 32)
529
+ })
551
530
  end
552
531
 
553
532
  let :setter do
@@ -555,7 +534,7 @@ module Protip::ResourceTest # Namespace for internal constants
555
534
  end
556
535
 
557
536
  before do
558
- resource_class.converter = converter
537
+ resource_class.transformer = transformer
559
538
  raise 'sanity check failed' if resource.changed? || resource.string_changed? || resource.nested_message_changed?
560
539
  end
561
540
 
@@ -573,12 +552,19 @@ module Protip::ResourceTest # Namespace for internal constants
573
552
 
574
553
  describe '(message attributes)' do
575
554
  before do
576
- converter.stubs(:convertible?).with(nested_message_class).returns(true)
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)
555
+ transformer.stubs(:to_message).
556
+ with(42, nested_message_field).
557
+ returns(nested_message_class.new(number: 52))
558
+
559
+ transformer.stubs(:to_object).
560
+ with(nested_message_class.new(number: 52), nested_message_field).
561
+ returns(42)
562
+
563
+ transformer.stubs(:to_object).
564
+ with(nested_message_class.new(number: 62), nested_message_field).
565
+ returns(72)
580
566
  end
581
- it 'marks convertible messages as changed if they are changed as Ruby values' do
567
+ it 'marks messages as changed if they are changed as Ruby values' do
582
568
  setter.set nested_message: 42
583
569
  assert resource.changed?, 'resource was not marked as changed'
584
570
  assert resource.nested_message_changed?, 'field was not marked as changed'
@@ -593,7 +579,7 @@ module Protip::ResourceTest # Namespace for internal constants
593
579
  assert resource.changed?, 'resource was not marked as changed'
594
580
  assert resource.nested_message_changed?, 'field was not marked as changed'
595
581
  end
596
- it 'recognizes when convertible messages are not changed when set as Ruby values' do
582
+ it 'recognizes when messages are not changed when set as Ruby values' do
597
583
  resource.message.nested_message.number = 52
598
584
  raise 'sanity check failed' if resource.changed? || resource.nested_message_changed?
599
585
  setter.set nested_message: 42
@@ -620,7 +606,7 @@ module Protip::ResourceTest # Namespace for internal constants
620
606
  it 'delegates writes to the wrapper object' do
621
607
  resource = resource_class.new
622
608
  test_string = 'new'
623
- Protip::Wrapper.any_instance.expects(:string=).with(test_string)
609
+ Protip::Decorator.any_instance.expects(:string=).with(test_string)
624
610
  resource.string = test_string
625
611
  end
626
612
 
@@ -647,7 +633,7 @@ module Protip::ResourceTest # Namespace for internal constants
647
633
  # called during #initialize as well
648
634
  resource
649
635
 
650
- Protip::Wrapper.any_instance.expects(:assign_attributes).with(string: 'foo')
636
+ Protip::Decorator.any_instance.expects(:assign_attributes).with(string: 'foo')
651
637
  resource.assign_attributes string: 'foo'
652
638
  end
653
639
 
@@ -656,6 +642,8 @@ module Protip::ResourceTest # Namespace for internal constants
656
642
  def set(attributes) ; @resource.assign_attributes attributes ; end
657
643
  end
658
644
  describe_dirty_attributes_setter setter_class
645
+
646
+ # This section relies on correct behavior of +Protip.default_transformer+.
659
647
  describe 'dirty attributes (nested hashes)' do
660
648
 
661
649
  it 'marks nested hashes as changed if they set a new field' do
@@ -964,7 +952,7 @@ module Protip::ResourceTest # Namespace for internal constants
964
952
 
965
953
  it 'returns the wrapped server response' do
966
954
  client.stubs(:request).returns(response)
967
- assert_equal Protip::Wrapper.new(response, resource_class.converter), target.action
955
+ assert_equal Protip::Decorator.new(response, resource_class.transformer), target.action
968
956
  end
969
957
  end
970
958
  end
@@ -1065,14 +1053,10 @@ module Protip::ResourceTest # Namespace for internal constants
1065
1053
  Protip::Resource::Associations::BelongsToPolymorphicAssociation) { }
1066
1054
  end
1067
1055
 
1068
- describe '.converter' do
1056
+ describe '.transformer' do
1069
1057
  describe 'default value' do
1070
- it 'defaults to the standard converter' do
1071
- assert_instance_of Protip::StandardConverter, resource_class.converter
1072
- end
1073
-
1074
- it 're-uses the same converter on repeated accesses' do
1075
- assert_same resource_class.converter, resource_class.converter
1058
+ it 'defaults to the default transformer' do
1059
+ assert_same Protip.default_transformer, resource_class.transformer
1076
1060
  end
1077
1061
  end
1078
1062
  end
@@ -0,0 +1,92 @@
1
+ require 'test_helper'
2
+ require 'protip/transformers/decorating_transformer'
3
+
4
+ require 'google/protobuf'
5
+
6
+ describe Protip::Transformers::DecoratingTransformer do
7
+ let(:parent_transformer) do
8
+ mock('parent transformer')
9
+ end
10
+ let(:transformer) do
11
+ Protip::Transformers::DecoratingTransformer.new(parent_transformer)
12
+ end
13
+ describe '#to_object' do
14
+ let(:field) { mock 'field' }
15
+ let(:from_message) { mock 'message' }
16
+ let(:object) { transformer.to_object(from_message, field) }
17
+ it 'returns a decorator' do
18
+ assert_instance_of Protip::Decorator, object
19
+ end
20
+ it 'passes along the message' do
21
+ assert_equal from_message, object.message
22
+ end
23
+ it 'passes along the transformer' do
24
+ assert_equal parent_transformer, object.transformer
25
+ end
26
+ end
27
+
28
+ describe '#to_message' do
29
+ let(:pool) do
30
+ pool = Google::Protobuf::DescriptorPool.new
31
+ pool.build do
32
+ add_message 'message' do
33
+ optional :value, :int64, 1
34
+ end
35
+ end
36
+ pool
37
+ end
38
+ let(:message_class) { pool.lookup('message').msgclass }
39
+ let(:field) do
40
+ field = mock.responds_like_instance_of ::Google::Protobuf::FieldDescriptor
41
+ field.stubs(:submsg_name).returns(message_class.descriptor.name)
42
+ field.stubs(:subtype).returns(message_class.descriptor)
43
+ field
44
+ end
45
+
46
+ describe 'when given a decorator' do
47
+ let(:decorated_message) do
48
+ mock('message')
49
+ end
50
+ let(:decorator) do
51
+ Protip::Decorator.new(decorated_message, mock('transformer'))
52
+ end
53
+ it 'returns the decorated message' do
54
+ assert_equal decorated_message, transformer.to_message(decorator, field)
55
+ end
56
+ end
57
+
58
+ describe 'when given a hash' do
59
+ it 'assigns the hash attributes through a decorator' do
60
+ created_message = mock('created message')
61
+ created_decorator =
62
+ mock('created decorator').responds_like_instance_of(Protip::Decorator)
63
+ Protip::Decorator.expects(:new).
64
+ once.
65
+ # We expect a newly-generated message, and the transformer
66
+ # that we use for our decorators.
67
+ with(message_class.new, parent_transformer).
68
+ returns(created_decorator)
69
+
70
+ assignment = sequence 'assignment'
71
+ created_decorator.expects(:assign_attributes).
72
+ once.
73
+ in_sequence(assignment).
74
+ with(value: 55)
75
+ created_decorator.expects(:message).
76
+ once.
77
+ in_sequence(assignment).
78
+ returns(created_message)
79
+
80
+ assert_equal created_message, transformer.to_message({value: 55}, field)
81
+ end
82
+
83
+ # Simple functional test without all the complex transformer
84
+ # mocks - transformer never touched because the message only has
85
+ # a primitive field.
86
+ it 'assigns the hash attributes (functional)' do
87
+ message = transformer.to_message({value: 12}, field)
88
+ assert_equal message_class.new(value: 12), message
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,83 @@
1
+ require 'test_helper'
2
+ require 'protip/transformers/delegating_transformer'
3
+
4
+ require 'google/protobuf'
5
+
6
+ describe Protip::Transformers::DelegatingTransformer do
7
+ let(:pool) do
8
+ pool = ::Google::Protobuf::DescriptorPool.new
9
+ pool.build do
10
+ # We just need a couple empty message types
11
+ add_message('message_one') { }
12
+ add_message('message_two') { }
13
+ end
14
+ pool
15
+ end
16
+ %w(one two).each do |name|
17
+ let(:"message_#{name}_class") do
18
+ pool.lookup("message_#{name}").msgclass
19
+ end
20
+ end
21
+ %i(one two fallback).each do |name|
22
+ let(:"transformer_#{name}") do
23
+ mock("transformer_#{name}").
24
+ responds_like_instance_of(Class.new { include Protip::Transformer })
25
+ end
26
+
27
+ let(:"field_#{name}") do
28
+ field = mock("field_#{name}").
29
+ responds_like_instance_of(::Google::Protobuf::FieldDescriptor)
30
+ field.stubs(:submsg_name).returns("message_#{name}")
31
+ field
32
+ end
33
+ end
34
+ let(:delegating_transformer) do
35
+ transformer = Protip::Transformers::DelegatingTransformer.new(transformer_fallback)
36
+ transformer['message_one'] = transformer_one
37
+ transformer['message_two'] = transformer_two
38
+ transformer
39
+ end
40
+
41
+ describe '#to_object' do
42
+ let(:transformed_message) { mock 'transformed message' }
43
+ let(:result) { mock 'result' }
44
+ it 'delegates to transformers based on the message type being transformed' do
45
+ transformer_one.expects(:to_object).
46
+ once.
47
+ with(transformed_message, field_one).
48
+ returns(result)
49
+
50
+ assert_equal result, delegating_transformer.to_object(transformed_message, field_one)
51
+ end
52
+
53
+ it 'delegates to the fallback transformer when the message type has not been configured' do
54
+ transformer_fallback.expects(:to_object).
55
+ once.
56
+ with(transformed_message, field_fallback).
57
+ returns(result)
58
+ assert_equal result,
59
+ delegating_transformer.to_object(transformed_message, field_fallback)
60
+ end
61
+ end
62
+
63
+ describe '#to_message' do
64
+ let(:result) { mock 'result' }
65
+ let(:object) { mock 'object' }
66
+ it 'delegates to transformers based on the message type being transformed' do
67
+ transformer_two.expects(:to_message).
68
+ once.
69
+ with(object, field_two).
70
+ returns(result)
71
+ assert_equal result, delegating_transformer.to_message(object, field_two)
72
+ end
73
+
74
+ it 'delegates to the fallback transformer when the message type has not been configured' do
75
+ transformer_fallback.expects(:to_message).
76
+ once.
77
+ with(object, field_fallback).
78
+ returns(result)
79
+ assert_equal result,
80
+ delegating_transformer.to_message(object, field_fallback)
81
+ end
82
+ end
83
+ end