resourcy-rails 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/LICENSE +25 -0
  2. data/lib/resourcy/engine.rb +4 -0
  3. data/lib/resourcy/rails.rb +4 -0
  4. data/lib/resourcy/version.rb +3 -0
  5. data/lib/resourcy-rails.rb +1 -0
  6. data/spec/dummy/Rakefile +7 -0
  7. data/spec/dummy/app/assets/javascripts/jquery-1.7.1.js +9266 -0
  8. data/spec/dummy/app/assets/javascripts/jquery-1.8.0.js +9227 -0
  9. data/spec/dummy/app/controllers/posts_controller.rb +85 -0
  10. data/spec/dummy/app/models/post.rb +3 -0
  11. data/spec/dummy/app/views/posts/_form.html.erb +25 -0
  12. data/spec/dummy/app/views/posts/edit.html.erb +6 -0
  13. data/spec/dummy/app/views/posts/index.html.erb +25 -0
  14. data/spec/dummy/app/views/posts/new.html.erb +5 -0
  15. data/spec/dummy/app/views/posts/show.html.erb +15 -0
  16. data/spec/dummy/config/application.rb +63 -0
  17. data/spec/dummy/config/boot.rb +9 -0
  18. data/spec/dummy/config/database.yml +10 -0
  19. data/spec/dummy/config/environment.rb +5 -0
  20. data/spec/dummy/config/environments/development.rb +37 -0
  21. data/spec/dummy/config/environments/test.rb +37 -0
  22. data/spec/dummy/config/evergreen.rb +47 -0
  23. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  24. data/spec/dummy/config/initializers/inflections.rb +15 -0
  25. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  26. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  27. data/spec/dummy/config/initializers/session_store.rb +8 -0
  28. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  29. data/spec/dummy/config/locales/en.yml +5 -0
  30. data/spec/dummy/config/routes.rb +64 -0
  31. data/spec/dummy/config.ru +4 -0
  32. data/spec/dummy/db/development.sqlite3 +0 -0
  33. data/spec/dummy/db/migrate/20120825050219_create_posts.rb +10 -0
  34. data/spec/dummy/db/structure.sql +4 -0
  35. data/spec/dummy/log/.gitkeep +0 -0
  36. data/spec/dummy/public/index.html +12 -0
  37. data/spec/dummy/script/rails +6 -0
  38. data/spec/javascripts/jquery.resourcy_spec.js.coffee +399 -0
  39. data/spec/javascripts/resourcy_spec.js.coffee +177 -0
  40. data/spec/javascripts/spec_helper.js +37 -0
  41. data/spec/javascripts/templates/ujs.html +3 -0
  42. data/vendor/assets/javascripts/jquery.resourcy.js.coffee +37 -0
  43. data/vendor/assets/javascripts/resourcy.js.coffee +104 -0
  44. metadata +229 -0
