sinatra 1.1.4 → 1.2.0

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.
data/README.es.rdoc CHANGED
@@ -18,6 +18,9 @@ Instalá la gem y ejecutá la aplicación con:
18
18
 
19
19
  Podés verla en: http://localhost:4567
20
20
 
21
+ Es recomendable además ejecutar <tt>gem install thin</tt>, ya que Sinatra lo va
22
+ a utilizar cuando esté disponible.
23
+
21
24
  == Rutas
22
25
 
23
26
  En Sinatra, una ruta está compuesta por un método HTTP y un patrón de una URL.
@@ -39,6 +42,11 @@ Cada ruta se asocia con un bloque:
39
42
  .. aniquilar algo ..
40
43
  end
41
44
 
45
+ options '/' do
46
+ .. informar algo ..
47
+ end
48
+
49
+
42
50
  Las rutas son comparadas en el orden en el que son definidas. La primer ruta
43
51
  que coincide con la petición es invocada.
44
52
 
@@ -121,7 +129,7 @@ Podés definir tus propias condiciones fácilmente:
121
129
  "Lo siento, perdiste."
122
130
  end
123
131
 
124
- === Valores de retorno
132
+ === Valores de Retorno
125
133
 
126
134
  El valor de retorno de un bloque de ruta determina al menos el cuerpo de la
127
135
  respuesta que se le pasa al cliente HTTP o al siguiente middleware en la pila
@@ -146,6 +154,47 @@ De esa manera podemos, por ejemplo, implementar fácilmente un streaming:
146
154
 
147
155
  get('/') { Stream.new }
148
156
 
157
+ === Comparadores de Rutas Personalizados
158
+
159
+ Como se mostró anteriormente, Sinatra permite utilizar Strings y expresiones
160
+ regulares para definir las rutas. Sin embargo, la cosa no termina ahí. Podés
161
+ definir tus propios comparadores muy fácilmente:
162
+
163
+ class PattronCualquieraMenos
164
+ Match = Struct.new(:captures)
165
+
166
+ def initialize(excepto)
167
+ @excepto = excepto
168
+ @caputras = Match.new([])
169
+ end
170
+
171
+ def match(str)
172
+ @caputras unless @excepto === str
173
+ end
174
+ end
175
+
176
+ def cualquiera_menos(patron)
177
+ PatronCualquieraMenos.new(patron)
178
+ end
179
+
180
+ get cualquiera_menos("/index") do
181
+ # ...
182
+ end
183
+
184
+ Tené en cuenta que el ejemplo anterior es un poco rebuscado. Un resultado
185
+ similar puede conseguirse más sencillamente:
186
+
187
+ get // do
188
+ pass if request.path_info == "/index"
189
+ # ...
190
+ end
191
+
192
+ O, usando un lookahead negativo:
193
+
194
+ get %r{^(?!/index$)} do
195
+ # ...
196
+ end
197
+
149
198
  == Archivos Estáticos
150
199
 
151
200
  Los archivos estáticos son servidos desde el directorio público
@@ -175,7 +224,7 @@ les pase como argumento.
175
224
 
176
225
  La gem/librería <tt>haml</tt> es necesaria para para renderizar plantillas HAML:
177
226
 
178
- ## Vas a necesitar requerir haml en tu app
227
+ # Vas a necesitar requerir haml en tu app
179
228
  require 'haml'
180
229
 
181
230
  get '/' do
@@ -197,7 +246,7 @@ y reemplazadas individualmente.
197
246
 
198
247
  === Plantillas Erb
199
248
 
200
- ## Vas a necesitar requerir erb en tu app
249
+ # Vas a necesitar requerir erb en tu app
201
250
  require 'erb'
202
251
 
203
252
  get '/' do
@@ -210,7 +259,7 @@ Renderiza <tt>./views/index.erb</tt>
210
259
 
211
260
  La gem/librería <tt>erubis</tt> es necesaria para renderizar plantillas Erubis:
212
261
 
213
- ## Vas a necesitar requerir erubis en tu app
262
+ # Vas a necesitar requerir erubis en tu app
214
263
  require 'erubis'
215
264
 
216
265
  get '/' do
@@ -235,7 +284,7 @@ Renderiza <tt>./views/index.erb</tt> con Erubis.
235
284
  La gem/librería <tt>builder</tt> es necesaria para renderizar plantillas
236
285
  builder:
237
286
 
238
- ## Vas a necesitar requerir builder en tu app
287
+ # Vas a necesitar requerir builder en tu app
239
288
  require 'builder'
240
289
 
241
290
  get '/' do
@@ -249,7 +298,7 @@ Renderiza <tt>./views/index.builder</tt>.
249
298
  La gem/librería <tt>nokogiri</tt> es necesaria para renderizar plantillas
250
299
  nokogiri:
251
300
 
252
- ## Vas a necesitar requerir nokogiri en tu app
301
+ # Vas a necesitar requerir nokogiri en tu app
253
302
  require 'nokogiri'
254
303
 
255
304
  get '/' do
@@ -263,7 +312,7 @@ Renderiza <tt>./views/index.nokogiri</tt>.
263
312
  La gem/librería <tt>haml</tt> o <tt>sass</tt> es necesaria para renderizar
264
313
  plantillas Sass:
265
314
 
266
- ## Vas a necesitar requerir haml o sass en tu app
315
+ # Vas a necesitar requerir haml o sass en tu app
267
316
  require 'sass'
268
317
 
269
318
  get '/stylesheet.css' do
@@ -288,7 +337,7 @@ y reemplazadas individualmente.
288
337
  La gem/librería <tt>haml</tt> o <tt>sass</tt>es necesaria para renderizar
289
338
  plantillas Scss:
290
339
 
291
- ## Vas a necesitar requerir haml o sass en tu app
340
+ # Vas a necesitar requerir haml o sass en tu app
292
341
  require 'sass'
293
342
 
294
343
  get '/stylesheet.css' do
@@ -312,7 +361,7 @@ y reemplazadas individualmente.
312
361
 
313
362
  La gem/librería <tt>less</tt> es necesaria para renderizar plantillas Less:
314
363
 
315
- ## Vas a necesitar requerir less en tu app
364
+ # Vas a necesitar requerir less en tu app
316
365
  require 'less'
317
366
 
318
367
  get '/stylesheet.css' do
