restful_json 3.0.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +313 -52
- data/lib/restful_json/config.rb +3 -1
- data/lib/restful_json/controller.rb +67 -22
- data/lib/restful_json/version.rb +1 -1
- data/lib/twinturbo/application_permitter.rb +93 -0
- metadata +11 -42
    
        data/README.md
    CHANGED
    
    | @@ -5,7 +5,13 @@ 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 | 
            -
             | 
| 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.
         | 
| 9 | 
            +
             | 
| 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.
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            An example app using restful_json with AngularJS is [employee-training-tracker][employee-training-tracker], featured in [Built with AngularJS][built_with_angularjs].
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            The goal of restful_json is to reduce service controller code in an intuitive way, not to be a be-all DSL. The intent is for there to be no "restful_json way". It is currently opinionated about use of Cancan, strong_parameters, etc. because twinturbo [permitter][permitter] strategy uses those, but you can cut the ties via a pull request if you'd like.
         | 
| 9 15 |  | 
| 10 16 | 
             
            ### Installation
         | 
| 11 17 |  | 
| @@ -17,7 +23,26 @@ and: | |
| 17 23 |  | 
| 18 24 | 
             
                bundle install
         | 
| 19 25 |  | 
| 20 | 
            -
             | 
| 26 | 
            +
            ### Dependencies
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            #### Rails
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            Need at least Rails 3.1.0, but supports 3.1.x, 3.2.x, and 4+.
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            Go with whatever you have in your Gemfile, e.g.:
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                gem 'rails', '3.2.11'
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            If you don't want all of Rails (probably not the case), you at least need actionpack and activerecord:
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                gem 'actionpack', '> 3.1.0'
         | 
| 39 | 
            +
                gem 'activerecord', '> 3.1.0'
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            #### Cancan
         | 
| 42 | 
            +
             | 
| 43 | 
            +
            To use [cancan][cancan], in your Gemfile, add:
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                gem 'cancan', '~> 1.6.7'
         | 
| 21 46 |  | 
| 22 47 | 
             
            In your `app/controllers/application_controller.rb` or in your service controllers, make sure `current_user` is set:
         | 
| 23 48 |  | 
| @@ -41,31 +66,68 @@ In `app/models/ability.rb`, setup a basic cancan ability. Just for testing we'll | |
| 41 66 | 
             
                  end
         | 
| 42 67 | 
             
                end
         | 
| 43 68 |  | 
| 44 | 
            -
             | 
| 69 | 
            +
            #### Strong Parameters
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            To use [strong_parameters][strong_parameters], in your Gemfile, add:
         | 
| 45 72 |  | 
| 46 | 
            -
             | 
| 73 | 
            +
                gem 'strong_parameters', '~> 0.1.3'
         | 
| 47 74 |  | 
| 48 | 
            -
             | 
