devcenter 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (192) hide show
  1. data/.gitignore +18 -0
  2. data/Gemfile +8 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +39 -0
  5. data/Rakefile +2 -0
  6. data/bin/devcenter +7 -0
  7. data/devcenter.gemspec +30 -0
  8. data/lib/devcenter.rb +6 -0
  9. data/lib/devcenter/cli.rb +45 -0
  10. data/lib/devcenter/coderay_extensions.rb +70 -0
  11. data/lib/devcenter/commands.rb +4 -0
  12. data/lib/devcenter/commands/base.rb +47 -0
  13. data/lib/devcenter/commands/open.rb +34 -0
  14. data/lib/devcenter/commands/preview.rb +28 -0
  15. data/lib/devcenter/commands/pull.rb +37 -0
  16. data/lib/devcenter/helpers.rb +41 -0
  17. data/lib/devcenter/layout.html +299 -0
  18. data/lib/devcenter/md_parser.rb +87 -0
  19. data/lib/devcenter/previewer.rb +36 -0
  20. data/lib/devcenter/previewer/assets/images/public/article-icon-large.png +0 -0
  21. data/lib/devcenter/previewer/assets/images/public/article-icon.png +0 -0
  22. data/lib/devcenter/previewer/assets/images/public/aside_accordion_indicator_default.png +0 -0
  23. data/lib/devcenter/previewer/assets/images/public/aside_accordion_indicator_open.png +0 -0
  24. data/lib/devcenter/previewer/assets/images/public/body_bg.png +0 -0
  25. data/lib/devcenter/previewer/assets/images/public/callout_bg.png +0 -0
  26. data/lib/devcenter/previewer/assets/images/public/feed-icon-sprite.png +0 -0
  27. data/lib/devcenter/previewer/assets/images/public/heroku-header-logo-mobile.png +0 -0
  28. data/lib/devcenter/previewer/assets/images/public/heroku-header-logo.png +0 -0
  29. data/lib/devcenter/previewer/assets/images/public/heroku-logo.png +0 -0
  30. data/lib/devcenter/previewer/assets/images/public/icon_sprite_16.png +0 -0
  31. data/lib/devcenter/previewer/assets/images/public/index_li_bullet.png +0 -0
  32. data/lib/devcenter/previewer/assets/images/public/intro_bg.png +0 -0
  33. data/lib/devcenter/previewer/assets/images/public/jive_discussion_arrow.png +0 -0
  34. data/lib/devcenter/previewer/assets/images/public/jive_discussion_glyph.png +0 -0
  35. data/lib/devcenter/previewer/assets/images/public/line.png +0 -0
  36. data/lib/devcenter/previewer/assets/images/public/pre_code_background.png +0 -0
  37. data/lib/devcenter/previewer/assets/images/public/search-icon.png +0 -0
  38. data/lib/devcenter/previewer/assets/images/public/search_glyph.png +0 -0
  39. data/lib/devcenter/previewer/assets/images/public/search_return.png +0 -0
  40. data/lib/devcenter/previewer/assets/images/public/tag-icon-large.png +0 -0
  41. data/lib/devcenter/previewer/assets/images/public/toc-icon.png +0 -0
  42. data/lib/devcenter/previewer/assets/public.css +2125 -0
  43. data/lib/devcenter/previewer/assets/public/article-icon-large.png +0 -0
  44. data/lib/devcenter/previewer/assets/public/article-icon.png +0 -0
  45. data/lib/devcenter/previewer/assets/public/aside_accordion_indicator_default.png +0 -0
  46. data/lib/devcenter/previewer/assets/public/aside_accordion_indicator_open.png +0 -0
  47. data/lib/devcenter/previewer/assets/public/body_bg.png +0 -0
  48. data/lib/devcenter/previewer/assets/public/callout_bg.png +0 -0
  49. data/lib/devcenter/previewer/assets/public/feed-icon-sprite.png +0 -0
  50. data/lib/devcenter/previewer/assets/public/heroku-header-logo-mobile.png +0 -0
  51. data/lib/devcenter/previewer/assets/public/heroku-header-logo.png +0 -0
  52. data/lib/devcenter/previewer/assets/public/heroku-logo.png +0 -0
  53. data/lib/devcenter/previewer/assets/public/icon_sprite_16.png +0 -0
  54. data/lib/devcenter/previewer/assets/public/index_li_bullet.png +0 -0
  55. data/lib/devcenter/previewer/assets/public/intro_bg.png +0 -0
  56. data/lib/devcenter/previewer/assets/public/jive_discussion_arrow.png +0 -0
  57. data/lib/devcenter/previewer/assets/public/jive_discussion_glyph.png +0 -0
  58. data/lib/devcenter/previewer/assets/public/line.png +0 -0
  59. data/lib/devcenter/previewer/assets/public/pre_code_background.png +0 -0
  60. data/lib/devcenter/previewer/assets/public/public.css +2125 -0
  61. data/lib/devcenter/previewer/assets/public/search-icon.png +0 -0
  62. data/lib/devcenter/previewer/assets/public/search_glyph.png +0 -0
  63. data/lib/devcenter/previewer/assets/public/search_return.png +0 -0
  64. data/lib/devcenter/previewer/assets/public/tag-icon-large.png +0 -0
  65. data/lib/devcenter/previewer/assets/public/toc-icon.png +0 -0
  66. data/lib/devcenter/previewer/file_listener.rb +23 -0
  67. data/lib/devcenter/previewer/views/article.erb +345 -0
  68. data/lib/devcenter/previewer/web_app.rb +53 -0
  69. data/lib/devcenter/previewer/web_server.rb +29 -0
  70. data/lib/devcenter/version.rb +3 -0
  71. data/vendor/sinatra/.gitignore +6 -0
  72. data/vendor/sinatra/.travis.yml +16 -0
  73. data/vendor/sinatra/.yardopts +4 -0
  74. data/vendor/sinatra/AUTHORS +61 -0
  75. data/vendor/sinatra/Gemfile +91 -0
  76. data/vendor/sinatra/LICENSE +22 -0
  77. data/vendor/sinatra/README.de.rdoc +2116 -0
  78. data/vendor/sinatra/README.es.rdoc +2106 -0
  79. data/vendor/sinatra/README.fr.rdoc +2133 -0
  80. data/vendor/sinatra/README.hu.rdoc +608 -0
  81. data/vendor/sinatra/README.jp.rdoc +1056 -0
  82. data/vendor/sinatra/README.ko.rdoc +1932 -0
  83. data/vendor/sinatra/README.pt-br.rdoc +778 -0
  84. data/vendor/sinatra/README.pt-pt.rdoc +647 -0
  85. data/vendor/sinatra/README.rdoc +2049 -0
  86. data/vendor/sinatra/README.ru.rdoc +2033 -0
  87. data/vendor/sinatra/README.zh.rdoc +1816 -0
  88. data/vendor/sinatra/Rakefile +182 -0
  89. data/vendor/sinatra/examples/chat.rb +61 -0
  90. data/vendor/sinatra/examples/simple.rb +3 -0
  91. data/vendor/sinatra/examples/stream.ru +26 -0
  92. data/vendor/sinatra/lib/sinatra.rb +5 -0
  93. data/vendor/sinatra/lib/sinatra/base.rb +1820 -0
  94. data/vendor/sinatra/lib/sinatra/images/404.png +0 -0
  95. data/vendor/sinatra/lib/sinatra/images/500.png +0 -0
  96. data/vendor/sinatra/lib/sinatra/main.rb +30 -0
  97. data/vendor/sinatra/lib/sinatra/showexceptions.rb +345 -0
  98. data/vendor/sinatra/lib/sinatra/version.rb +3 -0
  99. data/vendor/sinatra/sinatra.gemspec +18 -0
  100. data/vendor/sinatra/test/base_test.rb +172 -0
  101. data/vendor/sinatra/test/builder_test.rb +91 -0
  102. data/vendor/sinatra/test/coffee_test.rb +90 -0
  103. data/vendor/sinatra/test/compile_test.rb +139 -0
  104. data/vendor/sinatra/test/contest.rb +98 -0
  105. data/vendor/sinatra/test/creole_test.rb +65 -0
  106. data/vendor/sinatra/test/delegator_test.rb +160 -0
  107. data/vendor/sinatra/test/encoding_test.rb +20 -0
  108. data/vendor/sinatra/test/erb_test.rb +98 -0
  109. data/vendor/sinatra/test/extensions_test.rb +98 -0
  110. data/vendor/sinatra/test/filter_test.rb +437 -0
  111. data/vendor/sinatra/test/haml_test.rb +91 -0
  112. data/vendor/sinatra/test/helper.rb +123 -0
  113. data/vendor/sinatra/test/helpers_test.rb +1768 -0
  114. data/vendor/sinatra/test/integration/app.rb +62 -0
  115. data/vendor/sinatra/test/integration_helper.rb +222 -0
  116. data/vendor/sinatra/test/integration_test.rb +87 -0
  117. data/vendor/sinatra/test/less_test.rb +69 -0
  118. data/vendor/sinatra/test/liquid_test.rb +59 -0
  119. data/vendor/sinatra/test/mapped_error_test.rb +305 -0
  120. data/vendor/sinatra/test/markaby_test.rb +80 -0
  121. data/vendor/sinatra/test/markdown_test.rb +82 -0
  122. data/vendor/sinatra/test/middleware_test.rb +68 -0
  123. data/vendor/sinatra/test/nokogiri_test.rb +67 -0
  124. data/vendor/sinatra/test/public/favicon.ico +0 -0
  125. data/vendor/sinatra/test/rabl_test.rb +89 -0
  126. data/vendor/sinatra/test/rack_test.rb +45 -0
  127. data/vendor/sinatra/test/radius_test.rb +59 -0
  128. data/vendor/sinatra/test/rdoc_test.rb +66 -0
  129. data/vendor/sinatra/test/readme_test.rb +120 -0
  130. data/vendor/sinatra/test/request_test.rb +45 -0
  131. data/vendor/sinatra/test/response_test.rb +64 -0
  132. data/vendor/sinatra/test/result_test.rb +76 -0
  133. data/vendor/sinatra/test/route_added_hook_test.rb +59 -0
  134. data/vendor/sinatra/test/routing_test.rb +1175 -0
  135. data/vendor/sinatra/test/sass_test.rb +116 -0
  136. data/vendor/sinatra/test/scss_test.rb +89 -0
  137. data/vendor/sinatra/test/server_test.rb +48 -0
  138. data/vendor/sinatra/test/settings_test.rb +561 -0
  139. data/vendor/sinatra/test/sinatra_test.rb +12 -0
  140. data/vendor/sinatra/test/slim_test.rb +84 -0
  141. data/vendor/sinatra/test/static_test.rb +219 -0
  142. data/vendor/sinatra/test/streaming_test.rb +149 -0
  143. data/vendor/sinatra/test/templates_test.rb +333 -0
  144. data/vendor/sinatra/test/textile_test.rb +65 -0
  145. data/vendor/sinatra/test/views/a/in_a.str +1 -0
  146. data/vendor/sinatra/test/views/ascii.erb +2 -0
  147. data/vendor/sinatra/test/views/b/in_b.str +1 -0
  148. data/vendor/sinatra/test/views/calc.html.erb +1 -0
  149. data/vendor/sinatra/test/views/error.builder +3 -0
  150. data/vendor/sinatra/test/views/error.erb +3 -0
  151. data/vendor/sinatra/test/views/error.haml +3 -0
  152. data/vendor/sinatra/test/views/error.sass +2 -0
  153. data/vendor/sinatra/test/views/explicitly_nested.str +1 -0
  154. data/vendor/sinatra/test/views/foo/hello.test +1 -0
  155. data/vendor/sinatra/test/views/hello.builder +1 -0
  156. data/vendor/sinatra/test/views/hello.coffee +1 -0
  157. data/vendor/sinatra/test/views/hello.creole +1 -0
  158. data/vendor/sinatra/test/views/hello.erb +1 -0
  159. data/vendor/sinatra/test/views/hello.haml +1 -0
  160. data/vendor/sinatra/test/views/hello.less +5 -0
  161. data/vendor/sinatra/test/views/hello.liquid +1 -0
  162. data/vendor/sinatra/test/views/hello.mab +1 -0
  163. data/vendor/sinatra/test/views/hello.md +1 -0
  164. data/vendor/sinatra/test/views/hello.nokogiri +1 -0
  165. data/vendor/sinatra/test/views/hello.rabl +2 -0
  166. data/vendor/sinatra/test/views/hello.radius +1 -0
  167. data/vendor/sinatra/test/views/hello.rdoc +1 -0
  168. data/vendor/sinatra/test/views/hello.sass +2 -0
  169. data/vendor/sinatra/test/views/hello.scss +3 -0
  170. data/vendor/sinatra/test/views/hello.slim +1 -0
  171. data/vendor/sinatra/test/views/hello.str +1 -0
  172. data/vendor/sinatra/test/views/hello.test +1 -0
  173. data/vendor/sinatra/test/views/hello.textile +1 -0
  174. data/vendor/sinatra/test/views/hello.wlang +1 -0
  175. data/vendor/sinatra/test/views/hello.yajl +1 -0
  176. data/vendor/sinatra/test/views/layout2.builder +3 -0
  177. data/vendor/sinatra/test/views/layout2.erb +2 -0
  178. data/vendor/sinatra/test/views/layout2.haml +2 -0
  179. data/vendor/sinatra/test/views/layout2.liquid +2 -0
  180. data/vendor/sinatra/test/views/layout2.mab +2 -0
  181. data/vendor/sinatra/test/views/layout2.nokogiri +3 -0
  182. data/vendor/sinatra/test/views/layout2.rabl +3 -0
  183. data/vendor/sinatra/test/views/layout2.radius +2 -0
  184. data/vendor/sinatra/test/views/layout2.slim +3 -0
  185. data/vendor/sinatra/test/views/layout2.str +2 -0
  186. data/vendor/sinatra/test/views/layout2.test +1 -0
  187. data/vendor/sinatra/test/views/layout2.wlang +2 -0
  188. data/vendor/sinatra/test/views/nested.str +1 -0
  189. data/vendor/sinatra/test/views/utf8.erb +2 -0
  190. data/vendor/sinatra/test/wlang_test.rb +70 -0
  191. data/vendor/sinatra/test/yajl_test.rb +86 -0
  192. metadata +414 -0
