sinatra 1.4.0.c → 1.4.0.d
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sinatra might be problematic. Click here for more details.
- data/CHANGES +11 -1
- data/Gemfile +2 -4
- data/README.de.md +1 -0
- data/README.es.md +2777 -0
- data/{README.hu.rdoc → README.hu.md} +169 -83
- data/README.jp.md +1097 -0
- data/README.ko.md +2483 -0
- data/README.md +8 -0
- data/README.pt-br.md +954 -0
- data/README.pt-pt.md +791 -0
- data/README.zh.md +2136 -0
- data/Rakefile +2 -2
- data/examples/chat.rb +3 -3
- data/lib/sinatra/base.rb +17 -7
- data/lib/sinatra/version.rb +1 -1
- data/sinatra.gemspec +3 -3
- data/test/delegator_test.rb +1 -1
- data/test/helper.rb +8 -0
- data/test/helpers_test.rb +4 -1
- data/test/rdoc_test.rb +2 -2
- data/test/readme_test.rb +10 -0
- data/test/routing_test.rb +1 -1
- metadata +35 -29
- data/README.es.rdoc +0 -2112
- data/README.jp.rdoc +0 -1056
- data/README.ko.rdoc +0 -1932
- data/README.pt-br.rdoc +0 -778
- data/README.pt-pt.rdoc +0 -647
- data/README.zh.rdoc +0 -1816
data/Rakefile
CHANGED
@@ -98,8 +98,8 @@ desc "list of contributors"
|
|
98
98
|
task :thanks, [:release,:backports] do |t, a|
|
99
99
|
a.with_defaults :release => "#{prev_version}..HEAD",
|
100
100
|
:backports => "#{prev_feature}.0..#{prev_feature}.x"
|
101
|
-
included = `git log --format=format:"%aN\t%s" #{a.release}`.lines.
|
102
|
-
excluded = `git log --format=format:"%aN\t%s" #{a.backports}`.lines.
|
101
|
+
included = `git log --format=format:"%aN\t%s" #{a.release}`.lines.map { |l| l.force_encoding('binary') }
|
102
|
+
excluded = `git log --format=format:"%aN\t%s" #{a.backports}`.lines.map { |l| l.force_encoding('binary') }
|
103
103
|
commits = (included - excluded).group_by { |c| c[/^[^\t]+/] }
|
104
104
|
authors = commits.keys.sort_by { |n| - commits[n].size } - team
|
105
105
|
puts authors[0..-2].join(', ') << " and " << authors.last,
|
data/examples/chat.rb
CHANGED
@@ -42,6 +42,9 @@ __END__
|
|
42
42
|
|
43
43
|
@@ chat
|
44
44
|
<pre id='chat'></pre>
|
45
|
+
<form>
|
46
|
+
<input id='msg' placeholder='type message here...' />
|
47
|
+
</form>
|
45
48
|
|
46
49
|
<script>
|
47
50
|
// reading
|
@@ -56,6 +59,3 @@ __END__
|
|
56
59
|
});
|
57
60
|
</script>
|
58
61
|
|
59
|
-
<form>
|
60
|
-
<input id='msg' placeholder='type message here...' />
|
61
|
-
</form>
|
data/lib/sinatra/base.rb
CHANGED
@@ -48,7 +48,15 @@ module Sinatra
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def idempotent?
|
51
|
-
safe? or put? or delete?
|
51
|
+
safe? or put? or delete? or link? or unlink?
|
52
|
+
end
|
53
|
+
|
54
|
+
def link?
|
55
|
+
request_method == "LINK"
|
56
|
+
end
|
57
|
+
|
58
|
+
def unlink?
|
59
|
+
request_method == "UNLINK"
|
52
60
|
end
|
53
61
|
|
54
62
|
private
|
@@ -125,7 +133,7 @@ module Sinatra
|
|
125
133
|
headers["Content-Length"] = body.inject(0) { |l, p| l + Rack::Utils.bytesize(p) }.to_s
|
126
134
|
end
|
127
135
|
|
128
|
-
[status.to_i,
|
136
|
+
[status.to_i, headers, result]
|
129
137
|
end
|
130
138
|
|
131
139
|
private
|
@@ -159,7 +167,7 @@ module Sinatra
|
|
159
167
|
|
160
168
|
private
|
161
169
|
|
162
|
-
def setup_close(env, status,
|
170
|
+
def setup_close(env, status, headers, body)
|
163
171
|
return unless body.respond_to? :close and env.include? 'async.close'
|
164
172
|
env['async.close'].callback { body.close }
|
165
173
|
env['async.close'].errback { body.close }
|
@@ -1354,6 +1362,8 @@ module Sinatra
|
|
1354
1362
|
def head(path, opts = {}, &bk) route 'HEAD', path, opts, &bk end
|
1355
1363
|
def options(path, opts = {}, &bk) route 'OPTIONS', path, opts, &bk end
|
1356
1364
|
def patch(path, opts = {}, &bk) route 'PATCH', path, opts, &bk end
|
1365
|
+
def link(path, opts = {}, &bk) route 'LINK', path, opts, &bk end
|
1366
|
+
def unlink(path, opts = {}, &bk) route 'UNLINK', path, opts, &bk end
|
1357
1367
|
|
1358
1368
|
private
|
1359
1369
|
def route(verb, path, options = {}, &block)
|
@@ -1860,10 +1870,10 @@ module Sinatra
|
|
1860
1870
|
end
|
1861
1871
|
end
|
1862
1872
|
|
1863
|
-
delegate :get, :patch, :put, :post, :delete, :head, :options, :
|
1864
|
-
:
|
1865
|
-
:enable, :disable, :use, :development?, :test?,
|
1866
|
-
:helpers, :settings, :register
|
1873
|
+
delegate :get, :patch, :put, :post, :delete, :head, :options, :link, :unlink,
|
1874
|
+
:template, :layout, :before, :after, :error, :not_found, :configure,
|
1875
|
+
:set, :mime_type, :enable, :disable, :use, :development?, :test?,
|
1876
|
+
:production?, :helpers, :settings, :register
|
1867
1877
|
|
1868
1878
|
class << self
|
1869
1879
|
attr_accessor :target
|
data/lib/sinatra/version.rb
CHANGED
data/sinatra.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new 'sinatra', Sinatra::VERSION do |s|
|
|
12
12
|
s.extra_rdoc_files = s.files.select { |p| p =~ /^README/ } << 'LICENSE'
|
13
13
|
s.rdoc_options = %w[--line-numbers --inline-source --title Sinatra --main README.rdoc --encoding=UTF-8]
|
14
14
|
|
15
|
-
s.add_dependency 'rack',
|
16
|
-
s.add_dependency '
|
17
|
-
s.add_dependency '
|
15
|
+
s.add_dependency 'rack', '~> 1.5', '>= 1.5.2'
|
16
|
+
s.add_dependency 'tilt', '~> 1.3', '>= 1.3.4'
|
17
|
+
s.add_dependency 'rack-protection', '~> 1.4'
|
18
18
|
end
|
data/test/delegator_test.rb
CHANGED
@@ -60,7 +60,7 @@ class DelegatorTest < Test::Unit::TestCase
|
|
60
60
|
assert_equal Sinatra::Application, Sinatra::Delegator.target
|
61
61
|
end
|
62
62
|
|
63
|
-
%w[get put post delete options patch].each do |verb|
|
63
|
+
%w[get put post delete options patch link unlink].each do |verb|
|
64
64
|
it "delegates #{verb} correctly" do
|
65
65
|
delegation_app do
|
66
66
|
send(verb, '/hello') { 'Hello World' }
|
data/test/helper.rb
CHANGED
@@ -97,6 +97,14 @@ class Test::Unit::TestCase
|
|
97
97
|
request(uri, env.merge(:method => "PATCH", :params => params), &block)
|
98
98
|
end
|
99
99
|
|
100
|
+
def link(uri, params = {}, env = {}, &block)
|
101
|
+
request(uri, env.merge(:method => "LINK", :params => params), &block)
|
102
|
+
end
|
103
|
+
|
104
|
+
def unlink(uri, params = {}, env = {}, &block)
|
105
|
+
request(uri, env.merge(:method => "UNLINK", :params => params), &block)
|
106
|
+
end
|
107
|
+
|
100
108
|
# Delegate other missing methods to response.
|
101
109
|
def method_missing(name, *args, &block)
|
102
110
|
if response && response.respond_to?(name)
|
data/test/helpers_test.rb
CHANGED
@@ -531,7 +531,10 @@ class HelpersTest < Test::Unit::TestCase
|
|
531
531
|
end
|
532
532
|
end
|
533
533
|
|
534
|
-
|
534
|
+
# Silence warnings since Rack::Session::Cookie complains about the non-present session secret
|
535
|
+
silence_warnings do
|
536
|
+
get '/'
|
537
|
+
end
|
535
538
|
assert_body 'ok'
|
536
539
|
end
|
537
540
|
|
data/test/rdoc_test.rb
CHANGED
@@ -16,13 +16,13 @@ class RdocTest < Test::Unit::TestCase
|
|
16
16
|
it 'renders inline rdoc strings' do
|
17
17
|
rdoc_app { rdoc '= Hiya' }
|
18
18
|
assert ok?
|
19
|
-
assert_body /<h1[^>]*>Hiya
|
19
|
+
assert_body /<h1[^>]*>Hiya(<span><a href=\"#label-Hiya\">¶<\/a> <a href=\"#documentation\">↑<\/a><\/span>)?<\/h1>/
|
20
20
|
end
|
21
21
|
|
22
22
|
it 'renders .rdoc files in views path' do
|
23
23
|
rdoc_app { rdoc :hello }
|
24
24
|
assert ok?
|
25
|
-
assert_body /<h1[^>]*>Hello From RDoc
|
25
|
+
assert_body /<h1[^>]*>Hello From RDoc(<span><a href=\"#label-Hello\+From\+RDoc\">¶<\/a> <a href=\"#documentation\">↑<\/a><\/span>)?<\/h1>/
|
26
26
|
end
|
27
27
|
|
28
28
|
it "raises error if template not found" do
|
data/test/readme_test.rb
CHANGED
@@ -22,6 +22,10 @@ class ReadmeTest < Test::Unit::TestCase
|
|
22
22
|
delete('/') { ".. annihilate something .." }
|
23
23
|
|
24
24
|
options('/') { ".. appease something .." }
|
25
|
+
|
26
|
+
link('/') { ".. affiliate something .." }
|
27
|
+
|
28
|
+
unlink('/') { ".. separate something .." }
|
25
29
|
end
|
26
30
|
|
27
31
|
get '/'
|
@@ -41,6 +45,12 @@ class ReadmeTest < Test::Unit::TestCase
|
|
41
45
|
|
42
46
|
options '/'
|
43
47
|
assert_body '.. appease something ..'
|
48
|
+
|
49
|
+
link '/'
|
50
|
+
assert_body '.. affiliate something ..'
|
51
|
+
|
52
|
+
unlink '/'
|
53
|
+
assert_body '.. separate something ..'
|
44
54
|
end
|
45
55
|
|
46
56
|
example do
|
data/test/routing_test.rb
CHANGED
@@ -23,7 +23,7 @@ class RegexpLookAlike
|
|
23
23
|
end
|
24
24
|
|
25
25
|
class RoutingTest < Test::Unit::TestCase
|
26
|
-
%w[get put post delete options patch].each do |verb|
|
26
|
+
%w[get put post delete options patch link unlink].each do |verb|
|
27
27
|
it "defines #{verb.upcase} request handlers with #{verb}" do
|
28
28
|
mock_app {
|
29
29
|
send verb, '/hello' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sinatra
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.4.0.
|
4
|
+
version: 1.4.0.d
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date: 2013-
|
15
|
+
date: 2013-03-09 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: rack
|
@@ -21,7 +21,10 @@ dependencies:
|
|
21
21
|
requirements:
|
22
22
|
- - ~>
|
23
23
|
- !ruby/object:Gem::Version
|
24
|
-
version: '1.
|
24
|
+
version: '1.5'
|
25
|
+
- - ! '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 1.5.2
|
25
28
|
type: :runtime
|
26
29
|
prerelease: false
|
27
30
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -29,15 +32,21 @@ dependencies:
|
|
29
32
|
requirements:
|
30
33
|
- - ~>
|
31
34
|
- !ruby/object:Gem::Version
|
32
|
-
version: '1.
|
35
|
+
version: '1.5'
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: 1.5.2
|
33
39
|
- !ruby/object:Gem::Dependency
|
34
|
-
name:
|
40
|
+
name: tilt
|
35
41
|
requirement: !ruby/object:Gem::Requirement
|
36
42
|
none: false
|
37
43
|
requirements:
|
38
44
|
- - ~>
|
39
45
|
- !ruby/object:Gem::Version
|
40
46
|
version: '1.3'
|
47
|
+
- - ! '>='
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 1.3.4
|
41
50
|
type: :runtime
|
42
51
|
prerelease: false
|
43
52
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -46,17 +55,17 @@ dependencies:
|
|
46
55
|
- - ~>
|
47
56
|
- !ruby/object:Gem::Version
|
48
57
|
version: '1.3'
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 1.3.4
|
49
61
|
- !ruby/object:Gem::Dependency
|
50
|
-
name:
|
62
|
+
name: rack-protection
|
51
63
|
requirement: !ruby/object:Gem::Requirement
|
52
64
|
none: false
|
53
65
|
requirements:
|
54
66
|
- - ~>
|
55
67
|
- !ruby/object:Gem::Version
|
56
|
-
version: '1.
|
57
|
-
- - ! '>='
|
58
|
-
- !ruby/object:Gem::Version
|
59
|
-
version: 1.3.3
|
68
|
+
version: '1.4'
|
60
69
|
type: :runtime
|
61
70
|
prerelease: false
|
62
71
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -64,10 +73,7 @@ dependencies:
|
|
64
73
|
requirements:
|
65
74
|
- - ~>
|
66
75
|
- !ruby/object:Gem::Version
|
67
|
-
version: '1.
|
68
|
-
- - ! '>='
|
69
|
-
- !ruby/object:Gem::Version
|
70
|
-
version: 1.3.3
|
76
|
+
version: '1.4'
|
71
77
|
description: Sinatra is a DSL for quickly creating web applications in Ruby with minimal
|
72
78
|
effort.
|
73
79
|
email: sinatrarb@googlegroups.com
|
@@ -75,16 +81,16 @@ executables: []
|
|
75
81
|
extensions: []
|
76
82
|
extra_rdoc_files:
|
77
83
|
- README.de.md
|
78
|
-
- README.es.
|
84
|
+
- README.es.md
|
79
85
|
- README.fr.md
|
80
|
-
- README.hu.
|
81
|
-
- README.jp.
|
82
|
-
- README.ko.
|
86
|
+
- README.hu.md
|
87
|
+
- README.jp.md
|
88
|
+
- README.ko.md
|
83
89
|
- README.md
|
84
|
-
- README.pt-br.
|
85
|
-
- README.pt-pt.
|
90
|
+
- README.pt-br.md
|
91
|
+
- README.pt-pt.md
|
86
92
|
- README.ru.md
|
87
|
-
- README.zh.
|
93
|
+
- README.zh.md
|
88
94
|
- LICENSE
|
89
95
|
files:
|
90
96
|
- .yardopts
|
@@ -93,16 +99,16 @@ files:
|
|
93
99
|
- Gemfile
|
94
100
|
- LICENSE
|
95
101
|
- README.de.md
|
96
|
-
- README.es.
|
102
|
+
- README.es.md
|
97
103
|
- README.fr.md
|
98
|
-
- README.hu.
|
99
|
-
- README.jp.
|
100
|
-
- README.ko.
|
104
|
+
- README.hu.md
|
105
|
+
- README.jp.md
|
106
|
+
- README.ko.md
|
101
107
|
- README.md
|
102
|
-
- README.pt-br.
|
103
|
-
- README.pt-pt.
|
108
|
+
- README.pt-br.md
|
109
|
+
- README.pt-pt.md
|
104
110
|
- README.ru.md
|
105
|
-
- README.zh.
|
111
|
+
- README.zh.md
|
106
112
|
- Rakefile
|
107
113
|
- examples/chat.rb
|
108
114
|
- examples/simple.rb
|
@@ -230,7 +236,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
230
236
|
version: '0'
|
231
237
|
segments:
|
232
238
|
- 0
|
233
|
-
hash:
|
239
|
+
hash: -2481345757706038995
|
234
240
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
235
241
|
none: false
|
236
242
|
requirements:
|
data/README.es.rdoc
DELETED
@@ -1,2112 +0,0 @@
|
|
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 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 Stylus
|
541
|
-
|
542
|
-
Dependencias:: {ruby-stylus}[https://github.com/lucasmazza/ruby-stylus]
|
543
|
-
Extensiones de Archivo:: <tt>.styl</tt>
|
544
|
-
Ejemplo:: <tt>stylus :index</tt>
|
545
|
-
|
546
|
-
=== Plantillas Yajl
|
547
|
-
|
548
|
-
Dependencias:: {yajl-ruby}[https://github.com/brianmario/yajl-ruby]
|
549
|
-
Extensiones de Archivo:: <tt>.yajl</tt>
|
550
|
-
Ejemplo:: <tt>yajl :index, :locals => { :key => 'qux' }, :callback => 'present', :variable => 'resource'</tt>
|
551
|
-
|
552
|
-
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>.
|
553
|
-
|
554
|
-
json = { :foo => 'bar' }
|
555
|
-
json[:baz] = key
|
556
|
-
|
557
|
-
Las opciones <tt>:callback</tt> y <tt>:variable</tt> se pueden utilizar para decorar el objeto renderizado:
|
558
|
-
|
559
|
-
var resource = {"foo":"bar","baz":"qux"}; present(resource);
|
560
|
-
|
561
|
-
=== Plantillas WLang
|
562
|
-
|
563
|
-
Dependencias:: {wlang}[https://github.com/blambeau/wlang/]
|
564
|
-
Extensiones de Archivo:: <tt>.wlang</tt>
|
565
|
-
Ejemplo:: <tt>wlang :index, :locals => { :clave => 'valor' }</tt>
|
566
|
-
|
567
|
-
Como no vas a poder llamar a métodos de Ruby (excepto por +yield+) desde una
|
568
|
-
plantilla WLang, casi siempre vas a querer pasarle locales.
|
569
|
-
|
570
|
-
=== Plantillas Embebidas
|
571
|
-
|
572
|
-
get '/' do
|
573
|
-
haml '%div.titulo Hola Mundo'
|
574
|
-
end
|
575
|
-
|
576
|
-
Renderiza el template embebido en el string.
|
577
|
-
|
578
|
-
=== Accediendo a Variables en Plantillas
|
579
|
-
|
580
|
-
Las plantillas son evaluadas dentro del mismo contexto que los manejadores de
|
581
|
-
ruta. Las variables de instancia asignadas en los manejadores de ruta son
|
582
|
-
accesibles directamente por las plantillas:
|
583
|
-
|
584
|
-
get '/:id' do
|
585
|
-
@foo = Foo.find(params[:id])
|
586
|
-
haml '%h1= @foo.nombre'
|
587
|
-
end
|
588
|
-
|
589
|
-
O es posible especificar un Hash de variables locales explícitamente:
|
590
|
-
|
591
|
-
get '/:id' do
|
592
|
-
foo = Foo.find(params[:id])
|
593
|
-
haml '%h1= bar.nombre', :locals => { :bar => foo }
|
594
|
-
end
|
595
|
-
|
596
|
-
Esto es usado típicamente cuando se renderizan plantillas como parciales desde
|
597
|
-
adentro de otras plantillas.
|
598
|
-
|
599
|
-
=== Plantillas Inline
|
600
|
-
|
601
|
-
Las plantillas pueden ser definidas al final del archivo fuente:
|
602
|
-
|
603
|
-
require 'rubygems'
|
604
|
-
require 'sinatra'
|
605
|
-
|
606
|
-
get '/' do
|
607
|
-
haml :index
|
608
|
-
end
|
609
|
-
|
610
|
-
__END__
|
611
|
-
|
612
|
-
@@ layout
|
613
|
-
%html
|
614
|
-
= yield
|
615
|
-
|
616
|
-
@@ index
|
617
|
-
%div.titulo Hola mundo!!!!!
|
618
|
-
|
619
|
-
NOTA: únicamente las plantillas inline definidas en el archivo fuente que
|
620
|
-
requiere sinatra son cargadas automáticamente. Llamá <tt>enable
|
621
|
-
:inline_templates</tt> explícitamente si tenés plantillas inline en otros
|
622
|
-
archivos fuente.
|
623
|
-
|
624
|
-
=== Plantillas Nombradas
|
625
|
-
|
626
|
-
Las plantillas también pueden ser definidas usando el método top-level
|
627
|
-
<tt>template</tt>:
|
628
|
-
|
629
|
-
template :layout do
|
630
|
-
"%html\n =yield\n"
|
631
|
-
end
|
632
|
-
|
633
|
-
template :index do
|
634
|
-
'%div.titulo Hola Mundo!'
|
635
|
-
end
|
636
|
-
|
637
|
-
get '/' do
|
638
|
-
haml :index
|
639
|
-
end
|
640
|
-
|
641
|
-
Si existe una plantilla con el nombre "layout", va a ser usada cada vez que
|
642
|
-
una plantilla es renderizada. Podés desactivar los layouts individualmente
|
643
|
-
pasando <tt>:layout => false</tt> o globalmente con
|
644
|
-
<tt>set :haml, :layout => false</tt>:
|
645
|
-
|
646
|
-
get '/' do
|
647
|
-
haml :index, :layout => !request.xhr?
|
648
|
-
end
|
649
|
-
|
650
|
-
=== Asociando Extensiones de Archivo
|
651
|
-
|
652
|
-
Para asociar una extensión de archivo con un motor de renderizado, usá
|
653
|
-
<tt>Tilt.register</tt>. Por ejemplo, si querés usar la extensión +tt+ para
|
654
|
-
las plantillas Textile, podés hacer lo siguiente:
|
655
|
-
|
656
|
-
Tilt.register :tt, Tilt[:textile]
|
657
|
-
|
658
|
-
=== Agregando Tu Propio Motor de Renderizado
|
659
|
-
|
660
|
-
Primero, registrá tu motor con Tilt, y después, creá tu método de renderizado:
|
661
|
-
|
662
|
-
Tilt.register :mipg, MiMotorParaPlantillaGenial
|
663
|
-
|
664
|
-
helpers do
|
665
|
-
def mypg(*args) render(:mypg, *args) end
|
666
|
-
end
|
667
|
-
|
668
|
-
get '/' do
|
669
|
-
mypg :index
|
670
|
-
end
|
671
|
-
|
672
|
-
Renderiza <tt>./views/index.mypg</tt>. Mirá https://github.com/rtomayko/tilt
|
673
|
-
para aprender más de Tilt.
|
674
|
-
|
675
|
-
== Filtros
|
676
|
-
|
677
|
-
Los filtros +before+ son evaluados antes de cada petición dentro del mismo
|
678
|
-
contexto que las rutas. Pueden modificar la petición y la respuesta. Las
|
679
|
-
variables de instancia asignadas en los filtros son accesibles por las rutas y
|
680
|
-
las plantillas:
|
681
|
-
|
682
|
-
before do
|
683
|
-
@nota = 'Hey!'
|
684
|
-
request.path_info = '/foo/bar/baz'
|
685
|
-
end
|
686
|
-
|
687
|
-
get '/foo/*' do
|
688
|
-
@nota #=> 'Hey!'
|
689
|
-
params[:splat] #=> 'bar/baz'
|
690
|
-
end
|
691
|
-
|
692
|
-
Los filtros +after+ son evaluados después de cada petición dentro del mismo
|
693
|
-
contexto y también pueden modificar la petición y la respuesta. Las variables
|
694
|
-
de instancia asignadas en los filtros +before+ y en las rutas son accesibles por
|
695
|
-
los filtros +after+:
|
696
|
-
|
697
|
-
after do
|
698
|
-
puts response.status
|
699
|
-
end
|
700
|
-
|
701
|
-
Nota: A menos que usés el método +body+ en lugar de simplemente devolver un
|
702
|
-
string desde una ruta, el cuerpo de la respuesta no va a estar disponible en
|
703
|
-
un filtro after, debido a que todavía no se ha generado.
|
704
|
-
|
705
|
-
Los filtros aceptan un patrón opcional, que cuando está presente causa que los
|
706
|
-
mismos sean evaluados únicamente si el path de la petición coincide con ese
|
707
|
-
patrón:
|
708
|
-
|
709
|
-
before '/protegido/*' do
|
710
|
-
autenticar!
|
711
|
-
end
|
712
|
-
|
713
|
-
after '/crear/:slug' do |slug|
|
714
|
-
session[:ultimo_slug] = slug
|
715
|
-
end
|
716
|
-
|
717
|
-
Al igual que las rutas, los filtros también pueden aceptar condiciones:
|
718
|
-
|
719
|
-
before :agent => /Songbird/ do
|
720
|
-
# ...
|
721
|
-
end
|
722
|
-
|
723
|
-
after '/blog/*', :host_name => 'ejemplo.com' do
|
724
|
-
# ...
|
725
|
-
end
|
726
|
-
|
727
|
-
== Ayudantes
|
728
|
-
|
729
|
-
Usá el método top-level <tt>helpers</tt> para definir métodos ayudantes que
|
730
|
-
pueden ser utilizados dentro de los manejadores de rutas y las plantillas:
|
731
|
-
|
732
|
-
helpers do
|
733
|
-
def bar(nombre)
|
734
|
-
"#{nombre}bar"
|
735
|
-
end
|
736
|
-
end
|
737
|
-
|
738
|
-
get '/:nombre' do
|
739
|
-
bar(params[:nombre])
|
740
|
-
end
|
741
|
-
|
742
|
-
Por cuestiones organizativas, puede resultar conveniente organizar los métodos
|
743
|
-
ayudantes en distintos módulos:
|
744
|
-
|
745
|
-
module FooUtils
|
746
|
-
def foo(nombre) "#{nombre}foo" end
|
747
|
-
end
|
748
|
-
|
749
|
-
module BarUtils
|
750
|
-
def bar(nombre) "#{nombre}bar" end
|
751
|
-
end
|
752
|
-
|
753
|
-
helpers FooUtils, BarUtils
|
754
|
-
|
755
|
-
El efecto de utilizar <tt>helpers</tt> de esta manera es el mismo que resulta de
|
756
|
-
incluir los módulos en la clase de la aplicación.
|
757
|
-
|
758
|
-
=== Usando Sesiones
|
759
|
-
|
760
|
-
Una sesión es usada para mantener el estado a través de distintas peticiones.
|
761
|
-
Cuando están activadas, tenés un hash de sesión para cada sesión de usuario:
|
762
|
-
|
763
|
-
enable :sessions
|
764
|
-
|
765
|
-
get '/' do
|
766
|
-
"valor = " << session[:valor].inspect
|
767
|
-
end
|
768
|
-
|
769
|
-
get '/:valor' do
|
770
|
-
session[:valor] = params[:valor]
|
771
|
-
end
|
772
|
-
|
773
|
-
Tené en cuenta que <tt>enable :sessions</tt> guarda todos los datos en una
|
774
|
-
cookie, lo que no es siempre deseable (guardar muchos datos va a incrementar
|
775
|
-
tu tráfico, por citar un ejemplo). Podés usar cualquier middleware Rack para
|
776
|
-
manejar sesiones, de la misma manera que usarías cualquier otro middleware,
|
777
|
-
pero con la salvedad de que *no* tenés que llamar a <tt>enable :sessions</tt>:
|
778
|
-
|
779
|
-
use Rack::Session::Pool, :expire_after => 2592000
|
780
|
-
|
781
|
-
get '/' do
|
782
|
-
"valor = " << session[:valor].inspect
|
783
|
-
end
|
784
|
-
|
785
|
-
get '/:valor' do
|
786
|
-
session[:valor] = params[:valor]
|
787
|
-
end
|
788
|
-
|
789
|
-
Para incrementar la seguridad, los datos de la sesión almacenados en
|
790
|
-
la cookie son firmados con un secreto de sesión. Este secreto, es
|
791
|
-
generado aleatoriamente por Sinatra. De cualquier manera, hay que
|
792
|
-
tener en cuenta que cada vez que inicies la aplicación se va a generar
|
793
|
-
uno nuevo. Así, si querés que todas las instancias de tu aplicación
|
794
|
-
compartan un único secreto, tenés que definirlo vos:
|
795
|
-
|
796
|
-
set :session_secret, 'super secreto'
|
797
|
-
|
798
|
-
Si necesitás una configuración más específica, +sessions+ acepta un
|
799
|
-
Hash con opciones:
|
800
|
-
|
801
|
-
set :sessions, :domain => 'foo.com'
|
802
|
-
|
803
|
-
=== Interrupción
|
804
|
-
|
805
|
-
Para detener inmediatamente una petición dentro de un filtro o una ruta usá:
|
806
|
-
|
807
|
-
halt
|
808
|
-
|
809
|
-
También podés especificar el estado:
|
810
|
-
|
811
|
-
halt 410
|
812
|
-
|
813
|
-
O el cuerpo:
|
814
|
-
|
815
|
-
halt 'esto va a ser el cuerpo'
|
816
|
-
|
817
|
-
O los dos:
|
818
|
-
|
819
|
-
halt 401, 'salí de acá!'
|
820
|
-
|
821
|
-
Con cabeceras:
|
822
|
-
|
823
|
-
halt 402, { 'Content-Type' => 'text/plain' }, 'venganza'
|
824
|
-
|
825
|
-
Obviamente, es posible utilizar +halt+ con una plantilla:
|
826
|
-
|
827
|
-
halt erb(:error)
|
828
|
-
|
829
|
-
=== Paso
|
830
|
-
|
831
|
-
Una ruta puede pasarle el procesamiento a la siguiente ruta que coincida con
|
832
|
-
la petición usando <tt>pass</tt>:
|
833
|
-
|
834
|
-
get '/adivina/:quien' do
|
835
|
-
pass unless params[:quien] == 'Franco'
|
836
|
-
'Adivinaste!'
|
837
|
-
end
|
838
|
-
|
839
|
-
get '/adivina/*' do
|
840
|
-
'Erraste!'
|
841
|
-
end
|
842
|
-
|
843
|
-
Se sale inmediatamente del bloque de la ruta y se le pasa el control a la
|
844
|
-
siguiente ruta que coincida. Si no coincide ninguna ruta, se devuelve un 404.
|
845
|
-
|
846
|
-
=== Ejecutando Otra Ruta
|
847
|
-
|
848
|
-
Cuando querés obtener el resultado de la llamada a una ruta, +pass+ no te va a
|
849
|
-
servir. Para lograr esto, podés usar +call+:
|
850
|
-
|
851
|
-
get '/foo' do
|
852
|
-
status, headers, body = call env.merge("PATH_INFO" => '/bar')
|
853
|
-
[status, headers, body.map(&:upcase)]
|
854
|
-
end
|
855
|
-
|
856
|
-
get '/bar' do
|
857
|
-
"bar"
|
858
|
-
end
|
859
|
-
|
860
|
-
Notá que en el ejemplo anterior, es conveniente mover <tt>"bar"</tt> a un
|
861
|
-
helper, y llamarlo desde <tt>/foo</tt> y <tt>/bar</tt>. Así, vas a simplificar
|
862
|
-
las pruebas y a mejorar el rendimiento.
|
863
|
-
|
864
|
-
Si querés que la petición se envíe a la misma instancia de la aplicación en
|
865
|
-
lugar de a otra, usá <tt>call!</tt> en lugar de <tt>call</tt>.
|
866
|
-
|
867
|
-
En la especificación de Rack podés encontrar más información sobre
|
868
|
-
<tt>call</tt>.
|
869
|
-
|
870
|
-
=== Asignando el Código de Estado, los Encabezados y el Cuerpo de una Respuesta
|
871
|
-
|
872
|
-
Es posible, y se recomienda, asignar el código de estado y el cuerpo de una
|
873
|
-
respuesta con el valor de retorno de una ruta. De cualquier manera, en varios
|
874
|
-
escenarios, puede que sea conveniente asignar el cuerpo en un punto arbitrario
|
875
|
-
del flujo de ejecución con el método +body+. A partir de ahí, podés usar ese
|
876
|
-
mismo método para acceder al cuerpo de la respuesta:
|
877
|
-
|
878
|
-
get '/foo' do
|
879
|
-
body "bar"
|
880
|
-
end
|
881
|
-
|
882
|
-
after do
|
883
|
-
puts body
|
884
|
-
end
|
885
|
-
|
886
|
-
También es posible pasarle un bloque a +body+, que será ejecutado por el Rack
|
887
|
-
handler (podés usar esto para implementar streaming, mirá "Valores de retorno").
|
888
|
-
|
889
|
-
De manera similar, también podés asignar el código de estado y encabezados:
|
890
|
-
|
891
|
-
get '/foo' do
|
892
|
-
status 418
|
893
|
-
headers \
|
894
|
-
"Allow" => "BREW, POST, GET, PROPFIND, WHEN",
|
895
|
-
"Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
|
896
|
-
body "I'm a tea pot!"
|
897
|
-
end
|
898
|
-
|
899
|
-
También, al igual que +body+, tanto +status+ como +headers+ pueden utilizarse
|
900
|
-
para obtener sus valores cuando no se les pasa argumentos.
|
901
|
-
|
902
|
-
=== Streaming De Respuestas
|
903
|
-
|
904
|
-
A veces vas a querer empezar a enviar la respuesta a pesar de que todavía no
|
905
|
-
terminaste de generar su cuerpo. También es posible que, en algunos casos,
|
906
|
-
quieras seguir enviando información hasta que el cliente cierre la conexión.
|
907
|
-
Cuando esto ocurra, el +stream+ helper te va a ser de gran ayuda:
|
908
|
-
|
909
|
-
get '/' do
|
910
|
-
stream do |out|
|
911
|
-
out << "Esto va a ser legen -\n"
|
912
|
-
sleep 0.5
|
913
|
-
out << " (esperalo) \n"
|
914
|
-
sleep 1
|
915
|
-
out << "- dario!\n"
|
916
|
-
end
|
917
|
-
end
|
918
|
-
|
919
|
-
Podés implementar APIs de streaming,
|
920
|
-
{Server-Sent Events}[http://dev.w3.org/html5/eventsource/] y puede ser usado
|
921
|
-
como base para {WebSockets}[http://es.wikipedia.org/wiki/WebSockets]. También
|
922
|
-
puede ser usado para incrementar el throughput si solo una parte del contenido
|
923
|
-
depende de un recurso lento.
|
924
|
-
|
925
|
-
Hay que tener en cuenta que el comportamiento del streaming, especialmente el
|
926
|
-
número de peticiones concurrentes, depende del servidor web utilizado para
|
927
|
-
servir la aplicación. Puede que algunos servidores, como es el caso de
|
928
|
-
WEBRick, no soporten streaming directamente, así el cuerpo de la respuesta será
|
929
|
-
enviado completamente de una vez cuando el bloque pasado a +stream+ finalice su
|
930
|
-
ejecución. Si estás usando Shotgun, el streaming no va a funcionar.
|
931
|
-
|
932
|
-
Cuando se pasa +keep_open+ como parámetro, no se va a enviar el mensaje
|
933
|
-
+close+ al objeto de stream. Queda en vos cerrarlo en el punto de ejecución
|
934
|
-
que quieras. Nuevamente, hay que tener en cuenta que este comportamiento es
|
935
|
-
posible solo en servidores que soporten eventos, como Thin o Rainbows. El
|
936
|
-
resto de los servidores van a cerrar el stream de todos modos:
|
937
|
-
|
938
|
-
set :server, :thin
|
939
|
-
conexiones = []
|
940
|
-
|
941
|
-
get '/' do
|
942
|
-
# mantenemos abierto el stream
|
943
|
-
stream(:keep_open) { |salida| conexiones << salida }
|
944
|
-
end
|
945
|
-
|
946
|
-
post '/' do
|
947
|
-
# escribimos a todos los streams abiertos
|
948
|
-
conexiones.each { |salida| salida << params[:mensaje] << "\n" }
|
949
|
-
"mensaje enviado"
|
950
|
-
end
|
951
|
-
|
952
|
-
=== Log (Registro)
|
953
|
-
|
954
|
-
En el ámbito de la petición, el helper +logger+ (registrador) expone
|
955
|
-
una instancia de +Logger+:
|
956
|
-
|
957
|
-
get '/' do
|
958
|
-
logger.info "cargando datos"
|
959
|
-
# ...
|
960
|
-
end
|
961
|
-
|
962
|
-
Este logger tiene en cuenta la configuración de logueo de tu Rack
|
963
|
-
handler. Si el logueo está desactivado, este método va a devolver un
|
964
|
-
objeto que se comporta como un logger pero que en realidad no hace
|
965
|
-
nada. Así, no vas a tener que preocuparte por esta situación.
|
966
|
-
|
967
|
-
Tené en cuenta que el logueo está habilitado por defecto únicamente
|
968
|
-
para <tt>Sinatra::Application</tt>. Si heredaste de
|
969
|
-
<tt>Sinatra::Base</tt>, probablemente quieras habilitarlo manualmente:
|
970
|
-
|
971
|
-
class MiApp < Sinatra::Base
|
972
|
-
configure :production, :development do
|
973
|
-
enable :logging
|
974
|
-
end
|
975
|
-
end
|
976
|
-
|
977
|
-
Para evitar que se inicialice cualquier middleware de logging, configurá
|
978
|
-
+logging+ a +nil+. Tené en cuenta que, cuando hagas esto, +logger+ va a
|
979
|
-
devolver +nil+. Un caso común es cuando querés usar tu propio logger. Sinatra
|
980
|
-
va a usar lo que encuentre en <tt>env['rack.logger']</tt>.
|
981
|
-
|
982
|
-
=== Tipos Mime
|
983
|
-
|
984
|
-
Cuando usás <tt>send_file</tt> o archivos estáticos tal vez tengas tipos mime
|
985
|
-
que Sinatra no entiende. Usá +mime_type+ para registrarlos a través de la
|
986
|
-
extensión de archivo:
|
987
|
-
|
988
|
-
configure do
|
989
|
-
mime_type :foo, 'text/foo'
|
990
|
-
end
|
991
|
-
|
992
|
-
También lo podés usar con el ayudante +content_type+:
|
993
|
-
|
994
|
-
get '/' do
|
995
|
-
content_type :foo
|
996
|
-
"foo foo foo"
|
997
|
-
end
|
998
|
-
|
999
|
-
=== Generando URLs
|
1000
|
-
|
1001
|
-
Para generar URLs deberías usar el método +url+. Por ejemplo, en Haml:
|
1002
|
-
|
1003
|
-
%a{:href => url('/foo')} foo
|
1004
|
-
|
1005
|
-
Tiene en cuenta proxies inversos y encaminadores de Rack, si están presentes.
|
1006
|
-
|
1007
|
-
Este método también puede invocarse mediante su alias +to+ (mirá un ejemplo
|
1008
|
-
a continuación).
|
1009
|
-
|
1010
|
-
=== Redirección del Navegador
|
1011
|
-
|
1012
|
-
Podés redireccionar al navegador con el método +redirect+:
|
1013
|
-
|
1014
|
-
get '/foo' do
|
1015
|
-
redirect to('/bar')
|
1016
|
-
end
|
1017
|
-
|
1018
|
-
Cualquier parámetro adicional se utiliza de la misma manera que los argumentos
|
1019
|
-
pasados a +halt+:
|
1020
|
-
|
1021
|
-
redirect to('/bar'), 303
|
1022
|
-
redirect 'http://google.com', 'te confundiste de lugar, compañero'
|
1023
|
-
|
1024
|
-
También podés redireccionar fácilmente de vuelta hacia la página desde donde
|
1025
|
-
vino el usuario con +redirect back+:
|
1026
|
-
|
1027
|
-
get '/foo' do
|
1028
|
-
"<a href='/bar'>hacer algo</a>"
|
1029
|
-
end
|
1030
|
-
|
1031
|
-
get '/bar' do
|
1032
|
-
hacer_algo
|
1033
|
-
redirect back
|
1034
|
-
end
|
1035
|
-
|
1036
|
-
Para pasar argumentos con una redirección, podés agregarlos a la cadena de
|
1037
|
-
búsqueda:
|
1038
|
-
|
1039
|
-
redirect to('/bar?suma=42')
|
1040
|
-
|
1041
|
-
O usar una sesión:
|
1042
|
-
|
1043
|
-
enable :sessions
|
1044
|
-
|
1045
|
-
get '/foo' do
|
1046
|
-
session[:secreto] = 'foo'
|
1047
|
-
redirect to('/bar')
|
1048
|
-
end
|
1049
|
-
|
1050
|
-
get '/bar' do
|
1051
|
-
session[:secreto]
|
1052
|
-
end
|
1053
|
-
|
1054
|
-
=== Cache Control
|
1055
|
-
|
1056
|
-
Asignar tus encabezados correctamente es el cimiento para realizar un cacheo
|
1057
|
-
HTTP correcto.
|
1058
|
-
|
1059
|
-
Podés asignar el encabezado Cache-Control fácilmente:
|
1060
|
-
|
1061
|
-
get '/' do
|
1062
|
-
cache_control :public
|
1063
|
-
"cachealo!"
|
1064
|
-
end
|
1065
|
-
|
1066
|
-
Pro tip: configurar el cacheo en un filtro +before+:
|
1067
|
-
|
1068
|
-
before do
|
1069
|
-
cache_control :public, :must_revalidate, :max_age => 60
|
1070
|
-
end
|
1071
|
-
|
1072
|
-
Si estás usando el helper +expires+ para definir el encabezado correspondiente,
|
1073
|
-
<tt>Cache-Control</tt> se va a definir automáticamente:
|
1074
|
-
|
1075
|
-
before do
|
1076
|
-
expires 500, :public, :must_revalidate
|
1077
|
-
end
|
1078
|
-
|
1079
|
-
Para usar cachés adecuadamente, deberías considerar usar +etag+ o
|
1080
|
-
+last_modified+. Es recomendable que llames a estos helpers *antes* de hacer
|
1081
|
-
cualquier trabajo pesado, ya que van a enviar la respuesta inmediatamente si
|
1082
|
-
el cliente ya tiene la versión actual en su caché:
|
1083
|
-
|
1084
|
-
get '/articulo/:id' do
|
1085
|
-
@articulo = Articulo.find params[:id]
|
1086
|
-
last_modified @articulo.updated_at
|
1087
|
-
etag @articulo.sha1
|
1088
|
-
erb :articulo
|
1089
|
-
end
|
1090
|
-
|
1091
|
-
También es posible usar una
|
1092
|
-
{weak ETag}[http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation]:
|
1093
|
-
|
1094
|
-
etag @articulo.sha1, :weak
|
1095
|
-
|
1096
|
-
Estos helpers no van a cachear nada por vos, sino que van a facilitar la
|
1097
|
-
información necesaria para poder hacerlo. Si estás buscando soluciones rápidas
|
1098
|
-
de cacheo con proxys inversos, mirá
|
1099
|
-
{rack-cache}[https://github.com/rtomayko/rack-cache]:
|
1100
|
-
|
1101
|
-
require "rack/cache"
|
1102
|
-
require "sinatra"
|
1103
|
-
|
1104
|
-
use Rack::Cache
|
1105
|
-
|
1106
|
-
get '/' do
|
1107
|
-
cache_control :public, :max_age => 36000
|
1108
|
-
sleep 5
|
1109
|
-
"hola"
|
1110
|
-
end
|
1111
|
-
|
1112
|
-
Usá la configuración <tt>:static_cache_control</tt> para agregar el encabezado
|
1113
|
-
<tt>Cache-Control</tt> a archivos estáticos (ver la sección de configuración
|
1114
|
-
para más detalles).
|
1115
|
-
|
1116
|
-
De acuerdo con la RFC 2616 tu aplicación debería comportarse diferente si a las
|
1117
|
-
cabeceras If-Match o If-None-Match se le asigna el valor <tt>*</tt> cuando el
|
1118
|
-
recurso solicitado ya existe. Sinatra asume para peticiones seguras (como get)
|
1119
|
-
e idempotentes (como put) que el recurso existe, mientras que para el resto
|
1120
|
-
(como post), que no. Podes cambiar este comportamiento con la opción
|
1121
|
-
<tt>:new_resource</tt>:
|
1122
|
-
|
1123
|
-
get '/crear' do
|
1124
|
-
etag '', :new_resource => true
|
1125
|
-
Articulo.create
|
1126
|
-
erb :nuevo_articulo
|
1127
|
-
end
|
1128
|
-
|
1129
|
-
Si querés seguir usando una weak ETag, indicalo con la opción <tt>:kind</tt>:
|
1130
|
-
|
1131
|
-
etag '', :new_resource => true, :kind => :weak
|
1132
|
-
|
1133
|
-
=== Enviando Archivos
|
1134
|
-
|
1135
|
-
Para enviar archivos, podés usar el método <tt>send_file</tt>:
|
1136
|
-
|
1137
|
-
get '/' do
|
1138
|
-
send_file 'foo.png'
|
1139
|
-
end
|
1140
|
-
|
1141
|
-
Además acepta un par de opciones:
|
1142
|
-
|
1143
|
-
send_file 'foo.png', :type => :jpg
|
1144
|
-
|
1145
|
-
Estas opciones son:
|
1146
|
-
|
1147
|
-
[filename]
|
1148
|
-
nombre del archivo devuelto, por defecto es el nombre real del archivo.
|
1149
|
-
|
1150
|
-
[last_modified]
|
1151
|
-
valor para el encabezado Last-Modified, por defecto toma el mtime del archivo.
|
1152
|
-
|
1153
|
-
[type]
|
1154
|
-
el content type que se va a utilizar, si no está presente se intenta adivinar
|
1155
|
-
a partir de la extensión del archivo.
|
1156
|
-
|
1157
|
-
[disposition]
|
1158
|
-
se utiliza para el encabezado Content-Disposition, y puede tomar alguno de los
|
1159
|
-
siguientes valores: +nil+ (por defecto), <tt>:attachment</tt> e
|
1160
|
-
<tt>:inline</tt>
|
1161
|
-
|
1162
|
-
[length]
|
1163
|
-
encabezado Content-Length, por defecto toma el tamaño del archivo.
|
1164
|
-
|
1165
|
-
[status]
|
1166
|
-
código de estado devuelto. Resulta útil al enviar un archivo estático como una
|
1167
|
-
página de error.
|
1168
|
-
|
1169
|
-
Si el Rack handler lo soporta, se intentará no transmitir directamente desde el
|
1170
|
-
proceso de Ruby. Si usás este método, Sinatra se va a encargar automáticamente
|
1171
|
-
peticiones de rango.
|
1172
|
-
|
1173
|
-
=== Accediendo al objeto de la petición
|
1174
|
-
|
1175
|
-
El objeto de la petición entrante puede ser accedido desde el nivel de la
|
1176
|
-
petición (filtros, rutas y manejadores de errores) a través del método
|
1177
|
-
<tt>request</tt>:
|
1178
|
-
|
1179
|
-
# app corriendo en http://ejemplo.com/ejemplo
|
1180
|
-
get '/foo' do
|
1181
|
-
t = %w[text/css text/html application/javascript]
|
1182
|
-
request.accept # ['text/html', '*/*']
|
1183
|
-
request.accept? 'text/xml' # true
|
1184
|
-
request.preferred_type(t) # 'text/html'
|
1185
|
-
request.body # cuerpo de la petición enviado por el cliente (ver más abajo)
|
1186
|
-
request.scheme # "http"
|
1187
|
-
request.script_name # "/ejemplo"
|
1188
|
-
request.path_info # "/foo"
|
1189
|
-
request.port # 80
|
1190
|
-
request.request_method # "GET"
|
1191
|
-
request.query_string # ""
|
1192
|
-
request.content_length # longitud de request.body
|
1193
|
-
request.media_type # tipo de medio de request.body
|
1194
|
-
request.host # "ejemplo.com"
|
1195
|
-
request.get? # true (hay métodos análogos para los otros verbos)
|
1196
|
-
request.form_data? # false
|
1197
|
-
request["UNA_CABECERA"] # valor de la cabecera UNA_CABECERA
|
1198
|
-
request.referrer # la referencia del cliente o '/'
|
1199
|
-
request.user_agent # user agent (usado por la condición :agent)
|
1200
|
-
request.cookies # hash de las cookies del browser
|
1201
|
-
request.xhr? # es una petición ajax?
|
1202
|
-
request.url # "http://ejemplo.com/ejemplo/foo"
|
1203
|
-
request.path # "/ejemplo/foo"
|
1204
|
-
request.ip # dirección IP del cliente
|
1205
|
-
request.secure? # false (sería true sobre ssl)
|
1206
|
-
request.forwarded? # true (si se está corriendo atrás de un proxy inverso)
|
1207
|
-
requuest.env # hash de entorno directamente entregado por Rack
|
1208
|
-
end
|
1209
|
-
|
1210
|
-
Algunas opciones, como <tt>script_name</tt> o <tt>path_info</tt> pueden
|
1211
|
-
también ser escritas:
|
1212
|
-
|
1213
|
-
before { request.path_info = "/" }
|
1214
|
-
|
1215
|
-
get "/" do
|
1216
|
-
"todas las peticiones llegan acá"
|
1217
|
-
end
|
1218
|
-
|
1219
|
-
El objeto <tt>request.body</tt> es una instancia de IO o StringIO:
|
1220
|
-
|
1221
|
-
post "/api" do
|
1222
|
-
request.body.rewind # en caso de que alguien ya lo haya leído
|
1223
|
-
datos = JSON.parse request.body.read
|
1224
|
-
"Hola #{datos['nombre']}!"
|
1225
|
-
end
|
1226
|
-
|
1227
|
-
=== Archivos Adjuntos
|
1228
|
-
|
1229
|
-
Podés usar el método helper +attachment+ para indicarle al navegador que
|
1230
|
-
almacene la respuesta en el disco en lugar de mostrarla en pantalla:
|
1231
|
-
|
1232
|
-
get '/' do
|
1233
|
-
attachment
|
1234
|
-
"guardalo!"
|
1235
|
-
end
|
1236
|
-
|
1237
|
-
También podés pasarle un nombre de archivo:
|
1238
|
-
|
1239
|
-
get '/' do
|
1240
|
-
attachment "info.txt"
|
1241
|
-
"guardalo!"
|
1242
|
-
end
|
1243
|
-
|
1244
|
-
=== Fecha y Hora
|
1245
|
-
|
1246
|
-
Sinatra pone a tu disposición el helper +time_for+, que genera un objeto +Time+
|
1247
|
-
a partir del valor que recibe como argumento. Este valor puede ser un
|
1248
|
-
+String+, pero también es capaz de convertir objetos +DateTime+, +Date+ y de
|
1249
|
-
otras clases similares:
|
1250
|
-
|
1251
|
-
get '/' do
|
1252
|
-
pass if Time.now > time_for('Dec 23, 2012')
|
1253
|
-
"todavía hay tiempo"
|
1254
|
-
end
|
1255
|
-
|
1256
|
-
Este método es usado internamente por métodos como +expires+ y +last_modified+,
|
1257
|
-
entre otros. Por lo tanto, es posible extender el comportamiento de estos
|
1258
|
-
métodos sobreescribiendo +time_for+ en tu aplicación:
|
1259
|
-
|
1260
|
-
helpers do
|
1261
|
-
def time_for(value)
|
1262
|
-
case value
|
1263
|
-
when :ayer then Time.now - 24*60*60
|
1264
|
-
when :mañana then Time.now + 24*60*60
|
1265
|
-
else super
|
1266
|
-
end
|
1267
|
-
end
|
1268
|
-
end
|
1269
|
-
|
1270
|
-
get '/' do
|
1271
|
-
last_modified :ayer
|
1272
|
-
expires :mañana
|
1273
|
-
"hola"
|
1274
|
-
end
|
1275
|
-
|
1276
|
-
=== Buscando los Archivos de las Plantillas
|
1277
|
-
|
1278
|
-
El helper <tt>find_template</tt> se utiliza para encontrar los archivos de las
|
1279
|
-
plantillas que se van a renderizar:
|
1280
|
-
|
1281
|
-
find_template settings.views, 'foo', Tilt[:haml] do |archivo|
|
1282
|
-
puts "podría ser #{archivo}"
|
1283
|
-
end
|
1284
|
-
|
1285
|
-
Si bien esto no es muy útil, lo interesante es que podés sobreescribir este
|
1286
|
-
método, y así enganchar tu propio mecanismo de búsqueda. Por ejemplo, para
|
1287
|
-
poder utilizar más de un directorio de vistas:
|
1288
|
-
|
1289
|
-
set :views, ['vistas', 'plantillas']
|
1290
|
-
|
1291
|
-
helpers do
|
1292
|
-
def find_template(views, name, engine, &block)
|
1293
|
-
Array(views).each { |v| super(v, name, engine, &block) }
|
1294
|
-
end
|
1295
|
-
end
|
1296
|
-
|
1297
|
-
Otro ejemplo consiste en usar directorios diferentes para los distintos motores
|
1298
|
-
de renderizado:
|
1299
|
-
|
1300
|
-
set :views, :sass => 'vistas/sass', :haml => 'plantillas', :defecto => 'vistas'
|
1301
|
-
|
1302
|
-
helpers do
|
1303
|
-
def find_template(views, name, engine, &block)
|
1304
|
-
_, folder = views.detect { |k,v| engine == Tilt[k] }
|
1305
|
-
folder ||= views[:defecto]
|
1306
|
-
super(folder, name, engine, &block)
|
1307
|
-
end
|
1308
|
-
end
|
1309
|
-
|
1310
|
-
¡Es muy fácil convertir estos ejemplos en una extensión y compartirla!.
|
1311
|
-
|
1312
|
-
Notá que <tt>find_template</tt> no verifica si un archivo existe realmente, sino
|
1313
|
-
que llama al bloque que recibe para cada path posible. Esto no representa un
|
1314
|
-
problema de rendimiento debido a que +render+ va a usar +break+ ni bien
|
1315
|
-
encuentre un archivo que exista. Además, las ubicaciones de las plantillas (y
|
1316
|
-
su contenido) se cachean cuando no estás en el modo de desarrollo. Es bueno
|
1317
|
-
tener en cuenta lo anteiror si escribís un método medio loco.
|
1318
|
-
|
1319
|
-
== Configuración
|
1320
|
-
|
1321
|
-
Ejecutar una vez, en el inicio, en cualquier entorno:
|
1322
|
-
|
1323
|
-
configure do
|
1324
|
-
# asignando una opción
|
1325
|
-
set :opcion, 'valor'
|
1326
|
-
|
1327
|
-
# asignando varias opciones
|
1328
|
-
set :a => 1, :b => 2
|
1329
|
-
|
1330
|
-
# atajo para `set :opcion, true`
|
1331
|
-
enable :opcion
|
1332
|
-
|
1333
|
-
# atajo para `set :opcion, false`
|
1334
|
-
disable :opcion
|
1335
|
-
|
1336
|
-
# también podés tener configuraciones dinámicas usando bloques
|
1337
|
-
set(:css_dir) { File.join(views, 'css') }
|
1338
|
-
end
|
1339
|
-
|
1340
|
-
Ejecutar únicamente cuando el entorno (la variable de entorno RACK_ENV) es
|
1341
|
-
<tt>:production</tt>:
|
1342
|
-
|
1343
|
-
configure :production do
|
1344
|
-
...
|
1345
|
-
end
|
1346
|
-
|
1347
|
-
Ejecutar cuando el entorno es <tt>:production</tt> o <tt>:test</tt>:
|
1348
|
-
|
1349
|
-
configure :production, :test do
|
1350
|
-
...
|
1351
|
-
end
|
1352
|
-
|
1353
|
-
Podés acceder a estas opciones utilizando el método <tt>settings</tt>:
|
1354
|
-
|
1355
|
-
configure do
|
1356
|
-
set :foo, 'bar'
|
1357
|
-
end
|
1358
|
-
|
1359
|
-
get '/' do
|
1360
|
-
settings.foo? # => true
|
1361
|
-
settings.foo # => 'bar'
|
1362
|
-
...
|
1363
|
-
end
|
1364
|
-
|
1365
|
-
=== Configurando la Protección de Ataques
|
1366
|
-
|
1367
|
-
Sinatra usa {Rack::Protection}[https://github.com/rkh/rack-protection#readme]
|
1368
|
-
para defender a tu aplicación de los ataques más comunes. Si por algún motivo,
|
1369
|
-
querés desactivar esta funcionalidad, podés hacerlo como se indica a
|
1370
|
-
continuación (tené en cuenta que tu aplicación va a quedar expuesta a un
|
1371
|
-
montón de vulnerabilidades bien conocidas):
|
1372
|
-
|
1373
|
-
disable :protection
|
1374
|
-
|
1375
|
-
También es posible desactivar una única capa de defensa:
|
1376
|
-
|
1377
|
-
set :protection, :except => :path_traversal
|
1378
|
-
|
1379
|
-
O varias:
|
1380
|
-
|
1381
|
-
set :protection, :except => [:path_traversal, :session_hijacking]
|
1382
|
-
|
1383
|
-
=== Configuraciones Disponibles
|
1384
|
-
|
1385
|
-
[absolute_redirects] si está deshabilitada, Sinatra va a permitir
|
1386
|
-
redirecciones relativas, sin embargo, como consecuencia
|
1387
|
-
de esto, va a dejar de cumplir con el RFC 2616 (HTTP
|
1388
|
-
1.1), que solamente permite redirecciones absolutas.
|
1389
|
-
|
1390
|
-
Activalo si tu apliación está corriendo atrás de un proxy
|
1391
|
-
inverso que no se ha configurado adecuadamente. Notá que
|
1392
|
-
el helper +url+ va a seguir produciendo URLs absolutas, a
|
1393
|
-
menos que le pasés +false+ como segundo parámetro.
|
1394
|
-
|
1395
|
-
Deshabilitada por defecto.
|
1396
|
-
|
1397
|
-
[add_charsets] tipos mime a los que el helper <tt>content_type</tt> les
|
1398
|
-
añade automáticamente el charset.
|
1399
|
-
|
1400
|
-
En general, no deberías asignar directamente esta opción,
|
1401
|
-
sino añadirle los charsets que quieras:
|
1402
|
-
|
1403
|
-
settings.add_charsets << "application/foobar"
|
1404
|
-
|
1405
|
-
[app_file] path del archivo principal de la aplicación, se utiliza
|
1406
|
-
para detectar la raíz del proyecto, el directorio de las
|
1407
|
-
vistas y el público, así como las plantillas inline.
|
1408
|
-
|
1409
|
-
[bind] dirección IP que utilizará el servidor integrado (por
|
1410
|
-
defecto: 0.0.0.0).
|
1411
|
-
|
1412
|
-
[default_encoding] encoding utilizado cuando el mismo se desconoce (por
|
1413
|
-
defecto <tt>"utf-8"</tt>).
|
1414
|
-
|
1415
|
-
[dump_errors] mostrar errores en el log.
|
1416
|
-
|
1417
|
-
[environment] entorno actual, por defecto toma el valor de
|
1418
|
-
<tt>ENV['RACK_ENV']</tt>, o <tt>"development"</tt> si no
|
1419
|
-
está disponible.
|
1420
|
-
|
1421
|
-
[logging] define si se utiliza el logger.
|
1422
|
-
|
1423
|
-
[lock] coloca un lock alrededor de cada petición, procesando
|
1424
|
-
solamente una por proceso.
|
1425
|
-
|
1426
|
-
Habilitá esta opción si tu aplicación no es thread-safe.
|
1427
|
-
Se encuentra deshabilitada por defecto.
|
1428
|
-
|
1429
|
-
[method_override] utiliza el parámetro <tt>_method</tt> para permtir
|
1430
|
-
formularios put/delete en navegadores que no los
|
1431
|
-
soportan.
|
1432
|
-
|
1433
|
-
[port] puerto en el que escuchará el servidor integrado.
|
1434
|
-
|
1435
|
-
[prefixed_redirects] define si inserta <tt>request.script_name</tt> en las
|
1436
|
-
redirecciones cuando no se proporciona un path absoluto.
|
1437
|
-
De esta manera, cuando está habilitada,
|
1438
|
-
<tt>redirect '/foo'</tt> se comporta de la misma manera
|
1439
|
-
que <tt>redirect to('/foo')</tt>. Se encuentra
|
1440
|
-
deshabilitada por defecto.
|
1441
|
-
|
1442
|
-
[protection] define si deben activarse las protecciones para los
|
1443
|
-
ataques web más comunes. Para más detalles mirá la
|
1444
|
-
sección sobre la configuración de protección de ataques
|
1445
|
-
más arriba.
|
1446
|
-
|
1447
|
-
[public_dir] alias para <tt>public_folder</tt>, que se encuentra a
|
1448
|
-
continuación.
|
1449
|
-
|
1450
|
-
[public_folder] path del directorio desde donde se sirven los archivos
|
1451
|
-
públicos. Solo se utiliza cuando se sirven archivos
|
1452
|
-
estáticos (ver la opción <tt>static</tt>). Si no
|
1453
|
-
está presente, se infiere del valor de la opción
|
1454
|
-
<tt>app_file</tt>.
|
1455
|
-
|
1456
|
-
[reload_templates] define si se recargan las plantillas entre peticiones.
|
1457
|
-
|
1458
|
-
Se encuentra activado en el entorno de desarrollo.
|
1459
|
-
|
1460
|
-
[root] path del directorio raíz del proyecto. Si no está
|
1461
|
-
presente, se infiere del valor de la opción
|
1462
|
-
<tt>app_file</tt>.
|
1463
|
-
|
1464
|
-
[raise_errors] elevar excepciones (detiene la aplicación). Se
|
1465
|
-
encuentra activada por defecto cuando el valor de
|
1466
|
-
<tt>environment</tt> es <tt>"test"</tt>. En caso
|
1467
|
-
contrario estará desactivada.
|
1468
|
-
|
1469
|
-
[run] cuando está habilitada, Sinatra se va a encargar de
|
1470
|
-
iniciar el servidor web, no la habilités cuando estés
|
1471
|
-
usando rackup o algún otro medio.
|
1472
|
-
|
1473
|
-
[running] indica si el servidor integrado está ejecutandose, ¡no
|
1474
|
-
cambiés esta configuración!.
|
1475
|
-
|
1476
|
-
[server] servidor, o lista de servidores, para usar como servidor
|
1477
|
-
integrado. Por defecto: ['thin', 'mongrel', 'webrick'],
|
1478
|
-
el orden establece la prioridad.
|
1479
|
-
|
1480
|
-
[sessions] habilita el soporte de sesiones basadas en cookies a
|
1481
|
-
través de <tt>Rack::Session::Cookie</tt>. Ver la
|
1482
|
-
sección 'Usando Sesiones' para más información.
|
1483
|
-
|
1484
|
-
[show_exceptions] muestra un stack trace en el navegador cuando ocurre una
|
1485
|
-
excepción. Se encuentra activada por defecto cuando el
|
1486
|
-
valor de <tt>environment</tt> es <tt>"development"</tt>.
|
1487
|
-
En caso contrario estará desactivada.
|
1488
|
-
|
1489
|
-
[static] define si Sinatra debe encargarse de servir archivos
|
1490
|
-
estáticos.
|
1491
|
-
|
1492
|
-
Deshabilitala cuando usés un servidor capaz de
|
1493
|
-
hacerlo por sí solo, porque mejorará el
|
1494
|
-
rendimiento. Se encuentra habilitada por
|
1495
|
-
defecto en el estilo clásico y desactivado en el
|
1496
|
-
el modular.
|
1497
|
-
|
1498
|
-
[static_cache_control] cuando Sinatra está sirviendo archivos estáticos, y
|
1499
|
-
está opción está habilitada, les va a agregar encabezados
|
1500
|
-
<tt>Cache-Control</tt> a las respuestas. Para esto
|
1501
|
-
utiliza el helper +cache_control+. Se encuentra
|
1502
|
-
deshabilitada por defecto. Notar que es necesario
|
1503
|
-
utilizar un array cuando se asignan múltiples valores:
|
1504
|
-
<tt>set :static_cache_control, [:public, :max_age => 300]</tt>.
|
1505
|
-
|
1506
|
-
[views] path del directorio de las vistas. Si no está presente,
|
1507
|
-
se infiere del valor de la opción <tt>app_file</tt>.
|
1508
|
-
|
1509
|
-
== Entornos
|
1510
|
-
|
1511
|
-
Existen tres entornos (+environments+) predefinidos: <tt>development</tt>,
|
1512
|
-
<tt>production</tt> y <tt>test</tt>. El entorno por defecto es
|
1513
|
-
<tt>development</tt> y tiene algunas particularidades:
|
1514
|
-
|
1515
|
-
* Se recargan las plantillas entre una petición y la siguiente, a diferencia
|
1516
|
-
de <tt>production</tt> y <tt>test</tt>, donde se cachean.
|
1517
|
-
* Se instalan manejadores de errores <tt>not_found</tt> y <tt>error</tt>
|
1518
|
-
especiales que muestran un stack trace en el navegador cuando son disparados.
|
1519
|
-
|
1520
|
-
Para utilizar alguno de los otros entornos puede asignarse el valor
|
1521
|
-
correspondiente a la variable de entorno +RACK_ENV+, o bien utilizar la opción
|
1522
|
-
<tt>-e</tt> al ejecutar la aplicación:
|
1523
|
-
|
1524
|
-
ruby mi_app.rb -e <ENTORNO>
|
1525
|
-
|
1526
|
-
Los métodos +development?+, +test?+ y +production?+ te permiten conocer el
|
1527
|
-
entorno actual.
|
1528
|
-
|
1529
|
-
== Manejo de Errores
|
1530
|
-
|
1531
|
-
Los manejadores de errores se ejecutan dentro del mismo contexto que las rutas
|
1532
|
-
y los filtros +before+, lo que significa que podés usar, por ejemplo,
|
1533
|
-
<tt>haml</tt>, <tt>erb</tt>, <tt>halt</tt>, etc.
|
1534
|
-
|
1535
|
-
=== No encontrado <em>(Not Found)</em>
|
1536
|
-
|
1537
|
-
Cuando se eleva una excepción <tt>Sinatra::NotFound</tt>, o el código de
|
1538
|
-
estado de la respuesta es 404, el manejador <tt>not_found</tt> es invocado:
|
1539
|
-
|
1540
|
-
not_found do
|
1541
|
-
'No existo'
|
1542
|
-
end
|
1543
|
-
|
1544
|
-
=== Error
|
1545
|
-
|
1546
|
-
El manejador +error+ es invocado cada vez que una excepción es elevada
|
1547
|
-
desde un bloque de ruta o un filtro. El objeto de la excepción se puede
|
1548
|
-
obtener de la variable Rack <tt>sinatra.error</tt>:
|
1549
|
-
|
1550
|
-
error do
|
1551
|
-
'Disculpá, ocurrió un error horrible - ' + env['sinatra.error'].name
|
1552
|
-
end
|
1553
|
-
|
1554
|
-
Errores personalizados:
|
1555
|
-
|
1556
|
-
error MiErrorPersonalizado do
|
1557
|
-
'Lo que pasó fue...' + env['sinatra.error'].message
|
1558
|
-
end
|
1559
|
-
|
1560
|
-
Entonces, si pasa esto:
|
1561
|
-
|
1562
|
-
get '/' do
|
1563
|
-
raise MiErrorPersonalizado, 'algo malo'
|
1564
|
-
end
|
1565
|
-
|
1566
|
-
Obtenés esto:
|
1567
|
-
|
1568
|
-
Lo que pasó fue... algo malo
|
1569
|
-
|
1570
|
-
También, podés instalar un manejador de errores para un código de estado:
|
1571
|
-
|
1572
|
-
error 403 do
|
1573
|
-
'Acceso prohibido'
|
1574
|
-
end
|
1575
|
-
|
1576
|
-
get '/secreto' do
|
1577
|
-
403
|
1578
|
-
end
|
1579
|
-
|
1580
|
-
O un rango:
|
1581
|
-
|
1582
|
-
error 400..510 do
|
1583
|
-
'Boom'
|
1584
|
-
end
|
1585
|
-
|
1586
|
-
Sinatra instala manejadores <tt>not_found</tt> y <tt>error</ttt> especiales
|
1587
|
-
cuando se ejecuta dentro del entorno de desarrollo "development".
|
1588
|
-
|
1589
|
-
== Rack Middleware
|
1590
|
-
|
1591
|
-
Sinatra corre sobre Rack[http://rack.rubyforge.org/], una interfaz minimalista
|
1592
|
-
que es un estándar para frameworks webs escritos en Ruby. Una de las
|
1593
|
-
capacidades más interesantes de Rack para los desarrolladores de aplicaciones
|
1594
|
-
es el soporte de "middleware" -- componentes que se ubican entre el servidor y
|
1595
|
-
tu aplicación, supervisando y/o manipulando la petición/respuesta HTTP para
|
1596
|
-
proporcionar varios tipos de funcionalidades comunes.
|
1597
|
-
|
1598
|
-
Sinatra hace muy sencillo construir tuberías de Rack middleware a través del
|
1599
|
-
método top-level +use+:
|
1600
|
-
|
1601
|
-
require 'sinatra'
|
1602
|
-
require 'mi_middleware_personalizado'
|
1603
|
-
|
1604
|
-
use Rack::Lint
|
1605
|
-
use MiMiddlewarePersonalizado
|
1606
|
-
|
1607
|
-
get '/hola' do
|
1608
|
-
'Hola Mundo'
|
1609
|
-
end
|
1610
|
-
|
1611
|
-
Las semánticas de +use+ son idénticas a las definidas para el DSL
|
1612
|
-
Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] (más
|
1613
|
-
frecuentemente usado desde archivos rackup). Por ejemplo, el método +use+
|
1614
|
-
acepta argumentos múltiples/variables así como bloques:
|
1615
|
-
|
1616
|
-
use Rack::Auth::Basic do |nombre_de_usuario, password|
|
1617
|
-
nombre_de_usuario == 'admin' && password == 'secreto'
|
1618
|
-
end
|
1619
|
-
|
1620
|
-
Rack es distribuido con una variedad de middleware estándar para logging,
|
1621
|
-
debugging, enrutamiento URL, autenticación, y manejo de sesiones. Sinatra
|
1622
|
-
usa muchos de estos componentes automáticamente de acuerdo a su configuración
|
1623
|
-
para que típicamente no tengas que usarlas (con +use+) explícitamente.
|
1624
|
-
|
1625
|
-
Podés encontrar middleware útil en
|
1626
|
-
{rack}[https://github.com/rack/rack/tree/master/lib/rack],
|
1627
|
-
{rack-contrib}[https://github.com/rack/rack-contrib#readme],
|
1628
|
-
con {CodeRack}[http://coderack.org/] o en la
|
1629
|
-
{Rack wiki}[https://github.com/rack/rack/wiki/List-of-Middleware].
|
1630
|
-
|
1631
|
-
== Pruebas
|
1632
|
-
|
1633
|
-
Las pruebas para las aplicaciones Sinatra pueden ser escritas utilizando
|
1634
|
-
cualquier framework o librería de pruebas basada en Rack. Se recomienda usar
|
1635
|
-
{Rack::Test}[http://rdoc.info/github/brynary/rack-test/master/frames]:
|
1636
|
-
|
1637
|
-
require 'mi_app_sinatra'
|
1638
|
-
require 'test/unit'
|
1639
|
-
require 'rack/test'
|
1640
|
-
|
1641
|
-
class MiAppTest < Test::Unit::TestCase
|
1642
|
-
include Rack::Test::Methods
|
1643
|
-
|
1644
|
-
def app
|
1645
|
-
Sinatra::Application
|
1646
|
-
end
|
1647
|
-
|
1648
|
-
def test_mi_defecto
|
1649
|
-
get '/'
|
1650
|
-
assert_equal 'Hola Mundo!', last_response.body
|
1651
|
-
end
|
1652
|
-
|
1653
|
-
def test_con_parametros
|
1654
|
-
get '/saludar', :name => 'Franco'
|
1655
|
-
assert_equal 'Hola Frank!', last_response.body
|
1656
|
-
end
|
1657
|
-
|
1658
|
-
def test_con_entorno_rack
|
1659
|
-
get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
|
1660
|
-
assert_equal "Estás usando Songbird!", last_response.body
|
1661
|
-
end
|
1662
|
-
end
|
1663
|
-
|
1664
|
-
== Sinatra::Base - Middleware, Librerías, y Aplicaciones Modulares
|
1665
|
-
|
1666
|
-
Definir tu aplicación en el top-level funciona bien para micro-aplicaciones
|
1667
|
-
pero trae inconvenientes considerables a la hora de construir componentes
|
1668
|
-
reutilizables como Rack middleware, Rails metal, simple librerías con un
|
1669
|
-
componente de servidor, o incluso extensiones de Sinatra. El DSL de top-level
|
1670
|
-
asume una configuración apropiada para micro-aplicaciones (por ejemplo, un
|
1671
|
-
único archivo de aplicación, los directorios <tt>./public</tt> y
|
1672
|
-
<tt>./views</tt>, logging, página con detalles de excepción, etc.). Ahí es
|
1673
|
-
donde <tt>Sinatra::Base</tt> entra en el juego:
|
1674
|
-
|
1675
|
-
require 'sinatra/base'
|
1676
|
-
|
1677
|
-
class MiApp < Sinatra::Base
|
1678
|
-
set :sessions, true
|
1679
|
-
set :foo, 'bar'
|
1680
|
-
|
1681
|
-
get '/' do
|
1682
|
-
'Hola Mundo!'
|
1683
|
-
end
|
1684
|
-
end
|
1685
|
-
|
1686
|
-
Las subclases de <tt>Sinatra::Base</tt> tienen disponibles exactamente los
|
1687
|
-
mismos métodos que los provistos por el DSL de top-level. La mayoría de las
|
1688
|
-
aplicaciones top-level se pueden convertir en componentes
|
1689
|
-
<tt>Sinatra::Base</tt> con dos modificaciones:
|
1690
|
-
|
1691
|
-
* Tu archivo debe requerir <tt>sinatra/base</tt> en lugar de +sinatra+; de otra
|
1692
|
-
manera, todos los métodos del DSL de sinatra son importados dentro del
|
1693
|
-
espacio de nombres principal.
|
1694
|
-
* Poné las rutas, manejadores de errores, filtros y opciones de tu aplicación
|
1695
|
-
en una subclase de <tt>Sinatra::Base</tt>.
|
1696
|
-
|
1697
|
-
<tt>Sinatra::Base</tt> es una pizarra en blanco. La mayoría de las opciones están
|
1698
|
-
desactivadas por defecto, incluyendo el servidor incorporado. Mirá
|
1699
|
-
{Opciones y Configuraciones}[http://sinatra.github.com/configuration.html]
|
1700
|
-
para detalles sobre las opciones disponibles y su comportamiento.
|
1701
|
-
|
1702
|
-
=== Estilo Modular vs. Clásico
|
1703
|
-
|
1704
|
-
Contrariamente a la creencia popular, no hay nada de malo con el estilo clásico.
|
1705
|
-
Si se ajusta a tu aplicación, no es necesario que la cambies a una modular.
|
1706
|
-
|
1707
|
-
Las desventaja de usar el estilo clásico en lugar del modular consiste en que
|
1708
|
-
solamente podés tener una aplicación Sinatra por proceso Ruby. Si tenés
|
1709
|
-
planificado usar más, cambiá al estilo modular. Al mismo tiempo, tené en
|
1710
|
-
cuenta que no hay ninguna razón por la cuál no puedas mezclar los estilos
|
1711
|
-
clásico y modular.
|
1712
|
-
|
1713
|
-
A continuación se detallan las diferencias (sutiles) entre las configuraciones
|
1714
|
-
de ambos estilos:
|
1715
|
-
|
1716
|
-
Configuración Clásica Modular
|
1717
|
-
|
1718
|
-
app_file archivo que carga sinatra archivo con la subclase de Sinatra::Base
|
1719
|
-
run $0 == app_file false
|
1720
|
-
logging true false
|
1721
|
-
method_override true false
|
1722
|
-
inline_templates true false
|
1723
|
-
static true false
|
1724
|
-
|
1725
|
-
=== Sirviendo una Aplicación Modular
|
1726
|
-
|
1727
|
-
Las dos opciones más comunes para iniciar una aplicación modular son, iniciarla
|
1728
|
-
activamente con <tt>run!</tt>:
|
1729
|
-
|
1730
|
-
# mi_app.rb
|
1731
|
-
require 'sinatra/base'
|
1732
|
-
|
1733
|
-
class MiApp < Sinatra::Base
|
1734
|
-
# ... código de la app ...
|
1735
|
-
|
1736
|
-
# iniciar el servidor si el archivo fue ejecutado directamente
|
1737
|
-
run! if app_file == $0
|
1738
|
-
end
|
1739
|
-
|
1740
|
-
Iniciar con:
|
1741
|
-
|
1742
|
-
ruby mi_app.rb
|
1743
|
-
|
1744
|
-
O, con un archivo <tt>config.ru</tt>, que permite usar cualquier handler Rack:
|
1745
|
-
|
1746
|
-
# config.ru
|
1747
|
-
require './mi_app'
|
1748
|
-
run MiApp
|
1749
|
-
|
1750
|
-
Después ejecutar:
|
1751
|
-
|
1752
|
-
rackup -p 4567
|
1753
|
-
|
1754
|
-
=== Usando una Aplicación Clásica con un Archivo config.ru
|
1755
|
-
|
1756
|
-
Escribí el archivo de tu aplicación:
|
1757
|
-
|
1758
|
-
# app.rb
|
1759
|
-
require 'sinatra'
|
1760
|
-
|
1761
|
-
get '/' do
|
1762
|
-
'Hola mundo!'
|
1763
|
-
end
|
1764
|
-
|
1765
|
-
Y el <tt>config.ru</tt> correspondiente:
|
1766
|
-
|
1767
|
-
require './app'
|
1768
|
-
run Sinatra::Application
|
1769
|
-
|
1770
|
-
=== ¿Cuándo Usar config.ru?
|
1771
|
-
|
1772
|
-
Indicadores de que probablemente querés usar <tt>config.ru</tt>:
|
1773
|
-
|
1774
|
-
* Querés realizar el deploy con un hanlder Rack distinto (Passenger, Unicorn,
|
1775
|
-
Heroku, ...).
|
1776
|
-
* Querés usar más de una subclase de <tt>Sinatra::Base</tt>.
|
1777
|
-
* Querés usar Sinatra únicamente para middleware, pero no como un endpoint.
|
1778
|
-
|
1779
|
-
<b>No hay necesidad de utilizar un archivo <tt>config.ru</tt> exclusivamente
|
1780
|
-
porque tenés una aplicación modular, y no necesitás una aplicación modular para
|
1781
|
-
iniciarla con <tt>config.ru</tt>.</b>
|
1782
|
-
|
1783
|
-
=== Utilizando Sinatra como Middleware
|
1784
|
-
|
1785
|
-
Sinatra no solo es capaz de usar otro Rack middleware, sino que a su vez,
|
1786
|
-
cualquier aplicación Sinatra puede ser agregada delante de un endpoint Rack
|
1787
|
-
como middleware. Este endpoint puede ser otra aplicación Sinatra, o cualquier
|
1788
|
-
aplicación basada en Rack (Rails/Ramaze/Camping/...):
|
1789
|
-
|
1790
|
-
require 'sinatra/base'
|
1791
|
-
|
1792
|
-
class PantallaDeLogin < Sinatra::Base
|
1793
|
-
enable :sessions
|
1794
|
-
|
1795
|
-
get('/login') { haml :login }
|
1796
|
-
|
1797
|
-
post('/login') do
|
1798
|
-
if params[:nombre] == 'admin' && params[:password] == 'admin'
|
1799
|
-
session['nombre_de_usuario'] = params[:nombre]
|
1800
|
-
else
|
1801
|
-
redirect '/login'
|
1802
|
-
end
|
1803
|
-
end
|
1804
|
-
end
|
1805
|
-
|
1806
|
-
class MiApp < Sinatra::Base
|
1807
|
-
# el middleware se ejecutará antes que los filtros
|
1808
|
-
use PantallaDeLogin
|
1809
|
-
|
1810
|
-
before do
|
1811
|
-
unless session['nombre_de_usuario']
|
1812
|
-
halt "Acceso denegado, por favor <a href='/login'>iniciá sesión</a>."
|
1813
|
-
end
|
1814
|
-
end
|
1815
|
-
|
1816
|
-
get('/') { "Hola #{session['nombre_de_usuario']}." }
|
1817
|
-
end
|
1818
|
-
|
1819
|
-
=== Creación Dinámica de Aplicaciones
|
1820
|
-
|
1821
|
-
Puede que en algunas ocasiones quieras crear nuevas aplicaciones en
|
1822
|
-
tiempo de ejecución sin tener que asignarlas a una constante. Para
|
1823
|
-
esto tenés <tt>Sinatra.new</tt>:
|
1824
|
-
|
1825
|
-
require 'sinatra/base'
|
1826
|
-
mi_app = Sinatra.new { get('/') { "hola" } }
|
1827
|
-
mi_app.run!
|
1828
|
-
|
1829
|
-
Acepta como argumento opcional una aplicación desde la que se
|
1830
|
-
heredará:
|
1831
|
-
|
1832
|
-
# config.ru
|
1833
|
-
require 'sinatra/base'
|
1834
|
-
|
1835
|
-
controller = Sinatra.new do
|
1836
|
-
enable :logging
|
1837
|
-
helpers MisHelpers
|
1838
|
-
end
|
1839
|
-
|
1840
|
-
map('/a') do
|
1841
|
-
run Sinatra.new(controller) { get('/') { 'a' } }
|
1842
|
-
end
|
1843
|
-
|
1844
|
-
map('/b') do
|
1845
|
-
run Sinatra.new(controller) { get('/') { 'b' } }
|
1846
|
-
end
|
1847
|
-
|
1848
|
-
Construir aplicaciones de esta forma resulta especialmente útil para
|
1849
|
-
testear extensiones Sinatra o para usar Sinatra en tus librerías.
|
1850
|
-
|
1851
|
-
Por otro lado, hace extremadamente sencillo usar Sinatra como
|
1852
|
-
middleware:
|
1853
|
-
|
1854
|
-
require 'sinatra/base'
|
1855
|
-
|
1856
|
-
use Sinatra do
|
1857
|
-
get('/') { ... }
|
1858
|
-
end
|
1859
|
-
|
1860
|
-
run ProyectoRails::Application
|
1861
|
-
|
1862
|
-
== Ámbitos y Ligaduras
|
1863
|
-
|
1864
|
-
El ámbito en el que te encontrás determina que métodos y variables están
|
1865
|
-
disponibles.
|
1866
|
-
|
1867
|
-
=== Ámbito de Aplicación/Clase
|
1868
|
-
|
1869
|
-
Cada aplicación Sinatra es una subclase de <tt>Sinatra::Base</tt>. Si estás
|
1870
|
-
usando el DSL de top-level (<tt>require 'sinatra'</tt>), entonces esta clase es
|
1871
|
-
<tt>Sinatra::Application</tt>, de otra manera es la subclase que creaste
|
1872
|
-
explícitamente. Al nivel de la clase tenés métodos como +get+ o +before+, pero
|
1873
|
-
no podés acceder a los objetos +request+ o +session+, ya que hay una única
|
1874
|
-
clase de la aplicación para todas las peticiones.
|
1875
|
-
|
1876
|
-
Las opciones creadas utilizando +set+ son métodos al nivel de la clase:
|
1877
|
-
|
1878
|
-
class MiApp < Sinatra::Base
|
1879
|
-
# Ey, estoy en el ámbito de la aplicación!
|
1880
|
-
set :foo, 42
|
1881
|
-
foo # => 42
|
1882
|
-
|
1883
|
-
get '/foo' do
|
1884
|
-
# Hey, ya no estoy en el ámbito de la aplicación!
|
1885
|
-
end
|
1886
|
-
end
|
1887
|
-
|
1888
|
-
Tenés la ligadura al ámbito de la aplicación dentro de:
|
1889
|
-
|
1890
|
-
* El cuerpo de la clase de tu aplicación
|
1891
|
-
* Métodos definidos por extensiones
|
1892
|
-
* El bloque pasado a +helpers+
|
1893
|
-
* Procs/bloques usados como el valor para +set+
|
1894
|
-
|
1895
|
-
Este ámbito puede alcanzarse de las siguientes maneras:
|
1896
|
-
|
1897
|
-
* A través del objeto pasado a los bloques de configuración (<tt>configure { |c| ...}</tt>)
|
1898
|
-
* Llamando a +settings+ desde dentro del ámbito de la petición
|
1899
|
-
|
1900
|
-
=== Ámbito de Petición/Instancia
|
1901
|
-
|
1902
|
-
Para cada petición entrante, una nueva instancia de la clase de tu aplicación
|
1903
|
-
es creada y todos los bloques de rutas son ejecutados en ese ámbito. Desde este
|
1904
|
-
ámbito podés acceder a los objetos +request+ y +session+ o llamar a los métodos
|
1905
|
-
de renderización como +erb+ o +haml+. Podés acceder al ámbito de la aplicación
|
1906
|
-
desde el ámbito de la petición utilizando +settings+:
|
1907
|
-
|
1908
|
-
class MiApp < Sinatra::Base
|
1909
|
-
# Ey, estoy en el ámbito de la aplicación!
|
1910
|
-
get '/definir_ruta/:nombre' do
|
1911
|
-
# Ámbito de petición para '/definir_ruta/:nombre'
|
1912
|
-
@valor = 42
|
1913
|
-
|
1914
|
-
settings.get("/#{params[:nombre]}") do
|
1915
|
-
# Ámbito de petición para "/#{params[:nombre]}"
|
1916
|
-
@valor # => nil (no es la misma petición)
|
1917
|
-
end
|
1918
|
-
|
1919
|
-
"Ruta definida!"
|
1920
|
-
end
|
1921
|
-
end
|
1922
|
-
|
1923
|
-
Tenés la ligadura al ámbito de la petición dentro de:
|
1924
|
-
|
1925
|
-
* bloques pasados a get/head/post/put/delete/options
|
1926
|
-
* filtros before/after
|
1927
|
-
* métodos ayudantes
|
1928
|
-
* plantillas/vistas
|
1929
|
-
|
1930
|
-
=== Ámbito de Delegación
|
1931
|
-
|
1932
|
-
El ámbito de delegación solo reenvía métodos al ámbito de clase. De cualquier
|
1933
|
-
manera, no se comporta 100% como el ámbito de clase porque no tenés la ligadura
|
1934
|
-
de la clase: únicamente métodos marcados explícitamente para delegación están
|
1935
|
-
disponibles y no compartís variables/estado con el ámbito de clase (léase:
|
1936
|
-
tenés un +self+ diferente). Podés agregar delegaciones de método llamando a
|
1937
|
-
<tt>Sinatra::Delegator.delegate :nombre_del_metodo</tt>.
|
1938
|
-
|
1939
|
-
Tenés la ligadura al ámbito de delegación dentro de:
|
1940
|
-
|
1941
|
-
* La ligadura del top-level, si hiciste <tt>require "sinatra"</tt>
|
1942
|
-
* Un objeto extendido con el mixin <tt>Sinatra::Delegator</tt>
|
1943
|
-
|
1944
|
-
Pegale una mirada al código: acá está el
|
1945
|
-
{Sinatra::Delegator mixin}[https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633]
|
1946
|
-
que {extiende el objeto main}[https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30].
|
1947
|
-
|
1948
|
-
== Línea de Comandos
|
1949
|
-
|
1950
|
-
Las aplicaciones Sinatra pueden ser ejecutadas directamente:
|
1951
|
-
|
1952
|
-
ruby miapp.rb [-h] [-x] [-e ENTORNO] [-p PUERTO] [-o HOST] [-s MANEJADOR]
|
1953
|
-
|
1954
|
-
Las opciones son:
|
1955
|
-
|
1956
|
-
-h # ayuda
|
1957
|
-
-p # asigna el puerto (4567 es usado por defecto)
|
1958
|
-
-o # asigna el host (0.0.0.0 es usado por defecto)
|
1959
|
-
-e # asigna el entorno (development es usado por defecto)
|
1960
|
-
-s # especifica el servidor/manejador rack (thin es usado por defecto)
|
1961
|
-
-x # activa el mutex lock (está desactivado por defecto)
|
1962
|
-
|
1963
|
-
== Versiones de Ruby Soportadas
|
1964
|
-
|
1965
|
-
Las siguientes versiones de Ruby son soportadas oficialmente:
|
1966
|
-
|
1967
|
-
[ Ruby 1.8.7 ]
|
1968
|
-
1.8.7 es soportado completamente. Sin embargo, si no hay nada que te lo
|
1969
|
-
prohíba, te recomendamos que usés 1.9.2 o cambies a JRuby o Rubinius. No se
|
1970
|
-
dejará de dar soporte a 1.8.7 hasta Sinatra 2.0 y Ruby 2.0, aunque si se
|
1971
|
-
libera la versión 1.8.8 de Ruby las cosas podrían llegar a cambiar. Sin
|
1972
|
-
embargo, que eso ocurra es muy poco probable, e incluso el caso de que lo
|
1973
|
-
haga, puede que se siga dando soporte a 1.8.7. <b>Hemos dejado de soportar
|
1974
|
-
Ruby 1.8.6.</b> Si querés ejecutar Sinatra sobre 1.8.6, podés utilizar la
|
1975
|
-
versión 1.2, pero tené en cuenta que una vez que Sinatra 1.4.0 sea liberado,
|
1976
|
-
ya no se corregirán errores por más que se reciban reportes de los mismos.
|
1977
|
-
|
1978
|
-
[ Ruby 1.9.2 ]
|
1979
|
-
1.9.2 es soportado y recomendado. No usés 1.9.2p0, porque se producen fallos
|
1980
|
-
de segmentación cuando se ejecuta Sinatra. El soporte se mantendrá al menos
|
1981
|
-
hasta que se libere la versión 1.9.4/2.0 de Ruby. El soporte para la última
|
1982
|
-
versión de la serie 1.9 se mantendrá mientras lo haga el core team de Ruby.
|
1983
|
-
|
1984
|
-
[ Ruby 1.9.3 ]
|
1985
|
-
1.9.3 es soportado y recomendado. Tené en cuenta que el cambio a 1.9.3 desde
|
1986
|
-
una versión anterior va a invalidar todas las sesiones.
|
1987
|
-
|
1988
|
-
[ Rubinius ]
|
1989
|
-
Rubinius es soportado oficialmente (Rubinius >= 1.2.4). Todo funciona
|
1990
|
-
correctamente, incluyendo los lenguajes de plantillas. La próxima versión,
|
1991
|
-
2.0, también es soportada, incluyendo el modo 1.9.
|
1992
|
-
|
1993
|
-
[ JRuby ]
|
1994
|
-
JRuby es soportado oficialmente (JRuby >= 1.6.7). No se conocen problemas
|
1995
|
-
con librerías de plantillas de terceras partes. Sin embargo, si elegís usar
|
1996
|
-
JRuby, deberías examinar sus Rack handlers porque el servidor web Thin no es
|
1997
|
-
soportado completamente. El soporte de JRuby para extensiones C se encuentra
|
1998
|
-
en una etapa experimental, sin embargo, de momento solamente RDiscount,
|
1999
|
-
Redcarpet, RedCloth y Yajl, así como Thin y Mongrel se ven afectadas.
|
2000
|
-
|
2001
|
-
Siempre le prestamos atención a las nuevas versiones de Ruby.
|
2002
|
-
|
2003
|
-
Las siguientes implementaciones de Ruby no se encuentran soportadas
|
2004
|
-
oficialmente. De cualquier manera, pueden ejecutar Sinatra:
|
2005
|
-
|
2006
|
-
* Versiones anteriores de JRuby y Rubinius
|
2007
|
-
* Ruby Enterprise Edition
|
2008
|
-
* MacRuby, Maglev e IronRuby
|
2009
|
-
* Ruby 1.9.0 y 1.9.1 (pero no te recomendamos que los usés)
|
2010
|
-
|
2011
|
-
No estar soportada oficialmente, significa que si las cosas solamente se rompen
|
2012
|
-
ahí y no en una plataforma soportada, asumimos que no es nuestro problema sino
|
2013
|
-
el suyo.
|
2014
|
-
|
2015
|
-
Nuestro servidor CI también se ejecuta sobre ruby-head (que será la próxima
|
2016
|
-
versión 2.0.0) y la rama 1.9.4. Como están en movimiento constante, no podemos
|
2017
|
-
garantizar nada. De todas formas, podés contar con que tanto 1.9.4-p0 como
|
2018
|
-
2.0.0-p0 sea soportadas.
|
2019
|
-
|
2020
|
-
Sinatra debería funcionar en cualquier sistema operativo soportado por la
|
2021
|
-
implementación de Ruby elegida.
|
2022
|
-
|
2023
|
-
En este momento, no vas a poder ejecutar Sinatra en Cardinal, SmallRuby,
|
2024
|
-
BlueRuby o cualquier versión de Ruby anterior a 1.8.7.
|
2025
|
-
|
2026
|
-
== A la Vanguardia
|
2027
|
-
|
2028
|
-
Si querés usar el código de Sinatra más reciente, sentite libre de ejecutar
|
2029
|
-
tu aplicación sobre la rama master, en general es bastante estable.
|
2030
|
-
|
2031
|
-
También liberamos prereleases de vez en cuando, así, podés hacer
|
2032
|
-
|
2033
|
-
gem install sinatra --pre
|
2034
|
-
|
2035
|
-
Para obtener algunas de las últimas características.
|
2036
|
-
|
2037
|
-
=== Con Bundler
|
2038
|
-
|
2039
|
-
Esta es la manera recomendada para ejecutar tu aplicación sobre la última
|
2040
|
-
versión de Sinatra usando {Bundler}[http://gembundler.com/].
|
2041
|
-
|
2042
|
-
Primero, instalá bundler si no lo hiciste todavía:
|
2043
|
-
|
2044
|
-
gem install bundler
|
2045
|
-
|
2046
|
-
Después, en el directorio de tu proyecto, creá un archivo +Gemfile+:
|
2047
|
-
|
2048
|
-
source :rubygems
|
2049
|
-
gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git"
|
2050
|
-
|
2051
|
-
# otras dependencias
|
2052
|
-
gem 'haml' # por ejemplo, si usás haml
|
2053
|
-
gem 'activerecord', '~> 3.0' # quizás también necesités ActiveRecord 3.x
|
2054
|
-
|
2055
|
-
Tené en cuenta que tenés que listar todas las dependencias directas de tu
|
2056
|
-
aplicación. No es necesario listar las dependencias de Sinatra (Rack y Tilt)
|
2057
|
-
porque Bundler las agrega directamente.
|
2058
|
-
|
2059
|
-
Ahora podés arrancar tu aplicación así:
|
2060
|
-
|
2061
|
-
bundle exec ruby miapp.rb
|
2062
|
-
|
2063
|
-
=== Con Git
|
2064
|
-
|
2065
|
-
Cloná el repositorio localmente y ejecutá tu aplicación, asegurándote que el
|
2066
|
-
directorio <tt>sinatra/lib</tt> esté en el <tt>$LOAD_PATH</tt>:
|
2067
|
-
|
2068
|
-
cd miapp
|
2069
|
-
git clone git://github.com/sinatra/sinatra.git
|
2070
|
-
ruby -Isinatra/lib miapp.rb
|
2071
|
-
|
2072
|
-
Para actualizar el código fuente de Sinatra en el futuro:
|
2073
|
-
|
2074
|
-
cd miapp/sinatra
|
2075
|
-
git pull
|
2076
|
-
|
2077
|
-
=== Instalación Global
|
2078
|
-
|
2079
|
-
Podés construir la gem vos mismo:
|
2080
|
-
|
2081
|
-
git clone git://github.com/sinatra/sinatra.git
|
2082
|
-
cd sinatra
|
2083
|
-
rake sinatra.gemspec
|
2084
|
-
rake install
|
2085
|
-
|
2086
|
-
Si instalás tus gems como root, el último paso debería ser
|
2087
|
-
|
2088
|
-
sudo rake install
|
2089
|
-
|
2090
|
-
== Versionado
|
2091
|
-
|
2092
|
-
Sinatra utiliza el {Versionado Semántico}[http://semver.org/],
|
2093
|
-
siguiendo las especificaciones SemVer y SemVerTag.
|
2094
|
-
|
2095
|
-
== Lecturas Recomendadas
|
2096
|
-
|
2097
|
-
* {Sito web del proyecto}[http://www.sinatrarb.com/] - Documentación
|
2098
|
-
adicional, noticias, y enlaces a otros recursos.
|
2099
|
-
* {Contribuyendo}[http://www.sinatrarb.com/contributing] - ¿Encontraste un
|
2100
|
-
error?. ¿Necesitás ayuda?. ¿Tenés un parche?.
|
2101
|
-
* {Seguimiento de problemas}[http://github.com/sinatra/sinatra/issues]
|
2102
|
-
* {Twitter}[http://twitter.com/sinatra]
|
2103
|
-
* {Lista de Correo}[http://groups.google.com/group/sinatrarb/topics]
|
2104
|
-
* {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] en http://freenode.net
|
2105
|
-
* {Sinatra Book}[http://sinatra-book.gittr.com] Tutorial (en inglés).
|
2106
|
-
* {Sinatra Recipes}[http://recipes.sinatrarb.com/] Recetas contribuidas
|
2107
|
-
por la comunidad (en inglés).
|
2108
|
-
* Documentación de la API para la
|
2109
|
-
{última versión liberada}[http://rubydoc.info/gems/sinatra] o para la
|
2110
|
-
{rama de desarrollo actual}[http://rubydoc.info/github/sinatra/sinatra]
|
2111
|
-
en http://rubydoc.info/
|
2112
|
-
* {Servidor de CI}[http://travis-ci.org/sinatra/sinatra]
|