remotable 0.1.2 → 0.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.
@@ -2,6 +2,21 @@ require "active_resource"
2
2
 
3
3
 
4
4
  module ActiveResourceFixes
5
+
6
+ # ActiveResource hacks method_missing without hacking respond_to?
7
+ # In fact, it responds to any method that ends in an equals sign.
8
+ # It also responds to any method that matches an attribute name.
9
+ def respond_to?(method_symbol, include_private=false)
10
+ method_name = method_symbol.to_s
11
+ if method_name =~ /\w+=/
12
+ true
13
+ elsif attributes.include?(method_name)
14
+ true
15
+ else
16
+ super(method_symbol, include_private)
17
+ end
18
+ end
19
+
5
20
  end
6
21
 
7
22
 
@@ -11,30 +11,23 @@ module Remotable
11
11
  module ClassMethods
12
12
 
13
13
 
14
- attr_accessor :local_model
15
-
16
- delegate :local_attribute_name,
17
- :route_for,
18
- :to => :local_model
19
-
14
+ def new_resource
15
+ new
16
+ end
20
17
 
21
18
 
22
- def find_by!(key, value)
23
- find(:one, :from => path_for(key, value))
19
+ def find_by!(path)
20
+ find(:one, :from => expanded_path_for(path))
24
21
  end
25
22
 
26
- def find_by(key, value)
27
- find_by!(key, value)
23
+ def find_by(path)
24
+ find_by!(path)
28
25
  rescue ::ActiveResource::ResourceNotFound
29
26
  nil
30
27
  end
31
28
 
32
29
 
