Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 236 lines (167 sloc) 7.096 kB
7cc8aea @nakajima README is now markdown, added some more docs
authored
1 # Sinatra's Hat
2
3 Easy REST-ful apps with Sinatra.
4
5 Using Sinatra's Hat is centered around the `mount` method, which is
6 added to `Sinatra::Base`. It takes at bare minimum a model class. This
7 class will be mounted as a REST-ful resource, giving you all the CRUD
8 actions, as well as `new` and `edit` actions. Let's look at some code:
9
10 <pre>
11 mount Article
12 </pre>
13
14 Now you've basically got the same functionality as you'll get in Rails
15 by running `script/generate scaffold Article`. The views for your `Article`
16 will live in `views/articles`, and be named `index.erb`, `show.erb`, etc.
17
18 You can look at my [Hatter](http://github.com/nakajima/hatter/tree/master)
19 project, or the `examples/` directory in this one to see this in action.
20
21 Go ahead, try it.
22
23 ## ORM agnostic? Or ORM atheist?
24
25 By default, Sinatra's Hat works with ActiveRecord. That means that going
26 to `/articles` will simply call `Article.all` to populate the `@articles`
27 instance variable. Going to `/articles/2` will call `Article.find_by_id(2)`
28 to populate the `@article` instance variable.
29
7a6cf75 @nakajima some doc cleanup
authored
30 We call `find_by_id` instead of `find` because the `record` option should
31 simply return `nil` when the record can't be found.
32
7cc8aea @nakajima README is now markdown, added some more docs
authored
33 Not every class is an ActiveRecord though (especially if you're not using
34 ActiveRecord). That's why you can use the `finder` and `record` options.
35
36 This example will show you how to use DataMapper with Sinatra's Hat:
37
38 <pre>
39 mount Article do
40 finder { |model, params| model.all }
41 record { |model, params| model.first(:id => params[:id]) }
42 end
43 </pre>
44
45 As you can see, both `finder` and `record` take a block, which will get
46 passed the "model" and `params` for each request. The reason you should use
47 the "model" argument instead of referencing the class directly is that
48 when you start nesting mounted models, then Sinatra's Hat will attempt to
49 pass the association proxy as the model argument instead of the class itself.
50
51 "Nested mounted models?" you ask?
52
53 ## Nested mounted models.
54
55 You don't want to have to expose your entire application at the top level
56 of URL paths. That wouldn't be very RESTful, and more importantly, it'd be
57 damn ugly. So Sinatra's Hat allows you to nest resources:
58
59 <pre>
60 mount Article do
61 mount Comment
62 end
63 </pre>
64
65 With this example, you'd get `/articles/1/comments`, `/articles/1/comments/1`
66 and all the rest of the actions you get for articles, just nested. As long
67 as your `Article` model supports a `comments` association proxy, then the `finder`
68 and `record` options for `Comment` will automatically scope their results by
69 the parent `Article`.
70
9f870d3 @baldowl Tweaks and extensions to README.
baldowl authored
71 ## Limiting routes
72
73 By default, Sinatra's Hat creates seven routes for each mounted model (the
74 four ones for <acronym title="Create|Read|Update|Destroy">CRUD</acronym>
75 actions plus the routes for index, new and edit action), but you can reduce
76 the number of available routes with `only`:
77
78 <pre>
79 mount Article do
80 only :index, :show
81 end
82 </pre>
83
84 Only the listed actions will return valid responses; requests for the
85 "missing" routes will produce 404 "Not Found" HTTP responses.
7930aed @nakajima added note about nested resource bug, link to lighthouse project
authored
86
add8606 @nakajima added docs for #protect
authored
87 ## Basic Auth
88
89 To protect actions using basic authentication, you can use the `protect` method.
90
91 <pre>
92 mount Article do
93 protect :create, :update, :destroy, :username => "foo", :password => "bar", :realm => "BLOGZ"
94 end
95 </pre>
96
97 The above snippet will protect your <acronym title="Create|Update|Destroy">CUD</acronym>
98 actions with basic auth, using the username "foo" and password "bar". The realm
99 for the basic auth prompt will say "BLOGZ".
100
101 If you want to protect all of your actions, you cay say `protect :all`.
102
7cc8aea @nakajima README is now markdown, added some more docs
authored
103 ## `.xml`, `.json`, `.yaml`, and whatever else you want
104
105 If a request has a format extensions, then Sinatra's Hat will first check
106 to see if it has a custom way of serializing that format. To specify a
107 custom formatter, you can use the `formats` hash:
108
109 <pre>
110 mount Article do
111 formats[:ruby] = { |data| data.inspect }
112 end
113 </pre>
114
115 With that custom formatter, a request to `/articles.ruby` will return
116 the equivalent of `Article.all.inspect`.
117
bb558e8 @nakajima added bit about automatic serialized formatters
authored
118 ### Automatic formatters
119
120 If you don't specify a custom formatter, then Sinatra's Hat will try to
121 call `to_#{format}` on the record object. That means that with most ORMs,
122 things like `to_xml`, `to_json`, and `to_yaml` will be supported right out
123 of the box.
7cc8aea @nakajima README is now markdown, added some more docs
authored
124
9f870d3 @baldowl Tweaks and extensions to README.
baldowl authored
125 Requests for unknow formats will produce 406 "Not Acceptable" HTTP responses.
126
9967480 @nakajima added documentation for custom flow controls
authored
127 ## Default Flows
128
129 Sinatra's Hat has some default flows:
130
131 ### After the `create` action
132
7a6cf75 @nakajima some doc cleanup
authored
133 **On Success**: If a record is successfully created, Sinatra's Hat will redirect to that
9967480 @nakajima added documentation for custom flow controls
authored
134 record's show page.
135
7a6cf75 @nakajima some doc cleanup
authored
136 **On Failure**: If a record cannot be saved, Sinatra's Hat will render the `new` action.
9967480 @nakajima added documentation for custom flow controls
authored
137
138 ### After the `Update` action
139
7a6cf75 @nakajima some doc cleanup
authored
140 **On Success**: If a record is successfully updated, Sinatra's Hat will redirect to that
9967480 @nakajima added documentation for custom flow controls
authored
141 record's show page.
142
7a6cf75 @nakajima some doc cleanup
authored
143 **On Failure**: If a record cannot be updated, Sinatra's Hat will render the `edit` action.
9967480 @nakajima added documentation for custom flow controls
authored
144
145 ## Custom Flows
146
147 To specify custom flows for your actions, you can use the `after` method.
148
149 Let's say that after a user creates an Article, you want to render the
150 article's edit action, and if it can't be created, you want to redirect
151 back to the articles index.
152
153 <pre>
154 mount Article do
155 after :create do |on|
156 on.success { |record| render(:edit) }
157 on.failure { |record| redirect(:index) }
158 end
159 end
160 </pre>
161
9f870d3 @baldowl Tweaks and extensions to README.
baldowl authored
162 Only `:create` and `:update` actions allow to handle success and failure
163 differently; for the other actions you can customize only the `success` result
164 and if something goes wrong (i.e., when a record cannot be found) they will
165 simply return a 404 "Not Found" HTTP response.
166
9967480 @nakajima added documentation for custom flow controls
authored
167 ### `redirect` options
168
169 When specifying a custom redirect, you can pass one of a few things:
170
171 #### A String
172
173 When you pass `redirect` a string, the redirect will go to that string.
174
175 <pre>
176 after :create do |on|
177 on.success { |record| redirect("/articles/#{record.to_param}") }
178 end
179 </pre>
180
181 #### A Record
182
183 When you pass `redirect` a record, the redirect will go to the show
184 action for that record.
185
186 <pre>
187 after :create do |on|
188 on.success { |record| redirect(record) }
189 end
190 </pre>
191
192 #### A symbol
193
194 If you pass `redirect` the name of an action as a symbol (like `:index`),
195 then the redirect will go to the correct path for that option:
196
197 <pre>
198 after :create do |on|
199 on.success { redirect(:index) }
200 end
201 </pre>
202
203 When the action requires a record (like `:show`), then just pass the
204 record as a second argument:
205
206 <pre>
207 after :create do |on|
208 on.success { |record| redirect(:show, record) }
209 end
210 </pre>
211
212 ### Responding with a `render`
213
214 When you want your response to just render a template, just call `render`
215 with the name of the template:
216
217 <pre>
218 after :create do |on|
219 on.failure { |record| render(:new) }
220 end
221 </pre>
222
8edf2c2 @nakajima added TODO about making last_modified more efficient
authored
223 ## Todo
224
225 * Make `last_modified` calls more efficient
226 * Investigate other forms of caching
227
20f7b19 @nakajima more README
authored
228 ## Other Info
229
7930aed @nakajima added note about nested resource bug, link to lighthouse project
authored
230 * [View the Lighthouse Project](http://nakajima.lighthouseapp.com/projects/24609-sinatras-hat/overview)
20f7b19 @nakajima more README
authored
231 * [View the CI build](http://ci.patnakajima.com/sinatra-s-hat)
232 * Thanks a ton to the [Sinatra team](http://github.com/sinatra) for such an
233 awesome framework.
234
9f870d3 @baldowl Tweaks and extensions to README.
baldowl authored
235 (c) Copyright 2008-2009 Pat Nakajima. All Rights Reserved.
Something went wrong with that request. Please try again.