remotable 0.2.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/lib/remotable.rb CHANGED
@@ -73,7 +73,7 @@ module Remotable
73
73
  # * getters and setters for each attribute
74
74
  #
75
75
  def remote_model(*args)
76
- if args.any?
76
+ if args.length >= 1
77
77
  @remote_model = args.first
78
78
 
79
79
  @__remotable_included ||= begin
@@ -82,7 +82,7 @@ module Remotable
82
82
  true
83
83
  end
84
84
 
85
- extend_remote_model(@remote_model)
85
+ extend_remote_model(@remote_model) if @remote_model
86
86
  end
87
87
  @remote_model
88
88
  end
@@ -19,7 +19,7 @@ module Remotable
19
19
  before_create :create_remote_resource, :unless => :nosync?
20
20
  before_destroy :destroy_remote_resource, :unless => :nosync?
21
21
 
22
- before_validation :reset_expiration_date, :on => :create, :unless => :nosync?
22
+ before_validation :initialize_expiration_date, :on => :create
23
23
 
24
24
  validates_presence_of :expires_at
25
25
 
@@ -34,7 +34,7 @@ module Remotable
34
34
  include Nosync
35
35
 
36
36
  def nosync?
37
- Remotable.nosync? || super
37
+ Remotable.nosync? || remote_model.nil? || super
38
38
  end
39
39
 
40
40
  # Sets the key with which a resource is identified remotely.
@@ -326,7 +326,11 @@ module Remotable
326
326
 
327
327
 
328
328
  def pull_remote_data!
329
- merge_remote_data!(remote_resource)
329
+ if remote_resource
330
+ merge_remote_data!(remote_resource)
331
+ elsif fetch_value
332
+ remote_model_has_been_destroyed!
333
+ end
330
334
  end
331
335
 
332
336
 
@@ -343,33 +347,33 @@ module Remotable
343
347
 
344
348
  private
345
349
 
350
+ def fetch_value
351
+ self[local_key]
352
+ end
353
+
346
354
  def fetch_remote_resource
347
- fetch_value = self[local_key]
348
- # puts "local_key", local_key.inspect, "",
349
- # "remote_key", remote_key.inspect, "",
350
- # "fetch_value", fetch_value
351
- find_remote_resource_by(remote_key, fetch_value)
355
+ fetch_value && find_remote_resource_by(remote_key, fetch_value)
352
356
  end
353
357
 
354
358
  def merge_remote_data!(remote_resource)
355
- if remote_resource.nil?
356
- nosync { destroy }
357
-
358
- else
359
- merge_remote_data(remote_resource)
360
- reset_expiration_date
361
- nosync { save! }
362
- end
363
-
359
+ merge_remote_data(remote_resource)
360
+ reset_expiration_date
361
+ nosync { save! }
364
362
  self
365
363
  end
366
364
 
365
+ def remote_model_has_been_destroyed!
366
+ nosync { destroy }
367
+ end
368
+
367
369
 
368
370
 
369
371
  def update_remote_resource
370
- if any_remote_changes?
372
+ if any_remote_changes? && remote_resource
371
373
  merge_local_data(remote_resource, true)
372
- unless remote_resource.save
374
+ if remote_resource.save
375
+ merge_remote_data!(remote_resource)
376
+ else
373
377
  merge_remote_errors(remote_resource.errors)
374
378
  raise ActiveRecord::RecordInvalid.new(self)
375
379
  end
@@ -393,11 +397,20 @@ module Remotable
393
397
  end
394
398
 
395
399
  def destroy_remote_resource
396
- remote_resource && remote_resource.destroy
400
+ if remote_resource && remote_resource.destroy
401
+ true
402
+ else
403
+ merge_remote_errors(remote_resource.errors)
404
+ false
405
+ end
397
406
  end
398
407
 
399
408
 
400
409
 
410
+ def initialize_expiration_date
411
+ reset_expiration_date unless self.expires_at
412
+ end
413
+
401
414
  def reset_expiration_date
402
415
  self.expires_at = expires_after.from_now
403
416
  end
@@ -1,7 +1,9 @@
1
1
  require "active_resource"
2
+ require "active_support/concern"
2
3
 
3
4
 
4
5
  module ActiveResourceFixes
