protip 0.9.7 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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: []