restful_json 3.4.2 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- OTZlZGMwZTljNjIxNTlkN2JmMWRkNzcwYzljOGZkZWUzZDM0NjM4YQ==
4
+ ZjYzYjM4ODY4NjIyNWM4ZDdjMGQxODY0M2QyMGM5Y2MzMTVlMjFjZg==
5
5
  data.tar.gz: !binary |-
6
- MjdjZGQ4NDg3N2ZlOTk5NDM4NmViMDRjZmZmMGYxYTg5ODQxYzEzYg==
6
+ MjA0NzNiYzc0OTJiMjU0OGE3ZjQ4ZWU5YmUyZTJhMTliYjdlZDg0ZA==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- NTk2ZDUyNjZmZDg5ZTVlZjFhODhlM2E2ZTA2OTAyNTJiYjcxMjJkNTY4Y2Fm
10
- Y2RiYjczYWU5MzIxNTA1NzQzNzQ1NmFlMmM1MmUzMzczMjVlNzNmYzYyZjk2
11
- NjJkZThhZDEwNTY4MDNlMzdjZWFkYmEzNmI2Y2E4MDhmZDQwMzI=
9
+ YjIxMjNjYTFhZmM5M2E4Yjg3Mzc4MmMzZDA3MTIyMTgxMzYyZjBjZTRmODJi
10
+ YjgyOWI3M2EzMGIzNGI4ZmI5YzI3ODRmZDBiMDc1NGMxY2IzNTBmYTFmMmEy
11
+ ZjAzZmRlMjAzMWIyYWRmODVlYmFlNzhiODg0YTBmMWQ1Y2JiNTI=
12
12
  data.tar.gz: !binary |-
13
- YWE4NWE1MzBmZmEzNjkzNzY1MDE2NmZhMWIzZGVmNzMwNDEwZWI0OWQzYmVm
14
- M2FlMzY1YzliNTU0MGNmZDEzOGY0NzNmMmQ1YzE3N2YzMTU0ZTMxOWZmOGNl
15
- NWJhZmU0MzkyMDM5ZTMwNmE0OWUxOTkzMzFkYjc4NzcxOGJhNjU=
13
+ NDJlYjExZThhNzU0OWJmYmJiOTQ3YTI3ZmQ0NDZhNDU4YmQxODYzYTk2ODBi
14
+ NzgzNGZlOGJkMzZlMGJmODU3NDdkN2JhOTkxZDljZjc2NWE5ODliZTk4YjEy
15
+ NzM0NzFiODEzZDNhNWM1ZDdiNjdjNGEwOTBmZjNjYzY4NGUzNjk=
data/README.md CHANGED
@@ -1,265 +1,331 @@
1
- # Restful JSON [![Build Status](https://secure.travis-ci.org/rubyservices/restful_json.png?branch=master)](http://travis-ci.org/rubyservices/restful_json)
1
+ [![Build Status](https://secure.travis-ci.org/rubyservices/restful_json.png?branch=master)](http://travis-ci.org/rubyservices/restful_json) [![Gem Version](https://badge.fury.io/rb/restful_json.png)](http://badge.fury.io/rb/restful_json)
2
+ # Restful JSON
2
3
 
3
- Develop declarative, featureful RESTful-ish JSON service controllers to use with modern Javascript MVC frameworks like AngularJS, Ember, etc. with much less code. (I say "RESTful-ish" instead of RESTful, to differentiate them from true-REST, hypermedia-driven projects, but restful_json controllers are RESTful by the existing Rails definition of being RESTful, using the same actions and resourceful routes, but with more abilities.)
4
+ Develop declarative, featureful JSON service controllers to use with modern Javascript MVC frameworks like AngularJS, Ember, etc. with much less code. It is RESTful-ish instead of RESTful, since it isn't hypermedia-driven, but it meets the long-standing Rails definition of being RESTful.
4
5
 
5
6
  What does that mean? It means you typically won't have to write index, create, update, destroy, etc. methods in your controllers to filter, sort, and do complex queries.
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
+ 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. Your controllers will be easier to read, and there will be less code to maintain. When you need an action method more customized, that method is all you will have to write.
8
9
 
9
10
  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
11
 
11
12
  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
 
13
14
  You can use any of these for the JSON response (the view):
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).
15
- * JBuilder - to use, set render_enabled in the restful_json config to false.
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
+ * [ActiveModel::Serializers][active_model_serializers]
16
+ * [JBuilder][jbuilder]
17
+ * Almost anything else that will work with render/respond_with without anything special in the controller action method implementation.
17
18
 
18
19
  And can use any of the following for authorizing parameters in the incoming JSON (for create/update):
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.
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.
20
+ * [Permitters][permitters]
21
+ * [Strong Parameters][strong_parameters]
21
22
  * Mass assignment security in Rails 3.x (attr_accessible, etc.).
22
23
 
23
24
  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].
24
25
 
25
- ### Installation
26
-
27
- In your Rails app's `Gemfile`:
26
+ ### Usage
28
27
 
29
- gem 'restful_json', '~> 3.4.2'
28
+ 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.
30
29
 
31
- And if you go with the defaults to use ActiveModel::Serializers and Permitters (using Strong Parameters and Cancan):
30
+ Everything is well-declared and fairly concise.
32
31
 
33
- # comment this out if you don't want to use Strong Parameters or Permitters, or if you are using Rails 4, which includes it
34
- gem 'strong_parameters', '~> 0.2.0'
35
- # comment this out if you don't plan to use Permitters
36
- gem 'cancan', '~> 1.6.9'
37
- # comment this out if you don't plan to use ActiveModel::Serializers
38
- gem 'active_model_serializers', '~> 0.7.0'
39
-
40
- Then:
32
+ You can have something as simple as:
41
33
 
42
- bundle install
34
+ ```ruby
35
+ class FoobarsController < ApplicationController
36
+ include RestfulJson::DefaultController
37
+ end
38
+ ```
43
39
 
