restful_json 3.1.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -5,9 +5,15 @@ Develop declarative, featureful JSON RESTful-ish service controllers to use with
5
5
 
6
6
  Should work with Rails 3.1+ and Rails 4. Feel free to submit issues and/or do a pull requests if you run into anything.
7
7
 
8
- For the JSON response, you can either use [active_model_serializers][active_model_serializers], JBuilder, and perhaps others we haven't tested for control over the JSON response format. For active_model_serializers, it 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). For JBuilder, you can set render_enabled in the config to false.
8
+ Options for JSON response:
9
+ * [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).
10
+ * JBuilder - to use, set render_enabled in the restful_json config to false.
11
+ * Possibly 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.
9
12
 
10
- For the incoming JSON, it uses Adam Hawkin's [permitter][permitter] code which uses [strong_parameters][strong_parameters] and [cancan][cancan]. Permitters are an object-oriented way of defining what is permitted in the incoming JSON.
13
+ Options for authorizing parameters in incoming JSON (for POSTing create/update):
14
+ * 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 active_model_serializers are. Cancan supports [Authlogic][authlogic], [Devise][devise], etc.
15
+ * [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.
16
+ * Mass assignment security in Rails 3.x (attr_accessible, etc.).
11
17
 
12
18
  An example app using restful_json with AngularJS is [employee-training-tracker][employee-training-tracker], featured in [Built with AngularJS][built_with_angularjs].
13
19
 
@@ -38,11 +44,29 @@ If you don't want all of Rails (probably not the case), you at least need action
38
44
  gem 'actionpack', '> 3.1.0'
39
45
  gem 'activerecord', '> 3.1.0'
40
46
 
47
+ #### Strong Parameters
48
+
49
+ Strong Parameters is not required, but is used by default when `self.use_permitters = true`, and if you use `self.use_permitters = false` and specify a `create_(single model name)_params`, `update_(single model name)_params`, and/or `(single model name)_params` methods that use permit, etc. then it would be required.
50
+
51
+ To use [strong_parameters][strong_parameters], in your Gemfile, add:
52
+
53
+ gem 'strong_parameters', '~> 0.1.6'
54
+
55
+ If you're using Rails 3.1.x/3.2.x and you want to disable the default whitelisting that occurs in later versions of Rails because you are going to use Strong Parameters, change the config.active_record.whitelist_attributes property in your `config/application.rb`:
56
+
57
+ config.active_record.whitelist_attributes = false
58
+
59
+ This will allow you to remove / not have to use attr_accessible and do mass assignment inside your code and tests. Instead you will put this information into your permitters.
60
+
61
+ Note that if the Cancan gem is loaded, then, during Rails initialization, it will include `::CanCan::ModelAdditions` in `ActiveRecord::Base`.
62
+
41
63
  #### Cancan
42
64
 
65
+ Cancan is not required if `self.use_permitters = false` in config.
66
+
43
67
  To use [cancan][cancan], in your Gemfile, add:
44
68
 
45
- gem 'cancan', '~> 1.6.7'
69
+ gem 'cancan', '~> 1.6.8'
46
70
 
47
71
  In your `app/controllers/application_controller.rb` or in your service controllers, make sure `current_user` is set:
48
72
 
@@ -54,7 +78,7 @@ In your `app/controllers/application_controller.rb` or in your service controlle
54
78
  end
55
79
  end
56
80
 
57
- You could do that better by setting up some real authentication with [Authlogic][authlogic], [Devise][devise], or whatever cancan will support.
81
+ You could do that better by setting up some real authentication with [Authlogic][authlogic], [Devise][devise], or whatever Cancan will support.
58
82
 
59
83
  In `app/models/ability.rb`, setup a basic cancan ability. Just for testing we'll allow everything:
60
84
 
@@ -66,27 +90,15 @@ In `app/models/ability.rb`, setup a basic cancan ability. Just for testing we'll
66
90
  end
67
91
  end
68
92
 
69
- #### Strong Parameters
70
-
71
- To use [strong_parameters][strong_parameters], in your Gemfile, add:
72
-
73
- gem 'strong_parameters', '~> 0.1.3'
74
-
75
- If you're using Rails 3.1.x/3.2.x and you want to disable the default whitelisting that occurs in later versions of Rails because you are going to use Strong Parameters, change the config.active_record.whitelist_attributes property in your `config/application.rb`:
76
-
77
- config.active_record.whitelist_attributes = false
78
-
79
- This will allow you to remove / not have to use attr_accessible and do mass assignment inside your code and tests. Instead you will put this information into your permitters.
80
-
81
- Note that restful_json automatically includes `ActiveModel::ForbiddenAttributesProtection` on all models, as is done in Rails 4.
93
+ Note that if the Cancan gem is loaded, then, during Rails initialization, it will include `::CanCan::ModelAdditions` in `ActiveRecord::Base`.
82
94
 
83
95
  #### JSON Response Generators
84
96
 
85
97
  ##### ActiveModel Serializers
86
98
 
87
- If you want to use [ActiveModel Serializers][[active_model_serializers], use:
99
+ If you want to use [ActiveModel Serializers][active_model_serializers], use:
88
100
 
89
- gem 'active_model_serializers'
101
+ gem 'active_model_serializers', '~> 0.6.0';
90
102
 
91
103
  Then create your serializers, e.g.:
92
104
 
@@ -127,13 +139,59 @@ Then make sure you add a JBuilder view for each action you require. Although all
127
139
 
128
140
  See [Railscast #320][railscast320] for examples.
129
141
 
142
+ ###### Other
143
+
144
+ Anything that is ok with or without render/responds_with that doesn't require any additional code in the action methods of the controller are ok, e.g. as_json defined in model, etc.
145
+
146
+ #### Create/Update JSON Request/Params Acceptance
147
+
148
+ ##### Permitters
149
+
150
+ We include ApplicationPermitter and optional controller support for Adam Hawkins' [permitters][permitter].
151
+
152
+ Permitters use Cancan for authorization and strong_parameters for parameter whitelisting.
153
+
154
+ We have an implementation of ApplicationPermitter, so you just need permitters in `/app/permitters/`, e.g. `/app/permitters/foobar_permitter.rb`:
155
+
156
+ class FoobarPermitter < ApplicationPermitter
157
+ # attributes we accept (the new way to do attr_accessible, OO-styley! Thanks, twinturbo)
158
+ permit :id, :foo_id
159
+ permit :bar_id
160
+ permit :notes
161
+ # foobar has accepts_nested_attributes_for :barfoos
162
+ scope :barfoos_attributes do |barfoo|
163
+ barfoo.permit :id, :favorite_color, :favorite_chicken
164
+ end
165
+ end
166
+
167
+ If you don't accept anything in create/update, you should have an empty Permitter for the model:
168
+
169
+ class FoobarPermitter < ApplicationPermitter
170
+ end
171
+
172
+ ##### Strong Parameters
173
+
174
+ To use strong_parameters by themselves, without Permitters/Cancan, specify this in restful_json config/controller config:
175
+
176
+ self.use_permitters = false
177
+
178
+ As noted in [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. See below for details.
179
+
180
+ ##### Mass Assignment Security
181
+
182
+ To use mass assignment security in Rails 3.x, specify this in restful_json config/controller config:
183
+
184
+ self.use_permitters = false
185
+
186
+ And make sure that strong_parameters is not loaded.
187
+
130
188
  ### App-level Configuration
131
189
 
132
190
  At the bottom of `config/environment.rb`, you can set config items one at a time like:
133
191
 
134
192
  RestfulJson.debug = true
135
193
 
136
- or in bulk like:
194
+ or in bulk, like:
137
195
 
138
196
  RestfulJson.configure do
139
197
 
@@ -155,8 +213,14 @@ or in bulk like:
155
213
  # delimiter for ARel predicate in the request parameter name
156
214
  self.predicate_prefix = '!'
157
215
 
158
- # if true, will render resource and HTTP 201 for post/create or resource and HTTP 200 for put/update
159
- self.return_resource = false
216
+ # 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.
217
+ self.return_resource = false
218
+
219
+ # if false, controller actions will just set instance variable and return it instead of calling setting instance variable and then calling render/respond_with
220
+ self.render_enabled = true
221
+
222
+ # 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.
223
+ self.use_permitters = true
160
224
 
161
225
  end
162
226
 
@@ -172,7 +236,9 @@ All of the app-level configuration parameters are configurable at the controller
172
236
  self.formats = :json, :html
173
237
  self.number_of_records_in_a_page = 15
174
238
  self.predicate_prefix = '!'
175
- self.return_resource = false
239
+ self.return_resource = false
240
+ self.render_enabled = true
241
+ self.use_permitters = true
176
242
 
177
243
  In addition there are some that are controller-only...
178
244
 
@@ -205,11 +271,15 @@ You can have something as simple as:
205
271
 
206
272
  which would use the restful_json configuration and the controller's classname for the service definition.
207
273
 
208
- Or you can define more bells and whistles (read on to see what these do...):
274
+ Or you could have a superclass:
209
275
 
210
- class FoobarsController < ApplicationController
211
-
276
+ class ServiceController < ApplicationController
212
277
  acts_as_restful_json
278
+ end
279
+
280
+ And define more bells and whistles (read on to see what these do...):
281
+
282
+ class FoobarsController < ServiceController
213
283
 
214
284
  query_for :index, is: ->(t,q) {q.joins(:apples, :pears).where(apples: {color: 'green'}).where(pears: {color: 'green'})}
215
285
 
@@ -469,68 +539,7 @@ Note that in `/config/initializers/wrap_parameters.rb` you might need to add `in
469
539
 
470
540
  #### Customing the Default Behavior
471
541
 
472
- In `app/controllers/able_to_mark_on_destroy.rb`, you could have:
473
-
474
- module AbleToMarkOnDestroy
475
- extend ActiveSupport::Concern
476
-
477
- included do
478
- # things you would put in the body of the class
479
- class_attribute :deleted_status_column, instance_writer: true
480
- end
481
-
482
- # no need to define class methods for this example, but this is where you'd do it
483
- #module ClassMethods
484
- #end
485
-
486
- # instance methods from here on out...
487
-
488
- # Note: overriding destroy in restful_json to support marking deleted_status_column with 'deleted'
489
- def destroy
490
- # to_s for vulnerabilities similar to CVE-2013-1854 in older Rails...
491
- @value = @model_class.find(params[:id].to_s)
492
- unless @value.nil? || !self.deleted_status_column
493
- @value.update_attributes!({self.deleted_status_column.to_sym => 'deleted'})
494
- else
495
- @value.destroy
496
- end
497
-
498
- instance_variable_set(@model_at_singular_name_sym, @value)
499
-
500
- if self.render_enabled
501
- # you could take out the custom_action_serializer support if you aren't using
502
- custom_action_serializer = self.action_to_serializer[params[:action].to_s]
503
-
504
- if !@value.nil? && RestfulJson.return_resource
505
- respond_with(@value) do |format|
506
- format.json do
507
- # for errors added in before_destroy validation or in update_attributes
508
- if @value.errors.empty?
509
- render custom_action_serializer ? {json: @value, status: :ok, serializer: custom_action_serializer} : {json: @value, status: :ok}
510
- else
511
- render custom_action_serializer ? {json: {errors: @value.errors}, status: :unprocessable_entity, serializer: custom_action_serializer} : {json: {errors: @value.errors}, status: :unprocessable_entity}
512
- end
513
- end
514
- end
515
- else
516
- respond_with @value, custom_action_serializer ? {serializer: custom_action_serializer} : {}
517
- end
518
- end
519
- end
520
- end
521
-
522
- Then in your controller:
523
-
524
- acts_as_restful_json
525
- include AbleToMarkOnDestroy
526
-
527
- self.deleted_status_column = :foo_deleted
528
-
529
- And when you do a RESTful call to destroy id 123 via a DELETE:
530
-
531
- http://localhost:3000/foobars/123
532
-
533
- Instead of deleting the DB row, the 'foobars' table row's column 'foo_deleted' would be set to 'deleted'.
542
+ 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 just make sure that it is defined after `acts_as_restful_json` is called.
534
543
 
535
544
  ### Thanks!
536
545
 
@@ -7,7 +7,8 @@ module RestfulJson
7
7
  :number_of_records_in_a_page,
8
8
  :predicate_prefix,
9
9
  :return_resource,
10
- :render_enabled
10
+ :render_enabled,
11
+ :use_permitters
11
12
  ]
12
13
 
13
14
  class << self
@@ -26,4 +27,5 @@ RestfulJson.configure do
26
27
  self.predicate_prefix = '!'
27
28
  self.return_resource = false
28
29
  self.render_enabled = true
30
+ self.use_permitters = true
29
31
  end
@@ -11,10 +11,18 @@ module RestfulJson
11
11
  module ClassMethods
12
12
 
13
13
  def acts_as_restful_json(options = {})
14
- include ::ActionController::Serialization
15
- include ::ActionController::StrongParameters
16
- include ::CanCan::ControllerAdditions
17
- include ::TwinTurbo::Controller
14
+ if defined?(::ActionController::Serialization)
15
+ include ::ActionController::Serialization
16
+ end
17
+ if defined?(::ActionController::StrongParameters)
18
+ include ::ActionController::StrongParameters
19
+ end
20
+ if defined?(::CanCan::ModelAdditions)
21
+ include ::CanCan::ControllerAdditions
22
+ end
23
+ if defined?(::ActionController::StrongParameters) && defined?(::CanCan::ModelAdditions)
24
+ include ::TwinTurbo::Controller
25
+ end
18
26
  include ActsAsRestfulJson
19
27
  end
20
28
 
@@ -173,6 +181,12 @@ module RestfulJson
173
181
  @model_plural_name = self.model_plural_name || @model_singular_name.pluralize
174
182
  @model_at_plural_name_sym = "@#{@model_plural_name}".to_sym
175
183
  @model_at_singular_name_sym = "@#{@model_singular_name}".to_sym
184
+
185
+ # next 3 are for vanilla strong_parameters
186
+ @model_singular_name_params_sym = "#{@model_singular_name}_params".to_sym
187
+ @create_model_singular_name_params_sym = "create_#{@model_singular_name}_params".to_sym
188
+ @update_model_singular_name_params_sym = "update_#{@model_singular_name}_params".to_sym
189
+
176
190
  underscored_modules_and_underscored_plural_model_name = qualified_controller_name.gsub('::','_').underscore
177
191
 
178
192
  # This is a workaround for controllers that are in a different module than the model only works if the controller's base part of the unqualified name in the plural model name.
@@ -289,6 +303,8 @@ module RestfulJson
289
303
  if self.render_enabled
290
304
  custom_action_serializer = self.action_to_serializer[params[:action].to_s]
291
305
  respond_with @value, custom_action_serializer ? {serializer: custom_action_serializer} : {}
306
+ else
307
+ @value
292
308
  end
293
309
  end
294
310
 
@@ -301,6 +317,8 @@ module RestfulJson
301
317
  if self.render_enabled
302
318
  custom_action_serializer = self.action_to_serializer[params[:action].to_s]
303
319
  respond_with @value, custom_action_serializer ? {serializer: custom_action_serializer} : {}
320
+ else
321
+ @value
304
322
  end
305
323
  end
306
324
 
@@ -311,6 +329,8 @@ module RestfulJson
311
329
  if self.render_enabled
312
330
  custom_action_serializer = self.action_to_serializer[params[:action].to_s]
313
331
  respond_with @value, custom_action_serializer ? {serializer: custom_action_serializer} : {}
332
+ else
333
+ @value
314
334
  end
315
335
  end
316
336
 
@@ -319,20 +339,25 @@ module RestfulJson
319
339
  # to_s as safety measure for vulnerabilities similar to CVE-2013-1854
320
340
  @value = @model_class.find(params[:id].to_s)
321
341
  instance_variable_set(@model_at_singular_name_sym, @value)
342
+ @value
322
343
  end
323
344
 
324
345
  # The controller's create (post) method to create a resource.
325
346
  def create
326
- authorize! :create, @model_class
327
- @value = @model_class.new(permitted_params)
347
+ if self.use_permitters
348
+ authorize! :create, @model_class
349
+ allowed_params = permitted_params
350
+ elsif respond_to? @create_model_singular_name_params_sym
351
+ allowed_params = send(@create_model_singular_name_params_sym)
352
+ elsif respond_to? @model_singular_name_params_sym
353
+ allowed_params = send(@model_singular_name_params_sym)
354
+ else
355
+ allowed_params = params
356
+ end
357
+ @value = @model_class.new(allowed_params)
328
358
  @value.save
329
359
  instance_variable_set(@model_at_singular_name_sym, @value)
330
360
 
331
- if self.render_enabled
332
- custom_action_serializer = self.action_to_serializer[params[:action].to_s]
333
- respond_with @value, custom_action_serializer ? {serializer: custom_action_serializer} : {}
334
- end
335
-
336
361
  if self.render_enabled
337
362
  custom_action_serializer = self.action_to_serializer[params[:action].to_s]
338
363
  if !@value.nil? && RestfulJson.return_resource
@@ -348,15 +373,26 @@ module RestfulJson
348
373
  else
349
374
  respond_with @value, custom_action_serializer ? {serializer: custom_action_serializer} : {}
350
375
  end
376
+ else
377
+ @value
351
378
  end
352
379
  end
353
380
 
354
381
  # The controller's update (put) method to update a resource.
355
382
  def update
356
- authorize! :update, @model_class
383
+ if self.use_permitters
384
+ authorize! :update, @model_class
385
+ allowed_params = permitted_params
386
+ elsif respond_to? @create_model_singular_name_params_sym
387
+ allowed_params = send(@update_model_singular_name_params_sym)
388
+ elsif respond_to? @model_singular_name_params_sym
389
+ allowed_params = send(@model_singular_name_params_sym)
390
+ else
391
+ allowed_params = params
392
+ end
357
393
  # to_s as safety measure for vulnerabilities similar to CVE-2013-1854
358
394
  @value = @model_class.find(params[:id].to_s)
359
- @value.update_attributes(permitted_params)
395
+ @value.update_attributes(allowed_params)
360
396
  instance_variable_set(@model_at_singular_name_sym, @value)
361
397
 
362
398
  if self.render_enabled
@@ -374,6 +410,8 @@ module RestfulJson
374
410
  else
375
411
  respond_with @value, custom_action_serializer ? {serializer: custom_action_serializer} : {}
376
412
  end
413
+ else
414
+ @value
377
415
  end
378
416
  end
379
417
 
@@ -386,6 +424,8 @@ module RestfulJson
386
424
 
387
425
  if self.render_enabled
388
426
  respond_with @value, custom_action_serializer ? {serializer: custom_action_serializer} : {}
427
+ else
428
+ @value
389
429
  end
390
430
  end
391
431
 
@@ -4,10 +4,14 @@ module RestfulJson
4
4
  module Model
5
5
  extend ActiveSupport::Concern
6
6
  included do
7
- # strong parameters
8
- include ::ActiveModel::ForbiddenAttributesProtection
9
- # cancan, depended on by twinturbo's permitters
10
- include ::CanCan::ModelAdditions
7
+ if defined?(::ActiveModel::ForbiddenAttributesProtection)
8
+ # strong parameters
9
+ include ::ActiveModel::ForbiddenAttributesProtection
10
+ end
11
+ if defined?(::CanCan::ModelAdditions)
12
+ # cancan, depended on by twinturbo's permitters
13
+ include ::CanCan::ModelAdditions
14
+ end
11
15
  end
12
16
  end
13
17
  end
@@ -1,3 +1,3 @@
1
1
  module RestfulJson
2
- VERSION = '3.1.0'
2
+ VERSION = '3.2.0'
3
3
  end
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.1.0
4
+ version: 3.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-03-22 00:00:00.000000000 Z
13
+ date: 2013-03-25 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: actionpack
@@ -44,38 +44,6 @@ dependencies:
44
44
  - - ! '>='
45
45
  - !ruby/object:Gem::Version
46
46
  version: 3.1.0
47
- - !ruby/object:Gem::Dependency
48
- name: cancan
49
- requirement: !ruby/object:Gem::Requirement
50
- none: false
51
- requirements:
52
- - - ! '>='
53
- - !ruby/object:Gem::Version
54
- version: 1.6.7
55
- type: :runtime
56
- prerelease: false
57
- version_requirements: !ruby/object:Gem::Requirement
58
- none: false
59
- requirements:
60
- - - ! '>='
61
- - !ruby/object:Gem::Version
62
- version: 1.6.7
63
- - !ruby/object:Gem::Dependency
64
- name: strong_parameters
65
- requirement: !ruby/object:Gem::Requirement
66
- none: false
67
- requirements:
68
- - - ! '>='
69
- - !ruby/object:Gem::Version
70
- version: 0.1.3
71
- type: :runtime
72
- prerelease: false
73
- version_requirements: !ruby/object:Gem::Requirement
74
- none: false
75
- requirements:
76
- - - ! '>='
77
- - !ruby/object:Gem::Version
78
- version: 0.1.3
79
47
  description: Develop declarative, featureful JSON RESTful-ish service controllers
80
48
  to use with modern Javascript MVC frameworks like AngularJS, Ember, etc. with much
81
49
  less code.