@@ -325,7 +374,7 @@ Renderiza <tt>./views/stylesheet.less</tt>.
325
374
 
326
375
  La gem/librería <tt>liquid</tt> es necesaria para renderizar plantillas Liquid:
327
376
 
328
- ## Vas a necesitar requerir liquid en tu app
377
+ # Vas a necesitar requerir liquid en tu app
329
378
  require 'liquid'
330
379
 
331
380
  get '/' do
@@ -344,7 +393,7 @@ plantilla Liquid, casi siempre vas a querer pasarle locales:
344
393
  La gem/librería <tt>rdiscount</tt> es necesaria para renderizar plantillas
345
394
  Markdown:
346
395
 
347
- ## Vas a necesitar requerir rdiscount en tu app
396
+ # Vas a necesitar requerir rdiscount en tu app
348
397
  require "rdiscount"
349
398
 
350
399
  get '/' do
@@ -365,6 +414,27 @@ plantillas:
365
414
  %h1 Hola Desde Haml!
366
415
  %p= markdown(:saludos)
367
416
 
417
+ Como no podés utilizar Ruby desde Markdown, no podés usar layouts escritos en
418
+ Markdown. De todos modos, es posible usar un motor de renderizado para el
419
+ layout distinto al de la plantilla pasando la opción <tt>:layout_engine</tt>:
420
+
421
+ get '/' do
422
+ markdown :index, :layout_engine => :erb
423
+ end
424
+
425
+ Renderiza <tt>./views/index.md</tt> con el layout <tt>./views/layout.erb</tt>.
426
+
427
+ Recordá que podés asignar las opciones de renderizado globalmente:
428
+
429
+ set :markdown, :layout_engine => :haml, :layout => :post
430
+
431
+ get '/' do
432
+ markdown :index
433
+ end
434
+
435
+ Renderiza <tt>./views/index.md</tt> (o cualquier otra plantilla Markdown) con
436
+ el layout <tt>./views/post.haml</tt>.
437
+
368
438
  También es posible parsear Markdown con BlueCloth en lugar de RDiscount:
369
439
 
370
440
  require 'bluecloth'
@@ -384,7 +454,7 @@ Renderiza <tt>./views/index.md</tt> con BlueCloth.
384
454
  La gem/librería <tt>RedCloth</tt> es necesaria para renderizar plantillas
385
455
  Textile:
386
456
 
387
- ## Vas a necesitar requerir redcloth en tu app
457
+ # Vas a necesitar requerir redcloth en tu app
388
458
  require "redcloth"
389
459
 
390
460
  get '/' do
@@ -404,12 +474,34 @@ plantillas:
404
474
  %h1 Hola Desde Haml!
405
475
  %p= textile(:saludos)
406
476
 
477
+ Como no podés utilizar Ruby desde Textile, no podés usar layouts escritos en
478
+ Textile. De todos modos, es posible usar un motor de renderizado para el
479
+ layout distinto al de la plantilla pasando la opción <tt>:layout_engine</tt>:
480
+
481
+ get '/' do
482
+ textile :index, :layout_engine => :erb
483
+ end
484
+
485
+ Renderiza <tt>./views/index.textile</tt> con el layout
486
+ <tt>./views/layout.erb</tt>.
487
+
488
+ Recordá que podés asignar las opciones de renderizado globalmente:
489
+
490
+ set :textile, :layout_engine => :haml, :layout => :post
491
+
492
+ get '/' do
493
+ textile :index
494
+ end
495
+
496
+ Renderiza <tt>./views/index.textile</tt> (o cualquier otra plantilla Textile)
497
+ con el layout <tt>./views/post.haml</tt>.
498
+
407
499
  === Plantillas RDoc
408
500
 
409
501
  La gem/librería <tt>rdoc</tt> es necesaria para renderizar plantillas RDoc:
410
502
 
411
- ## Vas a necesitar requerir rdoc en tu app
412
- require "rdoc"
503
+ # Vas a necesitar requerir rdoc/markup/to_html en tu app
504
+ require "rdoc/markup/to_html"
413
505
 
414
506
  get '/' do
415
507
  rdoc :index
@@ -428,11 +520,32 @@ plantillas:
428
520
  %h1 Hola Desde Haml!
429
521
  %p= rdoc(:saludos)
430
522
 
523
+ Como no podés utilizar Ruby desde RDoc, no podés usar layouts escritos en RDoc.
524
+ De todos modos, es posible usar un motor de renderizado para el layout distinto
525
+ al de la plantilla pasando la opción <tt>:layout_engine</tt>:
526
+
527
+ get '/' do
528
+ rdoc :index, :layout_engine => :erb
529
+ end
530
+
531
+ Renderiza <tt>./views/index.rdoc</tt> con el layout <tt>./views/layout.erb</tt>.
532
+
533
+ Recordá que podés asignar las opciones de renderizado globalmente:
534
+
535
+ set :rdoc, :layout_engine => :haml, :layout => :post
536
+
537
+ get '/' do
538
+ rdoc :index
539
+ end
540
+
541
+ Renderiza <tt>./views/index.rdoc</tt> (o cualquier otra plantilla RDoc) con el
542
+ layout <tt>./views/post.haml</tt>.
543
+
431
544
  === Plantillas Radius
432
545
 
433
546
  La gem/librería <tt>radius</tt> es necesaria para renderizar plantillas Radius:
434
547
 
435
- ## Vas a necesitar requerir radius en tu app
548
+ # Vas a necesitar requerir radius en tu app
436
549
  require 'radius'
437
550
 
438
551
  get '/' do
@@ -451,7 +564,7 @@ plantilla Radius, casi siempre vas a querer pasarle locales:
451
564
  La gem/librería <tt>markaby</tt> es necesaria para renderizar plantillas
452
565
  Markaby:
453
566
 
454
- ## Vas a necesitar requerir markaby en tu app
567
+ # Vas a necesitar requerir markaby en tu app
455
568
  require 'markaby'
456
569
 
457
570
  get '/' do
@@ -460,6 +573,26 @@ Markaby:
460
573
 
461
574
  Renderiza <tt>./views/index.mab</tt>.
462
575
 
