protobuf-activerecord 1.2.6 → 2.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,6 +3,60 @@ require 'spec_helper'
3
3
  describe Protoable::Serialization do
4
4
  let(:protobuf_message) { UserMessage }
5
5
 
6
+ describe "._protobuf_convert_attributes_to_fields" do
7
+ context "when the column type is :date" do
8
+ let(:date) { Date.current }
9
+ let(:integer) { date.to_time.to_i }
10
+
11
+ before { User.stub(:_protobuf_date_column?).and_return(true) }
12
+
13
+ it "converts the given value to an integer" do
14
+ User._protobuf_convert_attributes_to_fields(:foo_date, date).should eq integer
15
+ end
16
+ end
17
+
18
+ context "when the column type is :datetime" do
19
+ let(:datetime) { DateTime.current }
20
+ let(:integer) { datetime.to_time.to_i }
21
+
22
+ before { User.stub(:_protobuf_datetime_column?).and_return(true) }
23
+
24
+ it "converts the given value to an integer" do
25
+ User._protobuf_convert_attributes_to_fields(:foo_datetime, datetime).should eq integer
26
+ end
27
+ end
28
+
29
+ context "when the column type is :time" do
30
+ let(:time) { Time.current }
31
+ let(:integer) { time.to_time.to_i }
32
+
33
+ before { User.stub(:_protobuf_time_column?).and_return(true) }
34
+
35
+ it "converts the given value to an integer" do
36
+ User._protobuf_convert_attributes_to_fields(:foo_time, time).should eq integer
37
+ end
38
+ end
39
+
40
+ context "when the column type is :timestamp" do
41
+ let(:timestamp) { Time.current }
42
+ let(:integer) { timestamp.to_time.to_i }
43
+
44
+ before { User.stub(:_protobuf_timestamp_column?).and_return(true) }
45
+
46
+ it "converts the given value to an integer" do
47
+ User._protobuf_convert_attributes_to_fields(:foo_timestamp, timestamp).should eq integer
48
+ end
49
+ end
50
+
51
+ context "when no conversion is necessary" do
52
+ let(:value) { "Foo" }
53
+
54
+ it "returns the given value" do
55
+ User._protobuf_convert_attributes_to_fields(:foo, value).should eq value
56
+ end
57
+ end
58
+ end
59
+
6
60
  describe ".field_from_record" do
7
61
  context "when the given converter is a symbol" do
8
62
  let(:callable) { lambda { |value| User.__send__(:extract_first_name) } }
@@ -35,87 +89,117 @@ describe Protoable::Serialization do
35
89
  end
36
90
  end
37
91
 
38
- describe ".protoable_attribute" do
39
- context "when the given converter is a hash" do
40
- let(:method) { lambda { |value| User.__send__(:convert_base64_to_string, value) } }
92
+ describe ".protobuf_message" do
93
+ let(:options) { { :only => [] } }
41
94
 
42
- before { User.protoable_attribute :public_key, :from => :base64, :to => :string }
95
+ before { User.protobuf_message(protobuf_message, options) }
96
+ after { User.protobuf_message(protobuf_message, {}) }
43
97
 
44
- it "determines the method using the hash's :to and :from keys" do
45
- User.should_receive(:convert_base64_to_string)
46
- User._protobuf_attribute_converters[:public_key].call(1)
98
+ context "given a value" do
99
+ it "defines #to_proto" do
100
+ User.allocate.should respond_to :to_proto
47
101
  end
48
102
  end
49
103
 
50
- context "when the given converter is a symbol" do
51
- let(:callable) { lambda { |value| User.__send__(:convert_email_to_lowercase, value) } }
104
+ context "given options" do
105
+ it "merges them with protobuf field options" do
106
+ User._protobuf_field_options.should eq options
107
+ end
108
+ end
109
+
110
+ it "returns the protobuf message for this object" do
111
+ User.protobuf_message.should eq protobuf_message
112
+ end
113
+ end
52
114
 