6
+ extend ActiveSupport::Concern
5
7
 
6
8
  # ActiveResource hacks method_missing without hacking respond_to?
7
9
  # In fact, it responds to any method that ends in an equals sign.
@@ -17,11 +19,30 @@ module ActiveResourceFixes
17
19
  end
18
20
  end
19
21
 
22
+
23
+ included do
24
+ alias_method_chain :destroy, :validation
25
+ end
26
+
27
+
28
+ # ActiveResource::Validations overrides ActiveResource::Base#save
29
+ # to rescue from ActiveResource::ResourceInvalid and record the
30
+ # resource's errors. Do the same for `destroy`.
31
+ def destroy_with_validation
32
+ destroy_without_validation
33
+ rescue ActiveResource::ResourceInvalid => error
34
+ # cache the remote errors because every call to <tt>valid?</tt> clears
35
+ # all errors. We must keep a copy to add these back after local
36
+ # validations.
37
+ @remote_errors = error
38
+ load_remote_errors(@remote_errors, true)
39
+ false
40
+ end
41
+
20
42
  end
21
43
 
22
44
 
23
45
  module ActiveResourceFixes30
24
- include ActiveResourceFixes
25
46
 
26
47
  # ! in this method, don't check the Content-Type header: rack doesn't always return it
27
48
  def load_attributes_from_response(response)
@@ -34,7 +55,6 @@ end
34
55
 
35
56
 
36
57
  module ActiveResourceFixes31
37
- include ActiveResourceFixes
38
58
 
39
59
  # ! in this method, don't check the Content-Type header: rack doesn't always return it
40
60
  def load_attributes_from_response(response)
@@ -54,6 +74,9 @@ else
54
74
  end
55
75
 
56
76
 
77
+ ActiveResource::Base.send(:include, ActiveResourceFixes)
78
+
79
+
57
80
  # ActiveResource expects that errors will be an array of string
58
81
  # However, this is not what Rails Responders are inclined to return.
59
82
 
@@ -1,3 +1,3 @@
1
1
  module Remotable
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.2"
3
3
  end
@@ -15,7 +15,7 @@ class ActiveResourceTest < ActiveSupport::TestCase
15
15
 
16
16
 
17
17
  # ========================================================================= #
18
- # Finding #
18
+ # Finding #
19
19
  # ========================================================================= #
20
20
 
21
21
  test "should be able to find resources by different attributes" do
@@ -166,7 +166,7 @@ class ActiveResourceTest < ActiveSupport::TestCase
166
166
  RemoteTenant.run_simulation do |s|
167
167
  s.show(tenant.remote_id, {
168
168
  :id => tenant.remote_id,
169
- :slug => tenant.slug,
169
+ :slug => "totally-wonky",
170
170
  :church_name => tenant.name
171
171
  })
172
172
 
@@ -178,6 +178,7 @@ class ActiveResourceTest < ActiveSupport::TestCase
178
178
  mock(tenant.remote_resource).save { true }
179
179
 
180
180
  tenant.save!
181
+ assert_equal "totally-wonky", tenant.slug, "After updating a record, remote data should be merge"
181
182
  end
182
183
  end
183
184
 
@@ -227,6 +228,7 @@ class ActiveResourceTest < ActiveSupport::TestCase
227
228
  tenant.save!
228
229
 
229
230
  assert_equal true, tenant.remote_resource.persisted?
231
+ assert_equal 143, tenant.remote_id, "After creating a record, remote data should be merge"
230
232
  end
231
233
  end
232
234
 
@@ -305,12 +307,14 @@ class ActiveResourceTest < ActiveSupport::TestCase
305
307
  :church_name => tenant.name
306
308
  })
307
309
 
308
- s.destroy(tenant.remote_id, :status => 500)
310
+ s.destroy(tenant.remote_id,
311
+ :body => { :errors => { :base => ["nope"] } },
312
+ :status => 422)
309
313
 
310
314
  tenant.nosync = false
311
- assert_raises(ActiveResource::ServerError) do
312
- tenant.destroy
313
- end
315
+ tenant.destroy
316
+ assert_equal false, tenant.destroyed?
317
+ assert_equal ["nope"], tenant.errors[:base]
314
318
  end
315
319
  end
