devcenter 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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]