| 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`:
         | 
| 49 76 |  | 
| 50 | 
            -
             | 
| 77 | 
            +
                config.active_record.whitelist_attributes = false
         | 
| 51 78 |  | 
| 52 | 
            -
             | 
| 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.
         | 
| 53 80 |  | 
| 54 | 
            -
             | 
| 81 | 
            +
            Note that restful_json automatically includes `ActiveModel::ForbiddenAttributesProtection` on all models, as is done in Rails 4.
         | 
| 55 82 |  | 
| 56 | 
            -
             | 
| 83 | 
            +
            #### JSON Response Generators
         | 
| 57 84 |  | 
| 58 | 
            -
             | 
| 85 | 
            +
            ##### ActiveModel Serializers
         | 
| 59 86 |  | 
| 60 | 
            -
            If you want to  | 
| 87 | 
            +
            If you want to use [ActiveModel Serializers][[active_model_serializers], use:
         | 
| 61 88 |  | 
| 62 | 
            -
                 | 
| 89 | 
            +
                gem 'active_model_serializers'
         | 
| 63 90 |  | 
| 64 | 
            -
             | 
| 91 | 
            +
            Then create your serializers, e.g.:
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                /app/serializers/singular_model_name_serializer.rb
         | 
| 94 | 
            +
             | 
| 95 | 
            +
            Without having to do anything else, each restful_json controller will use `/app/serializers/singular_model_name_serializer.rb`, e.g. `/app/serializers/bar_serializer.rb` for the actions: index, show, new, create, update, destroy (not edit).
         | 
| 96 | 
            +
             | 
| 97 | 
            +
            If you want to define a different serializer another action, e.g. the index action so that a list of instances has a different JSON format:
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                serialize_action :index, with: BarsSerializer
         | 
| 100 | 
            +
             | 
| 101 | 
            +
            You can also use a specific format for multiple actions:
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                serialize_action :index, :my_other_list_action, with: BarsSerializer
         | 
| 104 | 
            +
             | 
| 105 | 
            +
            The built-in actions that support custom serializers (you can add more) are: index, show, new, create, update, destroy, and any action you automatically have created via using the restful_json `query_for` method (keep reading!).
         | 
| 106 | 
            +
             | 
| 107 | 
            +
            ##### JBuilder
         | 
| 108 | 
            +
             | 
| 109 | 
            +
            If you want to use JBuilder instead to render, first:
         | 
| 65 110 |  | 
| 66 | 
            -
             | 
| 111 | 
            +
                gem 'jbuilder'
         | 
| 67 112 |  | 
| 68 | 
            -
             | 
| 113 | 
            +
            If you want to enable JBuilder for all restful_json services, you need to disable all renders and respond_withs in the controller:
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                RestfulJson.render_enabled = false
         | 
| 116 | 
            +
             | 
| 117 | 
            +
            Or you can also just enable/disable rendering in a controller via setting `self.render_enabled`:
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                self.render_enabled = false
         | 
| 120 | 
            +
             | 
| 121 | 
            +
            Then make sure you add a JBuilder view for each action you require. Although all may not be relevant, we support: index, show, new, edit, create, update, destroy. Maybe you'd create:
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                /app/views/plural_name_of_model/index.json.jbuilder
         | 
| 124 | 
            +
                /app/views/plural_name_of_model/show.json.jbuilder
         | 
| 125 | 
            +
                /app/views/plural_name_of_model/create.json.jbuilder
         | 
| 126 | 
            +
                /app/views/plural_name_of_model/update.json.jbuilder
         | 
| 127 | 
            +
             | 
| 128 | 
            +
            See [Railscast #320][railscast320] for examples.
         | 
| 129 | 
            +
             | 
| 130 | 
            +
            ### App-level Configuration
         | 
| 69 131 |  | 
| 70 132 | 
             
            At the bottom of `config/environment.rb`, you can set config items one at a time like:
         | 
| 71 133 |  | 
| @@ -74,67 +136,149 @@ At the bottom of `config/environment.rb`, you can set config items one at a time | |
| 74 136 | 
             
            or in bulk like:
         | 
| 75 137 |  | 
| 76 138 | 
             
                RestfulJson.configure do
         | 
| 77 | 
            -
                   | 
| 78 | 
            -
                   | 
| 79 | 
            -
                  self. | 
| 80 | 
            -
                   | 
| 81 | 
            -
                   | 
| 82 | 
            -
                  self. | 
| 83 | 
            -
                   | 
| 139 | 
            +
                  
         | 
| 140 | 
            +
                  # default for :using in can_filter_by
         | 
| 141 | 
            +
                  self.can_filter_by_default_using = [:eq]
         | 
| 142 | 
            +
                  
         | 
| 143 | 
            +
                  # to output debugging info during request handling
         | 
| 144 | 
            +
                  self.debug = false
         | 
| 145 | 
            +
                  
         | 
| 146 | 
            +
                  # delimiter for values in request parameter values
         | 
| 147 | 
            +
                  self.filter_split = ','
         | 
| 148 | 
            +
                  
         | 
| 149 | 
            +
                  # equivalent to specifying respond_to :json, :html in the controller, and can be overriden in the controller. Note that by default responders gem sets respond_to :html in application_controller.rb.
         | 
| 150 | 
            +
                  self.formats = :json, :html
         | 
| 151 | 
            +
                  
         | 
| 152 | 
            +
                  # default number of records to return if using the page request function
         | 
| 153 | 
            +
                  self.number_of_records_in_a_page = 15
         | 
| 154 | 
            +
                  
         | 
| 155 | 
            +
                  # delimiter for ARel predicate in the request parameter name
         | 
| 156 | 
            +
                  self.predicate_prefix = '!'
         | 
| 157 | 
            +
                  
         | 
| 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 
         | 
| 160 | 
            +
             | 
| 84 161 | 
             
                end
         | 
| 85 162 |  | 
| 86 | 
            -
             | 
| 163 | 
            +
            ### Controller-specific Configuration
         | 
| 164 | 
            +
             | 
| 165 | 
            +
            In the controller, you can set a variety of class attributes with `self.something = ...` in the body of your controller.
         | 
| 166 | 
            +
             | 
| 167 | 
            +
            All of the app-level configuration parameters are configurable at the controller level:
         | 
| 168 | 
            +
             | 
| 169 | 
            +
                  self.can_filter_by_default_using = [:eq]
         | 
| 170 | 
            +
                  self.debug = false
         | 
| 171 | 
            +
                  self.filter_split = ','
         | 
| 172 | 
            +
                  self.formats = :json, :html
         | 
| 173 | 
            +
                  self.number_of_records_in_a_page = 15
         | 
| 174 | 
            +
                  self.predicate_prefix = '!'
         | 
| 175 | 
            +
                  self.return_resource = false 
         | 
| 176 | 
            +
             | 
| 177 | 
            +
            In addition there are some that are controller-only...
         | 
| 178 | 
            +
             | 
| 179 | 
            +
            If you don't use the standard controller naming convention, you can define this in the controller:
         | 
| 87 180 |  | 
| 88 | 
            -
             | 
| 181 | 
            +
                    self.model_class = YourModel
         | 
| 182 | 
            +
             | 
| 183 | 
            +
            If it doesn't handle the other forms well, you can explicitly define the singular/plural names:
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                    self.model_singular_name = 'your_model'
         | 
| 186 | 
            +
                    self.model_plural_name = 'your_models'
         | 
| 187 | 
            +
             | 
| 188 | 
            +
            These are used for *_url method definitions, to set instance variables like `@foobar` and `@foobars` dynamically, etc.
         | 
| 189 | 
            +
             | 
| 190 | 
            +
            Other class attributes are available for setting/overriding, but they are all set by the other class methods defined in the next section.
         | 
| 89 191 |  | 
| 90 192 | 
             
            ### Usage
         | 
| 91 193 |  | 
| 92 | 
            -
            By using `acts_as_restful_json | 
| 194 | 
            +
            By using `acts_as_restful_json`, you have a configurable generic Rails 3.1+/4+ controller that does the index, show, create, and update and other custom actions easily for you.
         | 
| 93 195 |  | 
| 94 | 
            -
            Everything is well-declared and concise | 
| 196 | 
            +
            Everything is well-declared and fairly concise.
         | 
| 95 197 |  | 
| 96 | 
            -
             | 
| 198 | 
            +
            You can have something as simple as:
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                class FoobarsController < ApplicationController
         | 
| 201 | 
            +
                  
         | 
| 97 202 | 
             
                  acts_as_restful_json
         | 
| 203 | 
            +
             | 
| 204 | 
            +
                end
         | 
| 205 | 
            +
             | 
| 206 | 
            +
            which would use the restful_json configuration and the controller's classname for the service definition.
         | 
| 207 | 
            +
             | 
| 208 | 
            +
            Or you can define more bells and whistles (read on to see what these do...):
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                class FoobarsController < ApplicationController
         | 
| 211 | 
            +
                  
         | 
| 212 | 
            +
                  acts_as_restful_json
         | 
| 213 | 
            +
                  
         | 
| 98 214 | 
             
                  query_for :index, is: ->(t,q) {q.joins(:apples, :pears).where(apples: {color: 'green'}).where(pears: {color: 'green'})}
         | 
| 215 | 
            +
                  
         | 
| 99 216 | 
             
                  # args sent to can_filter_by are the request parameter name(s)
         | 
| 100 | 
            -
                   | 
| 101 | 
            -
                   | 
| 217 | 
            +
                  
         | 
| 218 | 
            +
                  # implies using: [:eq] because RestfulJson.can_filter_by_default_using = [:eq]
         | 
| 219 | 
            +
                  can_filter_by :foo_id
         | 
| 220 | 
            +
                  
         | 
| 221 | 
            +
                  # can specify multiple predicates and optionally a default value
         | 