576
+ También podés usar Markaby inline:
577
+
578
+ get '/' do
579
+ markaby { h1 "Bienvenido!" }
580
+ end
581
+
582
+
583
+ === Plantillas Slim
584
+
585
+ La gem/librería <tt>slim</tt> es necesaria para renderizar plantillas Slim:
586
+
587
+ # Vas a necesitar requerir slim en tu app
588
+ require 'slim'
589
+
590
+ get '/' do
591
+ slim :index
592
+ end
593
+
594
+ Renderiza <tt>./views/index.slim</tt>.
595
+
463
596
  === Plantillas CoffeeScript
464
597
 
465
598
  La gem/librería <tt>coffee-script</tt> y al menos <b>una</b> de las siguientes
@@ -471,7 +604,7 @@ opciones para ejecutar JavaScript:
471
604
 
472
605
  son necesarios para renderizar plantillas CoffeeScript:
473
606
 
474
- ## Vas a necesitar requerir coffee-script en tu app
607
+ # Vas a necesitar requerir coffee-script en tu app
475
608
  require 'coffee-script'
476
609
 
477
610
  get '/application.js' do
@@ -585,27 +718,12 @@ Primero, registrá tu motor con Tilt, y después, creá tu método de renderizad
585
718
  Renderiza <tt>./views/index.mypg</tt>. Mirá https://github.com/rtomayko/tilt
586
719
  para aprender más de Tilt.
587
720
 
588
- == Ayudantes
589
-
590
- Usá el método top-level <tt>helpers</tt> para definir métodos ayudantes que
591
- pueden ser utilizados dentro de los manejadores de rutas y las plantillas:
592
-
593
- helpers do
594
- def bar(nombre)
595
- "#{nombre}bar"
596
- end
597
- end
598
-
599
- get '/:nombre' do
600
- bar(params[:nombre])
601
- end
602
-
603
721
  == Filtros
604
722
 
605
- Los filtros before son evaluados antes de cada petición dentro del mismo
606
- contexto que las rutas serán evaluadas y pueden modificar la petición y la
607
- respuesta. Las variables de instancia asignadas en los filtros son accesibles
608
- por las rutas y las plantillas:
723
+ Los filtros +before+ son evaluados antes de cada petición dentro del mismo
724
+ contexto que las rutas. Pueden modificar la petición y la respuesta. Las
725
+ variables de instancia asignadas en los filtros son accesibles por las rutas y
726
+ las plantillas:
609
727
 
610
728
  before do
611
729
  @nota = 'Hey!'
@@ -617,15 +735,19 @@ por las rutas y las plantillas:
617
735
  params[:splat] #=> 'bar/baz'
618
736
  end
619
737
 
620
- Los filtros after son evaluados después de cada petición dentro del mismo
621
- contexto y también pueden modificar la petición y la respuesta. Las variables
622
- de instancia asignadas en los filtros before y rutas son accesibles por los
623
- filtros after:
738
+ Los filtros +after+ son evaluados después de cada petición dentro del mismo
739
+ contexto y también pueden modificar la petición y la respuesta. Las variables
740
+ de instancia asignadas en los filtros +before+ y en las rutas son accesibles por
741
+ los filtros +after+:
624
742
 
625
743
  after do
626
744
  puts response.status
627
745
  end
628
746
 
747
+ Nota: A menos que usés el método +body+ en lugar de simplemente devolver un
748
+ string desde una ruta, el cuerpo de la respuesta no va a estar disponible en
749
+ un filtro after, debido a que todavía no se ha generado.
750
+
629
751
  Los filtros aceptan un patrón opcional, que cuando está presente causa que los
630
752
  mismos sean evaluados únicamente si el path de la petición coincide con ese
631
753
  patrón:
@@ -638,7 +760,63 @@ patrón:
638
760
  session[:ultimo_slug] = slug
639
761
  end
640
762
 
641
- == Interrupción
763
+ Al igual que las rutas, los filtros también pueden aceptar condiciones:
764
+
765
+ before :agent => /Songbird/ do
766
+ # ...
767
+ end
768
+
769
+ after '/blog/*', :host_name => 'ejemplo.com' do
770
+ # ...
771
+ end
772
+
773
+ == Ayudantes
774
+
775
+ Usá el método top-level <tt>helpers</tt> para definir métodos ayudantes que
776
+ pueden ser utilizados dentro de los manejadores de rutas y las plantillas:
777
+
778
+ helpers do
779
+ def bar(nombre)
780
+ "#{nombre}bar"
781
+ end
782
+ end
783
+
784
+ get '/:nombre' do
785
+ bar(params[:nombre])
786
+ end
787
+
788
+ === Usando Sesiones
789
+
790
+ Una sesión es usada para mantener el estado a través de distintas peticiones.
791
+ Cuando están activadas, tenés un hash de sesión para cada sesión de usuario:
792
+
793
+ enable :sessions
794
+
795
+ get '/' do
796
+ "valor = " << session[:valor].inspect
797
+ end
798
+
799
+ get '/:valor' do
800
+ session[:valor] = params[:valor]
801
+ end
802
+
803
+ Tené en cuenta que <tt>enable :sessions</tt> guarda todos los datos en una
804
+ cookie, lo que no es siempre deseable (guardar muchos datos va a incrementar
805
+ tu tráfico, por citar un ejemplo). Podés usar cualquier middleware Rack para
806
+ manejar sesiones, de la misma manera que usarías cualquier otro middleware,
807
+ pero con la salvedad de que *no* tenés que llamar a <tt>enable :sessions</tt>:
808
+
809
+ use Rack::Session::Pool, :expire_after => 2592000
810
+
811
+ get '/' do
812
+ "valor = " << session[:valor].inspect
813
+ end
814
+
815
+ get '/:valor' do
816
+ session[:valor] = params[:valor]
817
+ end
818
+
819
+ === Interrupción
642
820
 
643
821
  Para detener inmediatamente una petición dentro de un filtro o una ruta usá:
644
822
 
@@ -660,7 +838,11 @@ Con cabeceras:
660
838
 
661
839
  halt 402, { 'Content-Type' => 'text/plain' }, 'venganza'
662
840
 
663
- == Paso
841
+ Obviamente, es posible utilizar +halt+ con una plantilla:
842
+
843
+ halt erb(:error)
844
+
845
+ === Paso
664
846
 
665
847
  Una ruta puede pasarle el procesamiento a la siguiente ruta que coincida con
