restful_json 3.3.0 → 3.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/README.md +165 -170
- data/lib/restful_json/controller.rb +17 -7
- data/lib/restful_json/version.rb +1 -1
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
YjYwZWQ2YjYyMTlmYTU5ZDJjNDg3NzBhNWUzMzFiYjA3MTEwYjdkYQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
NTQ2MWYxMzMxZDc2Y2E3ZTVjOWU1YzcxNWI1MTk3ZTlkZjI5YjdjNw==
|
7
7
|
!binary "U0hBNTEy":
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NzRhYjAxZjkwOTRiZDdiNmYyZjRiNGEzZWM2OTQwMTA4NzVhM2UwZjY1ZDg4
|
10
|
+
YzYzNjU4MmU4NjJiYjE1ODBhOWRiYzQ3Y2Y4M2Q2M2RjYjhmYzZmOTBmMTU3
|
11
|
+
ZDZkYWQyOWIyYmExZmI5NjA0ODQzY2I1NGQxOGFmNTAzZjU5NWU=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ODhlY2RiMTk3YTFlOGJiMGZiMjBlY2YxMDliMjczMzNiNDJmN2VmYmU2NTY1
|
14
|
+
OTUxNzhiNDJhZjMzZjI2ZjdlNWQzMzlhOWE2YThlNjQ3YWJlMmViOGJjOGMw
|
15
|
+
YWMwN2NiODVmM2IyNGE3YzMzZDllNTYzNWJjNDU3NTliOTlkNGI=
|
data/README.md
CHANGED
@@ -6,22 +6,22 @@ What does that mean? It means you typically won't have to write index, create, u
|
|
6
6
|
|
7
7
|
Why do you need this if Rails controllers already make it easy to provide RESTful JSON services via generated controllers? Because this is just as flexible, almost as declarative, and takes less code. That means your controllers will be easier to read and there will be less code to maintain, but when you need an action method more customized than we can provide, that's all you'll have to write.
|
8
8
|
|
9
|
-
|
9
|
+
The goal of the project is to reduce service controller code in an intuitive way, not to be a be-everything DSL or limit what you can do in a controller. Choose what features to expose, and you can still define/redefine actions etc. at will.
|
10
10
|
|
11
|
-
|
11
|
+
We test with travis-ci with with Rails 3.1, 3.2, and Rails 4. Feel free to submit issues and/or do a pull requests if you run into anything.
|
12
|
+
|
13
|
+
You can use any of these for the JSON response (the view):
|
12
14
|
* [active_model_serializers][active_model_serializers] - also provides the serialize_action class method in the controller to specify custom serializers (assuming you are using a later version of active_model_serializers that works with respond_with).
|
13
15
|
* JBuilder - to use, set render_enabled in the restful_json config to false.
|
14
16
|
* Just about anything else that works with render/respond_with, or that just adjust the view like JBuilder, and don't require extra work in the controller.
|
15
17
|
|
16
|
-
|
18
|
+
And can use any of the following for authorizing parameters in the incoming JSON (for create/update):
|
17
19
|
* Adam Hawkins' [permitters][permitter] which use [Strong Parameters][strong_parameters] and [Cancan][cancan]. Permitters are an object-oriented way of defining what is permitted in the incoming JSON, and are a great compliment in the same way that ActiveModel::Serializers are. Cancan supports [Authlogic][authlogic], [Devise][devise], etc.
|
18
20
|
* [Strong Parameters][strong_parameters] - lets you only have to define `(single model name)_params` and/or `create_(single model name)_params` and/or `update_(single model name)_params` which can call require, permit, etc. on params.
|
19
21
|
* Mass assignment security in Rails 3.x (attr_accessible, etc.).
|
20
22
|
|
21
23
|
An example app using an older version of restful_json with AngularJS is [employee-training-tracker][employee-training-tracker], featured in [Built with AngularJS][built_with_angularjs].
|
22
24
|
|
23
|
-
The goal of restful_json is to reduce service controller code in an intuitive way, not to be a be-everything DSL. You can choose whether or not to use it and what features to expose at the controller-level, and you can customize controllers and re-define actions/define custom actions.
|
24
|
-
|
25
25
|
### Installation
|
26
26
|
|
27
27
|
In your Rails app's `Gemfile`:
|
@@ -41,51 +41,19 @@ Then:
|
|
41
41
|
|
42
42
|
bundle install
|
43
43
|
|
44
|
-
####
|
45
|
-
|
46
|
-
To clean up your controllers and to make restful_json more flexible and less complex, we suggest use of a module to hold the includes that you'll need depending on what you decide to use. We'll call it ServiceControllerAdditions:
|
47
|
-
|
48
|
-
Create a file called `app/controllers/service_controller_additions.rb` and put this in it:
|
49
|
-
|
50
|
-
module ServiceControllerAdditions
|
51
|
-
extend ActiveSupport::Concern
|
52
|
-
|
53
|
-
included do
|
54
|
-
# comment this out if you don't want to use ActiveModel::Seriaizers
|
55
|
-
include ::ActionController::Serialization
|
56
|
-
# comment this out if you don't want to use Strong Parameters or Permitters
|
57
|
-
include ::ActionController::StrongParameters
|
58
|
-
# comment this out if you don't want to use Permitters
|
59
|
-
include ::TwinTurbo::Controller
|
60
|
-
# comment this out if you don't want to use restful_json
|
61
|
-
include ::RestfulJson::Controller
|
62
|
-
end
|
63
|
-
|
64
|
-
end
|
65
|
-
|
66
|
-
That way you can just put this at the top of your controller, and will be able to easily extend the functionality of multiple controllers at once, e.g.:
|
67
|
-
|
68
|
-
class FoobarsController < ApplicationController
|
69
|
-
include ServiceControllerAdditions
|
70
|
-
end
|
71
|
-
|
72
|
-
However, if you plan to use Permitters and ActiveModel::Serializers, use the "default". It is called the default, because v3 started off with Permitters and ActiveModel::Serializers controller, etc. includes being done by the restful_json gem, which no longer happens:
|
73
|
-
|
74
|
-
class FoobarsController < ApplicationController
|
75
|
-
include RestfulJson::DefaultController
|
76
|
-
end
|
44
|
+
#### Strong Parameters
|
77
45
|
|
78
|
-
|
46
|
+
Strong Parameters is not required, but can be used on its own or as a dependency of Permitters.
|
79
47
|
|
80
|
-
|
48
|
+
If you are using Rails 4.x, you might be able to skip this section, as [Strong Parameters][strong_parameters] is included.
|
81
49
|
|
82
|
-
If you plan to use Permitters or
|
50
|
+
If you are using Rails 3.x, then if you plan to use Permitters or want to use Strong Parameters by itself, you may need to tweak a few things for [Strong Parameters][strong_parameters]
|
83
51
|
|
84
52
|
To disable the default whitelisting that occurs in later versions of Rails 3.x, set the `config.active_record.whitelist_attributes` property in your `config/application.rb` to false:
|
85
53
|
|
86
54
|
config.active_record.whitelist_attributes = false
|
87
55
|
|
88
|
-
No more attr_accessible needed in your models (so take them out and convert them). Instead you will put this information into your
|
56
|
+
No more attr_accessible needed in your models (so take them out and convert them). Instead you will either put this information into your Permitters, or if you are using Strong Parameters without Permitters, you'll create `create_(single model name)_params`, `update_(single model name)_params`, and/or `(single model name)_params` methods in your controller(s). Encapsulating what params are permissible in such a method is encouraged and described in the [Strong Parameters][strong_parameters] documentation.
|
89
57
|
|
90
58
|
Strong Parameters (and Permitters) require a model include.
|
91
59
|
|
@@ -99,7 +67,11 @@ If you'd rather use Strong Parameters with all models, just put this in your `co
|
|
99
67
|
|
100
68
|
#### Cancan
|
101
69
|
|
102
|
-
|
70
|
+
Though optional, if you decide to use Permitters, the Permitters framework relies on [Cancan][cancan].
|
71
|
+
|
72
|
+
Permitters are an object-oriented representation of Strong Parameters, but they also integrate with Cancan. Cancan can restrict what resources a given user is allowed to access. In Cancan, all permissions are defined in a single location (the Ability class) and not duplicated across controllers, views, and database queries.
|
73
|
+
|
74
|
+
To setup Cancan, you need a `current_user` method in your `app/controllers/application_controller.rb` or in your service controllers. For the sake of example, we'll just have it return a new User:
|
103
75
|
|
104
76
|
class ApplicationController < ActionController::Base
|
105
77
|
protect_from_forgery
|
@@ -109,9 +81,9 @@ If you are using Permitters, they use [Cancan][cancan], which requires a `curren
|
|
109
81
|
end
|
110
82
|
end
|
111
83
|
|
112
|
-
|
84
|
+
Cancan integrates [Authlogic][authlogic], [Devise][devise], etc. to return a proper logged-in user or you can return it however you wish.
|
113
85
|
|
114
|
-
|
86
|
+
Cancan also needs an Ability defined in `app/models/ability.rb`. Just for testing we'll ignore the user object and allow everything:
|
115
87
|
|
116
88
|
class Ability
|
117
89
|
include CanCan::Ability
|
@@ -129,13 +101,17 @@ Or, if you'd rather use Cancan with all models, just put this in your `config/en
|
|
129
101
|
|
130
102
|
ActiveRecord::Base.send(:include, CanCan::ModelAdditions)
|
131
103
|
|
132
|
-
|
104
|
+
Once you get everything setup, go through the [Cancan][cancan] documentation, and then [Authlogic][authlogic], [Devise][devise], etc. to setup/integrate with proper authentication and authorization.
|
133
105
|
|
134
106
|
#### JSON Response Generators
|
135
107
|
|
136
108
|
##### ActiveModel Serializers
|
137
109
|
|
138
|
-
|
110
|
+
Use of [ActiveModel::Serializers][active_model_serializers] is optional, but a great way to have object-oriented model-like representation of JSON views.
|
111
|
+
|
112
|
+
The purpose of ActiveModel::Serializers is to provide an object to encapsulate serialization of ActiveModel objects, including ActiveRecord objects. Serializers know about both a model and the current_user, so you can customize serialization based upon whether a user is authorized to see the content. In short, serializers replace hash-driven development with object-oriented development.
|
113
|
+
|
114
|
+
If you chose to use ActiveModel::Serializers, you'll eventually want to create one or more serializers for each model that you will be returning via the service(s), e.g.:
|
139
115
|
|
140
116
|
/app/serializers/singular_model_name_serializer.rb
|
141
117
|
|
@@ -182,9 +158,9 @@ Then make sure you add a JBuilder view for each action you require. Although all
|
|
182
158
|
|
183
159
|
See [Railscast #320][railscast320] for more examples on setting up and using JBuilder.
|
184
160
|
|
185
|
-
|
161
|
+
##### Other Options
|
186
162
|
|
187
|
-
You should be able to use anything that works with normal render/responds_with in Rails controllers
|
163
|
+
You should be able to use anything that works with normal render/responds_with in Rails controllers without additional code in the controller. If you'd like to use something that requires additional code in the action methods of the controller, and you think it would be a good fit, feel free to do a pull request.
|
188
164
|
|
189
165
|
#### Create/Update JSON Request/Params Acceptance
|
190
166
|
|
@@ -246,9 +222,9 @@ Only the main controller is needed:
|
|
246
222
|
|
247
223
|
Then, make sure that attr_accessible and/or attr_protected, etc. are used properly.
|
248
224
|
|
249
|
-
###
|
225
|
+
### Application Configuration
|
250
226
|
|
251
|
-
At the bottom of `config/environment.rb`, you can set
|
227
|
+
At the bottom of `config/environment.rb`, you can set restful_json can be configured one line at a time.
|
252
228
|
|
253
229
|
RestfulJson.debug = true
|
254
230
|
|
@@ -285,7 +261,7 @@ or in bulk, like:
|
|
285
261
|
|
286
262
|
end
|
287
263
|
|
288
|
-
### Controller
|
264
|
+
### Controller Configuration
|
289
265
|
|
290
266
|
In the controller, you can set a variety of class attributes with `self.something = ...` in the body of your controller.
|
291
267
|
|
@@ -318,23 +294,22 @@ Other class attributes are available for setting/overriding, but they are all se
|
|
318
294
|
|
319
295
|
### Usage
|
320
296
|
|
321
|
-
You have a configurable generic Rails 3.1
|
297
|
+
You have a configurable generic Rails 3.1.x/3.2.x/4.0.x controller that does the index, show, create, and update and other custom actions easily for you.
|
322
298
|
|
323
299
|
Everything is well-declared and fairly concise.
|
324
300
|
|
325
301
|
You can have something as simple as:
|
326
302
|
|
327
303
|
class FoobarsController < ApplicationController
|
328
|
-
include
|
304
|
+
include RestfulJson::DefaultController
|
329
305
|
end
|
330
306
|
|
331
|
-
which would use the restful_json configuration and the controller's classname for the service definition and provide a simple no-frills JSON CRUD controller that behaves
|
307
|
+
which would use the restful_json configuration and the controller's classname for the service definition and provide a simple no-frills JSON CRUD controller that behaves somewhat similarly to a Rails controller created via `rails g scaffold ...`.
|
332
308
|
|
333
|
-
Or, you
|
309
|
+
Or, you can define many more bells and whistles:
|
334
310
|
|
335
311
|
class FoobarsController < ApplicationController
|
336
|
-
|
337
|
-
include ServiceControllerAdditions
|
312
|
+
include RestfulJson::DefaultController
|
338
313
|
|
339
314
|
query_for :index, is: ->(t,q) {q.joins(:apples, :pears).where(apples: {color: 'green'}).where(pears: {color: 'green'})}
|
340
315
|
|
@@ -365,6 +340,30 @@ Or, you could define more bells and whistles (read on to see what these do...):
|
|
365
340
|
|
366
341
|
end
|
367
342
|
|
343
|
+
#### Routing
|
344
|
+
|
345
|
+
You can just add normal Rails RESTful routes in `config/routes.rb`, e.g. for the Foobar model:
|
346
|
+
|
347
|
+
MyAppName::Application.routes.draw do
|
348
|
+
resources :foobars
|
349
|
+
end
|
350
|
+
|
351
|
+
Supports static, nested, etc. routes also, e.g.:
|
352
|
+
|
353
|
+
MyAppName::Application.routes.draw do
|
354
|
+
namespace :my_service_controller_module do
|
355
|
+
resources :foobars
|
356
|
+
end
|
357
|
+
end
|
358
|
+
|
359
|
+
Can pass in params from the path for use in filters, etc. as if they were request parameters:
|
360
|
+
|
361
|
+
MyAppName::Application.routes.draw do
|
362
|
+
namespace :my_service_controller_module do
|
363
|
+
match 'bar/:bar_id/foobars(.:format)' => 'foobars#index'
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
368
367
|
#### Default Filtering by Attribute(s)
|
369
368
|
|
370
369
|
First, declare in the controller:
|
@@ -433,7 +432,7 @@ First, declare in the controller:
|
|
433
432
|
|
434
433
|
supports_functions :uniq
|
435
434
|
|
436
|
-
|
435
|
+
Now this works:
|
437
436
|
|
438
437
|
http://localhost:3000/foobars?uniq=
|
439
438
|
|
@@ -443,27 +442,25 @@ First, declare in the controller:
|
|
443
442
|
|
444
443
|
supports_functions :count
|
445
444
|
|
446
|
-
|
445
|
+
Now this works:
|
447
446
|
|
448
447
|
http://localhost:3000/foobars?count=
|
449
448
|
|
450
449
|
##### Paging
|
451
450
|
|
452
|
-
|
451
|
+
First, declare in the controller:
|
453
452
|
|
454
453
|
supports_functions :page, :page_count
|
455
454
|
|
456
|
-
|
455
|
+
Now you can get the page count:
|
457
456
|
|
458
|
-
http://localhost:3000/foobars?
|
457
|
+
http://localhost:3000/foobars?page_count=
|
459
458
|
|
460
|
-
|
459
|
+
And access each page of results:
|
461
460
|
|
461
|
+
http://localhost:3000/foobars?page=1
|
462
462
|
http://localhost:3000/foobars?page=2
|
463
|
-
|
464
|
-
To get the total number of pages of results:
|
465
|
-
|
466
|
-
http://localhost:3000/foobars?page_count=
|
463
|
+
...
|
467
464
|
|
468
465
|
To set page size at application level:
|
469
466
|
|
@@ -473,11 +470,9 @@ To set page size at controller level:
|
|
473
470
|
|
474
471
|
self.number_of_records_in_a_page = 15
|
475
472
|
|
476
|
-
|
473
|
+
##### Skip and Take (OFFSET and LIMIT)
|
477
474
|
|
478
|
-
|
479
|
-
|
480
|
-
In controller make sure these are included:
|
475
|
+
First, declare in the controller:
|
481
476
|
|
482
477
|
supports_functions :skip, :take
|
483
478
|
|
@@ -489,47 +484,25 @@ To limit the number of rows returned, use 'take'. It is called take, because tak
|
|
489
484
|
|
490
485
|
http://localhost:3000/foobars.json?take=5
|
491
486
|
|
492
|
-
Combine skip and take for manual completely customized paging.
|
493
|
-
|
494
|
-
Get the first page of 15 results:
|
487
|
+
Combine skip and take for manual completely customized paging, e.g.
|
495
488
|
|
496
489
|
http://localhost:3000/foobars?take=15
|
497
|
-
|
498
|
-
Second page of 15 results:
|
499
|
-
|
500
490
|
http://localhost:3000/foobars?skip=15&take=15
|
501
|
-
|
502
|
-
Third page of 15 results:
|
503
|
-
|
504
491
|
http://localhost:3000/foobars?skip=30&take=15
|
505
492
|
|
506
|
-
First page of 30 results:
|
507
|
-
|
508
|
-
http://localhost:3000/foobars?take=30
|
509
|
-
|
510
|
-
Second page of 30 results:
|
511
|
-
|
512
|
-
http://localhost:3000/foobars?skip=30&take=30
|
513
|
-
|
514
|
-
Third page of 30 results:
|
515
|
-
|
516
|
-
http://localhost:3000/foobars?skip=60&take=30
|
517
|
-
|
518
493
|
##### Custom Serializers
|
519
494
|
|
520
|
-
If using ActiveModel::Serializers, you can use something other than the (singular model name)Serializer via serialize_action
|
495
|
+
If using ActiveModel::Serializers, you can use something other than the `(singular model name)Serializer` via `serialize_action`:
|
521
496
|
|
522
497
|
serialize_action :index, with: ListFoobarSerializer
|
523
498
|
|
524
499
|
The built-in actions that support custom serializers (you can add more) are: index, show, new, create, update, destroy, and any action you automatically have created via using the restful_json `query_for` method.
|
525
500
|
|
526
|
-
It will use `
|
527
|
-
|
528
|
-
To override, specify `for:` with `:array` or `:each`, e.g.:
|
501
|
+
It will use the `serializer` option for single result actions like show, new, create, update, destroy, and the `each_serializer` option with index and custom actions. Or, you can specify `for:` with `:array` or `:each`, e.g.:
|
529
502
|
|
530
503
|
serialize_action :index, :some_custom_action, with: FoosSerializer, for: :array
|
531
504
|
|
532
|
-
|
505
|
+
Or, you could just use the default serialization, if you want.
|
533
506
|
|
534
507
|
##### Custom Queries
|
535
508
|
|
@@ -538,7 +511,12 @@ To filter the list where the status_code attribute is 'green':
|
|
538
511
|
# t is self.model_class.arel_table and q is self.model_class.scoped
|
539
512
|
query_for :index, is: lambda {|t,q| q.where(:status_code => 'green')}
|
540
513
|
|
541
|
-
or use the `->` Ruby 1.9 lambda stab operator
|
514
|
+
or use the `->` Ruby 1.9 lambda stab operator (note lack of whitespace between stab and parenthesis):
|
515
|
+
|
516
|
+
# t is self.model_class.arel_table and q is self.model_class.scoped
|
517
|
+
query_for :index, is: is: ->(t,q) {q.where(:status_code => 'green')}
|
518
|
+
|
519
|
+
You can also filter out items that have associations that don't have a certain attribute value (or anything else you can think up with [ARel][arel]/[ActiveRecord relations][ar]), e.g. to filter the list where the object's apples and pears associations are green:
|
542
520
|
|
543
521
|
# t is self.model_class.arel_table and q is self.model_class.scoped
|
544
522
|
# note: must be no space between -> and parenthesis
|
@@ -550,7 +528,9 @@ or use the `->` Ruby 1.9 lambda stab operator. You can also filter out items tha
|
|
550
528
|
|
551
529
|
##### Define Custom Actions with Custom Queries
|
552
530
|
|
553
|
-
|
531
|
+
You are still working with regular controllers here, so add or override methods if you want more!
|
532
|
+
|
533
|
+
However `query_for` will create new action methods, so you can easily create custom non-RESTful action methods:
|
554
534
|
|
555
535
|
# t is self.model_class.arel_table and q is self.model_class.scoped
|
556
536
|
# note: must be no space between -> and parenthesis in lambda syntax!
|
@@ -558,31 +538,35 @@ or use the `->` Ruby 1.9 lambda stab operator. You can also filter out items tha
|
|
558
538
|
|
559
539
|
Note that it is a proc so you can really do whatever you want with it and will have access to other things in the environment or can call another method, etc.
|
560
540
|
|
561
|
-
|
562
|
-
|
563
|
-
|
541
|
+
query_for :some_action, is: ->(t,q) do
|
542
|
+
if @current_user.admin?
|
543
|
+
Rails.logger.debug("Notice: unfiltered results provided to admin #{@current_user.name}")
|
544
|
+
# just make sure the relation is returned!
|
545
|
+
q
|
546
|
+
else
|
547
|
+
q.where(:access => 'public')
|
548
|
+
end
|
549
|
+
end
|
564
550
|
|
565
|
-
|
551
|
+
Be sure to add a route for that action, e.g. in `config/routes.rb`, e.g. for the Barfoo model:
|
566
552
|
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
# why use nested if you only want to provide ways of querying via path
|
571
|
-
match 'bars/:bar_id/foobars(.:format)' => 'foobars#index'
|
553
|
+
MyAppName::Application.routes.draw do
|
554
|
+
resources :barfoos do
|
555
|
+
get 'some_action', :on => :collection
|
572
556
|
end
|
573
557
|
end
|
574
558
|
|
575
559
|
### With Rails-api
|
576
560
|
|
577
|
-
If you want to try out [rails-api][rails-api]
|
561
|
+
If you want to try out [rails-api][rails-api]:
|
578
562
|
|
579
563
|
gem 'rails-api', '~> 0.0.3'
|
580
564
|
|
581
|
-
In `
|
565
|
+
In `app/controllers/my_service_controller.rb`:
|
582
566
|
|
583
|
-
module
|
567
|
+
module MyServiceController
|
584
568
|
extend ActiveSupport::Concern
|
585
|
-
|
569
|
+
|
586
570
|
included do
|
587
571
|
# Rails-api lets you choose features. You might not need all of these, or may need others.
|
588
572
|
include AbstractController::Translation
|
@@ -591,25 +575,25 @@ In `apps/controllers/restful_json_api.rb`:
|
|
591
575
|
include ActionController::MimeResponds
|
592
576
|
include ActionController::Cookies
|
593
577
|
include ActionController::ParamsWrapper
|
578
|
+
|
579
|
+
# use Permitters and AMS
|
594
580
|
include RestfulJson::DefaultController
|
581
|
+
# or comment that last line and uncomment whatever you want to use
|
582
|
+
#include ::ActionController::Serialization # AMS
|
583
|
+
#include ::ActionController::StrongParameters
|
584
|
+
#include ::TwinTurbo::Controller # Permitters which uses Cancan and Strong Parameters
|
585
|
+
#include ::RestfulJson::Controller
|
595
586
|
|
596
587
|
# If you want any additional inline class stuff, it goes here...
|
597
|
-
end
|
598
|
-
|
599
|
-
module ClassMethods
|
600
|
-
# Any additional class methods...
|
601
|
-
end
|
602
|
-
|
603
|
-
# Instance methods...
|
604
|
-
|
588
|
+
end
|
605
589
|
end
|
606
590
|
|
607
591
|
class FoobarsController < ActionController::API
|
608
|
-
include
|
592
|
+
include MyServiceController
|
609
593
|
end
|
610
594
|
|
611
595
|
class BarfoosController < ActionController::API
|
612
|
-
include
|
596
|
+
include MyServiceController
|
613
597
|
end
|
614
598
|
|
615
599
|
Note that in `/config/initializers/wrap_parameters.rb` you might need to add `include ActionController::ParamsWrapper` prior to the `wrap_parameters` call. For example, for unwrapped JSON, it would look like:
|
@@ -630,6 +614,8 @@ Note that in `/config/initializers/wrap_parameters.rb` you might need to add `in
|
|
630
614
|
|
631
615
|
##### Parent/Ancestor Class Definition Not Supported
|
632
616
|
|
617
|
+
Don't subclass and include in the parent, that puts the class attributes into the parent which means they would be shared by the children and bad things can happen.
|
618
|
+
|
633
619
|
Don't do this:
|
634
620
|
|
635
621
|
class ServiceController < ApplicationController
|
@@ -639,72 +625,81 @@ Don't do this:
|
|
639
625
|
include ::RestfulJson::Controller
|
640
626
|
end
|
641
627
|
|
642
|
-
# nor should you do this
|
643
|
-
#class FoobarsController < ApplicationController
|
644
|
-
# include RestfulJson::DefaultController
|
645
|
-
#end
|
646
|
-
|
647
628
|
class FoobarsController < ServiceController
|
648
629
|
end
|
649
630
|
|
650
631
|
class BarfoosController < ServiceController
|
651
632
|
end
|
652
633
|
|
653
|
-
|
654
|
-
|
655
|
-
Do as a module instead!
|
656
|
-
|
657
|
-
##### Customizing Behavior via Module
|
658
|
-
|
659
|
-
Remember how we created a `apps/controllers/service_controller_additions.rb`?
|
660
|
-
|
661
|
-
You can add behavior to it!
|
634
|
+
And don't do this:
|
662
635
|
|
663
|
-
|
636
|
+
class FoobarsController < ApplicationController
|
637
|
+
include RestfulJson::DefaultController
|
638
|
+
end
|
639
|
+
|
640
|
+
class FoobarsController < ServiceController
|
641
|
+
end
|
642
|
+
|
643
|
+
class BarfoosController < ServiceController
|
644
|
+
end
|
664
645
|
|
665
|
-
|
666
|
-
extend ActiveSupport::Concern
|
667
|
-
included do
|
668
|
-
# see notes in Service Controller Additions section of restful_json doc for what is needed
|
669
|
-
include ::ActionController::Serialization
|
670
|
-
include ::ActionController::StrongParameters
|
671
|
-
include ::TwinTurbo::Controller
|
672
|
-
include ::RestfulJson::Controller
|
673
|
-
|
674
|
-
# let's add a name that can be set with a class method
|
675
|
-
class_attribute :name, instance_writer: true
|
676
|
-
end
|
646
|
+
It may appear to work when using the same controller or even on each new controller load, but when you make requests to BarfoosController, make a request to FoobarsController, and then make a request back to the BarfoosController, it may fail in very strange ways, such as missing column(s) from SQL results (because it isn't using the correct model).
|
677
647
|
|
678
|
-
|
679
|
-
|
680
|
-
|
648
|
+
##### Customizing Behavior via Patch
|
649
|
+
|
650
|
+
In `config/initializers/restful_json.rb` you can monkey patch the RestfulJson::Controller module. The DefaultController includes that, so it will get your changes also:
|
651
|
+
|
652
|
+
# a horrible Hello World example
|
653
|
+
module RestfulJson
|
654
|
+
module Controller
|
655
|
+
|
656
|
+
# class methods that should be implemented or overriden
|
657
|
+
module ClassMethods
|
658
|
+
def hello(name)
|
659
|
+
#TODO: find way to call hook into the block call in RJ controller's included block
|
660
|
+
# without having do funny things to ActiveSupport::Concern, because append_features(base)
|
661
|
+
# defined in the monkey patch is never called, and module_eval is a royal pain.
|
662
|
+
# Or, stop using ActiveSupport::Concern. For now, we'll defined class_attribute in the
|
663
|
+
# class method that uses it and use respond_to? in a nasty hack. I'm sorry.
|
664
|
+
class_attribute :name, instance_writer: true
|
665
|
+
self.name = name
|
666
|
+
end
|
681
667
|
end
|
682
|
-
end
|
683
668
|
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
669
|
+
# instance methods that should be implemented or overriden.
|
670
|
+
#
|
671
|
+
# note: you don't have to do this to override service methods at the controller-level.
|
672
|
+
# Instead, just define them in the controller. this is just an example of monkey-patching.
|
673
|
+
def index
|
674
|
+
name = self.respond_to?(:name) && self.name ? self.name : 'nobody'
|
675
|
+
render :json => {:hello => self.name}
|
676
|
+
rescue => e
|
677
|
+
# rescue to identify errors that otherwise can be swallowed
|
678
|
+
puts "index failed: #{self} #{e}"
|
679
|
+
raise e
|
689
680
|
end
|
681
|
+
|
690
682
|
end
|
691
683
|
end
|
692
684
|
|
693
|
-
|
685
|
+
Now in your controller, if you:
|
694
686
|
|
695
687
|
class FoobarsController < ApplicationController
|
696
|
-
include
|
697
|
-
hello
|
688
|
+
include RestfulJson::DefaultController
|
689
|
+
hello 'world'
|
698
690
|
end
|
699
691
|
|
700
|
-
|
701
|
-
include HelloWorld
|
702
|
-
hello :world
|
703
|
-
end
|
692
|
+
(Note again: RestfulJson::DefaultController includes RestfulJson::Controller.)
|
704
693
|
|
705
|
-
|
694
|
+
Now when you call:
|
695
|
+
|
696
|
+
http://localhost:3000/foobars
|
706
697
|
|
707
|
-
|
698
|
+
You would get the response:
|
699
|
+
|
700
|
+
{'hello': 'world'}
|
701
|
+
|
702
|
+
For more realistic use that takes advantage of existing configuration in the controller, take a look at the controller in `lib/restful_json/controller.rb` to see how the actions are defined, and just copy/paste into your controller or module, etc.
|
708
703
|
|
709
704
|
### Release Notes
|
710
705
|
|
@@ -734,9 +729,9 @@ Include this in `config/environment.rb`
|
|
734
729
|
|
735
730
|
### Rails Version-specific Eccentricities
|
736
731
|
|
737
|
-
Strong Parameters is included in Rails 4.
|
732
|
+
Strong Parameters is included in Rails 4.
|
738
733
|
|
739
|
-
If you are using Rails 3.1, note that respond_with returns HTTP 200 instead of 204 for update and destroy, unless return_resource is true.
|
734
|
+
If you are using Rails 3.1.x, note that respond_with returns HTTP 200 instead of 204 for update and destroy, unless return_resource is true.
|
740
735
|
|
741
736
|
### Thanks!
|
742
737
|
|
@@ -200,14 +200,19 @@ module RestfulJson
|
|
200
200
|
|
201
201
|
def render_or_respond(read_only_action, success_code = :ok)
|
202
202
|
if self.render_enabled
|
203
|
-
|
203
|
+
# 404/not found is just for update (not destroy, because idempotent destroy = no 404)
|
204
|
+
if success_code == :not_found
|
205
|
+
respond_to do |format|
|
206
|
+
format.html { render :file => "#{Rails.root}/public/404.html", :status => :not_found }
|
207
|
+
format.any { head :not_found }
|
208
|
+
end
|
209
|
+
elsif !@value.nil? && ((read_only_action && RestfulJson.return_resource) || RestfulJson.avoid_respond_with)
|
204
210
|
respond_with(@value) do |format|
|
205
211
|
format.json do
|
206
212
|
# define local variables in blocks, not outside of them, to be safe, even though would work in this case
|
207
213
|
custom_action_serializer = self.action_to_serializer[params[:action].to_s]
|
208
214
|
custom_action_serializer_for = self.action_to_serializer_for[params[:action].to_s]
|
209
215
|
serialization_key = single_value_response? ? (custom_action_serializer_for == :each ? :each_serializer : :serializer) : (custom_action_serializer_for == :array ? :serializer : :each_serializer)
|
210
|
-
puts "serialization key = #{serialization_key}, custom_action_serializer = #{custom_action_serializer}"
|
211
216
|
if !@value.respond_to?(:errors) || @value.errors.empty?
|
212
217
|
render custom_action_serializer ? {json: @value, status: success_code, serialization_key => custom_action_serializer} : {json: @value, status: success_code}
|
213
218
|
else
|
@@ -390,17 +395,22 @@ module RestfulJson
|
|
390
395
|
allowed_params = params
|
391
396
|
end
|
392
397
|
# to_s as safety measure for vulnerabilities similar to CVE-2013-1854
|
393
|
-
@value = @model_class.
|
394
|
-
|
398
|
+
@value = @model_class.where(id: params[:id].to_s).to_a[0]
|
399
|
+
status = :ok
|
400
|
+
if @value.nil?
|
401
|
+
status = :not_found
|
402
|
+
else
|
403
|
+
@value.update_attributes(allowed_params)
|
404
|
+
end
|
395
405
|
instance_variable_set(@model_at_singular_name_sym, @value)
|
396
|
-
render_or_respond(false)
|
406
|
+
render_or_respond(false, status)
|
397
407
|
end
|
398
408
|
|
399
409
|
# The controller's destroy (delete) method to destroy a resource.
|
400
410
|
def destroy
|
401
411
|
# to_s as safety measure for vulnerabilities similar to CVE-2013-1854
|
402
|
-
@value = @model_class.
|
403
|
-
@value.destroy
|
412
|
+
@value = @model_class.where(id: params[:id].to_s).to_a[0]
|
413
|
+
@value.destroy if @value
|
404
414
|
instance_variable_set(@model_at_singular_name_sym, @value)
|
405
415
|
render_or_respond(false)
|
406
416
|
end
|
data/lib/restful_json/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: restful_json
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.3.
|
4
|
+
version: 3.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gary S. Weaver
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -39,6 +39,20 @@ dependencies:
|
|
39
39
|
- - ! '>='
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: simplecov
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
42
56
|
description: Develop declarative, featureful JSON RESTful-ish service controllers
|
43
57
|
to use with modern Javascript MVC frameworks like AngularJS, Ember, etc. with much
|
44
58
|
less code.
|