| 222 | 
            +
                  can_filter_by :foo_date, :bar_date, using: [:lt, :eq, :gt], with_default: Time.now
         | 
| 223 | 
            +
                  
         | 
| 102 224 | 
             
                  can_filter_by :a_request_param_name, with_query: ->(t,q,param_value) {q.joins(:some_assoc).where(:some_assocs_table_name=>{some_attr: param_value})}
         | 
| 225 | 
            +
                  
         | 
| 103 226 | 
             
                  can_filter_by :and_another, through: [:some_attribute_on_this_model]
         | 
| 227 | 
            +
                  
         | 
| 104 228 | 
             
                  can_filter_by :one_more, through: [:some_association, :some_attribute_on_some_association_model]
         | 
| 229 | 
            +
                  
         | 
| 105 230 | 
             
                  can_filter_by :and_one_more, through: [:my_assoc, :my_assocs_assoc, :my_assocs_assocs_assoc, :an_attribute_on_my_assocs_assocs_assoc]
         | 
| 231 | 
            +
                  
         | 
| 106 232 | 
             
                  supports_functions :count, :uniq, :take, :skip, :page, :page_count
         | 
| 233 | 
            +
                  
         | 
| 107 234 | 
             
                  order_by {:foo_date => :asc}, :foo_color, {:bar_date => :desc} # an ordered array of hashes, assumes :asc if not a hash
         | 
| 108 | 
            -
                   | 
| 235 | 
            +
                  
         | 
| 236 | 
            +
                  # comma-delimited if you want more than :json, e.g. :json, :html
         | 
| 237 | 
            +
                  respond_to :json, :html
         | 
| 238 | 
            +
                  
         | 
| 109 239 | 
             
                end
         | 
| 110 240 |  | 
| 111 | 
            -
             | 
| 241 | 
            +
            #### Default Filtering by Attribute(s)
         | 
| 112 242 |  | 
| 113 | 
            -
             | 
| 114 | 
            -
                Arel::Predications.public_instance_methods.sort
         | 
| 243 | 
            +
            First, declare in the controller:
         | 
| 115 244 |  | 
| 116 | 
            -
             | 
| 245 | 
            +
                can_filter_by :foo_id
         | 
| 117 246 |  | 
| 118 | 
            -
             | 
| 247 | 
            +
            If `RestfulJson.can_filter_by_default_using = [:eq]` as it is by default, then you can now get Foobars with a foo_id of '1':
         | 
| 119 248 |  | 
| 120 | 
            -
             | 
| 249 | 
            +
                http://localhost:3000/foobars?foo_id=1
         | 
| 121 250 |  | 
| 122 | 
            -
            `can_filter_by` can  | 
| 251 | 
            +
            `can_filter_by` without an option means you can send in that request param (via routing or directly, just like normal in Rails) and it will use that in the ARel query (safe from SQL injection and only letting you do what you tell it). `:using` means you can use those [ARel][arel] predicates for filtering. If you do `Arel::Predications.public_instance_methods.sort` in Rails console, you can see a list of the available predicates. So, you could get crazy with:
         | 
| 123 252 |  | 
| 124 | 
            -
             | 
| 253 | 
            +
                can_filter_by :does_not_match, :does_not_match_all, :does_not_match_any, :eq, :eq_all, :eq_any, :gt, :gt_all, :gt_any, :gteq, :gteq_all, :gteq_any, :in, :in_all, :in_any, :lt, :lt_all, :lt_any, :lteq, :lteq_all, :lteq_any, :matches, :matches_all, :matches_any, :not_eq, :not_eq_all, :not_eq_any, :not_in, :not_in_all, :not_in_any
         | 
| 254 | 
            +
             | 
| 255 | 
            +
            `can_filter_by` can also specify a `:with_query` to provide a lambda that takes the request parameter in when it is provided by the request.
         | 
| 125 256 |  | 
| 126 257 | 
             
                can_filter_by :a_request_param_name, with_query: ->(t,q,param_value) {q.joins(:some_assoc).where(:some_assocs_table_name=>{some_attr: param_value})}
         | 
| 127 | 
            -
                can_filter_by :a_request_param_name, through: [:some_assoc, :some_attr] # much easier, but not as flexible as a lambda
         | 
| 128 258 |  | 
| 129 | 
            -
             | 
| 259 | 
            +
            And `can_filter_by` can specify a `:through` to provide an easy way to inner join through a bunch of models using ActiveRecord relations, by specifying 0-to-many association names to go "through" to the final argument, which is the attribute name on the last model. The following is equivalent to the last query:
         | 
| 130 260 |  | 
| 131 | 
            -
             | 
| 261 | 
            +
                can_filter_by :a_request_param_name, through: [:some_assoc, :some_attr]
         | 
| 132 262 |  | 
| 133 | 
            -
             | 
| 263 | 
            +
            Let's say you are in MagicalValleyController, and the MagicalValley model `has many :magical_unicorns`. The MagicalUnicorn model has an attribute called `name`. You want to return MagicalValleys that are associated with all of the MagicalUnicorns named 'Rainbow'. You could do either:
         | 
| 134 264 |  | 
| 135 | 
            -
             | 
| 265 | 
            +
                can_filter_by :magical_unicorn_name, with_query: ->(t,q,param_value) {q.joins(:magical_unicorns).where(:magical_unicorns=>{name: param_value})}
         | 
| 136 266 |  | 
| 137 | 
            -
             | 
| 267 | 
            +
            or:
         | 
| 268 | 
            +
             | 
| 269 | 
            +
                can_filter_by :magical_unicorn_name, through: [:magical_unicorns, :name]
         | 
| 270 | 
            +
             | 
| 271 | 
            +
            and you can then use this:
         | 
| 272 | 
            +
             | 
| 273 | 
            +
                http://localhost:3000/magical_valleys?magical_unicorn_name=Rainbow
         | 
| 274 | 
            +
             | 
| 275 | 
            +
            or if a MagicalUnicorn `has_many :friends` and a MagicalUnicorn's friend has a name attribute:
         | 
| 276 | 
            +
             | 
| 277 | 
            +
                can_filter_by :magical_unicorn_friend_name, through: [:magical_unicorns, :friends, :name]
         | 
| 278 | 
            +
             | 
| 279 | 
            +
            and use this to get valleys associated with unicorns who in turn have a friend named Oscar:
         | 