666
848
  la petición usando <tt>pass</tt>:
@@ -677,11 +859,230 @@ la petición usando <tt>pass</tt>:
677
859
  Se sale inmediatamente del bloque de la ruta y se le pasa el control a la
678
860
  siguiente ruta que coincida. Si no coincide ninguna ruta, se devuelve un 404.
679
861
 
680
- == Accediendo al objeto de la petición
862
+ === Ejecutando Otra Ruta
863
+
864
+ Cuando querés obtener el resultado de la llamada a una ruta, +pass+ no te va a
865
+ servir. Para lograr esto, podés usar +call+:
866
+
867
+ get '/foo' do
868
+ status, headers, body = call request.env.merge("PATH_INFO" => '/bar')
869
+ [status, body.upcase]
870
+ end
871
+
872
+ get '/bar' do
873
+ "bar"
874
+ end
875
+
876
+ Notá que en el ejemplo anterior, es conveniente mover <tt>"bar"</tt> a un
877
+ helper, y llamarlo desde <tt>/foo</tt> y <tt>/bar</tt>. Así, vas a simplificar
878
+ las pruebas y a mejorar el rendimiento.
879
+
880
+ Si querés que la petición se envíe a la misma instancia de la aplicación en
881
+ lugar de a otra, usá <tt>call!</tt> en lugar de <tt>call</tt>.
882
+
883
+ En la especificación de Rack podés encontrar más información sobre
884
+ <tt>call</tt>.
885
+
886
+ === Asignando el Código de Estado, los Encabezados y el Cuerpo de una Respuesta
887
+
888
+ Es posible, y se recomienda, asignar el código de estado y el cuerpo de una
889
+ respuesta con el valor de retorno de una ruta. De cualquier manera, en varios
890
+ escenarios, puede que sea conveniente asignar el cuerpo en un punto arbitrario
891
+ del flujo de ejecución con el método +body+. A partir de ahí, podés usar ese
892
+ mismo método para acceder al cuerpo de la respuesta:
893
+
894
+ get '/foo' do
895
+ body "bar"
896
+ end
897
+
898
+ after do
899
+ puts body
900
+ end
901
+
902
+ También es posible pasarle un bloque a +body+, que será ejecutado por el Rack
903
+ handler (podés usar esto para implementar streaming, mirá "Valores de retorno").
904
+
905
+ De manera similar, también podés asignar el código de estado y encabezados:
906
+
907
+ get '/foo' do
908
+ status 418
909
+ headers \
910
+ "Allow" => "BREW, POST, GET, PROPFIND, WHEN"
911
+ "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
912
+ body "I'm a tea pot!"
913
+ end
914
+
915
+ También, al igual que +body+, tanto +status+ como +headers+ pueden utilizarse
916
+ para obtener sus valores cuando no se les pasa argumentos.
917
+
918
+ === Tipos Mime
919
+
920
+ Cuando usás <tt>send_file</tt> o archivos estáticos tal vez tengas tipos mime
921
+ que Sinatra no entiende. Usá +mime_type+ para registrarlos a través de la
922
+ extensión de archivo:
923
+
924
+ mime_type :foo, 'text/foo'
925
+
926
+ También lo podés usar con el ayudante +content_type+:
927
+
928
+ get '/' do
929
+ content_type :foo
930
+ "foo foo foo"
931
+ end
932
+
933
+ === Generando URLs
934
+
935
+ Para generar URLs deberías usar el método +url+. Por ejemplo, en Haml:
936
+
937
+ %a{:href => url('/foo')} foo
938
+
939
+ Tiene en cuenta proxies inversos y encaminadores de Rack, si están presentes.
940
+
941
+ Este método también puede invocarse mediante su alias +to+ (mirá un ejemplo
942
+ a continuación).
943
+
944
+ === Redirección del Navegador
945
+
946
+ Podés redireccionar al navegador con el método +redirect+:
947
+
948
+ get '/foo' do
949
+ redirect to('/bar')
950
+ end
951
+
952
+ Cualquier parámetro adicional se utiliza de la misma manera que los argumentos
953
+ pasados a +halt+:
954
+
955
+ redirect to('/bar'), 303
956
+ redirect 'http://google.com', 'te confundiste de lugar, compañero'
957
+
958
+ También podés redireccionar fácilmente de vuelta hacia la página desde donde
959
+ vino el usuario con +redirect back+:
960
+
961
+ get '/foo' do
962
+ "<a href='/bar'>hacer algo</a>"
963
+ end
964
+
965
+ get '/bar' do
966
+ hacer_algo
967
+ redirect back
968
+ end
969
+
970
+ Para pasar argumentos con una redirección, podés agregarlos a la cadena de
971
+ búsqueda:
972
+
973
+ redirect to('/bar?suma=42')
974
+
975
+ O usar una sesión:
976
+
977
+ enable :session
978
+
979
+ get '/foo' do
980
+ session[:secreto] = 'foo'
981
+ redirect to('/bar')
982
+ end
983
+
984
+ get '/bar' do
985
+ session[:secreto]
986
+ end
987
+
988
+ === Cache Control
989
+
990
+ Asignar tus encabezados correctamente es el cimiento para realizar un cacheo
991
+ HTTP correcto.
992
+
993
+ Podés asignar el encabezado Cache-Control fácilmente:
994
+
995
+ get '/' do
996
+ cache_control :public
997
+ "cachealo!"
998
+ end
999
+
1000
+ Pro tip: configurar el cacheo en un filtro +before+.
1001
+
1002
+ before do
1003
+ cache_control :public, :must_revalidate, :max_age => 60
1004
+ end
1005
+
1006
+ Si estás usando el helper +expires+ para definir el encabezado correspondiente,
1007
+ <tt>Cache-Control</tt> se va a definir automáticamente:
1008
+
1009
+ before do
1010
+ expires 500, :public, :must_revalidate
1011
+ end
1012
+
1013
+ Para usar cachés adecuadamente, deberías considerar usar +etag+ y
1014
+ +last_modified+. Es recomendable que llames a estos helpers *antes* de hacer
1015
+ cualquier trabajo pesado, ya que van a enviar la respuesta inmediatamente si
1016
+ el cliente ya tiene la versión actual en su caché.
1017
+
1018
+ get '/articulo/:id' do
1019
+ @articulo = Articulo.find params[:id]
1020
+ last_modified @articulo.updated_at
1021
+ etag @articulo.sha1
1022
+ erb :articulo
1023
+ end
1024
+
1025
+ También es posible usar una
1026
+ {weak ETag}[http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation]:
1027
+
1028
+ etag @articulo.sha1, :weak
1029
+
1030
+ Estos helpers no van a cachear nada por vos, sino que van a facilitar la
1031
+ información necesaria para poder hacerlo. Si estás buscando soluciones rápidas
1032
+ de cacheo, mirá {rack-cache}[http://rtomayko.github.com/rack-cache/]:
1033
+
1034
+ require "rack/cache"
1035
+ require "sinatra"
1036
+
1037
+ use Rack::Cache
1038
+
1039
+ get '/' do
1040
+ cache_control :public, :max_age => 36000
1041
+ sleep 5
1042
+ "hola"
1043
+ end
1044
+
1045
+ === Enviando Archivos
1046
+
1047
+ Para enviar archivos, podés usar el método <tt>send_file</tt>:
1048
+
1049
+ get '/' do
1050
+ send_file 'foo.png'
1051
+ end
1052
+
1053
+ Además acepta un par de opciones:
1054
+
1055
+ send_file 'foo.png', :type => :jpg
1056
+
1057
+ Estas opciones son:
1058
+
1059
+ [filename]
1060
+ nombre del archivo respondido, por defecto es el nombre real del archivo.
1061
+
1062
+ [last_modified]
1063
+ valor para el encabezado Last-Modified, por defecto toma el mtime del archivo.
1064
+
1065
+ [type]
1066
+ el content type que se va a utilizar, si no está presente se intenta adivinar
1067
+ a partir de la extensión del archivo.
1068
+
1069
+ [disposition]
1070
+ se utiliza para el encabezado Content-Disposition, y puede tomar alguno de los
1071
+ siguientes valores: +nil+ (por defecto), <tt>:attachment</tt> e
1072
+ <tt>:inline</tt>
1073
+
1074
+ [length]
1075
+ encabezado Content-Length, por defecto toma el tamaño del archivo.
1076
+
1077
+ Si el Rack handler lo soporta, se intentará no transmitir directamente desde el
1078
+ proceso de Ruby. Si usás este método, Sinatra se va a encargar automáticamente
1079
+ peticiones de rango.
1080
+
1081
+ === Accediendo al objeto de la petición
681
1082
 
682
1083
  El objeto de la petición entrante puede ser accedido desde el nivel de la
683
1084
  petición (filtros, rutas y manejadores de errores) a través del método
684
- `request`:
1085
+ <tt>request</tt>:
685
1086
 
686
1087
  # app corriendo en http://ejemplo.com/ejemplo
687
1088
  get '/foo' do
@@ -698,14 +1099,15 @@ petición (filtros, rutas y manejadores de errores) a través del método
698
1099
  request.get? # verdadero (hay métodos análogos para los otros verbos)
699
1100
  request.form_data? # falso
700
1101
  request["UNA_CABECERA"] # valor de la cabecera UNA_CABECERA
701
- request.referer # la referencia del cliente o '/'
1102
+ request.referrer # la referencia del cliente o '/'
702
1103
  request.user_agent # user agent (usado por la condición :agent)
703
1104
  request.cookies # hash de las cookies del browser
704
1105
  request.xhr? # es una petición ajax?
705
1106
  request.url # "http://ejemplo.com/ejemplo/foo"
706
1107
  request.path # "/ejemplo/foo"
707
1108
  request.ip # dirección IP del cliente
708
- request.secure? # falso
1109
+ request.secure? # falso (sería verdadero sobre ssl)
1110
+ request.forwarded? # verdadero (si se está corriendo atrás de un proxy inverso)
709
1111
  requuest.env # hash de entorno directamente entregado por Rack
710
1112
  end
711
1113
 
@@ -726,12 +1128,85 @@ El objeto <tt>request.body</tt> es una instancia de IO o StringIO:
726
1128
  "Hola #{datos['nombre']}!"
727
1129
  end
728
1130
 
1131
+ === Archivos Adjuntos
1132
+
1133
+ Podés usar el método helper +attachment+ para indicarle al navegador que
1134
+ almacene la respuesta en el disco en lugar de mostrarla en pantalla.
1135
+
1136
+ get '/' do
1137
+ attachment
1138
+ "guardalo!"
1139
+ end
1140
+
1141
+ También podés pasarle un nombre de archivo:
1142
+
1143
+ get '/' do
1144
+ attachment "info.txt"
1145
+ "guardalo!"
1146
+ end
1147
+
1148
+ === Buscando los Archivos de las Plantillas
1149
+
1150
+ El helper <tt>find_template</tt> se utiliza para encontrar los archivos de las
1151
+ plantillas que se van a renderizar:
1152
+
1153
+ find_template settings.views, 'foo', Tilt[:haml] do |archivo|
1154
+ puts "podría ser #{archivo}"
1155
+ end
1156
+
1157
+ Si bien esto no es muy útil, lo interesante es que podés sobreescribir este
1158
+ método, y así enganchar tu propio mecanismo de búsqueda. Por ejemplo, para
1159
+ poder utilizar más de un directorio de vistas:
1160
+
1161
+ set :views, ['vistas', 'plantillas']
1162
+
1163
+ helpers do
1164
+ def find_template(views, name, engine, &block)
1165
+ Array(views).each { |v| super(v, name, engine, &block) }
1166
+ end
1167
+ end
1168
+
1169
+ Otro ejemplo consiste en usar directorios diferentes para los distintos motores
1170
+ de renderizado:
1171
+
1172
+ set :views, :sass => 'vistas/sass', :haml => 'plantillas', :defecto => 'vistas'
1173
+
1174
+ helpers do
1175
+ def find_template(views, name, engine, &block)
1176
+ _, folder = views.detect { |k,v| engine == Tilt[k] }
1177
+ folder ||= views[:defecto]
1178
+ super(folder, name, engine, &block)
1179
+ end
1180
+ end
1181
+
1182
+ ¡Es muy fácil convertir estos ejemplos en una extensión y compartirla!.
1183
+
1184
+ Notá que <tt>find_template</tt> no verifica si un archivo existe realmente, sino
1185
+ que llama al bloque que recibe para cada path posible. Esto no representa un
1186
+ problema de rendimiento debido a que +render+ va a usar +break+ ni bien
1187
+ encuentre un archivo que exista. Además, las ubicaciones de las plantillas (y
1188
+ su contenido) se cachean cuando no estás en el modo de desarrollo. Es bueno
1189
+ tener en cuenta lo anteiror si escribís un método medio loco.
1190
+
729
1191
  == Configuración
730
1192
 
731
1193
  Ejecutar una vez, en el inicio, en cualquier entorno:
732
1194
 
733
1195
  configure do
734
- ...
1196
+ # asignando una opción
1197
+ set :opcion, 'valor'
1198
+
1199
+ # asignando varias opciones
1200
+ set :a => 1, :b => 2
1201
+
1202
+ # atajo para `set :opcion, true`
1203
+ enable :opcion
1204
+
1205
+ # atajo para `set :opcion, false`
1206
+ disable :opcion
1207
+
1208
+ # también podés tener configuraciones dinámicas usando bloques
1209
+ set(:css_dir) { File.join(views, 'css') }
735
1210
  end
736
1211
 
737
1212
  Ejecutar únicamente cuando el entorno (la variable de entorno RACK_ENV) es
@@ -747,10 +1222,116 @@ Ejecutar cuando el entorno es <tt>:production</tt> o <tt>:test</tt>:
747
1222
  ...
748
1223
  end
749
1224
 
1225
+ Podés acceder a estas opciones utilizando el método <tt>settings</tt>:
1226
+
1227
+ configure do
1228
+ set :foo, 'bar'
1229
+ end
1230
+
1231
+ get '/' do
1232
+ settings.foo? # => true
1233
+ settings.foo # => 'bar'
1234
+ ...
1235
+ end
1236
+
1237
+ === Configuraciones Disponibles
1238
+
1239
+ [absolute_redirects] si está deshabilitada, Sinatra va a permitir redirecciones
1240
+ relativas, sin embargo, como consecuencia de esto, va a
1241
+ dejar de cumplir con el RFC 2616 (HTTP 1.1), que solamente
1242
+ permite redirecciones absolutas.
1243
+
1244
+ Activalo si tu apliación está corriendo atrás de un proxy
1245
+ inverso que no se ha configurado adecuadamente. Notá que
1246
+ el helper +url+ va a seguir produciendo URLs absolutas, a
1247
+ menos que le pasés +false+ como segundo parámetro.
1248
+
1249
+ Deshabilitada por defecto.
1250
+
1251
+ [add_charsets] tipos mime a los que el helper <tt>content_type</tt> les
1252
+ añade automáticamente el charset.
1253
+
1254
+ En general, no deberías asignar directamente esta opción,
1255
+ sino añadirle los charsets que quieras:
1256
+
1257
+ settings.add_charsets << "application/foobar"
1258
+
1259
+ [app_file] archivo principal de la aplicación, se utiliza para
1260
+ detectar la raíz del proyecto, el directorio de las vistas
1261
+ y el público así como las plantillas inline.
1262
+
1263
+ [bind] dirección IP que utilizará el servidor integrado (por
1264
+ defecto: 0.0.0.0).
1265
+
1266
+ [default_encoding] encoding utilizado cuando el mismo se desconoce (por
1267
+ defecto <tt>"utf-8"</tt>).
1268
+
1269
+ [dump_errors] mostrar errores en el log.
1270
+
1271
+ [environment] entorno actual, por defecto toma el valor de
1272
+ <tt>ENV['RACK_ENV']</tt>, o <tt>"development"</tt> si no
1273
+ está disponible.
1274
+
1275
+ [logging] define si se utiliza el logger.
1276
+
1277
+ [lock] coloca un lock alrededor de cada petición, procesando
1278
+ solamente una por proceso.
1279
+
1280
+ Habilitá esta opción si tu aplicación no es thread-safe.
1281
+ Se encuentra deshabilitada por defecto.
1282
+
1283
+ [method_override] utiliza el parámetro <tt>_method</tt> para permtir
1284
+ formularios put/delete en navegadores que no los soportan.
1285
+
1286
+ [port] puerto en el que escuchará el servidor integrado.
1287
+
1288
+ [prefixed_redirects] define si inserta <tt>request.script_name</tt> en las
1289
+ redirecciones cuando no se proporciona un path absoluto.
1290
+ De esta manera, cuando está habilitada,
1291
+ <tt>redirect '/foo'</tt> se comporta de la misma manera
1292
+ que <tt>redirect to('/foo')</tt>. Se encuentra
1293
+ deshabilitada por defecto.
1294
+
1295
+ [public] directorio desde donde se sirven los archivos públicos.
1296
+
1297
+ [reload_templates] define si se recargan las plantillas entre peticiones.
1298
+
1299
+ Se encuentra activado en el entorno de desarrollo y en
1300
+ Ruby 1.8.6 (para compoensar un bug en Ruby que provoca una
1301
+ pérdida de memoria).
1302
+
1303
+ [root] directorio raíz del proyecto.
1304
+
1305
+ [raise_errors] elevar excepciones (detiene la aplicación).
1306
+
1307
+ [run] cuando está habilitada, Sinatra se va a encargar de
1308
+ iniciar el servidor web, no la habilités cuando estés
1309
+ usando rackup o algún otro medio.
1310
+
1311
+ [running] indica si el servidor integrado está ejecutandose, ¡no
1312
+ cambiés esta configuración!.
1313
+
1314
+ [server] servidor, o lista de servidores, para usar como servidor
1315
+ integrado. Por defecto: ['thin', 'mongrel', 'webrick'],
1316
+ el orden establece la prioridad.
1317
+
1318
+ [sessions] habilita sesiones basadas en cookies.
1319
+
1320
+ [show_exceptions] muestra un stack trace en el navegador.
1321
+
1322
+ [static] define si Sinatra debe encargarse de servir archivos
1323
+ estáticos.
1324
+
1325
+ Deshabilitala cuando usés un servidor capaz de hacerlo
1326
+ por sí solo, porque mejorará el rendimiento. Se encuentra
1327
+ habilitada por defecto.
1328
+
1329
+ [views] directorio de las vistas.
1330
+
750
1331
  == Manejo de Errores
751
1332
 
752
1333
  Los manejadores de errores se ejecutan dentro del mismo contexto que las rutas
753
- y los filtros before, lo que significa que podés usar, por ejemplo,
1334
+ y los filtros +before+, lo que significa que podés usar, por ejemplo,
754
1335
  <tt>haml</tt>, <tt>erb</tt>, <tt>halt</tt>, etc.
755
1336
 
756
1337
  === No encontrado <em>(Not Found)</em>
@@ -807,18 +1388,6 @@ O un rango:
807
1388
  Sinatra instala manejadores <tt>not_found</tt> y <tt>error</ttt> especiales
808
1389
  cuando se ejecuta dentro del entorno de desarrollo "development".
809
1390
 
810
- == Tipos Mime
811
-
812
- Cuando usás <tt>send_file</tt> o archivos estáticos tal vez tengas tipos mime
813
- que Sinatra no entiende. Usá +mime_type+ para registrarlos a través de la
814
- extensión de archivo:
815
-
816
- mime_type :foo, 'text/foo'
817
-
818
- También lo podés usar con el ayudante +content_type+:
819
-
820
- content_type :foo
821
-
822
1391
  == Rack Middleware
823
1392
 
824
1393
  Sinatra corre sobre Rack[http://rack.rubyforge.org/], una interfaz minimalista
@@ -918,7 +1487,7 @@ métodos que los provistos por el DSL de top-level. La mayoría de las
918
1487
  aplicaciones top-level se pueden convertir en componentes Sinatra::Base con
919
1488
  dos modificaciones:
920
1489
 
921
- * Tu archivo debe requerir +sinatra/base+ en lugar de +sinatra+; de otra
1490
+ * Tu archivo debe requerir <tt>sinatra/base</tt> en lugar de +sinatra+; de otra
922
1491
  manera, todos los métodos del DSL de sinatra son importados dentro del
923
1492
  espacio de nombres principal.
924
1493
  * Poné las rutas, manejadores de errores, filtros y opciones de tu aplicación
@@ -929,6 +1498,34 @@ desactivadas por defecto, incluyendo el servidor incorporado. Mirá
929
1498
  {Opciones y Configuraciones}[http://sinatra.github.com/configuration.html]
930
1499
  para detalles sobre las opciones disponibles y su comportamiento.
931
1500
 
1501
+ === Estilo Modular vs. Clásico
1502
+
1503
+ Contrariamente a la creencia popular, no hay nada de malo con el estilo clásico.
1504
+ Si se ajusta a tu aplicación, no es necesario que la cambies a una modular.
1505
+
1506
+ Existen tan solo dos desventajas en comparación con el estilo modular:
1507
+
1508
+ * Solamente podés tener una aplicación Sinatra por proceso Ruby - si tenés
1509
+ planificado usar más, cambiá al estilo modular.
1510
+
1511
+ * El estilo clásico contamina Object con métodos delegadores - si tenés
1512
+ planificado empaquetar tu aplicación en una librería/gem, cambiá al estilo
1513
+ modular.
1514
+
1515
+ No hay ninguna razón por la cuál no puedas mezclar los estilos modular y
1516
+ clásico.
1517
+
1518
+ Cuando cambiés de un estilo al otro, tené en cuenta las sutiles diferencias
1519
+ entre sus configuraciones:
1520
+
1521
+ Configuración Clásica Modular
1522
+
1523
+ app_file archivo que carga sinatra nil
1524
+ run $0 == app_file false
1525
+ logging true false
1526
+ method_override true false
1527
+ inline_templates true false
1528
+
932
1529
  === Sirviendo una Aplicación Modular
933
1530
 
934
1531
  Las dos opciones más comunes para iniciar una aplicación modular son, iniciarla
@@ -996,7 +1593,7 @@ aplicación basada en Rack (Rails/Ramaze/Camping/...).
996
1593
 
997
1594
  require 'sinatra/base'
998
1595
 
999
- class PantallaDeLogin< Sinatra::Base
1596
+ class PantallaDeLogin < Sinatra::Base
1000
1597
  enable :sessions
1001
1598
 
1002
1599
  get('/login') { haml :login }
@@ -1033,11 +1630,11 @@ disponibles.
1033
1630
  Cada aplicación Sinatra es una subclase de Sinatra::Base. Si estás usando el
1034
1631
  DSL de top-level (<tt>require 'sinatra'</tt>), entonces esta clase es
1035
1632
  Sinatra::Application, de otra manera es la subclase que creaste explícitamente.
1036
- Al nivel de la clase tenés métodos como `get` o `before`, pero no podés acceder
1037
- a los objetos `request` o `session`, ya que hay una única clase de la
1633
+ Al nivel de la clase tenés métodos como +get+ o +before+, pero no podés acceder
1634
+ a los objetos +request+ o +session+, ya que hay una única clase de la
1038
1635
  aplicación para todas las peticiones.
1039
1636
 
1040
- Las opciones creadas utilizando `set` son métodos al nivel de la clase:
1637
+ Las opciones creadas utilizando +set+ son métodos al nivel de la clase:
1041
1638
 
1042
1639
  class MiApp < Sinatra::Base
1043
1640
  # Ey, estoy en el ámbito de la aplicación!
@@ -1053,21 +1650,21 @@ Tenés la ligadura al ámbito de la aplicación dentro de:
1053
1650
 
1054
1651
  * El cuerpo de la clase de tu aplicación
1055
1652
  * Métodos definidos por extensiones
1056
- * El bloque pasado a `helpers`
1057
- * Procs/bloques usados como el valor para `set`
1653
+ * El bloque pasado a +helpers+
1654
+ * Procs/bloques usados como el valor para +set+
1058
1655
 
1059
1656
  Este ámbito puede alcanzarse de las siguientes maneras:
1060
1657
 
1061
1658
  * A través del objeto pasado a los bloques de configuración (<tt>configure { |c| ...}</tt>)
1062
- * Llamando a `settings` desde dentro del ámbito de la petición
1659
+ * Llamando a +settings+ desde dentro del ámbito de la petición
1063
1660
 
1064
1661
  === Ámbito de Petición/Instancia
1065
1662
 
1066
1663
  Para cada petición entrante, una nueva instancia de la clase de tu aplicación
1067
1664
  es creada y todos los bloques de rutas son ejecutados en ese ámbito. Desde este
1068
- ámbito podés acceder a los objetos `request` y `session` o llamar a los métodos
1069
- de renderización como `erb` o `haml`. Podés acceder al ámbito de la aplicación
1070
- desde el ámbito de la petición utilizando `settings`:
1665
+ ámbito podés acceder a los objetos +request+ y +session+ o llamar a los métodos
1666
+ de renderización como +erb+ o +haml+. Podés acceder al ámbito de la aplicación
1667
+ desde el ámbito de la petición utilizando +settings+:
1071
1668
 
1072
1669
  class MiApp < Sinatra::Base
1073
1670
  # Ey, estoy en el ámbito de la aplicación!
@@ -1086,7 +1683,7 @@ desde el ámbito de la petición utilizando `settings`:
1086
1683
 
1087
1684
  Tenés la ligadura al ámbito de la petición dentro de:
1088
1685
 
1089
- * bloques pasados a get/head/post/put/delete
1686
+ * bloques pasados a get/head/post/put/delete/options
1090
1687
  * filtros before/after
1091
1688
  * métodos ayudantes
1092
1689
  * plantillas/vistas
@@ -1097,13 +1694,13 @@ El ámbito de delegación solo reenvía métodos al ámbito de clase. De cualqui
1097
1694
  manera, no se comporta 100% como el ámbito de clase porque no tenés la ligadura
1098
1695
  de la clase: únicamente métodos marcados explícitamente para delegación están
1099
1696
  disponibles y no compartís variables/estado con el ámbito de clase (léase:
1100
- tenés un `self` diferente). Podés agregar delegaciones de método llamando a
1697
+ tenés un +self+ diferente). Podés agregar delegaciones de método llamando a
1101
1698
  <tt>Sinatra::Delegator.delegate :nombre_del_metodo</tt>.
1102
1699
 
1103
1700
  Tenés la ligadura al ámbito de delegación dentro de:
1104
1701
 
1105
1702
  * La ligadura del top-level, si hiciste <tt>require "sinatra"</tt>
1106
- * Un objeto extendido con el mixin `Sinatra::Delegator`
1703
+ * Un objeto extendido con el mixin <tt>Sinatra::Delegator</tt>
1107
1704
 
1108
1705
  Pegale una mirada al código: acá está el
1109
1706
  {Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128]
@@ -1124,6 +1721,59 @@ Las opciones son:
1124
1721
  -s # especifica el servidor/manejador rack (thin es usado por defecto)
1125
1722
  -x # activa el mutex lock (está desactivado por defecto)
1126
1723
 
1724
+ == Requerimientos
1725
+
1726
+ Se recomienda instalar Sinatra en Ruby 1.8.7, 1.9.2, JRuby o Rubinius.
1727
+
1728
+ Las siguientes versiones de Ruby son soportadas oficialmente:
1729
+
1730
+ [ Ruby 1.8.6 ]
1731
+ No se recomienda utilizar Sinatra en 1.8.6. Sin embargo, esta versión será
1732
+ soportada oficialmente hasta que se libere Sinatra 1.3.0. RDoc y CoffeeScript
1733
+ no son soportadas por esta versión de Ruby. 1.8.6 contiene una falla
1734
+ importante de pérdida de memoria en su implementación de Hash, que afecta a
1735
+ las versiones de Sinatra anteriores a 1.1.1. La versión actual evita
1736
+ explícitamente esta falla a expensas de una disminución del rendimiento. Por
1737
+ último, Rack >= 1.2 dejó de soportar 1.8.6, por lo que vas a tener que usar
1738
+ alguna versión 1.1.x.
1739
+
1740
+ [ Ruby 1.8.7 ]
1741
+ 1.8.7 es soportado completamente. Sin embargo, si no hay nada que te lo
1742
+ prohíba, te recomendamos que usés 1.9.2 o cambies a JRuby o Rubinius.
1743
+
1744
+ [ Ruby 1.9.2 ]
1745
+ 1.9.2 es soportado y recomendado. Tené en cuenta que Radius y Markaby no
1746
+ son compatibles con 1.9 actualmente. Además, no usés 1.9.2p0, porque produce
1747
+ fallos de segmentación cuando se utiliza Sinatra.
1748
+
1749
+ [ Rubinius ]
1750
+ Rubinius es soportado oficialmente (Rubinius >= 1.2.2), con la excepción de
1751
+ las plantillas Textile.
1752
+
1753
+ [ JRuby ]
1754
+ JRuby es soportado oficialmente (JRuby >= 1.5.6). No se conocen problemas con
1755
+ librerías de plantillas de terceras partes. Sin embargo, si elegís usar
1756
+ JRuby, deberías examinar sus Rack handlers porque el servidor web Thin no es
1757
+ soportado actualmente.
1758
+
1759
+ Siempre le prestamos atención a las nuevas versiones de Ruby.
1760
+
1761
+ Las siguientes implementaciones de Ruby no se encuentran soportadas
1762
+ oficialmente. De cualquier manera, pueden ejecutar Sinatra:
1763
+
1764
+ * Versiones anteriores de JRuby y Rubinius
1765
+ * MacRuby
1766
+ * Maglev
1767
+ * IronRuby
1768
+ * Ruby 1.9.0 y 1.9.1
1769
+
1770
+ No estar soportada oficialmente, significa que si las cosas solamente se rompen
1771
+ ahí y no en una plataforma soportada, asumimos que no es nuestro problema sino
1772
+ el suyo.
1773
+
1774
+ Sinatra debería funcionar en cualquier sistema operativo soportado por la
1775
+ implementación de Ruby elegida.
1776
+
1127
1777
  == A la Vanguardia
1128
1778
 
1129
1779
  Si querés usar el código de Sinatra más reciente, sentite libre de ejecutar
@@ -1164,7 +1814,7 @@ Ahora podés arrancar tu aplicación así:
1164
1814
  === Con Git
1165
1815
 
1166
1816
  Cloná el repositorio localmente y ejecutá tu aplicación, asegurándote que el
1167
- directorio <tt>sinatra/lib</tt> esté en el <tt>LOAD_PATH</tt>:
1817
+ directorio <tt>sinatra/lib</tt> esté en el <tt>$LOAD_PATH</tt>:
1168
1818
 
1169
1819
  cd miapp
1170
1820
  git clone git://github.com/sinatra/sinatra.git