316
320
 
@@ -7,6 +7,10 @@ class RemotableTest < ActiveSupport::TestCase
7
7
 
8
8
 
9
9
 
10
+ # ========================================================================= #
11
+ # Keys #
12
+ # ========================================================================= #
13
+
10
14
  test "should consider :id to be the remote key if none is specified" do
11
15
  assert_equal :id, RemoteWithoutKey.remote_key
12
16
  assert_equal :remote_id, RemoteWithoutKey.local_key
@@ -28,6 +32,10 @@ class RemotableTest < ActiveSupport::TestCase
28
32
 
29
33
 
30
34
 
35
+ # ========================================================================= #
36
+ # Temporary remote models #
37
+ # ========================================================================= #
38
+
31
39
  test "should support temporary models" do
32
40
  assert_nil BespokeTenant.find_by_slug("404")
33
41
  assert_not_nil BespokeTenant.with_remote_model(BespokeModel2.new) { BespokeTenant.find_by_slug("404") }
@@ -40,10 +48,57 @@ class RemotableTest < ActiveSupport::TestCase
40
48
  assert_nil BespokeTenant.find_by_slug("405")
41
49
  end
42
50
 
51
+ test "should support setting remote_model to nil" do
52
+ Tenant.with_remote_model(nil) do
53
+ Tenant.remote_model
54
+ assert_equal nil, Tenant.remote_model, "remote_model didn't get set to nil"
55
+ Tenant.create!(:name => "Test 1", :slug => "test-1")
56
+ assert_not_nil Tenant.find_by_name("Test 1"), "new tenant was not found"
57
+ end
58
+ end
59
+
60
+
61
+
62
+ # ========================================================================= #
63
+ # Edge cases #
64
+ # #
65
+ # Suppose some records of a table are remoted and others aren't. #
66
+ # If a record is saved which is local-only, it's remote primary key #
67
+ # isn't stored. If this record is expired, then make sure that #
68
+ # Remotable doesn't break when it tries to look up a remote record #
69
+ # with a nil key. #
70
+ # ========================================================================= #
71
+
72
+ test "should not break when refreshing a record which does not have a foreign key" do
73
+ tenant = Tenant.nosync { Tenant.create!(
74
+ :name => "Test 1",
75
+ :slug => "test-1",
76
+ :remote_id => nil,
77
+ :expires_at => 1.day.ago ) }
78
+ assert_equal nil, tenant.remote_id
79
+
80
+ # Fetching this tenant, Remotable will want to
81
+ # refresh it, but it shouldn't because it can't.
82
+ puts "->"
83
+ assert_not_nil Tenant.where(:slug => "test-1").first, "A tenant with the slug \"test-1\" was not found"
84
+ end
85
+
86
+ test "should not break when updating a record which does not have a foreign key" do
87
+ tenant = Tenant.nosync { Tenant.create!(
88
+ :name => "Test 1",
89
+ :slug => "test-1",
90
+ :remote_id => nil ) }
91
+ assert_equal nil, tenant.remote_id
92
+
93
+ # Updating this tenatn, Remotable will want to
94
+ # sync it, but it shouldn't because it can't.
95
+ assert_equal true, tenant.update_attributes(:name => "Test 2"), "The tenant was not updated (errors: #{tenant.errors.full_messages.join(", ")})"
96
+ end
97
+
43
98
 
44
99
 
45
100
  # ========================================================================= #
46
- # Finders #
101
+ # Finders #
47
102
  # ========================================================================= #
48
103
 
49
104
  test "should create expected finders" do
@@ -68,7 +123,7 @@ class RemotableTest < ActiveSupport::TestCase
68
123
 
69
124
 
70
125
  # ========================================================================= #
71
- # Validating Models #
126
+ # Validating Models #
72
127
  # ========================================================================= #
73
128
 
74
129
  test "should raise an exception if a remote model does not respond to all class methods" do
metadata CHANGED
@@ -1,160 +1,156 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: remotable
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.2
4
5
  prerelease:
5
- version: 0.2.0
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Robert Lail
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2012-02-20 00:00:00 -06:00
14
- default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
12
+ date: 2012-04-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
17
15
  name: activeresource
18
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &2156933760 !ruby/object:Gem::Requirement
19
17
  none: false
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
24
22
  type: :runtime
