restfulness 0.3.2 → 0.3.3
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.
- checksums.yaml +4 -4
 - data/.gitignore +1 -0
 - data/.travis.yml +6 -5
 - data/README.md +98 -80
 - data/lib/restfulness.rb +3 -0
 - data/lib/restfulness/dispatchers/rack.rb +1 -0
 - data/lib/restfulness/headers/accept.rb +66 -0
 - data/lib/restfulness/headers/media_type.rb +127 -0
 - data/lib/restfulness/request.rb +43 -15
 - data/lib/restfulness/version.rb +1 -1
 - data/restfulness.gemspec +1 -1
 - data/spec/spec_helper.rb +1 -1
 - data/spec/unit/application_spec.rb +14 -14
 - data/spec/unit/dispatcher_spec.rb +1 -1
 - data/spec/unit/dispatchers/rack_spec.rb +21 -20
 - data/spec/unit/exceptions_spec.rb +5 -5
 - data/spec/unit/headers/accept_spec.rb +70 -0
 - data/spec/unit/headers/media_type_spec.rb +262 -0
 - data/spec/unit/http_authentication/basic_spec.rb +7 -7
 - data/spec/unit/path_spec.rb +19 -19
 - data/spec/unit/request_spec.rb +96 -44
 - data/spec/unit/requests/authorization_header_spec.rb +8 -8
 - data/spec/unit/requests/authorization_spec.rb +3 -3
 - data/spec/unit/resource_spec.rb +28 -28
 - data/spec/unit/resources/authentication_spec.rb +2 -2
 - data/spec/unit/resources/events_spec.rb +1 -1
 - data/spec/unit/response_spec.rb +53 -53
 - data/spec/unit/route_spec.rb +24 -24
 - data/spec/unit/router_spec.rb +29 -29
 - data/spec/unit/sanitizer_spec.rb +9 -9
 - metadata +13 -7
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: ce9b42d05b92e2107f89ce16f36a5f1bbfabe6e7
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: b9cb7bdc241661ab063b45f20a81a967f43eeee6
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 7809aad851643407a59b3d376a75d39c4a1f703cf18cc647817e018971434e547dd8fbfcbd6b01e6dafb3aa7e510eee7233ca5a75ecf839239b52596e5bac5ca
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: f8fbe0c48f0a05bd5f50f48ccb56545afed3408ed5d4941ffd7db5303dbb107203b6656de0c7f13cf937bb88ee716dd2b7d8a7499681abdfbdc2492dc3f14206
         
     | 
    
        data/.gitignore
    CHANGED
    
    
    
        data/.travis.yml
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | 
         @@ -6,93 +6,97 @@ Because REST APIs are all about resources, not routes. 
     | 
|
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
            ## Introduction
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
            Restfulness is  
     | 
| 
      
 9 
     | 
    
         
            +
            Restfulness is a simple Ruby library for creating REST APIs. Each endpoint defined in the routing configuration refers to a resource class containing HTTP actions and callbacks. When an HTTP request is received, the callbacks are checked, and the appropriate action is called to provide a response.
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
      
 11 
     | 
    
         
            +
            When creating Restfulness, we had a set of objectives we wanted to achieve:
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
      
 13 
     | 
    
         
            +
             * A true "resource" orientated interface.
         
     | 
| 
      
 14 
     | 
    
         
            +
             * Simple routing.
         
     | 
| 
      
 15 
     | 
    
         
            +
             * Fast.
         
     | 
| 
      
 16 
     | 
    
         
            +
             * JSON only responses.
         
     | 
| 
      
 17 
     | 
    
         
            +
             * Take advantage of HTTP flow control using callbacks.
         
     | 
| 
      
 18 
     | 
    
         
            +
             * Simple error handling, and "instant abort" exceptions.
         
     | 
| 
       14 
19 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
      
 20 
     | 
    
         
            +
            Here's a code example of what the restfulness side of a rack application might look like:
         
     | 
| 
       16 
21 
     | 
    
         | 
| 
       17 