44
- #### Strong Parameters
40
+ 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 ...`.
45
41
 
46
- Strong Parameters is not required, but can be used on its own or as a dependency of Permitters.
42
+ Or, you can define many more bells and whistles with declarative ARel through HTTP(S):
43
+
44
+ ```ruby
45
+ class FoobarsController < ApplicationController
46
+ include RestfulJson::DefaultController
47
+
48
+ query_for :index, is: ->(t,q) {q.joins(:apples, :pears).where(apples: {color: 'green'}).where(pears: {color: 'green'})}
49
+
50
+ # use can_filter_by followed by the request parameter name(s)
51
+
52
+ # implies using: [:eq] because RestfulJson.can_filter_by_default_using = [:eq]
53
+ can_filter_by :foo_id
54
+
55
+ can_filter_by :foo_date, :bar_date, using: [:lt, :eq, :gt], with_default: Time.now
56
+
57
+ can_filter_by :a_request_param_name, with_query: ->(t,q,param_value) {q.joins(:some_assoc).where(:some_assocs_table_name=>{some_attr: param_value})}
58
+
59
+ can_filter_by :and_another, through: [:some_attribute_on_this_model]
60
+
61
+ can_filter_by :one_more, through: [:some_association, :some_attribute_on_some_association_model]
62
+
63
+ can_filter_by :and_one_more, through: [:my_assoc, :my_assocs_assoc, :my_assocs_assocs_assoc, :an_attribute_on_my_assocs_assocs_assoc]
64
+
65
+ supports_functions :count, :uniq, :take, :skip, :page, :page_count
66
+
67
+ order_by {:foo_date => :asc}, :foo_color, {:bar_date => :desc} # assumes :asc for foo_color, since hash not provided
68
+
69
+ serialize_action :index, with: ListFoobarSerializer
70
+
71
+ # optional. by default acts like Rails and will serve in json or html
72
+ respond_to :json, :html
73
+
74
+ end
75
+ ```
76
+
77
+ Now you can query like these:
78
+
79
+ ```
80
+ https://example.org/foobars?foo_id=123
81
+ https://example.org/foobars?bar_date!gt=2012-08-08
82
+ https://example.org/foobars?bar_date!gt=2012-08-08&count=
83
+ https://example.org/foobars?and_one_more=an_attribute_value_on_my_assocs_assocs_assoc&uniq=
84
+ https://example.org/foobars?page_count=
85
+ https://example.org/foobars?page=1
86
+ https://example.org/foobars?skip=30&take=15
87
+ ```
47
88
 
48
- If you are using Rails 4.x, you might be able to skip this section, as [Strong Parameters][strong_parameters] is included.
89
+ ### Installation
49
90
 
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]
91
+ In your Rails app's `Gemfile`:
51
92
 
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:
93
+ ```ruby
94
+ gem 'restful_json', '~> 4.0.0'
95
+ ```
53
96
 
54
- config.active_record.whitelist_attributes = false
97
+ Then:
55
98
 
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.
99
+ ```
100
+ bundle install
101
+ ```
57
102
 
58
- Strong Parameters (and Permitters) require a model include.
103
+ #### Optional
59
104
 
60
- Put this in each model you want to use Strong Parameters with:
105
+ ##### Strong Parameters
61
106
 
62
- include ActiveModel::ForbiddenAttributesProtection
107
+ [Strong Parameters][strong_parameters] is part of Rails 4, so *don't* include this gem if using Rails 4. However, if you are using Rails 3, it can be used on its own or as a dependency of Permitters:
63
108
 
64
- If you'd rather use Strong Parameters with all models, just put this in your `config/environment.rb`:
109
+ ```ruby
110
+ gem 'strong_parameters', '~> 0.2.0'
111
+ ```
65
112
 
66
- ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection)
113
+ Be sure to read the [Strong Parameters][strong_parameters] docs, because you need to use `config.active_record.whitelist_attributes = false` in your app config, etc. if using Rails 3. Also, this removes the need for `attr_accessible` or `attr_protected` in your models, so convert those restrictions to either Permitters or Strong Parameters. And you'll need `ActiveModel::ForbiddenAttributesProtection` included in your models.
67
114
 
68
- #### Cancan
115
+ As noted in [Strong Parameters][strong_parameters], it is suggested to encapsulate the permitting into a private method in the controller, so we allow:
69
116
 
70
- Though optional, if you decide to use Permitters, the Permitters framework relies on [Cancan][cancan].
117
+ ```ruby
118
+ def foobar_params
119
+ params.require(:foobar).permit(:name, :age)
120
+ end
121
+ ```
71
122
 
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.
123
+ or if `self.allow_action_specific_params_methods = true` is set in restful_json configuration, as it is by default:
73
124
 
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:
125
+ ```ruby
126
+ def create_foobar_params
127
+ params.require(:foobar).permit(:name, :age)
128
+ end
75
129
 
76
- class ApplicationController < ActionController::Base
77
- protect_from_forgery
130
+ def update_foobar_params
131
+ params.require(:foobar).permit(:age)
132
+ end
133
+ ```
78
134
 
79
- def current_user
80
- User.new
81
- end
82
- end
135
+ and even other actions if you want:
83
136
 
84
- Cancan integrates [Authlogic][authlogic], [Devise][devise], etc. to return a proper logged-in user or you can return it however you wish.
137
+ ```ruby
138
+ def index_foobars_params
139
+ params.require(:foobars).permit(:foo_id)
140
+ end
85
141
 
86
- Cancan also needs an Ability defined in `app/models/ability.rb`. Just for testing we'll ignore the user object and allow everything:
142
+ # where 'some_action' is a custom action created by query_for
143
+ def some_action_foobars_params
144
+ params.require(:foobars).permit(:foo_id)
145
+ end
87
146
 
88
- class Ability
89
- include CanCan::Ability
147
+ def show_foobar_params
148
+ params.require(:foobar).permit(:id)
149
+ end
150
+ ```
90
151
 
91
- def initialize(user)
92
- can :manage, :all
93
- end
94
- end
152
+ ##### Permitters
95
153
 
96
- Put this in each model you want to use Cancan with:
154
+ [Permitters][permitters] can use Strong Parameters and CanCan or another authorization solution for parameter authorization:
97
155
 
98
- include CanCan::ModelAdditions
156
+ ```ruby
157
+ gem 'permitters', '~> 0.0.1'
158
+ ```
99
159
 
100
- Or, if you'd rather use Cancan with all models, just put this in your `config/environment.rb`:
160
+ The default restful_json configuration is for only create and update actions to use permitters:
101
161
 
102
- ActiveRecord::Base.send(:include, CanCan::ModelAdditions)
162
+ ```ruby
163
+ self.actions_that_permit = [:create, :update]
164
+ ```
103
165
 
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.
166
+ Read the [Permitters][permitters] documentation for more info on how you can encapsulate and easily share permittance and authorization.
105
167
 
106
- #### JSON Response Generators
168
+ ##### CanCan
107
169
 
108
- ##### ActiveModel Serializers
170
+ [CanCan][cancan] can be used via Permitters or on its own:
109
171
 
110
- Use of [ActiveModel::Serializers][active_model_serializers] is optional, but a great way to have object-oriented model-like representation of JSON views.
172
+ ```ruby
173
+ gem 'cancan', '~> 1.6.9'
174
+ ```
111
175
 
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.
176
+ And [CanCan][cancan] supports [Authlogic][authlogic], [Devise][devise], etc. for authentication. See the [CanCan][cancan] docs for more info.
113
177
 
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.:
178
+ The default restful_json configuration is for `authorize!(permission, model)` to be called for create and update:
115
179
 
116
- /app/serializers/singular_model_name_serializer.rb
180
+ ```ruby
181
+ self.actions_that_authorize = [:create, :update]
182
+ ```
117
183
 
118
- Without having to do anything else, each restful_json controller will use `/app/serializers/singular_model_name_serializer.rb`, e.g. `/app/serializers/bar_serializer.rb` for the actions: index, show, new, create, update, destroy (not edit).
184
+ So, for example, when a create is attempted, it will first call `authorize!(:create, Foobar)`.
119
185
 
120
- If you want to define a different serializer another action, e.g. the index action so that a list of instances has a different JSON format:
186
+ `CanCan::ModelAdditions` needs to be included on any model that you plan to use CanCan with, per the CanCan documentation.
121
187
 
122
- serialize_action :index, with: BarsSerializer
188
+ ##### JBuilder
123
189
 
124
- You can also use a specific format for multiple actions:
190
+ [JBuilder][jbuilder] comes with Rails 4, or can be included in Rails 3 to provide JSON views. See [Railscast #320][railscast320] for more info on using JBuilder:
125
191
 
126
- serialize_action :index, :my_other_list_action, with: BarsSerializer
192
+ ```ruby
193
+ gem 'jbuilder', '~> 1.3.0'
194
+ ```
127
195
 
128
- 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 (keep reading!).
196
+ If you want to enable JBuilder for all restful_json services, you may want to disable all renders and respond_withs in the controller:
129
197
 
130
- You can just use the default serialization provided by AMS if you want. No class needed.
198
+ ```ruby
199
+ RestfulJson.render_enabled = false
200
+ ```
131
201
 
132
- Because of some issues with some versions of ActiveModel::Serializers using respond_with, you might want to set the option:
202
+ ##### ActiveModel::Serializers
133
203
 
134
- RestfulJson.avoid_respond_with = true
204
+ [ActiveModel::Serializers][active_model_serializers] is great for specifying what should go into the JSON responses as an alternative to JBuilder, and restful_json provides a `serialize_action` method to specify custom serializer if you don't want to use the default, e.g. `serialize_action :index, with: BarsSerializer` and `serialize_action :index, :my_other_list_action, with: BarsSerializer`.
135
205
 
136
- Otherwise, custom serializers, etc. might not be used as intended.
206
+ ```ruby
207
+ gem 'active_model_serializers', '~> 0.7.0'
208
+ ```
137
209
 
138
- ##### JBuilder
210
+ Because of some issues with some versions of ActiveModel::Serializers using respond_with, you might want to set the option:
139
211
 
140
- If you want to use JBuilder instead to render, first:
212
+ ```ruby
213
+ RestfulJson.avoid_respond_with = true
214
+ ```
141
215
 
142
- gem 'jbuilder'
216
+ ##### Mass Assignment Security
143
217
 
144
- If you want to enable JBuilder for all restful_json services, you need to disable all renders and respond_withs in the controller:
218
+ To use mass assignment security in Rails 3.x, specify this in restful_json config/controller config:
145
219
 
146
- RestfulJson.render_enabled = false
220
+ ```ruby
221
+ self.use_permitters = false
222
+ ```
147
223
 
148
- Or you can also just enable/disable rendering in a controller via setting `self.render_enabled`:
224
+ Don't use any of these, as they each include Strong Parameters:
149
225
 
150
- self.render_enabled = false
226
+ ```ruby
227
+ include ActionController::StrongParameters
228
+ include RestfulJson::DefaultController
229
+ ```
151
230
 
152
- Then make sure you add a JBuilder view for each action you require. Although all may not be relevant, we support: index, show, new, edit, create, update, destroy. Maybe you'd create:
231
+ Only the main controller is needed:
153
232
 
154
- /app/views/plural_name_of_model/index.json.jbuilder
155
- /app/views/plural_name_of_model/show.json.jbuilder
156
- /app/views/plural_name_of_model/create.json.jbuilder
157
- /app/views/plural_name_of_model/update.json.jbuilder
233
+ ```ruby
234
+ include RestfulJson::Controller
235
+ ```
158
236
 
159
- See [Railscast #320][railscast320] for more examples on setting up and using JBuilder.
237
+ Then, make sure that attr_accessible and/or attr_protected, etc. are used properly.
160
238
 
161
239
  ##### Other Options
162
240
 
163
241
  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.
164
242
 
165
- #### Create/Update JSON Request/Params Acceptance
166
-
167
- ##### Permitters
168
-
169
- We include ApplicationPermitter and optional controller support for Adam Hawkins' [permitters][permitter].
170
-
171
- The default setting is for permitters to be used:
172
-
173
- self.use_permitters = true
243
+ ### Application Configuration
174
244
 
175
- Permitters use [Cancan][cancan] for authorization and [Strong Parameters][strong_parameters] for parameter permitting.
245
+ At the bottom of `config/environment.rb`, you can set restful_json can be configured one line at a time.
176
246
 
177
- We have an implementation of ApplicationPermitter, so you just need permitters in `/app/permitters/`, e.g. `/app/permitters/foobar_permitter.rb`:
247
+ ```ruby
248
+ RestfulJson.debug = true
249
+ ```
178
250
 
179
- class FoobarPermitter < ApplicationPermitter
180
- # attributes we accept (the new way to do attr_accessible, OO-styley! Thanks, twinturbo)
181
- permit :id, :foo_id
182
- permit :bar_id
183
- permit :notes
184
- # foobar has accepts_nested_attributes_for :barfoos
185
- scope :barfoos_attributes do |barfoo|
186
- barfoo.permit :id, :favorite_color, :favorite_chicken
187
- end
188
- end
251
+ or in bulk, like:
189
252
 
190
- If you don't accept anything in create/update, you should have an empty Permitter for the model:
253
+ ```ruby
254
+ RestfulJson.configure do
191
255
 