@@ -0,0 +1,399 @@
1
+ require '/assets/jquery-1.8.0.js'
2
+ require '/assets/jquery.resourcy.js'
3
+ require '/assets/jquery_ujs.js'
4
+
5
+ describe "Resourcy jQuery Adapter (1.8.0)", ->
6
+
7
+ beforeEach ->
8
+ R.removeAll()
9
+
10
+ describe "plugin signature", ->
11
+
12
+ it "has all the methods it should have", ->
13
+ expect(typeof($.resources)).toBe('function')
14
+ expect(typeof($.resource)).toBe('function')
15
+ expect(typeof($.routes)).toBe('function')
16
+ expect($.handleRequest).toBeUndefined()
17
+
18
+ it "removes Resourcy", ->
19
+ expect(window.Resourcy).toBeUndefined()
20
+
21
+
22
+ describe "plural resources", ->
23
+
24
+ beforeEach ->
25
+ spyOn(window, 'XMLHttpRequest') # keep these requests from being fully requested
26
+ @resource = $.resources('blogs/:blog_id/posts').add(RESOURCES.POSTS)
27
+ @url = '/blogs/1/posts'
28
+
29
+ it "handles the index action", ->
30
+ spy = spyOn(@resource.actions, 'index')
31
+ $.ajax(@url, {type: 'get'})
32
+
33
+ expect(spy.callCount).toBe(1)
34
+
35
+ it "handles the show action", ->
36
+ spy = spyOn(@resource.actions, 'show')
37
+ $.ajax("#{@url}/1", {type: 'get'})
38
+
39
+ expect(spy.callCount).toBe(1)
40
+
41
+ it "handles the new action", ->
42
+ spy = spyOn(@resource.actions, 'new')
43
+ $.ajax("#{@url}/new", {type: 'get'})
44
+
45
+ expect(spy.callCount).toBe(1)
46
+
47
+ it "handles the create action", ->
48
+ spy = spyOn(@resource.actions, 'create')
49
+ $.ajax(@url, {type: 'post'})
50
+
51
+ expect(spy.callCount).toBe(1)
52
+
53
+ it "handles the edit action", ->
54
+ spy = spyOn(@resource.actions, 'edit')
55
+ $.ajax("#{@url}/1/edit", {type: 'get'})
56
+
57
+ expect(spy.callCount).toBe(1)
58
+
59
+ it "handles the update action", ->
60
+ spy = spyOn(@resource.actions, 'update')
61
+ $.ajax("#{@url}/1", {type: 'put'})
62
+
63
+ expect(spy.callCount).toBe(1)
64
+
65
+ it "handles the destroy action", ->
66
+ spy = spyOn(@resource.actions, 'destroy')
67
+ $.ajax("#{@url}/1", {type: 'delete'})
68
+
69
+ expect(spy.callCount).toBe(1)
70
+
71
+ it "handles get members", ->
72
+ spy = spyOn(@resource.actions.get, 'comments')
73
+ $.ajax("#{@url}/1/comments", {type: 'get'})
74
+
75
+ expect(spy.callCount).toBe(1)
76
+
77
+ it "handles put members", ->
78
+ spy = spyOn(@resource.actions.put, 'publish')
79
+ $.ajax("#{@url}/1/publish", {type: 'put'})
80
+
81
+ expect(spy.callCount).toBe(1)
82
+
83
+ it "handles post members", ->
84
+ spy = spyOn(@resource.actions.post, 'reorder')
85
+ $.ajax("#{@url}/1/reorder", {type: 'post'})
86
+
87
+ expect(spy.callCount).toBe(1)
88
+
89
+ it "handles delete members", ->
90
+ spy = spyOn(@resource.actions.delete, 'comments')
91
+ $.ajax("#{@url}/1/comments", {type: 'delete'})
92
+
93
+ expect(spy.callCount).toBe(1)
94
+
95
+ describe "ajax arguments", ->
96
+
97
+ it "accepts the url in the options object", ->
98
+ spy = spyOn(@resource.actions, 'index')
99
+ $.ajax({url: @url, type: 'get'})
100
+
101
+ expect(spy.callCount).toBe(1)
102
+
103
+ it "falls back to _method if it's provided in the data", ->
104
+ spy = spyOn(@resource.actions, 'create')
105
+ data = [{name: '_method', value: 'post'}]
106
+ $.ajax({url: @url, data: data})
107
+
108
+ expect(spy.callCount).toBe(1)
109
+
110
+
111
+ describe "singular resources", ->
112
+
113
+ beforeEach ->
114
+ spyOn(window, 'XMLHttpRequest') # keep these requests from being fully requested
115
+ @resource = $.resource('blogs/:blog_id/owner').add(RESOURCES.OWNER)
116
+ @url = '/blogs/1/owner'
117
+
118
+ it "handles the show action", ->
119
+ spy = spyOn(@resource.actions, 'show')
120
+ $.ajax(@url, {type: 'get'})
121
+
122
+ expect(spy.callCount).toBe(1)
123
+
124
+ it "handles the new action", ->
125
+ spy = spyOn(@resource.actions, 'new')
126
+ $.ajax("#{@url}/new", {type: 'get'})
127
+
128
+ expect(spy.callCount).toBe(1)
129
+
130
+ it "handles the create action", ->
131
+ spy = spyOn(@resource.actions, 'create')
132
+ $.ajax(@url, {type: 'post'})
133
+
134
+ expect(spy.callCount).toBe(1)
135
+
136
+ it "handles the edit action", ->
137
+ spy = spyOn(@resource.actions, 'edit')
138
+ $.ajax(@url + '/edit', {type: 'get'})
139
+
140
+ expect(spy.callCount).toBe(1)
141
+
142
+ it "handles the update action", ->
143
+ spy = spyOn(@resource.actions, 'update')
144
+ $.ajax(@url, {type: 'put'})
145
+
146
+ expect(spy.callCount).toBe(1)
147
+
148
+ it "handles the destroy action", ->
149
+ spy = spyOn(@resource.actions, 'destroy')
150
+ $.ajax(@url, {type: 'delete'})
151
+
152
+ expect(spy.callCount).toBe(1)
153
+
154
+ it "handles get members", ->
155
+ @resource.add('get:blogs', ->)
156
+ spy = spyOn(@resource.actions.get, 'blogs')
157
+ $.ajax("#{@url}/blogs", {type: 'get'})
158
+
159
+ expect(spy.callCount).toBe(1)
160
+
161
+ it "handles put members", ->
162
+ @resource.add('put:demote', ->)
163
+ spy = spyOn(@resource.actions.put, 'demote')
164
+ $.ajax("#{@url}/demote", {type: 'put'})
165
+
166
+ expect(spy.callCount).toBe(1)
167
+
168
+ it "handles post members", ->
169
+ @resource.add('post:blogs', ->)
170
+ spy = spyOn(@resource.actions.post, 'blogs')
171
+ $.ajax("#{@url}/blogs", {type: 'post'})
172
+
173
+ expect(spy.callCount).toBe(1)
174
+
175
+ it "handles delete members", ->
176
+ @resource.add('delete:blog', ->)
177
+ spy = spyOn(@resource.actions.delete, 'blog')
178
+ $.ajax("#{@url}/blog", {type: 'delete'})
179
+
180
+ expect(spy.callCount).toBe(1)
181
+
182
+
183
+ describe "callbacks", ->
184
+
185
+ beforeEach ->
186
+ @resource = $.resources('blogs/:blog_id/posts')
187
+ @url = '/blogs/1/posts'
188
+ @callback = -> 'callback'
189
+
190
+ describe "passed arguments", ->
191
+
192
+ beforeEach ->
193
+ spyOn(window, 'XMLHttpRequest') # keep these requests from being fully requested
194
+ @resource.add('index', => {success: @callback})
195
+ @actionSpy = spyOn(@resource.actions, 'index').andCallThrough()
196
+
197
+ it "passes the proceed function", ->
198
+ $.ajax('/blogs/1/posts', {type: 'get'})
199
+
200
+ expect(typeof(@actionSpy.argsForCall[0][0])).toEqual('function')
201
+
202
+ it "passes parsed path variables with their values", ->
203
+ $.ajax('/blogs/42/posts', {type: 'get'})
204
+
205
+ expect(@actionSpy.argsForCall[0][1]).toEqual({blog_id: '42'})
206
+
207
+ it "passes the requests url parts", ->
208
+ $.ajax('http://jejacks0n:password@localhost:3000/blogs/42/posts.json?foo=bar#hash', {type: 'get'})
209
+
210
+ expect(@actionSpy.argsForCall[0][2]).toEqual
211
+ scheme: 'http'
212
+ credentials: 'jejacks0n:password'
213
+ host: 'localhost'
214
+ port: '3000'
215
+ path: '/blogs/42/posts'
216
+ action: 'posts'
217
+ format: 'json'
218
+ query: 'foo=bar'
219
+ hash: 'hash'
220
+
221
+
222
+ describe "returning values", ->
223
+
224
+ it "allows returning options that will be used for the request", ->
225
+ @resource.add('index', => {success: @callback})
226
+ spy = spyOn(@, 'callback')
227
+
228
+ runs -> $.ajax(@url, {type: 'get'})
229
+ waitsFor -> spy.callCount
230
+
231
+ runs -> expect(spy.callCount).toBe(1)
232
+
233
+ it "handles the complete callback", ->
234
+ @resource.add('index', => {success: @callback, complete: @callback})
235
+ spy = spyOn(@, 'callback')
236
+
237
+ runs -> $.ajax(@url, {type: 'get'})
238
+ waitsFor -> spy.callCount == 2
239
+
240
+ runs -> expect(spy.callCount).toBe(2)
241
+
242
+ it "handles the beforeSend callback", ->
243
+ spyOn(window, 'XMLHttpRequest') # keep these requests from being fully requested
244
+ @resource.add('index', => {beforeSend: @callback})
245
+ spy = spyOn(@, 'callback')
246
+
247
+ $.ajax(@url, {type: 'get'})
248
+
249
+ expect(spy.callCount).toBe(1)
250
+
251
+ it "handles the dataFilter callback", ->
252
+ @resource.add('index', => {dataFilter: @callback})
253
+ spy = spyOn(@, 'callback')
254
+
255
+ runs -> $.ajax(@url, {type: 'get', dataType: 'json'})
256
+ waitsFor -> spy.callCount
257
+
258
+ runs -> expect(spy.argsForCall[0][1]).toBe('json')
259
+
260
+ it "handles the error callback", ->
261
+ @resource = $.resources('blogs')
262
+ @resource.add('index', => {error: @callback})
263
+ spy = spyOn(@, 'callback')
264
+
265
+ runs -> $.ajax('/blogs', {type: 'get'})
266
+ waitsFor -> spy.callCount
267
+
268
+ runs -> expect(spy.callCount).toBe(1)
269
+
270
+ describe "and proceeding", ->
271
+
272
+ it "proceeds when you return an object", ->
273
+ @resource.add('index', => {success: @callback})
274
+ spy = spyOn(@, 'callback')
275
+
276
+ runs -> $.ajax(@url, {method: 'get'})
277
+ waitsFor -> spy.callCount
278
+
279
+ runs -> expect(spy.callCount).toBe(1)
280
+
281
+ it "doesn't proceed when proceed is called", ->
282
+ @resource.add('index', (proceed) => proceed({success: @callback}))
283
+ spy = spyOn(@, 'callback')
284
+
285
+ runs -> $.ajax(@url, {method: 'get'})
286
+ waitsFor -> spy.callCount
287
+
288
+ runs -> expect(spy.callCount).toBe(1)
289
+
290
+ it "doesn't proceed when returning false", ->
291
+ spy = spyOn(@, 'callback').andCallFake(-> false)
292
+ ajaxSpy = spyOn($, 'originalAjax')
293
+ @resource.add('index', @callback)
294
+ $.ajax(@url, {method: 'get'})
295
+
296
+ expect(ajaxSpy.callCount).toBe(0)
297
+
298
+ it "doesn't call proceed more than once", ->
299
+ @resource.add 'index', (proceed) =>
300
+ proceed({success: @callback})
301
+ return {success: @callback}
302
+ spy = spyOn(@, 'callback')
303
+
304
+ runs -> $.ajax(@url, {method: 'get'})
305
+ waitsFor -> spy.callCount
306
+
307
+ runs -> expect(spy.callCount).toBe(1)
308
+
309
+ describe "chained callbacks", ->
310
+
311
+ beforeEach ->
312
+ @resource = $.resources('blogs/:blog_id/posts')
313
+ @url = '/blogs/1/posts'
314
+ @callback = -> 'callback'
315
+
316
+ it "calls both success callbacks", ->
317
+ @resource.add('index', => {success: @callback})
318
+ spy = spyOn(@, 'callback')
319
+
320
+ runs -> $.ajax(@url, {method: 'get', success: @callback})
321
+ waitsFor -> spy.callCount == 2
322
+
323
+ runs -> expect(spy.callCount).toBe(2)
324
+
325
+ it "calls both beforeSend callbacks", ->
326
+ spyOn(window, 'XMLHttpRequest') # keep these requests from being fully requested
327
+ @resource.add('index', => {beforeSend: @callback})
328
+ spy = spyOn(@, 'callback')
329
+
330
+ $.ajax(@url, {type: 'get', beforeSend: @callback})
331
+
332
+ expect(spy.callCount).toBe(2)
333
+
334
+ it "calls both dataFilter callbacks", ->
335
+ @resource.add('index', => {dataFilter: @callback})
336
+ spy = spyOn(@, 'callback')
337
+
338
+ runs -> $.ajax(@url, {type: 'get', dataType: 'json', dataFilter: @callback})
339
+ waitsFor -> spy.callCount == 2
340
+
341
+ runs -> expect(spy.argsForCall[1][1]).toBe('json')
342
+
343
+ it "calls both complete callbacks", ->
344
+ @resource.add('index', => {complete: @callback})
345
+ spy = spyOn(@, 'callback')
346
+
347
+ runs -> $.ajax(@url, {type: 'get', dataType: 'json', complete: @callback})
348
+ waitsFor -> spy.callCount == 2
349
+
350
+ runs -> expect(spy.callCount).toBe(2)
351
+
352
+ it "calls both error callbacks", ->
353
+ @resource = $.resources('blogs')
354
+ @resource.add('index', => {error: @callback})
355
+ spy = spyOn(@, 'callback')
356
+
357
+ runs -> $.ajax('/blogs', {method: 'get', error: @callback})
358
+ waitsFor -> spy.callCount == 2
359
+
360
+ runs -> expect(spy.callCount).toBe(2)
361
+
362
+ it "allows mixing $.ajax().done() style callbacks", ->
363
+ @resource.add('index', => {success: @callback})
364
+ spy = spyOn(@, 'callback')
365
+
366
+ runs -> $.ajax(@url, {method: 'get'}).done(@callback)
367
+ waitsFor -> spy.callCount == 2
368
+
369
+ runs -> expect(spy.callCount).toBe(2)
370
+
371
+
372
+ describe "using UJS", ->
373
+
374
+ template 'ujs.html'
375
+
376
+ beforeEach ->
377
+ @resource = $.resources('posts')
378
+ @callback = -> 'callback'
379
+
380
+ it "allows :remote => true", ->
381
+ spyOn(window, 'XMLHttpRequest') # keep these requests from being fully requested
382
+ spy = spyOn(@, 'callback')
383
+ @resource.add('show', @callback)
384
+ @resource.add('edit', @callback)
385
+
386
+ $('a#show').click()
387
+ expect(spy.callCount).toBe(1)
388
+
389
+ $('a#edit').click()
390
+ expect(spy.callCount).toBe(2)
391
+
392
+ it "allows :remote => true to call callbacks", ->
393
+ spy = spyOn(@, 'callback')
394
+ @resource.add('show', => {success: @callback})
395
+
396
+ runs -> $('a#show').click()
397
+ waitsFor -> spy.callCount
398
+
399
+ runs -> expect(spy.callCount).toBe(1)
@@ -0,0 +1,177 @@
1
+ require '/assets/resourcy.js'
2
+
3
+ describe "Resourcy", ->
4
+
5
+ beforeEach ->
6
+ Resourcy.removeAll()
7
+
8
+ describe ".resources (creating a plural resource)", ->
9
+
10
+ beforeEach ->
11
+ @resource = Resourcy.resources('blogs/:blog_id/posts')
12
+
13
+ it "returns a resource", ->
14
+ expect(typeof(@resource)).toBe('object')
15
+
16
+ it "sets information on the resource", ->
17
+ expect(@resource.pathvars).toEqual(['blog_id'])
18
+ expect(@resource.actions).toEqual({})
19
+ expect(@resource.singular).toBe(false)
20
+ expect(@resource.name).toBe('posts')
21
+
22
+ it "has an add, remove, and describe method", ->
23
+ expect(typeof(@resource.add)).toBe('function')
24
+ expect(typeof(@resource.remove)).toBe('function')
25
+ expect(typeof(@resource.describe)).toBe('function')
26
+
27
+ it "can find an existing resource and return it", ->
28
+ r2 = Resourcy.resources('blogs/:blog_id/posts')
29
+ expect(r2).toBe(@resource)
30
+
31
+
32
+ describe ".resource (creating a singular resource)", ->
33
+
34
+ beforeEach ->
35
+ @resource = Resourcy.resource('blogs/:blog_id/owner')
36
+
37
+ it "returns a resource", ->
38
+ expect(typeof(@resource)).toBe('object')
39
+
40
+ it "sets information on the resource", ->
41
+ expect(@resource.pathvars).toEqual(['blog_id'])
42
+ expect(@resource.actions).toEqual({})
43
+ expect(@resource.singular).toBe(true)
44
+ expect(@resource.name).toBe('owner')
45
+
46
+ it "has an add, remove, and describe method", ->
47
+ expect(typeof(@resource.add)).toBe('function')
48
+ expect(typeof(@resource.remove)).toBe('function')
49
+ expect(typeof(@resource.describe)).toBe('function')
50
+
51
+ it "can find an existing resource and return it", ->
52
+ r2 = Resourcy.resource('blogs/:blog_id/owner')
53
+
54
+ expect(r2).toBe(@resource)
55
+
56
+
57
+ describe ".routes", ->
58
+
59
+ beforeEach ->
60
+ Resourcy.resources('blogs/:blog_id/posts')
61
+ Resourcy.resource('blogs/:blog_id/owner')
62
+
63
+ it "returns an object of registered resources", ->
64
+ expect(Resourcy.routes()).toEqual({owner: [], posts: []})
65
+
66
+ it "calls describe on all the resources", ->
67
+ spyOn(Resourcy.resources('blogs/:blog_id/posts'), 'describe').andCallFake(-> ['posts => foo'])
68
+ spyOn(Resourcy.resources('blogs/:blog_id/owner'), 'describe').andCallFake(-> ['owner => foo'])
69
+
70
+ expect(Resourcy.routes()).toEqual({owner: ['owner => foo'], posts: ['posts => foo']})
71
+
72
+
73
+ describe ".noConflict", ->
74
+
75
+ it "removes Resourcy", ->
76
+ Resourcy.noConflict() # in specs we've overridden noConflict to assign it to R (and remove it like normal)
77
+
78
+ expect(window.Resourcy).toBeUndefined()
79
+ window.Resourcy = window.R # and put it back now
80
+
81
+
82
+ describe "plural resources", ->
83
+
84
+ beforeEach ->
85
+ @resource = Resourcy.resources('blogs/:blog_id/posts')
86
+ @callback = -> 'callback'
87
+
88
+ describe "#add", ->
89
+
90
+ it "accepts an object, and adds to the actions", ->
91
+ @resource.add({index: @callback})
92
+
93
+ expect(@resource.actions.index).toBe(@callback)
94
+
95
+ it "accepts an string and callback, and adds to the actions", ->
96
+ @resource.add('put:activate', @callback)
97
+
98
+ expect(@resource.actions.put['activate']).toBe(@callback)
99
+
100
+ it "doesn't allow adding the same action more than once", ->
101
+ @resource.add({index: @callback})
102
+
103
+ expect(=> @resource.add({index: @callback}))
104
+ .toThrow("The index action already exists on the 'posts' resource. Try removing it first.")
105
+
106
+ it "returns itself for chaining", ->
107
+ expect(@resource.add('put:activate', @callback)).toBe(@resource)
108
+
109
+
110
+ describe "#remove", ->
111
+
112
+ beforeEach ->
113
+ @resource.add('index', @callback)
114
+ @resource.add('put:activate', @callback)
115
+
116
+ it "removes the action", ->
117
+ expect(@resource.actions.index).toBe(@callback)
118
+ expect(@resource.actions.put['activate']).toBe(@callback)
119
+
120
+ @resource.remove('index')
121
+ @resource.remove('put:activate')
122
+
123
+ expect(@resource.actions.index).toBeUndefined()
124
+ expect(@resource.actions.put['activate']).toBeUndefined()
125
+
126
+ it "returns itself for chaining", ->
127
+ expect(@resource.remove('index')).toBe(@resource)
128
+
129
+
130
+ describe "#removeAll", ->
131
+
132
+ beforeEach ->
133
+ @resource.add('index', @callback)
134
+ @resource.add('put:activate', @callback)
135
+
136
+ it "clears all actions", ->
137
+ expect(@resource.actions.index).toBe(@callback)
138
+ expect(@resource.actions.put['activate']).toBe(@callback)
139
+
140
+ @resource.removeAll()
141
+
142
+ expect(@resource.actions).toEqual({})
143
+
144
+ it "returns itself for chaining", ->
145
+ expect(@resource.removeAll()).toBe(@resource)
146
+
147
+
148
+ describe "#describe", ->
149
+
150
+ beforeEach ->
151
+ @resource.add('index', @callback)
152
+ @resource.add('put:activate', @callback)
153
+
154
+ it "returns an array describing the resource actions", ->
155
+ expect(@resource.describe()).toEqual(['blogs/:blog_id/posts/activate PUT => posts#activate', 'blogs/:blog_id/posts GET => posts#index'])
156
+
157
+
158
+ describe "singular resources", ->
159
+
160
+ beforeEach ->
161
+ @resource = Resourcy.resource('blogs/:blog_id/owner')
162
+ @callback = -> 'callback'
163
+
164
+ describe "#add", ->
165
+
166
+ it "doesn't allow adding the index action", ->
167
+ expect(=> @resource.add('index')).toThrow("Adding index to 'owner' isn't possible (singular resource).")
168
+
169
+
170
+ describe "#describe", ->
171
+
172
+ beforeEach ->
173
+ @resource.add('show', @callback)
174
+ @resource.add('put:activate', @callback)
175
+
176
+ it "returns an array describing the resource actions", ->
177
+ expect(@resource.describe()).toEqual(['blogs/:blog_id/owner/activate PUT => owner#activate', 'blogs/:blog_id/owner GET => owner#show'])
@@ -0,0 +1,37 @@
1
+ RESOURCES = {
2
+ POSTS: {
3
+ 'index': function() { return 'PostsController#index' },
4
+ 'show': function() { return 'PostsController#show' },
5
+ 'new': function() { return 'PostsController#new' },
6
+ 'create': function() { return 'PostsController#create' },
7
+ 'edit': function() { return 'PostsController#edit' },
8
+ 'update': function() { return 'PostsController#update' },
9
+ 'destroy': function() { return 'PostsController#destroy'},
10
+ 'get:comments': function() { return 'PostsController#comments:GET' },
11
+ 'post:reorder': function() { return 'PostsController#reorder:POST' },
12
+ 'delete:comments': function() { return 'PostsController#comments:DELETE' },
13
+ 'put:publish': function() { return 'PostsController#publish:PUT' }
14
+ },
15
+ COMMENTS: {
16
+ 'index': function() { return 'CommentsController#index' },
17
+ 'new': function() { return 'CommentsController#new' },
18
+ 'create': function() { return 'CommentsController#create' },
19
+ 'destroy': function() { return 'CommentsController#destroy' },
20
+ 'put:approve': function() { return 'CommentsController#publish:PUT' }
21
+ },
22
+ OWNER: {
23
+ 'show': function() { return 'OwnerController#show' },
24
+ 'new': function() { return 'OwnerController#new' },
25
+ 'create': function() { return 'OwnerController#create' },
26
+ 'edit': function() { return 'OwnerController#edit' },
27
+ 'update': function() { return 'OwnerController#update' },
28
+ 'destroy': function() { return 'OwnerController#destroy'},
29
+ }
30
+ };
31
+
32
+ window.Resourcy = {
33
+ noConflict: function() {
34
+ window.R = Resourcy;
35
+ delete(window.Resourcy);
36
+ }
37
+ };
@@ -0,0 +1,3 @@
1
+ <a id="show" href="/posts/1" data-remote="true">Show</a>
2
+ <a id="edit" href="/posts/1/edit" data-remote="true">Edit</a>
3
+ <a id="destroy" href="/posts/1" data-confirm="Are you sure?" data-method="delete" data-remote="true" rel="nofollow">Destroy</a>
@@ -0,0 +1,37 @@
1
+ #= require resourcy
2
+ #
3
+ # Resourcy Ajax adapter
4
+ #
5
+ # This overrides the default jQuery.ajax method and mixes in the logic required to make Resourcy work with the options
6
+ # and arguments for the jQuery Ajax api.
7
+
8
+ # Get the original jQuery.ajax and merge in some additional methods from Resourcy.
9
+ original = jQuery.ajax
10
+ jQuery.extend(jQuery, {originalAjax: original, resources: Resourcy.resources, resource: Resourcy.resource, routes: Resourcy.routes})
11
+
12
+ # Remove Resourcy from the global namespace.
13
+ handleRequest = Resourcy.handleRequest
14
+ Resourcy.noConflict()
15
+
16
+ # Define an options handler, that properly merges options for the Ajax request.
17
+ optionsHandler = (opts1, opts2) ->
18
+ options = jQuery.extend(true, {}, opts1, opts2)
19
+ for method in ['beforeSend', 'error', 'dataFilter', 'success', 'complete']
20
+ if opts1[method] && opts2[method]
21
+ c1 = opts1[method]
22
+ c2 = opts2[method]
23
+ options[method] = ->
24
+ c1.apply(window, arguments)
25
+ c2.apply(window, arguments)
26
+ return options
27
+
28
+ # Create a new jQuery.ajax method that works with Resourcy.
29
+ jQuery.ajax = (url, options = {}) ->
30
+ if typeof(url) is 'object'
31
+ options = url
32
+ url = options.url
33
+ for data in options.data || []
34
+ if data.name is '_method'
35
+ method = data.value
36
+ break
37
+ return handleRequest(method || options.type || 'get', url, options, jQuery.originalAjax, optionsHandler)