protip 0.20.7 → 0.30.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.
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