Skip to content

Commit

Permalink
Update README
Browse files Browse the repository at this point in the history
  • Loading branch information
sammyhenningsson committed Nov 24, 2018
1 parent 28dd114 commit 854e107
Showing 1 changed file with 57 additions and 15 deletions.
72 changes: 57 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,6 @@ Your newly created project should contain the following files:
│   ├── initializers.rb
│   └── settings.yml
├── config.ru
├── db
│   ├── development.sqlite3
│   └── migrations
├── frontend
│   ├── assets
│   │   └── css
Expand Down Expand Up @@ -290,6 +287,14 @@ Response:
}
```

#### Recap
We have built a very basic hypermedia driven API with only one type of resource. The neatest thing about this is that it only took four commands:
```sh
shaf new blog
bundle
shaf generate scaffold post title:string message:string
rake db:migrate
```

## Upgrading a project created with shaf version < 0.6.0
Shaf version 0.6.0 introduced a few changes that are not backward compatible with previous versions. This means that if you created your Shaf project with an older version of this gem and then upgrade this gem to v0.6.0, your project will not function. To remedy this you will need to execute (from inside your project directory):
Expand Down Expand Up @@ -358,17 +363,20 @@ Note: You can also add custom migrations, see [Customizations](#Customizations)

## Routing/Controllers
As usual with Sinatra applications routes are declared together with the controller code rather than in a separate file (as with Rails). All controllers should be subclasses of `BaseController` found in `api/controllers/base_controller.rb` (which was created along with the project).
Controllers generated with `shaf generate` uses two extensions, `Shaf::ResourceUris` and `Shaf::Authorize`.
Controllers generated with `shaf generate` uses three extensions, `Shaf::ResourceUris`, `Shaf::ControllerHooks` and `Shaf::Authorize`.

#### Shaf::ResourceUris
This extension adds two class methods, `resource_uris_for` and `register_uri`. Both methods are used to create uri helpers.
This extension is used to create _uri_-/_path_ helpers. When registered with Sinatra (which is done in the generated `BaseController`) it adds two class methods, `resource_uris_for` and `register_uri`. The former adds four conventional uris (basically the CRUD actions). The later adds a single helper, for more custom actions. Each helper (created by `resource_uris_for` or `register_uri`) will be added as an instance method and a module method to the module `Shaf::UriHelper`. This means that they can be accessed from any file in your API through the module. Or you can include/extend `Shaf::UriHelper` into a class.
When included the module is also extended so all _uri_-/_path_ helpers will be available in the class as well. Also, the class that calls `resource_uris_for` or `register_uri` will automatically include `Shaf::UriHelper`, i.e. controllers registering new helpers will have access to the helper methods form instances and the class.
`resource_uris_for(name, base: nil, plural_name: nil)` - creates four pairs of uri helpers and adds them as class methods and instance methods to the caller.
The keyword argument `:base` is used to specify a path namespace (such as '/api') that will be prepended to the uri. This can also be used to nest resources (though this is in general considered bad), like `resource_uris_for :post, base: '/users/:id/'`.
The keyword argument `:plural_name` sets the pluralization of the name (when excluded the plural name will be `name` + 's').
```sh
class PostController < BaseController
resource_uris_for :post
end
```
Would add the following methods as instance method on Shaf::UriHelper. This module is then both included and extended into the `PostController` (which means that all uri helpers are available as both class methods and instance methods in the controller). Each method as an __uri_ version and an __path_ version.
This adds four helpers for the conventional four CRUD actions. Each one has a __uri_ and a __path_ version. The `PostController` above would create these methods:

| Method | Returned string with no query_params (id may vary) |
| --------------------------------------- | -------------------------------------------------- |
Expand All @@ -381,20 +389,37 @@ Would add the following methods as instance method on Shaf::UriHelper. This modu
| `new_post_path(**query_params)` | /posts/form |
| `edit_post_path(post, **query_params)` | /posts/5/edit |

Methods taking an argument (`post_uri` and `edit_post_uri`) may be called with an object responding to `:id` or else `:to_s` will be called on it. The keyword arguments `:base` and `:plural_name` is used to specify a path namespace (such as '/api') that will be prepended to the uri resp. the pluralization of the name (when excluded the plural name will be `name` + 's').

Methods taking an argument (e.g. `post_uri` and `edit_post_uri`) may be called with an object responding to `:id` or else `:to_s` will be called on it. E.g `post_uri(Post[27])` or `post_uri(27)`.
The optional `query_params` takes any given keyword arguments and appends a query string with them.
```sh
post_uri(post, foo: 'bar') # => /posts/5?foo=bar
```
Each of the helper also has a __path?_ version that can be used to check if a path matches the one of the helper. If given an argument it is matched against the helpers path else the caller must respond to `request` (returning an object responding to `path_info`). Use cases
```sh
UriHelper.post_path? "/posts/" # => false
UriHelper.post_path? "/posts/5" # => true
```
Or
```sh
class PostController < BaseController
resource_uris_for :post