192
- class FoobarPermitter < ApplicationPermitter
193
- end
256
+ # default for :using in can_filter_by
257
+ self.can_filter_by_default_using = [:eq]
194
258
 
195
- ##### Strong Parameters
259
+ # to log debug info during request handling
260
+ self.debug = false
196
261
 
197
- To use strong_parameters by themselves, without Permitters/Cancan, specify this in restful_json config/controller config:
262
+ # delimiter for values in request parameter values
263
+ self.filter_split = ','.freeze
198
264
 
199
- self.use_permitters = false
265
+ # equivalent to specifying respond_to :json, :html in the controller, and can be overriden in the controller. Note that by default responders gem sets respond_to :html in application_controller.rb.
266
+ self.formats = :json, :html
200
267
 
201
- As noted in [Strong Parameters][strong_parameters], it is suggested to encapsulate the permitting into a private method in the controller, so we've taken that to heart and the controller just attempts to call the relevant *_params method or create_*_params/update_*_params, e.g. in the controller:
268
+ # default number of records to return if using the page request function
269
+ self.number_of_records_in_a_page = 15
202
270
 
203
- def foobar_params
204
- params.require(:foobar).permit(:name, :age)
205
- end
271
+ # delimiter for ARel predicate in the request parameter name
272
+ self.predicate_prefix = '!'.freeze
206
273
 
207
- ##### Mass Assignment Security
274
+ # if true, will render resource and HTTP 201 for post/create or resource and HTTP 200 for put/update. ignored if render_enabled is false.
275
+ self.return_resource = false
208
276
 
209
- To use mass assignment security in Rails 3.x, specify this in restful_json config/controller config:
277
+ # if false, controller actions will just set instance variable and return it instead of calling setting instance variable and then calling render/respond_with
278
+ self.render_enabled = true
210
279
 
211
- self.use_permitters = false
280
+ # use Permitters
281
+ self.use_permitters = true
212
282
 
213
- Don't use any of these, as they each include Strong Parameters:
283
+ # instead of using Rails default respond_with, explicitly define render in respond_with block
284
+ self.avoid_respond_with = true
214
285
 
215
- include ActionController::StrongParameters
216
- include RestfulJson::DefaultController
217
- acts_as_restful_json
286
+ # use the permitter_class for create and update, if use_permitters = true
287
+ self.action_to_permitter = {create: nil, update: nil}
218
288
 
219
- Only the main controller is needed:
289
+ # the methods that call authorize! action_sym, @model_class
290
+ self.actions_that_authorize = [:create, :update]
291
+
292
+ # if not using permitters, will check respond_to?("(action)_(plural_or_singular_model_name)_params".to_sym) and if true will __send__(method)
293
+ self.allow_action_specific_params_methods = true
294
+
295
+ # if not using permitters, will check respond_to?("(singular_model_name)_params".to_sym) and if true will __send__(method)
296
+ self.actions_that_permit = [:create, :update]
220
297
 
221
- include RestfulJson::Controller
298
+ # in error JSON, break out the exception info into fields for debugging
299
+ self.return_error_data = true
222
300
 
223
- Then, make sure that attr_accessible and/or attr_protected, etc. are used properly.
301
+ # the class that is rescued in each action method, but if nil will always reraise and not handle
302
+ self.rescue_class = StandardError
303
+
304
+ # will define order of errors handled and what status and/or i18n message key to use
305
+ self.rescue_handlers = []
224
306
 
225
- ### Application Configuration
307
+ # rescue_handlers are an ordered array of handlers to handle rescue of self.rescue_class or sub types.
308
+ # can use optional i18n_key for message, but will default to e.message if i18n_key not found.
226
309
 
227
- At the bottom of `config/environment.rb`, you can set restful_json can be configured one line at a time.
310
+ # support 404 error for ActiveRecord::RecordNotFound if using ActiveRecord.
311
+ begin
312
+ require 'active_record/errors'
313
+ self.rescue_handlers << {exception_classes: [ActiveRecord::RecordNotFound], status: :not_found, i18n_key: 'api.not_found'.freeze}
314
+ rescue LoadError, NameError
315
+ end
228
316
 
229
- RestfulJson.debug = true
317
+ # support 403 error for CanCan::AccessDenied if using CanCan
318
+ begin
319
+ require 'cancan/exceptions'
320
+ self.rescue_handlers << {exception_classes: [CanCan::AccessDenied], status: :forbidden, i18n_key: 'api.not_found'.freeze}
321
+ rescue LoadError, NameError
322
+ end
230
323
 
231
- or in bulk, like:
324
+ # support 500 error for everything else that is a self.rescue_class (in action)
325
+ self.rescue_handlers << {status: :internal_server_error, i18n_key: 'api.internal_server_error'.freeze}
232
326
 