22 
     | 
    
         
             
            ```ruby
         
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
                 
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
                  desc "Return a public timeline."
         
     | 
| 
       27 
     | 
    
         
            -
                  get :public_timeline do
         
     | 
| 
       28 
     | 
    
         
            -
                    Status.limit(20)
         
     | 
| 
       29 
     | 
    
         
            -
                  end
         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
                  desc "Return a personal timeline."
         
     | 
| 
       32 
     | 
    
         
            -
                  get :home_timeline do
         
     | 
| 
       33 
     | 
    
         
            -
                    authenticate!
         
     | 
| 
       34 
     | 
    
         
            -
                    current_user.statuses.limit(20)
         
     | 
| 
       35 
     | 
    
         
            -
                  end
         
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
                  desc "Return a status."
         
     | 
| 
       38 
     | 
    
         
            -
                  params do
         
     | 
| 
       39 
     | 
    
         
            -
                    requires :id, type: Integer, desc: "Status id."
         
     | 
| 
       40 
     | 
    
         
            -
                  end
         
     | 
| 
       41 
     | 
    
         
            -
                  route_param :id do
         
     | 
| 
       42 
     | 
    
         
            -
                    get do
         
     | 
| 
       43 
     | 
    
         
            -
                      Status.find(params[:id])
         
     | 
| 
       44 
     | 
    
         
            -
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
            # The API definition, this matches incoming request paths to resources
         
     | 
| 
      
 24 
     | 
    
         
            +
            class TwitterAPI < Restfullness::Application
         
     | 
| 
      
 25 
     | 
    
         
            +
              routes do
         
     | 
| 
      
 26 
     | 
    
         
            +
                scope 'api' do
         
     | 
| 
      
 27 
     | 
    
         
            +
                  add 'task',      Tasks::ItemResource
         
     | 
| 
      
 28 
     | 
    
         
            +
                  scope 'tasks' do
         
     | 
| 
      
 29 
     | 
    
         
            +
                    add 'public',  Tasks::PublicResource
         
     | 
| 
      
 30 
     | 
    
         
            +
                    add 'private', Tasks::PrivateResource
         
     | 
| 
       45 
31 
     | 
    
         
             
                  end
         
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
32 
     | 
    
         
             
                end
         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
33 
     | 
    
         
             
              end
         
     | 
| 
       50 
34 
     | 
    
         
             
            end
         
     | 
| 
       51 
35 
     | 
    
         | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
      
 36 
     | 
    
         
            +
            # Modules are always a good idea to group resources
         
     | 
| 
      
 37 
     | 
    
         
            +
            module Tasks
         
     | 
| 
      
 38 
     | 
    
         
            +
              # A simple resource for returning tasks
         
     | 
| 
      
 39 
     | 
    
         
            +
              class ItemResource < Restfulness::Resource
         
     | 
| 
      
 40 
     | 
    
         
            +
                # Callback to see if task exsists
         
     | 
| 
      
 41 
     | 
    
         
            +
                def exists?
         
     | 
| 
      
 42 
     | 
    
         
            +
                  !!task
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
       53 
44 
     | 
    
         | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
      
 45 
     | 
    
         
            +
                # Provide the task, if the #exists? call worked
         
     | 
| 
      
 46 
     | 
    
         
            +
                def get
         
     | 
| 
      
 47 
     | 
    
         
            +
                  task  
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
       55 
49 
     | 
    
         | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
      
 50 
     | 
    
         
            +
                # Create a new task, this will bypass the #exits? call
         
     | 
| 
      
 51 
     | 
    
         
            +
                def post
         
     | 
| 
      
 52 
     | 
    
         
            +
                  Task.create(request.params)
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
       57 
54 
     | 
    
         | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
                add 'status',             StatusResource
         
     | 
| 
       62 
     | 
    
         
            -
                scope 'timeline' do
         
     | 
| 
       63 
     | 
    
         
            -
                  add 'public', Timelines::PublicResource
         
     | 
| 
       64 
     | 
    
         
            -
                  add 'home',   Timelines::HomeResource
         
     | 
| 
      
 55 
     | 
    
         
            +
                # Update the task, and raise an error with error response if failed
         
     | 
| 
      
 56 
     | 
    
         
            +
                def patch
         
     | 
| 
      
 57 
     | 
    
         
            +
                  task.update_attributes(request.params) || forbidden!(task.errors)
         
     | 
| 
       65 
58 
     | 
    
         
             
                end
         
     | 
| 
       66 
     | 
    
         
            -
              end
         
     | 
| 
       67 
     | 
    
         
            -
            end
         
     | 
| 
       68 
59 
     | 
    
         | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
                 
     | 
| 
      
 60 
     | 
    
         
            +
                protected
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                def task
         
     | 
| 
      
 63 
     | 
    
         
            +
                  @task ||= Task.find(request.path[:id])
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
       72 
65 
     | 
    
         
             
              end
         
     | 
| 
       73 
     | 
    
         
            -
            end
         
     | 
| 
       74 
66 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
      
 67 
     | 
    
         
            +
              # Simple resource that provides list of public tasks, that may be empty
         
     | 
| 
       76 
68 
     | 
    
         
             
              class PublicResource < Restfulness::Resource
         
     | 
| 
       77 
69 
     | 
    
         
             
                def get
         
     | 
| 
       78 
     | 
    
         
            -
                   
     | 
| 
      
 70 
     | 
    
         
            +
                  Task.public.limit(20)
         
     | 
| 
       79 
71 
     | 
    
         
             
                end
         
     | 
| 
       80 
72 
     | 
    
         
             
              end
         
     | 
| 
       81 
73 
     | 
    
         | 
| 
       82 
     | 
    
         
            -
              #  
     | 
| 
       83 
     | 
    
         
            -
              class  
     | 
| 
      
 74 
     | 
    
         
            +
              # Authorization requires additional code to authenticate the user
         
     | 
| 
      
 75 
     | 
    
         
            +
              class PrivateResource < Restfulness::Resource
         
     | 
| 
      
 76 
     | 
    
         
            +
                # If this fails, abort and return 401 Unauthorized response
         
     | 
| 
       84 
77 
     | 
    
         
             
                def authorized?
         
     | 
| 
       85 
     | 
    
         
            -
                   
     | 
| 
      
 78 
     | 
    
         
            +
                  !current_user
         
     | 
| 
       86 
79 
     | 
    
         
             
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                # Assuming authorized, attept to load tasks
         
     | 
| 
       87 
82 
     | 
    
         
             
                def get
         
     | 
| 
       88 
     | 
    
         
            -
                  current_user. 
     | 
| 
      
 83 
     | 
    
         
            +
                  current_user.tasks.limit(20)
         
     | 
| 
      
 84 
     | 
    
         
            +
                end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                protected
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                # Very simple example of authentication
         
     | 
| 
      
 89 
     | 
    
         
            +
                def current_user
         
     | 
| 
      
 90 
     | 
    
         
            +
                  @current_user ||= authenticate_with_http_basic do |username, password|
         
     | 
| 
      
 91 
     | 
    
         
            +
                    User.authenticate(username, password)
         
     | 
| 
      
 92 
     | 
    
         
            +
                  end
         
     | 
| 
       89 
93 
     | 
    
         
             
                end
         
     | 
| 
       90 
94 
     | 
    
         
             
              end
         
     | 
| 
       91 
95 
     | 
    
         
             
            end
         
     | 
| 
       92 
96 
     | 
    
         | 
| 
       93 
97 
     | 
    
         
             
            ```
         
     | 