25
23
  prerelease: false
26
- version_requirements: *id001
27
- - !ruby/object:Gem::Dependency
24
+ version_requirements: *2156933760
25
+ - !ruby/object:Gem::Dependency
28
26
  name: activerecord
29
- requirement: &id002 !ruby/object:Gem::Requirement
27
+ requirement: &2156930420 !ruby/object:Gem::Requirement
30
28
  none: false
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: "0"
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
35
33
  type: :runtime
36
34
  prerelease: false
37
- version_requirements: *id002
38
- - !ruby/object:Gem::Dependency
35
+ version_requirements: *2156930420
36
+ - !ruby/object:Gem::Dependency
39
37
  name: activesupport
40
- requirement: &id003 !ruby/object:Gem::Requirement
38
+ requirement: &2156927360 !ruby/object:Gem::Requirement
41
39
  none: false
42
- requirements:
43
- - - ">="
44
- - !ruby/object:Gem::Version
45
- version: "0"
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
46
44
  type: :runtime
47
45
  prerelease: false
48
- version_requirements: *id003
49
- - !ruby/object:Gem::Dependency
46
+ version_requirements: *2156927360
47
+ - !ruby/object:Gem::Dependency
50
48
  name: rails
51
- requirement: &id004 !ruby/object:Gem::Requirement
49
+ requirement: &2156946400 !ruby/object:Gem::Requirement
52
50
  none: false
53
- requirements:
54
- - - ">="
55
- - !ruby/object:Gem::Version
56
- version: "0"
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
57
55
  type: :development
58
56
  prerelease: false
59
- version_requirements: *id004
60
- - !ruby/object:Gem::Dependency
57
+ version_requirements: *2156946400
58
+ - !ruby/object:Gem::Dependency
61
59
  name: minitest
62
- requirement: &id005 !ruby/object:Gem::Requirement
60
+ requirement: &2156943380 !ruby/object:Gem::Requirement
63
61
  none: false
64
- requirements:
65
- - - ">="
66
- - !ruby/object:Gem::Version
67
- version: "0"
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
68
66
  type: :development
69
67
  prerelease: false
70
- version_requirements: *id005
71
- - !ruby/object:Gem::Dependency
68
+ version_requirements: *2156943380
69
+ - !ruby/object:Gem::Dependency
72
70
  name: turn
73
- requirement: &id006 !ruby/object:Gem::Requirement
71
+ requirement: &2156962240 !ruby/object:Gem::Requirement
74
72
  none: false
75
- requirements:
76
- - - ">="
77
- - !ruby/object:Gem::Version
78
- version: "0"
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
79
77
  type: :development
80
78
  prerelease: false
81
- version_requirements: *id006
82
- - !ruby/object:Gem::Dependency
79
+ version_requirements: *2156962240
80
+ - !ruby/object:Gem::Dependency
83
81
  name: factory_girl
84
- requirement: &id007 !ruby/object:Gem::Requirement
82
+ requirement: &2156969980 !ruby/object:Gem::Requirement
85
83
  none: false
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: "0"
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
90
88
  type: :development
91
89
  prerelease: false
92
- version_requirements: *id007
93
- - !ruby/object:Gem::Dependency
90
+ version_requirements: *2156969980
91
+ - !ruby/object:Gem::Dependency
94
92
  name: sqlite3
95
- requirement: &id008 !ruby/object:Gem::Requirement
93
+ requirement: &2156968120 !ruby/object:Gem::Requirement
96
94
  none: false
97
- requirements:
98
- - - ">="
99
- - !ruby/object:Gem::Version
100
- version: "0"
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
101
99
  type: :development
102
100
  prerelease: false
103
- version_requirements: *id008
104
- - !ruby/object:Gem::Dependency
101
+ version_requirements: *2156968120
102
+ - !ruby/object:Gem::Dependency
105
103
  name: active_resource_simulator
106
- requirement: &id009 !ruby/object:Gem::Requirement
104
+ requirement: &2156965300 !ruby/object:Gem::Requirement
107
105
  none: false
108
- requirements:
109
- - - ">="
110
- - !ruby/object:Gem::Version
111
- version: "0"
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
112
110
  type: :development