| 280 | 
            +
             | 
| 281 | 
            +
                http://localhost:3000/magical_valleys?magical_unicorn_friend_name=Oscar
         | 
| 138 282 |  | 
| 139 283 | 
             
            #### Other Filters by Attribute(s)
         | 
| 140 284 |  | 
| @@ -150,17 +294,23 @@ Multiple values are separated by `filter_split` (configurable): | |
| 150 294 |  | 
| 151 295 | 
             
                http://localhost:3000/foobars?seen_on!eq_any=2012-08-08,2012-09-09
         | 
| 152 296 |  | 
| 153 | 
            -
            ####  | 
| 297 | 
            +
            #### Supported Functions
         | 
| 298 | 
            +
             | 
| 299 | 
            +
            ##### Declaring
         | 
| 300 | 
            +
             | 
| 301 | 
            +
            `supports_functions` lets you allow the [ARel][arel] functions: `:uniq`, `:skip`, `:take`, and/or `:count`.
         | 
| 302 | 
            +
             | 
| 303 | 
            +
            ##### Unique (DISTINCT)
         | 
| 154 304 |  | 
| 155 305 | 
             
            First, declare in the controller:
         | 
| 156 306 |  | 
| 157 307 | 
             
                supports_functions :uniq
         | 
| 158 308 |  | 
| 159 | 
            -
            To return a simple unique view of a model, combine use of an  | 
| 309 | 
            +
            To return a simple unique view of a model, combine use of an [ActiveModel Serializer][active_model_serializers] that returns just the attribute you want along with the uniq param, e.g. to return unique/distinct colors of foobars you'd have a serializer to just return the color and then use:
         | 
| 160 310 |  | 
| 161 311 | 
             
                http://localhost:3000/foobars?uniq=
         | 
| 162 312 |  | 
| 163 | 
            -
             | 
| 313 | 
            +
            ##### Count
         | 
| 164 314 |  | 
| 165 315 | 
             
            First, declare in the controller:
         | 
| 166 316 |  | 
| @@ -170,7 +320,7 @@ This is another filter that can be used with the others, but instead of returnin | |
| 170 320 |  | 
| 171 321 | 
             
                http://localhost:3000/foobars?count=
         | 
| 172 322 |  | 
| 173 | 
            -
             | 
| 323 | 
            +
            ##### Paging
         | 
| 174 324 |  | 
| 175 325 | 
             
            In controller make sure these are included:
         | 
| 176 326 |  | 
| @@ -198,7 +348,7 @@ To set page size at controller level: | |
| 198 348 |  | 
| 199 349 | 
             
            For a better idea of how this works on the backend, look at [ARel][arel]'s skip and take, or see Variable Paging.
         | 
| 200 350 |  | 
| 201 | 
            -
             | 
| 351 | 
            +
            ###### Skip and Take (OFFSET and LIMIT)
         | 
| 202 352 |  | 
| 203 353 | 
             
            In controller make sure these are included:
         | 
| 204 354 |  | 
| @@ -212,8 +362,6 @@ To limit the number of rows returned, use 'take'. It is called take, because tak | |
| 212 362 |  | 
| 213 363 | 
             
                http://localhost:3000/foobars.json?take=5
         | 
| 214 364 |  | 
| 215 | 
            -
            ##### Variable Paging
         | 
| 216 | 
            -
             | 
| 217 365 | 
             
            Combine skip and take for manual completely customized paging.
         | 
| 218 366 |  | 
| 219 367 | 
             
            Get the first page of 15 results:
         | 
| @@ -257,7 +405,7 @@ or use the `->` Ruby 1.9 lambda stab operator. You can also filter out items tha | |
| 257 405 | 
             
                  .where(pears: {color: 'green'})
         | 
| 258 406 | 
             
                }
         | 
| 259 407 |  | 
| 260 | 
            -
            ##### Custom Actions
         | 
| 408 | 
            +
            ##### Define Custom Actions with Custom Queries
         | 
| 261 409 |  | 
| 262 410 | 
             
            `query_for` also will `alias_method (some action), :index` anything other than `:index`, so you can easily create custom non-RESTful action methods:
         | 
| 263 411 |  | 
| @@ -267,6 +415,8 @@ or use the `->` Ruby 1.9 lambda stab operator. You can also filter out items tha | |
| 267 415 |  | 
| 268 416 | 
             
            Note that it is a proc so you can really do whatever you want with it and will have access to other things in the environment or can call another method, etc.
         | 
| 269 417 |  | 
| 418 | 
            +
            You are still working with regular controllers here, so add or override methods if you want more!
         | 
| 419 | 
            +
             | 
| 270 420 | 
             
            ### Routing
         | 
| 271 421 |  | 
| 272 422 | 
             
            Respects regular and nested Rails resourceful routing and controller namespacing, e.g. in `config/routes.rb`:
         | 
| @@ -279,6 +429,113 @@ Respects regular and nested Rails resourceful routing and controller namespacing | |
| 279 429 | 
             
                  end
         | 
| 280 430 | 
             
                end
         | 
| 281 431 |  | 
| 432 | 
            +
            ### Experimental Usage
         | 
| 433 | 
            +
             | 
| 434 | 
            +
            #### Rails-api
         | 
| 435 | 
            +
             | 
| 436 | 
            +
            If you want to try out [rails-api][rails-api], maybe use:
         | 
| 437 | 
            +
             | 
| 438 | 
            +
                gem 'rails-api', '~> 0.0.3'
         | 
| 439 | 
            +
             | 
| 440 | 
            +
                class ServiceController < ActionController::API
         | 
| 441 | 
            +
                  
         | 
| 442 | 
            +
                  include AbstractController::Translation
         | 
| 443 | 
            +
                  include ActionController::HttpAuthentication::Basic::ControllerMethods
         | 
| 444 | 
            +
                  include AbstractController::Layouts
         | 
| 445 | 
            +
                  include ActionController::MimeResponds
         | 
| 446 | 
            +
                  include ActionController::Cookies
         | 
| 447 | 
            +
                  include ActionController::ParamsWrapper
         | 
| 448 | 
            +
                  acts_as_restful_json
         | 
| 449 | 
            +
                  
         | 
| 450 | 
            +
                  def current_user
         | 
| 451 | 
            +
                    User.new
         | 
| 452 | 
            +
                  end
         | 
