remotable 0.2.0 → 0.2.2

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.
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