113
111
  prerelease: false
114
- version_requirements: *id009
115
- - !ruby/object:Gem::Dependency
112
+ version_requirements: *2156965300
113
+ - !ruby/object:Gem::Dependency
116
114
  name: simplecov
117
- requirement: &id010 !ruby/object:Gem::Requirement
115
+ requirement: &2156982000 !ruby/object:Gem::Requirement
118
116
  none: false
119
- requirements:
120
- - - ">="
121
- - !ruby/object:Gem::Version
122
- version: "0"
117
+ requirements:
118
+ - - ! '>='
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
123
121
  type: :development
124
122
  prerelease: false
125
- version_requirements: *id010
126
- - !ruby/object:Gem::Dependency
123
+ version_requirements: *2156982000
124
+ - !ruby/object:Gem::Dependency
127
125
  name: rr
128
- requirement: &id011 !ruby/object:Gem::Requirement
126
+ requirement: &2156980560 !ruby/object:Gem::Requirement
129
127
  none: false
130
- requirements:
131
- - - ">="
132
- - !ruby/object:Gem::Version
133
- version: "0"
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
134
132
  type: :development
135
133
  prerelease: false
136
- version_requirements: *id011
137
- - !ruby/object:Gem::Dependency
134
+ version_requirements: *2156980560
135
+ - !ruby/object:Gem::Dependency
138
136
  name: database_cleaner
139
- requirement: &id012 !ruby/object:Gem::Requirement
137
+ requirement: &2156979980 !ruby/object:Gem::Requirement
140
138
  none: false
141
- requirements:
142
- - - ">="
143
- - !ruby/object:Gem::Version
144
- version: "0"
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
145
143
  type: :development
146
144
  prerelease: false
147
- version_requirements: *id012
148
- description: Remotable keeps a locally-stored ActiveRecord synchronized with a remote resource.
149
- email:
145
+ version_requirements: *2156979980
146
+ description: Remotable keeps a locally-stored ActiveRecord synchronized with a remote
147
+ resource.
148
+ email:
150
149
  - robert.lail@cph.org
151
150
  executables: []
152
-
153
151
  extensions: []
154
-
155
152
  extra_rdoc_files: []
156
-
157
- files:
153
+ files:
158
154
  - .gitignore
159
155
  - .simplecov
160
156
  - Gemfile
@@ -184,41 +180,37 @@ files:
184
180
  - test/support/schema.rb
185
181
  - test/test_helper.rb
186
182
  - test/understanding_test.rb
187
- has_rdoc: true
188
- homepage: ""
183
+ homepage: ''
189
184
  licenses: []
190
-
191
185
  post_install_message:
192
186
  rdoc_options: []
193
-
194
- require_paths:
187
+ require_paths:
195
188
  - lib
196
- required_ruby_version: !ruby/object:Gem::Requirement
189
+ required_ruby_version: !ruby/object:Gem::Requirement
197
190
  none: false
198
- requirements:
199
- - - ">="
200
- - !ruby/object:Gem::Version
201
- hash: -758603566209047915
202
- segments:
191
+ requirements:
192
+ - - ! '>='
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ segments:
203
196
  - 0
204
- version: "0"
205
- required_rubygems_version: !ruby/object:Gem::Requirement
197
+ hash: -2708411385846481518
198
+ required_rubygems_version: !ruby/object:Gem::Requirement
206
199
  none: false
207
- requirements:
208
- - - ">="
209
- - !ruby/object:Gem::Version
210
- hash: -758603566209047915
211
- segments:
200
+ requirements:
201
+ - - ! '>='
202
+ - !ruby/object:Gem::Version
203
+ version: '0'
204
+ segments:
212
205
  - 0
213
- version: "0"
206
+ hash: -2708411385846481518
214
207
  requirements: []
215
-
216
208
  rubyforge_project: remotable
217
- rubygems_version: 1.6.2
209
+ rubygems_version: 1.8.15
218
210
  signing_key:
219
211
  specification_version: 3
220
212
  summary: Binds an ActiveRecord model to a remote resource and keeps the two synchronized
221
- test_files:
213
+ test_files:
222
214
  - test/active_resource_test.rb
223
215
  - test/bespoke_test.rb
224
216
  - test/factories/tenants.rb