| 453 | 
            +
                  
         | 
| 454 | 
            +
                end
         | 
| 455 | 
            +
             | 
| 456 | 
            +
            Note that in `/config/initializers/wrap_parameters.rb` you might need to add `include ActionController::ParamsWrapper` prior to the `wrap_parameters` call. For example, for unwrapped JSON, it would look like:
         | 
| 457 | 
            +
             | 
| 458 | 
            +
                ActiveSupport.on_load(:action_controller) do
         | 
| 459 | 
            +
                  # without include of ParamsWrapper, will get undefined method `wrap_parameters' for ActionController::API:Class (NoMethodError)
         | 
| 460 | 
            +
                  include ActionController::ParamsWrapper
         | 
| 461 | 
            +
                  # in this case it's expecting unwrapped params, but we could maybe use wrap_parameters format: [:json]
         | 
| 462 | 
            +
                  wrap_parameters format: []
         | 
| 463 | 
            +
                end
         | 
| 464 | 
            +
             | 
| 465 | 
            +
                # Disable root element in JSON by default.
         | 
| 466 | 
            +
                ActiveSupport.on_load(:active_record) do
         | 
| 467 | 
            +
                  self.include_root_in_json = false
         | 
| 468 | 
            +
                end
         | 
| 469 | 
            +
             | 
| 470 | 
            +
            #### Customing the Default Behavior
         | 
| 471 | 
            +
             | 
| 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'.
         | 
| 534 | 
            +
             | 
| 535 | 
            +
            ### Thanks!
         | 
| 536 | 
            +
             | 
| 537 | 
            +
            Without our users, where would we be? Feedback, bug reports, and code/documentation contributions are always welcome!
         | 
| 538 | 
            +
             | 
| 282 539 | 
             
            ### Contributors
         | 
| 283 540 |  | 
| 284 541 | 
             
            * Gary Weaver (https://github.com/garysweaver)
         | 
| @@ -288,6 +545,8 @@ Respects regular and nested Rails resourceful routing and controller namespacing | |
| 288 545 |  | 
| 289 546 | 
             
            Copyright (c) 2012 Gary S. Weaver, released under the [MIT license][lic].
         | 
| 290 547 |  | 
| 548 | 
            +
            [employee-training-tracker]: https://github.com/FineLinePrototyping/employee-training-tracker
         | 
| 549 | 
            +
            [built_with_angularjs]: http://builtwith.angularjs.org/
         | 
| 291 550 | 
             
            [permitter]: http://broadcastingadam.com/2012/07/parameter_authorization_in_rails_apis/
         | 
| 292 551 | 
             
            [cancan]: https://github.com/ryanb/cancan
         | 
| 293 552 | 
             
            [strong_parameters]: https://github.com/rails/strong_parameters
         | 
| @@ -296,4 +555,6 @@ Copyright (c) 2012 Gary S. Weaver, released under the [MIT license][lic]. | |
| 296 555 | 
             
            [devise]: https://github.com/plataformatec/devise
         | 
| 297 556 | 
             
            [arel]: https://github.com/rails/arel
         | 
| 298 557 | 
             
            [ar]: http://api.rubyonrails.org/classes/ActiveRecord/Relation.html
         | 
| 558 | 
            +
            [rails-api]: https://github.com/rails-api/rails-api
         | 
| 559 | 
            +
            [railscast320]: http://railscasts.com/episodes/320-jbuilder
         | 
| 299 560 | 
             
            [lic]: http://github.com/rubyservices/restful_json/blob/master/LICENSE
         | 
    
        data/lib/restful_json/config.rb
    CHANGED
    
    | @@ -6,7 +6,8 @@ module RestfulJson | |
| 6 6 | 
             
                :formats,
         | 
| 7 7 | 
             
                :number_of_records_in_a_page,
         | 
| 8 8 | 
             
                :predicate_prefix,
         | 
| 9 | 
            -
                :return_resource
         | 
| 9 | 
            +
                :return_resource,
         | 
| 10 | 
            +
                :render_enabled
         | 
| 10 11 | 
             
              ]
         | 
| 11 12 |  | 
| 12 13 | 
             
              class << self
         | 
| @@ -24,4 +25,5 @@ RestfulJson.configure do | |
| 24 25 | 
             
              self.number_of_records_in_a_page = 15
         | 
| 25 26 | 
             
              self.predicate_prefix = '!'
         | 
| 26 27 | 
             
              self.return_resource = false
         | 
| 28 | 
            +
              self.render_enabled = true
         | 
| 27 29 | 
             
            end
         | 
| @@ -40,6 +40,7 @@ module RestfulJson | |
| 40 40 | 
             
                    class_attribute :action_to_query, instance_writer: true
         | 
| 41 41 | 
             
                    class_attribute :param_to_query, instance_writer: true
         | 
| 42 42 | 
             
                    class_attribute :param_to_through, instance_writer: true
         | 
| 43 | 
            +
                    class_attribute :action_to_serializer, instance_writer: true
         | 
| 43 44 |  | 
| 44 45 | 
             
                    # use values from config
         | 
| 45 46 | 
             
                    # debug uses RestfulJson.debug? because until this is done no local debug class attribute exists to check
         | 
| @@ -54,6 +55,7 @@ module RestfulJson | |
| 54 55 | 
             
                    self.action_to_query ||= {}
         | 
| 55 56 | 
             
                    self.param_to_query ||= {}
         | 
| 56 57 | 
             
                    self.param_to_through ||= {}
         | 
| 58 | 
            +
                    self.action_to_serializer ||= {}
         | 
| 57 59 | 
             
                  end
         | 
| 58 60 |  | 
| 59 61 | 
             
                  module ClassMethods
         | 
| @@ -134,6 +136,21 @@ module RestfulJson | |
| 134 136 | 
             
                    def order_by(args)
         | 
| 135 137 | 
             
                      self.ordered_by = (Array.wrap(self.ordered_by) + Array.wrap(args)).flatten.compact.collect {|item|item.is_a?(Hash) ? item : {item.to_sym => :asc}}
         | 
| 136 138 | 
             
                    end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                    # Associate a non-standard ActiveModel Serializer for one or more actions, e.g.
         | 
| 141 | 
            +
                    #    serialize_action :index, with: FoosSerializer
         | 
| 142 | 
            +
                    # or
         | 