| 
       94 
98 
     | 
    
         | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
      
 99 
     | 
    
         
            +
            Checkout the rest of this document for more of the details on the api, integration with your existing apps, and additional features.
         
     | 
| 
       96 
100 
     | 
    
         | 
| 
       97 
101 
     | 
    
         | 
| 
       98 
102 
     | 
    
         
             
            ## Installation
         
     | 
| 
         @@ -159,7 +163,7 @@ The aim of routes in Restfulness are to be stupid simple. These are the basic ru 
     | 
|
| 
       159 
163 
     | 
    
         
             
             * Order is important.
         
     | 
| 
       160 
164 
     | 
    
         
             
             * Strings are matched directly.
         
     | 
| 
       161 
165 
     | 
    
         
             
             * Symbols match anything, and are accessible as path attributes.
         
     | 
| 
       162 
     | 
    
         
            -
             * Every route  
     | 
| 
      
 166 
     | 
    
         
            +
             * Every route automatically gets an :id parameter at the end, that may or may not have a null value.
         
     | 
| 
       163 
167 
     | 
    
         
             
             * Scopes save repeating shared route array entries.
         
     | 
| 
       164 
168 
     | 
    
         | 
| 
       165 
169 
     | 
    
         
             
            Lets see a few examples:
         
     | 
| 
         @@ -204,7 +208,7 @@ end 
     | 
|
| 
       204 
208 
     | 
    
         | 
| 
       205 
209 
     | 
    
         
             
            ### Resources
         
     | 
| 
       206 
210 
     | 
    
         | 
| 
       207 
     | 
    
         
            -
            Resources are like  
     | 
| 
      
 211 
     | 
    
         
            +
            Resources are like controllers in a Rails project. They handle the basic HTTP actions using methods that match the same name as the action. The result of an action is serialized into a JSON object automatically. The actions supported by a resource are:
         
     | 
| 
       208 
212 
     | 
    
         | 
| 
       209 
213 
     | 
    
         
             
             * `get`
         
     | 
| 
       210 
214 
     | 
    
         
             
             * `head`
         
     | 
| 
         @@ -212,7 +216,7 @@ Resources are like Controllers in a Rails project. They handle the basic HTTP ac 
     | 
