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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9f3d97797f1ded3cce21e24d653a5960c292f3af
4
- data.tar.gz: d1fa2cea1e89f3fdb5ebf9be570e5dde1144397a
3
+ metadata.gz: c1e044c1437371db39928f0e1ea08104e1a15554
4
+ data.tar.gz: 5672c792162be0304a20520d89d4f0a4a8cf1f4a
5
5
  SHA512:
6
- metadata.gz: 5775bd4b337ab6d1f31ea6de1c0fc3d2b2f07a8075dda4bdcdb0581b69dd61b5154d1dd5bffd7dbfc069e34d8bd533740411d3cb97e107657ee433d8729c35c4
7
- data.tar.gz: 496f91676d206299a1ebbc4328722be7976b685d1d02988ac44a76472519e19e740896a6abb9f93d8d46a600a46aee2d9cae4ecf4dc34fb7ae45b8d8d945f688
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
+
@@ -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
- @message = self.class.client.request path: self.class.base_path,
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
- @message = self.class.client.request path: "#{self.class.base_path}/#{id}",
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
- @message = self.class.client.request path: "#{self.class.base_path}/#{id}",
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
- define_method :"#{field.name}" do
152
- @message.public_send field.name
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
- @message = message_or_params
236
+ self.message = message_or_params
230
237
  else
231
- @message = self.class.message.new(message_or_params)
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 encode. Also define them as local constants so they don't
15
- # interfere with any other tests.
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, 1
18
- optional :string, :ordered_tests, 2
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, 3
26
+ optional :string, :param, 6
23
27
  end
24
28
 
25
29
  class NameResponse < ::Protobuf::Message
26
- optional :string, :name, 4
30
+ optional :string, :name, 7
27
31
  end
28
32
 
29
33
  class SearchRequest < ::Protobuf::Message
30
- optional :string, :term, 5
34
+ optional :string, :term, 8
31
35
  end
32
36
 
33
37
  class SearchResponse < ::Protobuf::Message
34
- repeated :string, :results, 6
38
+ repeated :string, :results, 9
35
39
  end
36
40
 
37
41
  class FetchRequest < ::Protobuf::Message
38
- repeated :string, :names, 7
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}, results[0].attributes)
83
- assert_equal({ordered_tests: 'baggins', id: 1}, results[1].attributes)
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.class_eval do
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, :id=, :string, :string=].each do |method|
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.9.7
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-13 00:00:00.000000000 Z
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
- homepage:
155
- licenses: []
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: []