233
- RestfulJson.configure do
234
-
235
- # default for :using in can_filter_by
236
- self.can_filter_by_default_using = [:eq]
237
-
238
- # to output debugging info during request handling
239
- self.debug = false
240
-
241
- # delimiter for values in request parameter values
242
- self.filter_split = ','
243
-
244
- # equivalent to specifying respond_to :json, :html in the controller, and can be overriden in the controller. Note that by default responders gem sets respond_to :html in application_controller.rb.
245
- self.formats = :json, :html
246
-
247
- # default number of records to return if using the page request function
248
- self.number_of_records_in_a_page = 15
249
-
250
- # delimiter for ARel predicate in the request parameter name
251
- self.predicate_prefix = '!'
252
-
253
- # if true, will render resource and HTTP 201 for post/create or resource and HTTP 200 for put/update. ignored if render_enabled is false.
254
- self.return_resource = false
255
-
256
- # if false, controller actions will just set instance variable and return it instead of calling setting instance variable and then calling render/respond_with
257
- self.render_enabled = true
258
-
259
- # if false, will assume that it should either try calling create_(single model name)_params or fall back to calling (single model name)_params if create, or update_(single model name)_params then (single model name)_params if that didn't respond, if update. if it can't call those, it will either use mass assignment security, no parameter security, or some other solution, depending on how it is configured.
260
- self.use_permitters = true
261
-
262
- end
327
+ end
328
+ ```
263
329
 
264
330
  ### Controller Configuration
265
331
 
@@ -267,158 +333,163 @@ In the controller, you can set a variety of class attributes with `self.somethin
267
333
 
268
334
  All of the app-level configuration parameters are configurable at the controller level:
269
335
 
270
- self.can_filter_by_default_using = [:eq]
271
- self.debug = false
272
- self.filter_split = ','
273
- self.formats = :json, :html
274
- self.number_of_records_in_a_page = 15
275
- self.predicate_prefix = '!'
276
- self.return_resource = false
277
- self.render_enabled = true
278
- self.use_permitters = true
336
+ ```ruby
337
+ self.can_filter_by_default_using = [:eq]
338
+ self.debug = false
339
+ self.filter_split = ','.freeze
340
+ self.formats = :json, :html
341
+ self.number_of_records_in_a_page = 15
342
+ self.predicate_prefix = '!'.freeze
343
+ self.return_resource = false
344
+ self.render_enabled = true
345
+ self.use_permitters = true
346
+ self.avoid_respond_with = true
347
+ self.return_error_data = true
348
+ self.rescue_class = StandardError
349
+ self.action_to_permitter = {create: nil, update: nil}
350
+ self.actions_that_authorize = [:create, :update]
351
+ self.allow_action_specific_params_methods = true
352
+ self.actions_that_permit = [:create, :update]
353
+
354
+ require 'active_record/errors'
355
+ require 'cancan/exceptions'
356
+ self.rescue_handlers [
357
+ {exception_classes: [ActiveRecord::RecordNotFound], status: :not_found, i18n_key: 'api.not_found'.freeze},
358
+ {exception_classes: [CanCan::AccessDenied], status: :forbidden, i18n_key: 'api.not_found'.freeze},
359
+ {status: :internal_server_error, i18n_key: 'api.internal_server_error'.freeze}
360
+ ]
361
+ ```
279
362
 
280
363
  In addition there are some that are controller-only...
281
364
 
282
365
  If you don't use the standard controller naming convention, you can define this in the controller:
283
366
 
284
- self.model_class = YourModel
367
+ ```ruby
368
+ self.model_class = YourModel
369
+ ```
285
370
 
286
371
  If it doesn't handle the other forms well, you can explicitly define the singular/plural names:
287
372
 
288
- self.model_singular_name = 'your_model'
289
- self.model_plural_name = 'your_models'
373
+ ```ruby
374
+ self.model_singular_name = 'your_model'
375
+ self.model_plural_name = 'your_models'
376
+ ```
290
377
 
291
378
  These are used for *_url method definitions, to set instance variables like `@foobar` and `@foobars` dynamically, etc.
292
379
 
293
380
  Other class attributes are available for setting/overriding, but they are all set by the other class methods defined in the next section.
294
381
 
