active_remote 2.1.1 → 2.2.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: f3b021e02d82dd94367950f76e8601d35dd47c28
4
- data.tar.gz: 23885a9b4e9a3166c7096fc983b6ede7c692f874
3
+ metadata.gz: ad233ac94027246310c94addc5e595b1cb6d25fb
4
+ data.tar.gz: 0a1985fd4e52f98fb757fccdb04ccd097ee4abd4
5
5
  SHA512:
6
- metadata.gz: 755211aa1e0e8db43d1eaf5c590f625edac7b56b73d2a91267b6473453774d2ddac17822c17f8456f90d3df549704f8995e5955bc63f135add6ade14bab1a49e
7
- data.tar.gz: f0c44e8adb7043d79b172b9745b0d089a1824ed0ff4a8f3d94d24fde9e3740208db5805e5817158ed81c7f3a3133908cd97359bc44460ecaf28ad652f8987a32
6
+ metadata.gz: 7dabce988979749626a43fa3ddcfbaa2df930f8c70e57c172e6a89c7390d9ab11e47def151c2b7773cf486572b719affcb41e329e0aca7e40a00a16389c6172c
7
+ data.tar.gz: 9c57148a75afc5dd51f5ec5a6d31e96c446888153099c38cc970a2d04a57431a194022fcd88a21d057c319e11f5d20d6e6563b4b5f87cf8b4fef2af1a72202ca
@@ -29,8 +29,9 @@ Gem::Specification.new do |s|
29
29
  s.add_development_dependency "better_receive"
30
30
  s.add_development_dependency "rake"
31
31
  s.add_development_dependency "rspec"
32
+ s.add_development_dependency "rspec-its"
32
33
  s.add_development_dependency "rspec-pride"
33
- s.add_development_dependency "pry-nav"
34
+ s.add_development_dependency "pry"
34
35
  s.add_development_dependency "protobuf-rspec", ">= 1.0"
35
36
  s.add_development_dependency "simplecov"
36
37
  end
@@ -15,6 +15,7 @@ require 'active_remote/rpc'
15
15
  require 'active_remote/scope_keys'
16
16
  require 'active_remote/search'
17
17
  require 'active_remote/serialization'
18
+ require 'active_remote/validations'
18
19
 
19
20
  module ActiveRemote
20
21
  class Base
@@ -39,6 +40,10 @@ module ActiveRemote
39
40
  # so it needs to be included last.
40
41
  include Dirty
41
42
 
43
+ # Overrides persistence methods, so it must included after
44
+ include Validations
45
+ include ActiveModel::Validations::Callbacks
46
+
42
47
  attr_reader :last_request, :last_response
43
48
 
44
49
  define_model_callbacks :initialize, :only => :after
@@ -9,6 +9,17 @@ module ActiveRemote
9
9
  class ReadOnlyRemoteRecord < ActiveRemoteError
10
10
  end
11
11
 
12
+ # Raised by ActiveRemote::Validations when save is called on an invalid record.
13
+ class RemoteRecordInvalid < ActiveRemoteError
14
+ attr_reader :record
15
+
16
+ def initialize(record)
17
+ @record = record
18
+ errors = @record.errors.full_messages.join(', ')
19
+ super(errors)
20
+ end
21
+ end
22
+
12
23
  # Raised by ActiveRemove::Base.find when remote record is not found when
13
24
  # searching with the given arguments.
14
25
  class RemoteRecordNotFound < ActiveRemoteError
@@ -176,9 +176,9 @@ module ActiveRemote
176
176
  #
177
177
  # Also runs any before/after save callbacks that are defined.
178
178
  #
179
- def save
179
+ def save(*args)
180
180
  run_callbacks :save do
181
- create_or_update
181
+ create_or_update(*args)
182
182
  end
183
183
  end
184
184
 
@@ -192,8 +192,8 @@ module ActiveRemote
192
192
  #
193
193
  # Also runs any before/after save callbacks that are defined.
194
194
  #
195
- def save!
196
- save || raise(RemoteRecordNotSaved)
195
+ def save!(*args)
196
+ save(*args) || raise(RemoteRecordNotSaved)
197
197
  end
198
198
 
199
199
  # Returns true if the record doesn't have errors; otherwise, returns false.
@@ -245,9 +245,9 @@ module ActiveRemote
245
245
  # are created, existing records are updated. If the record is marked as
