protobuf-activerecord 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +6 -2
- data/README.md +83 -44
- data/lib/protoable/persistence.rb +4 -4
- data/lib/protobuf/activerecord/version.rb +1 -1
- data/spec/protoable/persistence_spec.rb +48 -0
- metadata +4 -4
data/Gemfile
CHANGED
@@ -4,6 +4,10 @@ source 'https://rubygems.org'
|
|
4
4
|
# Specify your gem's dependencies in protobuf-activerecord.gemspec
|
5
5
|
gemspec
|
6
6
|
|
7
|
-
gem 'builder', '~> 3.0.3
|
7
|
+
gem 'builder', '~> 3.0.4' # Builder 3.1.x is not supported by Active Record
|
8
|
+
# and Bundler has trouble resolving the dependency
|
9
|
+
# with Geminabox.
|
8
10
|
|
9
|
-
gem 'timecop', '~> 0.3.5', :group => :development
|
11
|
+
gem 'timecop', '~> 0.3.5', :group => :development # Timecop 0.4.x is has a bug
|
12
|
+
# when dealing with timezones
|
13
|
+
# on Time objects.
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Protobuf ActiveRecord
|
2
2
|
|
3
|
-
Protobuf Active Record provides the ability to create
|
3
|
+
Protobuf Active Record provides the ability to create and update Active Record objects from protobuf messages and to serialize Active Record objects to protobuf messages.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -21,14 +21,12 @@ Or install it yourself as:
|
|
21
21
|
Protobuf Active Record's functionality is contained within the `Protoable` module. To endow your Active Record models with the protoable behaviour, simply include it into your model:
|
22
22
|
|
23
23
|
```Ruby
|
24
|
+
class User < ActiveRecord::Base
|
25
|
+
include Protoable
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
# Your awesome methods...
|
29
|
-
#
|
30
|
-
end
|
31
|
-
|
27
|
+
# Your awesome methods...
|
28
|
+
#
|
29
|
+
end
|
32
30
|
```
|
33
31
|
|
34
32
|
Now you can pass protobuf messages to your user model just like you would attributes. Protoable will take care of converting the protobuf message to attributes and continue on with Active Record's normal behavior.
|
@@ -40,29 +38,25 @@ Just like Active Record maps database columns to your model's attributes, Protoa
|
|
40
38
|
Given a table that looks like this:
|
41
39
|
|
42
40
|
```Ruby
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
|
41
|
+
create_table :users do |t|
|
42
|
+
t.string :first_name
|
43
|
+
t.string :last_name
|
44
|
+
t.string :email
|
45
|
+
t.integer :account_id
|
46
|
+
|
47
|
+
t.timestamps
|
48
|
+
end
|
53
49
|
```
|
54
50
|
|
55
51
|
and a protobuf message that looks like this:
|
56
52
|
|
57
53
|
```Ruby
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
65
|
-
|
54
|
+
class UserMessage < ::Protobuf::Message
|
55
|
+
optional ::Protobuf::Field::StringField, :first_name, 1
|
56
|
+
optional ::Protobuf::Field::StringField, :last_name, 2
|
57
|
+
optional ::Protobuf::Field::StringField, :email, 3
|
58
|
+
optional ::Protobuf::Field::IntegerField, :account_id, 4
|
59
|
+
end
|
66
60
|
```
|
67
61
|
|
68
62
|
Protoable will map the `first_name`, `last_name`, `email`, & `account_id` columns, skipping the timestamp columns. Repeated fields and fields that are nil will not be mapped.
|
@@ -74,42 +68,87 @@ Since Protocol Buffer messages don't support sending date, time, or datetime fie
|
|
74
68
|
Picking up our users table example again, if you wanted to add a `created_at` field to your protobuf message, if you add it as an integer field, Protoable will handle the conversions for you:
|
75
69
|
|
76
70
|
```Ruby
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
end
|
87
|
-
|
71
|
+
class UserMessage < ::Protobuf::Message
|
72
|
+
optional ::Protobuf::Field::StringField, :first_name, 1
|
73
|
+
optional ::Protobuf::Field::StringField, :last_name, 2
|
74
|
+
optional ::Protobuf::Field::StringField, :email, 3
|
75
|
+
optional ::Protobuf::Field::IntegerField, :account_id, 4
|
76
|
+
|
77
|
+
# Add a datetime field as an integer and Protoable will map it for you
|
78
|
+
optional ::Protobuf::Field::IntegerField, :created_at, 5
|
79
|
+
end
|
88
80
|
```
|
89
81
|
|
90
82
|
**Mass-assignment**
|
91
83
|
|
92
84
|
If a model has protected attributes defined, Protoable will skip any fields that map to them. Likewise, if there are accessible attributes defined, only they will be mapped.
|
93
85
|
|
94
|
-
###
|
86
|
+
### Creating/Updating
|
95
87
|
|
96
88
|
Protoable doesn't alter Active Record's normal persistence methods. It simply adds to ability to pass protobuf messages to them in place of an attributes hash.
|
97
89
|
|
98
|
-
### Serialization
|
90
|
+
### Serialization to protobuf
|
99
91
|
|
100
92
|
In addition to mapping protobuf message fields to Active Record objects when creating or updating records, Protoable also provides the ability to serialize Active Record objects to protobuf messages. Simply tell Protoable the protobuf message that should be used and it will take care of the rest:
|
101
93
|
|
102
94
|
```Ruby
|
95
|
+
class User < ActiveRecord::Base
|
96
|
+
include Protoable
|
103
97
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
end
|
108
|
-
|
98
|
+
# Configures Protoable to use the UserMessage class and adds :to_proto.
|
99
|
+
protobuf_message :user_message
|
100
|
+
end
|
109
101
|
```
|
110
102
|
|
111
103
|
Once the desired protobuf message has been specified, Protoable adds a `to_proto` method to the model. Calling `to_proto` will automatically convert the model to the specified protobuf message using the same attribute to field mapping it uses to create and update objects from protobuf messages.
|
112
104
|
|
105
|
+
### Field & column converters
|
106
|
+
|
107
|
+
Protoable will handle regular field conversions out of the box, but for those times when custom conversions are needed, they can be defined with the `convert_field` and `convert_column` methods. Field converters are used when creating or updating objects from a protobuf message and column converters are used when serializing objects to protobuf messages.
|
108
|
+
|
109
|
+
`convert_field` and `convert_column` both take the name of the field/column being converted and a method name or callable (lambda or proc). when converting that field, calls the given callable, passing it the value of the field being converted.
|
110
|
+
|
111
|
+
**Converting fields**
|
112
|
+
|
113
|
+
```Ruby
|
114
|
+
class User < ActiveRecord::Base
|
115
|
+
include Protoable
|
116
|
+
|
117
|
+
# Calls :map_status_from_proto when creating/updating objects, passing it the
|
118
|
+
# value of the status field from the protobuf message.
|
119
|
+
def self.map_status_from_proto(field_value)
|
120
|
+
# Some custom mapping
|
121
|
+
end
|
122
|
+
convert_field :status, :map_status_from_proto
|
123
|
+
end
|
124
|
+
```
|
125
|
+
|
126
|
+
**Converting columns**
|
127
|
+
|
128
|
+
```Ruby
|
129
|
+
class User < ActiveRecord::Base
|
130
|
+
include Protoable
|
131
|
+
|
132
|
+
# Calls the lambda when serializing objects to protobuf messages, passing it
|
133
|
+
# the value of the status column from the database.
|
134
|
+
convert_column :status, lambda { |column_value| column_value_.to_s }
|
135
|
+
end
|
136
|
+
```
|
137
|
+
|
138
|
+
### Column transformers
|
139
|
+
|
140
|
+
Protoable handles mapping protobuf message fields to object attributes, but what happens when an attribute doesn't have a matching field? Using the `transform_column` method, you can define custom column transformations. Simply call `transform_column`, passing it the name of the column and a method name or callable (lambda or proc). When creating or updating objects, Protoable will call the transformer, passing it the protobuf message.
|
141
|
+
|
142
|
+
```Ruby
|
143
|
+
class User < ActiveRecord::Base
|
144
|
+
include Protoable
|
145
|
+
|
146
|
+
# Calls the lambda when creating/updating objects, passing it the protobuf
|
147
|
+
# message.
|
148
|
+
transform_column :account_id, lambda { |protobuf_message| # Some custom transformation... }
|
149
|
+
end
|
150
|
+
```
|
151
|
+
|
113
152
|
## Contributing
|
114
153
|
|
115
154
|
1. Fork it
|
@@ -53,14 +53,14 @@ module Protoable
|
|
53
53
|
|
54
54
|
# :nodoc:
|
55
55
|
def create(attributes, options = {}, &block)
|
56
|
-
attributes = attributes_from_proto(
|
56
|
+
attributes = attributes_from_proto(attributes) if attributes.is_a?(::Protobuf::Message)
|
57
57
|
|
58
58
|
super(attributes, options)
|
59
59
|
end
|
60
60
|
|
61
61
|
# :nodoc:
|
62
62
|
def create!(attributes, options = {}, &block)
|
63
|
-
attributes = attributes_from_proto(
|
63
|
+
attributes = attributes_from_proto(attributes) if attributes.is_a?(::Protobuf::Message)
|
64
64
|
|
65
65
|
super(attributes, options)
|
66
66
|
end
|
@@ -99,14 +99,14 @@ module Protoable
|
|
99
99
|
|
100
100
|
# :nodoc:
|
101
101
|
def update_attributes(attributes, options = {})
|
102
|
-
attributes = attributes_from_proto(
|
102
|
+
attributes = attributes_from_proto(attributes) if attributes.is_a?(::Protobuf::Message)
|
103
103
|
|
104
104
|
super(attributes, options)
|
105
105
|
end
|
106
106
|
|
107
107
|
# :nodoc:
|
108
108
|
def update_attributes!(attributes, options = {})
|
109
|
-
attributes = attributes_from_proto(
|
109
|
+
attributes = attributes_from_proto(attributes) if attributes.is_a?(::Protobuf::Message)
|
110
110
|
|
111
111
|
super(attributes, options)
|
112
112
|
end
|
@@ -52,6 +52,30 @@ describe Protoable::Persistence do
|
|
52
52
|
end
|
53
53
|
end
|
54
54
|
|
55
|
+
describe ".create" do
|
56
|
+
it "accepts a protobuf message" do
|
57
|
+
User.any_instance.should_receive(:save)
|
58
|
+
User.create(proto)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "accepts a hash" do
|
62
|
+
User.any_instance.should_receive(:save)
|
63
|
+
User.create(user_attributes)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe ".create!" do
|
68
|
+
it "accepts a protobuf message" do
|
69
|
+
User.any_instance.should_receive(:save!)
|
70
|
+
User.create!(proto)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "accepts a hash" do
|
74
|
+
User.any_instance.should_receive(:save!)
|
75
|
+
User.create!(user_attributes)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
55
79
|
describe ".create_from_proto" do
|
56
80
|
it "initializes a new object with attributes from the given protobuf message" do
|
57
81
|
user = User.create_from_proto(proto)
|
@@ -110,6 +134,30 @@ describe Protoable::Persistence do
|
|
110
134
|
end
|
111
135
|
end
|
112
136
|
|
137
|
+
describe ".update_attributes" do
|
138
|
+
it "accepts a protobuf message" do
|
139
|
+
User.any_instance.should_receive(:save)
|
140
|
+
user.update_attributes(proto)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "accepts a hash" do
|
144
|
+
User.any_instance.should_receive(:save)
|
145
|
+
user.update_attributes(user_attributes)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe ".update_attributes!" do
|
150
|
+
it "accepts a protobuf message" do
|
151
|
+
User.any_instance.should_receive(:save!)
|
152
|
+
user.update_attributes!(proto)
|
153
|
+
end
|
154
|
+
|
155
|
+
it "accepts a hash" do
|
156
|
+
User.any_instance.should_receive(:save!)
|
157
|
+
user.update_attributes!(user_attributes)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
113
161
|
describe "#update_from_proto" do
|
114
162
|
it "updates the object with attributes from the given protobuf message" do
|
115
163
|
user.should_receive(:assign_attributes).with(user_attributes, {})
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: protobuf-activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -222,7 +222,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
222
222
|
version: '0'
|
223
223
|
segments:
|
224
224
|
- 0
|
225
|
-
hash:
|
225
|
+
hash: 763481842519497626
|
226
226
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
227
227
|
none: false
|
228
228
|
requirements:
|
@@ -231,7 +231,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
231
231
|
version: '0'
|
232
232
|
segments:
|
233
233
|
- 0
|
234
|
-
hash:
|
234
|
+
hash: 763481842519497626
|
235
235
|
requirements: []
|
236
236
|
rubyforge_project:
|
237
237
|
rubygems_version: 1.8.24
|