295
- ### Usage
296
-
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.
298
-
299
- Everything is well-declared and fairly concise.
300
-
301
- You can have something as simple as:
302
-
303
- class FoobarsController < ApplicationController
304
- include RestfulJson::DefaultController
305
- end
306
-
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 ...`.
308
-
309
- Or, you can define many more bells and whistles:
310
-
311
- class FoobarsController < ApplicationController
312
- include RestfulJson::DefaultController
313
-
314
- query_for :index, is: ->(t,q) {q.joins(:apples, :pears).where(apples: {color: 'green'}).where(pears: {color: 'green'})}
315
-
316
- # args sent to can_filter_by are the request parameter name(s)
317
-
318
- # implies using: [:eq] because RestfulJson.can_filter_by_default_using = [:eq]
319
- can_filter_by :foo_id
320
-
321
- # can specify multiple predicates and optionally a default value
322
- can_filter_by :foo_date, :bar_date, using: [:lt, :eq, :gt], with_default: Time.now
323
-
324
- can_filter_by :a_request_param_name, with_query: ->(t,q,param_value) {q.joins(:some_assoc).where(:some_assocs_table_name=>{some_attr: param_value})}
325
-
326
- can_filter_by :and_another, through: [:some_attribute_on_this_model]
327
-
328
- can_filter_by :one_more, through: [:some_association, :some_attribute_on_some_association_model]
329
-
330
- can_filter_by :and_one_more, through: [:my_assoc, :my_assocs_assoc, :my_assocs_assocs_assoc, :an_attribute_on_my_assocs_assocs_assoc]
331
-
332
- supports_functions :count, :uniq, :take, :skip, :page, :page_count
333
-
334
- order_by {:foo_date => :asc}, :foo_color, {:bar_date => :desc} # an ordered array of hashes, assumes :asc if not a hash
335
-
336
- serialize_action :index, with: ListFoobarSerializer
337
-
338
- # comma-delimited if you want more than :json, e.g. :json, :html
339
- respond_to :json, :html
340
-
341
- end
342
-
343
382
  #### Routing
344
383
 
345
384
  You can just add normal Rails RESTful routes in `config/routes.rb`, e.g. for the Foobar model:
346
385
 
347
- MyAppName::Application.routes.draw do
348
- resources :foobars
349
- end
386
+ ```ruby
387
+ MyAppName::Application.routes.draw do
388
+ resources :foobars
389
+ end
390
+ ```
350
391
 
351
392
  Supports static, nested, etc. routes also, e.g.:
352
393
 
353
- MyAppName::Application.routes.draw do
354
- namespace :my_service_controller_module do
355
- resources :foobars
356
- end
357
- end
394
+ ```ruby
395
+ MyAppName::Application.routes.draw do
396
+ namespace :my_service_controller_module do
397
+ resources :foobars
398
+ end
399
+ end
400
+ ```
358
401
 
359
402
  Can pass in params from the path for use in filters, etc. as if they were request parameters:
360
403
 
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
404
+ ```ruby
405
+ MyAppName::Application.routes.draw do
406
+ namespace :my_service_controller_module do
407
+ match 'bar/:bar_id/foobars(.:format)' => 'foobars#index'
408
+ end
409
+ end
410
+ ```
366
411
 
367
412
  #### Default Filtering by Attribute(s)
368
413
 
369
414
  First, declare in the controller:
370
415
 
371
- can_filter_by :foo_id
416
+ ```ruby
417
+ can_filter_by :foo_id
418
+ ```
372
419
 
373
420
  If `RestfulJson.can_filter_by_default_using = [:eq]` as it is by default, then you can now get Foobars with a foo_id of '1':
374
421
 
375
- http://localhost:3000/foobars?foo_id=1
422
+ ```
423
+ http://localhost:3000/foobars?foo_id=1
424
+ ```
376
425
 
377
426
  `can_filter_by` without an option means you can send in that request param (via routing or directly, just like normal in Rails) and it will use that in the ARel query (safe from SQL injection and only letting you do what you tell it). `:using` means you can use those [ARel][arel] predicates for filtering. If you do `Arel::Predications.public_instance_methods.sort` in Rails console, you can see a list of the available predicates. So, you could get crazy with:
378
427
 
379
- can_filter_by :does_not_match, :does_not_match_all, :does_not_match_any, :eq, :eq_all, :eq_any, :gt, :gt_all, :gt_any, :gteq, :gteq_all, :gteq_any, :in, :in_all, :in_any, :lt, :lt_all, :lt_any, :lteq, :lteq_all, :lteq_any, :matches, :matches_all, :matches_any, :not_eq, :not_eq_all, :not_eq_any, :not_in, :not_in_all, :not_in_any
428
+ ```ruby
429
+ can_filter_by :does_not_match, :does_not_match_all, :does_not_match_any, :eq, :eq_all, :eq_any, :gt, :gt_all, :gt_any, :gteq, :gteq_all, :gteq_any, :in, :in_all, :in_any, :lt, :lt_all, :lt_any, :lteq, :lteq_all, :lteq_any, :matches, :matches_all, :matches_any, :not_eq, :not_eq_all, :not_eq_any, :not_in, :not_in_all, :not_in_any
430
+ ```
380
431
 
381
432
  `can_filter_by` can also specify a `:with_query` to provide a lambda that takes the request parameter in when it is provided by the request.
382
433
 
383
- can_filter_by :a_request_param_name, with_query: ->(t,q,param_value) {q.joins(:some_assoc).where(:some_assocs_table_name=>{some_attr: param_value})}
434
+ ```ruby
435
+ can_filter_by :a_request_param_name, with_query: ->(t,q,param_value) {q.joins(:some_assoc).where(:some_assocs_table_name=>{some_attr: param_value})}
436
+ ```
384
437
 
385
438
  And `can_filter_by` can specify a `:through` to provide an easy way to inner join through a bunch of models using ActiveRecord relations, by specifying 0-to-many association names to go "through" to the final argument, which is the attribute name on the last model. The following is equivalent to the last query:
386
439
 
387
- can_filter_by :a_request_param_name, through: [:some_assoc, :some_attr]
440
+ ```ruby
441
+ can_filter_by :a_request_param_name, through: [:some_assoc, :some_attr]
442
+ ```
388
443
 
389
444
  Let's say you are in MagicalValleyController, and the MagicalValley model `has many :magical_unicorns`. The MagicalUnicorn model has an attribute called `name`. You want to return MagicalValleys that are associated with all of the MagicalUnicorns named 'Rainbow'. You could do either:
390
445
 
391
- can_filter_by :magical_unicorn_name, with_query: ->(t,q,param_value) {q.joins(:magical_unicorns).where(:magical_unicorns=>{name: param_value})}
446
+ ```ruby
447
+ can_filter_by :magical_unicorn_name, with_query: ->(t,q,param_value) {q.joins(:magical_unicorns).where(:magical_unicorns=>{name: param_value})}
448
+ ```
392
449
 
393
450
  or:
394
451
 
395
- can_filter_by :magical_unicorn_name, through: [:magical_unicorns, :name]
452
+ ```ruby
453
+ can_filter_by :magical_unicorn_name, through: [:magical_unicorns, :name]
454
+ ```
396
455
 
397
456
  and you can then use this:
398
457
 
399
- http://localhost:3000/magical_valleys?magical_unicorn_name=Rainbow
458
+ ```
459
+ http://localhost:3000/magical_valleys?magical_unicorn_name=Rainbow
460
+ ```
400
461
 
401
462
  or if a MagicalUnicorn `has_many :friends` and a MagicalUnicorn's friend has a name attribute:
402
463
 
403
- can_filter_by :magical_unicorn_friend_name, through: [:magical_unicorns, :friends, :name]
464
+ ```ruby
465
+ can_filter_by :magical_unicorn_friend_name, through: [:magical_unicorns, :friends, :name]
466
+ ```
404
467
 
405
468
  and use this to get valleys associated with unicorns who in turn have a friend named Oscar:
406
469
 
407
- http://localhost:3000/magical_valleys?magical_unicorn_friend_name=Oscar
470
+ ```
471
+ http://localhost:3000/magical_valleys?magical_unicorn_friend_name=Oscar
472
+ ```
408
473
 
409
474
  #### Other Filters by Attribute(s)
410
475
 
411
476
  First, declare in the controller:
412
477
 
413
- can_filter_by :seen_on, using: [:gteq, :eq_any]
478
+ ```ruby
479
+ can_filter_by :seen_on, using: [:gteq, :eq_any]
480
+ ```
414
481
 
415
482
  Get Foobars with seen_on of 2012-08-08 or later using the [ARel][arel] gteq predicate splitting the request param on `predicate_prefix` (configurable), you'd use:
416
483
 
417
- http://localhost:3000/foobars?seen_on!gteq=2012-08-08
484
+ ```
485
+ http://localhost:3000/foobars?seen_on!gteq=2012-08-08
486
+ ```
418
487
 
419
488
  Multiple values are separated by `filter_split` (configurable):
420
489
 
421
- http://localhost:3000/foobars?seen_on!eq_any=2012-08-08,2012-09-09
490
+ ```
491
+ http://localhost:3000/foobars?seen_on!eq_any=2012-08-08,2012-09-09
492
+ ```
422
493
 
423
494
  #### Supported Functions
424
495
 
@@ -430,185 +501,266 @@ Multiple values are separated by `filter_split` (configurable):
430
501
 
431
502
  First, declare in the controller:
432
503
 
433
- supports_functions :uniq
504
+ ```ruby
505
+ supports_functions :uniq
506
+ ```
434
507
 
435
508
  Now this works:
436
509
 
437
- http://localhost:3000/foobars?uniq=
510
+ ```
511
+ http://localhost:3000/foobars?uniq=
512
+ ```
438
513
 
439
514
  ##### Count
440
515
 
441
516
  First, declare in the controller:
442
517
 
443
- supports_functions :count
518
+ ```ruby
519
+ supports_functions :count
520
+ ```
444
521
 
445
522
  Now this works:
446
523
 
447
- http://localhost:3000/foobars?count=
524
+ ```
525
+ http://localhost:3000/foobars?count=
526
+ ```
448
527
 
449
528
  ##### Paging
450
529
 
451
530
  First, declare in the controller:
452
531
 
453
- supports_functions :page, :page_count
532
+ ```ruby
533
+ supports_functions :page, :page_count
534
+ ```
454
535
 
455
536
  Now you can get the page count:
456
537
 
457
- http://localhost:3000/foobars?page_count=
538
+ ```
539
+ http://localhost:3000/foobars?page_count=
540
+ ```
458
541
 
459
542
  And access each page of results:
460
543
 
461
- http://localhost:3000/foobars?page=1
462
- http://localhost:3000/foobars?page=2
463
- ...
544
+ ```
545
+ http://localhost:3000/foobars?page=1
546
+ http://localhost:3000/foobars?page=2
547
+ ```
464
548
 
465
549
  To set page size at application level:
466
550
 
467
- RestfulJson.number_of_records_in_a_page = 15
551
+ ```ruby
552
+ RestfulJson.number_of_records_in_a_page = 15
553
+ ```
468
554
 
469
555
  To set page size at controller level:
470
556
 
471
- self.number_of_records_in_a_page = 15
557
+ ```ruby
558
+ self.number_of_records_in_a_page = 15
559
+ ```
472
560
 
473
561
  ##### Skip and Take (OFFSET and LIMIT)
474
562
 
475
563
  First, declare in the controller:
476
564
 
477
- supports_functions :skip, :take
565
+ ```ruby
566
+ supports_functions :skip, :take
567
+ ```
478
568
 
479
569
  To skip rows returned, use 'skip'. It is called take, because skip is the [ARel][arel] equivalent of SQL OFFSET:
480
570
 
481
- http://localhost:3000/foobars?skip=5
571
+ ```
572
+ http://localhost:3000/foobars?skip=5
573
+ ```
482
574
 
483
575
  To limit the number of rows returned, use 'take'. It is called take, because take is the [ARel][arel] equivalent of SQL LIMIT:
484
576
 
485
- http://localhost:3000/foobars.json?take=5
577
+ ```
578
+ http://localhost:3000/foobars.json?take=5
579
+ ```
486
580
 
487
581
  Combine skip and take for manual completely customized paging, e.g.
488
582
 
489
- http://localhost:3000/foobars?take=15
490
- http://localhost:3000/foobars?skip=15&take=15
491
- http://localhost:3000/foobars?skip=30&take=15
583
+ ```
584
+ http://localhost:3000/foobars?take=15
585
+ http://localhost:3000/foobars?skip=15&take=15
586
+ http://localhost:3000/foobars?skip=30&take=15
587
+ ```
588
+
589
+ ##### Custom Queries
590
+
591
+ To filter the list where the status_code attribute is 'green':
592
+
593
+ ```ruby
594
+ # t is self.model_class.arel_table and q is self.model_class.scoped
595
+ query_for :index, is: lambda {|t,q| q.where(:status_code => 'green')}
596
+ ```
597
+
598
+ or use the `->` Ruby 1.9 lambda stab operator (note lack of whitespace between stab and parenthesis):
599
+
600
+ ```ruby
601
+ # t is self.model_class.arel_table and q is self.model_class.scoped
602
+ query_for :index, is: is: ->(t,q) {q.where(:status_code => 'green')}
603
+ ```
604
+
605
+ 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:
606
+
607
+ ```ruby
608
+ # t is self.model_class.arel_table and q is self.model_class.scoped
609
+ # note: must be no space between -> and parenthesis
610
+ query_for :index, is: ->(t,q) {
611
+ q.joins(:apples, :pears)
612
+ .where(apples: {color: 'green'})
613
+ .where(pears: {color: 'green'})
614
+ }
615
+ ```
616
+
617
+ ##### Define Custom Actions with Custom Queries
618
+
619
+ You are still working with regular controllers here, so add or override methods if you want more!
620
+
621
+ However `query_for` will create new action methods, so you can easily create custom non-RESTful action methods:
622
+
623
+ ```ruby
624
+ # t is self.model_class.arel_table and q is self.model_class.scoped
625
+ # note: must be no space between -> and parenthesis in lambda syntax!
626
+ query_for :some_action, is: ->(t,q) {q.where(:status_code => 'green')}
627
+ ```
628
+
629
+ 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.
630
+
631
+ ```ruby
632
+ query_for :some_action, is: ->(t,q) do
633
+ if @current_user.admin?
634
+ Rails.logger.debug("Notice: unfiltered results provided to admin #{@current_user.name}")
635
+ # just make sure the relation is returned!
636
+ q
637
+ else
638
+ q.where(:access => 'public')
639
+ end
640
+ end
641
+ ```
642
+
643
+ Be sure to add a route for that action, e.g. in `config/routes.rb`, e.g. for the Barfoo model:
644
+
645
+ ```ruby
646
+ MyAppName::Application.routes.draw do
647
+ resources :barfoos do
648
+ get 'some_action', :on => :collection
649
+ end
650
+ end
651
+ ```
492
652
 
493
653
  ##### Custom Serializers
494
654
 
495
655
  If using ActiveModel::Serializers, you can use something other than the `(singular model name)Serializer` via `serialize_action`:
496
656
 
497
- serialize_action :index, with: ListFoobarSerializer
657
+ ```ruby
658
+ serialize_action :index, with: ListFoobarSerializer
659
+ ```
498
660
 
499
661
  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.
500
662
 
501
663
  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.:
502
664
 
503
- serialize_action :index, :some_custom_action, with: FoosSerializer, for: :array
665
+ ```ruby
666
+ serialize_action :index, :some_custom_action, with: FoosSerializer, for: :array
667
+ ```
504
668
 
505
669
  Or, you could just use the default serialization, if you want.
506
670
 
507
- ##### Custom Queries
671
+ ##### Custom Permitters
508
672
 
509
- To filter the list where the status_code attribute is 'green':
673
+ If using Permitters, you can use something other than the `(singular model name)Permitter` via `permit_action`:
510
674
 
511
- # t is self.model_class.arel_table and q is self.model_class.scoped
512
- query_for :index, is: lambda {|t,q| q.where(:status_code => 'green')}
675
+ ```ruby
676
+ permit_action :index, with: ListFoobarPermitter
677
+ ```
513
678
 
514
- or use the `->` Ruby 1.9 lambda stab operator (note lack of whitespace between stab and parenthesis):
679
+ The built-in actions that support custom permitters (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.
515
680
 
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')}
681
+ The default configuration specifies permitter as `nil` which indicates the default of `(singular model name)Permitter`:
518
682
 
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:
683
+ ```ruby
684
+ self.action_to_permitter = {create: nil, update: nil}
685
+ ```
520
686
 
521
- # t is self.model_class.arel_table and q is self.model_class.scoped
522
- # note: must be no space between -> and parenthesis
523
- query_for :index, is: ->(t,q) {
524
- q.joins(:apples, :pears)
525
- .where(apples: {color: 'green'})
526
- .where(pears: {color: 'green'})
527
- }
528
-
529
- ##### Define Custom Actions with Custom Queries
530
-
531
- You are still working with regular controllers here, so add or override methods if you want more!
687
+ By using that app or controller config parameter, you can define default permitter classes for other actions that restful_json manages if you wish.
532
688
 
533
- However `query_for` will create new action methods, so you can easily create custom non-RESTful action methods:
689
+ ##### Strong Parameters Params Methods
534
690
 
535
- # t is self.model_class.arel_table and q is self.model_class.scoped
536
- # note: must be no space between -> and parenthesis in lambda syntax!
537
- query_for :some_action, is: ->(t,q) {q.where(:status_code => 'green')}
691
+ Strong Parameters documentation suggests encapsulating permitting into a private method in the controller. We make this suggestion a convention to make development easier.
538
692
 
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.
693
+ By convention, a restful_json controller can call the `(singular model name)_params` method for create and update actions. This is configured via:
540
694
 
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
695
+ ```ruby
696
+ self.actions_supporting_params_methods = [:create, :update]
697
+ ```
550
698
 
551
- Be sure to add a route for that action, e.g. in `config/routes.rb`, e.g. for the Barfoo model:
699
+ And by default restful_json allows action specific `(action)_(model)_params` methods, so you only need to define a method like `create_foobar_params` and it will try to call that on create:
552
700
 
553
- MyAppName::Application.routes.draw do
554
- resources :barfoos do
555
- get 'some_action', :on => :collection
556
- end
557
- end
701
+ ```ruby
702
+ self.allow_action_specific_params_methods = true
703
+ ```
558
704
 
559
705
  ### With Rails-api
560
706
 
561
707
  If you want to try out [rails-api][rails-api]:
562
708
 
563
- gem 'rails-api', '~> 0.0.3'
709
+ ```ruby
710
+ gem 'rails-api', '~> 0.0.3'
711
+ ```
564
712
 
565
713
  In `app/controllers/my_service_controller.rb`:
566
714
 
567
- module MyServiceController
568
- extend ActiveSupport::Concern
569
-
570
- included do
571
- # Rails-api lets you choose features. You might not need all of these, or may need others.
572
- include AbstractController::Translation
573
- include ActionController::HttpAuthentication::Basic::ControllerMethods
574
- include AbstractController::Layouts
575
- include ActionController::MimeResponds
576
- include ActionController::Cookies
577
- include ActionController::ParamsWrapper
578
-
579
- # use Permitters and AMS
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
586
-
587
- # If you want any additional inline class stuff, it goes here...
588
- end
589
- end
715
+ ```ruby
716
+ module MyServiceController
717
+ extend ActiveSupport::Concern
718
+
719
+ included do
720
+ # Rails-api lets you choose features. You might not need all of these, or may need others.
721
+ include AbstractController::Translation
722
+ include ActionController::HttpAuthentication::Basic::ControllerMethods
723
+ include AbstractController::Layouts
724
+ include ActionController::MimeResponds
725
+ include ActionController::Cookies
726
+ include ActionController::ParamsWrapper
727
+
728
+ # use Permitters and AMS
729
+ include RestfulJson::DefaultController
730
+ # or comment that last line and uncomment whatever you want to use
731
+ #include ::ActionController::Serialization # AMS
732
+ #include ::ActionController::StrongParameters
733
+ #include ::TwinTurbo::Controller # Permitters which uses CanCan and Strong Parameters
734
+ #include ::RestfulJson::Controller
590
735
 