53
- before { User.protoable_attribute :email, :convert_email_to_lowercase }
115
+ context "when protobuf_message is defined" do
116
+ let(:attributes) { Hash.new }
117
+ let(:user) { User.new(attributes) }
54
118
 
55
- it "creates a callable method object from the converter" do
56
- User.should_receive(:convert_email_to_lowercase)
57
- User._protobuf_attribute_converters[:email].call(1)
119
+ before { User.protobuf_message(protobuf_message) }
120
+
121
+ describe "#_filter_field_attributes" do
122
+ context "when options has :only" do
123
+ it "only returns the given field(s)" do
124
+ fields = user._filter_field_attributes(:only => :name).should
125
+ fields.should eq [ :name ]
126
+ end
58
127
  end
59
- end
60
128
 
61
- context "when the given converter is not callable" do
62
- it "raises an exception" do
63
- expect { User.protoable_attribute :email, nil }.to raise_exception(Protoable::AttributeConverterError)
129
+ context "when options has :except" do
130
+ it "returns all except the given field(s)" do
131
+ fields = user._filter_field_attributes(:except => :name).should
132
+ fields.should eq [ :guid, :email, :email_domain ]
133
+ end
64
134
  end
65
135
  end
66
136
 
67
- context "when the given converter is callable" do
68
- let(:callable) { lambda { |value| value } }
69
-
70
- before { User.protoable_attribute :email, callable }
137
+ describe "#_filtered_fields" do
138
+ it "returns protobuf fields" do
139
+ user._filtered_fields.should eq [ :guid, :name, :email, :email_domain ]
140
+ end
71
141
 
72
- it "adds the given converter to list of attribute converters" do
73
- User._protobuf_attribute_converters[:email].should eq callable
142
+ context "given :deprecated => false" do
143
+ it "filters all deprecated fields" do
144
+ fields = user._filtered_fields(:deprecated => false).should
145
+ fields.should eq [ :guid, :name, :email ]
146
+ end
74
147
  end
75
148
  end
76
- end
77
149
 
78
- describe ".protobuf_message" do
79
- before { User.protobuf_message(protobuf_message) }
150
+ describe "#_normalize_options" do
151
+ let(:options) { { :only => [ :name ] } }
80
152
 
81
- context "given a value" do
82
- let(:protobuf_fields) { [ :guid, :name, :email, :email_domain ] }
153
+ context "given empty options" do
154
+ before { User.protobuf_message(protobuf_message, options) }
83
155
 
84
- it "sets .protobuf_fields" do
85
- User.protobuf_fields.should =~ protobuf_fields
156
+ it "returns the class's protobuf field options" do
157
+ User.allocate._normalize_options({}).should eq options
158
+ end
86
159
  end
87
160
 
88
- it "defines #to_proto" do
89
- User.allocate.should respond_to :to_proto
161
+ context "given options" do
162
+ before { User.protobuf_message(protobuf_message, {}) }
163
+
164
+ it "merges them with the class's protobuf field options" do
165
+ normalized_options = User.allocate._normalize_options(options)
166
+ normalized_options[:only].should eq options[:only]
167
+ end
90
168
  end
91
169
 
92
- it "defines #to_proto_hash" do
93
- User.allocate.should respond_to :to_proto_hash
170
+ context "given options with :only" do
171
+ before { User.protobuf_message(protobuf_message, {}) }
172
+
173
+ it "ensures that :except exists" do
174
+ normalized_options = User.allocate._normalize_options(options)
175
+ normalized_options[:except].should eq []
176
+ end
94
177
  end
95
- end
96
178
 
97
- it "returns the protobuf message for this object" do
98
- User.protobuf_message.should eq protobuf_message
99
- end
100
- end
179
+ context "given options with :except" do
180
+ let(:options) { { :except => [ :name ] } }
101
181
 