246
246
  # readonly, an ActiveRemote::ReadOnlyRemoteRecord is raised.
247
247
  #
248
- def create_or_update
248
+ def create_or_update(*args)
249
249
  raise ReadOnlyRemoteRecord if readonly?
250
- new_record? ? create : update
250
+ new_record? ? create : update(*args)
251
251
  end
252
252
 
253
253
  # Handles updating a remote object and serializing it's attributes and
@@ -0,0 +1,64 @@
1
+ module ActiveRemote
2
+ module Validations
3
+ extend ActiveSupport::Concern
4
+
5
+ # Attempts to save the record like Persistence, but will run
6
+ # validations and return false if the record is invalid
7
+ #
8
+ # Validations can be skipped by passing :validate => false
9
+ #
10
+ # example Save a record
11
+ # post.save
12
+ #
13
+ # example Save a record, skip validations
14
+ # post.save(:validate => false)
15
+ #
16
+ def save(options = {})
17
+ perform_validations(options) ? super : false
18
+ end
19
+
20
+ # Attempts to save the record like Persistence, but will raise
21
+ # ActiveRemote::RemoteRecordInvalid if the record is not valid
22
+ #
23
+ # Validations can be skipped by passing :validate => false
24
+ #
25
+ # example Save a record, raise and error if invalid
26
+ # post.save!
27
+ #
28
+ # example Save a record, skip validations
29
+ # post.save!(:validate => false)
30
+ #
31
+ def save!(options = {})
32
+ perform_validations(options) ? super : raise_validation_error
33
+ end
34
+
35
+ # Runs all the validations within the specified context. Returns true if
36
+ # no errors are found, false otherwise.
37
+ #
38
+ # Aliased as validate.
39
+ #
40
+ # example Is the record valid?
41
+ # post.valid?
42
+ #
43
+ # example Is the record valid for creation?
44
+ # post.valid?(:create)
45
+ #
46
+ def valid?(context = nil)
47
+ context ||= (new_record? ? :create : :update)
48
+ output = super(context)
49
+ errors.empty? && output
50
+ end
51
+
52
+ alias_method :validate, :valid?
53
+
54
+ protected
55
+
56
+ def raise_validation_error
57
+ fail(::ActiveRemote::RemoteRecordInvalid.new(self))
58
+ end
59
+
60
+ def perform_validations(options = {})
61
+ options[:validate] == false || valid?(options[:context])
62
+ end
63
+ end
64
+ end
@@ -1,3 +1,3 @@
1
1
  module ActiveRemote
2
- VERSION = "2.1.1"
2
+ VERSION = "2.2.0"
3
3
  end
@@ -11,43 +11,43 @@ describe ActiveRemote::Association do
11
11
  let(:default_category_guid) { "CAT-123" }
12
12
  subject { Post.new(:author_guid => author_guid, :user_guid => user_guid) }
13
13
 
14
- it { should respond_to(:author) }
14
+ it { is_expected.to respond_to(:author) }
15
15
 
16
16
  it "searches the associated model for a single record" do
17
- Author.should_receive(:search).with(:guid => subject.author_guid).and_return(records)
18
- subject.author.should eq record
17
+ expect(Author).to receive(:search).with(:guid => subject.author_guid).and_return(records)
18
+ expect(subject.author).to eq record
19
19
  end
20
20
 
21
21
  it "memoizes the result record" do
22
- Author.should_receive(:search).once.with(:guid => subject.author_guid).and_return(records)
23
- 3.times { subject.author.should eq record }
22
+ expect(Author).to receive(:search).once.with(:guid => subject.author_guid).and_return(records)
23
+ 3.times { expect(subject.author).to eq record }
24
24
  end
25
25
 
26
26
  context "when guid is nil" do
27
27
  subject { Post.new }
28
28
 
29
29
  it "returns nil" do
30
- subject.author.should be_nil
30
+ expect(subject.author).to be_nil
31
31
  end
32
32
  end
33
33
 
34
34
  context "when the search is empty" do
35
35
  it "returns a nil" do
36
- Author.should_receive(:search).with(:guid => subject.author_guid).and_return([])
37
- subject.author.should be_nil
36
+ expect(Author).to receive(:search).with(:guid => subject.author_guid).and_return([])
37
+ expect(subject.author).to be_nil
38
38
  end
39
39
  end
40
40
 