|
| 
       212 
216 
     | 
    
         
             
             * `patch`
         
     | 
| 
       213 
217 
     | 
    
         
             
             * `put`
         
     | 
| 
       214 
218 
     | 
    
         
             
             * `delete`
         
     | 
| 
       215 
     | 
    
         
            -
             * `options` - this is the only action  
     | 
| 
      
 219 
     | 
    
         
            +
             * `options` - this is the only action provided by default
         
     | 
| 
       216 
220 
     | 
    
         | 
| 
       217 
221 
     | 
    
         
             
            When creating your resource, simply define the methods you'd like to use and ensure each has a result:
         
     | 
| 
       218 
222 
     | 
    
         | 
| 
         @@ -286,11 +290,11 @@ end 
     | 
|
| 
       286 
290 
     | 
    
         | 
| 
       287 
291 
     | 
    
         
             
            #### I18n in Resources
         
     | 
| 
       288 
292 
     | 
    
         | 
| 
       289 
     | 
    
         
            -
            Restfulness uses the [http_accept_language](https://github.com/iain/http_accept_language) gem to automatically handle the `Accept-Language` header  
     | 
| 
      
 293 
     | 
    
         
            +
            Restfulness uses the [http_accept_language](https://github.com/iain/http_accept_language) gem to automatically handle the `Accept-Language` header received from a client. After trying to make a match between the available locales, it will automatically set the `I18n.locale`. You can access the http_accept_language parser via the `request.http_accept_language` method.
         
     | 
| 
       290 
294 
     | 
    
         | 
| 
       291 
295 
     | 
    
         
             
            For most APIs this should work great, especially for mobile applications where this header is automatically set by the phone. There may however be situations where you need a bit more control. If a user has a preferred language setting for example.
         
     | 
| 
       292 
296 
     | 
    
         | 
| 
       293 
     | 
    
         
            -
            Resources contain two protected methods that can be overwritten 
     | 
| 
      
 297 
     | 
    
         
            +
            Resources contain two protected methods that can be overwritten. This is what they look like in the Restfulness code:
         
     | 
| 
       294 
298 
     | 
    
         | 
| 
       295 
299 
     | 
    
         
             
            ```ruby
         
     | 
| 
       296 
300 
     | 
    
         
             
            protected
         
     | 
| 
         @@ -302,16 +306,15 @@ end 
     | 
|
| 
       302 
306 
     | 
    
         
             
            def set_locale
         
     | 
| 
       303 
307 
     | 
    
         
             
              I18n.locale = locale
         
     | 
| 
       304 
308 
     | 
    
         
             
            end
         
     | 
| 
       305 
     | 
    
         
            -
            ``` 
     | 
| 
      
 309 
     | 
    
         
            +
            ```
         
     | 
| 
       306 
310 
     | 
    
         | 
| 
       307 
311 
     | 
    
         
             
            The `Resource#set_locale` method is called before any of the other callbacks are handled. This is important as it allows the locale to be set before returning any translatable error messages.
         
     | 
| 
       308 
312 
     | 
    
         | 
| 
       309 
     | 
    
         
            -
            Most users will probably just want to override the `Resource#locale` method and provide the appropriate locale for the request. If you are using a User object or similar, double check your authentication process as the default `authorized?` method will be called *after* the locale is prepared.
         
     | 
| 
       310 
     | 
    
         
            -
             
     | 
| 
      
 313 
     | 
    
         
            +
            Most users will probably just want to override the `Resource#locale` method and provide the appropriate locale for the request. If you are using a User object or similar, double check your authentication process as the default `authorized?` method will be called *after* the locale is prepared so that error responses are always in the requested language.
         
     | 
| 
       311 
314 
     | 
    
         | 
| 
       312 
315 
     | 
    
         
             
            #### Authentication in Resources
         
     | 
| 
       313 
316 
     | 
    
         | 
| 
       314 
     | 
    
         
            -
            Restfulness  
     | 
| 
      
 317 
     | 
    
         
            +
            Restfulness provides basic support for [HTTP Basic Authentication](http://en.wikipedia.org/wiki/Basic_access_authentication). To use, simply call the `authenticate_with_http_basic` method in your resource definition.
         
     | 
| 
       315 
318 
     | 
    
         | 
| 
       316 
319 
     | 
    
         
             
            Here's an example with the authentication details in the code, you'd obviously want to use something a bit more advanced than this in production:
         
     | 
| 
       317 
320 
     | 
    
         | 
| 
         @@ -332,9 +335,9 @@ def authorized? 
     | 
|
| 
       332 
335 
     | 
    
         
             
            end
         
     | 
| 
       333 
336 
     | 
    
         
             
            ```
         
     | 
| 
       334 
337 
     | 
    
         | 
| 
       335 
     | 
    
         
            -
             
     | 
| 
      
 338 
     | 
    
         
            +
            Digest authentication is not currently supported, but your contributions would be more than welcome. Checkout the [HttpAuthentication/basic.rb](blob/master/lib/restfulness/http_authentication/basic.rb) source for an example.
         
     | 
| 
       336 
339 
     | 
    
         | 
| 
       337 
     | 
    
         
            -
            Restfulness doesn't make any provisions for requesting authentication from the client as most APIs don't  
     | 
| 
      
 340 
     | 
    
         
            +
            Restfulness doesn't make any provisions for requesting authentication from the client as in our experience most APIs don't need to offer this functionality. You can achieve the same effect however by providing the `WWW-Authenticate` header in the response. For example:
         
     | 
| 
       338 
341 
     | 
    
         | 
| 
       339 
342 
     | 
    
         
             
            ```ruby
         
     | 
| 
       340 
343 
     | 
    
         
             
            def authorized?
         
     | 
| 
         @@ -353,12 +356,11 @@ def request_authentication 
     | 
|
| 
       353 
356 
     | 
    
         
             
            end
         
     | 
| 
       354 
357 
     | 
    
         
             
            ```
         
     | 
| 
       355 
358 
     | 
    
         | 
| 
       356 
     | 
    
         
            -
             
     | 
| 
       357 
359 
     | 
    
         
             
            ### Requests
         
     | 
| 
       358 
360 
     | 
    
         | 
| 
       359 
     | 
    
         
            -
            All resource instances have access to a `Request` object via the  
     | 
| 
      
 361 
     | 
    
         
            +
            All resource instances have access to a `Request` object via the `Resource#request` method, much like you'd find in a Rails project. It provides access to the details including in the HTTP request: headers, the request URL, path entries, the query, body and/or parameters.
         
     | 
| 
       360 
362 
     | 
    
         | 
| 
       361 
     | 
    
         
            -
            Restfulness takes a slightly different approach to handling paths, queries, and parameters. Rails and Sinatra  
     | 
| 
      
 363 
     | 
    
         
            +
            Restfulness takes a slightly different approach to handling paths, queries, and parameters, as each has their own independent method. Rails and Sinatra will typically mash everything together into a `params` hash. While this is convenient for use cases involving a browser, it is less useful for APIs when body parameters should only contain attributes of the model managed by the resource. If you've ever used Models from Backbone.js or similar Javascript library you appreciate this. When saving a Model, Backbone.js assumes by default that attributes will be provided without a prefix in the POST body.
         
     | 
| 
       362 
364 
     | 
    
         | 
| 
       363 
365 
     | 
    
         
             
            The following key methods are provided in a request object:
         
     | 
| 
       364 
366 
     | 
    
         | 
| 
         @@ -387,7 +389,13 @@ request.query[:page]       # 1 
     | 
|
| 
       387 
389 
     | 
    
         
             
            request.body               # "{'key':'value'}" - string payload
         
     | 
| 
       388 
390 
     | 
    
         | 
| 
       389 
391 
     | 
    
         
             
            # Request params
         
     | 
| 
       390 
     | 
    
         
            -
            request.params             # {'key' => 'value'} - usually a JSON  
     | 
| 
      
 392 
     | 
    
         
            +
            request.params             # {'key' => 'value'} - usually a JSON de-serialized object
         
     | 
| 
      
 393 
     | 
    
         
            +
             
     | 
| 
      
 394 
     | 
    
         
            +
            # Accept header object (nil if none!)
         
     | 
| 
      
 395 
     | 
    
         
            +
            request.accept.version     # For "Accept: application/vnd.example.api+json;version=3", returns "3"
         
     | 
| 
      
 396 
     | 
    
         
            +
             
     | 
| 
      
 397 
     | 
    
         
            +
            # Content Type object (nil if none!)
         
     | 
| 
      
 398 
     | 
    
         
            +
            request.content_type.to_s  # Something like "application/json"
         
     | 
| 
       391 
399 
     | 
    
         
             
            ```
         
     | 
| 
       392 
400 
     | 
    
         | 
| 
       393 
401 
     | 
    
         
             
            ### Logging
         
     | 
| 
         @@ -408,7 +416,7 @@ Restfulness.sensitive_params = [:password, :secretkey] 
     | 
|
| 
       408 
416 
     | 
    
         | 
| 
       409 
417 
     | 
    
         
             
            ## Error Handling
         
     | 
| 
       410 
418 
     | 
    
         | 
| 
       411 
     | 
    
         
            -
            If you  
     | 
| 
      
 419 
     | 
    
         
            +
            If you'd like your application to return anything other than a 200 (or 202) status, you have a couple of options that allow you to send codes back to the client.
         
     | 
| 
       412 
420 
     | 
    
         | 
| 
       413 
421 
     | 
    
         
             
            One of the easiest approaches is to update the `response` code. Take the following example where we set a 403 response and the model's errors object in the payload:
         
     | 
| 
       414 
422 
     | 
    
         | 
| 
         @@ -470,7 +478,7 @@ end 
     | 
|
| 
       470 
478 
     | 
    
         | 
| 
       471 
479 
     | 
    
         
             
            This can be a really nice way to mold your errors into a standard format. All HTTP exceptions generated inside resources will pass through `error!`, even those that a triggered by a callback. It gives a great way to provide your own JSON error payload, or even just resort to a simple string.
         
     | 
| 
       472 
480 
     | 
    
         | 
| 
       473 
     | 
    
         
            -
            The currently built in  
     | 
| 
      
 481 
     | 
    
         
            +
            The currently built in exception methods are:
         
     | 
| 
       474 
482 
     | 
    
         | 
| 
       475 
483 
     | 
    
         
             
             * `not_modified!`
         
     | 
| 
       476 
484 
     | 
    
         
             
             * `bad_request!`
         
     | 
| 
         @@ -499,19 +507,19 @@ We're all used to the way Rails projects magically reload files so you don't hav 
     | 
|
| 
       499 
507 
     | 
    
         | 
| 
       500 
508 
     | 
    
         
             
            Using Restfulness in Rails is the easiest way to take advantage support reloading.
         
     | 
| 
       501 
509 
     | 
    
         | 
| 
       502 
     | 
    
         
            -
            The  
     | 
| 
      
 510 
     | 
    
         
            +
            The recommended approach is to create two directories in your Rails projects `/app` path:
         
     | 
| 
       503 
511 
     | 
    
         | 
| 
       504 
512 
     | 
    
         
             
             * `/app/apis` can be used for defining your API route files, and
         
     | 
| 
       505 
513 
     | 
    
         
             
             * `/app/resources` for defining a tree of resource definition files.
         
     | 
| 
       506 
514 
     | 
    
         | 
| 
       507 
     | 
    
         
            -
            Add the two paths to your rails  
     | 
| 
      
 515 
     | 
    
         
            +
            Add the two paths to your rails auto-loading configuration in `/config/application.rb`, there will already be a sample in your config provided by Rails:
         
     | 
| 
       508 
516 
     | 
    
         | 
| 
       509 
517 
     | 
    
         
             
            ```ruby
         
     | 
| 
       510 
518 
     | 
    
         
             
            # Custom directories with classes and modules you want to be autoloadable.
         
     | 
| 
       511 
519 
     | 
    
         
             
            config.autoload_paths += %W( #{config.root}/app/resources #{config.root}/app/apis )
         
     | 
| 
       512 
520 
     | 
    
         
             
            ```
         
     | 
| 
       513 
521 
     | 
    
         | 
| 
       514 
     | 
    
         
            -
            Your Resource and API files will now be  
     | 
| 
      
 522 
     | 
    
         
            +
            Your Resource and API files will now be auto-loadable from your Rails project. The next step is to update the Rails router to be able to find our API. Modify the `/config/routes.rb` file so that it includes the mount method call:
         
     | 
| 
       515 
523 
     | 
    
         | 
| 
       516 
524 
     | 
    
         
             
            ```ruby
         
     | 
| 
       517 
525 
     | 
    
         
             
            YourRailsApp::Application.routes.draw do
         
     | 
| 
         @@ -526,7 +534,7 @@ end 
     | 
|
| 
       526 
534 
     | 
    
         | 
| 
       527 
535 
     | 
    
         
             
            ```
         
     | 
| 
       528 
536 
     | 
    
         | 
| 
       529 
     | 
    
         
            -
            You'll see in the code sample that we're only loading the Restfulness API during development. Our recommendation is to use Restfulness as close to Rack as possible and avoid any of the Rails overhead. To support  
     | 
| 
      
 537 
     | 
    
         
            +
            You'll see in the code sample that we're only loading the Restfulness API during development. Our recommendation is to use Restfulness as close to Rack as possible and avoid any of the Rails overhead. To support requests in production, you'll need to update your `/config.rb` so that it looks something like the following:
         
     | 
| 
       530 
538 
     | 
    
         | 
| 
       531 
539 
     | 
    
         
             
            ```ruby
         
     | 
| 
       532 
540 
     | 
    
         
             
            # This file is used by Rack-based servers to start the application.
         
     | 
| 
         @@ -546,8 +554,7 @@ Thats all there is to it! You'll now have auto-reloading in Rails, and fast requ 
     | 
|
| 
       546 
554 
     | 
    
         | 
| 
       547 
555 
     | 
    
         
             
            ### The Rack Way
         
     | 
| 
       548 
556 
     | 
    
         | 
| 
       549 
     | 
    
         
            -
            If you're using Restfulness as a standalone project, we recommend using a rack extension like [Shotgun](https://github.com/rtomayko/shotgun).
         
     | 
| 
       550 
     | 
    
         
            -
             
     | 
| 
      
 557 
     | 
    
         
            +
            If you're using Restfulness as a standalone project, we recommend using a rack extension like [Shotgun](https://github.com/rtomayko/shotgun) to automatically reload on changes.
         
     | 
| 
       551 
558 
     | 
    
         | 
| 
       552 
559 
     | 
    
         
             
            ## Writing Tests
         
     | 
| 
       553 
560 
     | 
    
         | 
| 
         @@ -649,6 +656,7 @@ Restfulness was created by Sam Lown <me@samlown.com> as a solution for building 
     | 
|
| 
       649 
656 
     | 
    
         
             
            The project is now awesome, thanks to contributions by:
         
     | 
| 
       650 
657 
     | 
    
         | 
| 
       651 
658 
     | 
    
         
             
             * [Adam Williams](https://github.com/awilliams)
         
     | 
| 
      
 659 
     | 
    
         
            +
             * [Laura Morillo](https://github.com/lauramorillo)
         
     | 
| 
       652 
660 
     | 
    
         | 
| 
       653 
661 
     | 
    
         | 
| 
       654 
662 
     | 
    
         
             
            ## Caveats and TODOs
         
     | 
| 
         @@ -659,10 +667,20 @@ Restfulness is still a work in progress but at Cabify we are using it in product 
     | 
|
| 
       659 
667 
     | 
    
         
             
             * Support path methods for automatic URL generation.
         
     | 
| 
       660 
668 
     | 
    
         
             
             * Support redirect exceptions.
         
     | 
| 
       661 
669 
     | 
    
         
             
             * Needs more functional testing.
         
     | 
| 
       662 
     | 
    
         
            -
             * Support for before and after filters in resources, although I'm slightly  
     | 
| 
      
 670 
     | 
    
         
            +
             * Support for before and after filters in resources, although I'm slightly apprehensive about this.
         
     | 
| 
       663 
671 
     | 
    
         | 
| 
       664 
672 
     | 
    
         
             
            ## History
         
     | 
| 
       665 
673 
     | 
    
         | 
| 
      
 674 
     | 
    
         
            +
            ### 0.3.3 - January 19, 2016
         
     | 
| 
      
 675 
     | 
    
         
            +
             
     | 
| 
      
 676 
     | 
    
         
            +
             * Basic support for handling large request bodies received as Tempfile (@lauramorillo)
         
     | 
| 
      
 677 
     | 
    
         
            +
             * Providing human readable payload for invalid JSON.
         
     | 
| 
      
 678 
     | 
    
         
            +
             * Added support for Accept and Content-Type header handling. (@samlown)
         
     | 
| 
      
 679 
     | 
    
         
            +
             * Better handling of IO objects from `rack.input`, such as `Puma::NullIO`. (@samlown)
         
     | 
| 
      
 680 
     | 
    
         
            +
             * Upgrading to latest version of RSpec (@samlown)
         
     | 
| 
      
 681 
     | 
    
         
            +
             * Adding `request.env` accessor to Rack env (@amuino)
         
     | 
| 
      
 682 
     | 
    
         
            +
             * Removing support for Ruby 1.9 (@samlown)
         
     | 
| 
      
 683 
     | 
    
         
            +
             
     | 
| 
       666 
684 
     | 
    
         
             
            ### 0.3.2 - February 9, 2015
         
     | 
| 
       667 
685 
     | 
    
         | 
| 
       668 
686 
     | 
    
         
             
             * Added support for application/x-www-form-urlencoded parameter decoding (@samlown)
         
     | 
    
        data/lib/restfulness.rb
    CHANGED
    
    | 
         @@ -20,6 +20,9 @@ require "restfulness/resources/authentication" 
     | 
|
| 
       20 
20 
     | 
    
         
             
            require "restfulness/requests/authorization"
         
     | 
| 
       21 
21 
     | 
    
         
             
            require "restfulness/requests/authorization_header"
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
      
 23 
     | 
    
         
            +
            require "restfulness/headers/media_type"
         
     | 
| 
      
 24 
     | 
    
         
            +
            require "restfulness/headers/accept"
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
       23 
26 
     | 
    
         
             
            require "restfulness/application"
         
     | 
| 
       24 
27 
     | 
    
         
             
            require "restfulness/dispatcher"
         
     | 
| 
       25 
28 
     | 
    
         
             
            require "restfulness/exceptions"
         
     | 
| 
         @@ -0,0 +1,66 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Restfulness
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Headers
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                # The Accept header handler provides an array of Media Types that the
         
     | 
| 
      
 5 
     | 
    
         
            +
                # client is willing to accept.
         
     | 
| 
      
 6 
     | 
    
         
            +
                #
         
     | 
| 
      
 7 
     | 
    
         
            +
                # Based on a simplified RFC2616 implementation, each media type is stored
         
     | 
| 
      
 8 
     | 
    
         
            +
                # and ordered.
         
     | 
| 
      
 9 
     | 
    
         
            +
                #
         
     | 
| 
      
 10 
     | 
    
         
            +
                # Restfulness does not currently deal with the special 'q' paremeter defined
         
     | 
| 
      
 11 
     | 
    
         
            +
                # in the standard as quality is not something APIs normally need to handle.
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # Aside from media type detection, a useful feature of the accept header is to 
         
     | 
| 
      
 14 
     | 
    
         
            +
                # provide the desired version of content to provide in the response. This class
         
     | 
| 
      
 15 
     | 
    
         
            +
                # offers a helper method that will attempt to determine the version.
         
     | 
| 
      
 16 
     | 
    
         
            +
                #
         
     | 
| 
      
 17 
     | 
    
         
            +
                # Given the HTTP header:
         
     | 
| 
      
 18 
     | 
    
         
            +
                #
         
     | 
| 
      
 19 
     | 
    
         
            +
                #   Accept: application/com.example.api+json;version=1
         
     | 
| 
      
 20 
     | 
    
         
            +
                #
         
     | 
| 
      
 21 
     | 
    
         
            +
                # The resource instace has access to the version via:
         
     | 
| 
      
 22 
     | 
    
         
            +
                #
         
     | 
| 
      
 23 
     | 
    
         
            +
                #   request.accept.version == "1"
         
     | 
| 
      
 24 
     | 
    
         
            +
                #
         
     | 
| 
      
 25 
     | 
    
         
            +
                class Accept
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  # The -ordered- array of media types provided in the headers
         
     | 
| 
      
 28 
     | 
    
         
            +
                  attr_accessor :media_types
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  def initialize(str = "")
         
     | 
| 
      
 31 
     | 
    
         
            +
                    self.media_types = []
         
     | 
| 
      
 32 
     | 
    
         
            +
                    parse(str) unless str.empty?
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  def parse(str)
         
     | 
| 
      
 36 
     | 
    
         
            +
                    types = str.split(',').map{|t| t.strip}
         
     | 
| 
      
 37 
     | 
    
         
            +
             
         
     | 
| 
      
 38 
     | 
    
         
            +
                    # Attempt to crudely determine order based on length, and store
         
     | 
| 
      
 39 
     | 
    
         
            +
                    types.sort{|a,b| b.length <=> a.length}.each do |t|
         
     | 
| 
      
 40 
     | 
    
         
            +
                      media_types << MediaType.new(t)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  # Request the version, always assumes that the first media type is the most relevant
         
     | 
| 
      
 45 
     | 
    
         
            +
                  def version
         
     | 
| 
      
 46 
     | 
    
         
            +
                    media_types.first.version
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def json?
         
     | 
| 
      
 50 
     | 
    
         
            +
                    media_types.each do |mt|
         
     | 
| 
      
 51 
     | 
    
         
            +
                      return true if mt.json?
         
     | 
| 
      
 52 
     | 
    
         
            +
                    end 
         
     | 
| 
      
 53 
     | 
    
         
            +
                    false
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  def xml?
         
     | 
| 
      
 57 
     | 
    
         
            +
                    media_types.each do |mt|
         
     | 
| 
      
 58 
     | 
    
         
            +
                      return true if mt.xml?
         
     | 
| 
      
 59 
     | 
    
         
            +
                    end 
         
     | 
| 
      
 60 
     | 
    
         
            +
                    false
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
            end
         
     |