| 143 | 
            +
                    #    serialize_action :index, :some_custom_action, with: FoosSerializer
         | 
| 144 | 
            +
                    def serialize_action(*args)
         | 
| 145 | 
            +
                      options = args.extract_options!
         | 
| 146 | 
            +
                      args.each do |an_action|
         | 
| 147 | 
            +
                        if options[:with]
         | 
| 148 | 
            +
                          self.action_to_serializer[an_action.to_s] = options[:with]
         | 
| 149 | 
            +
                        else
         | 
| 150 | 
            +
                          raise "#{self.class.name} must supply an :with option with serialize_action #{an_action.inspect}"
         | 
| 151 | 
            +
                        end
         | 
| 152 | 
            +
                      end
         | 
| 153 | 
            +
                    end
         | 
| 137 154 | 
             
                  end
         | 
| 138 155 |  | 
| 139 156 | 
             
                  # In initialize we:
         | 
| @@ -268,7 +285,11 @@ module RestfulJson | |
| 268 285 |  | 
| 269 286 | 
             
                    @value = value
         | 
| 270 287 | 
             
                    instance_variable_set(@model_at_plural_name_sym, @value)
         | 
| 271 | 
            -
                     | 
| 288 | 
            +
                    
         | 
| 289 | 
            +
                    if self.render_enabled  
         | 
| 290 | 
            +
                      custom_action_serializer = self.action_to_serializer[params[:action].to_s]
         | 
| 291 | 
            +
                      respond_with @value, custom_action_serializer ? {serializer: custom_action_serializer} : {}
         | 
| 292 | 
            +
                    end
         | 
| 272 293 | 
             
                  end
         | 
| 273 294 |  | 
| 274 295 | 
             
                  # The controller's show (get) method to return a resource.
         | 
| @@ -276,13 +297,21 @@ module RestfulJson | |
| 276 297 | 
             
                    # to_s as safety measure for vulnerabilities similar to CVE-2013-1854
         | 
| 277 298 | 
             
                    @value = @model_class.find(params[:id].to_s)
         | 
| 278 299 | 
             
                    instance_variable_set(@model_at_singular_name_sym, @value)
         | 
| 279 | 
            -
                     | 
| 300 | 
            +
                    
         | 
| 301 | 
            +
                    if self.render_enabled  
         | 
| 302 | 
            +
                      custom_action_serializer = self.action_to_serializer[params[:action].to_s]
         | 
| 303 | 
            +
                      respond_with @value, custom_action_serializer ? {serializer: custom_action_serializer} : {}
         | 
| 304 | 
            +
                    end
         | 
| 280 305 | 
             
                  end
         | 
| 281 306 |  | 
| 282 307 | 
             
                  # The controller's new method (e.g. used for new record in html format).
         | 
| 283 308 | 
             
                  def new
         | 
| 284 309 | 
             
                    @value = @model_class.new
         | 
| 285 | 
            -
             | 
| 310 | 
            +
             | 
| 311 | 
            +
                    if self.render_enabled  
         | 
| 312 | 
            +
                      custom_action_serializer = self.action_to_serializer[params[:action].to_s]
         | 
| 313 | 
            +
                      respond_with @value, custom_action_serializer ? {serializer: custom_action_serializer} : {}
         | 
| 314 | 
            +
                    end
         | 
| 286 315 | 
             
                  end
         | 
| 287 316 |  | 
| 288 317 | 
             
                  # The controller's edit method (e.g. used for edit record in html format).
         | 
| @@ -298,18 +327,27 @@ module RestfulJson | |
| 298 327 | 
             
                    @value = @model_class.new(permitted_params)
         | 
| 299 328 | 
             
                    @value.save
         | 
| 300 329 | 
             
                    instance_variable_set(@model_at_singular_name_sym, @value)
         | 
| 301 | 
            -
             | 
| 302 | 
            -
             | 
| 303 | 
            -
             | 
| 304 | 
            -
             | 
| 305 | 
            -
             | 
| 306 | 
            -
             | 
| 307 | 
            -
             | 
| 330 | 
            +
             | 
| 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 | 
            +
                    if self.render_enabled
         | 
| 337 | 
            +
                      custom_action_serializer = self.action_to_serializer[params[:action].to_s]
         | 
| 338 | 
            +
                      if !@value.nil? && RestfulJson.return_resource
         | 
| 339 | 
            +
                        respond_with(@value) do |format|
         | 
| 340 | 
            +
                          format.json do
         | 
| 341 | 
            +
                            if @value.errors.empty?
         | 
| 342 | 
            +
                              render custom_action_serializer ? {json: @value, status: :created, serializer: custom_action_serializer} : {json: @value, status: :created}
         | 
| 343 | 
            +
                            else
         | 
| 344 | 
            +
                              render custom_action_serializer ? {json: {errors: @value.errors}, status: :unprocessable_entity, serializer: custom_action_serializer} : {json: {errors: @value.errors}, status: :unprocessable_entity}
         | 
| 345 | 
            +
                            end
         | 
| 308 346 | 
             
                          end
         | 
| 309 347 | 
             
                        end
         | 
| 348 | 
            +
                      else
         | 
| 349 | 
            +
                        respond_with @value, custom_action_serializer ? {serializer: custom_action_serializer} : {}
         | 
| 310 350 | 
             
                      end
         | 
| 311 | 
            -
                    else
         | 
| 312 | 
            -
                      respond_with @value
         | 
| 313 351 | 
             
                    end
         | 
| 314 352 | 
             
                  end
         | 
| 315 353 |  | 
| @@ -320,18 +358,22 @@ module RestfulJson | |
| 320 358 | 
             
                    @value = @model_class.find(params[:id].to_s)
         | 
| 321 359 | 
             
                    @value.update_attributes(permitted_params)
         | 
| 322 360 | 
             
                    instance_variable_set(@model_at_singular_name_sym, @value)
         | 
| 323 | 
            -
                     | 
| 324 | 
            -
             | 
| 325 | 
            -
             | 
| 326 | 
            -
             | 
| 327 | 
            -
             | 
| 328 | 
            -
                           | 
| 329 | 
            -
                             | 
| 361 | 
            +
                    
         | 
| 362 | 
            +
                    if self.render_enabled
         | 
| 363 | 
            +
                      custom_action_serializer = self.action_to_serializer[params[:action].to_s]
         | 
