protobuf-activerecord 3.4.4 → 3.5.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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ebddfb7f1a2b6a894d213d63cec1a179e69c88c
|
4
|
+
data.tar.gz: 2ddf9cc3994a46fb2ec4c3118121be657faf7e2a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a5386b5374b38a42c3b507ff848c2afef0bea3d93ac3e8435e6c9863c87ff6fe1ef6bdf2c3d9ff7de7931b9735d92e0b673e268744140b28e23a733d7617ff9
|
7
|
+
data.tar.gz: f17ccbe9174babd312ebb75d35ffb65f8aff1c31886e20420d5f314029a3febf258eb377bc995b12e7cdf9e9256286938994d63bf801b0b83676d1b666581b45
|
data/README.md
CHANGED
@@ -255,6 +255,53 @@ User.limit(10).search_scope(request)
|
|
255
255
|
|
256
256
|
Protobuf Active Record also provides some aliases for the `search_scope` method in the event that you'd like something a little more descriptive: `by_fields` and `scope_from_proto` are all aliases of `search_scope`.
|
257
257
|
|
258
|
+
#### Upsert
|
259
|
+
|
260
|
+
Protobuf Active Record provides the ability to create a new record, or update an existing record if an existing record is found. This is implemented using Active Record's `first_or_initialize` method.
|
261
|
+
|
262
|
+
```Ruby
|
263
|
+
class User < ActiveRecord::Base
|
264
|
+
scope :by_guid, lambda { |*values| where(:guid => values) }
|
265
|
+
scope :by_first_name, lambda { |*values| where(:first_name => values) }
|
266
|
+
scope :by_last_name, lambda { |*values| where(:last_name => values) }
|
267
|
+
|
268
|
+
field_scope :guid
|
269
|
+
field_scope :first_name
|
270
|
+
field_scope :last_name
|
271
|
+
|
272
|
+
upsert_key :guid
|
273
|
+
upsert_key :first_name, :last_name
|
274
|
+
end
|
275
|
+
|
276
|
+
@user = User.for_upsert(request)
|
277
|
+
```
|
278
|
+
|
279
|
+
Note: An upsert_key should only be defined on a field or set of fields that have a unique constraint
|
280
|
+
|
281
|
+
Note: All fields used in an upsert key must also have a field_scope defined
|
282
|
+
|
283
|
+
If multiple upsert_keys match the request, the first matching upsert key will be used, in order of declaration. In the typical use-case where upsert keys have corresponding unique constraints the results should be equivalent regardless of order.
|
284
|
+
|
285
|
+
Protobuf Active Record provides several methods for invoking an upsert.
|
286
|
+
|
287
|
+
The first approach is to use the `for_upsert` method to look up a record.
|
288
|
+
|
289
|
+
```Ruby
|
290
|
+
@user = User.for_upsert(proto)
|
291
|
+
```
|
292
|
+
|
293
|
+
Alternatively, you can use `upsert` to look up the record and perform the persistence in the same call.
|
294
|
+
|
295
|
+
```Ruby
|
296
|
+
# Example
|
297
|
+
User.upsert(proto)
|
298
|
+
|
299
|
+
# This is equivalent to
|
300
|
+
user = User.for_upsert(proto)
|
301
|
+
user.assign_attributes(proto)
|
302
|
+
user.save
|
303
|
+
```
|
304
|
+
|
258
305
|
## Contributing
|
259
306
|
|
260
307
|
1. Fork it
|
@@ -38,5 +38,13 @@ module Protobuf
|
|
38
38
|
# Raised by `field_scope` when given scope is not defined.
|
39
39
|
class SearchScopeError < ProtobufActiveRecordError
|
40
40
|
end
|
41
|
+
|
42
|
+
# Raised by `upsert_scope` when a given scope is not defined
|
43
|
+
class UpsertScopeError < ProtobufActiveRecordError
|
44
|
+
end
|
45
|
+
|
46
|
+
# Raised by `for_upsert` when no valid upsert_scopes are found
|
47
|
+
class UpsertNotFoundError < ProtobufActiveRecordError
|
48
|
+
end
|
41
49
|
end
|
42
50
|
end
|
@@ -111,6 +111,80 @@ module Protobuf
|
|
111
111
|
def searchable_field_parsers
|
112
112
|
@_searchable_field_parsers ||= {}
|
113
113
|
end
|
114
|
+
|
115
|
+
# Defines a scope that is eligible for upsert. The scope will be
|
116
|
+
# used to initialize a record with first_or_initialize. An upsert scope
|
117
|
+
# declariation must specify one or more fields that are required to
|
118
|
+
# be present on the request and also must have a field_scope defined.
|
119
|
+
#
|
120
|
+
# If multiple upsert scopes are specified, they will be searched in
|
121
|
+
# the order they are declared for the first valid scope.
|
122
|
+
#
|
123
|
+
# Examples:
|
124
|
+
#
|
125
|
+
# class User < ActiveRecord::Base
|
126
|
+
# scope :by_guid, lambda { |*guids| where(:guid => guids) }
|
127
|
+
# scope :by_external_guid, lambda { |*external_guids|
|
128
|
+
# where(:external_guid => exteranl_guids)
|
129
|
+
# }
|
130
|
+
# scope :by_client_guid, lambda { |*client_guids|
|
131
|
+
# joins(:client).where(
|
132
|
+
# :clients => { :guid => client_guids }
|
133
|
+
# )
|
134
|
+
# }
|
135
|
+
#
|
136
|
+
# field_scope :guid
|
137
|
+
# field_scope :client_guid
|
138
|
+
# field_scope :external_guid
|
139
|
+
#
|
140
|
+
# upsert_scope :external_guid, :client_guid
|
141
|
+
# upsert_scope :guid
|
142
|
+
#
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
def upsert_key(*fields)
|
146
|
+
fields = fields.flatten
|
147
|
+
|
148
|
+
fields.each do |field|
|
149
|
+
fail UpsertScopeError unless searchable_fields[field].present?
|
150
|
+
end
|
151
|
+
|
152
|
+
upsert_keys << fields
|
153
|
+
end
|
154
|
+
|
155
|
+
def upsert_keys
|
156
|
+
@_upsert_keys ||= []
|
157
|
+
end
|
158
|
+
|
159
|
+
def for_upsert(proto)
|
160
|
+
valid_upsert = upsert_keys.find do |upsert_key|
|
161
|
+
upsert_key.all? do |field|
|
162
|
+
proto.respond_to_and_has_and_present?(field)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
fail UpsertNotFoundError unless valid_upsert.present?
|
167
|
+
|
168
|
+
upsert_scope = model_scope
|
169
|
+
valid_upsert.each do |field|
|
170
|
+
value = proto.__send__(field)
|
171
|
+
upsert_scope = upsert_scope.__send__(searchable_fields[field], value)
|
172
|
+
end
|
173
|
+
|
174
|
+
upsert_scope.first_or_initialize
|
175
|
+
end
|
176
|
+
|
177
|
+
def upsert(proto)
|
178
|
+
record = for_upsert(proto)
|
179
|
+
record.assign_attributes(proto)
|
180
|
+
record.save
|
181
|
+
end
|
182
|
+
|
183
|
+
def upsert!(proto)
|
184
|
+
record = for_upsert(proto)
|
185
|
+
record.assign_attributes(proto)
|
186
|
+
record.save!
|
187
|
+
end
|
114
188
|
end
|
115
189
|
end
|
116
190
|
end
|
@@ -9,6 +9,7 @@ describe Protobuf::ActiveRecord::Scope do
|
|
9
9
|
after do
|
10
10
|
User.instance_variable_set("@_searchable_field_parsers", @field_parsers)
|
11
11
|
User.instance_variable_set("@_searchable_fields", @fields)
|
12
|
+
User.instance_variable_set("@_upsert_keys", [])
|
12
13
|
end
|
13
14
|
|
14
15
|
|
@@ -78,7 +79,6 @@ describe Protobuf::ActiveRecord::Scope do
|
|
78
79
|
end
|
79
80
|
|
80
81
|
describe ".parse_search_values" do
|
81
|
-
|
82
82
|
it "converts single values to collections" do
|
83
83
|
proto = UserMessage.new(:email => "the.email@test.in")
|
84
84
|
|
@@ -116,4 +116,87 @@ describe Protobuf::ActiveRecord::Scope do
|
|
116
116
|
end
|
117
117
|
end
|
118
118
|
end
|
119
|
+
|
120
|
+
describe ".upsert_key" do
|
121
|
+
it "adds the fields to the upsert_keys" do
|
122
|
+
::User.field_scope(:guid)
|
123
|
+
::User.upsert_key(:guid)
|
124
|
+
expect(::User.upsert_keys).to eq([[:guid]])
|
125
|
+
end
|
126
|
+
|
127
|
+
context "no field_scope defined" do
|
128
|
+
it "raises an error" do
|
129
|
+
expect { ::User.upsert_key(:foobar) }.to raise_error(::Protobuf::ActiveRecord::UpsertScopeError)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe ".for_upsert" do
|
135
|
+
let(:guid) { "USR-1" }
|
136
|
+
let(:proto) { ::UserMessage.new(:guid => guid) }
|
137
|
+
|
138
|
+
before do
|
139
|
+
::User.delete_all
|
140
|
+
::User.field_scope(:guid)
|
141
|
+
::User.upsert_key(:guid)
|
142
|
+
end
|
143
|
+
|
144
|
+
context "no matching upsert keys" do
|
145
|
+
let(:proto) { ::UserMessage.new }
|
146
|
+
|
147
|
+
it "raises an error" do
|
148
|
+
expect { ::User.for_upsert(proto) }.to raise_error(::Protobuf::ActiveRecord::UpsertNotFoundError)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context "no existing records" do
|
153
|
+
it "returns a new record" do
|
154
|
+
record = ::User.for_upsert(proto)
|
155
|
+
expect(record.new_record?).to be true
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context "existing record" do
|
160
|
+
before { ::User.create(:guid => guid) }
|
161
|
+
after { ::User.delete_all }
|
162
|
+
|
163
|
+
it "returns the existing record" do
|
164
|
+
record = ::User.for_upsert(proto)
|
165
|
+
expect(record.new_record?).to be false
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
describe ".upsert" do
|
171
|
+
let(:guid) { "USR-1" }
|
172
|
+
let(:proto) { ::UserMessage.new(:guid => guid, :email => "bar") }
|
173
|
+
|
174
|
+
before do
|
175
|
+
::User.delete_all
|
176
|
+
::User.field_scope(:guid)
|
177
|
+
::User.upsert_key(:guid)
|
178
|
+
end
|
179
|
+
|
180
|
+
context "no existing records" do
|
181
|
+
it "creates a new record" do
|
182
|
+
::User.upsert(proto)
|
183
|
+
expect(::User.count).to eq(1)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "existing record" do
|
188
|
+
before { ::User.create(:guid => guid, :email => "foo") }
|
189
|
+
after { ::User.delete_all }
|
190
|
+
|
191
|
+
it "updates the existing record" do
|
192
|
+
::User.upsert(proto)
|
193
|
+
expect(::User.first.email).to eq("bar")
|
194
|
+
end
|
195
|
+
|
196
|
+
it "returns true when valid" do
|
197
|
+
result = ::User.upsert(proto)
|
198
|
+
expect(result).to be true
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
119
202
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: protobuf-activerecord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Hutchison
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-01-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -250,7 +250,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
250
250
|
version: '0'
|
251
251
|
requirements: []
|
252
252
|
rubyforge_project:
|
253
|
-
rubygems_version: 2.6.
|
253
|
+
rubygems_version: 2.6.10
|
254
254
|
signing_key:
|
255
255
|
specification_version: 4
|
256
256
|
summary: Google Protocol Buffers integration for Active Record
|