before do
setup_stuff
setup_more_stuff_before_edit if edit_post_path?
end

end
```
`register_uri` is used to create a single uri helper that does not follow the "normal" conventions of `resource_uris_for`.
```sh
class PostController < BaseController
register_uri :archive_post, '/posts/:id/archive'
end
```
Would add an `archive_post_uri(post, **query_params)` method to the `PostController` class as well as instances of `PostController`. Each parameter in the uri template (section begining with ':', e.g. _:some_param_) will become a parameter in the helper method. The correponding argument will get sent the paramter name if it respond to it, else `to_s` will be sent with the argument as receiver. An example will make things more clear:
This adds the helper method `archive_post_uri(post, **query_params)` (plus the __path_ and the __path?_ methods). Each parameter in the uri template (section begining with ':', e.g. _:some_param_) will become a parameter in the helper method. The correponding argument will get sent the paramter name if it respond to it, else `to_s` will be sent with the argument as receiver. An example will make things more clear:
```sh
class FooController < BaseController
register_uri :foo_bar, '/:foo/hello/:bar/:baz'
Expand All @@ -407,21 +432,38 @@ Shaf::UriHelper.foo_bar_path(obj1, obj2, 'BAZZZZ') # => /FOOO/hello/1337/BAZZZZ
Shaf::UriHelper.foo_bar_path('FOOZA', obj2, obj3) # => /FOOZA/hello/1337/BAAAZA
```
The helper above takes three arguments (since there's three sections begining with ':'). In the first call to `foo_bar_path` we pass in two objects responding to `:foo` resp. `:bar`, thus `obj1.foo` resp. `obj2.bar` is what ends up in the corresponding uri sections. The third argument does not respond to `:baz`, thus `to_s` is sent instead. The second call to `foo_bar_path` is just to clearify that we can call this helper in many ways.
Uri helpers added by `resource_uris_for` and `register_uri` gets added to the module `Shaf::UriHelper` as both module methods and instance methods. So to use them outside of Controllers, either call them directly on the module (e.g. `Shaf::UriHelper.my_foo_uri`) or include `Shaf::UriHelper` and get all helpers as instance methods.
To make it easier to see the connection between controller routes and uri helpers, Shaf makes it possible to specify routes with symbols. These symbols must be the same as the corresponding uri helper:
To make it easier to see the connection between controller routes and uri helpers, Shaf makes it possible to specify routes with symbols. These symbols must be the same as the __path_ version of the corresponding uri helper:
```sh
class PostController < BaseController
register_uri :archive_post '/posts/:id/archive'
post :archive_post_uri do
post :archive_post_path do
"Post was archived!"
end
end
```
#### Shaf::ControllerHooks
This extension adds a two hooks to run before or after a request. Sinatra already has the `before` and `after` filters, which are great if you want them to run before/after all routes. But if you want a filter to kick in for just some routes, then there are prettier ways of doing this. `Shaf::ControllerHooks` (which is registered in the generated `BaseController`) adds the `before_action` and `after_action` filters. They are used together with uri helpers so that we don't have to care about building some Regexp to make the filter apply only to a few routes. Example:
```sh
class PostController < BaseController
resource_uris_for :post
before_action :setup_index, only: posts_path
before_action only: [:new_path_path, :edit_post_path] do
# Do some form setup
end
def setup_index
# some setup
end
end
```
These methods either take a symbol to an instance methods as first argument or a block as the action to be executed. The optional keyword arguments `:only` and `:except` may be used to target just certain routes. When both `:only` and `:exept` are left out, then the action applies to all routes within the given controller.
#### Shaf::Authorize
This module adds an `authorize_with(policy)` class method and an `authorize!(action, resource = nil)` instance method. The class method is used to register a Policy class. The instance method is used to ensure that a certain action is authorized. The following policy class makes sure that a user is logged in to be able to see posts and that users may only edit their own posts. See [Policies](#policies) for more info.
This extension adds an `authorize_with(policy)` class method and an `authorize!(action, resource = nil)` instance method. The class method is used to register a Policy class. The instance method is used to ensure that a certain action is authorized. The following policy class makes sure that a user is logged in to be able to see posts and that users may only edit their own posts. See [Policies](#policies) for more info.
```sh
class PostPolicy
include HALPresenter::Policy::DSL
Expand All @@ -442,13 +484,13 @@ The following controller validates actions using the `PostPolicy`. If the policy
class PostController < BaseController
authorize_with PostPolicy
get :post_uri do
get :post_path do
authorize! :show
respond_with post
end
put :edit_post_uri do
put :edit_post_path do
authorize! :edit, post
post.update(params)
Expand Down

0 comments on commit 854e107

Please sign in to comment.