| 364 | 
            +
                      if !@value.nil? && RestfulJson.return_resource
         | 
| 365 | 
            +
                        respond_with(@value) do |format|
         | 
| 366 | 
            +
                          format.json do
         | 
| 367 | 
            +
                            if @value.errors.empty?
         | 
| 368 | 
            +
                              render custom_action_serializer ? {json: @value, status: :ok, serializer: custom_action_serializer} : {json: @value, status: :ok}
         | 
| 369 | 
            +
                            else
         | 
| 370 | 
            +
                              render custom_action_serializer ? {json: {errors: @value.errors}, status: :unprocessable_entity, serializer: custom_action_serializer} : {json: {errors: @value.errors}, status: :unprocessable_entity}
         | 
| 371 | 
            +
                            end
         | 
| 330 372 | 
             
                          end
         | 
| 331 373 | 
             
                        end
         | 
| 374 | 
            +
                      else
         | 
| 375 | 
            +
                        respond_with @value, custom_action_serializer ? {serializer: custom_action_serializer} : {}
         | 
| 332 376 | 
             
                      end
         | 
| 333 | 
            -
                    else
         | 
| 334 | 
            -
                      respond_with @value
         | 
| 335 377 | 
             
                    end
         | 
| 336 378 | 
             
                  end
         | 
| 337 379 |  | 
| @@ -341,7 +383,10 @@ module RestfulJson | |
| 341 383 | 
             
                    @value = @model_class.find(params[:id].to_s)
         | 
| 342 384 | 
             
                    @value.destroy
         | 
| 343 385 | 
             
                    instance_variable_set(@model_at_singular_name_sym, @value)
         | 
| 344 | 
            -
             | 
| 386 | 
            +
             | 
| 387 | 
            +
                    if self.render_enabled
         | 
| 388 | 
            +
                      respond_with @value, custom_action_serializer ? {serializer: custom_action_serializer} : {}
         | 
| 389 | 
            +
                    end
         | 
| 345 390 | 
             
                  end
         | 
| 346 391 |  | 
| 347 392 | 
             
                end
         | 
    
        data/lib/restful_json/version.rb
    CHANGED
    
    
| @@ -0,0 +1,93 @@ | |
| 1 | 
            +
            # from Adam Hawkins's gist:
         | 
| 2 | 
            +
            # https://gist.github.com/3150306
         | 
| 3 | 
            +
            # http://www.broadcastingadam.com/2012/07/parameter_authorization_in_rails_apis/
         | 
| 4 | 
            +
            class ApplicationPermitter
         | 
| 5 | 
            +
              class PermittedAttribute < Struct.new(:name, :options) ; end
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              delegate :authorize!, :to => :ability
         | 
| 8 | 
            +
              class_attribute :permitted_attributes
         | 
| 9 | 
            +
              self.permitted_attributes = []
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              class << self
         | 
| 12 | 
            +
                def permit(*args)
         | 
| 13 | 
            +
                  options = args.extract_options!
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  args.each do |name|
         | 
| 16 | 
            +
                    self.permitted_attributes += [PermittedAttribute.new(name, options)]
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def scope(name)
         | 
| 21 | 
            +
                  with_options :scope => name do |nested|
         | 
| 22 | 
            +
                    yield nested
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
              end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              def initialize(params, user, ability = nil)
         | 
| 28 | 
            +
                @params, @user, @ability = params, user, ability
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              def permitted_params
         | 
| 32 | 
            +
                authorize_params!
         | 
| 33 | 
            +
                filtered_params
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              def resource_name
         | 
| 37 | 
            +
                self.class.to_s.match(/(.+)Permitter/)[1].underscore.to_sym
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
             | 
| 41 | 
            +
            private
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              def authorize_params!
         | 
| 44 | 
            +
                needing_authorization = permitted_attributes.select { |a| a.options[:authorize] }
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                needing_authorization.each do |attribute|
         | 
| 47 | 
            +
                  if attribute.options[:scope]
         | 
| 48 | 
            +
                    values = Array.wrap(filtered_params[attribute.options[:scope]]).collect do |hash|
         | 
| 49 | 
            +
                      hash[attribute.name]
         | 
| 50 | 
            +
                    end.compact
         | 
| 51 | 
            +
                  else
         | 
| 52 | 
            +
                    values = Array.wrap filtered_params[attribute.name]
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  klass = (attribute.options[:as].try(:to_s) || attribute.name.to_s.split(/(.+)_ids?/)[1]).classify.constantize
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                  values.each do |record_id|
         | 
| 58 | 
            +
                    record = klass.find record_id
         | 
| 59 | 
            +
                    permission = attribute.options[:authorize].to_sym || :read
         | 
| 60 | 
            +
                    authorize! permission, record
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
              def filtered_params
         | 
| 66 | 
            +
                scopes = {}
         | 
| 67 | 
            +
                unscoped_attributes = []
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                permitted_attributes.each do |attribute|
         | 
| 70 | 
            +
                  if attribute.options[:scope]
         | 
| 71 | 
            +
                    key = attribute.options[:scope]
         | 
| 72 | 
            +
                    scopes[key] ||= []
         | 
| 73 | 
            +
                    scopes[key] << attribute.name
         | 
| 74 | 
            +
                  else
         | 
| 75 | 
            +
                    unscoped_attributes << attribute.name
         | 
| 76 | 
            +
                  end
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                @filtered_params ||= params.require(resource_name).permit(*unscoped_attributes, scopes)
         | 
| 80 | 
            +
              end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
              def params
         | 
| 83 | 
            +
                @params
         | 
| 84 | 
            +
              end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
              def user
         | 
| 87 | 
            +
                @user
         | 
| 88 | 
            +
              end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
              def ability
         | 
| 91 | 
            +
                @ability ||= Ability.new user
         | 
| 92 | 
            +
              end
         | 
| 93 | 
            +
            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.0 | 
| 4 | 
            +
              version: 3.1.0
         | 
| 5 5 | 
             
              prerelease: 
         | 
| 6 6 | 
             
            platform: ruby
         | 
| 7 7 | 
             
            authors:
         | 
| @@ -10,40 +10,8 @@ authors: | |
| 10 10 | 
             
            autorequire: 
         | 
| 11 11 | 
             
            bindir: bin
         | 
| 12 12 | 
             
            cert_chain: []
         | 