41
41
  context 'scoped field' do
42
- it { should respond_to(:user) }
42
+ it { is_expected.to respond_to(:user) }
43
43
 
44
44
  it "searches the associated model for multiple records" do
45
- Author.should_receive(:search).with(:guid => subject.author_guid, :user_guid => subject.user_guid).and_return(records)
46
- subject.user.should eq(record)
45
+ expect(Author).to receive(:search).with(:guid => subject.author_guid, :user_guid => subject.user_guid).and_return(records)
46
+ expect(subject.user).to eq(record)
47
47
  end
48
48
 
49
49
  context 'when user_guid doesnt exist on model 'do
50
- before { subject.stub(:respond_to?).with("user_guid").and_return(false) }
50
+ before { allow(subject).to receive(:respond_to?).with("user_guid").and_return(false) }
51
51
 
52
52
  it 'raises an error' do
53
53
  expect {subject.user}.to raise_error
@@ -69,11 +69,11 @@ describe ActiveRemote::Association do
69
69
  let(:author_guid) { "AUT-456" }
70
70
 
71
71
  subject { Post.new(:author_guid => author_guid) }
72
- it { should respond_to(:coauthor) }
72
+ it { is_expected.to respond_to(:coauthor) }
73
73
 
74
74
  it "searches the associated model for a single record" do
75
- Author.should_receive(:search).with(:guid => subject.author_guid).and_return(records)
76
- subject.coauthor.should eq record
75
+ expect(Author).to receive(:search).with(:guid => subject.author_guid).and_return(records)
76
+ expect(subject.coauthor).to eq record
77
77
  end
78
78
  end
79
79
 
@@ -81,11 +81,11 @@ describe ActiveRemote::Association do
81
81
  let(:author_guid) { "AUT-456" }
82
82
 
83
83
  subject { Post.new(:bestseller_guid => author_guid) }
84
- it { should respond_to(:bestseller) }
84
+ it { is_expected.to respond_to(:bestseller) }
85
85
 
86
86
  it "searches the associated model for a single record" do
87
- Author.should_receive(:search).with(:guid => subject.bestseller_guid).and_return(records)
88
- subject.bestseller.should eq record
87
+ expect(Author).to receive(:search).with(:guid => subject.bestseller_guid).and_return(records)
88
+ expect(subject.bestseller).to eq record
89
89
  end
90
90
  end
91
91
  end
@@ -97,61 +97,61 @@ describe ActiveRemote::Association do
97
97
 
98
98
  subject { Author.new(:guid => guid, :user_guid => user_guid) }
99
99
 
100
- it { should respond_to(:posts) }
100
+ it { is_expected.to respond_to(:posts) }
101
101
 
102
102
  it "searches the associated model for all associated records" do
103
- Post.should_receive(:search).with(:author_guid => subject.guid).and_return(records)
104
- subject.posts.should eq records
103
+ expect(Post).to receive(:search).with(:author_guid => subject.guid).and_return(records)
104
+ expect(subject.posts).to eq records
105
105
  end
106
106
 
107
107
  it "memoizes the result record" do
108
- Post.should_receive(:search).once.with(:author_guid => subject.guid).and_return(records)
109
- 3.times { subject.posts.should eq records }
108
+ expect(Post).to receive(:search).once.with(:author_guid => subject.guid).and_return(records)
109
+ 3.times { expect(subject.posts).to eq records }
110
110
  end
111
111
 
112
112
  context "when guid is nil" do
113
113
  subject { Author.new }
114
114
 
115
115
  it "returns []" do
116
- subject.posts.should eq []
116
+ expect(subject.posts).to eq []
117
117
  end
118
118
  end
119
119
 
120
120
  context "when the search is empty" do
121
121
  it "returns the empty set" do
122
- Post.should_receive(:search).with(:author_guid => subject.guid).and_return([])
123
- subject.posts.should be_empty
122
+ expect(Post).to receive(:search).with(:author_guid => subject.guid).and_return([])
123
+ expect(subject.posts).to be_empty
124
124
  end
125
125
  end
126
126
 
127
127
  context "specific association with class name" do
128
- it { should respond_to(:flagged_posts) }
128
+ it { is_expected.to respond_to(:flagged_posts) }
129
129
 
130
130
  it "searches the associated model for a single record" do
