restful_json 4.2.0 → 4.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +6 -14
- data/README.md +85 -42
- data/lib/restful_json/controller.rb +69 -48
- data/lib/restful_json/version.rb +1 -1
- metadata +12 -12
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
ODZmNGI1MDljOGM4YTRhZmY1OTU5MjAyYzRmZGY5ZGNhZjdmMGJkZTE3OTE0
|
10
|
-
M2M5M2FjMzAxYTMwNDJhMzhlMDY0YWMyNDZmMDk0N2JmNjI5MTg5NDUwMmM5
|
11
|
-
NDU0YWZmOTA0NjMyNTE1MmY3MWU1NGNiNzlkZTk2YjUyNWU1MDk=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
ZmMwNjgxMDI1ZDhhNjc0YzA3YzUyNDJlZjI0NDZiYTZhYzg0MjJhYTIwNTIx
|
14
|
-
YTEwZWNiYzcwOGVmNTcxYWY1ODc1ODc5NDQ3NzY5YTY0YTAxYmQ5Y2UxZjNk
|
15
|
-
ZjM5OWEwZjIzODk4MDc1MmExM2RjMDQ1NzdiNzZjMzNmNmRlMmY=
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f05edb16346a80a688564ca06a5463d3b4739b48
|
4
|
+
data.tar.gz: 71f4b6e1770c4e026c2f5f9be6dedd62485d7fa3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 03ead94cce9a811f74feeddd61bca48361769aae629748cf9ddd9ba0fa3e2e60791fb87b9db8f73080175680879f4da76a2dac3acf84046a833cf5b046819a66
|
7
|
+
data.tar.gz: d0feaec66ab9ccf77953468e33f28b888280fd4dcde651f3168a1e7e916edaf928e2decacd513f1348e03ed3f53b169d0def3efd2b92a7b148df5e939fd0ea05
|
data/README.md
CHANGED
@@ -1,35 +1,26 @@
|
|
1
|
-
[![Build Status](https://secure.travis-ci.org/rubyservices/restful_json.png?branch=master)]
|
2
|
-
|
1
|
+
[![Build Status](https://secure.travis-ci.org/rubyservices/restful_json.png?branch=master)][travis] [![Gem Version](https://badge.fury.io/rb/restful_json.png)][badgefury]
|
2
|
+
|
3
|
+
# restful_json
|
3
4
|
|
4
5
|
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.
|
5
6
|
|
6
7
|
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.
|
7
8
|
|
8
|
-
Why do you need this if Rails controllers already make it easy to provide RESTful JSON services via generated controllers? Because
|
9
|
+
Why do you need this if Rails controllers already make it easy to provide RESTful JSON services via generated controllers? Because 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.
|
9
10
|
|
10
11
|
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.
|
11
12
|
|
12
|
-
|
13
|
+
For request parameter authorization, you can use [Strong Parameters][strong_parameters] (part of Rails 4) or [Permitters][permitters] (which uses Strong Parameters and an authorization solution like CanCan or custom authR). Or, you can use mass assignment security (part of Rails 3.x, i.e. attr_accessible and attr_protected).
|
13
14
|
|
14
|
-
|
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.
|
15
|
+
For responses, you can use [JBuilder][jbuilder] (part of Rails 4), [ActiveModel::Serializers][active_model_serializers], or almost anything else that will work with render/respond_with without anything special in the controller action method implementation.
|
18
16
|
|
19
|
-
|
20
|
-
* [Permitters][permitters]
|
21
|
-
* [Strong Parameters][strong_parameters]
|
22
|
-
* Mass assignment security in Rails 3.x (attr_accessible, etc.).
|
17
|
+
An example app using restful_json with AngularJS is [employee-training-tracker][employee-training-tracker], featured in [Built with AngularJS][built_with_angularjs].
|
23
18
|
|
24
|
-
|
19
|
+
[Travis-ci][travis] tests restful_json with Rails 3.2 and Rails 4. Feel free to submit issues and/or do a pull request if you run into anything.
|
25
20
|
|
26
21
|
### Usage
|
27
22
|
|
28
|
-
|
29
|
-
|
30
|
-
Everything is well-declared and fairly concise.
|
31
|
-
|
32
|
-
You can have something as simple as:
|
23
|
+
The following implements all common controller action methods, providing a basic JSON CRUD controller similar to a Rails controller created via `rails g scaffold ...`, except with less code.
|
33
24
|
|
34
25
|
```ruby
|
35
26
|
class FoobarsController < ApplicationController
|
@@ -37,9 +28,7 @@ class FoobarsController < ApplicationController
|
|
37
28
|
end
|
38
29
|
```
|
39
30
|
|
40
|
-
|
41
|
-
|
42
|
-
Or, you can define many more bells and whistles with declarative ARel through HTTP(S):
|
31
|
+
Or, use the provided class methods to declaratively allow use ARel-like queries via requests:
|
43
32
|
|
44
33
|
```ruby
|
45
34
|
class FoobarsController < ApplicationController
|
@@ -74,24 +63,28 @@ class FoobarsController < ApplicationController
|
|
74
63
|
end
|
75
64
|
```
|
76
65
|
|
77
|
-
|
66
|
+
Then, for example, you could call these:
|
78
67
|
|
79
68
|
```
|
80
69
|
https://example.org/foobars?foo_id=123
|
81
70
|
https://example.org/foobars?bar_date!gt=2012-08-08
|
82
71
|
https://example.org/foobars?bar_date!gt=2012-08-08&count=
|
83
|
-
https://example.org/foobars?and_one_more=
|
72
|
+
https://example.org/foobars?and_one_more=123&uniq=
|
84
73
|
https://example.org/foobars?page_count=
|
85
74
|
https://example.org/foobars?page=1
|
86
75
|
https://example.org/foobars?skip=30&take=15
|
87
76
|
```
|
88
77
|
|
78
|
+
where each respectively will filter Foobars by foo_id 123, filter Foobars with bar_date greater than 2012-08-08, count Foobars with bar_date greater than 2012-08-08, return distinct Foobars where the attribute of an association of an association of an association is 123, count of all Foobars, or get the first page of Foobars, or get the 16th-30th Foobar in the index list, sorted by foo_date, foo_color, then bar_date descending.
|
79
|
+
|
80
|
+
You only define what you need to provide and it can easily integrate with commonly used gems for authorization and authentication.
|
81
|
+
|
89
82
|
### Installation
|
90
83
|
|
91
84
|
In your Rails app's `Gemfile`:
|
92
85
|
|
93
86
|
```ruby
|
94
|
-
gem 'restful_json', '~> 4.
|
87
|
+
gem 'restful_json', '~> 4.3.0'
|
95
88
|
```
|
96
89
|
|
97
90
|
Then:
|
@@ -107,7 +100,7 @@ bundle install
|
|
107
100
|
[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:
|
108
101
|
|
109
102
|
```ruby
|
110
|
-
gem 'strong_parameters', '~> 0.2.
|
103
|
+
gem 'strong_parameters', '~> 0.2.1'
|
111
104
|
```
|
112
105
|
|
113
106
|
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.
|
@@ -170,7 +163,7 @@ Read the [Permitters][permitters] documentation for more info on how you can enc
|
|
170
163
|
[CanCan][cancan] can be used via Permitters or on its own:
|
171
164
|
|
172
165
|
```ruby
|
173
|
-
gem 'cancan', '~> 1.6.
|
166
|
+
gem 'cancan', '~> 1.6.10'
|
174
167
|
```
|
175
168
|
|
176
169
|
And [CanCan][cancan] supports [Authlogic][authlogic], [Devise][devise], etc. for authentication. See the [CanCan][cancan] docs for more info.
|
@@ -190,7 +183,7 @@ So, for example, when a create is attempted, it will first call `authorize!(:cre
|
|
190
183
|
[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:
|
191
184
|
|
192
185
|
```ruby
|
193
|
-
gem 'jbuilder', '~> 1.
|
186
|
+
gem 'jbuilder', '~> 1.4.2'
|
194
187
|
```
|
195
188
|
|
196
189
|
If you want to enable JBuilder for all restful_json services, you may want to disable all renders and respond_withs in the controller:
|
@@ -204,7 +197,7 @@ RestfulJson.render_enabled = false
|
|
204
197
|
[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`.
|
205
198
|
|
206
199
|
```ruby
|
207
|
-
gem 'active_model_serializers', '~> 0.
|
200
|
+
gem 'active_model_serializers', '~> 0.8.1'
|
208
201
|
```
|
209
202
|
|
210
203
|
Because of some issues with some versions of ActiveModel::Serializers using respond_with, you might want to set the option:
|
@@ -242,7 +235,9 @@ You should be able to use anything that works with normal render/responds_with i
|
|
242
235
|
|
243
236
|
### Application Configuration
|
244
237
|
|
245
|
-
|
238
|
+
In an initializer like `config/initializers/restful_json.rb` or in `config/environment.rb`, you can configure restful_json.
|
239
|
+
|
240
|
+
Each application-level configuration option can be configured one line at a time:
|
246
241
|
|
247
242
|
```ruby
|
248
243
|
RestfulJson.debug = true
|
@@ -295,7 +290,7 @@ RestfulJson.configure do
|
|
295
290
|
# if not using permitters, will check respond_to?("(singular_model_name)_params".to_sym) and if true will __send__(method)
|
296
291
|
self.actions_that_permit = [:create, :update]
|
297
292
|
|
298
|
-
# will call .includes(...) for including and/or
|
293
|
+
# will call .includes(...) for including and/or includes_for when action was generated by query_for
|
299
294
|
self.apply_includes_to_custom_queries = true
|
300
295
|
|
301
296
|
# in error JSON, break out the exception info into fields for debugging
|
@@ -602,7 +597,7 @@ or use the `->` Ruby 1.9 lambda stab operator (note lack of whitespace between s
|
|
602
597
|
|
603
598
|
```ruby
|
604
599
|
# t is self.model_class.arel_table and q is self.model_class.scoped
|
605
|
-
query_for :index, is:
|
600
|
+
query_for :index, is: ->(t,q) {q.where(:status_code => 'green')}
|
606
601
|
```
|
607
602
|
|
608
603
|
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:
|
@@ -657,7 +652,9 @@ end
|
|
657
652
|
|
658
653
|
##### Custom Serializers
|
659
654
|
|
660
|
-
If using ActiveModel::Serializers,
|
655
|
+
If using ActiveModel::Serializers, the default is to use the default serializer used by ActiveModel::Serializers, e.g. `(singular model name)Serializer`. If you need to customize the serializer, there are a few ways to do it.
|
656
|
+
|
657
|
+
The simplest way to customize it is by specifying `serialize_action`, e.g.
|
661
658
|
|
662
659
|
```ruby
|
663
660
|
serialize_action :index, with: ListFoobarSerializer
|
@@ -665,13 +662,30 @@ serialize_action :index, with: ListFoobarSerializer
|
|
665
662
|
|
666
663
|
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.
|
667
664
|
|
668
|
-
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
|
665
|
+
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`, e.g. the following would override the array serializer and the each serializer for the index and some_custom_action actions:
|
669
666
|
|
670
667
|
```ruby
|
671
|
-
serialize_action :index, :some_custom_action, with:
|
668
|
+
serialize_action :index, :some_custom_action, with: FooSerializer # implies each item in array will be serialized with this
|
669
|
+
serialize_action :index, :some_custom_action, with: FooArraySerializer, for: :array
|
672
670
|
```
|
673
671
|
|
674
|
-
|
672
|
+
If you need more controller over the serializer used, you may override `additional_render_or_respond_success_options`:
|
673
|
+
|
674
|
+
```ruby
|
675
|
+
# Returns additional rendering options. By default will massage self.action_to_render_options a little and return that,
|
676
|
+
# e.g. if you had used serialize_action to specify an array and each serializer for a specific action, if it is that action,
|
677
|
+
# it may return something like: {serializer: MyFooArraySerializer, each_serializer: MyFooSerializer}.
|
678
|
+
def additional_render_or_respond_success_options
|
679
|
+
if params['minimize']
|
680
|
+
result = {}
|
681
|
+
result[(single_value_response? ? :serializer : :each_serializer)] = MinimalBarfooSerializer
|
682
|
+
result[:serializer] = MinimalBarfooArraySerializer if !single_value_response?
|
683
|
+
else
|
684
|
+
result = default_additional_render_or_respond_success_options
|
685
|
+
end
|
686
|
+
result
|
687
|
+
end
|
688
|
+
```
|
675
689
|
|
676
690
|
##### Custom Permitters
|
677
691
|
|
@@ -731,7 +745,7 @@ class PostsController < ApplicationController
|
|
731
745
|
end
|
732
746
|
```
|
733
747
|
|
734
|
-
Be careful- Rails doesn't raise an error if it includes associations that don't exist (at least in Rails 3.1-4.
|
748
|
+
Be careful- Rails doesn't raise an error if it includes associations that don't exist (at least in Rails 3.1-4.x).
|
735
749
|
|
736
750
|
A relevant config option is:
|
737
751
|
|
@@ -739,9 +753,9 @@ A relevant config option is:
|
|
739
753
|
self.apply_includes_to_custom_queries = true
|
740
754
|
```
|
741
755
|
|
742
|
-
If that is instead set to true as it is by default, it will also try to call `.includes` on the relation returned from your custom `query_for` query, e.g. if you called index and in the controller defined `including ...` or `
|
756
|
+
If that is instead set to true as it is by default, it will also try to call `.includes` on the relation returned from your custom `query_for` query, e.g. if you called index and in the controller defined `including ...` or `includes_for :index, are: ...`, it will execute your custom query and then take the resuling relation and call `.includes(...)` on it. If `self.apply_includes_to_custom_queries = false`, it won't do that.
|
743
757
|
|
744
|
-
If you have action-specific ActiveModel::Serializers or JBuilder views that require different includes (such as an index action that only includes abbreviated info and a show action that includes more associations), you can handle that with `
|
758
|
+
If you have action-specific ActiveModel::Serializers or JBuilder views that require different includes (such as an index action that only includes abbreviated info and a show action that includes more associations), you can handle that with `includes_for`. Some examples:
|
745
759
|
|
746
760
|
```ruby
|
747
761
|
includes_for :create, are: [:category, :comments]
|
@@ -753,7 +767,7 @@ includes_for :index, :a_custom_action, are: [posts: [{comments: :guest}, :tags]]
|
|
753
767
|
If you want to try out [rails-api][rails-api]:
|
754
768
|
|
755
769
|
```ruby
|
756
|
-
gem 'rails-api', '~> 0.0
|
770
|
+
gem 'rails-api', '~> 0.1.0'
|
757
771
|
```
|
758
772
|
|
759
773
|
In `app/controllers/my_service_controller.rb`:
|
@@ -1011,7 +1025,7 @@ or if using Rails 4.x, you should consider including the modules separately so t
|
|
1011
1025
|
|
1012
1026
|
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.
|
1013
1027
|
|
1014
|
-
If you are using Rails 3.
|
1028
|
+
If you are using Rails 3.2 and want to use Permitters or Strong Parameters in all models:
|
1015
1029
|
|
1016
1030
|
Make sure you include Strong Parameters:
|
1017
1031
|
|
@@ -1047,19 +1061,48 @@ Strong Parameters and JBuilder are included in Rails 4. You can use Permitters a
|
|
1047
1061
|
|
1048
1062
|
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.
|
1049
1063
|
|
1064
|
+
### Troubleshooting
|
1065
|
+
|
1066
|
+
If you get `missing FROM-clause entry for table` errors, it might mean that `including`/`includes_for` you are using are overlapping with joins that are being done in the query. This is the nasty head of AR relational includes, unfortunately.
|
1067
|
+
|
1068
|
+
To fix, you may decide to either: (1) change order/definition of includes in `including`/`includes_for`, (2) don't use `including`/`includes_for` for the actions it affects (may cause n+1 queries), (3) implement `apply_includes` to apply `value = value.includes(*current_action_includes)` in an appropriate order (messy), or (4) use custom query (if index/custom list action) to define joins with handcoded SQL, e.g. (thanks to Tommy):
|
1069
|
+
|
1070
|
+
```ruby
|
1071
|
+
query_for :index, is: ->(t,q) {
|
1072
|
+
# Using standard joins performs an INNER JOIN like we want, but doesn't eager load.
|
1073
|
+
# Using includes does an eager load, but does a LEFT OUTER JOIN, which isn't really what we want, but in this scenario is probably ok.
|
1074
|
+
# Using standard joins & includes results in bad SQL with table aliases.
|
1075
|
+
# So, using includes & custom joins seems like a decent solution.
|
1076
|
+
q.includes(:bartender, :waitress, :owner, :customer)
|
1077
|
+
.joins('INNER JOIN employees bartenders ON bartenders.employee_id = shifts.bartender_id')
|
1078
|
+
.joins('INNER JOIN waitresses shift_workers ON shift_workers.id = shifts.waitress_id')
|
1079
|
+
.where(bartenders: {certified: 'yes'})
|
1080
|
+
.where(shift_workers: {attitude: 'great'})
|
1081
|
+
}
|
1082
|
+
|
1083
|
+
# set includes for all actions except index
|
1084
|
+
including :owner, :customer, :bartender, :waitress
|
1085
|
+
|
1086
|
+
# includes specified in query_for function above
|
1087
|
+
includes_for :index, are: []
|
1088
|
+
```
|
1089
|
+
|
1050
1090
|
### Contributing
|
1051
1091
|
|
1052
1092
|
Please fork, make changes in a separate branch, and do a pull request for your branch. Thanks!
|
1053
1093
|
|
1054
|
-
###
|
1094
|
+
### Authors
|
1055
1095
|
|
1096
|
+
This app was written by [FineLine Prototyping, Inc.](http://www.finelineprototyping.com) by the following contributors:
|
1056
1097
|
* Gary Weaver (https://github.com/garysweaver)
|
1057
1098
|
* Tommy Odom (https://github.com/tpodom)
|
1058
1099
|
|
1059
1100
|
### License
|
1060
1101
|
|
1061
|
-
Copyright (c) 2013
|
1102
|
+
Copyright (c) 2013 FineLine Prototyping, Inc., released under the [MIT license][lic].
|
1062
1103
|
|
1104
|
+
[travis]: http://travis-ci.org/rubyservices/restful_json
|
1105
|
+
[badgefury]: http://badge.fury.io/rb/restful_json
|
1063
1106
|
[employee-training-tracker]: https://github.com/FineLinePrototyping/employee-training-tracker
|
1064
1107
|
[built_with_angularjs]: http://builtwith.angularjs.org/
|
1065
1108
|
[permitters]: https://github.com/permitters/permitters
|
@@ -22,7 +22,7 @@ module RestfulJson
|
|
22
22
|
included do
|
23
23
|
# this can be overriden in the controller via defining respond_to
|
24
24
|
formats = RestfulJson.formats || ::Mime::EXTENSION_LOOKUP.keys.collect{|m|m.to_sym}
|
25
|
-
respond_to
|
25
|
+
respond_to(*formats)
|
26
26
|
|
27
27
|
# create class attributes for each controller option and set the value to the value in the app configuration
|
28
28
|
class_attribute :model_class, instance_writer: true
|
@@ -34,8 +34,7 @@ module RestfulJson
|
|
34
34
|
class_attribute :action_to_query, instance_writer: true
|
35
35
|
class_attribute :param_to_query, instance_writer: true
|
36
36
|
class_attribute :param_to_through, instance_writer: true
|
37
|
-
class_attribute :
|
38
|
-
class_attribute :action_to_serializer_for, instance_writer: true
|
37
|
+
class_attribute :action_to_render_options, instance_writer: true
|
39
38
|
class_attribute :query_includes, instance_writer: true
|
40
39
|
class_attribute :action_to_query_includes, instance_writer: true
|
41
40
|
|
@@ -51,8 +50,7 @@ module RestfulJson
|
|
51
50
|
self.action_to_query ||= {}
|
52
51
|
self.param_to_query ||= {}
|
53
52
|
self.param_to_through ||= {}
|
54
|
-
self.
|
55
|
-
self.action_to_serializer_for ||= {}
|
53
|
+
self.action_to_render_options ||= {}
|
56
54
|
self.action_to_query_includes ||= {}
|
57
55
|
end
|
58
56
|
|
@@ -64,7 +62,7 @@ module RestfulJson
|
|
64
62
|
# can_filter_by :attr_name_1, :attr_name_2 # implied using: [eq] if RestfulJson.can_filter_by_default_using = [:eq]
|
65
63
|
# can_filter_by :attr_name_1, :attr_name_2, using: [:eq, :not_eq]
|
66
64
|
#
|
67
|
-
# When :with_query is specified, it will call a supplied lambda. e.g. t is self.model_class.arel_table, q is self.model_class.
|
65
|
+
# When :with_query is specified, it will call a supplied lambda. e.g. t is self.model_class.arel_table, q is self.model_class.all, and p is params[:my_param_name]:
|
68
66
|
# can_filter_by :my_param_name, with_query: ->(t,q,p) {...}
|
69
67
|
#
|
70
68
|
# When :through is specified, it will take the array supplied to through as 0 to many model names following by an attribute name. It will follow through
|
@@ -131,7 +129,7 @@ module RestfulJson
|
|
131
129
|
|
132
130
|
# Specify a custom query. If action specified does not have a method, it will alias_method index to create a new action method with that query.
|
133
131
|
#
|
134
|
-
# t is self.model_class.arel_table and q is self.model_class.
|
132
|
+
# t is self.model_class.arel_table and q is self.model_class.all, e.g.
|
135
133
|
# query_for :index, is: -> {|t,q| q.where(:status_code => 'green')}
|
136
134
|
def query_for(*args)
|
137
135
|
options = args.extract_options!
|
@@ -168,8 +166,8 @@ module RestfulJson
|
|
168
166
|
options = args.extract_options!
|
169
167
|
args.each do |an_action|
|
170
168
|
if options[:with]
|
171
|
-
self.
|
172
|
-
self.
|
169
|
+
self.action_to_render_options[an_action.to_sym] ||= {}
|
170
|
+
self.action_to_render_options[an_action.to_sym]["restful_json_serialization_#{options[:for] && options[:for].to_sym == :array ? 'array' : 'default'}".to_sym] = options[:with]
|
173
171
|
else
|
174
172
|
raise "#{self.class.name} must supply an :with option with serialize_action #{an_action.inspect}"
|
175
173
|
end
|
@@ -228,6 +226,11 @@ module RestfulJson
|
|
228
226
|
class_eval "def #{@model_singular_name}_url(record);#{singularized_underscored_modules_and_underscored_plural_model_name}_url(record);end" unless @model_singular_name == singularized_underscored_modules_and_underscored_plural_model_name
|
229
227
|
end
|
230
228
|
|
229
|
+
# If Rails 3, returns @model_class.scoped. If not, returns @model_class.all. This helps avoid a deprecation warning.
|
230
|
+
def model_class_scoped
|
231
|
+
Rails::VERSION::MAJOR == 3 ? @model_class.scoped : @model_class.all
|
232
|
+
end
|
233
|
+
|
231
234
|
def convert_request_param_value_for_filtering(attr_sym, value)
|
232
235
|
value && NILS.include?(value) ? nil : value
|
233
236
|
end
|
@@ -269,20 +272,26 @@ module RestfulJson
|
|
269
272
|
render_error(e, handling_data) unless @performed_render
|
270
273
|
end
|
271
274
|
|
272
|
-
|
273
|
-
# via where()...first.
|
274
|
-
#
|
275
|
-
# Supports composite_keys.
|
276
|
-
def find_model_instance
|
275
|
+
def do_find_model_instance(first_method)
|
277
276
|
# to_s as safety measure for vulnerabilities similar to CVE-2013-1854.
|
278
277
|
# primary_key array support for composite_primary_keys.
|
279
278
|
if @model_class.primary_key.is_a? Array
|
280
279
|
c = @model_class
|
281
280
|
c.primary_key.each {|pkey|c.where(pkey.to_sym => params[pkey].to_s)}
|
282
|
-
@value = c.first
|
283
281
|
else
|
284
|
-
|
282
|
+
c = @model_class.where(@model_class.primary_key.to_sym => params[@model_class.primary_key].to_s)
|
285
283
|
end
|
284
|
+
|
285
|
+
c = apply_includes(c)
|
286
|
+
@value = c.send first_method
|
287
|
+
end
|
288
|
+
|
289
|
+
# Finds model using provided info in params, prior to any permittance,
|
290
|
+
# via where()...first.
|
291
|
+
#
|
292
|
+
# Supports composite_keys.
|
293
|
+
def find_model_instance
|
294
|
+
do_find_model_instance(:first)
|
286
295
|
end
|
287
296
|
|
288
297
|
# Finds model using provided info in params, prior to any permittance,
|
@@ -290,24 +299,19 @@ module RestfulJson
|
|
290
299
|
#
|
291
300
|
# Supports composite_keys.
|
292
301
|
def find_model_instance!
|
293
|
-
|
294
|
-
# primary_key array support for composite_primary_keys.
|
295
|
-
if @model_class.primary_key.is_a? Array
|
296
|
-
c = @model_class
|
297
|
-
apply_includes params[:action].to_sym, value
|
298
|
-
c.primary_key.each {|pkey|c.where(pkey.to_sym => params[pkey].to_s)}
|
299
|
-
# raise exception if not found
|
300
|
-
@value = c.first!
|
301
|
-
else
|
302
|
-
@value = @model_class.where(@model_class.primary_key.to_sym => params[@model_class.primary_key].to_s).first! # raise exception if not found
|
303
|
-
end
|
302
|
+
do_find_model_instance(:first!)
|
304
303
|
end
|
305
304
|
|
306
|
-
def
|
307
|
-
|
305
|
+
def current_action_includes
|
306
|
+
self.action_to_query_includes[params[:action].to_sym] || self.query_includes
|
307
|
+
end
|
308
|
+
|
309
|
+
def apply_includes(value)
|
310
|
+
this_includes = current_action_includes
|
308
311
|
if this_includes
|
309
|
-
value.includes(*this_includes)
|
312
|
+
value = value.includes(*this_includes)
|
310
313
|
end
|
314
|
+
value
|
311
315
|
end
|
312
316
|
|
313
317
|
def allowed_params
|
@@ -347,7 +351,7 @@ module RestfulJson
|
|
347
351
|
# It handles any format in theory that is supported by respond_to and has a `to_(some format)` method.
|
348
352
|
def render_error(e, handling_data)
|
349
353
|
i18n_key = handling_data[:i18n_key]
|
350
|
-
msg =
|
354
|
+
msg = t(i18n_key, default: e.message)
|
351
355
|
status = handling_data[:status] || :internal_server_error
|
352
356
|
if include_error_data?
|
353
357
|
respond_to do |format|
|
@@ -375,23 +379,21 @@ module RestfulJson
|
|
375
379
|
elsif !@value.nil? && ((read_only_action && RestfulJson.return_resource) || RestfulJson.avoid_respond_with)
|
376
380
|
respond_with(@value) do |format|
|
377
381
|
format.json do
|
378
|
-
# define local variables in blocks, not outside of them, to be safe, even though would work in this case
|
379
|
-
custom_action_serializer = self.action_to_serializer[params[:action].to_sym]
|
380
|
-
custom_action_serializer_for = self.action_to_serializer_for[params[:action].to_sym]
|
381
|
-
serialization_key = single_value_response? ? (custom_action_serializer_for == :each ? :each_serializer : :serializer) : (custom_action_serializer_for == :array ? :serializer : :each_serializer)
|
382
382
|
if !@value.respond_to?(:errors) || @value.errors.empty?
|
383
|
-
|
383
|
+
result = {json: @value, status: success_code}
|
384
|
+
result.merge!(additional_render_or_respond_success_options)
|
384
385
|
else
|
385
|
-
|
386
|
+
result = {json: {errors: @value.errors}, status: :unprocessable_entity}
|
386
387
|
end
|
388
|
+
render result
|
387
389
|
end
|
388
390
|
end
|
389
391
|
else
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
392
|
+
if !@value.respond_to?(:errors) || @value.errors.empty?
|
393
|
+
respond_with @value, additional_render_or_respond_success_options
|
394
|
+
else
|
395
|
+
respond_with @value
|
396
|
+
end
|
395
397
|
end
|
396
398
|
else
|
397
399
|
@value
|
@@ -402,22 +404,41 @@ module RestfulJson
|
|
402
404
|
SINGLE_VALUE_ACTIONS.include?(params[:action].to_sym)
|
403
405
|
end
|
404
406
|
|
407
|
+
# Returns additional rendering options. By default will massage self.action_to_render_options a little and return that,
|
408
|
+
# e.g. if you had used serialize_action to specify an array and each serializer for a specific action, if it is that action,
|
409
|
+
# it may return something like: {serializer: MyFooArraySerializer, each_serializer: MyFooSerializer}. If you'd like to do
|
410
|
+
# something custom in some situations, but default in others, you may also call default_additional_render_or_respond_success_options
|
411
|
+
# from within this method to get the defaults.
|
412
|
+
def additional_render_or_respond_success_options
|
413
|
+
default_additional_render_or_respond_success_options
|
414
|
+
end
|
415
|
+
|
416
|
+
def default_additional_render_or_respond_success_options
|
417
|
+
result = {}
|
418
|
+
if self.action_to_render_options[params[:action].to_sym]
|
419
|
+
custom_action_serializer = self.action_to_render_options[params[:action].to_sym][:restful_json_serialization_default]
|
420
|
+
result[(single_value_response? ? :serializer : :each_serializer)] = custom_action_serializer if custom_action_serializer
|
421
|
+
custom_action_array_serializer = self.action_to_render_options[params[:action].to_sym][:restful_json_serialization_array]
|
422
|
+
result[:serializer] = custom_action_array_serializer if custom_action_array_serializer && !single_value_response?
|
423
|
+
end
|
424
|
+
result
|
425
|
+
end
|
426
|
+
|
405
427
|
# The controller's index (list) method to list resources.
|
406
428
|
#
|
407
429
|
# Note: this method be alias_method'd by query_for, so it is more than just index.
|
408
430
|
def index
|
409
431
|
# could be index or another action if alias_method'd by query_for
|
410
432
|
logger.debug "#{params[:action]} called in #{self.class}: model=#{@model_class}, request.format=#{request.format}, request.content_type=#{request.content_type}, params=#{params.inspect}" if self.debug
|
411
|
-
action_sym = params[:action].to_sym
|
412
433
|
p_params = allowed_params
|
413
434
|
t = @model_class.arel_table
|
414
|
-
value =
|
415
|
-
custom_query = self.action_to_query[
|
435
|
+
value = model_class_scoped
|
436
|
+
custom_query = self.action_to_query[params[:action].to_sym]
|
416
437
|
if custom_query
|
417
438
|
value = custom_query.call(t, value)
|
418
439
|
end
|
419
440
|
|
420
|
-
|
441
|
+
value = apply_includes(value)
|
421
442
|
|
422
443
|
self.param_to_query.each do |param_name, param_query|
|
423
444
|
if params[param_name]
|
@@ -429,7 +450,7 @@ module RestfulJson
|
|
429
450
|
self.param_to_through.each do |param_name, through_array|
|
430
451
|
if p_params[param_name]
|
431
452
|
# build query
|
432
|
-
# e.g. SomeModel.
|
453
|
+
# e.g. SomeModel.all.joins({:assoc_name => {:sub_assoc => {:sub_sub_assoc => :sub_sub_sub_assoc}}).where(sub_sub_sub_assoc_model_table_name: {column_name: value})
|
433
454
|
last_model_class = @model_class
|
434
455
|
joins = nil # {:assoc_name => {:sub_assoc => {:sub_sub_assoc => :sub_sub_sub_assoc}}
|
435
456
|
through_array.each do |association_or_attribute|
|
@@ -587,7 +608,7 @@ module RestfulJson
|
|
587
608
|
@value = find_model_instance
|
588
609
|
# allowed_params used primarily for authorization. can't do this to get id param(s) because those are sent via route, not
|
589
610
|
# in wrapped params (if wrapped)
|
590
|
-
|
611
|
+
allowed_params
|
591
612
|
@value.destroy if @value
|
592
613
|
instance_variable_set(@model_at_singular_name_sym, @value)
|
593
614
|
if !@value.respond_to?(:errors) || @value.errors.empty? || (request.format != 'text/html' && request.content_type != 'text/html')
|
data/lib/restful_json/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: restful_json
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.
|
4
|
+
version: 4.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gary S. Weaver
|
@@ -9,25 +9,24 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-05
|
12
|
+
date: 2013-08-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- -
|
18
|
+
- - '>='
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: 3.1.0
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- -
|
25
|
+
- - '>='
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: 3.1.0
|
28
|
-
description: Develop declarative, featureful JSON
|
29
|
-
|
30
|
-
less code.
|
28
|
+
description: Develop declarative, featureful JSON service controllers to use with
|
29
|
+
modern Javascript MVC frameworks like AngularJS, Ember, etc. with much less code.
|
31
30
|
email:
|
32
31
|
- garysweaver@gmail.com
|
33
32
|
executables: []
|
@@ -41,7 +40,7 @@ files:
|
|
41
40
|
- lib/restful_json.rb
|
42
41
|
- Rakefile
|
43
42
|
- README.md
|
44
|
-
homepage: https://github.com/
|
43
|
+
homepage: https://github.com/FineLinePrototyping/restful_json
|
45
44
|
licenses:
|
46
45
|
- MIT
|
47
46
|
metadata: {}
|
@@ -51,18 +50,19 @@ require_paths:
|
|
51
50
|
- lib
|
52
51
|
required_ruby_version: !ruby/object:Gem::Requirement
|
53
52
|
requirements:
|
54
|
-
- -
|
53
|
+
- - '>='
|
55
54
|
- !ruby/object:Gem::Version
|
56
55
|
version: '0'
|
57
56
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
57
|
requirements:
|
59
|
-
- -
|
58
|
+
- - '>='
|
60
59
|
- !ruby/object:Gem::Version
|
61
60
|
version: '0'
|
62
61
|
requirements: []
|
63
62
|
rubyforge_project:
|
64
|
-
rubygems_version: 2.0.
|
63
|
+
rubygems_version: 2.0.6
|
65
64
|
signing_key:
|
66
65
|
specification_version: 4
|
67
|
-
summary: RESTful JSON controllers
|
66
|
+
summary: Declarative RESTful JSON service controllers to use with AngularJS, Ember,
|
67
|
+
etc. with less code.
|
68
68
|
test_files: []
|