591
- class FoobarsController < ActionController::API
592
- include MyServiceController
593
- end
736
+ # If you want any additional inline class stuff, it goes here...
737
+ end
738
+ end
594
739
 
595
- class BarfoosController < ActionController::API
596
- include MyServiceController
597
- end
740
+ class FoobarsController < ActionController::API
741
+ include MyServiceController
742
+ end
743
+
744
+ class BarfoosController < ActionController::API
745
+ include MyServiceController
746
+ end
747
+ ```
598
748
 
599
749
  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:
600
750
 
601
- ActiveSupport.on_load(:action_controller) do
602
- # without include of ParamsWrapper, will get undefined method `wrap_parameters' for ActionController::API:Class (NoMethodError)
603
- include ActionController::ParamsWrapper
604
- # in this case it's expecting unwrapped params, but we could maybe use wrap_parameters format: [:json]
605
- wrap_parameters format: []
606
- end
751
+ ```ruby
752
+ ActiveSupport.on_load(:action_controller) do
753
+ # without include of ParamsWrapper, will get undefined method `wrap_parameters' for ActionController::API:Class (NoMethodError)
754
+ include ActionController::ParamsWrapper
755
+ # in this case it's expecting unwrapped params, but we could maybe use wrap_parameters format: [:json]
756
+ wrap_parameters format: []
757
+ end
607
758
 