102
- context "when protobuf_message is defined" do
103
- let(:attributes) { Hash.new }
104
- let(:user) { User.new(attributes) }
182
+ before { User.protobuf_message(protobuf_message, {}) }
105
183
 
106
- before { User.protobuf_message(protobuf_message) }
184
+ it "ensures that :only exists" do
185
+ normalized_options = User.allocate._normalize_options(options)
186
+ normalized_options[:only].should eq []
187
+ end
188
+ end
189
+ end
107
190
 
108
- describe "#protoable_attributes" do
109
- context "when a transformer is defined for the field" do
110
- let(:attributes) {
111
- {
112
- :guid => "foo",
113
- :first_name => "bar",
114
- :last_name => "baz",
115
- :email => "foo@test.co"
116
- }
191
+ describe "#fields_from_record" do
192
+ let(:attributes) {
193
+ {
194
+ :guid => "foo",
195
+ :first_name => "bar",
196
+ :last_name => "baz",
197
+ :email => "foo@test.co"
117
198
  }
118
- let(:protoable_attributes) { { :guid => user.guid, :name => user.name, :email => user.email, :email_domain => 'test.co' } }
199
+ }
200
+
201
+ context "when a transformer is defined for the field" do
202
+ let(:fields_from_record) { { :guid => user.guid, :name => user.name, :email => user.email, :email_domain => 'test.co' } }
119
203
  let(:transformer) { { :email_domain => lambda { |record| record.email.split('@').last } } }
120
204
 
121
205
  before {
@@ -123,28 +207,27 @@ describe Protoable::Serialization do
123
207
  }
124
208
 
125
209
  it "gets the field from the transformer" do
126
- user.protoable_attributes.should eq protoable_attributes
210
+ user.fields_from_record.should eq fields_from_record
127
211
  end
128
212
  end
129
213
 
130
214
  context "when a transformer is not defined for the field" do
131
- let(:attributes) {
132
- {
133
- :guid => "foo",
134
- :first_name => "bar",
135
- :last_name => "baz",
136
- :email => "foo@test.co"
137
- }
138
- }
139
- let(:protoable_attributes) { { :guid => user.guid, :name => user.name, :email => user.email, :email_domain => nil } }
215
+ let(:fields_from_record) { { :guid => user.guid, :name => user.name, :email => user.email, :email_domain => nil } }
140
216
 
141
217
  it "returns a hash of protobuf fields that this object has getters for" do
142
- user.protoable_attributes.should eq protoable_attributes
218
+ user.fields_from_record.should eq fields_from_record
143
219
  end
144
220
 
145
221
  it "converts attributes values for protobuf messages" do
146
222
  user.should_receive(:_protobuf_convert_attributes_to_fields).any_number_of_times
147
- user.protoable_attributes
223
+ user.fields_from_record
224
+ end
225
+ end
226
+
227
+ context "given options with :include" do
228
+ it "adds the given field to the list of serialized fields" do
229
+ fields = user.fields_from_record(:include => :token)
230
+ fields.include?(:token).should be_true
148
231
  end
149
232
  end
150
233
  end
@@ -153,21 +236,11 @@ describe Protoable::Serialization do
153
236
  let(:proto) { protobuf_message.new(proto_hash) }
154
237
  let(:proto_hash) { { :name => "foo" } }
155
238
 
156
- before { user.stub(:to_proto_hash).and_return(proto_hash) }
239
+ before { user.stub(:fields_from_record).and_return(proto_hash) }
157
240
 
158
241
  it "intializes a new protobuf message with attributes from #to_proto_hash" do
159
242
  user.to_proto.should eq proto
160
243
  end
161
244
  end
162
-
163
- describe "#to_proto_hash" do
164
- let(:proto_hash) { { :name => "foo" } }
165
-
166
- before { user.stub(:protoable_attributes).and_return(proto_hash) }
167
-
168
- it "returns #protoable_attributes" do
169
- user.to_proto_hash.should eq proto_hash
170
- end
171
- end
172
245
  end
173
246
  end
@@ -0,0 +1,214 @@
1
+ require 'spec_helper'
2
+
3
+ describe Protoable::Transformation 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) { UserMessage.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 "includes attributes that aren't fields, but have attribute transformers" do
21
+ User.stub(:_protobuf_attribute_transformers).and_return({ :account_id => :fetch_account_id })
22
+ attribute_fields = User._filter_attribute_fields(proto)
23
+ attribute_fields.has_key?(:account_id).should be_true
24
+ end
25
+ end
26
+
27
+ describe "._protobuf_convert_fields_to_columns" do
28
+ context "when the given field's corresponding column type is :date" do
29
+ let(:date) { Date.current }
30
+ let(:value) { date.to_time.to_i }
31
+
32
+ before { User.stub(:_protobuf_date_column?).and_return(true) }
33
+
34
+ it "converts the given value to a Date object" do
35
+ User._protobuf_convert_fields_to_columns(:foo_date, value).should eq date
36
+ end
37
+ end
38
+
39
+ context "when given field's corresponding the column type is :datetime" do
40
+ let(:datetime) { DateTime.current }
41
+ let(:value) { datetime.to_i }
42
+
43
+ before { User.stub(:_protobuf_datetime_column?).and_return(true) }
44
+
45
+ it "converts the given value to a DateTime object" do
46
+ User._protobuf_convert_fields_to_columns(:foo_datetime, value).should be_a(DateTime)
47
+ end
48
+
49
+ it "converts the given value to a DateTime object of the same value" do
50
+ User._protobuf_convert_fields_to_columns(:foo_datetime, value).should be_within(1).of(datetime)
51
+ end
52
+ end
53
+
54
+ context "when given field's corresponding the column type is :time" do
55
+ let(:time) { Time.current }
56
+ let(:value) { time.to_i }
57
+
58
+ before { User.stub(:_protobuf_time_column?).and_return(true) }
59
+
60
+ it "converts the given value to a Time object" do
61
+ User._protobuf_convert_fields_to_columns(:foo_time, value).should be_a(Time)
62
+ end
63
+
64
+ it "converts the given value to a Time object of the same value" do
65
+ User._protobuf_convert_fields_to_columns(:foo_time, value).should be_within(1).of(time)
66
+ end
67
+ end
68
+
69
+ context "when given field's corresponding the column type is :timestamp" do
70
+ let(:time) { Time.current }
71
+ let(:value) { time.to_i }
72
+
73
+ before { User.stub(:_protobuf_timestamp_column?).and_return(true) }
74
+
75
+ it "converts the given value to a Time object" do
76
+ User._protobuf_convert_fields_to_columns(:foo_time, value).should be_a(Time)
77
+ end
78
+
79
+ it "converts the given value to a Time object of the same value" do
80
+ User._protobuf_convert_fields_to_columns(:foo_timestamp, value).should be_within(1).of(time)
81
+ end
82
+ end
83
+
84
+ context "when no conversion is necessary" do
85
+ let(:value) { "Foo" }
86
+
87
+ it "returns the given value" do
88
+ User._protobuf_convert_fields_to_columns(:foo, value).should eq value
89
+ end
90
+ end
91
+ end
92
+
93
+ describe ".attributes_from_proto" do
94
+ context "when a transformer is defined for the attribute" do
95
+ it "transforms the field value" do
96
+ attribute_fields = User.attributes_from_proto(proto)
97
+ attribute_fields[:first_name].should eq user_attributes[:first_name]
98
+ end
99
+ end
100
+
101
+ context "when a transformer is a callable that returns nil" do
102
+ before do
103
+ transformers = User._protobuf_attribute_transformers
104
+ User.stub(:_protobuf_attribute_transformers).and_return(
105
+ {:account_id => lambda { |proto| nil }}.merge(transformers)
106
+ )
107
+ end
108
+
109
+ it "does not set the attribute" do
110
+ attribute_fields = User.attributes_from_proto(proto)
111
+ attribute_fields.should eq user_attributes
112
+ end
113
+ end
114
+
115
+ context "when a transformer is a callable that returns a value" do
116
+ before do
117
+ transformers = User._protobuf_attribute_transformers
118
+ User.stub(:_protobuf_attribute_transformers).and_return(
119
+ {:account_id => lambda { |proto| 1 }}.merge(transformers)
120
+ )
121
+ end
122
+
123
+ it "sets the attribute" do
124
+ attribute_fields = User.attributes_from_proto(proto)
125
+ attribute_fields.should eq user_attributes.merge(:account_id => 1)
126
+ end
127
+ end
128
+
129
+ context "when a transformer is not defined for the attribute" do
130
+ before {
131
+ User.stub(:_protobuf_convert_fields_to_columns) do |key, value|
132
+ value
133
+ end
134
+ }
135
+
136
+ it "converts the field value" do
137
+ attribute_fields = User.attributes_from_proto(proto)
138
+ attribute_fields.should eq user_attributes
139
+ end
140
+ end
141
+ end
142
+
143
+ describe ".attribute_from_proto" do
144
+ context "when the given transformer is a symbol" do
145
+ let(:callable) { lambda { |value| User.__send__(:extract_first_name) } }
146
+
147
+ before { User.attribute_from_proto :first_name, :extract_first_name }
148
+
149
+ it "creates a callable method object from the converter" do
150
+ User.should_receive(:extract_first_name)
151
+ User._protobuf_attribute_transformers[:first_name].call(1)
152
+ end
153
+ end
154
+
155
+ context "when the given transformer is not callable" do
156
+ it "raises an exception" do
157
+ expect { User.attribute_from_proto :name, nil }.to raise_exception(Protoable::AttributeTransformerError)
158
+ end
159
+ end
160
+
161
+ context "when the given transformer is callable" do
162
+ let(:callable) { lambda { |proto| nil } }
163
+
164
+ before {
165
+ User.stub(:_protobuf_attribute_transformers).and_return(Hash.new)
166
+ User.attribute_from_proto :account_id, callable
167
+ }
168
+
169
+ it "adds the given converter to the list of protobuf field transformers" do
170
+ User._protobuf_attribute_transformers[:account_id] = callable
171
+ end
172
+ end
173
+ end
174
+
175
+ describe ".convert_int64_to_date" do
176
+ let(:date) { Date.current }
177
+ let(:int64) { date.to_time.to_i }
178
+
179
+ it "initializes a new Date object from the value" do
180
+ Timecop.freeze(Date.current) do
181
+ User.convert_int64_to_date(int64).should eq date
182
+ end
183
+ end
184
+ end
185
+
186
+ describe ".convert_int64_to_datetime" do
187
+ let(:datetime) { DateTime.current }
188
+ let(:int64) { datetime.to_time.to_i }
189
+
190
+ it "initializes a new DateTime object from the value" do
191
+ Timecop.freeze(DateTime.current) do
192
+ User.convert_int64_to_datetime(int64).should eq datetime
193
+ end
194
+ end
195
+ end
196
+
197
+ describe ".convert_int64_to_time" do
198
+ let(:time) { Time.current }
199
+ let(:int64) { time.to_time.to_i }
200
+
201
+ it "initializes a new Time object from the value" do
202
+ Timecop.freeze(Time.current) do
203
+ User.convert_int64_to_time(int64).should be_within(1).of(time)
204
+ end
205
+ end
206
+ end
207
+
208
+ describe "#attributes_from_proto" do
209
+ it "gets attributes from the given protobuf message" do
210
+ User.should_receive(:attributes_from_proto).with(proto)
211
+ user.attributes_from_proto(proto)
212
+ end
213
+ end
214
+ end