131
- Post.should_receive(:search).with(:author_guid => subject.guid).and_return([])
132
- subject.flagged_posts.should be_empty
131
+ expect(Post).to receive(:search).with(:author_guid => subject.guid).and_return([])
132
+ expect(subject.flagged_posts).to be_empty
133
133
  end
134
134
  end
135
135
 
136
136
  context "specific association with class name and foreign_key" do
137
- it { should respond_to(:bestseller_posts) }
137
+ it { is_expected.to respond_to(:bestseller_posts) }
138
138
 
139
139
  it "searches the associated model for multiple record" do
140
- Post.should_receive(:search).with(:bestseller_guid => subject.guid).and_return(records)
141
- subject.bestseller_posts.should eq(records)
140
+ expect(Post).to receive(:search).with(:bestseller_guid => subject.guid).and_return(records)
141
+ expect(subject.bestseller_posts).to eq(records)
142
142
  end
143
143
  end
144
144
 
145
145
  context 'scoped field' do
146
- it { should respond_to(:user_posts) }
146
+ it { is_expected.to respond_to(:user_posts) }
147
147
 
148
148
  it "searches the associated model for multiple records" do
149
- Post.should_receive(:search).with(:author_guid => subject.guid, :user_guid => subject.user_guid).and_return(records)
150
- subject.user_posts.should eq(records)
149
+ expect(Post).to receive(:search).with(:author_guid => subject.guid, :user_guid => subject.user_guid).and_return(records)
150
+ expect(subject.user_posts).to eq(records)
151
151
  end
152
152
 
153
153
  context 'when user_guid doesnt exist on model 'do
154
- before { subject.stub(:respond_to?).with("user_guid").and_return(false) }
154
+ before { allow(subject).to receive(:respond_to?).with("user_guid").and_return(false) }
155
155
 
156
156
  it 'raises an error' do
157
157
  expect {subject.user_posts}.to raise_error
@@ -180,61 +180,61 @@ describe ActiveRemote::Association do
180
180
 
181
181
  subject { Category.new(category_attributes) }
182
182
 
183
- it { should respond_to(:author) }
183
+ it { is_expected.to respond_to(:author) }
184
184
 
185
185
  it "searches the associated model for all associated records" do
186
- Author.should_receive(:search).with(:category_guid => subject.guid).and_return(records)
187
- subject.author.should eq record
186
+ expect(Author).to receive(:search).with(:category_guid => subject.guid).and_return(records)
187
+ expect(subject.author).to eq record
188
188
  end
189
189
 
190
190
  it "memoizes the result record" do
191
- Author.should_receive(:search).once.with(:category_guid => subject.guid).and_return(records)
192
- 3.times { subject.author.should eq record }
191
+ expect(Author).to receive(:search).once.with(:category_guid => subject.guid).and_return(records)
192
+ 3.times { expect(subject.author).to eq record }
193
193
  end
194
194
 
195
195
  context "when guid is nil" do
196
196
  subject { Category.new }
197
197
 
198
198
  it "returns nil" do
199
- subject.author.should be_nil
199
+ expect(subject.author).to be_nil
200
200
  end
201
201
  end
202
202
 
203
203
  context "when the search is empty" do
204
204
  it "returns a nil value" do
205
- Author.should_receive(:search).with(:category_guid => subject.guid).and_return([])
206
- subject.author.should be_nil
205
+ expect(Author).to receive(:search).with(:category_guid => subject.guid).and_return([])
206
+ expect(subject.author).to be_nil
207
207
  end
208
208
  end
209
209
 
210
210
  context "specific association with class name" do
211
- it { should respond_to(:senior_author) }
211
+ it { is_expected.to respond_to(:senior_author) }
212
212
 
213
213
  it "searches the associated model for a single record" do
214
- Author.should_receive(:search).with(:category_guid => subject.guid).and_return(records)
215
- subject.senior_author.should eq record
214
+ expect(Author).to receive(:search).with(:category_guid => subject.guid).and_return(records)
215
+ expect(subject.senior_author).to eq record
216
216
  end
217
217
  end
218
218
 
219
219
  context "specific association with class name and foreign_key" do
220
- it { should respond_to(:primary_editor) }
220
+ it { is_expected.to respond_to(:primary_editor) }
221
221
 
222
222
  it "searches the associated model for a single record" do