@@ -0,0 +1,2106 @@
1
+ = Sinatra
2
+ <i>Atención: Este documento es una traducción de la versión en inglés y puede estar desactualizado.</i>
3
+
4
+ Sinatra es un
5
+ {DSL}[http://es.wikipedia.org/wiki/Lenguaje_específico_del_dominio] para
6
+ crear aplicaciones web rápidamente en Ruby con un mínimo esfuerzo:
7
+
8
+ # miapp.rb
9
+ require 'sinatra'
10
+
11
+ get '/' do
12
+ 'Hola mundo!'
13
+ end
14
+
15
+ Instalá la gem y ejecutá la aplicación con:
16
+
17
+ gem install sinatra
18
+ ruby -rubygems miapp.rb
19
+
20
+ Podés verla en: http://localhost:4567
21
+
22
+ Es recomendable además ejecutar <tt>gem install thin</tt>, ya que Sinatra lo va
23
+ a utilizar cuando esté disponible.
24
+
25
+ == Rutas
26
+
27
+ En Sinatra, una ruta está compuesta por un método HTTP y un patrón de una URL.
28
+ Cada ruta se asocia con un bloque:
29
+
30
+ get '/' do
31
+ .. mostrar algo ..
32
+ end
33
+
34
+ post '/' do
35
+ .. crear algo ..
36
+ end
37
+
38
+ put '/' do
39
+ .. reemplazar algo ..
40
+ end
41
+
42
+ patch '/' do
43
+ .. modificar algo ..
44
+ end
45
+
46
+ delete '/' do
47
+ .. aniquilar algo ..
48
+ end
49
+
50
+ options '/' do
51
+ .. informar algo ..
52
+ end
53
+
54
+
55
+ Las rutas son comparadas en el orden en el que son definidas. La primer ruta
56
+ que coincide con la petición es invocada.
57
+
58
+ Los patrones de las rutas pueden incluir parámetros nombrados, accesibles a
59
+ través de el hash <tt>params</tt>:
60
+
61
+ get '/hola/:nombre' do
62
+ # coincide con "GET /hola/foo" y "GET /hola/bar"
63
+ # params[:nombre] es 'foo' o 'bar'
64
+ "Hola #{params[:nombre]}!"
65
+ end
66
+
67
+ También podés acceder a los parámetros nombrados usando parámetros de bloque:
68
+
69
+ get '/hola/:nombre' do |n|
70
+ "Hola #{n}!"
71
+ end
72
+
73
+ Los patrones de ruta también pueden incluir parámetros splat (o wildcard),
74
+ accesibles a través del arreglo <tt>params[:splat]</tt>:
75
+
76
+ get '/decir/*/al/*' do
77
+ # coincide con /decir/hola/al/mundo
78
+ params[:splat] # => ["hola", "mundo"]
79
+ end
80
+
81
+ get '/descargar/*.*' do
82
+ # coincide con /descargar/path/al/archivo.xml
83
+ params[:splat] # => ["path/al/archivo", "xml"]
84
+ end
85
+
86
+ O, con parámetros de bloque:
87
+
88
+ get '/descargar/*.*' do |path, ext|
89
+ [path, ext] # => ["path/al/archivo", "xml"]
90
+ end
91
+
92
+ Rutas con Expresiones Regulares:
93
+
94
+ get %r{/hola/([\w]+)} do
95
+ "Hola, #{params[:captures].first}!"
96
+ end
97
+
98
+ O con un parámetro de bloque:
99
+
100
+ get %r{/hola/([\w]+)} do |c|
101
+ "Hola, #{c}!"
102
+ end
103
+
104
+ Los patrones de ruta pueden contener parámetros opcionales:
105
+
106
+ get '/posts.?:formato?' do
107
+ # coincide con "GET /posts" y además admite cualquier extensión, por
108
+ # ejemplo, "GET /posts.json", "GET /posts.xml", etc.
109
+ end
110
+
111
+ A propósito, a menos que desactivés la protección para el ataque <em>path
112
+ traversal</em> (ver más abajo), el path de la petición puede ser modificado
113
+ antes de que se compare con los de tus rutas.
114
+
115
+ === Condiciones
116
+
117
+ Las rutas pueden incluir una variedad de condiciones de selección, como por
118
+ ejemplo el user agent:
119
+
120
+ get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
121
+ "Estás usando la versión de Songbird #{params[:agent][0]}"
122
+ end
123
+
124
+ get '/foo' do
125
+ # Coincide con browsers que no sean songbird
126
+ end
127
+
128
+ Otras condiciones disponibles son +host_name+ y +provides+:
129
+
130
+ get '/', :host_name => /^admin\./ do
131
+ "Área de Administración, Acceso denegado!"
132
+ end
133
+
134
+ get '/', :provides => 'html' do
135
+ haml :index
136
+ end
137
+
138
+ get '/', :provides => ['rss', 'atom', 'xml'] do
139
+ builder :feed
140
+ end
141
+
142
+ Podés definir tus propias condiciones fácilmente:
143
+
144
+ set(:probabilidad) { |valor| condition { rand <= valor } }
145
+
146
+ get '/gana_un_auto', :probabilidad => 0.1 do
147
+ "Ganaste!"
148
+ end
149
+
150
+ get '/gana_un_auto' do
151
+ "Lo siento, perdiste."
152
+ end
153
+
154
+ Si tu condición acepta más de un argumento, podés pasarle un arreglo. Al
155
+ definir la condición puede resultarte conveniente utilizar el operador splat en
156
+ la lista de parámetros:
157
+
158
+ set(:autorizar) do |*roles| # <- mirá el splat
159
+ condition do
160
+ unless sesion_iniciada? && roles.any? {|rol| usuario_actual.tiene_rol? rol }
161
+ redirect "/iniciar_sesion/", 303
162
+ end
163
+ end
164
+ end
165
+
166
+ get "/mi/cuenta/", :autorizar => [:usuario, :administrador] do
167
+ "Detalles de mi cuenta"
168
+ end
169
+
170
+ get "/solo/administradores/", :autorizar => :administrador do
171
+ "Únicamente para administradores!"
172
+ end
173
+
174
+ === Valores de Retorno
175
+
176
+ El valor de retorno de un bloque de ruta determina al menos el cuerpo de la
177
+ respuesta que se le pasa al cliente HTTP o al siguiente middleware en la pila
178
+ de Rack. Lo más común es que sea un string, como en los ejemplos anteriores.
179
+ Sin embargo, otros valor también son aceptados.
180
+
181
+ Podés devolver cualquier objeto que sea una respuesta Rack válida, un objeto
182
+ que represente el cuerpo de una respuesta Rack o un código de estado HTTP:
183
+
184
+ * Un arreglo con tres elementos: <tt>[estado (Fixnum), cabeceras (Hash), cuerpo de la respuesta (responde a #each)]</tt>
185
+ * Un arreglo con dos elementos: <tt>[estado (Fixnum), cuerpo de la respuesta (responde a #each)]</tt>
186
+ * Un objeto que responde a <tt>#each</tt> y que le pasa únicamente strings al bloque dado
187
+ * Un Fixnum representando el código de estado
188
+
189
+ De esa manera podemos, por ejemplo, implementar fácilmente un streaming:
190
+
191
+ class Stream
192
+ def each
193
+ 100.times { |i| yield "#{i}\n" }
194
+ end
195
+ end
196
+
197
+ get('/') { Stream.new }
198
+
199
+ === Comparadores de Rutas Personalizados
200
+
201
+ Como se mostró anteriormente, Sinatra permite utilizar Strings y expresiones
202
+ regulares para definir las rutas. Sin embargo, la cosa no termina ahí. Podés
203
+ definir tus propios comparadores muy fácilmente:
204
+
205
+ class PattronCualquieraMenos
206
+ Match = Struct.new(:captures)
207
+
208
+ def initialize(excepto)
209
+ @excepto = excepto
210
+ @capturas = Match.new([])
211
+ end
212
+
213
+ def match(str)
214
+ @capturas unless @excepto === str
215
+ end
216
+ end
217
+
218
+ def cualquiera_menos(patron)
219
+ PatronCualquieraMenos.new(patron)
220
+ end
221
+
222
+ get cualquiera_menos("/index") do
223
+ # ...
224
+ end
225
+
226
+ Tené en cuenta que el ejemplo anterior es un poco rebuscado. Un resultado
227
+ similar puede conseguirse más sencillamente:
228
+
229
+ get // do
230
+ pass if request.path_info == "/index"
231
+ # ...
232
+ end
233
+
234
+ O, usando un lookahead negativo:
235
+
236
+ get %r{^(?!/index$)} do
237
+ # ...
238
+ end
239
+
240
+ == Archivos Estáticos
241
+
242
+ Los archivos estáticos son servidos desde el directorio público
243
+ <tt>./public</tt>. Podés especificar una ubicación diferente ajustando la
244
+ opción <tt>:public_folder</tt>:
245
+
246
+ set :public_folder, File.dirname(__FILE__) + '/estaticos'
247
+
248
+ Notá que el nombre del directorio público no está incluido en la URL. Por
249
+ ejemplo, el archivo <tt>./public/css/style.css</tt> se accede a través de
250
+ <tt>http://ejemplo.com/css/style.css</tt>.
251
+
252
+ Usá la configuración <tt>:static_cache_control</tt> para agregar el encabezado
253
+ <tt>Cache-Control</tt> (ver la sección de configuración para más detalles).
254
+
255
+ == Vistas / Plantillas
256
+
257
+ Cada lenguaje de plantilla se expone a través de un método de renderizado que
258
+ lleva su nombre. Estos métodos simplemente devuelven un string:
259
+
260
+ get '/' do
261
+ erb :index
262
+ end
263
+
264
+ Renderiza <tt>views/index.erb</tt>.
265
+
266
+ En lugar del nombre de la plantilla podés proporcionar directamente el
267
+ contenido de la misma:
268
+
269
+ get '/' do
270
+ codigo = "<%= Time.now %>"
271
+ erb codigo
272
+ end
273
+
274
+ Los métodos de renderizado, aceptan además un segundo argumento, el hash de
275
+ opciones:
276
+
277
+ get '/' do
278
+ erb :index, :layout => :post
279
+ end
280
+
281
+ Renderiza <tt>views/index.erb</tt> embebido en <tt>views/post.erb</tt> (por
282
+ defecto, la plantilla :index es embebida en <tt>views/layout.erb</tt> siempre y
283
+ cuando este último archivo exista).
284
+
285
+ Cualquier opción que Sinatra no entienda le será pasada al motor de renderizado
286
+ de la plantilla:
287
+
288
+ get '/' do
289
+ haml :index, :format => :html5
290
+ end
291
+
292
+ Además podés definir las opciones para un lenguaje de plantillas de forma
293
+ general:
294
+
295
+ set :haml, :format => :html5
296
+
297
+ get '/' do
298
+ haml :index
299
+ end
300
+
301
+ Las opciones pasadas al método de renderizado tienen precedencia sobre las
302
+ definidas mediante +set+.
303
+
304
+ Opciones disponibles:
305
+
306
+ [locals]
307
+ Lista de variables locales pasadas al documento. Resultan muy útiles cuando
308
+ se combinan con parciales.
309
+ Ejemplo: <tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt>
310
+
311
+ [default_encoding]
312
+ Encoding utilizado cuando el de un string es dudoso. Por defecto toma el
313
+ valor de <tt>settings.default_encoding</tt>.
314
+
315
+ [views]
316
+ Directorio desde donde se cargan las vistas. Por defecto toma el valor de
317
+ <tt>settings.views</tt>.
318
+
319
+ [layout]
320
+ Si es +true+ o +false+ indica que se debe usar, o nó, un layout,
321
+ respectivamente. También puede ser un símbolo que especifique qué plantilla
322
+ usar. Ejemplo: <tt>erb :index, :layout => !request.xhr?</tt>
323
+
324
+ [content_type]
325
+ Content-Type que produce la plantilla. El valor por defecto depende de cada
326
+ lenguaje de plantillas.
327
+
328
+ [scope]
329
+ Ámbito en el que se renderiza la plantilla. Por defecto utiliza la instancia
330
+ de la aplicación. Tené en cuenta que si cambiás esta opción las variables de
331
+ instancia y los helpers van a dejar de estar disponibles.
332
+
333
+ [layout_engine]
334
+ Motor de renderizado de plantillas que usa para el layout. Resulta
335
+ conveniente para lenguajes que no soportan layouts. Por defecto toma el valor
336
+ del motor usado para renderizar la plantilla.
337
+ Ejemplo: <tt>set :rdoc, :layout_engine => :erb</tt>
338
+
339
+ Se asume que las plantillas están ubicadas directamente bajo el directorio
340
+ <tt>./views</tt>. Para usar un directorio de vistas diferente:
341
+
342
+ set :views, settings.root + '/plantillas'
343
+
344
+ Es importante acordarse que siempre tenés que referenciar a las plantillas con
345
+ símbolos, incluso cuando se encuentran en un subdirectorio (en este caso tenés
346
+ que usar <tt>:'subdir/plantilla'</tt>). Tenés que usar un símbolo porque los
347
+ métodos de renderización van a renderizar directamente cualquier string que se
348
+ les pase como argumento.
349
+
350
+ === Lenguajes de Plantillas Disponibles
351
+
352
+ Algunos lenguajes tienen varias implementaciones. Para especificar que
353
+ implementación usar (y para ser thread-safe), deberías requerirla antes de
354
+ usarla:
355
+
356
+ require 'rdiscount' # o require 'bluecloth'
357
+ get('/') { markdown :index }
358
+
359
+ === Plantillas Haml
360
+
361
+ Dependencias:: {haml}[http://haml.info/]
362
+ Extensiones de Archivo:: <tt>.haml</tt>
363
+ Ejemplo:: <tt>haml :index, :format => :html5</tt>
364
+
365
+ === Plantillas Erb
366
+
367
+ Dependencias:: {erubis}[http://www.kuwata-lab.com/erubis/] o
368
+ erb (incluida en Ruby)
369
+ Extensiones de Archivo:: <tt>.erb</tt>, <tt>.rhtml</tt> o <tt>.erubis</tt>
370
+ (solamente con Erubis)
371
+ Ejemplo:: <tt>erb :index</tt>
372
+
373
+ === Plantillas Builder
374
+
375
+ Dependencias:: {builder}[http://builder.rubyforge.org/]
376
+ Extensiones de Archivo:: <tt>.builder</tt>
377
+ Ejemplo:: <tt>builder { |xml| xml.em "hola" }</tt>
378
+
379
+ Además, acepta un bloque con la definición de la plantilla (ver el ejemplo).
380
+
381
+ === Plantillas Nokogiri
382
+
383
+ Dependencias:: {nokogiri}[http://nokogiri.org/]
384
+ Extensiones de Archivo:: <tt>.nokogiri</tt>
385
+ Ejemplo:: <tt>nokogiri { |xml| xml.em "hola" }</tt>
386
+
387
+ Además, acepta un bloque con la definición de la plantilla (ver el ejemplo).
388
+
389
+ === Plantillas Sass
390
+
391
+ Dependencias:: {sass}[http://sass-lang.com/]
392
+ Extensiones de Archivo:: <tt>.sass</tt>
393
+ Ejemplo:: <tt>sass :stylesheet, :style => :expanded</tt>
394
+
395
+ === Plantillas SCSS
396
+
397
+ Dependencias:: {scss}[http://sass-lang.com/]
398
+ Extensiones de Archivo:: <tt>.scss</tt>
399
+ Ejemplo:: <tt>scss :stylesheet, :style => :expanded</tt>
400
+
401
+ === Plantillas Less
402
+
403
+ Dependencias:: {less}[http://www.lesscss.org/]
404
+ Extensiones de Archivo:: <tt>.less</tt>
405
+ Ejemplo:: <tt>less :stylesheet</tt>
406
+
407
+ === Plantillas Liquid
408
+
409
+ Dependencias:: {liquid}[http://www.liquidmarkup.org/]
410
+ Extensiones de Archivo:: <tt>.liquid</tt>
411
+ Ejemplo:: <tt>liquid :index, :locals => { :clave => 'valor' }</tt>
412
+
413
+ Como no vas a poder llamar a métodos de Ruby (excepto por +yield+) desde una
414
+ plantilla Liquid, casi siempre vas a querer pasarle locales.
415
+
416
+ === Plantillas Markdown
417
+
418
+ Dependencias:: {rdiscount}[https://github.com/rtomayko/rdiscount],
419
+ {redcarpet}[https://github.com/vmg/redcarpet],
420
+ {bluecloth}[http://deveiate.org/projects/BlueCloth],
421
+ {kramdown}[http://kramdown.rubyforge.org/] *o*
422
+ {maruku}[http://maruku.rubyforge.org/]
423
+ Extensiones de Archivo:: <tt>.markdown</tt>, <tt>.mkd</tt> y <tt>.md</tt>
424
+ Ejemplo:: <tt>markdown :index, :layout_engine => :erb</tt>
425
+
426
+ No es posible llamar métodos desde markdown, ni pasarle locales. Por lo tanto,
427
+ generalmente vas a usarlo en combinación con otro motor de renderizado:
428
+
429
+ erb :resumen, :locals => { :texto => markdown(:introduccion) }
430
+
431
+ Tené en cuenta que también podés llamar al método +markdown+ desde otras
432
+ plantillas:
433
+
434
+ %h1 Hola Desde Haml!
435
+ %p= markdown(:saludos)
436
+
437
+ Como no podés utilizar Ruby desde Markdown, no podés usar layouts escritos en
438
+ Markdown. De todos modos, es posible usar un motor de renderizado para el
439
+ layout distinto al de la plantilla pasando la opción <tt>:layout_engine</tt>.
440
+
441
+ === Plantillas Textile
442
+
443
+ Dependencias:: {RedCloth}[http://redcloth.org/]
444
+ Extensiones de Archivo:: <tt>.textile</tt>
445
+ Ejemplo:: <tt>textile :index, :layout_engine => :erb</tt>
446
+
447
+ No es posible llamar métodos desde textile, ni pasarle locales. Por lo tanto,
448
+ generalmente vas a usarlo en combinación con otro motor de renderizado:
449
+
450
+ erb :resumen, :locals => { :texto => textile(:introduccion) }
451
+
452
+ Tené en cuenta que también podés llamar al método +textile+ desde otras
453
+ plantillas:
454
+
455
+ %h1 Hola Desde Haml!
456
+ %p= textile(:saludos)
457
+
458
+ Como no podés utilizar Ruby desde Textile, no podés usar layouts escritos en
459
+ Textile. De todos modos, es posible usar un motor de renderizado para el
460
+ layout distinto al de la plantilla pasando la opción <tt>:layout_engine</tt>.
461
+
462
+ === Plantillas RDoc
463
+
464
+ Dependencias:: {rdoc}[http://rdoc.rubyforge.org/]
465
+ Extensiones de Archivo:: <tt>.rdoc</tt>
466
+ Ejemplo:: <tt>rdoc :LEEME, :layout_engine => :erb</tt>
467
+
468
+ No es posible llamar métodos desde rdoc, ni pasarle locales. Por lo tanto,
469
+ generalmente vas a usarlo en combinación con otro motor de renderizado:
470
+
471
+ erb :resumen, :locals => { :texto => rdoc(:introduccion) }
472
+
473
+ Tené en cuenta que también podés llamar al método +rdoc+ desde otras
474
+ plantillas:
475
+
476
+ %h1 Hola Desde Haml!
477
+ %p= rdoc(:saludos)
478
+
479
+ Como no podés utilizar Ruby desde RDoc, no podés usar layouts escritos en RDoc.
480
+ De todos modos, es posible usar un motor de renderizado para el layout distinto
481
+ al de la plantilla pasando la opción <tt>:layout_engine</tt>.
482
+
483
+ === Plantillas Radius
484
+
485
+ Dependencias:: {radius}[http://radius.rubyforge.org/]
486
+ Extensiones de Archivo:: <tt>.radius</tt>
487
+ Ejemplo:: <tt>radius :index, :locals => { :clave => 'valor' }</tt>
488
+
489
+ Como no vas a poder llamar a métodos de Ruby (excepto por +yield+) desde una
490
+ plantilla Radius, casi siempre vas a querer pasarle locales.
491
+
492
+ === Plantillas Markaby
493
+
494
+ Dependencias:: {markaby}[http://markaby.github.com/]
495
+ Extensiones de Archivo:: <tt>.mab</tt>
496
+ Ejemplos:: <tt>markaby { h1 "Bienvenido!" }</tt>
497
+
498
+ Además, acepta un bloque con la definición de la plantilla (ver el ejemplo).
499
+
500
+ === Plantillas RABL
501
+
502
+ Dependencias:: {rabl}[https://github.com/nesquena/rabl]
503
+ Extensiones de Archivo:: <tt>.rabl</tt>
504
+ Ejemplo:: <tt>rabl :index</tt>
505
+
506
+ === Plantillas Slim
507
+
508
+ Dependencias:: {slim}[http://slim-lang.com/]
509
+ Extensiones de Archivo:: <tt>.slim</tt>
510
+ Ejemplo:: <tt>slim :index</tt>
511
+
512
+ === Plantillas Creole
513
+
514
+ Dependencias:: {creole}[https://github.com/minad/creole]
515
+ Extensiones de Archivo:: <tt>.creole</tt>
516
+ Ejemplo:: <tt>creole :wiki, :layout_engine => :erb</tt>
517
+
518
+ No es posible llamar métodos desde creole, ni pasarle locales. Por lo tanto,
519
+ generalmente vas a usarlo en combinación con otro motor de renderizado:
520
+
521
+ erb :resumen, :locals => { :texto => cerole(:introduccion) }
522
+
523
+ Tené en cuenta que también podés llamar al método +creole+ desde otras
524
+ plantillas:
525
+
526
+ %h1 Hola Desde Haml!
527
+ %p= creole(:saludos)
528
+
529
+ Como no podés utilizar Ruby desde Creole, no podés usar layouts escritos en
530
+ Creloe. De todos modos, es posible usar un motor de renderizado para el layout
531
+ distinto al de la plantilla pasando la opción <tt>:layout_engine</tt>.
532
+
533
+ === Plantillas CoffeeScript
534
+
535
+ Dependencias:: {coffee-script}[https://github.com/josh/ruby-coffee-script]
536
+ y un {mecanismo para ejecutar javascript}[https://github.com/sstephenson/execjs/blob/master/README.md#readme]
537
+ Extensiones de Archivo:: <tt>.coffee</tt>
538
+ Ejemplo:: <tt>coffee :index</tt>
539
+
540
+ === Plantillas Yajl
541
+
542
+ Dependencias:: {yajl-ruby}[https://github.com/brianmario/yajl-ruby]
543
+ Extensiones de Archivo:: <tt>.yajl</tt>
544
+ Ejemplo:: <tt>yajl :index, :locals => { :key => 'qux' }, :callback => 'present', :variable => 'resource'</tt>
545
+
546
+ El contenido de La plantilla se evalúa como código Ruby, y la variable +json+ es convertida a JSON mediante <tt>#to_json</tt>.
547
+
548
+ json = { :foo => 'bar' }
549
+ json[:baz] = key
550
+
551
+ Las opciones <tt>:callback</tt> y <tt>:variable</tt> se pueden utilizar para decorar el objeto renderizado:
552
+
553
+ var resource = {"foo":"bar","baz":"qux"}; present(resource);
554
+
555
+ === Plantillas WLang
556
+
557
+ Dependencias:: {wlang}[https://github.com/blambeau/wlang/]
558
+ Extensiones de Archivo:: <tt>.wlang</tt>
559
+ Ejemplo:: <tt>wlang :index, :locals => { :clave => 'valor' }</tt>
560
+
561
+ Como no vas a poder llamar a métodos de Ruby (excepto por +yield+) desde una
562
+ plantilla WLang, casi siempre vas a querer pasarle locales.
563
+
564
+ === Plantillas Embebidas
565
+
566
+ get '/' do
567
+ haml '%div.titulo Hola Mundo'
568
+ end
569
+
570
+ Renderiza el template embebido en el string.
571
+
572
+ === Accediendo a Variables en Plantillas
573
+
574
+ Las plantillas son evaluadas dentro del mismo contexto que los manejadores de
575
+ ruta. Las variables de instancia asignadas en los manejadores de ruta son
576
+ accesibles directamente por las plantillas:
577
+
578
+ get '/:id' do
579
+ @foo = Foo.find(params[:id])
580
+ haml '%h1= @foo.nombre'
581
+ end
582
+
583
+ O es posible especificar un Hash de variables locales explícitamente:
584
+
585
+ get '/:id' do
586
+ foo = Foo.find(params[:id])
587
+ haml '%h1= bar.nombre', :locals => { :bar => foo }
588
+ end
589
+
590
+ Esto es usado típicamente cuando se renderizan plantillas como parciales desde
591
+ adentro de otras plantillas.
592
+
593
+ === Plantillas Inline
594
+
595
+ Las plantillas pueden ser definidas al final del archivo fuente:
596
+
597
+ require 'rubygems'
598
+ require 'sinatra'
599
+
600
+ get '/' do
601
+ haml :index
602
+ end
603
+
604
+ __END__
605
+
606
+ @@ layout
607
+ %html
608
+ = yield
609
+
610
+ @@ index
611
+ %div.titulo Hola mundo!!!!!
612
+
613
+ NOTA: únicamente las plantillas inline definidas en el archivo fuente que
614
+ requiere sinatra son cargadas automáticamente. Llamá <tt>enable
615
+ :inline_templates</tt> explícitamente si tenés plantillas inline en otros
616
+ archivos fuente.
617
+
618
+ === Plantillas Nombradas
619
+
620
+ Las plantillas también pueden ser definidas usando el método top-level
621
+ <tt>template</tt>:
622
+
623
+ template :layout do
624
+ "%html\n =yield\n"
625
+ end
626
+
627
+ template :index do
628
+ '%div.titulo Hola Mundo!'
629
+ end
630
+
631
+ get '/' do
632
+ haml :index
633
+ end
634
+
635
+ Si existe una plantilla con el nombre "layout", va a ser usada cada vez que
636
+ una plantilla es renderizada. Podés desactivar los layouts individualmente
637
+ pasando <tt>:layout => false</tt> o globalmente con
638
+ <tt>set :haml, :layout => false</tt>:
639
+
640
+ get '/' do
641
+ haml :index, :layout => !request.xhr?
642
+ end
643
+
644
+ === Asociando Extensiones de Archivo
645
+
646
+ Para asociar una extensión de archivo con un motor de renderizado, usá
647
+ <tt>Tilt.register</tt>. Por ejemplo, si querés usar la extensión +tt+ para
648
+ las plantillas Textile, podés hacer lo siguiente:
649
+
650
+ Tilt.register :tt, Tilt[:textile]
651
+
652
+ === Agregando Tu Propio Motor de Renderizado
653
+
654
+ Primero, registrá tu motor con Tilt, y después, creá tu método de renderizado:
655
+
656
+ Tilt.register :mipg, MiMotorParaPlantillaGenial
657
+
658
+ helpers do
659
+ def mypg(*args) render(:mypg, *args) end
660
+ end
661
+
662
+ get '/' do
663
+ mypg :index
664
+ end
665
+
666
+ Renderiza <tt>./views/index.mypg</tt>. Mirá https://github.com/rtomayko/tilt
667
+ para aprender más de Tilt.
668
+
669
+ == Filtros
670
+
671
+ Los filtros +before+ son evaluados antes de cada petición dentro del mismo
672
+ contexto que las rutas. Pueden modificar la petición y la respuesta. Las
673
+ variables de instancia asignadas en los filtros son accesibles por las rutas y
674
+ las plantillas:
675
+
676
+ before do
677
+ @nota = 'Hey!'
678
+ request.path_info = '/foo/bar/baz'
679
+ end
680
+
681
+ get '/foo/*' do
682
+ @nota #=> 'Hey!'
683
+ params[:splat] #=> 'bar/baz'
684
+ end
685
+
686
+ Los filtros +after+ son evaluados después de cada petición dentro del mismo
687
+ contexto y también pueden modificar la petición y la respuesta. Las variables
688
+ de instancia asignadas en los filtros +before+ y en las rutas son accesibles por
689
+ los filtros +after+:
690
+
691
+ after do
692
+ puts response.status
693
+ end
694
+
695
+ Nota: A menos que usés el método +body+ en lugar de simplemente devolver un
696
+ string desde una ruta, el cuerpo de la respuesta no va a estar disponible en
697
+ un filtro after, debido a que todavía no se ha generado.
698
+
699
+ Los filtros aceptan un patrón opcional, que cuando está presente causa que los
700
+ mismos sean evaluados únicamente si el path de la petición coincide con ese
701
+ patrón:
702
+
703
+ before '/protegido/*' do
704
+ autenticar!
705
+ end
706
+
707
+ after '/crear/:slug' do |slug|
708
+ session[:ultimo_slug] = slug
709
+ end
710
+
711
+ Al igual que las rutas, los filtros también pueden aceptar condiciones:
712
+
713
+ before :agent => /Songbird/ do
714
+ # ...
715
+ end
716
+
717
+ after '/blog/*', :host_name => 'ejemplo.com' do
718
+ # ...
719
+ end
720
+
721
+ == Ayudantes
722
+
723
+ Usá el método top-level <tt>helpers</tt> para definir métodos ayudantes que
724
+ pueden ser utilizados dentro de los manejadores de rutas y las plantillas:
725
+
726
+ helpers do
727
+ def bar(nombre)
728
+ "#{nombre}bar"
729
+ end
730
+ end
731
+
732
+ get '/:nombre' do
733
+ bar(params[:nombre])
734
+ end
735
+
736
+ Por cuestiones organizativas, puede resultar conveniente organizar los métodos
737
+ ayudantes en distintos módulos:
738
+
739
+ module FooUtils
740
+ def foo(nombre) "#{nombre}foo" end
741
+ end
742
+
743
+ module BarUtils
744
+ def bar(nombre) "#{nombre}bar" end
745
+ end
746
+
747
+ helpers FooUtils, BarUtils
748
+
749
+ El efecto de utilizar <tt>helpers</tt> de esta manera es el mismo que resulta de
750
+ incluir los módulos en la clase de la aplicación.
751
+
752
+ === Usando Sesiones
753
+
754
+ Una sesión es usada para mantener el estado a través de distintas peticiones.
755
+ Cuando están activadas, tenés un hash de sesión para cada sesión de usuario:
756
+
757
+ enable :sessions
758
+
759
+ get '/' do
760
+ "valor = " << session[:valor].inspect
761
+ end
762
+
763
+ get '/:valor' do
764
+ session[:valor] = params[:valor]
765
+ end
766
+
767
+ Tené en cuenta que <tt>enable :sessions</tt> guarda todos los datos en una
768
+ cookie, lo que no es siempre deseable (guardar muchos datos va a incrementar
769
+ tu tráfico, por citar un ejemplo). Podés usar cualquier middleware Rack para
770
+ manejar sesiones, de la misma manera que usarías cualquier otro middleware,
771
+ pero con la salvedad de que *no* tenés que llamar a <tt>enable :sessions</tt>:
772
+
773
+ use Rack::Session::Pool, :expire_after => 2592000
774
+
775
+ get '/' do
776
+ "valor = " << session[:valor].inspect
777
+ end
778
+
779
+ get '/:valor' do
780
+ session[:valor] = params[:valor]
781
+ end
782
+
783
+ Para incrementar la seguridad, los datos de la sesión almacenados en
784
+ la cookie son firmados con un secreto de sesión. Este secreto, es
785
+ generado aleatoriamente por Sinatra. De cualquier manera, hay que
786
+ tener en cuenta que cada vez que inicies la aplicación se va a generar
787
+ uno nuevo. Así, si querés que todas las instancias de tu aplicación
788
+ compartan un único secreto, tenés que definirlo vos:
789
+
790
+ set :session_secret, 'super secreto'
791
+
792
+ Si necesitás una configuración más específica, +sessions+ acepta un
793
+ Hash con opciones:
794
+
795
+ set :sessions, :domain => 'foo.com'
796
+
797
+ === Interrupción
798
+
799
+ Para detener inmediatamente una petición dentro de un filtro o una ruta usá:
800
+
801
+ halt
802
+
803
+ También podés especificar el estado:
804
+
805
+ halt 410
806
+
807
+ O el cuerpo:
808
+
809
+ halt 'esto va a ser el cuerpo'
810
+
811
+ O los dos:
812
+
813
+ halt 401, 'salí de acá!'
814
+
815
+ Con cabeceras:
816
+
817
+ halt 402, { 'Content-Type' => 'text/plain' }, 'venganza'
818
+
819
+ Obviamente, es posible utilizar +halt+ con una plantilla:
820
+
821
+ halt erb(:error)
822
+
823
+ === Paso
824
+
825
+ Una ruta puede pasarle el procesamiento a la siguiente ruta que coincida con
826
+ la petición usando <tt>pass</tt>:
827
+
828
+ get '/adivina/:quien' do
829
+ pass unless params[:quien] == 'Franco'
830
+ 'Adivinaste!'
831
+ end
832
+
833
+ get '/adivina/*' do
834
+ 'Erraste!'
835
+ end
836
+
837
+ Se sale inmediatamente del bloque de la ruta y se le pasa el control a la
838
+ siguiente ruta que coincida. Si no coincide ninguna ruta, se devuelve un 404.
839
+
840
+ === Ejecutando Otra Ruta
841
+
842
+ Cuando querés obtener el resultado de la llamada a una ruta, +pass+ no te va a
843
+ servir. Para lograr esto, podés usar +call+:
844
+
845
+ get '/foo' do
846
+ status, headers, body = call env.merge("PATH_INFO" => '/bar')
847
+ [status, headers, body.map(&:upcase)]
848
+ end
849
+
850
+ get '/bar' do
851
+ "bar"
852
+ end
853
+
854
+ Notá que en el ejemplo anterior, es conveniente mover <tt>"bar"</tt> a un
855
+ helper, y llamarlo desde <tt>/foo</tt> y <tt>/bar</tt>. Así, vas a simplificar
856
+ las pruebas y a mejorar el rendimiento.
857
+
858
+ Si querés que la petición se envíe a la misma instancia de la aplicación en
859
+ lugar de a otra, usá <tt>call!</tt> en lugar de <tt>call</tt>.
860
+
861
+ En la especificación de Rack podés encontrar más información sobre
862
+ <tt>call</tt>.
863
+
864
+ === Asignando el Código de Estado, los Encabezados y el Cuerpo de una Respuesta
865
+
866
+ Es posible, y se recomienda, asignar el código de estado y el cuerpo de una
867
+ respuesta con el valor de retorno de una ruta. De cualquier manera, en varios
868
+ escenarios, puede que sea conveniente asignar el cuerpo en un punto arbitrario
869
+ del flujo de ejecución con el método +body+. A partir de ahí, podés usar ese
870
+ mismo método para acceder al cuerpo de la respuesta:
871
+
872
+ get '/foo' do
873
+ body "bar"
874
+ end
875
+
876
+ after do
877
+ puts body
878
+ end
879
+
880
+ También es posible pasarle un bloque a +body+, que será ejecutado por el Rack
881
+ handler (podés usar esto para implementar streaming, mirá "Valores de retorno").
882
+
883
+ De manera similar, también podés asignar el código de estado y encabezados:
884
+
885
+ get '/foo' do
886
+ status 418
887
+ headers \
888
+ "Allow" => "BREW, POST, GET, PROPFIND, WHEN",
889
+ "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
890
+ body "I'm a tea pot!"
891
+ end
892
+
893
+ También, al igual que +body+, tanto +status+ como +headers+ pueden utilizarse
894
+ para obtener sus valores cuando no se les pasa argumentos.
895
+
896
+ === Streaming De Respuestas
897
+
898
+ A veces vas a querer empezar a enviar la respuesta a pesar de que todavía no
899
+ terminaste de generar su cuerpo. También es posible que, en algunos casos,
900
+ quieras seguir enviando información hasta que el cliente cierre la conexión.
901
+ Cuando esto ocurra, el +stream+ helper te va a ser de gran ayuda:
902
+
903
+ get '/' do
904
+ stream do |out|
905
+ out << "Esto va a ser legen -\n"
906
+ sleep 0.5
907
+ out << " (esperalo) \n"
908
+ sleep 1
909
+ out << "- dario!\n"
910
+ end
911
+ end
912
+
913
+ Podés implementar APIs de streaming,
914
+ {Server-Sent Events}[http://dev.w3.org/html5/eventsource/] y puede ser usado
915
+ como base para {WebSockets}[http://es.wikipedia.org/wiki/WebSockets]. También
916
+ puede ser usado para incrementar el throughput si solo una parte del contenido
917
+ depende de un recurso lento.
918
+
919
+ Hay que tener en cuenta que el comportamiento del streaming, especialmente el
920
+ número de peticiones concurrentes, depende del servidor web utilizado para
921
+ servir la aplicación. Puede que algunos servidores, como es el caso de
922
+ WEBRick, no soporten streaming directamente, así el cuerpo de la respuesta será
923
+ enviado completamente de una vez cuando el bloque pasado a +stream+ finalice su
924
+ ejecución. Si estás usando Shotgun, el streaming no va a funcionar.
925
+
926
+ Cuando se pasa +keep_open+ como parámetro, no se va a enviar el mensaje
927
+ +close+ al objeto de stream. Queda en vos cerrarlo en el punto de ejecución
928
+ que quieras. Nuevamente, hay que tener en cuenta que este comportamiento es
929
+ posible solo en servidores que soporten eventos, como Thin o Rainbows. El
930
+ resto de los servidores van a cerrar el stream de todos modos:
931
+
932
+ set :server, :thin
933
+ conexiones = []
934
+
935
+ get '/' do
936
+ # mantenemos abierto el stream
937
+ stream(:keep_open) { |salida| conexiones << salida }
938
+ end
939
+
940
+ post '/' do
941
+ # escribimos a todos los streams abiertos
942
+ conexiones.each { |salida| salida << params[:mensaje] << "\n" }
943
+ "mensaje enviado"
944
+ end
945
+
946
+ === Log (Registro)
947
+
948
+ En el ámbito de la petición, el helper +logger+ (registrador) expone
949
+ una instancia de +Logger+:
950
+
951
+ get '/' do
952
+ logger.info "cargando datos"
953
+ # ...
954
+ end
955
+
956
+ Este logger tiene en cuenta la configuración de logueo de tu Rack
957
+ handler. Si el logueo está desactivado, este método va a devolver un
958
+ objeto que se comporta como un logger pero que en realidad no hace
959
+ nada. Así, no vas a tener que preocuparte por esta situación.
960
+
961
+ Tené en cuenta que el logueo está habilitado por defecto únicamente
962
+ para <tt>Sinatra::Application</tt>. Si heredaste de
963
+ <tt>Sinatra::Base</tt>, probablemente quieras habilitarlo manualmente:
964
+
965
+ class MiApp < Sinatra::Base
966
+ configure :production, :development do
967
+ enable :logging
968
+ end
969
+ end
970
+
971
+ Para evitar que se inicialice cualquier middleware de logging, configurá
972
+ +logging+ a +nil+. Tené en cuenta que, cuando hagas esto, +logger+ va a
973
+ devolver +nil+. Un caso común es cuando querés usar tu propio logger. Sinatra
974
+ va a usar lo que encuentre en <tt>env['rack.logger']</tt>.
975
+
976
+ === Tipos Mime
977
+
978
+ Cuando usás <tt>send_file</tt> o archivos estáticos tal vez tengas tipos mime
979
+ que Sinatra no entiende. Usá +mime_type+ para registrarlos a través de la
980
+ extensión de archivo:
981
+
982
+ configure do
983
+ mime_type :foo, 'text/foo'
984
+ end
985
+
986
+ También lo podés usar con el ayudante +content_type+:
987
+
988
+ get '/' do
989
+ content_type :foo
990
+ "foo foo foo"
991
+ end
992
+
993
+ === Generando URLs
994
+
995
+ Para generar URLs deberías usar el método +url+. Por ejemplo, en Haml:
996
+
997
+ %a{:href => url('/foo')} foo
998
+
999
+ Tiene en cuenta proxies inversos y encaminadores de Rack, si están presentes.
1000
+
1001
+ Este método también puede invocarse mediante su alias +to+ (mirá un ejemplo
1002
+ a continuación).
1003
+
1004
+ === Redirección del Navegador
1005
+
1006
+ Podés redireccionar al navegador con el método +redirect+:
1007
+
1008
+ get '/foo' do
1009
+ redirect to('/bar')
1010
+ end
1011
+
1012
+ Cualquier parámetro adicional se utiliza de la misma manera que los argumentos
1013
+ pasados a +halt+:
1014
+
1015
+ redirect to('/bar'), 303
1016
+ redirect 'http://google.com', 'te confundiste de lugar, compañero'
1017
+
1018
+ También podés redireccionar fácilmente de vuelta hacia la página desde donde
1019
+ vino el usuario con +redirect back+:
1020
+
1021
+ get '/foo' do
1022
+ "<a href='/bar'>hacer algo</a>"
1023
+ end
1024
+
1025
+ get '/bar' do
1026
+ hacer_algo
1027
+ redirect back
1028
+ end
1029
+
1030
+ Para pasar argumentos con una redirección, podés agregarlos a la cadena de
1031
+ búsqueda:
1032
+
1033
+ redirect to('/bar?suma=42')
1034
+
1035
+ O usar una sesión:
1036
+
1037
+ enable :sessions
1038
+
1039
+ get '/foo' do
1040
+ session[:secreto] = 'foo'
1041
+ redirect to('/bar')
1042
+ end
1043
+
1044
+ get '/bar' do
1045
+ session[:secreto]
1046
+ end
1047
+
1048
+ === Cache Control
1049
+
1050
+ Asignar tus encabezados correctamente es el cimiento para realizar un cacheo
1051
+ HTTP correcto.
1052
+
1053
+ Podés asignar el encabezado Cache-Control fácilmente:
1054
+
1055
+ get '/' do
1056
+ cache_control :public
1057
+ "cachealo!"
1058
+ end
1059
+
1060
+ Pro tip: configurar el cacheo en un filtro +before+:
1061
+
1062
+ before do
1063
+ cache_control :public, :must_revalidate, :max_age => 60
1064
+ end
1065
+
1066
+ Si estás usando el helper +expires+ para definir el encabezado correspondiente,
1067
+ <tt>Cache-Control</tt> se va a definir automáticamente:
1068
+
1069
+ before do
1070
+ expires 500, :public, :must_revalidate
1071
+ end
1072
+
1073
+ Para usar cachés adecuadamente, deberías considerar usar +etag+ o
1074
+ +last_modified+. Es recomendable que llames a estos helpers *antes* de hacer
1075
+ cualquier trabajo pesado, ya que van a enviar la respuesta inmediatamente si
1076
+ el cliente ya tiene la versión actual en su caché:
1077
+
1078
+ get '/articulo/:id' do
1079
+ @articulo = Articulo.find params[:id]
1080
+ last_modified @articulo.updated_at
1081
+ etag @articulo.sha1
1082
+ erb :articulo
1083
+ end
1084
+
1085
+ También es posible usar una
1086
+ {weak ETag}[http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation]:
1087
+
1088
+ etag @articulo.sha1, :weak
1089
+
1090
+ Estos helpers no van a cachear nada por vos, sino que van a facilitar la
1091
+ información necesaria para poder hacerlo. Si estás buscando soluciones rápidas
1092
+ de cacheo con proxys inversos, mirá
1093
+ {rack-cache}[https://github.com/rtomayko/rack-cache]:
1094
+
1095
+ require "rack/cache"
1096
+ require "sinatra"
1097
+
1098
+ use Rack::Cache
1099
+
1100
+ get '/' do
1101
+ cache_control :public, :max_age => 36000
1102
+ sleep 5
1103
+ "hola"
1104
+ end
1105
+
1106
+ Usá la configuración <tt>:static_cache_control</tt> para agregar el encabezado
1107
+ <tt>Cache-Control</tt> a archivos estáticos (ver la sección de configuración
1108
+ para más detalles).
1109
+
1110
+ De acuerdo con la RFC 2616 tu aplicación debería comportarse diferente si a las
1111
+ cabeceras If-Match o If-None-Match se le asigna el valor <tt>*</tt> cuando el
1112
+ recurso solicitado ya existe. Sinatra asume para peticiones seguras (como get)
1113
+ e idempotentes (como put) que el recurso existe, mientras que para el resto
1114
+ (como post), que no. Podes cambiar este comportamiento con la opción
1115
+ <tt>:new_resource</tt>:
1116
+
1117
+ get '/crear' do
1118
+ etag '', :new_resource => true
1119
+ Articulo.create
1120
+ erb :nuevo_articulo
1121
+ end
1122
+
1123
+ Si querés seguir usando una weak ETag, indicalo con la opción <tt>:kind</tt>:
1124
+
1125
+ etag '', :new_resource => true, :kind => :weak
1126
+
1127
+ === Enviando Archivos
1128
+
1129
+ Para enviar archivos, podés usar el método <tt>send_file</tt>:
1130
+
1131
+ get '/' do
1132
+ send_file 'foo.png'
1133
+ end
1134
+
1135
+ Además acepta un par de opciones:
1136
+
1137
+ send_file 'foo.png', :type => :jpg
1138
+
1139
+ Estas opciones son:
1140
+
1141
+ [filename]
1142
+ nombre del archivo devuelto, por defecto es el nombre real del archivo.
1143
+
1144
+ [last_modified]
1145
+ valor para el encabezado Last-Modified, por defecto toma el mtime del archivo.
1146
+
1147
+ [type]
1148
+ el content type que se va a utilizar, si no está presente se intenta adivinar
1149
+ a partir de la extensión del archivo.
1150
+
1151
+ [disposition]
1152
+ se utiliza para el encabezado Content-Disposition, y puede tomar alguno de los
1153
+ siguientes valores: +nil+ (por defecto), <tt>:attachment</tt> e
1154
+ <tt>:inline</tt>
1155
+
1156
+ [length]
1157
+ encabezado Content-Length, por defecto toma el tamaño del archivo.
1158
+
1159
+ [status]
1160
+ código de estado devuelto. Resulta útil al enviar un archivo estático como una
1161
+ página de error.
1162
+
1163
+ Si el Rack handler lo soporta, se intentará no transmitir directamente desde el
1164
+ proceso de Ruby. Si usás este método, Sinatra se va a encargar automáticamente
1165
+ peticiones de rango.
1166
+
1167
+ === Accediendo al objeto de la petición
1168
+
1169
+ El objeto de la petición entrante puede ser accedido desde el nivel de la
1170
+ petición (filtros, rutas y manejadores de errores) a través del método
1171
+ <tt>request</tt>:
1172
+
1173
+ # app corriendo en http://ejemplo.com/ejemplo
1174
+ get '/foo' do
1175
+ t = %w[text/css text/html application/javascript]
1176
+ request.accept # ['text/html', '*/*']
1177
+ request.accept? 'text/xml' # true
1178
+ request.preferred_type(t) # 'text/html'
1179
+ request.body # cuerpo de la petición enviado por el cliente (ver más abajo)
1180
+ request.scheme # "http"
1181
+ request.script_name # "/ejemplo"
1182
+ request.path_info # "/foo"
1183
+ request.port # 80
1184
+ request.request_method # "GET"
1185
+ request.query_string # ""
1186
+ request.content_length # longitud de request.body
1187
+ request.media_type # tipo de medio de request.body
1188
+ request.host # "ejemplo.com"
1189
+ request.get? # true (hay métodos análogos para los otros verbos)
1190
+ request.form_data? # false
1191
+ request["UNA_CABECERA"] # valor de la cabecera UNA_CABECERA
1192
+ request.referrer # la referencia del cliente o '/'
1193
+ request.user_agent # user agent (usado por la condición :agent)
1194
+ request.cookies # hash de las cookies del browser
1195
+ request.xhr? # es una petición ajax?
1196
+ request.url # "http://ejemplo.com/ejemplo/foo"
1197
+ request.path # "/ejemplo/foo"
1198
+ request.ip # dirección IP del cliente
1199
+ request.secure? # false (sería true sobre ssl)
1200
+ request.forwarded? # true (si se está corriendo atrás de un proxy inverso)
1201
+ requuest.env # hash de entorno directamente entregado por Rack
1202
+ end
1203
+
1204
+ Algunas opciones, como <tt>script_name</tt> o <tt>path_info</tt> pueden
1205
+ también ser escritas:
1206
+
1207
+ before { request.path_info = "/" }
1208
+
1209
+ get "/" do
1210
+ "todas las peticiones llegan acá"
1211
+ end
1212
+
1213
+ El objeto <tt>request.body</tt> es una instancia de IO o StringIO:
1214
+
1215
+ post "/api" do
1216
+ request.body.rewind # en caso de que alguien ya lo haya leído
1217
+ datos = JSON.parse request.body.read
1218
+ "Hola #{datos['nombre']}!"
1219
+ end
1220
+
1221
+ === Archivos Adjuntos
1222
+
1223
+ Podés usar el método helper +attachment+ para indicarle al navegador que
1224
+ almacene la respuesta en el disco en lugar de mostrarla en pantalla:
1225
+
1226
+ get '/' do
1227
+ attachment
1228
+ "guardalo!"
1229
+ end
1230
+
1231
+ También podés pasarle un nombre de archivo:
1232
+
1233
+ get '/' do
1234
+ attachment "info.txt"
1235
+ "guardalo!"
1236
+ end
1237
+
1238
+ === Fecha y Hora
1239
+
1240
+ Sinatra pone a tu disposición el helper +time_for+, que genera un objeto +Time+
1241
+ a partir del valor que recibe como argumento. Este valor puede ser un
1242
+ +String+, pero también es capaz de convertir objetos +DateTime+, +Date+ y de
1243
+ otras clases similares:
1244
+
1245
+ get '/' do
1246
+ pass if Time.now > time_for('Dec 23, 2012')
1247
+ "todavía hay tiempo"
1248
+ end
1249
+
1250
+ Este método es usado internamente por métodos como +expires+ y +last_modified+,
1251
+ entre otros. Por lo tanto, es posible extender el comportamiento de estos
1252
+ métodos sobreescribiendo +time_for+ en tu aplicación:
1253
+
1254
+ helpers do
1255
+ def time_for(value)
1256
+ case value
1257
+ when :ayer then Time.now - 24*60*60
1258
+ when :mañana then Time.now + 24*60*60
1259
+ else super
1260
+ end
1261
+ end
1262
+ end
1263
+
1264
+ get '/' do
1265
+ last_modified :ayer
1266
+ expires :mañana
1267
+ "hola"
1268
+ end
1269
+
1270
+ === Buscando los Archivos de las Plantillas
1271
+
1272
+ El helper <tt>find_template</tt> se utiliza para encontrar los archivos de las
1273
+ plantillas que se van a renderizar:
1274
+
1275
+ find_template settings.views, 'foo', Tilt[:haml] do |archivo|
1276
+ puts "podría ser #{archivo}"
1277
+ end
1278
+
1279
+ Si bien esto no es muy útil, lo interesante es que podés sobreescribir este
1280
+ método, y así enganchar tu propio mecanismo de búsqueda. Por ejemplo, para
1281
+ poder utilizar más de un directorio de vistas:
1282
+
1283
+ set :views, ['vistas', 'plantillas']
1284
+
1285
+ helpers do
1286
+ def find_template(views, name, engine, &block)
1287
+ Array(views).each { |v| super(v, name, engine, &block) }
1288
+ end
1289
+ end
1290
+
1291
+ Otro ejemplo consiste en usar directorios diferentes para los distintos motores
1292
+ de renderizado:
1293
+
1294
+ set :views, :sass => 'vistas/sass', :haml => 'plantillas', :defecto => 'vistas'
1295
+
1296
+ helpers do
1297
+ def find_template(views, name, engine, &block)
1298
+ _, folder = views.detect { |k,v| engine == Tilt[k] }
1299
+ folder ||= views[:defecto]
1300
+ super(folder, name, engine, &block)
1301
+ end
1302
+ end
1303
+
1304
+ ¡Es muy fácil convertir estos ejemplos en una extensión y compartirla!.
1305
+
1306
+ Notá que <tt>find_template</tt> no verifica si un archivo existe realmente, sino
1307
+ que llama al bloque que recibe para cada path posible. Esto no representa un
1308
+ problema de rendimiento debido a que +render+ va a usar +break+ ni bien
1309
+ encuentre un archivo que exista. Además, las ubicaciones de las plantillas (y
1310
+ su contenido) se cachean cuando no estás en el modo de desarrollo. Es bueno
1311
+ tener en cuenta lo anteiror si escribís un método medio loco.
1312
+
1313
+ == Configuración
1314
+
1315
+ Ejecutar una vez, en el inicio, en cualquier entorno:
1316
+
1317
+ configure do
1318
+ # asignando una opción
1319
+ set :opcion, 'valor'
1320
+
1321
+ # asignando varias opciones
1322
+ set :a => 1, :b => 2
1323
+
1324
+ # atajo para `set :opcion, true`
1325
+ enable :opcion
1326
+
1327
+ # atajo para `set :opcion, false`
1328
+ disable :opcion
1329
+
1330
+ # también podés tener configuraciones dinámicas usando bloques
1331
+ set(:css_dir) { File.join(views, 'css') }
1332
+ end
1333
+
1334
+ Ejecutar únicamente cuando el entorno (la variable de entorno RACK_ENV) es
1335
+ <tt>:production</tt>:
1336
+
1337
+ configure :production do
1338
+ ...
1339
+ end
1340
+
1341
+ Ejecutar cuando el entorno es <tt>:production</tt> o <tt>:test</tt>:
1342
+
1343
+ configure :production, :test do
1344
+ ...
1345
+ end
1346
+
1347
+ Podés acceder a estas opciones utilizando el método <tt>settings</tt>:
1348
+
1349
+ configure do
1350
+ set :foo, 'bar'
1351
+ end
1352
+
1353
+ get '/' do
1354
+ settings.foo? # => true
1355
+ settings.foo # => 'bar'
1356
+ ...
1357
+ end
1358
+
1359
+ === Configurando la Protección de Ataques
1360
+
1361
+ Sinatra usa {Rack::Protection}[https://github.com/rkh/rack-protection#readme]
1362
+ para defender a tu aplicación de los ataques más comunes. Si por algún motivo,
1363
+ querés desactivar esta funcionalidad, podés hacerlo como se indica a
1364
+ continuación (tené en cuenta que tu aplicación va a quedar expuesta a un
1365
+ montón de vulnerabilidades bien conocidas):
1366
+
1367
+ disable :protection
1368
+
1369
+ También es posible desactivar una única capa de defensa:
1370
+
1371
+ set :protection, :except => :path_traversal
1372
+
1373
+ O varias:
1374
+
1375
+ set :protection, :except => [:path_traversal, :session_hijacking]
1376
+
1377
+ === Configuraciones Disponibles
1378
+
1379
+ [absolute_redirects] si está deshabilitada, Sinatra va a permitir
1380
+ redirecciones relativas, sin embargo, como consecuencia
1381
+ de esto, va a dejar de cumplir con el RFC 2616 (HTTP
1382
+ 1.1), que solamente permite redirecciones absolutas.
1383
+
1384
+ Activalo si tu apliación está corriendo atrás de un proxy
1385
+ inverso que no se ha configurado adecuadamente. Notá que
1386
+ el helper +url+ va a seguir produciendo URLs absolutas, a
1387
+ menos que le pasés +false+ como segundo parámetro.
1388
+
1389
+ Deshabilitada por defecto.
1390
+
1391
+ [add_charsets] tipos mime a los que el helper <tt>content_type</tt> les
1392
+ añade automáticamente el charset.
1393
+
1394
+ En general, no deberías asignar directamente esta opción,
1395
+ sino añadirle los charsets que quieras:
1396
+
1397
+ settings.add_charsets << "application/foobar"
1398
+
1399
+ [app_file] path del archivo principal de la aplicación, se utiliza
1400
+ para detectar la raíz del proyecto, el directorio de las
1401
+ vistas y el público, así como las plantillas inline.
1402
+
1403
+ [bind] dirección IP que utilizará el servidor integrado (por
1404
+ defecto: 0.0.0.0).
1405
+
1406
+ [default_encoding] encoding utilizado cuando el mismo se desconoce (por
1407
+ defecto <tt>"utf-8"</tt>).
1408
+
1409
+ [dump_errors] mostrar errores en el log.
1410
+
1411
+ [environment] entorno actual, por defecto toma el valor de
1412
+ <tt>ENV['RACK_ENV']</tt>, o <tt>"development"</tt> si no
1413
+ está disponible.
1414
+
1415
+ [logging] define si se utiliza el logger.
1416
+
1417
+ [lock] coloca un lock alrededor de cada petición, procesando
1418
+ solamente una por proceso.
1419
+
1420
+ Habilitá esta opción si tu aplicación no es thread-safe.
1421
+ Se encuentra deshabilitada por defecto.
1422
+
1423
+ [method_override] utiliza el parámetro <tt>_method</tt> para permtir
1424
+ formularios put/delete en navegadores que no los
1425
+ soportan.
1426
+
1427
+ [port] puerto en el que escuchará el servidor integrado.
1428
+
1429
+ [prefixed_redirects] define si inserta <tt>request.script_name</tt> en las
1430
+ redirecciones cuando no se proporciona un path absoluto.
1431
+ De esta manera, cuando está habilitada,
1432
+ <tt>redirect '/foo'</tt> se comporta de la misma manera
1433
+ que <tt>redirect to('/foo')</tt>. Se encuentra
1434
+ deshabilitada por defecto.
1435
+
1436
+ [protection] define si deben activarse las protecciones para los
1437
+ ataques web más comunes. Para más detalles mirá la
1438
+ sección sobre la configuración de protección de ataques
1439
+ más arriba.
1440
+
1441
+ [public_dir] alias para <tt>public_folder</tt>, que se encuentra a
1442
+ continuación.
1443
+
1444
+ [public_folder] path del directorio desde donde se sirven los archivos
1445
+ públicos. Solo se utiliza cuando se sirven archivos
1446
+ estáticos (ver la opción <tt>static</tt>). Si no
1447
+ está presente, se infiere del valor de la opción
1448
+ <tt>app_file</tt>.
1449
+
1450
+ [reload_templates] define si se recargan las plantillas entre peticiones.
1451
+
1452
+ Se encuentra activado en el entorno de desarrollo.
1453
+
1454
+ [root] path del directorio raíz del proyecto. Si no está
1455
+ presente, se infiere del valor de la opción
1456
+ <tt>app_file</tt>.
1457
+
1458
+ [raise_errors] elevar excepciones (detiene la aplicación). Se
1459
+ encuentra activada por defecto cuando el valor de
1460
+ <tt>environment</tt> es <tt>"test"</tt>. En caso
1461
+ contrario estará desactivada.
1462
+
1463
+ [run] cuando está habilitada, Sinatra se va a encargar de
1464
+ iniciar el servidor web, no la habilités cuando estés
1465
+ usando rackup o algún otro medio.
1466
+
1467
+ [running] indica si el servidor integrado está ejecutandose, ¡no
1468
+ cambiés esta configuración!.
1469
+
1470
+ [server] servidor, o lista de servidores, para usar como servidor
1471
+ integrado. Por defecto: ['thin', 'mongrel', 'webrick'],
1472
+ el orden establece la prioridad.
1473
+
1474
+ [sessions] habilita el soporte de sesiones basadas en cookies a
1475
+ través de <tt>Rack::Session::Cookie</tt>. Ver la
1476
+ sección 'Usando Sesiones' para más información.
1477
+
1478
+ [show_exceptions] muestra un stack trace en el navegador cuando ocurre una
1479
+ excepción. Se encuentra activada por defecto cuando el
1480
+ valor de <tt>environment</tt> es <tt>"development"</tt>.
1481
+ En caso contrario estará desactivada.
1482
+
1483
+ [static] define si Sinatra debe encargarse de servir archivos
1484
+ estáticos.
1485
+
1486
+ Deshabilitala cuando usés un servidor capaz de
1487
+ hacerlo por sí solo, porque mejorará el
1488
+ rendimiento. Se encuentra habilitada por
1489
+ defecto en el estilo clásico y desactivado en el
1490
+ el modular.
1491
+
1492
+ [static_cache_control] cuando Sinatra está sirviendo archivos estáticos, y
1493
+ está opción está habilitada, les va a agregar encabezados
1494
+ <tt>Cache-Control</tt> a las respuestas. Para esto
1495
+ utiliza el helper +cache_control+. Se encuentra
1496
+ deshabilitada por defecto. Notar que es necesario
1497
+ utilizar un array cuando se asignan múltiples valores:
1498
+ <tt>set :static_cache_control, [:public, :max_age => 300]</tt>.
1499
+
1500
+ [views] path del directorio de las vistas. Si no está presente,
1501
+ se infiere del valor de la opción <tt>app_file</tt>.
1502
+
1503
+ == Entornos
1504
+
1505
+ Existen tres entornos (+environments+) predefinidos: <tt>development</tt>,
1506
+ <tt>production</tt> y <tt>test</tt>. El entorno por defecto es
1507
+ <tt>development</tt> y tiene algunas particularidades:
1508
+
1509
+ * Se recargan las plantillas entre una petición y la siguiente, a diferencia
1510
+ de <tt>production</tt> y <tt>test</tt>, donde se cachean.
1511
+ * Se instalan manejadores de errores <tt>not_found</tt> y <tt>error</tt>
1512
+ especiales que muestran un stack trace en el navegador cuando son disparados.
1513
+
1514
+ Para utilizar alguno de los otros entornos puede asignarse el valor
1515
+ correspondiente a la variable de entorno +RACK_ENV+, o bien utilizar la opción
1516
+ <tt>-e</tt> al ejecutar la aplicación:
1517
+
1518
+ ruby mi_app.rb -e <ENTORNO>
1519
+
1520
+ Los métodos +development?+, +test?+ y +production?+ te permiten conocer el
1521
+ entorno actual.
1522
+
1523
+ == Manejo de Errores
1524
+
1525
+ Los manejadores de errores se ejecutan dentro del mismo contexto que las rutas
1526
+ y los filtros +before+, lo que significa que podés usar, por ejemplo,
1527
+ <tt>haml</tt>, <tt>erb</tt>, <tt>halt</tt>, etc.
1528
+
1529
+ === No encontrado <em>(Not Found)</em>
1530
+
1531
+ Cuando se eleva una excepción <tt>Sinatra::NotFound</tt>, o el código de
1532
+ estado de la respuesta es 404, el manejador <tt>not_found</tt> es invocado:
1533
+
1534
+ not_found do
1535
+ 'No existo'
1536
+ end
1537
+
1538
+ === Error
1539
+
1540
+ El manejador +error+ es invocado cada vez que una excepción es elevada
1541
+ desde un bloque de ruta o un filtro. El objeto de la excepción se puede
1542
+ obtener de la variable Rack <tt>sinatra.error</tt>:
1543
+
1544
+ error do
1545
+ 'Disculpá, ocurrió un error horrible - ' + env['sinatra.error'].name
1546
+ end
1547
+
1548
+ Errores personalizados:
1549
+
1550
+ error MiErrorPersonalizado do
1551
+ 'Lo que pasó fue...' + env['sinatra.error'].message
1552
+ end
1553
+
1554
+ Entonces, si pasa esto:
1555
+
1556
+ get '/' do
1557
+ raise MiErrorPersonalizado, 'algo malo'
1558
+ end
1559
+
1560
+ Obtenés esto:
1561
+
1562
+ Lo que pasó fue... algo malo
1563
+
1564
+ También, podés instalar un manejador de errores para un código de estado:
1565
+
1566
+ error 403 do
1567
+ 'Acceso prohibido'
1568
+ end
1569
+
1570
+ get '/secreto' do
1571
+ 403
1572
+ end
1573
+
1574
+ O un rango:
1575
+
1576
+ error 400..510 do
1577
+ 'Boom'
1578
+ end
1579
+
1580
+ Sinatra instala manejadores <tt>not_found</tt> y <tt>error</ttt> especiales
1581
+ cuando se ejecuta dentro del entorno de desarrollo "development".
1582
+
1583
+ == Rack Middleware
1584
+
1585
+ Sinatra corre sobre Rack[http://rack.rubyforge.org/], una interfaz minimalista
1586
+ que es un estándar para frameworks webs escritos en Ruby. Una de las
1587
+ capacidades más interesantes de Rack para los desarrolladores de aplicaciones
1588
+ es el soporte de "middleware" -- componentes que se ubican entre el servidor y
1589
+ tu aplicación, supervisando y/o manipulando la petición/respuesta HTTP para
1590
+ proporcionar varios tipos de funcionalidades comunes.
1591
+
1592
+ Sinatra hace muy sencillo construir tuberías de Rack middleware a través del
1593
+ método top-level +use+:
1594
+
1595
+ require 'sinatra'
1596
+ require 'mi_middleware_personalizado'
1597
+
1598
+ use Rack::Lint
1599
+ use MiMiddlewarePersonalizado
1600
+
1601
+ get '/hola' do
1602
+ 'Hola Mundo'
1603
+ end
1604
+
1605
+ Las semánticas de +use+ son idénticas a las definidas para el DSL
1606
+ Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] (más
1607
+ frecuentemente usado desde archivos rackup). Por ejemplo, el método +use+
1608
+ acepta argumentos múltiples/variables así como bloques:
1609
+
1610
+ use Rack::Auth::Basic do |nombre_de_usuario, password|
1611
+ nombre_de_usuario == 'admin' && password == 'secreto'
1612
+ end
1613
+
1614
+ Rack es distribuido con una variedad de middleware estándar para logging,
1615
+ debugging, enrutamiento URL, autenticación, y manejo de sesiones. Sinatra
1616
+ usa muchos de estos componentes automáticamente de acuerdo a su configuración
1617
+ para que típicamente no tengas que usarlas (con +use+) explícitamente.
1618
+
1619
+ Podés encontrar middleware útil en
1620
+ {rack}[https://github.com/rack/rack/tree/master/lib/rack],
1621
+ {rack-contrib}[https://github.com/rack/rack-contrib#readme],
1622
+ con {CodeRack}[http://coderack.org/] o en la
1623
+ {Rack wiki}[https://github.com/rack/rack/wiki/List-of-Middleware].
1624
+
1625
+ == Pruebas
1626
+
1627
+ Las pruebas para las aplicaciones Sinatra pueden ser escritas utilizando
1628
+ cualquier framework o librería de pruebas basada en Rack. Se recomienda usar
1629
+ {Rack::Test}[http://rdoc.info/github/brynary/rack-test/master/frames]:
1630
+
1631
+ require 'mi_app_sinatra'
1632
+ require 'test/unit'
1633
+ require 'rack/test'
1634
+
1635
+ class MiAppTest < Test::Unit::TestCase
1636
+ include Rack::Test::Methods
1637
+
1638
+ def app
1639
+ Sinatra::Application
1640
+ end
1641
+
1642
+ def test_mi_defecto
1643
+ get '/'
1644
+ assert_equal 'Hola Mundo!', last_response.body
1645
+ end
1646
+
1647
+ def test_con_parametros
1648
+ get '/saludar', :name => 'Franco'
1649
+ assert_equal 'Hola Frank!', last_response.body
1650
+ end
1651
+
1652
+ def test_con_entorno_rack
1653
+ get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
1654
+ assert_equal "Estás usando Songbird!", last_response.body
1655
+ end
1656
+ end
1657
+
1658
+ == Sinatra::Base - Middleware, Librerías, y Aplicaciones Modulares
1659
+
1660
+ Definir tu aplicación en el top-level funciona bien para micro-aplicaciones
1661
+ pero trae inconvenientes considerables a la hora de construir componentes
1662
+ reutilizables como Rack middleware, Rails metal, simple librerías con un
1663
+ componente de servidor, o incluso extensiones de Sinatra. El DSL de top-level
1664
+ asume una configuración apropiada para micro-aplicaciones (por ejemplo, un
1665
+ único archivo de aplicación, los directorios <tt>./public</tt> y
1666
+ <tt>./views</tt>, logging, página con detalles de excepción, etc.). Ahí es
1667
+ donde <tt>Sinatra::Base</tt> entra en el juego:
1668
+
1669
+ require 'sinatra/base'
1670
+
1671
+ class MiApp < Sinatra::Base
1672
+ set :sessions, true
1673
+ set :foo, 'bar'
1674
+
1675
+ get '/' do
1676
+ 'Hola Mundo!'
1677
+ end
1678
+ end
1679
+
1680
+ Las subclases de <tt>Sinatra::Base</tt> tienen disponibles exactamente los
1681
+ mismos métodos que los provistos por el DSL de top-level. La mayoría de las
1682
+ aplicaciones top-level se pueden convertir en componentes
1683
+ <tt>Sinatra::Base</tt> con dos modificaciones:
1684
+
1685
+ * Tu archivo debe requerir <tt>sinatra/base</tt> en lugar de +sinatra+; de otra
1686
+ manera, todos los métodos del DSL de sinatra son importados dentro del
1687
+ espacio de nombres principal.
1688
+ * Poné las rutas, manejadores de errores, filtros y opciones de tu aplicación
1689
+ en una subclase de <tt>Sinatra::Base</tt>.
1690
+
1691
+ <tt>Sinatra::Base</tt> es una pizarra en blanco. La mayoría de las opciones están
1692
+ desactivadas por defecto, incluyendo el servidor incorporado. Mirá
1693
+ {Opciones y Configuraciones}[http://sinatra.github.com/configuration.html]
1694
+ para detalles sobre las opciones disponibles y su comportamiento.
1695
+
1696
+ === Estilo Modular vs. Clásico
1697
+
1698
+ Contrariamente a la creencia popular, no hay nada de malo con el estilo clásico.
1699
+ Si se ajusta a tu aplicación, no es necesario que la cambies a una modular.
1700
+
1701
+ Las desventaja de usar el estilo clásico en lugar del modular consiste en que
1702
+ solamente podés tener una aplicación Sinatra por proceso Ruby. Si tenés
1703
+ planificado usar más, cambiá al estilo modular. Al mismo tiempo, tené en
1704
+ cuenta que no hay ninguna razón por la cuál no puedas mezclar los estilos
1705
+ clásico y modular.
1706
+
1707
+ A continuación se detallan las diferencias (sutiles) entre las configuraciones
1708
+ de ambos estilos:
1709
+
1710
+ Configuración Clásica Modular
1711
+
1712
+ app_file archivo que carga sinatra archivo con la subclase de Sinatra::Base
1713
+ run $0 == app_file false
1714
+ logging true false
1715
+ method_override true false
1716
+ inline_templates true false
1717
+ static true false
1718
+
1719
+ === Sirviendo una Aplicación Modular
1720
+
1721
+ Las dos opciones más comunes para iniciar una aplicación modular son, iniciarla
1722
+ activamente con <tt>run!</tt>:
1723
+
1724
+ # mi_app.rb
1725
+ require 'sinatra/base'
1726
+
1727
+ class MiApp < Sinatra::Base
1728
+ # ... código de la app ...
1729
+
1730
+ # iniciar el servidor si el archivo fue ejecutado directamente
1731
+ run! if app_file == $0
1732
+ end
1733
+
1734
+ Iniciar con:
1735
+
1736
+ ruby mi_app.rb
1737
+
1738
+ O, con un archivo <tt>config.ru</tt>, que permite usar cualquier handler Rack:
1739
+
1740
+ # config.ru
1741
+ require './mi_app'
1742
+ run MiApp
1743
+
1744
+ Después ejecutar:
1745
+
1746
+ rackup -p 4567
1747
+
1748
+ === Usando una Aplicación Clásica con un Archivo config.ru
1749
+
1750
+ Escribí el archivo de tu aplicación:
1751
+
1752
+ # app.rb
1753
+ require 'sinatra'
1754
+
1755
+ get '/' do
1756
+ 'Hola mundo!'
1757
+ end
1758
+
1759
+ Y el <tt>config.ru</tt> correspondiente:
1760
+
1761
+ require './app'
1762
+ run Sinatra::Application
1763
+
1764
+ === ¿Cuándo Usar config.ru?
1765
+
1766
+ Indicadores de que probablemente querés usar <tt>config.ru</tt>:
1767
+
1768
+ * Querés realizar el deploy con un hanlder Rack distinto (Passenger, Unicorn,
1769
+ Heroku, ...).
1770
+ * Querés usar más de una subclase de <tt>Sinatra::Base</tt>.
1771
+ * Querés usar Sinatra únicamente para middleware, pero no como un endpoint.
1772
+
1773
+ <b>No hay necesidad de utilizar un archivo <tt>config.ru</tt> exclusivamente
1774
+ porque tenés una aplicación modular, y no necesitás una aplicación modular para
1775
+ iniciarla con <tt>config.ru</tt>.</b>
1776
+
1777
+ === Utilizando Sinatra como Middleware
1778
+
1779
+ Sinatra no solo es capaz de usar otro Rack middleware, sino que a su vez,
1780
+ cualquier aplicación Sinatra puede ser agregada delante de un endpoint Rack
1781
+ como middleware. Este endpoint puede ser otra aplicación Sinatra, o cualquier
1782
+ aplicación basada en Rack (Rails/Ramaze/Camping/...):
1783
+
1784
+ require 'sinatra/base'
1785
+
1786
+ class PantallaDeLogin < Sinatra::Base
1787
+ enable :sessions
1788
+
1789
+ get('/login') { haml :login }
1790
+
1791
+ post('/login') do
1792
+ if params[:nombre] == 'admin' && params[:password] == 'admin'
1793
+ session['nombre_de_usuario'] = params[:nombre]
1794
+ else
1795
+ redirect '/login'
1796
+ end
1797
+ end
1798
+ end
1799
+
1800
+ class MiApp < Sinatra::Base
1801
+ # el middleware se ejecutará antes que los filtros
1802
+ use PantallaDeLogin
1803
+
1804
+ before do
1805
+ unless session['nombre_de_usuario']
1806
+ halt "Acceso denegado, por favor <a href='/login'>iniciá sesión</a>."
1807
+ end
1808
+ end
1809
+
1810
+ get('/') { "Hola #{session['nombre_de_usuario']}." }
1811
+ end
1812
+
1813
+ === Creación Dinámica de Aplicaciones
1814
+
1815
+ Puede que en algunas ocasiones quieras crear nuevas aplicaciones en
1816
+ tiempo de ejecución sin tener que asignarlas a una constante. Para
1817
+ esto tenés <tt>Sinatra.new</tt>:
1818
+
1819
+ require 'sinatra/base'
1820
+ mi_app = Sinatra.new { get('/') { "hola" } }
1821
+ mi_app.run!
1822
+
1823
+ Acepta como argumento opcional una aplicación desde la que se
1824
+ heredará:
1825
+
1826
+ # config.ru
1827
+ require 'sinatra/base'
1828
+
1829
+ controller = Sinatra.new do
1830
+ enable :logging
1831
+ helpers MisHelpers
1832
+ end
1833
+
1834
+ map('/a') do
1835
+ run Sinatra.new(controller) { get('/') { 'a' } }
1836
+ end
1837
+
1838
+ map('/b') do
1839
+ run Sinatra.new(controller) { get('/') { 'b' } }
1840
+ end
1841
+
1842
+ Construir aplicaciones de esta forma resulta especialmente útil para
1843
+ testear extensiones Sinatra o para usar Sinatra en tus librerías.
1844
+
1845
+ Por otro lado, hace extremadamente sencillo usar Sinatra como
1846
+ middleware:
1847
+
1848
+ require 'sinatra/base'
1849
+
1850
+ use Sinatra do
1851
+ get('/') { ... }
1852
+ end
1853
+
1854
+ run ProyectoRails::Application
1855
+
1856
+ == Ámbitos y Ligaduras
1857
+
1858
+ El ámbito en el que te encontrás determina que métodos y variables están
1859
+ disponibles.
1860
+
1861
+ === Ámbito de Aplicación/Clase
1862
+
1863
+ Cada aplicación Sinatra es una subclase de <tt>Sinatra::Base</tt>. Si estás
1864
+ usando el DSL de top-level (<tt>require 'sinatra'</tt>), entonces esta clase es
1865
+ <tt>Sinatra::Application</tt>, de otra manera es la subclase que creaste
1866
+ explícitamente. Al nivel de la clase tenés métodos como +get+ o +before+, pero
1867
+ no podés acceder a los objetos +request+ o +session+, ya que hay una única
1868
+ clase de la aplicación para todas las peticiones.
1869
+
1870
+ Las opciones creadas utilizando +set+ son métodos al nivel de la clase:
1871
+
1872
+ class MiApp < Sinatra::Base
1873
+ # Ey, estoy en el ámbito de la aplicación!
1874
+ set :foo, 42
1875
+ foo # => 42
1876
+
1877
+ get '/foo' do
1878
+ # Hey, ya no estoy en el ámbito de la aplicación!
1879
+ end
1880
+ end
1881
+
1882
+ Tenés la ligadura al ámbito de la aplicación dentro de:
1883
+
1884
+ * El cuerpo de la clase de tu aplicación
1885
+ * Métodos definidos por extensiones
1886
+ * El bloque pasado a +helpers+
1887
+ * Procs/bloques usados como el valor para +set+
1888
+
1889
+ Este ámbito puede alcanzarse de las siguientes maneras:
1890
+
1891
+ * A través del objeto pasado a los bloques de configuración (<tt>configure { |c| ...}</tt>)
1892
+ * Llamando a +settings+ desde dentro del ámbito de la petición
1893
+
1894
+ === Ámbito de Petición/Instancia
1895
+
1896
+ Para cada petición entrante, una nueva instancia de la clase de tu aplicación
1897
+ es creada y todos los bloques de rutas son ejecutados en ese ámbito. Desde este
1898
+ ámbito podés acceder a los objetos +request+ y +session+ o llamar a los métodos
1899
+ de renderización como +erb+ o +haml+. Podés acceder al ámbito de la aplicación
1900
+ desde el ámbito de la petición utilizando +settings+:
1901
+
1902
+ class MiApp < Sinatra::Base
1903
+ # Ey, estoy en el ámbito de la aplicación!
1904
+ get '/definir_ruta/:nombre' do
1905
+ # Ámbito de petición para '/definir_ruta/:nombre'
1906
+ @valor = 42
1907
+
1908
+ settings.get("/#{params[:nombre]}") do
1909
+ # Ámbito de petición para "/#{params[:nombre]}"
1910
+ @valor # => nil (no es la misma petición)
1911
+ end
1912
+
1913
+ "Ruta definida!"
1914
+ end
1915
+ end
1916
+
1917
+ Tenés la ligadura al ámbito de la petición dentro de:
1918
+
1919
+ * bloques pasados a get/head/post/put/delete/options
1920
+ * filtros before/after
1921
+ * métodos ayudantes
1922
+ * plantillas/vistas
1923
+
1924
+ === Ámbito de Delegación
1925
+
1926
+ El ámbito de delegación solo reenvía métodos al ámbito de clase. De cualquier
1927
+ manera, no se comporta 100% como el ámbito de clase porque no tenés la ligadura
1928
+ de la clase: únicamente métodos marcados explícitamente para delegación están
1929
+ disponibles y no compartís variables/estado con el ámbito de clase (léase:
1930
+ tenés un +self+ diferente). Podés agregar delegaciones de método llamando a
1931
+ <tt>Sinatra::Delegator.delegate :nombre_del_metodo</tt>.
1932
+
1933
+ Tenés la ligadura al ámbito de delegación dentro de:
1934
+
1935
+ * La ligadura del top-level, si hiciste <tt>require "sinatra"</tt>
1936
+ * Un objeto extendido con el mixin <tt>Sinatra::Delegator</tt>
1937
+
1938
+ Pegale una mirada al código: acá está el
1939
+ {Sinatra::Delegator mixin}[https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633]
1940
+ que {extiende el objeto main}[https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30].
1941
+
1942
+ == Línea de Comandos
1943
+
1944
+ Las aplicaciones Sinatra pueden ser ejecutadas directamente:
1945
+
1946
+ ruby miapp.rb [-h] [-x] [-e ENTORNO] [-p PUERTO] [-o HOST] [-s MANEJADOR]
1947
+
1948
+ Las opciones son:
1949
+
1950
+ -h # ayuda
1951
+ -p # asigna el puerto (4567 es usado por defecto)
1952
+ -o # asigna el host (0.0.0.0 es usado por defecto)
1953
+ -e # asigna el entorno (development es usado por defecto)
1954
+ -s # especifica el servidor/manejador rack (thin es usado por defecto)
1955
+ -x # activa el mutex lock (está desactivado por defecto)
1956
+
1957
+ == Versiones de Ruby Soportadas
1958
+
1959
+ Las siguientes versiones de Ruby son soportadas oficialmente:
1960
+
1961
+ [ Ruby 1.8.7 ]
1962
+ 1.8.7 es soportado completamente. Sin embargo, si no hay nada que te lo
1963
+ prohíba, te recomendamos que usés 1.9.2 o cambies a JRuby o Rubinius. No se
1964
+ dejará de dar soporte a 1.8.7 hasta Sinatra 2.0 y Ruby 2.0, aunque si se
1965
+ libera la versión 1.8.8 de Ruby las cosas podrían llegar a cambiar. Sin
1966
+ embargo, que eso ocurra es muy poco probable, e incluso el caso de que lo
1967
+ haga, puede que se siga dando soporte a 1.8.7. <b>Hemos dejado de soportar
1968
+ Ruby 1.8.6.</b> Si querés ejecutar Sinatra sobre 1.8.6, podés utilizar la
1969
+ versión 1.2, pero tené en cuenta que una vez que Sinatra 1.4.0 sea liberado,
1970
+ ya no se corregirán errores por más que se reciban reportes de los mismos.
1971
+
1972
+ [ Ruby 1.9.2 ]
1973
+ 1.9.2 es soportado y recomendado. No usés 1.9.2p0, porque se producen fallos
1974
+ de segmentación cuando se ejecuta Sinatra. El soporte se mantendrá al menos
1975
+ hasta que se libere la versión 1.9.4/2.0 de Ruby. El soporte para la última
1976
+ versión de la serie 1.9 se mantendrá mientras lo haga el core team de Ruby.
1977
+
1978
+ [ Ruby 1.9.3 ]
1979
+ 1.9.3 es soportado y recomendado. Tené en cuenta que el cambio a 1.9.3 desde
1980
+ una versión anterior va a invalidar todas las sesiones.
1981
+
1982
+ [ Rubinius ]
1983
+ Rubinius es soportado oficialmente (Rubinius >= 1.2.4). Todo funciona
1984
+ correctamente, incluyendo los lenguajes de plantillas. La próxima versión,
1985
+ 2.0, también es soportada, incluyendo el modo 1.9.
1986
+
1987
+ [ JRuby ]
1988
+ JRuby es soportado oficialmente (JRuby >= 1.6.7). No se conocen problemas
1989
+ con librerías de plantillas de terceras partes. Sin embargo, si elegís usar
1990
+ JRuby, deberías examinar sus Rack handlers porque el servidor web Thin no es
1991
+ soportado completamente. El soporte de JRuby para extensiones C se encuentra
1992
+ en una etapa experimental, sin embargo, de momento solamente RDiscount,
1993
+ Redcarpet, RedCloth y Yajl, así como Thin y Mongrel se ven afectadas.
1994
+
1995
+ Siempre le prestamos atención a las nuevas versiones de Ruby.
1996
+
1997
+ Las siguientes implementaciones de Ruby no se encuentran soportadas
1998
+ oficialmente. De cualquier manera, pueden ejecutar Sinatra:
1999
+
2000
+ * Versiones anteriores de JRuby y Rubinius
2001
+ * Ruby Enterprise Edition
2002
+ * MacRuby, Maglev e IronRuby
2003
+ * Ruby 1.9.0 y 1.9.1 (pero no te recomendamos que los usés)
2004
+
2005
+ No estar soportada oficialmente, significa que si las cosas solamente se rompen
2006
+ ahí y no en una plataforma soportada, asumimos que no es nuestro problema sino
2007
+ el suyo.
2008
+
2009
+ Nuestro servidor CI también se ejecuta sobre ruby-head (que será la próxima
2010
+ versión 2.0.0) y la rama 1.9.4. Como están en movimiento constante, no podemos
2011
+ garantizar nada. De todas formas, podés contar con que tanto 1.9.4-p0 como
2012
+ 2.0.0-p0 sea soportadas.
2013
+
2014
+ Sinatra debería funcionar en cualquier sistema operativo soportado por la
2015
+ implementación de Ruby elegida.
2016
+
2017
+ En este momento, no vas a poder ejecutar Sinatra en Cardinal, SmallRuby,
2018
+ BlueRuby o cualquier versión de Ruby anterior a 1.8.7.
2019
+
2020
+ == A la Vanguardia
2021
+
2022
+ Si querés usar el código de Sinatra más reciente, sentite libre de ejecutar
2023
+ tu aplicación sobre la rama master, en general es bastante estable.
2024
+
2025
+ También liberamos prereleases de vez en cuando, así, podés hacer
2026
+
2027
+ gem install sinatra --pre
2028
+
2029
+ Para obtener algunas de las últimas características.
2030
+
2031
+ === Con Bundler
2032
+
2033
+ Esta es la manera recomendada para ejecutar tu aplicación sobre la última
2034
+ versión de Sinatra usando {Bundler}[http://gembundler.com/].
2035
+
2036
+ Primero, instalá bundler si no lo hiciste todavía:
2037
+
2038
+ gem install bundler
2039
+
2040
+ Después, en el directorio de tu proyecto, creá un archivo +Gemfile+:
2041
+
2042
+ source :rubygems
2043
+ gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git"
2044
+
2045
+ # otras dependencias
2046
+ gem 'haml' # por ejemplo, si usás haml
2047
+ gem 'activerecord', '~> 3.0' # quizás también necesités ActiveRecord 3.x
2048
+
2049
+ Tené en cuenta que tenés que listar todas las dependencias directas de tu
2050
+ aplicación. No es necesario listar las dependencias de Sinatra (Rack y Tilt)
2051
+ porque Bundler las agrega directamente.
2052
+
2053
+ Ahora podés arrancar tu aplicación así:
2054
+
2055
+ bundle exec ruby miapp.rb
2056
+
2057
+ === Con Git
2058
+
2059
+ Cloná el repositorio localmente y ejecutá tu aplicación, asegurándote que el
2060
+ directorio <tt>sinatra/lib</tt> esté en el <tt>$LOAD_PATH</tt>:
2061
+
2062
+ cd miapp
2063
+ git clone git://github.com/sinatra/sinatra.git
2064
+ ruby -Isinatra/lib miapp.rb
2065
+
2066
+ Para actualizar el código fuente de Sinatra en el futuro:
2067
+
2068
+ cd miapp/sinatra
2069
+ git pull
2070
+
2071
+ === Instalación Global
2072
+
2073
+ Podés construir la gem vos mismo:
2074
+
2075
+ git clone git://github.com/sinatra/sinatra.git
2076
+ cd sinatra
2077
+ rake sinatra.gemspec
2078
+ rake install
2079
+
2080
+ Si instalás tus gems como root, el último paso debería ser
2081
+
2082
+ sudo rake install
2083
+
2084
+ == Versionado
2085
+
2086
+ Sinatra utiliza el {Versionado Semántico}[http://semver.org/],
2087
+ siguiendo las especificaciones SemVer y SemVerTag.
2088
+
2089
+ == Lecturas Recomendadas
2090
+
2091
+ * {Sito web del proyecto}[http://www.sinatrarb.com/] - Documentación
2092
+ adicional, noticias, y enlaces a otros recursos.
2093
+ * {Contribuyendo}[http://www.sinatrarb.com/contributing] - ¿Encontraste un
2094
+ error?. ¿Necesitás ayuda?. ¿Tenés un parche?.
2095
+ * {Seguimiento de problemas}[http://github.com/sinatra/sinatra/issues]
2096
+ * {Twitter}[http://twitter.com/sinatra]
2097
+ * {Lista de Correo}[http://groups.google.com/group/sinatrarb/topics]
2098
+ * {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] en http://freenode.net
2099
+ * {Sinatra Book}[http://sinatra-book.gittr.com] Tutorial (en inglés).
2100
+ * {Sinatra Recipes}[http://recipes.sinatrarb.com/] Recetas contribuidas
2101
+ por la comunidad (en inglés).
2102
+ * Documentación de la API para la
2103
+ {última versión liberada}[http://rubydoc.info/gems/sinatra] o para la
2104
+ {rama de desarrollo actual}[http://rubydoc.info/github/sinatra/sinatra]
2105
+ en http://rubydoc.info/
2106
+ * {Servidor de CI}[http://travis-ci.org/sinatra/sinatra]