608
- # Disable root element in JSON by default.
609
- ActiveSupport.on_load(:active_record) do
610
- self.include_root_in_json = false
611
- end
759
+ # Disable root element in JSON by default.
760
+ ActiveSupport.on_load(:active_record) do
761
+ self.include_root_in_json = false
762
+ end
763
+ ```
612
764
 
613
765
  ### Refactoring and Customing the Default Behavior
614
766
 
@@ -618,30 +770,34 @@ Don't subclass and include in the parent, that puts the class attributes into th
618
770
 
619
771
  Don't do this:
620
772
 
621
- class ServiceController < ApplicationController
622
- include ::ActionController::Serialization
623
- include ::ActionController::StrongParameters
624
- include ::TwinTurbo::Controller
625
- include ::RestfulJson::Controller
626
- end
627
-
628
- class FoobarsController < ServiceController
629
- end
630
-
631
- class BarfoosController < ServiceController
632
- end
773
+ ```ruby
774
+ class ServiceController < ApplicationController
775
+ include ::ActionController::Serialization
776
+ include ::ActionController::StrongParameters
777
+ include ::TwinTurbo::Controller
778
+ include ::RestfulJson::Controller
779
+ end
780
+
781
+ class FoobarsController < ServiceController
782
+ end
783
+
784
+ class BarfoosController < ServiceController
785
+ end
786
+ ```
633
787
 
634
788
  And don't do this:
635
789
 
636
- class FoobarsController < ApplicationController
637
- include RestfulJson::DefaultController
638
- end
639
-
640
- class FoobarsController < ServiceController
641
- end
642
-
643
- class BarfoosController < ServiceController
644
- end
790
+ ```ruby
791
+ class FoobarsController < ApplicationController
792
+ include RestfulJson::DefaultController
793
+ end
794
+
795
+ class FoobarsController < ServiceController
796
+ end
797
+
798
+ class BarfoosController < ServiceController
799
+ end
800
+ ```
645
801
 
646
802
  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).
647
803
 
@@ -649,57 +805,51 @@ It may appear to work when using the same controller or even on each new control
649
805
 
650
806
  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
807
 
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
667
- end
668
-
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
680
- end
808
+ ```ruby
809
+ module RestfulJson
810
+ module Controller
811
+
812
+ # class methods that should be implemented or overriden go in ClassMethods
681
813
 
814
+ module ClassMethods
815
+ def hello(name)
816
+ class_attribute :name, instance_writer: true
817
+ self.name = name
682
818
  end
683
819
  end
684
820
 
685
- Now in your controller, if you:
821
+ # instance methods that should be implemented or overriden.
686
822
 
687
- class FoobarsController < ApplicationController
688
- include RestfulJson::DefaultController
689
- hello 'world'
823
+ def index
824
+ render :json => {:hello => self.name}
690
825
  end
691
826
 
692
- (Note again: RestfulJson::DefaultController includes RestfulJson::Controller.)
827
+ end
828
+ end
829
+ ```
830
+
831
+ Now in your controller, if you:
832
+
833
+ ```ruby
834
+ class FoobarsController < ApplicationController
835
+ include RestfulJson::DefaultController
836
+ hello 'world'
837
+ end
838
+ ```
693
839
 
694
- Now when you call:
840
+ RestfulJson::DefaultController includes RestfulJson::Controller, which you patched, so when you call:
695
841
 
696
- http://localhost:3000/foobars
842
+ ```
843
+ http://localhost:3000/foobars
844
+ ```
697
845
 
698
846
  You would get the response:
699
847
 
700
- {'hello': 'world'}
848
+ ```
849
+ {'hello': 'world'}
850
+ ```
701
851
 
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.
852
+ 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. and modify as needed.
703
853
 
704
854
  ### Error Handling
705
855
 
@@ -707,34 +857,36 @@ For more realistic use that takes advantage of existing configuration in the con
707
857
 
708
858
  Some things restful_json can't do in the controller, like responding with json for a json request when the route is not setup correctly or an action is missing.
709
859
 
710
- Rails 4 has basic error handling defined in the [public_exceptions][public_exceptions] and [show_exceptions][show_exceptions] Rack middleware.
860
+ Rails 4 has basic error handling for non-HTML formats defined in the [public_exceptions][public_exceptions] and [show_exceptions][show_exceptions] Rack middleware.
711
861
 
712
862
  Rails 3.2.x has support for `config.exceptions_app` which can be defined as the following to simulate Rails 4 exception handling:
713
863
 
714
- config.exceptions_app = lambda do |env|
715
- exception = env["action_dispatch.exception"]
716
- status = env["PATH_INFO"][1..-1]
717
- request = ActionDispatch::Request.new(env)
718
- content_type = request.formats.first
719
- body = { :status => status, :error => exception.message }
720
- format = content_type && "to_#{content_type.to_sym}"
721
- if format && body.respond_to?(format)
722
- formatted_body = body.public_send(format)
723
- [status, {'Content-Type' => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}",
724
- 'Content-Length' => body.bytesize.to_s}, [formatted_body]]
725
- else
726
- found = false
727
- path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
728
- path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path))
729
-
730
- if found || File.exist?(path)
731
- [status, {'Content-Type' => "text/html; charset=#{ActionDispatch::Response.default_charset}",
732
- 'Content-Length' => body.bytesize.to_s}, [File.read(path)]]
733
- else
734
- [404, { "X-Cascade" => "pass" }, []]
735
- end
736
- end
864
+ ```ruby
865
+ config.exceptions_app = lambda do |env|
866
+ exception = env["action_dispatch.exception"]
867
+ status = env["PATH_INFO"][1..-1]
868
+ request = ActionDispatch::Request.new(env)
869
+ content_type = request.formats.first
870
+ body = { :status => status, :error => exception.message }
871
+ format = content_type && "to_#{content_type.to_sym}"
872
+ if format && body.respond_to?(format)
873
+ formatted_body = body.public_send(format)
874
+ [status, {'Content-Type' => "#{content_type}; charset=#{ActionDispatch::Response.default_charset}",
875
+ 'Content-Length' => body.bytesize.to_s}, [formatted_body]]
876
+ else
877
+ found = false
878
+ path = "#{public_path}/#{status}.#{I18n.locale}.html" if I18n.locale
879
+ path = "#{public_path}/#{status}.html" unless path && (found = File.exist?(path))
880
+
881
+ if found || File.exist?(path)
882
+ [status, {'Content-Type' => "text/html; charset=#{ActionDispatch::Response.default_charset}",
883
+ 'Content-Length' => body.bytesize.to_s}, [File.read(path)]]
884
+ else
885
+ [404, { "X-Cascade" => "pass" }, []]
737
886
  end