| 13 | 
            -
            date: 2013-03- | 
| 13 | 
            +
            date: 2013-03-22 00:00:00.000000000 Z
         | 
| 14 14 | 
             
            dependencies:
         | 
| 15 | 
            -
            - !ruby/object:Gem::Dependency
         | 
| 16 | 
            -
              name: rails-api
         | 
| 17 | 
            -
              requirement: !ruby/object:Gem::Requirement
         | 
| 18 | 
            -
                none: false
         | 
| 19 | 
            -
                requirements:
         | 
| 20 | 
            -
                - - ! '>='
         | 
| 21 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 22 | 
            -
                    version: '0'
         | 
| 23 | 
            -
              type: :runtime
         | 
| 24 | 
            -
              prerelease: false
         | 
| 25 | 
            -
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 26 | 
            -
                none: false
         | 
| 27 | 
            -
                requirements:
         | 
| 28 | 
            -
                - - ! '>='
         | 
| 29 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 30 | 
            -
                    version: '0'
         | 
| 31 | 
            -
            - !ruby/object:Gem::Dependency
         | 
| 32 | 
            -
              name: active_model_serializers
         | 
| 33 | 
            -
              requirement: !ruby/object:Gem::Requirement
         | 
| 34 | 
            -
                none: false
         | 
| 35 | 
            -
                requirements:
         | 
| 36 | 
            -
                - - ! '>='
         | 
| 37 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 38 | 
            -
                    version: '0'
         | 
| 39 | 
            -
              type: :runtime
         | 
| 40 | 
            -
              prerelease: false
         | 
| 41 | 
            -
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 42 | 
            -
                none: false
         | 
| 43 | 
            -
                requirements:
         | 
| 44 | 
            -
                - - ! '>='
         | 
| 45 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 46 | 
            -
                    version: '0'
         | 
| 47 15 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 48 16 | 
             
              name: actionpack
         | 
| 49 17 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -51,7 +19,7 @@ dependencies: | |
| 51 19 | 
             
                requirements:
         | 
| 52 20 | 
             
                - - ! '>='
         | 
| 53 21 | 
             
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            -
                    version:  | 
| 22 | 
            +
                    version: 3.1.0
         | 
| 55 23 | 
             
              type: :runtime
         | 
| 56 24 | 
             
              prerelease: false
         | 
| 57 25 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| @@ -59,7 +27,7 @@ dependencies: | |
| 59 27 | 
             
                requirements:
         | 
| 60 28 | 
             
                - - ! '>='
         | 
| 61 29 | 
             
                  - !ruby/object:Gem::Version
         | 
| 62 | 
            -
                    version:  | 
| 30 | 
            +
                    version: 3.1.0
         | 
| 63 31 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 64 32 | 
             
              name: activerecord
         | 
| 65 33 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -67,7 +35,7 @@ dependencies: | |
| 67 35 | 
             
                requirements:
         | 
| 68 36 | 
             
                - - ! '>='
         | 
| 69 37 | 
             
                  - !ruby/object:Gem::Version
         | 
| 70 | 
            -
                    version:  | 
| 38 | 
            +
                    version: 3.1.0
         | 
| 71 39 | 
             
              type: :runtime
         | 
| 72 40 | 
             
              prerelease: false
         | 
| 73 41 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| @@ -75,7 +43,7 @@ dependencies: | |
| 75 43 | 
             
                requirements:
         | 
| 76 44 | 
             
                - - ! '>='
         | 
| 77 45 | 
             
                  - !ruby/object:Gem::Version
         | 
| 78 | 
            -
                    version:  | 
| 46 | 
            +
                    version: 3.1.0
         | 
| 79 47 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 80 48 | 
             
              name: cancan
         | 
| 81 49 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -83,7 +51,7 @@ dependencies: | |
| 83 51 | 
             
                requirements:
         | 
| 84 52 | 
             
                - - ! '>='
         | 
| 85 53 | 
             
                  - !ruby/object:Gem::Version
         | 
| 86 | 
            -
                    version:  | 
| 54 | 
            +
                    version: 1.6.7
         | 
| 87 55 | 
             
              type: :runtime
         | 
| 88 56 | 
             
              prerelease: false
         | 
| 89 57 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| @@ -91,7 +59,7 @@ dependencies: | |
| 91 59 | 
             
                requirements:
         | 
| 92 60 | 
             
                - - ! '>='
         | 
| 93 61 | 
             
                  - !ruby/object:Gem::Version
         | 
| 94 | 
            -
                    version:  | 
| 62 | 
            +
                    version: 1.6.7
         | 
| 95 63 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 96 64 | 
             
              name: strong_parameters
         | 
| 97 65 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -99,7 +67,7 @@ dependencies: | |
| 99 67 | 
             
                requirements:
         | 
| 100 68 | 
             
                - - ! '>='
         | 
| 101 69 | 
             
                  - !ruby/object:Gem::Version
         | 
| 102 | 
            -
                    version:  | 
| 70 | 
            +
                    version: 0.1.3
         | 
| 103 71 | 
             
              type: :runtime
         | 
| 104 72 | 
             
              prerelease: false
         | 
| 105 73 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| @@ -107,7 +75,7 @@ dependencies: | |
| 107 75 | 
             
                requirements:
         | 
| 108 76 | 
             
                - - ! '>='
         | 
| 109 77 | 
             
                  - !ruby/object:Gem::Version
         | 
| 110 | 
            -
                    version:  | 
| 78 | 
            +
                    version: 0.1.3
         | 
| 111 79 | 
             
            description: Develop declarative, featureful JSON RESTful-ish service controllers
         | 
| 112 80 | 
             
              to use with modern Javascript MVC frameworks like AngularJS, Ember, etc. with much
         | 
| 113 81 | 
             
              less code.
         | 
| @@ -124,6 +92,7 @@ files: | |
| 124 92 | 
             
            - lib/restful_json/railtie.rb
         | 
| 125 93 | 
             
            - lib/restful_json/version.rb
         | 
| 126 94 | 
             
            - lib/restful_json.rb
         | 
| 95 | 
            +
            - lib/twinturbo/application_permitter.rb
         | 
| 127 96 | 
             
            - lib/twinturbo/controller.rb
         | 
| 128 97 | 
             
            - Rakefile
         | 
| 129 98 | 
             
            - README.md
         |