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 CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- OTZiNzMzYTllNTRkNGQ1N2I1ZjYyOWM4ZDJlZjI3MWQ4ZDMzMTg4Yg==
5
- data.tar.gz: !binary |-
6
- NTRiMGMwZjRmZjllYTJhNTUxZDdhYWQ0ZTIzM2U2OTY0MmJkZTkzZA==
7
- !binary "U0hBNTEy":
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)](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
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 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.
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
- 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 request if you run into anything.
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
- You can use any of these for the JSON response (the view):
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
- And can use any of the following for authorizing parameters in the incoming JSON (for create/update):
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
- 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].
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
- 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.
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
- 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 ...`.
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
- Now you can query like these:
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=an_attribute_value_on_my_assocs_assocs_assoc&uniq=
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.0.0'
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.0'
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.9'
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.3.0'
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.7.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
- At the bottom of `config/environment.rb`, you can set restful_json can be configured one line at a time.
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 including_for_action when action was generated by query_for
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: is: ->(t,q) {q.where(:status_code => 'green')}
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, you can use something other than the `(singular model name)Serializer` via `serialize_action`:
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` or `:each`, e.g.:
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: FoosSerializer, for: :array
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
- Or, you could just use the default serialization, if you want.
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.0).
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 `including_for_action index: ...`, 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.
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 `including_for_action`. Some examples:
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.3'
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.1-3.2 and want to use Permitters or Strong Parameters in all models:
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
- ### Contributors
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 Gary S. Weaver, released under the [MIT license][lic].
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 *formats
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 :action_to_serializer, instance_writer: true
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.action_to_serializer ||= {}
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.scoped, and p is params[:my_param_name]:
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.scoped, e.g.
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.action_to_serializer[an_action.to_sym] = options[:with]
172
- self.action_to_serializer_for[an_action.to_sym] = options[:for] if options[:for]
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
- # Finds model using provided info in params, prior to any permittance,
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
- @value = @model_class.where(@model_class.primary_key.to_sym => params[@model_class.primary_key].to_s).first
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
- # to_s as safety measure for vulnerabilities similar to CVE-2013-1854.
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 apply_includes(action_sym, value)
307
- this_includes = self.action_to_query_includes[action_sym] || self.query_includes
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 = result = t(i18n_key, default: e.message)
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
- render custom_action_serializer ? {json: @value, status: success_code, serialization_key => custom_action_serializer} : {json: @value, status: success_code}
383
+ result = {json: @value, status: success_code}
384
+ result.merge!(additional_render_or_respond_success_options)
384
385
  else
385
- render custom_action_serializer ? {json: {errors: @value.errors}, status: :unprocessable_entity, serialization_key => custom_action_serializer} : {json: {errors: @value.errors}, status: :unprocessable_entity}
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
- # code duplicated from above because local vars don't always traverse well into block (based on wierd ruby-proc bug experienced)
391
- custom_action_serializer = self.action_to_serializer[params[:action].to_sym]
392
- custom_action_serializer_for = self.action_to_serializer_for[params[:action].to_sym]
393
- serialization_key = single_value_response? ? (custom_action_serializer_for == :array ? :serializer : :each_serializer) : (custom_action_serializer_for == :each ? :each_serializer : :serializer)
394
- respond_with @value, custom_action_serializer ? {(self.action_to_serializer_for[params[:action].to_sym] == :each ? :each_serializer : :serializer) => custom_action_serializer} : {}
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 = @model_class.scoped # returns ActiveRecord::Relation equivalent to select with no where clause
415
- custom_query = self.action_to_query[action_sym]
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
- apply_includes action_sym, value
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.scoped.joins({:assoc_name => {:sub_assoc => {:sub_sub_assoc => :sub_sub_sub_assoc}}).where(sub_sub_sub_assoc_model_table_name: {column_name: value})
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
- p_params = allowed_params
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')
@@ -1,3 +1,3 @@
1
1
  module RestfulJson
2
- VERSION = '4.2.0'
2
+ VERSION = '4.3.1'
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: 4.2.0
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-02 00:00:00.000000000 Z
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 RESTful-ish service controllers
29
- to use with modern Javascript MVC frameworks like AngularJS, Ember, etc. with much
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/rubyservices/restful_json
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.3
63
+ rubygems_version: 2.0.6
65
64
  signing_key:
66
65
  specification_version: 4
67
- summary: RESTful JSON controllers using Rails 3.1+, Rails 4+.
66
+ summary: Declarative RESTful JSON service controllers to use with AngularJS, Ember,
67
+ etc. with less code.
68
68
  test_files: []