protobuf-activerecord 1.0.1
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.
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/.rvmrc +1 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +8 -0
- data/lib/protoable/convert.rb +75 -0
- data/lib/protoable/errors.rb +23 -0
- data/lib/protoable/fields.rb +116 -0
- data/lib/protoable/inheritable_class_instance_variables.rb +54 -0
- data/lib/protoable/persistence.rb +94 -0
- data/lib/protoable/serialization.rb +120 -0
- data/lib/protoable.rb +15 -0
- data/lib/protobuf/activerecord/version.rb +5 -0
- data/lib/protobuf-activerecord.rb +3 -0
- data/protobuf-activerecord.gemspec +37 -0
- data/spec/protoable/convert_spec.rb +265 -0
- data/spec/protoable/fields_spec.rb +131 -0
- data/spec/protoable/inheritable_class_instance_variables_spec.rb +5 -0
- data/spec/protoable/persistence_spec.rb +158 -0
- data/spec/protoable/serialization_spec.rb +133 -0
- data/spec/spec_helper.rb +13 -0
- data/spec/support/db/setup.rb +21 -0
- data/spec/support/db.rb +1 -0
- data/spec/support/models/user.rb +36 -0
- data/spec/support/models.rb +1 -0
- data/spec/support/protobuf/user.pb.rb +9 -0
- data/spec/support/protobuf.rb +2 -0
- metadata +253 -0
@@ -0,0 +1,265 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Protoable::Convert do
|
4
|
+
describe ".convert_int64_to_date" do
|
5
|
+
let(:date) { Date.current }
|
6
|
+
let(:int64) { date.to_time.to_i }
|
7
|
+
|
8
|
+
it "initializes a new Date object from the value" do
|
9
|
+
Timecop.freeze(Date.current) do
|
10
|
+
User.convert_int64_to_date(int64).should eq date
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe ".convert_int64_to_datetime" do
|
16
|
+
let(:datetime) { DateTime.current }
|
17
|
+
let(:int64) { datetime.to_time.to_i }
|
18
|
+
|
19
|
+
it "initializes a new DateTime object from the value" do
|
20
|
+
Timecop.freeze(DateTime.current) do
|
21
|
+
User.convert_int64_to_datetime(int64).should eq datetime
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe ".convert_int64_to_time" do
|
27
|
+
let(:time) { Time.current }
|
28
|
+
let(:int64) { time.to_time.to_i }
|
29
|
+
|
30
|
+
it "initializes a new Time object from the value" do
|
31
|
+
Timecop.freeze(Time.current) do
|
32
|
+
User.convert_int64_to_time(int64).should be_within(1).of(time)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "._protobuf_convert_columns_to_fields" do
|
38
|
+
context "when there is a column converter for the field" do
|
39
|
+
let(:email_value) { "foo@test.co" }
|
40
|
+
let(:email_converter) { User.method(:convert_email_to_lowercase) }
|
41
|
+
|
42
|
+
before { User.stub(:_protobuf_column_converters).and_return({ :email => email_converter }) }
|
43
|
+
|
44
|
+
it "calls the column converter with the given value" do
|
45
|
+
email_converter.should_receive(:call).with(email_value)
|
46
|
+
User._protobuf_convert_columns_to_fields(:email, email_value)
|
47
|
+
end
|
48
|
+
|
49
|
+
context "and it's corresponding column type has a default converter" do
|
50
|
+
before { User.stub(:_protobuf_date_column?).and_return(true) }
|
51
|
+
|
52
|
+
it "calls the column converter with the given value" do
|
53
|
+
email_converter.should_receive(:call).with(email_value)
|
54
|
+
User._protobuf_convert_columns_to_fields(:email, email_value)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "when the column type is :date" do
|
60
|
+
let(:date) { Date.current }
|
61
|
+
let(:integer) { date.to_time.to_i }
|
62
|
+
|
63
|
+
before { User.stub(:_protobuf_date_column?).and_return(true) }
|
64
|
+
|
65
|
+
it "converts the given value to an integer" do
|
66
|
+
User._protobuf_convert_columns_to_fields(:foo_date, date).should eq integer
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "when the column type is :datetime" do
|
71
|
+
let(:datetime) { DateTime.current }
|
72
|
+
let(:integer) { datetime.to_time.to_i }
|
73
|
+
|
74
|
+
before { User.stub(:_protobuf_datetime_column?).and_return(true) }
|
75
|
+
|
76
|
+
it "converts the given value to an integer" do
|
77
|
+
User._protobuf_convert_columns_to_fields(:foo_datetime, datetime).should eq integer
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "when the column type is :time" do
|
82
|
+
let(:time) { Time.current }
|
83
|
+
let(:integer) { time.to_time.to_i }
|
84
|
+
|
85
|
+
before { User.stub(:_protobuf_time_column?).and_return(true) }
|
86
|
+
|
87
|
+
it "converts the given value to an integer" do
|
88
|
+
User._protobuf_convert_columns_to_fields(:foo_time, time).should eq integer
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "when the column type is :timestamp" do
|
93
|
+
let(:timestamp) { Time.current }
|
94
|
+
let(:integer) { timestamp.to_time.to_i }
|
95
|
+
|
96
|
+
before { User.stub(:_protobuf_timestamp_column?).and_return(true) }
|
97
|
+
|
98
|
+
it "converts the given value to an integer" do
|
99
|
+
User._protobuf_convert_columns_to_fields(:foo_timestamp, timestamp).should eq integer
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "when no conversion is necessary" do
|
104
|
+
let(:value) { "Foo" }
|
105
|
+
|
106
|
+
it "returns the given value" do
|
107
|
+
User._protobuf_convert_columns_to_fields(:foo, value).should eq value
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "._protobuf_convert_fields_to_columns" do
|
113
|
+
let(:email_value) { "foo@test.co" }
|
114
|
+
let(:email_converter) { lambda { |value| value } }
|
115
|
+
|
116
|
+
before { User.stub(:_protobuf_field_converters).and_return({ :email => email_converter }) }
|
117
|
+
|
118
|
+
it "calls the field converter with the given value" do
|
119
|
+
email_converter.should_receive(:call).with(email_value)
|
120
|
+
User._protobuf_convert_fields_to_columns(:email, email_value)
|
121
|
+
end
|
122
|
+
|
123
|
+
context "and it's corresponding column type has a default converter" do
|
124
|
+
before { User.stub(:_protobuf_date_column?).and_return(true) }
|
125
|
+
|
126
|
+
it "calls the field converter with the given value" do
|
127
|
+
email_converter.should_receive(:call).with(email_value)
|
128
|
+
User._protobuf_convert_fields_to_columns(:email, email_value)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
context "when the given field's corresponding column type is :date" do
|
133
|
+
let(:date) { Date.current }
|
134
|
+
let(:value) { date.to_time.to_i }
|
135
|
+
|
136
|
+
before { User.stub(:_protobuf_date_column?).and_return(true) }
|
137
|
+
|
138
|
+
it "converts the given value to a Date object" do
|
139
|
+
User._protobuf_convert_fields_to_columns(:foo_date, value).should eq date
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
context "when given field's corresponding the column type is :datetime" do
|
144
|
+
let(:datetime) { DateTime.current }
|
145
|
+
let(:value) { datetime.to_i }
|
146
|
+
|
147
|
+
before { User.stub(:_protobuf_datetime_column?).and_return(true) }
|
148
|
+
|
149
|
+
it "converts the given value to a DateTime object" do
|
150
|
+
User._protobuf_convert_fields_to_columns(:foo_datetime, value).should be_a(DateTime)
|
151
|
+
end
|
152
|
+
|
153
|
+
it "converts the given value to a DateTime object of the same value" do
|
154
|
+
User._protobuf_convert_fields_to_columns(:foo_datetime, value).should be_within(1).of(datetime)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context "when given field's corresponding the column type is :time" do
|
159
|
+
let(:time) { Time.current }
|
160
|
+
let(:value) { time.to_i }
|
161
|
+
|
162
|
+
before { User.stub(:_protobuf_time_column?).and_return(true) }
|
163
|
+
|
164
|
+
it "converts the given value to a Time object" do
|
165
|
+
User._protobuf_convert_fields_to_columns(:foo_time, value).should be_a(Time)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "converts the given value to a Time object of the same value" do
|
169
|
+
User._protobuf_convert_fields_to_columns(:foo_time, value).should be_within(1).of(time)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context "when given field's corresponding the column type is :timestamp" do
|
174
|
+
let(:time) { Time.current }
|
175
|
+
let(:value) { time.to_i }
|
176
|
+
|
177
|
+
before { User.stub(:_protobuf_timestamp_column?).and_return(true) }
|
178
|
+
|
179
|
+
it "converts the given value to a Time object" do
|
180
|
+
User._protobuf_convert_fields_to_columns(:foo_time, value).should be_a(Time)
|
181
|
+
end
|
182
|
+
|
183
|
+
it "converts the given value to a Time object of the same value" do
|
184
|
+
User._protobuf_convert_fields_to_columns(:foo_timestamp, value).should be_within(1).of(time)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context "when no conversion is necessary" do
|
189
|
+
let(:value) { "Foo" }
|
190
|
+
|
191
|
+
it "returns the given value" do
|
192
|
+
User._protobuf_convert_fields_to_columns(:foo, value).should eq value
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context "column type predicates" do
|
198
|
+
before { User.stub(:_protobuf_column_types).and_return(Hash.new) }
|
199
|
+
after { User.unstub(:_protobuf_column_types) }
|
200
|
+
|
201
|
+
describe "._protobuf_date_column?" do
|
202
|
+
before { User._protobuf_column_types[:date] = [ :foo_date ] }
|
203
|
+
|
204
|
+
context "when the column type is :date" do
|
205
|
+
it "is true" do
|
206
|
+
User._protobuf_date_column?(:foo_date).should be_true
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
context "when the column type is not :date" do
|
211
|
+
it "is false" do
|
212
|
+
User._protobuf_date_column?(:bar_date).should be_false
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
describe "._protobuf_datetime_column?" do
|
218
|
+
before { User._protobuf_column_types[:datetime] = [ :foo_datetime ] }
|
219
|
+
|
220
|
+
context "when the column type is :datetime" do
|
221
|
+
it "is true" do
|
222
|
+
User._protobuf_datetime_column?(:foo_datetime).should be_true
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
context "when the column type is not :datetime" do
|
227
|
+
it "is false" do
|
228
|
+
User._protobuf_datetime_column?(:bar_datetime).should be_false
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe "._protobuf_time_column?" do
|
234
|
+
before { User._protobuf_column_types[:time] = [ :foo_time ] }
|
235
|
+
|
236
|
+
context "when the column type is :time" do
|
237
|
+
it "is true" do
|
238
|
+
User._protobuf_time_column?(:foo_time).should be_true
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
context "when the column type is not :time" do
|
243
|
+
it "is false" do
|
244
|
+
User._protobuf_time_column?(:bar_time).should be_false
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
describe "._protobuf_timestamp_column?" do
|
250
|
+
before { User._protobuf_column_types[:timestamp] = [ :foo_timestamp ] }
|
251
|
+
|
252
|
+
context "when the column type is :timestamp" do
|
253
|
+
it "is true" do
|
254
|
+
User._protobuf_timestamp_column?(:foo_timestamp).should be_true
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
context "when the column type is not :timestamp" do
|
259
|
+
it "is false" do
|
260
|
+
User._protobuf_timestamp_column?(:bar_timestamp).should be_false
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Protoable::Fields do
|
4
|
+
describe "._protobuf_map_columns" do
|
5
|
+
context "when the class has a table" do
|
6
|
+
let(:expected_column_names) {
|
7
|
+
User.columns.inject({}) do |hash, column|
|
8
|
+
hash[column.name.to_sym] = column
|
9
|
+
hash
|
10
|
+
end
|
11
|
+
}
|
12
|
+
|
13
|
+
let(:expected_column_types) {
|
14
|
+
User.columns.inject({}) do |hash, column|
|
15
|
+
hash[column.type.to_sym] ||= []
|
16
|
+
hash[column.type.to_sym] << column.name.to_sym
|
17
|
+
hash
|
18
|
+
end
|
19
|
+
}
|
20
|
+
|
21
|
+
it "maps columns by name" do
|
22
|
+
User._protobuf_columns.should eq expected_column_names
|
23
|
+
end
|
24
|
+
|
25
|
+
it "maps column names by column type" do
|
26
|
+
User._protobuf_column_types.should eq expected_column_types
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe ".convert_field" do
|
32
|
+
context "when the given converter is a hash" do
|
33
|
+
let(:method) { User.method(:convert_base64_to_string) }
|
34
|
+
|
35
|
+
before { User.convert_field :public_key, :from => :base64, :to => :string }
|
36
|
+
|
37
|
+
it "determines the method using the hash's :to and :from keys" do
|
38
|
+
User._protobuf_field_converters[:public_key].should eq method
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when the given converter is a symbol" do
|
43
|
+
context "when the converter is not a defined method" do
|
44
|
+
let(:callable) { User.method(:convert_base64_to_string) }
|
45
|
+
|
46
|
+
before { User.convert_field :email, :base64 }
|
47
|
+
|
48
|
+
it "determines the method using the converter as the 'from' and the column type as the 'to'" do
|
49
|
+
User._protobuf_field_converters[:email].should eq callable
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "when the converter is a defined method" do
|
54
|
+
let(:callable) { User.method(:convert_email_to_lowercase) }
|
55
|
+
|
56
|
+
before { User.convert_field :email, :convert_email_to_lowercase }
|
57
|
+
|
58
|
+
it "creates a callable method object from the converter" do
|
59
|
+
User._protobuf_field_converters[:email].should eq callable
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when the given converter is nil" do
|
65
|
+
it "raises an exception" do
|
66
|
+
expect { User.convert_field :email, nil }.to raise_exception(Protoable::FieldConverterError)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "when the given converter is not callable" do
|
71
|
+
it "raises an exception" do
|
72
|
+
expect { User.convert_field :email, :foo }.to raise_exception(Protoable::FieldConverterError)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when the given converter is callable" do
|
77
|
+
let(:callable) { lambda { |value| value } }
|
78
|
+
|
79
|
+
before { User.convert_field :email, callable }
|
80
|
+
|
81
|
+
it "adds the given converter to list of field converters" do
|
82
|
+
User._protobuf_field_converters[:email].should eq callable
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe ".transform_column" do
|
88
|
+
context "when the given converter is a symbol" do
|
89
|
+
context "when the converter is not a defined method" do
|
90
|
+
it "raises an exception" do
|
91
|
+
expect { User.transform_column :name, :foo }.to raise_exception(Protoable::ColumnTransformerError)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "when the converter is a defined method" do
|
96
|
+
let(:callable) { User.method(:extract_first_name) }
|
97
|
+
|
98
|
+
before { User.transform_column :first_name, :extract_first_name }
|
99
|
+
|
100
|
+
it "creates a callable method object from the converter" do
|
101
|
+
User._protobuf_column_transformers[:first_name].should eq callable
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context "when the given converter is nil" do
|
107
|
+
it "raises an exception" do
|
108
|
+
expect { User.transform_column :name, nil }.to raise_exception(Protoable::ColumnTransformerError)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
context "when the given converter is not callable" do
|
113
|
+
it "raises an exception" do
|
114
|
+
expect { User.transform_column :name, double(:not_callable) }.to raise_exception(Protoable::ColumnTransformerError)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "when the given transformer is callable" do
|
119
|
+
let(:callable) { lambda { |proto| nil } }
|
120
|
+
|
121
|
+
before {
|
122
|
+
User.stub(:_protobuf_column_transformers).and_return(Hash.new)
|
123
|
+
User.transform_column :account_id, callable
|
124
|
+
}
|
125
|
+
|
126
|
+
it "adds the given converter to the list of protobuf field transformers" do
|
127
|
+
User._protobuf_column_transformers[:account_id] = callable
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Protoable::Persistence do
|
4
|
+
let(:user) { User.new(user_attributes) }
|
5
|
+
let(:user_attributes) { { :first_name => 'foo', :last_name => 'bar', :email => 'foo@test.co' } }
|
6
|
+
let(:proto_hash) { { :name => 'foo bar', :email => 'foo@test.co' } }
|
7
|
+
let(:proto) { Proto::User.new(proto_hash) }
|
8
|
+
|
9
|
+
describe "._filter_attribute_fields" do
|
10
|
+
it "includes fields that have values" do
|
11
|
+
attribute_fields = User._filter_attribute_fields(proto)
|
12
|
+
attribute_fields[:email].should eq proto_hash[:email]
|
13
|
+
end
|
14
|
+
|
15
|
+
it "filters repeated fields" do
|
16
|
+
attribute_fields = User._filter_attribute_fields(proto)
|
17
|
+
attribute_fields.has_key?(:tags).should be_false
|
18
|
+
end
|
19
|
+
|
20
|
+
it "filters fields that map to protected attributes" do
|
21
|
+
User.stub(:protected_attributes).and_return([ "email" ])
|
22
|
+
attribute_fields = User._filter_attribute_fields(proto)
|
23
|
+
attribute_fields.has_key?(:email).should be_false
|
24
|
+
end
|
25
|
+
|
26
|
+
it "includes attributes that aren't fields, but have column transformers" do
|
27
|
+
User.stub(:_protobuf_column_transformers).and_return({ :account_id => :fetch_account_id })
|
28
|
+
attribute_fields = User._filter_attribute_fields(proto)
|
29
|
+
attribute_fields.has_key?(:account_id).should be_true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe ".attributes_from_proto" do
|
34
|
+
context "when a column transformer is defined for the field" do
|
35
|
+
it "transforms the field value" do
|
36
|
+
attribute_fields = User.attributes_from_proto(proto)
|
37
|
+
attribute_fields[:first_name].should eq user_attributes[:first_name]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "when column transformer is not defined for the field" do
|
42
|
+
before {
|
43
|
+
User.stub(:_protobuf_convert_fields_to_columns) do |key, value|
|
44
|
+
value
|
45
|
+
end
|
46
|
+
}
|
47
|
+
|
48
|
+
it "converts the field value" do
|
49
|
+
attribute_fields = User.attributes_from_proto(proto)
|
50
|
+
attribute_fields.should eq user_attributes
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe ".create_from_proto" do
|
56
|
+
it "initializes a new object with attributes from the given protobuf message" do
|
57
|
+
user = User.create_from_proto(proto)
|
58
|
+
attributes = user.attributes.slice("first_name", "last_name", "email")
|
59
|
+
attributes.should eq user_attributes.stringify_keys
|
60
|
+
end
|
61
|
+
|
62
|
+
context "when a block is given" do
|
63
|
+
it "yields to the block with attributes from the given protobuf message" do
|
64
|
+
yielded = nil
|
65
|
+
|
66
|
+
User.create_from_proto(proto) do |attributes|
|
67
|
+
yielded = "boom!"
|
68
|
+
end
|
69
|
+
|
70
|
+
yielded.should eq "boom!"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
context "when the object is valid" do
|
75
|
+
it "saves the record" do
|
76
|
+
user = User.create_from_proto(proto)
|
77
|
+
user.persisted?.should be_true
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when an error occurs while saving" do
|
81
|
+
before { User.any_instance.stub(:save!).and_raise(RuntimeError) }
|
82
|
+
|
83
|
+
it "raises an exception" do
|
84
|
+
expect { User.create_from_proto(proto) }.to raise_exception
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context "when the object is not valid" do
|
90
|
+
before { User.any_instance.stub(:valid?).and_return(false) }
|
91
|
+
|
92
|
+
it "returns the new object, unsaved" do
|
93
|
+
user = User.create_from_proto(proto)
|
94
|
+
user.persisted?.should be_false
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "#attributes_from_proto" do
|
100
|
+
it "gets attributes from the given protobuf message" do
|
101
|
+
User.should_receive(:attributes_from_proto).with(proto)
|
102
|
+
user.attributes_from_proto(proto)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "#destroy_from_proto" do
|
107
|
+
it "destroys the object" do
|
108
|
+
user.should_receive(:destroy)
|
109
|
+
user.destroy_from_proto
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#update_from_proto" do
|
114
|
+
it "updates the object with attributes from the given protobuf message" do
|
115
|
+
user.should_receive(:assign_attributes).with(user_attributes)
|
116
|
+
user.update_from_proto(proto)
|
117
|
+
end
|
118
|
+
|
119
|
+
context "when a block is given" do
|
120
|
+
it "yields to the block with attributes from the given protobuf message" do
|
121
|
+
yielded = nil
|
122
|
+
|
123
|
+
user.update_from_proto(proto) do |attributes|
|
124
|
+
yielded = "boom!"
|
125
|
+
end
|
126
|
+
|
127
|
+
yielded.should eq "boom!"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "when the object is valid" do
|
132
|
+
before { user.stub(:valid?).and_return(true) }
|
133
|
+
|
134
|
+
context "when saving is successful" do
|
135
|
+
it "returns true" do
|
136
|
+
user.stub(:save!).and_return(true)
|
137
|
+
user.update_from_proto(proto).should be_true
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context "when an error occurs while saving" do
|
142
|
+
before { user.stub(:save!).and_raise(RuntimeError) }
|
143
|
+
|
144
|
+
it "raises an exception" do
|
145
|
+
expect { user.update_from_proto }.to raise_exception
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context "when the object is invalid" do
|
151
|
+
before { user.stub(:valid?).and_return(false) }
|
152
|
+
|
153
|
+
it "returns false" do
|
154
|
+
user.update_from_proto(proto).should be_false
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Protoable::Serialization do
|
4
|
+
let(:protobuf_message) { ::Proto::User }
|
5
|
+
|
6
|
+
describe ".convert_column" do
|
7
|
+
context "when the given converter is a hash" do
|
8
|
+
let(:method) { User.method(:convert_base64_to_string) }
|
9
|
+
|
10
|
+
before { User.convert_column :public_key, :from => :base64, :to => :string }
|
11
|
+
|
12
|
+
it "determines the method using the hash's :to and :from keys" do
|
13
|
+
User._protobuf_column_converters[:public_key].should eq method
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when the given converter is a symbol" do
|
18
|
+
context "when the converter is not a defined method" do
|
19
|
+
let(:callable) { User.method(:convert_base64_to_string) }
|
20
|
+
|
21
|
+
before { User.convert_column :email, :base64 }
|
22
|
+
|
23
|
+
it "determines the method using the converter as the 'from' and the column type as the 'to'" do
|
24
|
+
User._protobuf_column_converters[:email].should eq callable
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when the converter is a defined method" do
|
29
|
+
let(:callable) { User.method(:convert_email_to_lowercase) }
|
30
|
+
|
31
|
+
before { User.convert_column :email, :convert_email_to_lowercase }
|
32
|
+
|
33
|
+
it "creates a callable method object from the converter" do
|
34
|
+
User._protobuf_column_converters[:email].should eq callable
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when the given converter is nil" do
|
40
|
+
it "raises an exception" do
|
41
|
+
expect { User.convert_column :email, nil }.to raise_exception(Protoable::ColumnConverterError)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "when the given converter is not callable" do
|
46
|
+
it "raises an exception" do
|
47
|
+
expect { User.convert_column :email, :foo }.to raise_exception(Protoable::ColumnConverterError)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when the given converter is callable" do
|
52
|
+
let(:callable) { lambda { |value| value } }
|
53
|
+
|
54
|
+
before { User.convert_column :email, callable }
|
55
|
+
|
56
|
+
it "adds the given converter to list of column converters" do
|
57
|
+
User._protobuf_column_converters[:email].should eq callable
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe ".protobuf_message" do
|
63
|
+
before { User.protobuf_message(protobuf_message) }
|
64
|
+
|
65
|
+
context "given a value" do
|
66
|
+
let(:protobuf_fields) { [ :name, :email, :tags ] }
|
67
|
+
|
68
|
+
it "sets .protobuf_fields" do
|
69
|
+
User.protobuf_fields.should =~ protobuf_fields
|
70
|
+
end
|
71
|
+
|
72
|
+
it "defines #to_proto" do
|
73
|
+
User.allocate.should respond_to :to_proto
|
74
|
+
end
|
75
|
+
|
76
|
+
it "defines #to_proto_hash" do
|
77
|
+
User.allocate.should respond_to :to_proto_hash
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it "returns the protobuf message for this object" do
|
82
|
+
User.protobuf_message.should eq protobuf_message
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "when protobuf_message is defined" do
|
87
|
+
let(:attributes) { Hash.new }
|
88
|
+
let(:user) { User.new(attributes) }
|
89
|
+
|
90
|
+
before { User.protobuf_message(protobuf_message) }
|
91
|
+
|
92
|
+
describe "#protoable_attributes" do
|
93
|
+
let(:attributes) {
|
94
|
+
{
|
95
|
+
:first_name => "foo",
|
96
|
+
:last_name => "bar",
|
97
|
+
:email => "foo@test.co"
|
98
|
+
}
|
99
|
+
}
|
100
|
+
let(:protoable_attributes) { { :name => user.name, :email => user.email, :tags => nil } }
|
101
|
+
|
102
|
+
it "returns a hash of protobuf fields that this object has getters for" do
|
103
|
+
user.protoable_attributes.should eq protoable_attributes
|
104
|
+
end
|
105
|
+
|
106
|
+
it "converts attributes values for protobuf messages" do
|
107
|
+
user.should_receive(:_protobuf_convert_columns_to_fields).any_number_of_times
|
108
|
+
user.protoable_attributes
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
describe "#to_proto" do
|
113
|
+
let(:proto) { protobuf_message.new(proto_hash) }
|
114
|
+
let(:proto_hash) { { :name => "foo" } }
|
115
|
+
|
116
|
+
before { user.stub(:to_proto_hash).and_return(proto_hash) }
|
117
|
+
|
118
|
+
it "intializes a new protobuf message with attributes from #to_proto_hash" do
|
119
|
+
user.to_proto.should eq proto
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "#to_proto_hash" do
|
124
|
+
let(:proto_hash) { { :name => "foo" } }
|
125
|
+
|
126
|
+
before { user.stub(:protoable_attributes).and_return(proto_hash) }
|
127
|
+
|
128
|
+
it "returns #protoable_attributes" do
|
129
|
+
user.to_proto_hash.should eq proto_hash
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
data/spec/spec_helper.rb
ADDED