887
+ end
888
+ end
889
+ ```
738
890
 
739
891
  That is just a collapsed version of the behavior of [public_exceptions][public_exceptions] as of April 2013, pre-Rails 4.0.0, so please look at the latest version and adjust accordingly. Use at your own risk, obviously.
740
892
 
@@ -742,7 +894,15 @@ Unfortunately, this doesn't work for Rails 3.1.x. However, in many scenarios the
742
894
 
743
895
  But, if you can make Rack respond a little better for some errors, that's great.
744
896
 
745
- #### Controller Error Handling Configuration
897
+ To let all errors and exceptions fall out of restful_json action methods so that they will all be handled (without `error_data` in response) in the same way as routing, missing action, and other errors caught by Rack, just use:
898
+
899
+ ```ruby
900
+ RestfulJson.configure do
901
+ self.rescue_handlers = []
902
+ end
903
+ ```
904
+
905
+ #### Controller Error-handling Configuration
746
906
 
747
907
  The default configuration will rescue StandardError in each action method and will render as 404 for ActiveRecord::RecordNotFound or 500 for all other StandardError (and ancestors, like a normal rescue).
748
908
 
@@ -752,58 +912,99 @@ The `rescue_class` config option specifies what to rescue. Set to StandardError
752
912
 
753
913
  The `rescue_handlers` config option is like a minimalist set of rescue blocks that apply to every action method. For example, the following would effectively `rescue => e` (rescuing `StandardError`) and then for `ActiveRecord::RecordNotFound`, it would uses response status `:not_found` (HTTP 404). Otherwise it uses status `:internal_server_error` (HTTP 500). In both cases the error message is `e.message`:
754
914
 
755
- self.rescue_class = StandardError
756
- self.rescue_handlers = [
757
- {exception_classes: [ActiveRecord::RecordNotFound], status: :not_found},
758
- {status: :internal_server_error}
759
- ]
915
+ ```ruby
916
+ RestfulJson.configure do
917
+ self.rescue_class = StandardError
918
+ self.rescue_handlers = [
919
+ {exception_classes: [ActiveRecord::RecordNotFound], status: :not_found},
920
+ {status: :internal_server_error}
921
+ ]
922
+ end
923
+ ```
760
924
 
761
925
  In a slightly more complicated case, this configuration would catch all exceptions raised with each actinon method that had `ActiveRecord::RecordNotFound` as an ancestor and use the error message defined by i18n key 'api.not_found'. All other exceptions would use status `:internal_server_error` (because it is a default, and doesn't have to be specified) but would use the error message defined by i18n key 'api.internal_server_error':
762
926
 
763
- self.rescue_class = Exception
764
- self.rescue_handlers = [
765
- {exception_ancestor_classes: [ActiveRecord::RecordNotFound], status: :not_found, i18n_key: 'api.not_found'.freeze},
766
- {i18n_key: 'api.internal_server_error'.freeze}
767
- ]
768
-
927
+ ```ruby
928
+ RestfulJson.configure do
929
+ self.rescue_class = Exception
930
+ self.rescue_handlers = [
931
+ {exception_ancestor_classes: [ActiveRecord::RecordNotFound], status: :not_found, i18n_key: 'api.not_found'.freeze},
932
+ {i18n_key: 'api.internal_server_error'.freeze}
933
+ ]
934
+ end
935
+ ```
769
936
 
770
937
  The `return_error_data` config option will not only return a response with `status` and `error` but also an `error_data` containing the `e.class.name`, `e.message`, and cleaned `e.backtrace`.
771
938
 
939
+ If you want to rescue using `rescue_from` in a controller or ApplicationController, let all errors and exceptions fall out of restful_json action methods with:
940
+
941
+ ```ruby
942
+ RestfulJson.configure do
943
+ self.rescue_handlers = []
944
+ end
945
+ ```
946
+
772
947
  ### Release Notes
773
948
 
774
- #### restful_json v3.3
949
+ See the [changelog][changelog] for basically what happened when, and git log for everything else.
950
+
951
+ ### Upgrading
952
+
953
+ The class method:
954
+
955
+ ```ruby
956
+ acts_as_restful_json
957
+ ```
775
958
 
776
- In past versions, everything was done to the models whether you wanted it done or not. Have been trying to transition away from forcing anything, so starting with v3.3, ensure the following is done.
959
+ Which depended on [Permitters][permitters] for Rails 3.x can be replaced with:
777
960
 
778
- If you are using Rails 3.1-3.2 and want to use permitters or strong_parameters in all models:
961
+ ```ruby
962
+ include RestfulJson::DefaultController
963
+ ```
964
+
965
+ or if using Rails 4.x, you should consider including the modules separately so that you don't include the `ActionController::StrongParameters` module twice in a controller, for example.
966
+
967
+ Also, in past versions, everything was done to the models whether you wanted it done or not. Have been trying to transition away from forcing anything, so starting with v3.3, ensure the following is done.
968
+
969
+ If you are using Rails 3.1-3.2 and want to use Permitters or Strong Parameters in all models:
779
970
 
780
971
  Make sure you include Strong Parameters:
781
972
 
782
- gem "strong_parameters"
973
+ ```ruby
974
+ gem "strong_parameters"
975
+ ```
783
976
 
784
977
  Include this in `config/environment.rb`:
785
978
 
786
- ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection)
979
+ ```ruby
980
+ ActiveRecord::Base.send(:include, ActiveModel::ForbiddenAttributesProtection)
981
+ ```
787
982
 
788
- If you want to use permitters in all models, you need Cancan:
983
+ If you want to use Permitters in all models, you need CanCan:
789
984
 
790
- Make sure you include Cancan:
985
+ Make sure you include CanCan:
791
986
 
792
- gem "cancan"
987
+ ```ruby
988
+ gem "cancan"
989
+ ```
793
990
 
794
991
  Include this in `config/environment.rb`
795
992
 
796
- ActiveRecord::Base.send(:include, CanCan::ModelAdditions)
993
+ ```ruby
994
+ ActiveRecord::Base.send(:include, CanCan::ModelAdditions)
995
+ ```
996
+
997
+ Configuration, suggestions, and what to use and how may continue to change, but read this doc fully and hopefully it is correct!
797
998
 
798
999
  ### Rails Version-specific Eccentricities
799
1000
 
800
- Strong Parameters is included in Rails 4.
1001
+ Strong Parameters and JBuilder are included in Rails 4. You can use Permitters and ActiveModel::Serializers but for Permitters, you shouldn't define `gem 'strong_parameters'`.
801
1002
 
802
1003
  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.
803
1004
 
804
- ### Thanks!
1005
+ ### Contributing
805
1006
 
806
- Without our users, where would we be? Feedback, bug reports, and code/documentation contributions are always welcome!
1007
+ Please fork, make changes in a separate branch, and do a pull request for your branch. Thanks!
807
1008
 
808
1009
  ### Contributors
809
1010
 
@@ -816,7 +1017,8 @@ Copyright (c) 2013 Gary S. Weaver, released under the [MIT license][lic].
816
1017
 
817
1018
  [employee-training-tracker]: https://github.com/FineLinePrototyping/employee-training-tracker
818
1019
  [built_with_angularjs]: http://builtwith.angularjs.org/
819
- [permitter]: http://broadcastingadam.com/2012/07/parameter_authorization_in_rails_apis/
1020
+ [permitters]: https://github.com/permitters/permitters
1021
+ [jbuilder]: https://github.com/rails/jbuilder
820
1022
  [cancan]: https://github.com/ryanb/cancan
821
1023
  [strong_parameters]: https://github.com/rails/strong_parameters
822
1024
  [active_model_serializers]: https://github.com/josevalim/active_model_serializers
@@ -828,4 +1030,5 @@ Copyright (c) 2013 Gary S. Weaver, released under the [MIT license][lic].
828
1030
  [railscast320]: http://railscasts.com/episodes/320-jbuilder
829
1031
  [public_exceptions]: https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/public_exceptions.rb
830
1032
  [show_exceptions]: https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
1033
+ [changelog]: https://github.com/rubyservices/restful_json/blob/master/CHANGELOG.md
831
1034
  [lic]: http://github.com/rubyservices/restful_json/blob/master/LICENSE