remotable 0.0.1 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,5 +1,4 @@
1
1
  require 'bundler/gem_tasks'
2
-
3
2
  require 'rake/testtask'
4
3
 
5
4
  Rake::TestTask.new(:test) do |t|
data/lib/remotable.rb CHANGED
@@ -1,7 +1,4 @@
1
1
  require "remotable/version"
2
- require "remotable/core_ext"
3
- require "remotable/active_resource_fixes"
4
- require "active_support/concern"
5
2
 
6
3
 
7
4
  # Remotable keeps a locally-stored ActiveRecord
@@ -31,268 +28,23 @@ require "active_support/concern"
31
28
  #
32
29
  #
33
30
  module Remotable
34
- extend ActiveSupport::Concern
35
31
 
36
32
 
37
33
 
38
- included do
39
- before_update :update_remote_resource, :unless => :nosync?
40
- before_create :create_remote_resource, :unless => :nosync?
41
- before_destroy :destroy_remote_resource, :unless => :nosync?
42
-
43
- before_validation :reset_expiration_date, :on => :create, :unless => :nosync?
44
-
45
- validates_presence_of :expires_at
46
-
47
- default_remote_attributes = column_names - ["id", "created_at", "updated_at", "expires_at"]
48
- @remote_model_name = "#{self.name}::Remote#{self.name}"
49
- @remote_attribute_map = default_remote_attributes.map_to_self
50
- @expires_after = 1.day
51
- end
52
-
53
-
54
-
55
- module ClassMethods
56
-
57
- def remote_model_name(name)
58
- @remote_model = nil
59
- @remote_model_name = name
60
- end
61
-
62
- def attr_remote(*attrs)
63
- map = attrs.extract_options!
64
- map = attrs.map_to_self(map)
65
- @remote_attribute_map = map
66
- end
67
-
68
- def fetch_with(local_key)
69
- @local_key = local_key
70
- @remote_key = remote_attribute_name(local_key)
71
-
72
- class_eval <<-RUBY
73
- def self.find_by_#{local_key}(value)
74
- local_resource = where(:#{local_key} => value).first
75
- local_resource || fetch_new_from_remote(value)
76
- end
77
-
78
- def self.find_by_#{local_key}!(value)
79
- find_by_#{local_key}(value) || raise(ActiveRecord::RecordNotFound)
80
- end
81
- RUBY
82
- end
83
-
84
- def expires_after(val)
85
- @expires_after = val
86
- end
87
-
88
-
89
-
90
- attr_reader :local_key,
91
- :remote_key,
92
- :expires_after,
93
- :remote_attribute_map
94
-
95
- def remote_model
96
- @remote_model ||= @remote_model_name.constantize
97
- end
98
-
99
- def remote_attribute_names
100
- remote_attribute_map.keys
101
- end
102
-
103
- def local_attribute_names
104
- remote_attribute_map.values
105
- end
106
-
107
- def remote_attribute_name(local_attr)
108
- remote_attribute_map.key(local_attr) || local_attr
109
- end
110
-
111
- def local_attribute_name(remote_attr)
112
- remote_attribute_map[remote_attr] || remote_attr
113
- end
114
-
115
-
116
-
117
- # !nb: this method is called when associations are loaded
118
- # so you can use the remoted record in associations.
119
- def instantiate(*args)
120
- record = super
121
- record.pull_remote_data! if record.expired?
122
- record = nil if record.destroyed?
123
- record
124
- end
125
-
126
-
127
-
128
- private
129
-
130
-
131
-
132
- def fetch_new_from_remote(value)
133
- record = self.new
134
- record.send("#{local_key}=", value) # {local_key => value} not passed to :new so local_key can be protected
135
- if record.remote_resource
136
- record.pull_remote_data!
137
- else
138
- nil
139
- end
140
- end
141
-
142
- end
143
-
144
-
145
-
146
- delegate :local_key,
147
- :remote_key,
148
- :remote_model,
149
- :remote_attribute_map,
150
- :remote_attribute_names,
151
- :remote_attribute_name,
152
- :local_attribute_names,
153
- :local_attribute_name,
154
- :expires_after,
155
- :to => "self.class"
156
-
157
- def expired?
158
- expires_at.nil? || expires_at < Time.now
159
- end
160
-
161
- def expired!
162
- update_attribute(:expires_at, 1.day.ago)
163
- end
164
-
165
-
166
-
167
- def pull_remote_data!
168
- merge_remote_data!(remote_resource)
169
- end
170
-
171
-
172
-
173
- def remote_resource
174
- @remote_resource ||= fetch_remote_resource
175
- end
176
-
177
- def any_remote_changes?
178
- (changed.map(&:to_sym) & local_attribute_names).any?
179
- end
180
-
181
-
182
-
183
- def nosync!
184
- @nosync = true
185
- end
186
-
187
- def nosync
188
- value = @nosync
189
- @nosync = true
190
- yield
191
- ensure
192
- @nosync = value
193
- end
194
-
195
- def nosync=(val)
196
- @nosync = (val == true)
197
- end
198
-
199
- def nosync?
200
- @nosync == true
201
- end
202
-
203
-
204
-
205
- private
206
-
207
- def fetch_remote_resource
208
- fetch_value = self[local_key]
209
- if remote_key == :id
210
- remote_model.find(fetch_value)
211
- else
212
- remote_model.send("find_by_#{remote_key}", fetch_value)
213
- end
214
- end
215
-
216
- def merge_remote_data!(remote_resource)
217
- if remote_resource.nil?
218
- nosync { destroy }
219
-
220
- else
221
- merge_remote_data(remote_resource)
222
- reset_expiration_date
223
- nosync { save! }
224
- end
225
-
226
- self
227
- end
228
-
229
-
230
-
231
- def update_remote_resource
232
- if any_remote_changes?
233
- merge_local_data(remote_resource, true)
234
- unless remote_resource.save
235
- merge_remote_errors(remote_resource.errors)
236
- raise ActiveRecord::RecordInvalid.new(self)
237
- end
238
- end
239
- end
240
-
241
- def create_remote_resource
242
- @remote_resource = remote_model.new
243
- merge_local_data(@remote_resource)
244
-
245
- if @remote_resource.save
34
+ def remote_model(*args)
35
+ if args.any?
36
+ @remote_model = args.first
246
37
 
247
- # This line is especially crucial if the primary key
248
- # of the remote resource needs to be stored locally.
249
- merge_remote_data(@remote_resource)
38
+ require "remotable/active_record_extender"
39
+ include Remotable::ActiveRecordExtender
250
40
  else
251
-
252
- merge_remote_errors(remote_resource.errors)
253
- raise ActiveRecord::RecordInvalid.new(self)
41
+ @remote_model
254
42
  end
255
43
  end
256
44
 
257
- def destroy_remote_resource
258
- remote_resource && remote_resource.destroy
259
- end
260
-
261
-
262
-
263
- def reset_expiration_date
264
- self.expires_at = expires_after.from_now
265
- end
266
-
267
-
268
-
269
- protected
270
-
271
- def merge_remote_errors(errors)
272
- errors.each do |attribute, message|
273
- self.errors[local_attribute_name(attribute)] = message
274
- end
275
- self
276
- end
277
-
278
- def merge_remote_data(remote_resource)
279
- remote_attribute_map.each do |remote_attr, local_attr|
280
- if remote_resource.respond_to?(remote_attr)
281
- send("#{local_attr}=", remote_resource.send(remote_attr))
282
- end
283
- end
284
- self
285
- end
286
-
287
- def merge_local_data(remote_resource, changes_only=false)
288
- remote_attribute_map.each do |remote_attr, local_attr|
289
- if !changes_only || changed.member?(local_attr.to_s)
290
- remote_resource.send("#{remote_attr}=", send(local_attr))
291
- end
292
- end
293
- self
294
- end
295
-
296
45
 
297
46
 
298
47
  end
48
+
49
+
50
+ ActiveRecord::Base.extend(Remotable) if defined?(ActiveRecord::Base)
@@ -0,0 +1,343 @@
1
+ require "remotable/core_ext"
2
+ require "active_support/concern"
3
+
4
+
5
+ module Remotable
6
+ module ActiveRecordExtender
7
+ extend ActiveSupport::Concern
8
+
9
+
10
+
11
+ included do
12
+ before_update :update_remote_resource, :unless => :nosync?
13
+ before_create :create_remote_resource, :unless => :nosync?
14
+ before_destroy :destroy_remote_resource, :unless => :nosync?
15
+
16
+ before_validation :reset_expiration_date, :on => :create, :unless => :nosync?
17
+
18
+ validates_presence_of :expires_at
19
+
20
+ default_remote_attributes = column_names - %w{id created_at updated_at expires_at}
21
+ @remote_attribute_map = default_remote_attributes.map_to_self
22
+ @remote_attribute_routes = {}
23
+ @expires_after = 1.day
24
+
25
+ extend_remote_model
26
+ end
27
+
28
+
29
+
30
+ module ClassMethods
31
+
32
+ def remote_key(*args)
33
+ if args.any?
34
+ remote_key = args.first
35
+ raise("#{remote_key} is not the name of a remote attribute") unless remote_attribute_names.member?(remote_key)
36
+ @remote_key = remote_key
37
+ fetch_with(remote_key)
38
+ remote_key
39
+ else
40
+ @remote_key || generate_default_remote_key
41
+ end
42
+ end
43
+
44
+ def expires_after(*args)
45
+ if args.any?
46
+ @expires_after = args.first
47
+ else
48
+ @expires_after
49
+ end
50
+ end
51
+
52
+ def attr_remote(*attrs)
53
+ map = attrs.extract_options!
54
+ map = attrs.map_to_self(map)
55
+ @remote_attribute_map = map
56
+ end
57
+
58
+ def fetch_with(*args)
59
+ keys_and_routes = extract_keys_and_routes(*args)
60
+ @remote_attribute_routes.merge!(keys_and_routes)
61
+ end
62
+ alias :find_by :fetch_with
63
+
64
+
65
+
66
+ attr_reader :remote_attribute_map,
67
+ :remote_attribute_routes
68
+
69
+ def local_key
70
+ local_attribute_name(remote_key)
71
+ end
72
+
73
+ def remote_attribute_names
74
+ remote_attribute_map.keys
75
+ end
76
+
77
+ def local_attribute_names
78
+ remote_attribute_map.values
79
+ end
80
+
81
+ def remote_attribute_name(local_attr)
82
+ remote_attribute_map.key(local_attr) || local_attr
83
+ end
84
+
85
+ def local_attribute_name(remote_attr)
86
+ remote_attribute_map[remote_attr] || remote_attr
87
+ end
88
+
89
+ def route_for(local_key)
90
+ remote_key = remote_attribute_name(local_key)
91
+ remote_attribute_routes[remote_key] || default_route_for(local_key, remote_key)
92
+ end
93
+
94
+ def default_route_for(local_key, remote_key=nil)
95
+ remote_key ||= remote_attribute_name(local_key)
96
+ "by_#{remote_key}/:#{local_key}.json"
97
+ end
98
+
99
+
100
+
101
+ # !nb: this method is called when associations are loaded
102
+ # so you can use the remoted record in associations.
103
+ def instantiate(*args)
104
+ record = super
105
+ record.pull_remote_data! if record.expired?
106
+ record = nil if record.destroyed?
107
+ record
108
+ end
109
+
110
+
111
+
112
+ def method_missing(method_sym, *args, &block)
113
+ method_name = method_sym.to_s
114
+
115
+ if method_name =~ /find_by_(.*)(!?)/
116
+ local_attr, bang, value = $1.to_sym, !$2.blank?, args.first
117
+ remote_attr = remote_attribute_name(local_attr)
118
+
119
+ remote_key # Make sure we've figured out the remote
120
+ # primary key if we're evaluating a finder
121
+
122
+ if remote_attribute_routes.key?(remote_attr)
123
+ local_resource = where(local_attr => value).first
124
+ unless local_resource
125
+ remote_resource = remote_model.find_by(remote_attr, value)
126
+ local_resource = new_from_remote(remote_resource) if remote_resource
127
+ end
128
+
129
+ raise ActiveRecord::RecordNotFound if local_resource.nil? && bang
130
+ return local_resource
131
+ end
132
+ end
133
+
134
+ super(method_sym, *args, &block)
135
+ end
136
+
137
+
138
+
139
+ private
140
+
141
+
142
+
143
+ def extend_remote_model
144
+ if remote_model < ActiveResource::Base
145
+ require "remotable/adapters/active_resource"
146
+ remote_model.send(:include, Remotable::Adapters::ActiveResource)
147
+ remote_model.local_model = self
148
+
149
+ # !todo
150
+ # Adapters for other API consumers can be implemented here
151
+ #
152
+
153
+ else
154
+ raise("#{remote_model} is not a recognized remote resource")
155
+ end
156
+ end
157
+
158
+
159
+ def extract_keys_and_routes(*local_keys)
160
+ keys_and_routes = local_keys.extract_options!
161
+ {}.tap do |hash|
162
+ local_keys.each {|local_key| hash[remote_attribute_name(local_key)] = nil}
163
+ keys_and_routes.each {|local_key, value| hash[remote_attribute_name(local_key)] = value}
164
+ end
165
+ end
166
+
167
+
168
+ def generate_default_remote_key
169
+ raise("No remote key supplied and :id is not a remote attribute") unless remote_attribute_names.member?(:id)
170
+ remote_key(:id)
171
+ end
172
+
173
+
174
+ def new_from_remote(remote_resource)
175
+ record = self.new
176
+ record.instance_variable_set(:@remote_resource, remote_resource)
177
+ record.pull_remote_data!
178
+ end
179
+
180
+
181
+
182
+ end
183
+
184
+
185
+
186
+ delegate :local_key,
187
+ :remote_key,
188
+ :remote_model,
189
+ :remote_attribute_map,
190
+ :remote_attribute_names,
191
+ :remote_attribute_name,
192
+ :local_attribute_names,
193
+ :local_attribute_name,
194
+ :expires_after,
195
+ :to => "self.class"
196
+
197
+ def expired?
198
+ expires_at.nil? || expires_at < Time.now
199
+ end
200
+
201
+ def expired!
202
+ update_attribute(:expires_at, 1.day.ago)
203
+ end
204
+
205
+
206
+
207
+ def pull_remote_data!
208
+ merge_remote_data!(remote_resource)
209
+ end
210
+
211
+
212
+
213
+ def remote_resource
214
+ @remote_resource ||= fetch_remote_resource
215
+ end
216
+
217
+ def any_remote_changes?
218
+ (changed.map(&:to_sym) & local_attribute_names).any?
219
+ end
220
+
221
+
222
+
223
+ def nosync!
224
+ @nosync = true
225
+ end
226
+
227
+ def nosync
228
+ value = @nosync
229
+ @nosync = true
230
+ yield
231
+ ensure
232
+ @nosync = value
233
+ end
234
+
235
+ def nosync=(val)
236
+ @nosync = (val == true)
237
+ end
238
+
239
+ def nosync?
240
+ @nosync == true
241
+ end
242
+
243
+
244
+
245
+ private
246
+
247
+ def fetch_remote_resource
248
+ fetch_value = self[local_key]
249
+ remote_model.find_by(remote_key, fetch_value)
250
+ end
251
+
252
+ def merge_remote_data!(remote_resource)
253
+ if remote_resource.nil?
254
+ nosync { destroy }
255
+
256
+ else
257
+ merge_remote_data(remote_resource)
258
+ reset_expiration_date
259
+ nosync { save! }
260
+ end
261
+
262
+ self
263
+ end
264
+
265
+
266
+
267
+ def update_remote_resource
268
+ if any_remote_changes?
269
+ merge_local_data(remote_resource, true)
270
+ unless remote_resource.save
271
+ merge_remote_errors(remote_resource.errors)
272
+ raise ActiveRecord::RecordInvalid.new(self)
273
+ end
274
+ end
275
+ end
276
+
277
+ def create_remote_resource
278
+ @remote_resource = remote_model.new
279
+ merge_local_data(@remote_resource)
280
+
281
+ if @remote_resource.save
282
+
283
+ # This line is especially crucial if the primary key
284
+ # of the remote resource needs to be stored locally.
285
+ merge_remote_data(@remote_resource)
286
+ else
287
+
288
+ merge_remote_errors(remote_resource.errors)
289
+ raise ActiveRecord::RecordInvalid.new(self)
290
+ end
291
+ end
292
+
293
+ def destroy_remote_resource
294
+ remote_resource && remote_resource.destroy
295
+ end
296
+
297
+
298
+
299
+ def reset_expiration_date
300
+ self.expires_at = expires_after.from_now
301
+ end
302
+
303
+
304
+
305
+ def local_attribute_changed?(name)
306
+ changed.member?(name.to_s)
307
+ end
308
+
309
+
310
+
311
+ protected
312
+
313
+
314
+
315
+ def merge_remote_errors(errors)
316
+ errors.each do |attribute, message|
317
+ self.errors[local_attribute_name(attribute)] = message
318
+ end
319
+ self
320
+ end
321
+
322
+ def merge_remote_data(remote_resource)
323
+ remote_attribute_map.each do |remote_attr, local_attr|
324
+ if remote_resource.respond_to?(remote_attr)
325
+ send("#{local_attr}=", remote_resource.send(remote_attr))
326
+ end
327
+ end
328
+ self
329
+ end
330
+
331
+ def merge_local_data(remote_resource, changes_only=false)
332
+ remote_attribute_map.each do |remote_attr, local_attr|
333
+ if !changes_only || local_attribute_changed?(local_attr)
334
+ remote_resource.send("#{remote_attr}=", send(local_attr))
335
+ end
336
+ end
337
+ self
338
+ end
339
+
340
+
341
+
342
+ end
343
+ end
@@ -1,4 +1,4 @@
1
- require 'active_resource'
1
+ require "active_resource"
2
2
 
3
3
 
4
4
  module ActiveResourceFixes
@@ -0,0 +1,68 @@
1
+ require "remotable/active_resource_fixes"
2
+ require "active_support/concern"
3
+
4
+
5
+ module Remotable
6
+ module Adapters
7
+ module ActiveResource
8
+ extend ActiveSupport::Concern
9
+
10
+ module ClassMethods
11
+
12
+
13
+ attr_accessor :local_model
14
+
15
+ delegate :local_attribute_name,
16
+ :route_for,
17
+ :to => :local_model
18
+
19
+
20
+
21
+ def find_by!(key, value)
22
+ if key == :id
23
+ find(value)
24
+ else
25
+ find(:one, :from => path_for(key, value))
26
+ end
27
+ end
28
+
29
+ def find_by(key, value)
30
+ find_by!(key, value)
31
+ rescue ::ActiveResource::ResourceNotFound
32
+ nil
33
+ end
34
+
35
+
36
+
37
+ def path_for(remote_key, value)
38
+ local_key = local_attribute_name(remote_key)
39
+ route = route_for(local_key)
40
+ path = route.gsub(/:#{local_key}/, value.to_s)
41
+ if relative_path?(path)
42
+ path
43
+ else
44
+ join_url_segments(prefix, collection_name, path)
45
+ end
46
+ end
47
+
48
+
49
+
50
+ private
51
+
52
+
53
+
54
+ def relative_path?(path)
55
+ path.start_with?("/") || path["://"]
56
+ end
57
+
58
+ def join_url_segments(*segments)
59
+ segments.flatten.join("/").gsub(/\/+/, "/")
60
+ end
61
+
62
+
63
+
64
+ end
65
+
66
+ end
67
+ end
68
+ end
@@ -1,3 +1,3 @@
1
1
  module Remotable
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -1,8 +1,7 @@
1
1
  Factory.define :tenant do |f|
2
2
  f.sequence(:slug) { |n| "test#{n}" }
3
3
  f.name "Test"
4
-
5
- f.active true
4
+ f.sequence(:remote_id)
6
5
  f.expires_at 100.years.from_now
7
6
  f.nosync true
8
7
  end
@@ -1,29 +1,88 @@
1
- require 'test_helper'
2
- require 'remotable'
3
- require 'support/active_resource'
4
- require 'active_resource_simulator'
1
+ require "test_helper"
2
+ require "remotable"
3
+ require "support/active_resource"
4
+ require "active_resource_simulator"
5
5
 
6
6
 
7
7
  class RemotableTest < ActiveSupport::TestCase
8
8
 
9
9
 
10
- test "should create a record locally when fetching a new remote resource" do
10
+
11
+ test "should consider :id to be the remote key if none is specified" do
12
+ assert_equal :id, RemoteWithoutKey.remote_key
13
+ assert_equal :remote_id, RemoteWithoutKey.local_key
14
+ end
15
+
16
+ test "should use a different remote_key if one is supplied" do
17
+ assert_equal :slug, RemoteWithKey.remote_key
18
+ assert_equal :slug, RemoteWithKey.local_key
19
+ end
20
+
21
+ test "should be able to generate paths for with different attributes" do
22
+ assert_equal "/api/accounts/by_slug/value.json", RemoteTenant.path_for(:slug, "value")
23
+ assert_equal "/api/accounts/by_nombre/value.json", RemoteTenant.path_for(:name, "value")
24
+ end
25
+
26
+
27
+
28
+ test "should be able to find resources by different attributes" do
11
29
  new_tenant_slug = "not_found"
12
30
 
13
31
  assert_equal 0, Tenant.where(:slug => new_tenant_slug).count,
14
- "There's not supposed to be a Tenant with the subdomain #{new_tenant_slug}."
32
+ "There's not supposed to be a Tenant with the slug #{new_tenant_slug}."
15
33
 
16
34
  assert_difference "Tenant.count", +1 do
17
- Tenant::RemoteTenant.run_simulation do |s|
18
- attrs = {
19
- :id => 1,
20
- :slug => "not_found",
35
+ RemoteTenant.run_simulation do |s|
36
+ s.show(nil, {
37
+ :id => 46,
38
+ :slug => new_tenant_slug,
21
39
  :church_name => "Not Found"
22
- }
40
+ }, :path => "/api/accounts/by_slug/#{new_tenant_slug}.json")
23
41
 
24
- s.show(nil, attrs, :path => "/api/accounts/by_slug/#{attrs[:slug]}.json")
42
+ new_tenant = Tenant.find_by_slug(new_tenant_slug)
43
+ assert_not_nil new_tenant, "A remote tenant was not found with the slug #{new_tenant_slug.inspect}"
44
+ end
45
+ end
46
+ end
47
+
48
+ test "should be able to find resources by different attributes and specify a path" do
49
+ new_tenant_name = "JohnnyG"
50
+
51
+ assert_equal 0, Tenant.where(:name => new_tenant_name).count,
52
+ "There's not supposed to be a Tenant with the name #{new_tenant_name}."
53
+
54
+ assert_difference "Tenant.count", +1 do
55
+ RemoteTenant.run_simulation do |s|
56
+ s.show(nil, {
57
+ :id => 46,
58
+ :slug => "not_found",
59
+ :church_name => new_tenant_name
60
+ }, :path => "/api/accounts/by_nombre/#{new_tenant_name}.json")
61
+
62
+ new_tenant = Tenant.find_by_name(new_tenant_name)
63
+ assert_not_nil new_tenant, "A remote tenant was not found with the name #{new_tenant_name.inspect}"
64
+ end
65
+ end
66
+ end
67
+
68
+
69
+
70
+ test "should create a record locally when fetching a new remote resource" do
71
+ new_tenant_id = 17
72
+
73
+ assert_equal 0, Tenant.where(:remote_id => new_tenant_id).count,
74
+ "There's not supposed to be a Tenant with the id #{new_tenant_id}."
75
+
76
+ assert_difference "Tenant.count", +1 do
77
+ RemoteTenant.run_simulation do |s|
78
+ s.show(new_tenant_id, {
79
+ :id => new_tenant_id,
80
+ :slug => "not_found",
81
+ :church_name => "Not Found"
82
+ })
25
83
 
26
- Tenant.find_by_slug(new_tenant_slug)
84
+ new_tenant = Tenant.find_by_remote_id(new_tenant_id)
85
+ assert_not_nil new_tenant, "A remote tenant was not found with the id #{new_tenant_id.inspect}"
27
86
  end
28
87
  end
29
88
  end
@@ -34,16 +93,14 @@ class RemotableTest < ActiveSupport::TestCase
34
93
  tenant = Factory(:tenant, :expires_at => 100.years.from_now)
35
94
  unexpected_name = "Totally Wonky"
36
95
 
37
- Tenant::RemoteTenant.run_simulation do |s|
38
- attrs = {
39
- :id => tenant.id,
96
+ RemoteTenant.run_simulation do |s|
97
+ s.show(tenant.remote_id, {
98
+ :id => tenant.remote_id,
40
99
  :slug => tenant.slug,
41
100
  :church_name => unexpected_name
42
- }
43
-
44
- s.show(nil, attrs, :path => "/api/accounts/by_slug/#{attrs[:slug]}.json")
101
+ })
45
102
 
46
- tenant = Tenant.find_by_slug(tenant.slug)
103
+ tenant = Tenant.find_by_remote_id(tenant.remote_id)
47
104
  assert_not_equal unexpected_name, tenant.name
48
105
  end
49
106
  end
@@ -54,16 +111,14 @@ class RemotableTest < ActiveSupport::TestCase
54
111
  tenant = Factory(:tenant, :expires_at => 1.year.ago)
55
112
  unexpected_name = "Totally Wonky"
56
113
 
57
- Tenant::RemoteTenant.run_simulation do |s|
58
- attrs = {
59
- :id => tenant.id,
114
+ RemoteTenant.run_simulation do |s|
115
+ s.show(tenant.remote_id, {
116
+ :id => tenant.remote_id,
60
117
  :slug => tenant.slug,
61
118
  :church_name => unexpected_name
62
- }
63
-
64
- s.show(nil, attrs, :path => "/api/accounts/by_slug/#{attrs[:slug]}.json")
119
+ })
65
120
 
66
- tenant = Tenant.find_by_slug(tenant.slug)
121
+ tenant = Tenant.find_by_remote_id(tenant.remote_id)
67
122
  assert_equal unexpected_name, tenant.name
68
123
  end
69
124
  end
@@ -74,11 +129,10 @@ class RemotableTest < ActiveSupport::TestCase
74
129
  tenant = Factory(:tenant, :expires_at => 1.year.ago)
75
130
 
76
131
  assert_difference "Tenant.count", -1 do
77
- Tenant::RemoteTenant.run_simulation do |s|
78
-
79
- s.show(nil, nil, :path => "/api/accounts/by_slug/#{tenant.slug}.json", :status => 404)
132
+ RemoteTenant.run_simulation do |s|
133
+ s.show(tenant.remote_id, nil, :status => 404)
80
134
 
81
- tenant = Tenant.find_by_slug(tenant.slug)
135
+ tenant = Tenant.find_by_remote_id(tenant.remote_id)
82
136
  assert_equal nil, tenant
83
137
  end
84
138
  end
@@ -90,14 +144,13 @@ class RemotableTest < ActiveSupport::TestCase
90
144
  tenant = Factory(:tenant)
91
145
  new_name = "Totally Wonky"
92
146
 
93
- Tenant::RemoteTenant.run_simulation do |s|
94
- s.show(nil, {
95
- :id => tenant.id,
147
+ RemoteTenant.run_simulation do |s|
148
+ s.show(tenant.remote_id, {
149
+ :id => tenant.remote_id,
96
150
  :slug => tenant.slug,
97
151
  :church_name => tenant.name
98
- }, :path => "/api/accounts/by_slug/#{tenant.slug}.json")
99
-
100
- s.update(tenant.id)
152
+ })
153
+ s.update(tenant.remote_id)
101
154
 
102
155
  tenant.nosync = false
103
156
  tenant.name = "Totally Wonky"
@@ -113,14 +166,13 @@ class RemotableTest < ActiveSupport::TestCase
113
166
  tenant = Factory(:tenant)
114
167
  new_name = "Totally Wonky"
115
168
 
116
- Tenant::RemoteTenant.run_simulation do |s|
117
- s.show(nil, {
118
- :id => tenant.id,
119
- :slug => tenant.slug,
120
- :church_name => tenant.name
121
- }, :path => "/api/accounts/by_slug/#{tenant.slug}.json")
122
-
123
- s.update(tenant.id, :status => 422, :body => {
169
+ RemoteTenant.run_simulation do |s|
170
+ s.show(tenant.remote_id, {
171
+ :id => tenant.remote_id,
172
+ :slug => tenant.slug,
173
+ :church_name => tenant.name
174
+ })
175
+ s.update(tenant.remote_id, :status => 422, :body => {
124
176
  :errors => {:church_name => ["is already taken"]}
125
177
  })
126
178
 
@@ -141,7 +193,7 @@ class RemotableTest < ActiveSupport::TestCase
141
193
  :name => "Brand New"
142
194
  })
143
195
 
144
- Tenant::RemoteTenant.run_simulation do |s|
196
+ RemoteTenant.run_simulation do |s|
145
197
  s.create({
146
198
  :id => 143,
147
199
  :slug => tenant.slug,
@@ -160,7 +212,7 @@ class RemotableTest < ActiveSupport::TestCase
160
212
  :name => "Brand New"
161
213
  })
162
214
 
163
- Tenant::RemoteTenant.run_simulation do |s|
215
+ RemoteTenant.run_simulation do |s|
164
216
  s.create({
165
217
  :errors => {
166
218
  :what => ["ever"],
@@ -180,14 +232,13 @@ class RemotableTest < ActiveSupport::TestCase
180
232
  test "should destroy a record remotely when destroying one locally" do
181
233
  tenant = Factory(:tenant)
182
234
 
183
- Tenant::RemoteTenant.run_simulation do |s|
184
- s.show(nil, {
185
- :id => tenant.id,
186
- :slug => tenant.slug,
187
- :church_name => tenant.name
188
- }, :path => "/api/accounts/by_slug/#{tenant.slug}.json")
189
-
190
- s.destroy(tenant.id)
235
+ RemoteTenant.run_simulation do |s|
236
+ s.show(tenant.remote_id, {
237
+ :id => tenant.remote_id,
238
+ :slug => tenant.slug,
239
+ :church_name => tenant.name
240
+ })
241
+ s.destroy(tenant.remote_id)
191
242
 
192
243
  tenant.nosync = false
193
244
  tenant.destroy
@@ -199,14 +250,14 @@ class RemotableTest < ActiveSupport::TestCase
199
250
  test "should fail to destroy a record locally when failing to destroy one remotely" do
200
251
  tenant = Factory(:tenant)
201
252
 
202
- Tenant::RemoteTenant.run_simulation do |s|
203
- s.show(nil, {
204
- :id => tenant.id,
205
- :slug => tenant.slug,
206
- :church_name => tenant.name
207
- }, :path => "/api/accounts/by_slug/#{tenant.slug}.json")
253
+ RemoteTenant.run_simulation do |s|
254
+ s.show(tenant.remote_id, {
255
+ :id => tenant.remote_id,
256
+ :slug => tenant.slug,
257
+ :church_name => tenant.name
258
+ })
208
259
 
209
- s.destroy(tenant.id, :status => 500)
260
+ s.destroy(tenant.remote_id, :status => 500)
210
261
 
211
262
  tenant.nosync = false
212
263
  assert_raises(ActiveResource::ServerError) do
@@ -1,32 +1,43 @@
1
- require 'active_record'
2
- require 'active_resource'
1
+ require "active_record"
2
+ require "active_resource"
3
+
4
+
5
+ class RemoteTenant < ActiveResource::Base
6
+ self.site = "http://example.com/api/"
7
+ self.element_name = "account"
8
+ self.format = :json
9
+ self.include_root_in_json = false
10
+ self.user = "username"
11
+ self.password = "password"
12
+ end
3
13
 
4
14
  class Tenant < ActiveRecord::Base
5
- include Remotable
15
+ remote_model RemoteTenant
16
+ attr_remote :slug, :church_name => :name, :id => :remote_id
17
+ find_by :slug
18
+ find_by :name => "by_nombre/:name.json"
19
+ end
20
+
21
+
22
+
23
+ class RemoteTenant2 < ActiveResource::Base
24
+ end
25
+
26
+ class RemoteWithoutKey < ActiveRecord::Base
27
+ set_table_name "tenants"
6
28
 
7
- attr_remote :slug, :church_name => :name, :active_unite_account => :active
8
- fetch_with :slug
29
+ remote_model RemoteTenant2
30
+ attr_remote :id => :remote_id
31
+ end
32
+
33
+
34
+ class RemoteTenant3 < ActiveResource::Base
35
+ end
36
+
37
+ class RemoteWithKey < ActiveRecord::Base
38
+ set_table_name "tenants"
9
39
 
10
- class RemoteTenant < ActiveResource::Base
11
-
12
- self.site = "http://example.com/api/"
13
- self.element_name = "account"
14
- self.format = :json
15
- self.include_root_in_json = false
16
- self.user = "username"
17
- self.password = "password"
18
-
19
- class << self
20
- def find_by_slug!(slug)
21
- find(:one, :from => "/api/accounts/by_slug/#{slug}.json")
22
- end
23
-
24
- def find_by_slug(slug)
25
- find_by_slug!(slug)
26
- rescue ActiveResource::ResourceNotFound
27
- nil
28
- end
29
- end
30
-
31
- end
40
+ remote_model RemoteTenant3
41
+ attr_remote :slug
42
+ remote_key :slug
32
43
  end
@@ -13,12 +13,12 @@
13
13
  ActiveRecord::Schema.define(:version => 20110507152635) do
14
14
 
15
15
  create_table "tenants", :force => true do |t|
16
- t.string "slug", :null => false
17
- t.string "name", :null => false
18
- t.boolean "active"
16
+ t.string "slug"
17
+ t.string "name"
18
+ t.integer "remote_id"
19
19
  t.datetime "created_at"
20
20
  t.datetime "updated_at"
21
- t.datetime "expires_at"
21
+ t.datetime "expires_at", :null => false
22
22
  end
23
23
 
24
24
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: remotable
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.1
5
+ version: 0.1.1
6
6
  platform: ruby
7
7
  authors:
8
8
  - Robert Lail
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-08-18 00:00:00 -05:00
13
+ date: 2011-08-26 00:00:00 -05:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -115,7 +115,9 @@ files:
115
115
  - Gemfile
116
116
  - Rakefile
117
117
  - lib/remotable.rb
118
+ - lib/remotable/active_record_extender.rb
118
119
  - lib/remotable/active_resource_fixes.rb
120
+ - lib/remotable/adapters/active_resource.rb
119
121
  - lib/remotable/core_ext.rb
120
122
  - lib/remotable/core_ext/enumerable.rb
121
123
  - lib/remotable/version.rb