33
-
34
- def path_for(remote_key, value)
35
- local_key = local_attribute_name(remote_key)
36
- route = route_for(local_key)
37
- path = route.gsub(/:#{local_key}/, value.to_s)
30
+ def expanded_path_for(path)
38
31
  if relative_path?(path)
39
32
  URI.join_url_segments(prefix, collection_name, "#{path}.#{format.extension}")
40
33
  else
@@ -43,19 +36,15 @@ module Remotable
43
36
  end
44
37
 
45
38
 
46
-
47
39
  private
48
40
 
49
41
 
50
-
51
42
  def relative_path?(path)
52
43
  !(path.start_with?("/") || path["://"])
53
44
  end
54
45
 
55
46
 
56
-
57
47
  end
58
-
59
48
  end
60
49
  end
61
50
  end
@@ -1 +1,2 @@
1
- require "remotable/core_ext/enumerable"
1
+ require "remotable/core_ext/enumerable"
2
+ require "remotable/core_ext/object"
@@ -0,0 +1,27 @@
1
+ module Remotable
2
+ module CoreExt
3
+ module Object
4
+
5
+
6
+ def respond_to_all?(*methods)
7
+ respond_to = method(:respond_to?)
8
+ methods.flatten.all?(&respond_to)
9
+ end
10
+
11
+ def responds_to(*methods)
12
+ respond_to = method(:respond_to?)
13
+ methods.flatten.select(&respond_to)
14
+ end
15
+
16
+ def does_not_respond_to(*methods)
17
+ methods.flatten - self.responds_to(methods)
18
+ end
19
+
20
+
21
+ end
22
+ end
23
+ end
24
+
25
+
26
+ ::Object.extend(Remotable::CoreExt::Object)
27
+ ::Object.send(:include, Remotable::CoreExt::Object)
@@ -1,4 +1,3 @@
1
-
2
1
  module Remotable
3
2
  module Nosync
4
3
 
@@ -0,0 +1,23 @@
1
+ module Remotable
2
+ module ValidateModels
3
+
4
+
5
+ def validate_models=(val)
6
+ @validate_models = (val == true)
7
+ end
8
+
9
+ def validate_models?
10
+ @validate_models == true
11
+ end
12
+
13
+ def without_validation
14
+ value = self.validate_models?
15
+ self.validate_models = false
16
+ yield
17
+ ensure
18
+ self.validate_models = value
19
+ end
20
+
21
+
22
+ end
23
+ end
@@ -1,3 +1,3 @@
1
1
  module Remotable
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -0,0 +1,18 @@
1
+ module Remotable
2
+ class WithRemoteModelProxy
3
+
4
+ def initialize(model, remote_model)
5
+ @model = model
6
+ @remote_model = remote_model
7
+ end
8
+
9
+ delegate :respond_to?, :to => :@model
10
+
11
+ def method_missing(sym, *args, &block)
12
+ @model.with_remote_model(@remote_model) do
13
+ @model.send(sym, *args, &block)
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -18,11 +18,14 @@ Gem::Specification.new do |s|
18
18
  s.add_dependency "activesupport"
19
19
 
20
20
  s.add_development_dependency "rails"
21
+ s.add_development_dependency "minitest"
21
22
  s.add_development_dependency "turn"
22
23
  s.add_development_dependency "factory_girl"
23
24
  s.add_development_dependency "sqlite3"
24
25
  s.add_development_dependency "active_resource_simulator"
25
26
  s.add_development_dependency "simplecov"
27
+ s.add_development_dependency "rr"
28
+ s.add_development_dependency "database_cleaner"
26
29
 
27
30
  s.files = `git ls-files`.split("\n")
28
31
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -0,0 +1,332 @@
1
+ require "test_helper"
2
+ require "remotable"
3
+ require "support/active_resource"
4
+ require "active_resource_simulator"
5
+ require "rr"
6
+
7
+
8
+ class ActiveResourceTest < ActiveSupport::TestCase
9
+ include RR::Adapters::TestUnit
10
+
11
+ test "should make an absolute path and add the format" do
12
+ assert_equal "/api/accounts/by_slug/value.json", RemoteTenant.expanded_path_for("by_slug/value")
13
+ end
14
+
15
+
16
+
17
+ # ========================================================================= #
18
+ # Finding #
19
+ # ========================================================================= #
20
+
21
+ test "should be able to find resources by different attributes" do
22
+ new_tenant_slug = "not_found"
23
+
24
+ assert_equal 0, Tenant.where(:slug => new_tenant_slug).count,
25
+ "There's not supposed to be a Tenant with the slug #{new_tenant_slug}."
26
+
27
+ assert_difference "Tenant.count", +1 do
28
+ RemoteTenant.run_simulation do |s|
29
+ s.show(nil, {
30
+ :id => 46,
31
+ :slug => new_tenant_slug,
32
+ :church_name => "Not Found"
33
+ }, :path => "/api/accounts/by_slug/#{new_tenant_slug}.json")
34
+
35
+ new_tenant = Tenant.find_by_slug(new_tenant_slug)
36
+ assert_not_nil new_tenant, "A remote tenant was not found with the slug #{new_tenant_slug.inspect}"
37
+ end
38
+ end
39
+ end
40
+
41
+ test "should be able to find resources with a composite key" do
42
+ group_id = 5
43
+ slug = "not_found"
44
+
45
+ assert_equal 0, RemoteWithCompositeKey.where(:group_id => group_id, :slug => slug).count,
46
+ "There's not supposed to be a Tenant with the group_id #{group_id} and the slug #{slug}."
47
+
48
+ assert_difference "RemoteWithCompositeKey.count", +1 do
49
+ RemoteTenant.run_simulation do |s|
50
+ s.show(nil, {
51
+ :id => 46,
52
+ :group_id => group_id,
53
+ :slug => slug,
54
+ :church_name => "Not Found"
55
+ }, :path => "/api/accounts/groups/#{group_id}/tenants/#{slug}.json")
56
+
57
+ new_tenant = RemoteWithCompositeKey.find_by_group_id_and_slug(group_id, slug)
58
+ assert_not_nil new_tenant, "A remote tenant was not found with the group_id #{group_id} and the slug #{slug}."
59
+ end
60
+ end
61
+ end
62
+
63
+ test "should be able to find resources with the bang method" do
64
+ new_tenant_slug = "not_found2"
65
+
66
+ assert_equal 0, Tenant.where(:slug => new_tenant_slug).count,
67
+ "There's not supposed to be a Tenant with the slug #{new_tenant_slug}."
68
+
69
+ assert_difference "Tenant.count", +1 do
70
+ RemoteTenant.run_simulation do |s|
71
+ s.show(nil, {
72
+ :id => 46,
73
+ :slug => new_tenant_slug,
74
+ :church_name => "Not Found"
75
+ }, :path => "/api/accounts/by_slug/#{new_tenant_slug}.json")
76
+
77
+ new_tenant = Tenant.find_by_slug!(new_tenant_slug)
78
+ assert_not_nil new_tenant, "A remote tenant was not found with the slug #{new_tenant_slug.inspect}"
79
+ end
80
+ end
81
+ end
82
+
83
+ test "if a resource is neither local nor remote, raise an exception with the bang method" do
84
+ new_tenant_slug = "not_found3"
85
+
86
+ assert_equal 0, Tenant.where(:slug => new_tenant_slug).count,
87
+ "There's not supposed to be a Tenant with the slug #{new_tenant_slug}."
88
+
89
+ RemoteTenant.run_simulation do |s|
90
+ s.show(nil, nil, :status => 404, :path => "/api/accounts/by_slug/#{new_tenant_slug}.json")
91
+
92
+ assert_raises ActiveRecord::RecordNotFound do
93
+ Tenant.find_by_slug!(new_tenant_slug)
94
+ end
95
+ end
96
+ end
97
+
98
+ test "should be able to find resources by different attributes and specify a path" do
99
+ new_tenant_name = "JohnnyG"
100
+
101
+ assert_equal 0, Tenant.where(:name => new_tenant_name).count,
102
+ "There's not supposed to be a Tenant with the name #{new_tenant_name}."
103
+
104
+ assert_difference "Tenant.count", +1 do
105
+ RemoteTenant.run_simulation do |s|
106
+ s.show(nil, {
107
+ :id => 46,
108
+ :slug => "not_found",
109
+ :church_name => new_tenant_name
110
+ }, :path => "/api/accounts/by_nombre/#{new_tenant_name}.json")
111
+
112
+ new_tenant = Tenant.find_by_name(new_tenant_name)
113
+ assert_not_nil new_tenant, "A remote tenant was not found with the name #{new_tenant_name.inspect}"
114
+ end
115
+ end
116
+ end
117
+
118
+
119
+
120
+ # ========================================================================= #
121
+ # Expiration #
122
+ # ========================================================================= #
123
+
124
+ test "should not fetch a remote record when a local record is not expired" do
125
+ tenant = Factory(:tenant, :expires_at => 100.years.from_now)
126
+ unexpected_name = "Totally Wonky"
127
+
128
+ RemoteTenant.run_simulation do |s|
129
+ s.show(tenant.remote_id, {
130
+ :id => tenant.remote_id,
131
+ :slug => tenant.slug,
132
+ :church_name => unexpected_name
133
+ })
134
+
135
+ tenant = Tenant.find_by_remote_id(tenant.remote_id)
136
+ assert_not_equal unexpected_name, tenant.name
137
+ end
138
+ end
139
+
140
+ test "should fetch a remote record when a local record is expired" do
141
+ tenant = Factory(:tenant, :expires_at => 1.year.ago)
142
+ unexpected_name = "Totally Wonky"
143
+
144
+ RemoteTenant.run_simulation do |s|
145
+ s.show(tenant.remote_id, {
146
+ :id => tenant.remote_id,
147
+ :slug => tenant.slug,
148
+ :church_name => unexpected_name
149
+ })
150
+
151
+ tenant = Tenant.find_by_remote_id(tenant.remote_id)
152
+ assert_equal unexpected_name, tenant.name
153
+ end
154
+ end
155
+
156
+
157
+
158
+ # ========================================================================= #
159
+ # Updating #
160
+ # ========================================================================= #
161
+
162
+ test "should update a record remotely when updating one locally" do
163
+ tenant = Factory(:tenant)
164
+ new_name = "Totally Wonky"
165
+
166
+ RemoteTenant.run_simulation do |s|
167
+ s.show(tenant.remote_id, {
168
+ :id => tenant.remote_id,
169
+ :slug => tenant.slug,
170
+ :church_name => tenant.name
171
+ })
172
+
173
+ tenant.nosync = false
174
+ tenant.name = "Totally Wonky"
175
+ assert_equal true, tenant.any_remote_changes?
176
+
177
+ # Throws an error if save is not called on the remote resource
178
+ mock(tenant.remote_resource).save { true }
179
+
180
+ tenant.save!
181
+ end
182
+ end
183
+
184
+ test "should fail to update a record locally when failing to update one remotely" do
185
+ tenant = Factory(:tenant)
186
+ new_name = "Totally Wonky"
187
+
188
+ RemoteTenant.run_simulation do |s|
189
+ s.show(tenant.remote_id, {
190
+ :id => tenant.remote_id,
191
+ :slug => tenant.slug,
192
+ :church_name => tenant.name
193
+ })
194
+ s.update(tenant.remote_id, :status => 422, :body => {
195
+ :errors => {:church_name => ["is already taken"]}
196
+ })
197
+
198
+ tenant.nosync = false
199
+ tenant.name = new_name
200
+ assert_raises(ActiveRecord::RecordInvalid) do
201
+ tenant.save!
202
+ end
203
+ assert_equal ["is already taken"], tenant.errors[:name]
204
+ end
205
+ end
206
+
207
+
208
+
209
+
210
+ # ========================================================================= #
211
+ # Creating #
212
+ # ========================================================================= #
213
+
214
+ test "should create a record remotely when creating one locally" do
215
+ tenant = Tenant.new({
216
+ :slug => "brand_new",
217
+ :name => "Brand New"
218
+ })
219
+
220
+ RemoteTenant.run_simulation do |s|
221
+ s.create({
222
+ :id => 143,
223
+ :slug => tenant.slug,
224
+ :church_name => tenant.name
225
+ })
226
+
227
+ tenant.save!
228
+
229
+ assert_equal true, tenant.remote_resource.persisted?
230
+ end
231
+ end
232
+
233
+ test "should fail to create a record locally when failing to create one remotely" do
234
+ tenant = Tenant.new({
235
+ :slug => "brand_new",
236
+ :name => "Brand New"
237
+ })
238
+
239
+ RemoteTenant.run_simulation do |s|
240
+ s.create({
241
+ :errors => {
242
+ :what => ["ever"],
243
+ :church_name => ["is already taken"]}
244
+ }, :status => 422)
245
+
246
+ assert_raises(ActiveRecord::RecordInvalid) do
247
+ tenant.save!
248
+ end
249
+
250
+ assert_equal ["is already taken"], tenant.errors[:name]
251
+ end
252
+ end
253
+
254
+ test "should create a record locally when fetching a new remote resource" do
255
+ new_tenant_id = 17
256
+
257
+ assert_equal 0, Tenant.where(:remote_id => new_tenant_id).count,
258
+ "There's not supposed to be a Tenant with the id #{new_tenant_id}."
259
+
260
+ assert_difference "Tenant.count", +1 do
261
+ RemoteTenant.run_simulation do |s|
262
+ s.show(new_tenant_id, {
263
+ :id => new_tenant_id,
264
+ :slug => "not_found",
265
+ :church_name => "Not Found"
266
+ })
267
+
268
+ new_tenant = Tenant.find_by_remote_id(new_tenant_id)
269
+ assert_not_nil new_tenant, "A remote tenant was not found with the id #{new_tenant_id.inspect}"
270
+ end
271
+ end
272
+ end
273
+
274
+
275
+
276
+ # ========================================================================= #
277
+ # Destroying #
278
+ # ========================================================================= #
279
+
280
+ test "should destroy a record remotely when destroying one locally" do
281
+ tenant = Factory(:tenant)
282
+
283
+ RemoteTenant.run_simulation do |s|
284
+ s.show(tenant.remote_id, {
285
+ :id => tenant.remote_id,
286
+ :slug => tenant.slug,
287
+ :church_name => tenant.name
288
+ })
289
+
290
+ # Throws an error if save is not called on the remote resource
291
+ mock(tenant.remote_resource).destroy { true }
292
+
293
+ tenant.nosync = false
294
+ tenant.destroy
295
+ end
296
+ end
297
+
298
+ test "should fail to destroy a record locally when failing to destroy one remotely" do
299
+ tenant = Factory(:tenant)
300
+
301
+ RemoteTenant.run_simulation do |s|
302
+ s.show(tenant.remote_id, {
303
+ :id => tenant.remote_id,
304
+ :slug => tenant.slug,
305
+ :church_name => tenant.name
306
+ })
307
+
308
+ s.destroy(tenant.remote_id, :status => 500)
309
+
310
+ tenant.nosync = false
311
+ assert_raises(ActiveResource::ServerError) do
312
+ tenant.destroy
313
+ end
314
+ end
315
+ end
316
+
317
+ test "should delete a local record when a remote record has been deleted" do
318
+ tenant = Factory(:tenant, :expires_at => 1.year.ago)
319
+
320
+ assert_difference "Tenant.count", -1 do
321
+ RemoteTenant.run_simulation do |s|
322
+ s.show(tenant.remote_id, nil, :status => 404)
323
+
324
+ tenant = Tenant.find_by_remote_id(tenant.remote_id)
325
+ assert_equal nil, tenant
326
+ end
327
+ end
328
+ end
329
+
330
+
331
+
332
+ end