223
- Author.should_receive(:search).with(:editor_guid => subject.guid).and_return(records)
224
- subject.primary_editor.should eq record
223
+ expect(Author).to receive(:search).with(:editor_guid => subject.guid).and_return(records)
224
+ expect(subject.primary_editor).to eq record
225
225
  end
226
226
  end
227
227
 
228
228
  context 'scoped field' do
229
- it { should respond_to(:chief_editor) }
229
+ it { is_expected.to respond_to(:chief_editor) }
230
230
 
231
231
  it "searches the associated model for multiple records" do
232
- Author.should_receive(:search).with(:chief_editor_guid => subject.guid, :user_guid => subject.user_guid).and_return(records)
233
- subject.chief_editor.should eq(record)
232
+ expect(Author).to receive(:search).with(:chief_editor_guid => subject.guid, :user_guid => subject.user_guid).and_return(records)
233
+ expect(subject.chief_editor).to eq(record)
234
234
  end
235
235
 
236
236
  context 'when user_guid doesnt exist on model 'do
237
- before { subject.stub(:respond_to?).with("user_guid").and_return(false) }
237
+ before { allow(subject).to receive(:respond_to?).with("user_guid").and_return(false) }
238
238
 
239
239
  it 'raises an error' do
240
240
  expect {subject.chief_editor}.to raise_error
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe ActiveRemote::Base do
4
4
  describe "#initialize" do
5
5
  it "runs callbacks" do
6
- described_class.any_instance.should_receive(:run_callbacks).with(:initialize)
6
+ expect_any_instance_of(described_class).to receive(:run_callbacks).with(:initialize)
7
7
  described_class.new
8
8
  end
9
9
  end
@@ -12,13 +12,13 @@ describe ActiveRemote::Dirty do
12
12
  context "when the value changes" do
13
13
  before { subject.name = 'bar' }
14
14
 
15
- its(:name_changed?) { should be_true }
15
+ its(:name_changed?) { should be_truthy }
16
16
  end
17
17
 
18
18
  context "when the value doesn't change" do
19
19
  before { subject.name = 'foo' }
20
20
 
21
- its(:name_changed?) { should be_false }
21
+ its(:name_changed?) { should be_falsey }
22
22
  end
23
23
  end
24
24
 
@@ -33,13 +33,13 @@ describe ActiveRemote::Dirty do
33
33
  context "when the value changes" do
34
34
  before { subject[:name] = 'bar' }
35
35
 
36
- its(:name_changed?) { should be_true }
36
+ its(:name_changed?) { should be_truthy }
37
37
  end
38
38
 
39
39
  context "when the value doesn't change" do
40
40
  before { subject[:name] = 'foo' }
41
41
 
42
- its(:name_changed?) { should be_false }
42
+ its(:name_changed?) { should be_falsey }
43
43
  end
44
44
  end
45
45
 
@@ -47,7 +47,7 @@ describe ActiveRemote::Dirty do
47
47
  subject { Post.new(:name => 'foo') }
48
48
 
49
49
  before {
50
- Post.stub(:find).and_return({})
50
+ allow(Post).to receive(:find).and_return({})
51
51
  subject.reload
52
52
  }
53
53
 
@@ -60,7 +60,7 @@ describe ActiveRemote::Dirty do
60
60
  subject { Post.new(:name => 'foo') }
61
61
 
62
62
  before {
63
- subject.stub(:create_or_update).and_return(true)
63
+ allow(subject).to receive(:create_or_update).and_return(true)
64
64
  subject.save
65
65
  }
66
66
 
@@ -74,7 +74,7 @@ describe ActiveRemote::Dirty do
74
74
  subject { Post.new(:name => 'foo') }
75
75
 
76
76
  before {
77
- subject.stub(:save).and_return(true)
77
+ allow(subject).to receive(:save).and_return(true)
78
78
  subject.save!
79
79
  }
80
80
 
@@ -88,12 +88,12 @@ describe ActiveRemote::Dirty do
88
88
 
89
89
  it "clears previous changes" do
90
90
  new_record = post.instantiate(record.to_hash)
91
- new_record.previous_changes.should be_nil
91
+ expect(new_record.previous_changes).to eq({})
92
92
  end
93
93
 
94
94
  it "clears changes" do
95
95
  new_record = post.instantiate(record.to_hash)
96
- new_record.changes.should be_empty
96
+ expect(new_record.changes).to be_empty
97
97
  end
98
98
  end
99
99
  end