Skip to content
Newer
Older
100644 1933 lines (1330 sloc) 67.7 KB
280bbdc @sjoonk add README.ko.rdoc for Korean translation
sjoonk authored
1 = Sinatra
2
bf9c3a5 @sjoonk adding version mismatch attention message
sjoonk authored
3 <i>주의: 이 문서는 영문판의 번역본이며 최신판 문서와 다를 수 있음.</i>
4
280bbdc @sjoonk add README.ko.rdoc for Korean translation
sjoonk authored
5 Sinatra는 최소한의 노력으로 루비 기반 웹 애플리케이션을 신속하게 만들 수 있게 해 주는 DSL이다:
6
7 # myapp.rb
8 require 'sinatra'
9
10 get '/' do
11 'Hello world!'
12 end
13
14 다음과 같이 젬을 설치하고 실행한다:
15
16 gem install sinatra
17 ruby -rubygems myapp.rb
18
19 확인: http://localhost:4567
20
21 <tt>gem install thin</tt>도 함께 실행하기를 권장하며, 그럴 경우 Sinatra는 thin을 부른다.
22
23 == 라우터(Routes)
24
25 Sinatra에서, 라우터(route)는 URL-매칭 패턴과 쌍을 이루는 HTTP 메서드다.
26 각각의 라우터는 블록과 연결된다:
27
28 get '/' do
29 .. 무언가 보여주기(show) ..
30 end
31
32 post '/' do
33 .. 무언가 만들기(create) ..
34 end
35
36 put '/' do
37 .. 무언가 대체하기(replace) ..
38 end
39
40 patch '/' do
41 .. 무언가 수정하기(modify) ..
42 end
43
44 delete '/' do
45 .. 무언가 없애기(annihilate) ..
46 end
47
48 options '/' do
49 .. 무언가 주기(appease) ..
50 end
51
52 라우터는 정의된 순서에 따라 매치되며 매칭된 첫 번째 라우터가 호출된다.
53
54 라우터 패턴에는 이름을 가진 매개변수가 포함될 수있으며, <tt>params</tt> 해시로 접근할 수 있다:
55
56 get '/hello/:name' do
57 # "GET /hello/foo" 및 "GET /hello/bar"와 매치
58 # params[:name]은 'foo' 또는 'bar'
59 "Hello #{params[:name]}!"
60 end
61
62 또한 블록 매개변수를 통하여도 이름을 가진 매개변수에 접근할 수 있다:
63
64 get '/hello/:name' do |n|
65 "Hello #{n}!"
66 end
67
68 라우터 패턴에는 스플랫(splat, 또는 와일드카드)도 포함될 수 있으며, 이럴 경우 <tt>params[:splat]</tt> 배열로 접근할 수 있다:
69
70 get '/say/*/to/*' do
71 # /say/hello/to/world와 매치
72 params[:splat] # => ["hello", "world"]
73 end
74
75 get '/download/*.*' do
76 # /download/path/to/file.xml과 매치
77 params[:splat] # => ["path/to/file", "xml"]
78 end
79
80 또는 블록 매개변수도 가능하다:
81
82 get '/download/*.*' do |path, ext|
83 [path, ext] # => ["path/to/file", "xml"]
84 end
85
86 정규표현식을 이용한 라우터 매칭:
87
88 get %r{/hello/([\w]+)} do
89 "Hello, #{params[:captures].first}!"
90 end
91
92 또는 블록 매개변수로도 가능:
93
94 get %r{/hello/([\w]+)} do |c|
95 "Hello, #{c}!"
96 end
97
98 라우터 패턴에는 선택적인(optional) 매개변수도 올 수 있다:
99
100 get '/posts.?:format?' do
101 # "GET /posts" 및 "GET /posts.json", "GET /posts.xml" 와 같은 어떤 확장자와도 매칭
102 end
103
104 한편, 경로 탐색 공격 방지(path traversal attack protection, 아래 참조)를 비활성화시키지 않았다면,
105 요청 경로는 라우터와 매칭되기 이전에 수정될 수 있다.
106
107 === 조건(Conditions)
108
109 라우터는 예를 들면 사용자 에이전트(user agent)와 같은 다양한 매칭 조건을 포함할 수 있다:
110
111 get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
112 "Songbird 버전 #{params[:agent][0]}을 사용하는군요!"
113 end
114
115 get '/foo' do
116 # songbird 브라우저가 아닌 경우 매치
117 end
118
119 그 밖에 다른 조건으로는 +host_name+과 +provides+가 있다:
120
121 get '/', :host_name => /^admin\./ do
122 "Admin Area, Access denied!"
123 end
124
125 get '/', :provides => 'html' do
126 haml :index
127 end
128
129 get '/', :provides => ['rss', 'atom', 'xml'] do
130 builder :feed
131 end
132
133 여러분만의 조건도 쉽게 정의할 수 있다:
134
135 set(:probability) { |value| condition { rand <= value } }
136
137 get '/win_a_car', :probability => 0.1 do
138 "내가 졌소!"
139 end
140
141 get '/win_a_car' do
142 "미안해서 어쩌나."
143 end
144
145 여러 값을 받는 조건에는 스플랫(splat)을 사용하자:
146
147 set(:auth) do |*roles| # <- 이게 스플랫
148 condition do
149 unless logged_in? && roles.any? {|role| current_user.in_role? role }
150 redirect "/login/", 303
151 end
152 end
153 end
154
155 get "/my/account/", :auth => [:user, :admin] do
156 "내 계정 정보"
157 end
158
159 get "/only/admin/", :auth => :admin do
160 "관리자 외 접근불가!"
161 end
162
163 === 반환값(Return Values)
164
165 라우터 블록의 반환값은 HTTP 클라이언트로 전달되는 응답 본문을 결정하거나, 또는 Rack 스택에서 다음 번 미들웨어를 결정한다.
166 대부분의 경우, 이 반환값은 위의 예제에서 보듯 문자열이지만, 다른 값도 가능하다.
167
168 유효한 Rack 응답, Rack 본문 객체 또는 HTTP 상태 코드가 되는 어떠한 객체라도 반환할 수 있다:
169
170 * 세 요소를 가진 배열: <tt>[상태 (Fixnum), 헤더 (Hash), 응답 본문 (#each에 반응)]</tt>
171 * 두 요소를 가진 배열: <tt>[상태 (Fixnum), 응답 본문 (#each에 반응)]</tt>
172 * <tt>#each</tt>에 반응하고 주어진 블록으로 문자열만을 전달하는 객체
173 * 상태 코드를 의미하는 Fixnum
174
175 이에 따라 우리는, 예를 들면, 스트리밍(streaming) 예제를 쉽게 구현할 수 있다:
176
177 class Stream
178 def each
179 100.times { |i| yield "#{i}\n" }
180 end
181 end
182
183 get('/') { Stream.new }
184
185 이런 번거로움을 줄이기 위해 +stream+ 헬퍼 메서드(아래 참조)를 사용하여 스트리밍 로직을 라우터 속에 둘 수도 있다.
186
187 === 커스텀 라우터 매처(Custom Route Matchers)
188
189 위에서 보듯, Sinatra에는 문자열 패턴 및 정규표현식을 이용한 라우터 매칭 지원이 내장되어 있다.
190 그렇지만, 그게 끝이 아니다. 여러분 만의 매처(matcher)도 쉽게 정의할 수 있다:
191
192 class AllButPattern
193 Match = Struct.new(:captures)
194
195 def initialize(except)
196 @except = except
197 @captures = Match.new([])
198 end
199
200 def match(str)
201 @captures unless @except === str
202 end
203 end
204
205 def all_but(pattern)
206 AllButPattern.new(pattern)
207 end
208
209 get all_but("/index") do
210 # ...
211 end
212
213 사실 위의 예제는 조금 과하게 작성된 면이 있다. 다음과 같이 표현할 수도 있다:
214
215 get // do
216 pass if request.path_info == "/index"
217 # ...
218 end
219
220 또는 네거티브 룩어헤드(negative look ahead)를 사용할 수도 있다:
221
222 get %r{^(?!/index$)} do
223 # ...
224 end
225
226 == 정적 파일(Static Files)
227
228 정적 파일들은 <tt>./public</tt>에서 제공된다.
229 위치를 다른 곳으로 변경하려면 <tt>:public_folder</tt> 옵션을 사용하면 된다:
230
231 set :public_folder, File.dirname(__FILE__) + '/static'
232
233 이 때 public 디렉터리명은 URL에 포함되지 않는다는 점에 유의.
234 <tt>./public/css/style.css</tt> 파일은 <tt>http://example.com/css/style.css</tt>로 접근할 수 있다.
235
236 <tt>Cache-Control</tt> 헤더 정보를 추가하려면 <tt>:static_cache_control</tt> 설정(아래 참조)을 사용하면 된다.
237
238 == 뷰 / 템플릿(Views / Templates)
239
240 각 템플릿 언어는 그들만의 고유한 렌더링 메서드를 통해 표출된다.
241 이들 메서드는 단순히 문자열을 반환한다.
242
243 get '/' do
244 erb :index
245 end
246
247 이 메서드는 <tt>views/index.erb</tt>를 렌더한다.
248
249 템플릿 이름 대신 템플릿의 내용을 직접 전달할 수도 있다:
250
251 get '/' do
252 code = "<%= Time.now %>"
253 erb code
254 end
255
256 템플릿은 두 번째 인자로 옵션값의 해시를 받는다:
257
258 get '/' do
259 erb :index, :layout => :post
260 end
261
262 이렇게 하면 <tt>views/post.erb</tt> 속에 내장된 <tt>views/index.erb</tt>를 렌더한다.
263 (기본값은 <tt>views/layout.erb</tt>이며, 이 파일이 존재할 경우에만 먹는다).
264
265 Sinatra가 이해하지 못하는 모든 옵션값들은 템플릿 엔진으로 전달될 것이다:
266
267 get '/' do
268 haml :index, :format => :html5
269 end
270
271 옵션값은 템플릿 언어별로 일반적으로 설정할 수도 있다:
272
273 set :haml, :format => :html5
274
275 get '/' do
276 haml :index
277 end
278
279 render 메서드에서 전달된 옵션값들은 +set+을 통해 설정한 옵션값을 덮어 쓴다.
280
281 가능한 옵션값들:
282
283 [locals]
284 문서로 전달되는 local 목록. 파셜과 함께 사용하기 좋음.
285 예제: <tt>erb "<%= foo %>", :locals => {:foo => "bar"}</tt>
286
287 [default_encoding]
288 불확실한 경우에 사용할 문자열 인코딩. 기본값은 <tt>settings.default_encoding</tt>.
289
290 [views]
291 템플릿을 로드할 뷰 폴더. 기본값은 <tt>settings.views</tt>.
292
293 [layout]
294 레이아웃을 사용할지 여부 (+true+ 또는 +false+), 만약 이 값이 심볼일 경우,
295 사용할 템플릿을 지정. 예제: <tt>erb :index, :layout => !request.xhr?</tt>
296
297 [content_type]
298 템플릿이 생성하는 Content-Type, 기본값은 템플릿 언어에 의존.
299
300 [scope]
301 템플릿을 렌더링하는 범위. 기본값은 어플리케이션 인스턴스.
302 만약 이 값을 변경하면, 인스턴스 변수와 헬퍼 메서드들을 사용할 수 없게 됨.
303
304 [layout_engine]
305 레이아웃 렌더링에 사용할 템플릿 엔진. 레이아웃을 지원하지 않는 언어인 경우에 유용.
306 기본값은 템플릿에서 사용하는 엔진. 예제: <tt>set :rdoc, :layout_engine => :erb</tt>
307
308 템플릿은 <tt>./views</tt> 아래에 놓이는 것으로 가정됨. 만약 뷰 디렉터리를 다른 곳으로 두려면:
309
310 set :views, settings.root + '/templates'
311
312 꼭 알아야 할 중요한 점 한 가지는 템플릿은 언제나 심볼로 참조된다는 것이며,
313 템플릿이 하위 디렉터리에 위치한 경우라도 마찬가지임(그럴 경우에는 <tt>:'subdir/template'</tt>을 사용).
314 반드시 심볼이어야 하는 이유는, 만약 그렇게 하지 않으면 렌더링 메서드가 전달된 문자열을 직접 렌더하려 할 것이기 때문임.
315
316 === 가능한 템플릿 언어들(Available Template Languages)
317
318 일부 언어는 여러 개의 구현이 있음. 어느 구현을 사용할지 저정하려면(그리고 스레드-안전thread-safe 모드로 하려면),
319 먼저 require 시키기만 하면 됨:
320
321 require 'rdiscount' # or require 'bluecloth'
322 get('/') { markdown :index }
323
324 === Haml 템플릿
325
f095019 @mattwildig Update README links to Haml
mattwildig authored
326 의존:: {haml}[http://haml.info/]
280bbdc @sjoonk add README.ko.rdoc for Korean translation
sjoonk authored
327 파일 확장자:: <tt>.haml</tt>
328 예:: <tt>haml :index, :format => :html5</tt>
329
330 === Erb 템플릿
331
332 의존:: {erubis}[http://www.kuwata-lab.com/erubis/] 또는 erb (루비 속에 포함)
333 파일 확장자:: <tt>.erb</tt>, <tt>.rhtml</tt> 또는 <tt>.erubis</tt> (Erubis만 해당)
334 예제:: <tt>erb :index</tt>
335
336 === Builder 템플릿
337
338 의존:: {builder}[http://builder.rubyforge.org/]
339 파일 확장자:: <tt>.builder</tt>
340 Example:: <tt>builder { |xml| xml.em "hi" }</tt>
341
342 인라인 템플릿으로 블록을 받음(예제 참조).
343
344 === Nokogiri 템플릿
345
346 의존:: {nokogiri}[http://nokogiri.org/]
347 파일 확장자:: <tt>.nokogiri</tt>
348 예제:: <tt>nokogiri { |xml| xml.em "hi" }</tt>
349
350 인라인 템플릿으로 블록을 받음(예제 참조).
351
352 === Sass 템플릿
353
354 의존:: {sass}[http://sass-lang.com/]
355 파일 확장자:: <tt>.sass</tt>
356 예제:: <tt>sass :stylesheet, :style => :expanded</tt>
357
358 === SCSS 템플릿
359
360 의존:: {sass}[http://sass-lang.com/]
361 파일 확장자:: <tt>.scss</tt>
362 예제:: <tt>scss :stylesheet, :style => :expanded</tt>
363
364 === Less 템플릿
365
366 의존:: {less}[http://www.lesscss.org/]
367 파일 확장자:: <tt>.less</tt>
368 예제:: <tt>less :stylesheet</tt>
369
370 === Liquid 템플릿
371
372 의존:: {liquid}[http://www.liquidmarkup.org/]
373 파일 확장자:: <tt>.liquid</tt>
374 예제:: <tt>liquid :index, :locals => { :key => 'value' }</tt>
375
376 Liquid 템플릿에서는 루비 메서드(+yield+ 제외)를 호출할 수 없기 때문에, 거의 대부분의 경우 locals를 전달해야 함.
377
378 === Markdown 템플릿
379
380 의존:: {rdiscount}[https://github.com/rtomayko/rdiscount],
381 {redcarpet}[https://github.com/tanoku/redcarpet],
382 {bluecloth}[http://deveiate.org/projects/BlueCloth],
383 {kramdown}[http://kramdown.rubyforge.org/] *또는*
384 {maruku}[http://maruku.rubyforge.org/]
385 파일 확장:: <tt>.markdown</tt>, <tt>.mkd</tt>, <tt>.md</tt>
386 예제:: <tt>markdown :index, :layout_engine => :erb</tt>
387
388 마크다운에서는 메서드 호출 뿐 아니라 locals 전달도 안됨.
389 따라서 일반적으로는 다른 렌더링 엔진과 함께 사용하게 될 것임:
390
391 erb :overview, :locals => { :text => markdown(:introduction) }
392
393 또한 다른 템플릿 속에서 +markdown+ 메서드를 호출할 수도 있음:
394
395 %h1 안녕 Haml!
396 %p= markdown(:greetings)
397
398 Markdown에서 루비를 호출할 수 없기 때문에, Markdown으로 작성된 레이아웃은 사용할 수 없음.
399 단, <tt>:layout_engine</tt> 옵션으로 템플릿의 레이아웃은 다른 렌더링 엔진을 사용하는 것은 가능.
400
401 === Textile 템플릿
402
403 의존:: {RedCloth}[http://redcloth.org/]
404 파일 확장자:: <tt>.textile</tt>
405 예제:: <tt>textile :index, :layout_engine => :erb</tt>
406
407 Textile에서 메서드를 호출하거나 locals를 전달하는 것은 불가능함.
408 따라서 일반적으로 다른 렌더링 엔진과 함께 사용하게 될 것임:
409
410 erb :overview, :locals => { :text => textile(:introduction) }
411
412 또한 다른 템플릿 속에서 +textile+ 메서드를 호출할 수도 있음:
413
414 %h1 안녕 Haml!
415 %p= textile(:greetings)
416
417 Textile에서 루비를 호출할 수 없기 때문에, Textile로 작성된 레이아웃은 사용할 수 없음.
418 단, <tt>:layout_engine</tt> 옵션으로 템플릿의 레이아웃은 다른 렌더링 엔진을 사용하는 것은 가능.
419
420 === RDoc 템플릿
421
422 의존:: {rdoc}[http://rdoc.rubyforge.org/]
423 파일 확장자:: <tt>.rdoc</tt>
424 예제:: <tt>rdoc :README, :layout_engine => :erb</tt>
425
426 rdoc에서 메서드를 호출하거나 locals를 전달하는 것은 불가능함.
427 따라서 일반적으로 다른 렌더링 엔진과 함께 사용하게 될 것임:
428
429 erb :overview, :locals => { :text => rdoc(:introduction) }
430
431 또한 다른 템플릿 속에서 +rdoc+ 메서드를 호출할 수도 있음:
432
433 %h1 Hello From Haml!
434 %p= rdoc(:greetings)
435
436 RDoc에서 루비를 호출할 수 없기 때문에, RDoc로 작성된 레이아웃은 사용할 수 없음.
437 단, <tt>:layout_engine</tt> 옵션으로 템플릿의 레이아웃은 다른 렌더링 엔진을 사용하는 것은 가능.
438
439
440 === Radius 템플릿
441
442 의존:: {radius}[http://radius.rubyforge.org/]
443 파일 확장자:: <tt>.radius</tt>
444 예제:: <tt>radius :index, :locals => { :key => 'value' }</tt>
445
446 Radius 템플릿에서는 루비 메서드를 호출할 수 없기 때문에, 거의 대부분의 경우 locals로 전달하게 될 것임.
447
448
449 === Markaby 템플릿
450
451 의존:: {markaby}[http://markaby.github.com/]
452 파일확장:: <tt>.mab</tt>
453 예제:: <tt>markaby { h1 "Welcome!" }</tt>
454
455 인라인 템플릿으로 블록을 받을 수도 있음(예제 참조).
456
5bf54d5 @jc00ke Adding RABL support
jc00ke authored
457 === RABL 템플릿
458
459 의존:: {rabl}[https://github.com/nesquena/rabl]
460 파일 확장자:: <tt>.rabl</tt>
461 예제:: <tt>rabl :index</tt>
462
280bbdc @sjoonk add README.ko.rdoc for Korean translation
sjoonk authored
463 === Slim 템플릿
464
465 의존:: {slim}[http://slim-lang.com/]
466 파일 확장자:: <tt>.slim</tt>
467 예제:: <tt>slim :index</tt>
468
469 === Creole 템플릿
470
471 의존:: {creole}[https://github.com/minad/creole]
472 파일 확장자:: <tt>.creole</tt>
473 예제:: <tt>creole :wiki, :layout_engine => :erb</tt>
474
475 creole에서는 루비 메서드를 호출할 수 없고 locals도 전달할 수 없음.
476 따라서 일반적으로는 다른 렌더링 엔진과 함께 사용하게 될 것임.
477
478 erb :overview, :locals => { :text => creole(:introduction) }
479
480 또한 다른 템플릿 속에서 +creole+ 메서드를 호출할 수도 있음:
481
482 %h1 Hello From Haml!
483 %p= creole(:greetings)
484
485 Creole에서 루비를 호출할 수 없기 때문에, Creole로 작성된 레이아웃은 사용할 수 없음.
486 단, <tt>:layout_engine</tt> 옵션으로 템플릿의 레이아웃은 다른 렌더링 엔진을 사용하는 것은 가능.
487
488
489 === CoffeeScript 템플릿
490
491 의존성:: {coffee-script}[https://github.com/josh/ruby-coffee-script]
492 와 {자바스크립트 실행법}[https://github.com/sstephenson/execjs/blob/master/README.md#readme]
493 파일 확장자:: <tt>.coffee</tt>
494 예제:: <tt>coffee :index</tt>
495
496 === Yajl 템플릿
497
498 의존:: {yajl-ruby}[https://github.com/brianmario/yajl-ruby]
499 파일 확장자:: <tt>.yajl</tt>
500 예제:: <tt>yajl :index, :locals => { :key => 'qux' }, :callback => 'present', :variable => 'resource' </tt>
501
502 The template source is evaluated as a Ruby string, and the resulting json variable is converted #to_json.
503 템플릿 소스는 루비 문자열로 평가(evaluate)되고, 결과인 json 변수는 #to_json으로 변환됨.
504
505 json = { :foo => 'bar' }
506 json[:baz] = key
507
508 <tt>:callback</tt>과 <tt>:variable</tt> 옵션은 렌더된 객체를 꾸미는데(decorate) 사용할 수 있음.
509
510 var resource = {"foo":"bar","baz":"qux"}; present(resource);
511
512 === 내장된(Embedded) 템플릿
513
514 get '/' do
515 haml '%div.title Hello World'
516 end
517
518 내장된 템플릿 문자열을 렌더함.
519
520 === 템플릿에서 변수에 접근하기
521
522 Templates are evaluated within the same context as route handlers. Instance
523 variables set in route handlers are directly accessible by templates:
524 템플릿은 라우터 핸들러와 같은 맥락(context)에서 평가된다.
525 라우터 핸들러에서 설정한 인스턴스 변수들은 템플릿에서 접근 가능하다:
526
527 get '/:id' do
528 @foo = Foo.find(params[:id])
529 haml '%h1= @foo.name'
530 end
531
532 또는, 명시적으로 로컬 변수의 해시를 지정:
533
534 get '/:id' do
535 foo = Foo.find(params[:id])
536 haml '%h1= bar.name', :locals => { :bar => foo }
537 end
538
539 This is typically used when rendering templates as partials from within
540 other templates.
541 이 방법은 통상적으로 템플릿을 다른 템플릿 속에서 파셜(partial)로 렌더링할 때 사용된다.
542
543 === 인라인 템플릿
544
545 템플릿은 소스 파일의 마지막에서 정의할 수도 있다:
546
547 require 'sinatra'
548
549 get '/' do
550 haml :index
551 end
552
553 __END__
554
555 @@ layout
556 %html
557 = yield
558
559 @@ index
560 %div.title Hello world.
561
562 참고: require sinatra 시킨 소스 파일에 정의된 인라인 템플릿은 자동으로 로드된다.
563 다른 소스 파일에서 인라인 템플릿을 사용하려면 명시적으로 <tt>enable :inline_templates</tt>을 호출하면 됨.
564
565 === 이름을 가지는 템플릿(Named Templates)
566
567 템플릿은 톱 레벨(top-level)에서 <tt>template</tt>메서드를 사용하여 정의할 수 있다:
568
569 template :layout do
570 "%html\n =yield\n"
571 end
572
573 template :index do
574 '%div.title Hello World!'
575 end
576
577 get '/' do
578 haml :index
579 end
580
581 "layout"이라는 이름의 템플릿이 존재하면, 매번 템플릿이 렌더될 때마다 사용될 것이다.
582 이 때 <tt>:layout => false</tt>를 전달하여 개별적으로 레이아웃을 비활성시키거나
583 또는 <tt>set :haml, :layout => false</tt>으로 기본값을 비활성으로 둘 수 있다:
584
585 get '/' do
586 haml :index, :layout => !request.xhr?
587 end
588
589 === 파일 확장자 연결하기
590
591 어떤 파일 확장자를 특정 템플릿 엔진과 연결하려면, <tt>Tilt.register</tt>를 사용하면 된다.
592 예를 들어, +tt+라는 파일 확장자를 Textile 템플릿과 연결하고 싶다면, 다음과 같이 하면 된다:
593
594 Tilt.register :tt, Tilt[:textile]
595
596 === 나만의 고유한 템플릿 엔진 추가하기
597
598 우선, Tilt로 여러분 엔진을 등록하고, 그런 다음 렌더링 메서드를 생성하자:
599
600 Tilt.register :myat, MyAwesomeTemplateEngine
601
602 helpers do
603 def myat(*args) render(:myat, *args) end
604 end
605
606 get '/' do
607 myat :index
608 end
609
610 <tt>./views/index.myat</tt>를 렌더함.
611 Tilt에 대한 더 자세한 내용은 https://github.com/rtomayko/tilt 참조.
612
613 == 필터(Filters)
614
615 사전 필터(before filter)는 라우터와 동일한 맥락에서 매 요청 전에 평가되며 요청과 응답을 변형할 수 있다.
616 필터에서 설정된 인스턴스 변수들은 라우터와 템플릿 속에서 접근 가능하다:
617
618 before do
619 @note = 'Hi!'
620 request.path_info = '/foo/bar/baz'
621 end
622
623 get '/foo/*' do
624 @note #=> 'Hi!'
625 params[:splat] #=> 'bar/baz'
626 end
627
628 사후 필터(after filter)는 라우터와 동일한 맥락에서 매 요청 이후에 평가되며 마찬가지로 요청과 응답을 변형할 수 있다.
629 사전 필터와 라우터에서 설정된 인스턴스 변수들은 사후 필터에서 접근 가능하다:
630
631 after do
632 puts response.status
633 end
634
635 참고: 만약 라우터에서 +body+ 메서드를 사용하지 않고 그냥 문자열만 반환한 경우라면, body는 나중에 생성되는 탓에, 아직 사후 필터에서 사용할 수 없을 것이다.
636
637 필터는 선택적으로 패턴을 취할 수 있으며, 이 경우 요청 경로가 그 패턴과 매치할 경우에만 필터가 평가될 것이다.
638
639 before '/protected/*' do
640 authenticate!
641 end
642
643 after '/create/:slug' do |slug|
644 session[:last_slug] = slug
645 end
646
647 라우터와 마찬가지로, 필터 역시 조건을 갖는다:
648
649 before :agent => /Songbird/ do
650 # ...
651 end
652
653 after '/blog/*', :host_name => 'example.com' do
654 # ...
655 end
656
657 == 헬퍼(Helpers)
658
659 톱-레벨의 <tt>helpers</tt> 메서드를 사용하여 라우터 핸들러와 템플릿에서 사용할 헬퍼 메서드들을 정의할 수 있다:
660
661 helpers do
662 def bar(name)
663 "#{name}bar"
664 end
665 end
666
667 get '/:name' do
668 bar(params[:name])
669 end
670
671 또는, 헬퍼 메서드는 별도의 모듈 속에 정의할 수도 있다:
672
673 module FooUtils
674 def foo(name) "#{name}foo" end
675 end
676
677 module BarUtils
678 def bar(name) "#{name}bar" end
679 end
680
681 helpers FooUtils, BarUtils
682
683 이 경우 모듈을 애플리케이션 클래스에 포함(include)시킨 것과 동일한 효과를 갖는다.
684
685 === 세션(Sessions) 사용하기
686
687 세션은 요청 동안에 상태를 유지하기 위해 사용한다.
688 세션이 활성화되면, 사용자 세션 당 session 해시 하나씩을 갖게 된다:
689
690 enable :sessions
691
692 get '/' do
693 "value = " << session[:value].inspect
694 end
695
696 get '/:value' do
697 session[:value] = params[:value]
698 end
699
700 <tt>enable :sessions</tt>은 실은 모든 데이터를 쿠키 속에 저장함에 유의하자.
701 항상 이렇게 하고 싶지 않을 수도 있을 것이다(예를 들어, 많은 양의 데이터를 저장하게 되면 트래픽이 높아진다).
702 이 때는 여러 가지 랙 세션 미들웨어(Rack session middleware)를 사용할 수 있을 것이다:
703 이렇게 할 경우라면, <tt>enable :sessions</tt>을 호출하지 *말고*,
704 대신 여러분이 선택한 미들웨어를 다른 모든 미들웨어들처럼 포함시키면 된다:
705
706 use Rack::Session::Pool, :expire_after => 2592000
707
708 get '/' do
709 "value = " << session[:value].inspect
710 end
711
712 get '/:value' do
713 session[:value] = params[:value]
714 end
715
716 보안을 위해서, 쿠키 속의 세션 데이터는 세션 시크릿(secret)으로 사인(sign)된다.
717 Sinatra는 여러분을 위해 무작위 시크릿을 생성한다.
718 그렇지만, 이 시크릿은 여러분 애플리케이션 시작 시마다 변경될 수 있기 때문에,
719 여러분은 여러분 애플리케이션의 모든 인스턴스들이 공유할 시크릿을 직접 만들고 싶을 수도 있다:
720
721 set :session_secret, 'super secret'
722
723 조금 더 세부적인 설정이 필요하다면, +sessions+ 설정에서 옵션이 있는 해시를 저장할 수도 있을 것이다:
724
725 set :sessions, :domain => 'foo.com'
726
727 === 중단하기(Halting)
728
729 필터나 라우터에서 요청을 즉각 중단하고 싶을 때 사용하라:
730
731 halt
732
733 중단할 때 상태를 지정할 수도 있다:
734
735 halt 410
736
737 또는 본문을 넣을 수도 있다:
738
739 halt 'this will be the body'
740
741 또는 둘 다도 가능하다:
742
743 halt 401, 'go away!'
744
745 헤더를 추가할 경우에는 다음과 같이 하면 된다:
746
747 halt 402, {'Content-Type' => 'text/plain'}, 'revenge'
748
749 물론 +halt+를 템플릿과 결합하는 것도 가능하다:
750
751 halt erb(:error)
752
753 === 넘기기(Passing)
754
755 라우터는 <tt>pass</tt>를 사용하여 다음 번 매칭되는 라우터로 처리를 넘길 수 있다:
756
757 get '/guess/:who' do
758 pass unless params[:who] == 'Frank'
759 'You got me!'
760 end
761
762 get '/guess/*' do
763 'You missed!'
764 end
765
766 이 떄 라우터 블록에서 즉각 빠져나오게 되고 제어는 다음 번 매칭되는 라우터로 넘어간다.
767 만약 매칭되는 라우터를 찾지 못하면, 404가 반환된다.
768
769 === 다른 라우터 부르기(Triggering Another Route)
770
771 경우에 따라서는 +pass+가 아니라, 다른 라우터를 호출한 결과를 얻고 싶은 경우도 있을 것이다.
772 이 때는 간단하게 ++call++을 사용하면 된다:
773
774 get '/foo' do
775 status, headers, body = call env.merge("PATH_INFO" => '/bar')
776 [status, headers, body.map(&:upcase)]
777 end
778
779 get '/bar' do
780 "bar"
781 end
782
783 위 예제의 경우, <tt>"bar"</tt>를 헬퍼로 옮겨 <tt>/foo</tt>와 <tt>/bar</tt> 모두에서 사용하도록 함으로써
784 테스팅을 쉽게 하고 성능을 높일 수 있을 것이다.
785
786 만약 그 요청이 사본이 아닌 바로 그 동일 인스턴스로 보내지도록 하고 싶다면,
787 <tt>call</tt> 대신 <tt>call!</tt>을 사용하면 된다.
788
789 <tt>call</tt>에 대한 더 자세한 내용은 Rack 명세를 참고하면 된다.
790
791 === 본문, 상태 코드 및 헤더 설정하기
792
793 라우터 블록의 반환값과 함께 상태 코드(status code)와 응답 본문(response body)을 설정하는 것은 가능하기도 하거니와 권장되는 방법이다. 그렇지만, 경우에 따라서는 본문을 실행 흐름 중의 임의 지점에서 설정하고 싶을 수도 있다.
794 이 때는 +body+ 헬퍼 메서드를 사용하면 된다.
795 이렇게 하면, 그 순간부터 본문에 접근할 때 그 메서드를 사용할 수가 있다:
796
797 get '/foo' do
798 body "bar"
799 end
800
801 after do
802 puts body
803 end
804
805 +body+로 블록을 전달하는 것도 가능하며, 이 블록은 랙(Rack) 핸들러에 의해 실행될 것이다.
806 (이 방법은 스트리밍을 구현할 때 사용할 수 있는데, "값 반환하기"를 참고).
807
808 본문와 마찬가지로, 상태코드와 헤더도 설정할 수 있다:
809
810 get '/foo' do
811 status 418
812 headers \
813 "Allow" => "BREW, POST, GET, PROPFIND, WHEN",
814 "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
815 body "I'm a tea pot!"
816 end
817
818 +body+처럼, +header+와 +status+도 매개변수 없이 사용하여 그것의 현재 값을 액세스하는 데 사용될 수 있다.
819
820 === 응답 스트리밍(Streaming Responses)
821
822 응답 본문의 일정 부분을 계속 생성하는 가운데 데이터를 내보내기 시작하고 싶을 경우도 있을 것이다.
823 극단적인 예제로, 클라이언트가 접속을 끊기 전까지 계속 데이터를 내보내고 싶을 수도 있다.
824 여러분만의 래퍼(wrapper)를 만들기 싫다면 +stream+ 헬퍼를 사용하면 된다:
825
826 get '/' do
827 stream do |out|
828 out << "It's gonna be legen -\n"
829 sleep 0.5
830 out << " (wait for it) \n"
831 sleep 1
832 out << "- dary!\n"
833 end
834 end
835
836 이렇게 하면 스트리밍 API나
837 {서버 발송 이벤트Server Sent Events}[http://dev.w3.org/html5/eventsource/]를 구현할 수 있게 해 주며,
838 {WebSockets}[http://en.wikipedia.org/wiki/WebSocket]을 위한 기반으로 사용될 수 있다.
839 또한 이 방법은 일부 콘텐츠가 느린 자원에 의존하는 경우에
840 스로풋(throughtput)을 높이기 위해 사용될 수도 있다.
841
842 스트리밍 동작, 특히 동시 요청의 수는 애플리케이션을 서빙하는 웹서버에 크게 의존적이다.
843 어떤 서버, 예컨대 WEBRick 같은 경우는 아예 스트리밍을 지원조차 하지 못할 것이다.
844 만약 서버가 스트리밍을 지원하지 않는다면, 본문은 +stream+ 으로 전달된 블록이 수행을 마친 후에 한꺼번에 반환될 것이다.
845 스트리밍은 Shotgun에서는 작동하지 않는다.
846
847 만약 선택적 매개변수 +keep_open+이 설정되어 있다면, 스트림 객체에서 +close+를 호출하지 않을 것이고,
848 따라서 여러분은 나중에 실행 흐름 상의 어느 시점에서 스트림을 닫을 수 있다.
849 이 옵션은 Thin과 Rainbow 같은 이벤트 기반 서버에서만 작동한다.
850 다른 서버들은 여전히 스트림을 닫을 것이다:
851
852 set :server, :thin
853 connections = []
854
855 get '/' do
856 # 스트림을 열린 채 유지
857 stream(:keep_open) { |out| connections << out }
858 end
859
860 post '/' do
861 # 모든 열린 스트림에 쓰기
862 connections.each { |out| out << params[:message] << "\n" }
863 "message sent"
864 end
865
866 === 로깅(Logging)
867
868 In the request scope, the +logger+ helper exposes a +Logger+ instance:
869 요청 스코프(request scope) 내에서, +Logger+의 인스턴스인 +logger+ 헬퍼를 사용할 수 있다:
870
871 get '/' do
872 logger.info "loading data"
873 # ...
874 end
875
876 이 로거는 여러분이 Rack 핸들러에서 설정한 로그 셋팅을 자동으로 참고한다.
877 만약 로깅이 비활성이라면, 이 메서드는 더미(dummy) 객체를 반환할 것이며,
878 따라서 여러분은 라우터나 필터에서 이 부분에 대해 걱정할 필요는 없다.
879
880 로깅은 <tt>Sinatra::Application</tt>에서만 기본으로 활성화되어 있음에 유의하자.
881 만약 <tt>Sinatra::Base</tt>로부터 상속받은 경우라면 직접 활성화시켜 줘야 한다:
882
883 class MyApp < Sinatra::Base
884 configure :production, :development do
885 enable :logging
886 end
887 end
888
889 어떠한 로깅 미들웨어도 설정되지 않게 하려면, +logging+ 설정을 +nil+로 두면 된다.
890 그렇지만, 이럴 경우 +logger+는 +nil+을 반환할 것임에 유의하자.
891 통상적인 유스케이스는 여러분만의 로거를 사용하고자 할 경우일 것이다.
892 Sinatra는 <tt>env['rack.logger']</tt>에서 찾은 것을 사용할 것이다.
893
894 === 마임 타입(Mime Types)
895
896 <tt>send_file</tt>이나 정적인 파일을 사용할 때에 Sinatra가 인식하지 못하는 마임 타입이 있을 수 있다.
897 이 경우 +mime_type+을 사용하여 파일 확장자를 등록하면 된다:
898
899 configure do
900 mime_type :foo, 'text/foo'
901 end
902
903 또는 +content_type+ 헬퍼와 함께 사용할 수도 있다:
904
905 get '/' do
906 content_type :foo
907 "foo foo foo"
908 end
909
910 === URL 생성하기
911
912 URL을 생성하려면 +url+ 헬퍼 메서드를 사용해야 한다. 예를 들어 Haml에서:
913
914 %a{:href => url('/foo')} foo
915
916 이것은 리버스 프록시(reverse proxies)와 Rack 라우터를, 만약 존재한다면, 참고한다.
917
918 This method is also aliased to +to+ (see below for an example).
919 이 메서드는 +to+라는 별칭으로도 사용할 수 있다 (아래 예제 참조).
920
921 === 브라우저 재지정(Browser Redirect)
922
923 +redirect+ 헬퍼 메서드를 사용하여 브라우저 리다이렉트를 촉발시킬 수 있다:
924
925 get '/foo' do
926 redirect to('/bar')
927 end
928
929 여타 부가적인 매개변수들은 +halt+에서 전달한 인자들처럼 다루어 진다:
930
931 redirect to('/bar'), 303
932 redirect 'http://google.com', 'wrong place, buddy'
933
934 <tt>redirect back</tt>을 사용하면 사용자가 왔던 페이지로 다시 돌아가는 리다이렉트도 쉽게 할 수 있다:
935
936 get '/foo' do
937 "<a href='/bar'>do something</a>"
938 end
939
940 get '/bar' do
941 do_something
942 redirect back
943 end
944
945 리다이렉트와 함께 인자를 전달하려면, 쿼리에 붙이거나:
946
947 redirect to('/bar?sum=42')
948
949 또는 세션을 사용하면 된다:
950
951 enable :sessions
952
953 get '/foo' do
954 session[:secret] = 'foo'
955 redirect to('/bar')
956 end
957
958 get '/bar' do
959 session[:secret]
960 end
961
962 === 캐시 컨트롤(Cache Control)
963
964 헤더를 정확하게 설정하는 것은 적절한 HTTP 캐싱의 기본이다.
965
966 Cache-Control 헤더를 다음과 같이 간단하게 설정할 수 있다:
967
968 get '/' do
969 cache_control :public
970 "cache it!"
971 end
972
973 프로 팁: 캐싱은 사전 필터에서 설정하라:
974
975 before do
976 cache_control :public, :must_revalidate, :max_age => 60
977 end
978
979 +expires+ 헬퍼를 사용하여 그에 상응하는 헤더를 설정한다면,
980 <tt>Cache-Control</tt>이 자동으로 설정될 것이다:
981
982 before do
983 expires 500, :public, :must_revalidate
984 end
985
986 캐시를 잘 사용하려면, +etag+ 또는 +last_modified+의 사용을 고려해야 할 것이다.
987 무거운 작업을 하기 *전*에 이들 헬퍼를 호출할 것을 권장하는데,
988 이러면 만약 클라이언트 캐시에 현재 버전이 이미 들어 있을 경우엔 즉각 응답을 반환(flush)하게 될 것이다:
989
990 get '/article/:id' do
991 @article = Article.find params[:id]
992 last_modified @article.updated_at
993 etag @article.sha1
994 erb :article
995 end
996
997 {약한 ETag}[http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation]를 사용하는 것도 가능하다:
998
999 etag @article.sha1, :weak
1000
1001 이들 헬퍼는 어떠한 캐싱도 하지 않으며, 대신 필요한 정보를 캐시에 제공한다.
1002 여러분이 만약 손쉬운 리버스 프록시(reverse-proxy) 캐싱 솔루션을 찾고 있다면,
1003 {rack-cache}[http://rtomayko.github.com/rack-cache/]를 써보라:
1004
1005 require "rack/cache"
1006 require "sinatra"
1007
1008 use Rack::Cache
1009
1010 get '/' do
1011 cache_control :public, :max_age => 36000
1012 sleep 5
1013 "hello"
1014 end
1015
1016 정적 파일에 <tt>Cache-Control</tt> 헤더 정보를 추가하려면 <tt>:static_cache_control</tt> 설정(아래 참조)을 사용하라:
1017
1018 RFC 2616에 따르면 If-Match 또는 If-None-Match 헤더가 <tt>*</tt>로 설정된 경우 요청한 리소스(resource)가 이미 존재하느냐 여부에 따라 다르게 취급해야 한다고 되어 있다.
1019 Sinatra는 (get 처럼) 안전하거나 (put 처럼) 멱등인 요청에 대한 리소스는 이미 존재한다고 가정하며,
1020 반면 다른 리소스(예를 들면 post 요청 같은)의 경우는 새 리소스로 취급한다.
1021 이런 설정은 <tt>:new_resource</tt> 옵션으로 전달하여 변경할 수 있다:
1022
1023 get '/create' do
1024 etag '', :new_resource => true
1025 Article.create
1026 erb :new_article
1027 end
1028
1029 여전히 약한 ETag를 사용하고자 한다면, <tt>:kind</tt>으로 전달하자:
1030
1031 etag '', :new_resource => true, :kind => :weak
1032
1033 === 파일 전송하기(Sending Files)
1034
1035 파일을 전송하려면, <tt>send_file</tt> 헬퍼 메서드를 사용하면 된다:
1036
1037 get '/' do
1038 send_file 'foo.png'
1039 end
1040
1041 이 메서드는 몇 가지 옵션을 받는다:
1042
1043 send_file 'foo.png', :type => :jpg
1044
1045 옵션들:
1046
1047 [filename]
1048 응답에서의 파일명. 기본값은 실제 파일명이다.
1049
1050 [last_modified]
1051 Last-Modified 헤더값. 기본값은 파일의 mtime.
1052
1053 [type]
1054 사용할 컨텐츠 유형. 없으면 파일 확장자로부터 유추된다.
1055
1056 [disposition]
1057 Content-Disposition에서 사용됨. 가능한 값들: +nil+ (기본값),
1058 <tt>:attachment</tt> 및 <tt>:inline</tt>
1059
1060 [length]
1061 Content-Length, 기본값은 파일 크기.
1062
1063 [status]
1064 전송할 상태 코드. 오류 페이지로 정적 파일을 전송할 경우에 유용.
1065
1066 Rack 핸들러가 지원할 경우, Ruby 프로세스로부터의 스트리밍이 아닌 다른 수단을 사용할 수 있다.
1067 만약 이 헬퍼 메서드를 사용하게 되면, Sinatra는 자동으로 범위 요청(range request)을 처리할 것이다.
1068
1069 === 요청 객체에 접근하기(Accessing the Request Object)
1070
1071 인입되는 요청 객에는 요청 레벨(필터, 라우터, 오류 핸들러)에서 <tt>request</tt> 메서드를 통해 접근 가능하다:
1072
1073 # http://example.com/example 상에서 실행 중인 앱
1074 get '/foo' do
1075 t = %w[text/css text/html application/javascript]
1076 request.accept # ['text/html', '*/*']
1077 request.accept? 'text/xml' # true
1078 request.preferred_type(t) # 'text/html'
1079 request.body # 클라이언트로부터 전송된 요청 본문 (아래 참조)
1080 request.scheme # "http"
1081 request.script_name # "/example"
1082 request.path_info # "/foo"
1083 request.port # 80
1084 request.request_method # "GET"
1085 request.query_string # ""
1086 request.content_length # request.body의 길이
1087 request.media_type # request.body의 미디어 유형
1088 request.host # "example.com"
1089 request.get? # true (다른 동사에 대해 유사한 메서드 있음)
1090 request.form_data? # false
1091 request["SOME_HEADER"] # SOME_HEADER 헤더의 값
1092 request.referrer # 클라이언트의 리퍼러 또는 '/'
1093 request.user_agent # 사용자 에이전트 (:agent 조건에서 사용됨)
1094 request.cookies # 브라우저 쿠키의 해시
1095 request.xhr? # 이게 ajax 요청인가요?
1096 request.url # "http://example.com/example/foo"
1097 request.path # "/example/foo"
1098 request.ip # 클라이언트 IP 주소
1099 request.secure? # false (ssl 접속인 경우 true)
1100 request.forwarded? # true (리버스 프록시 하에서 작동 중이라면)
1101 request.env # Rack에 의해 처리되는 로우(raw) env 해시
1102 end
1103
1104 일부 옵션들, <tt>script_name</tt> 또는 <tt>path_info</tt>와 같은 일부 옵션은 쓸 수도 있다:
1105
1106 before { request.path_info = "/" }
1107
1108 get "/" do
1109 "all requests end up here"
1110 end
1111
1112 <tt>request.body</tt>는 IO 또는 StringIO 객체이다:
1113
1114 post "/api" do
1115 request.body.rewind # 누군가 이미 읽은 경우
1116 data = JSON.parse request.body.read
1117 "Hello #{data['name']}!"
1118 end
1119
1120 === 첨부(Attachments)
1121
1122 +attachment+ 헬퍼를 사용하여 브라우저에게 응답이 브라우저에 표시되는 게 아니라
1123 디스크에 저장되어야 함을 알릴 수 있다:
1124
1125 get '/' do
1126 attachment
1127 "store it!"
1128 end
1129
1130 이 때 파일명을 전달할 수도 있다:
1131
1132 get '/' do
1133 attachment "info.txt"
1134 "store it!"
1135 end
1136
1137 === 날짜와 시간 다루기
1138
1139 Sinatra는 +time_for_+ 헬퍼 메서드를 제공하는데, 이 메서드는 주어진 값으로부터 Time 객체를 생성한다.
1140 +DateTime+ 이나 +Date+ 또는 유사한 클래스들도 변환 가능하다:
1141
1142 get '/' do
1143 pass if Time.now > time_for('Dec 23, 2012')
1144 "still time"
1145 end
1146
1147 이 메서드는 내부적으로 +expires+ 나 +last_modified+ 같은 곳에서 사용된다.
1148 따라서 여러분은 애플리케이션에서 +time_for+를 오버라이딩하여
1149 이들 메서드의 동작을 쉽게 확장할 수 있다:
1150
1151 helpers do
1152 def time_for(value)
1153 case value
1154 when :yesterday then Time.now - 24*60*60
1155 when :tomorrow then Time.now + 24*60*60
1156 else super
1157 end
1158 end
1159 end
1160
1161 get '/' do
1162 last_modified :yesterday
1163 expires :tomorrow
1164 "hello"
1165 end
1166
1167 === 템플릿 파일 참조하기
1168
1169 <tt>find_template</tt>는 렌더링할 템플릿 파일을 찾는데 사용된다:
1170
1171 find_template settings.views, 'foo', Tilt[:haml] do |file|
1172 puts "could be #{file}"
1173 end
1174
1175 This is not really useful. But it is useful that you can actually override this
1176 method to hook in your own lookup mechanism. For instance, if you want to be
1177 able to use more than one view directory:
1178 이건 별로 유용하지 않다. 그렇지만 이 메서드를 오버라이드하여 여러분만의 참조 메커니즘에서 가로채는 것은 유용하다.
1179 예를 들어, 하나 이상의 뷰 디렉터리를 사용하고자 한다면:
1180
1181 set :views, ['views', 'templates']
1182
1183 helpers do
1184 def find_template(views, name, engine, &block)
1185 Array(views).each { |v| super(v, name, engine, &block) }
1186 end
1187 end
1188
1189 또다른 예제는 각각의 엔진마다 다른 디렉터리를 사용할 경우다:
1190
1191 set :views, :sass => 'views/sass', :haml => 'templates', :default => 'views'
1192
1193 helpers do
1194 def find_template(views, name, engine, &block)
1195 _, folder = views.detect { |k,v| engine == Tilt[k] }
1196 folder ||= views[:default]
1197 super(folder, name, engine, &block)
1198 end
1199 end
1200
1201 여러분은 이것을 간단하게 확장(extension)으로 만들어 다른 사람들과 공유할 수 있다!
1202
1203 <tt>find_template</tt>은 그 파일이 실제 존재하는지 검사하지 않음에 유의하자.
1204 대신 모든 가능한 경로에 대해 주어진 블록을 호출할 뿐이다.
1205 이것은 성능 문제는 아닌 것이, +render+는 파일이 발견되는 즉시 +break+를 사용할 것이기 때문이다.
1206 또한, 템플릿 위치(그리고 콘텐츠)는 개발 모드에서 실행 중이 아니라면 캐시될 것이다.
1207 정말로 멋진 메세드를 작성하고 싶다면 이 점을 명심하자.
1208
1209 == 설정(Configuration)
1210
1211 모든 환경에서, 시작될 때, 한번만 실행:
1212
1213 configure do
1214 # 옵션 하나 설정
1215 set :option, 'value'
1216
1217 # 여러 옵션 설정
1218 set :a => 1, :b => 2
1219
1220 # `set :option, true`와 동일
1221 enable :option
1222
1223 # `set :option, false`와 동일
1224 disable :option
1225
1226 # 블록으로 동적인 설정을 할 수도 있음
1227 set(:css_dir) { File.join(views, 'css') }
1228 end
1229
1230 환경(RACK_ENV 환경 변수)이 <tt>:production</tt>일 때만 실행:
1231
1232 configure :production do
1233 ...
1234 end
1235
1236 환경이 <tt>:production</tt> 또는 <tt>:test</tt>일 때 실행:
1237
1238 configure :production, :test do
1239 ...
1240 end
1241
1242 이들 옵션은 <tt>settings</tt>를 통해 접근 가능하다:
1243
1244 configure do
1245 set :foo, 'bar'
1246 end
1247
1248 get '/' do
1249 settings.foo? # => true
1250 settings.foo # => 'bar'
1251 ...
1252 end
1253
1254 === 공격 방어 설정하기(Configuring attack protection)
1255
1256 Sinatra는 {Rack::Protection}[https://github.com/rkh/rack-protection#readme]을 사용하여
1257 일반적인, 일어날 수 있는 공격에 대비한다.
1258 이 부분은 간단하게 비활성시킬 수 있다(성능 향상 효과를 가져올 것이다):
1259
1260 disable :protection
1261
1262 하나의 방어층만 스킵하려면, 옵션 해시에 +protection+을 설정하면 된다:
1263
1264 set :protection, :except => :path_traversal
1265
1266 방어막 여러 개를 비활성하려면, 배열로 주면 된다:
1267
1268 set :protection, :except => [:path_traversal, :session_hijacking]
1269
1270 === 가능한 설정들(Available Settings)
1271
1272 [absolute_redirects] 만약 비활성이면, Sinatra는 상대경로 리다이렉트를 허용할 것이지만,
1273 이렇게 되면 Sinatra는 더 이상 오직 절대경로 리다이렉트만 허용하고 있는
1274 RFC 2616(HTTP 1.1)에 위배될 것이다.
1275
1276 적정하게 설정되지 않은 리버스 프록시 하에서 앱을 실행 중이라면 활성화시킬 것.
1277 +rul+ 헬퍼는, 만약 두 번째 매개변수로 +false+를 전달하지만 않는다면,
1278 여전히 절대경로 URL을 생성할 것임에 유의하자.
1279
1280 기본값은 비활성.
1281
1282 [add_charsets] <tt>content_type</tt>가 문자셋 정보에 자동으로 추가하게 될 마임(mime) 타입.
1283
1284 이 옵션은 오버라이딩하지 말고 추가해야 한다:
1285
1286 settings.add_charsets << "application/foobar"
1287
1288 [app_file] 메인 애플리케이션 파일의 경로. 프로젝트 루트와 뷰, 그리고 public 폴더, 인라인 템플릿을
1289 파악할 때 사용됨.
1290
1291 [bind] 바인드할 IP 주소(기본값: 0.0.0.0).
1292 오직 빌트인(built-in) 서버에서만 사용됨.
1293
1294 [default_encoding] 모를 때 가정할 인코딩
1295 (기본값은 <tt>"utf-8"</tt>).
1296
1297 [dump_errors] 로그로 에러 출력.
1298
1299 [environment] 현재 환경, 기본값은 <tt>ENV['RACK_ENV']</tt> 또는 알 수 없을 경우 "development".
1300
1301 [logging] 로거(logger) 사용.
1302
1303 [lock] 매 요청에 걸쳐 잠금(lock)을 설정. Ruby 프로세스 당 요청을 동시에 할 경우.
1304
1305 앱이 스레드 안전(thread-safe)이 아니라면 활성화시킬 것.
1306 기본값은 비활성.
1307
1308 [method_override] put/delete를 지원하지 않는 브라우저에서 put/delete 폼을 허용하는
1309 <tt>_method</tt> 꼼수 사용.
1310
1311 [port] 접속 포트. 빌트인 서버에서만 사용됨.
1312
1313 [prefixed_redirects] 절대경로가 주어지지 않은 리다이렉트에 <tt>request.script_name</tt>를
1314 삽입할지 여부. 이렇게 하면 <tt>redirect '/foo'</tt>는 <tt>redirect to('/foo')</tt>
1315 처럼 동작. 기본값은 비활성.
1316
1317 [protection] 웹 공격 방어를 활성화시킬 건지 여부. 위의 보안 섹션 참조.
1318
1319 [public_folder] public 파일이 제공될 폴더의 경로.
1320 static 파일 제공이 활성화된 경우만 사용됨(아래 <tt>static</tt>참조).
1321 만약 설정이 없으면 <tt>app_file</tt>로부터 유추됨.
1322
1323 [reload_templates] 요청 간에 템플릿을 리로드(reload)할 건지 여부.
1324 개발 모드에서는 활성됨.
1325
1326 [root] 프로젝트 루트 디렉터리 경로.
1327 설정이 없으면 +app_file+ 설정으로부터 유추됨.
1328
1329 [raise_errors] 예외 발생(애플리케이션은 중단됨).
1330 기본값은 <tt>environment</tt>가 <tt>"test"</tt>인 경우는 활성, 그렇지 않으면 비활성.
1331
1332 [run] 활성화되면, Sinatra가 웹서버의 시작을 핸들링.
1333 rackup 또는 다른 도구를 사용하는 경우라면 활성화시키지 말 것.
1334
1335 [running] 빌트인 서버가 실행 중인지?
1336 이 설정은 변경하지 말 것!
1337
1338 [server] 빌트인 서버로 사용할 서버 또는 서버 목록.
1339 기본값은 ['thin', 'mongrel', 'webrick']이며 순서는 우선순위를 의미.
1340
1341 [sessions] <tt>Rack::Session::Cookie</tt>를 사용한 쿠키 기반 세션 활성화.
1342 보다 자세한 정보는 '세션 사용하기' 참조.
1343
1344 [show_exceptions] 예외 발생 시에 브라우저에 스택 추적을 보임.
1345 기본값은 <tt>environment</tt>가 <tt>"development"</tt>인 경우는 활성, 나머지는 비활성.
1346
1347 [static] Sinatra가 정적(static) 파일을 핸들링할 지 여부.
1348 이 기능을 수행하는 서버를 사용하는 경우라면 비활성시킬 것.
1349 비활성시키면 성능이 올라감.
1350 기본값은 전통적 방식에서는 활성, 모듈 앱에서는 비활성.
1351
1352 [static_cache_control] Sinatra가 정적 파일을 제공하는 경우, 응답에 <tt>Cache-Control</tt> 헤더를 추가할 때 설정.
1353 +cache_control+ 헬퍼를 사용.
1354 기본값은 비활성.
1355 여러 값을 설정할 경우는 명시적으로 배열을 사용할 것:
1356 <tt>set :static_cache_control, [:public, :max_age => 300]</tt>
1357
1358 [threaded] +true+로 설정하면, Thin이 요청을 처리하는데 있어 <tt>EventMachine.defer</tt>를 사용하도록 함.
1359
1360 [views] 뷰 폴더 경로. 설정하지 않은 경우 <tt>app_file</tt>로부터 유추됨.
1361
1362 == 환경(Environments)
1363
1364 환경은 +RACK_ENV+ 환경 변수를 통해서도 설정할 수 있다. 기본값은 "development"다.
1365 이 모드에서, 모든 템플릿들은 요청 간에 리로드된다.
1366 특별한 <tt>not_found</tt> 와 <tt>error</tt> 핸들러가 이 환경에 설치되기 때문에
1367 브라우저에서 스택 추적을 볼 수 있을 것이다.
1368 <tt>"production"</tt>과 <tt>"test"</tt>에서는 템플릿은 캐시되는 게 기본값이다.
1369
1370 다른 환경으로 실행시키려면 <tt>-e</tt>옵션을 사용하면 된다:
1371
1372 ruby my_app.rb -e [ENVIRONMENT]
1373
1374 현재 설정된 환경이 무엇인지 검사하기 위해 사전 정의된 +development?+, +test?+ 및 +production?+ 메서드를
1375 사용할 수 있다.
1376
1377 == 예외 처리(Error Handling)
1378
1379 예외 핸들러는 라우터 및 사전 필터와 동일한 맥락에서 실행된다.
1380 이 말인즉, 이들이 제공하는 모든 것들을 사용할 수 있다는 말이다. 예를 들면 <tt>haml</tt>,
1381 <tt>erb</tt>, <tt>halt</tt>, 등등.
1382
1383 === 찾을 수 없음(Not Found)
1384
1385 <tt>Sinatra::NotFound</tt> 예외가 발생하거나 또는 응답의 상태 코드가 404라면,
1386 <tt>not_found</tt> 핸들러가 호출된다:
1387
1388 not_found do
1389 '아무 곳에도 찾을 수 없습니다.'
1390 end
1391
1392 === 오류(Error)
1393
1394 +error+ 핸들러는 라우터 또는 필터에서 뭐든 오류가 발생할 경우에 호출된다.
1395 예외 객체는 Rack 변수 <tt>sinatra.error</tt>로부터 얻을 수 있다:
1396
1397 error do
1398 '고약한 오류가 발생했군요 - ' + env['sinatra.error'].name
1399 end
1400
1401 사용자 정의 오류:
1402
1403 error MyCustomError do
1404 '무슨 일이 생겼나면요...' + env['sinatra.error'].message
1405 end
1406
1407 그런 다음, 이 오류가 발생하면:
1408
1409 get '/' do
1410 raise MyCustomError, '안좋은 일'
1411 end
1412
1413 다음을 얻는다:
1414
1415 무슨 일이 생겼냐면요... 안좋은 일
1416
1417 또는, 상태 코드에 대해 오류 핸들러를 설치할 수 있다:
1418
1419 error 403 do
1420 '액세스가 금지됨'
1421 end
1422
1423 get '/secret' do
1424 403
1425 end
1426
1427 Or a range:
1428
1429 error 400..510 do
1430 '어이쿠'
1431 end
1432
1433 Sinatra는 개발 환경에서 동작할 경우에
1434 특별한 <tt>not_found</tt> 와 <tt>error</tt> 핸들러를 설치한다.
1435
1436 == Rack 미들웨어(Rack Middleware)
1437
1438 Sinatra는 Rack[http://rack.rubyforge.org/] 위에서 동작하며,
1439 Rack은 루비 웹 프레임워크를 위한 최소한의 표준 인터페이스이다.
1440 Rack이 애플리케이션 개발자들에게 제공하는 가장 흥미로운 기능 중 하나가 바로
1441 "미들웨어(middleware)"에 대한 지원이며, 여기서 미들웨어란 서버와 여러분의 애플리케이션 사이에
1442 위치하면서 HTTP 요청/응답을 모니터링하거나/또는 조작함으로써
1443 다양한 유형의 공통 기능을 제공하는 컴포넌트(component)다.
1444
1445 Sinatra는 톱레벨의 +use+ 메서드를 사용하여 Rack 미들웨어의 파이프라인을 만드는 일을 식은 죽 먹기로 만든다:
1446
1447 require 'sinatra'
1448 require 'my_custom_middleware'
1449
1450 use Rack::Lint
1451 use MyCustomMiddleware
1452
1453 get '/hello' do
1454 'Hello World'
1455 end
1456
1457 +use+의 의미는 Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] DSL
1458 (rackup 파일에서 가장 많이 사용된다)에서 정의한 것들과 동일하다.
1459 예를 들어, +use+ 메서드는 블록 뿐 아니라 여러 개의/가변적인 인자도 받는다:
1460
1461 use Rack::Auth::Basic do |username, password|
1462 username == 'admin' && password == 'secret'
1463 end
1464
1465 Rack은 로깅, 디버깅, URL 라우팅, 인증, 그리고 세센 핸들링을 위한 다양한 표준 미들웨어로 분산되어 있다.
1466 Sinatra는 설정에 기반하여 이들 컴포넌트들 중 많은 것들을 자동으로 사용하며,
1467 따라서 여러분은 일반적으로는 +use+를 명시적으로 사용할 필요가 없을 것이다.
1468
1469 유용한 미들웨어들은
1470 {rack}[https://github.com/rack/rack/tree/master/lib/rack],
1471 {rack-contrib}[https://github.com/rack/rack-contrib#readme],
1472 {CodeRack}[http://coderack.org/] 또는
1473 {Rack wiki}[https://github.com/rack/rack/wiki/List-of-Middleware]
1474 에서 찾을 수 있다.
1475
1476 == 테스팅(Testing)
1477
1478 Sinatra 테스트는 Rack 기반 어떠한 테스팅 라이브러리 또는 프레임워크를 사용하여도 작성할 수 있다.
1479 {Rack::Test}[http://rdoc.info/github/brynary/rack-test/master/frames]를 권장한다:
1480
1481 require 'my_sinatra_app'
1482 require 'test/unit'
1483 require 'rack/test'
1484
1485 class MyAppTest < Test::Unit::TestCase
1486 include Rack::Test::Methods
1487
1488 def app
1489 Sinatra::Application
1490 end
1491
1492 def test_my_default
1493 get '/'
1494 assert_equal 'Hello World!', last_response.body
1495 end
1496
1497 def test_with_params
1498 get '/meet', :name => 'Frank'
1499 assert_equal 'Hello Frank!', last_response.body
1500 end
1501
1502 def test_with_rack_env
1503 get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
1504 assert_equal "You're using Songbird!", last_response.body
1505 end
1506 end
1507
1508 == Sinatra::Base - 미들웨어(Middleware), 라이브러리(Libraries), 그리고 모듈 앱(Modular Apps)
1509
1510 톱레벨에서 앱을 정의하는 것은 마이크로 앱(micro-app) 수준에서는 잘 동작하지만,
1511 Rack 미들웨어나, Rails 메탈(metal) 또는 서버 컴포넌트를 갖는 간단한 라이브러리, 또는 더 나아가
1512 Sinatra 익스텐션(extension) 같은 재사용 가능한 컴포넌트들을 구축할 경우에는 심각한 약점을 가진다.
1513 톱레벨은 마이크로 앱 스타일의 설정을 가정한다(즉, 하나의 단일 애플리케이션 파일과
1514 <tt>./public</tt> 및 <tt>./views</tt> 디렉터리, 로깅, 예외 상세 페이지 등등).
1515 이게 바로 <tt>Sinatra::Base</tt>가 필요한 부분이다:
1516
1517 require 'sinatra/base'
1518
1519 class MyApp < Sinatra::Base
1520 set :sessions, true
1521 set :foo, 'bar'
1522
1523 get '/' do
1524 'Hello world!'
1525 end
1526 end
1527
1528 <tt>Sinatra::Base</tt> 서브클래스에서 사용가능한 메서드들은 톱레벨 DSL로 접근 가능한 것들과 동일하다.
1529 대부분의 톱레벨 앱들이 다음 두 가지만 수정하면 <tt>Sinatra::Base</tt> 컴포넌트로 변환 가능하다:
1530
1531 * 파일은 +sinatra+가 아닌 <tt>sinatra/base</tt>를 require해야 하며, 그렇지 않으면
1532 모든 Sinatra의 DSL 메서드들이 메인 네임스페이스에 불러지게 된다.
1533 * 앱의 라우터, 예외 핸들러, 필터, 그리고 옵션들을 <tt>Sinatra::Base</tt>의 서브클래스에 둘 것.
1534
1535 <tt>Sinatra::Base</tt>는 빈서판(blank slate)이다.
1536 빌트인 서버를 비롯한 대부분의 옵션들이 기본값으로 꺼져 있다.
1537 가능한 옵션들과 그 작동에 대한 상세는 {Options and Configuration}[http://sinatra.github.com/configuration.html]을 참조할 것.
1538
1539 === 모듈(Modular) vs. 전통적 방식(Classic Style)
1540
1541 일반적인 믿음과는 반대로, 전통적 방식에 잘못된 부분은 없다.
1542 여러분 애플리케이션에 맞다면, 모듈 애플리케이션으로 전환할 필요는 없다.
1543
1544 모듈 방식이 아닌 전통적 방식을 사용할 경우 생기는 주된 단점은 루비 프로세스 당
1545 오직 하나의 Sinatra 애플리케이션만 사용할 수 있다는 점이다.
1546 만약 하나 이상을 사용할 계획이라면, 모듈 방식으로 전환하라.
1547 모듈 방식과 전통적 방식을 섞어쓰지 못할 이유는 없다.
1548
1549 하나의 방식에서 다른 것으로 전환할 경우에는, 기본값 설정의 미묘한 차이에 유의해야 한다:
1550
1551 설정 전통적 방식 모듈 방식
1552
1553
1554 app_file sinatra를 로딩하는 파일 Sinatra::Base를 서브클래싱한 파일
1555 run $0 == app_file false
1556 logging true false
1557 method_override true false
1558 inline_templates true false
1559 static true false
1560
1561
1562 === 모듈 애플리케이션(Modular Application) 제공하기
1563
1564 모듈 앱을 시작하는 두 가지 일반적인 옵션이 있는데,
1565 공격적으로 <tt>run!</tt>으로 시작하거나:
1566
1567 # my_app.rb
1568 require 'sinatra/base'
1569
1570 class MyApp < Sinatra::Base
1571 # ... 여기에 앱 코드가 온다 ...
1572
1573 # 루비 파일이 직접 실행될 경우에 서버를 시작
1574 run! if app_file == $0
1575 end
1576
1577 다음과 같이 시작:
1578
1579 ruby my_app.rb
1580
1581 또는 <tt>config.ru</tt>와 함께 사용하며, 이 경우는 어떠한 Rack 핸들러라도 사용할 수 있다:
1582
1583 # config.ru
1584 require './my_app'
1585 run MyApp
1586
1587 실행:
1588
1589 rackup -p 4567
1590
1591 === config.ru로 전통적 방식의 애플리케이션 사용하기
1592
1593 앱 파일을 다음과 같이 작성하고:
1594
1595 # app.rb
1596 require 'sinatra'
1597
1598 get '/' do
1599 'Hello world!'
1600 end
1601
1602 대응하는 <tt>config.ru</tt>는 다음과 같이 작성:
1603
1604 require './app'
1605 run Sinatra::Application
1606
1607 === 언제 config.ru를 사용할까?
1608
1609 Good signs you probably want to use a <tt>config.ru</tt>:
1610 다음은 <tt>config.ru</tt>를 사용하게 될 징후들이다:
1611
1612 * 다른 Rack 핸들러(Passenger, Unicorn, Heroku, ...)로 배포하고자 할 때.
1613 * 하나 이상의 <tt>Sinatra::Base</tt> 서브클래스를 사용하고자 할 때.
1614 * Sinatra를 최종점(endpoint)이 아니라, 오로지 미들웨어로만 사용하고자 할 때.
1615
1616 <b>모듈 방식으로 전환했다는 이유만으로 <tt>config.ru</tt>로 전환할 필요는 없으며,
1617 또한 <tt>config.ru</tt>를 사용한다고 해서 모듈 방식을 사용해야 하는 것도 아니다.</b>
1618
1619 === Sinatra를 미들웨어로 사용하기
1620
1621 Sinatra에서 다른 Rack 미들웨어를 사용할 수 있을 뿐 아니라,
1622 모든 Sinatra 애플리케이션은 순차로 어떠한 Rack 종착점 앞에 미들웨어로 추가될 수 있다.
1623 이 종착점은 다른 Sinatra 애플리케이션이 될 수도 있고,
1624 또는 Rack 기반의 어떠한 애플리케이션(Rails/Ramaze/Camping/...)이라도 가능하다:
1625
1626 require 'sinatra/base'
1627
1628 class LoginScreen < Sinatra::Base
1629 enable :sessions
1630
1631 get('/login') { haml :login }
1632
1633 post('/login') do
1634 if params[:name] == 'admin' && params[:password] == 'admin'
1635 session['user_name'] = params[:name]
1636 else
1637 redirect '/login'
1638 end
1639 end
1640 end
1641
1642 class MyApp < Sinatra::Base
1643 # 미들웨어는 사전 필터보다 앞서 실행됨
1644 use LoginScreen
1645
1646 before do
1647 unless session['user_name']
1648 halt "접근 거부됨, <a href='/login'>로그인</a> 하세요."
1649 end
1650 end
1651
1652 get('/') { "Hello #{session['user_name']}." }
1653 end
1654
1655 === 동적인 애플리케이션 생성(Dynamic Application Creation)
1656
1657 경우에 따라선 어떤 상수에 할당하지 않고 런타임에서 새 애플리케이션들을 생성하고 싶을 수도 있을 것인데,
1658 이 때는 <tt>Sinatra.new</tt>를 쓰면 된다:
1659
1660 require 'sinatra/base'
1661 my_app = Sinatra.new { get('/') { "hi" } }
1662 my_app.run!
1663
1664 이것은 선택적 인자로 상속할 애플리케이션을 받는다:
1665
1666 # config.ru
1667 require 'sinatra/base'
1668
1669 controller = Sinatra.new do
1670 enable :logging
1671 helpers MyHelpers
1672 end
1673
1674 map('/a') do
1675 run Sinatra.new(controller) { get('/') { 'a' } }
1676 end
1677
1678 map('/b') do
1679 run Sinatra.new(controller) { get('/') { 'b' } }
1680 end
1681
1682 이것은 Sintra 익스텐션을 테스팅하거나 또는 여러분의 라이브러리에서 Sinatra를 사용할 경우에 특히 유용하다.
1683
1684 또한 이 방법은 Sinatra를 미들웨어로 사용하는 것을 아주 쉽게 만들어 준다:
1685
1686 require 'sinatra/base'
1687
1688 use Sinatra do
1689 get('/') { ... }
1690 end
1691
1692 run RailsProject::Application
1693
1694 == 범위(Scopes)와 바인딩(Binding)
1695
1696 현재 어느 범위에 있느냐가 어떤 메서드와 변수를 사용할 수 있는지를 결정한다.
1697
1698 === 애플리케이션/클래스 범위
1699
1700 모든 Sinatra 애플리케이션은 <tt>Sinatra::Base</tt>의 서브클래스에 대응된다.
1701 만약 톱레벨 DSL (<tt>require 'sinatra'</tt>)을 사용한다면,
1702 이 클래스는 <tt>Sinatra::Application</tt>이며, 그렇지 않을 경우라면 여러분이 명시적으로 생성한
1703 그 서브클래스가 된다. 클래스 레벨에서는 +get+ 이나 +before+ 같은 메서드들을 가지나,
1704 +request+ 객체나 +session+ 에는 접근할 수 없다. 왜냐면 모든 요청에 대해
1705 애플리케이션 클래스는 오직 하나이기 때문이다.
1706
1707 +set+으로 생성한 옵션들은 클래스 레벨의 메서드들이다:
1708
1709 class MyApp < Sinatra::Base
1710 # 이봐요, 저는 애플리케이션 범위에 있다구요!
1711 set :foo, 42
1712 foo # => 42
1713
1714 get '/foo' do
1715 # 저기요, 전 이제 더 이상 애플리케이션 범위 속에 있지 않아요!
1716 end
1717 end
1718
1719 다음 속에 있을 때 애플리케이션 범위가 된다:
1720
1721 * 애플리케이션 클래스 본문
1722 * 확장으로 정의된 메서드
1723 * +helpers+로 전달된 블록
1724 * +set+의 값으로 사용된 Procs/blocks
1725 * <tt>Sinatra.new</tt>로 전달된 블록
1726
1727 범위 객체 (클래스)는 다음과 같이 접근할 수 있다:
1728
1729 * configure 블록으로 전달된 객체를 통해(<tt>configure { |c| ... }</tt>)
1730 * 요청 범위 내에서 +settings+
1731
1732 === 요청/인스턴스 범위
1733
1734 매 요청마다, 애플리케이션 클래스의 새 인스턴스가 생성되고 모든 핸들러 블록은 그 범위 내에서 실행된다.
1735 이 범위 내에서 여러분은 +request+ 와 +session+ 객체에 접근하거나
1736 +erb+ 나 +haml+ 같은 렌더링 메서드를 호출할 수 있다.
1737 요청 범위 내에서 애플리케이션 범위는 +settings+ 헬퍼를 통해 접근 가능하다:
1738
1739 class MyApp < Sinatra::Base
1740 # 이봐요, 전 애플리케이션 범위에 있다구요!
1741 get '/define_route/:name' do
1742 # '/define_route/:name'의 요청 범위
1743 @value = 42
1744
1745 settings.get("/#{params[:name]}") do
1746 # "/#{params[:name]}"의 요청 범위
1747 @value # => nil (동일한 요청이 아님)
1748 end
1749
1750 "라우터가 정의됨!"
1751 end
1752 end
1753
1754 다음 속에 있을 때 요청 범위 바인딩이 된다:
1755
1756 * get/head/post/put/delete/options 블록
1757 * before/after 필터
1758 * 헬퍼(helper) 메서드
1759 * 템플릿/뷰
1760
1761 === 위임 범위(Delegation Scope)
1762
1763 위임 범위(delegation scope)는 메서드를 단순히 클래스 범위로 보낸다(forward).
1764 그렇지만, 100% 클래스 범위처럼 움직이진 않는데, 왜냐면 클래스 바인딩을 갖지 않기 때문이다.
1765 오직 명시적으로 위임(delegation) 표시된 메서드들만 사용 가능하며
1766 또한 클래스 범위와 변수/상태를 공유하지 않는다 (유의: +self+가 다르다).
1767 <tt>Sinatra::Delegator.delegate :method_name</tt>을 호출하여 메서드 위임을 명시적으로 추가할 수 있다.
1768
1769 다음 속에 있을 때 위임 범위 바인딩을 갖는다:
1770
1771 * 톱레벨 바인딩, <tt>require "sinatra"</tt>를 한 경우
1772 * <tt>Sinatra::Delegator</tt> 믹스인으로 확장된 객체
1773
1774 직접 코드를 살펴보길 바란다:
1775 {Sinatra::Delegator 믹스인}[https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/base.rb#L1609-1633]
1776 코드는 {메인 객체를 확장한 것}[https://github.com/sinatra/sinatra/blob/ca06364/lib/sinatra/main.rb#L28-30]이다.
1777
1778 == 명령행(Command Line)
1779
1780 Sinatra 애플리케이션은 직접 실행할 수 있다:
1781
1782 ruby myapp.rb [-h] [-x] [-e ENVIRONMENT] [-p PORT] [-o HOST] [-s HANDLER]
1783
1784 옵션들:
1785
1786 -h # 도움말
1787 -p # 포트 설정 (기본값은 4567)
1788 -o # 호스트 설정 (기본값은 0.0.0.0)
1789 -e # 환경 설정 (기본값은 development)
1790 -s # rack 서버/핸들러 지정 (기본값은 thin)
1791 -x # mutex 잠금 켜기 (기본값은 off)
1792
1793 == 요구사항(Requirement)
1794
1795 다음의 루비 버전은 공식적으로 지원한다:
1796
1797 [ Ruby 1.8.7 ]
1798 1.8.7은 완전하게 지원되지만, 꼭 그래야할 특별한 이유가 없다면,
1799 1.9.2로 업그레이드하거나 또는 JRuby나 Rubinius로 전환할 것을 권장한다.
1800 1.8.7에 대한 지원은 Sinatra 2.0과 Ruby 2.0 이전에는 중단되지 않을 것이다.
1801 또한 그때도, 우리는 계속 지원할 것이다.
1802 <b>Ruby 1.8.6은 더이상 지원되지 않는다.</b>
1803 만약 1.8.6으로 실행하려 한다면, Sinatra 1.2로 다운그레이드하라.
1804 Sinatra 1.4.0이 릴리스될 때 까지는 버그 픽스를 받을 수 있을 것이다.
1805
1806 [ Ruby 1.9.2 ]
1807 1.9.2는 완전하게 지원되면 권장된다. Radius와 Maraby는 현재 1.9와 호환되지 않음에 유의하라.
1808 1.9.2p0은, Sinatra를 실행했을 때 세그먼트 오류가 발생한다고 알려져 있으니 사용하지 말라.
1809 Ruby 1.9.4/2.0 릴리스까지는 적어도 지원을 계속할 것이며,
1810 최신 1.9 릴리스에 대한 지원은 Ruby 코어팀이 지원하고 있는 한 계속 지원할 것이다.
1811
1812 [ Ruby 1.9.3 ]
1813 1.9.3은 완전하게 지원된다. 그렇지만 프로덕션에서의 사용은
1814 보다 상위의 패치 레벨이 릴리스될 때까지 기다리길 권장한다(현재는 p0).
1815 이전 버전에서 1.9.3으로 전환할 경우 모든 세션이 무효화된다는 점을 유의하라.
1816
1817 [ Rubinius ]
1818 Rubinius는 공식적으로 지원되며 (Rubinius >= 1.2.4), 모든 템플릿 언어를 포함한 모든 것들이 작동한다.
1819 조만간 출시될 2.0 릴리스 역시 지원할 것이다.
1820
1821 [ JRuby ]
1822 JRuby는 공식적으로 지원된다 (JRuby >= 1.6.5). 서드 파티 템플릿 라이브러리와의 문제는 알려진 바 없지만,
1823 만약 JRuby를 사용하기로 했다면, JRuby rack 핸들러를 찾아보길 바란다.
1824 Thin 웹 서버는 JRuby에서 완전하게 지원되지 않는다.
1825 JRuby의 C 확장 지원은 아직 실험 단계이며, RDiscount, Redcarpet 및 RedCloth가 현재
1826 이 영향을 받는다.
1827
1828 또한 우리는 새로 나오는 루비 버전을 주시한다.
1829
1830 다음 루비 구현체들은 공식적으로 지원하지 않지만
1831 여전히 Sinatra를 실행할 수 있는 것으로 알려져 있다:
1832
1833 * JRuby와 Rubinius 예전 버전
1834 * Ruby Enterprise Edition
1835 * MacRuby, Maglev, IronRuby
1836 * Ruby 1.9.0 및 1.9.1 (그러나 이 버전들은 사용하지 말 것을 권함)
1837
1838 공식적으로 지원하지 않는다는 것의 의미는 무언가가 그쪽에서만 잘못되고
1839 지원되는 플랫폼에서는 그러지 않을 경우, 우리의 문제가 아니라 그쪽의 문제로 간주한다는 뜻이다.
1840
1841 또한 우리는 CI를 ruby-head (곧 나올 2.0.0) 과 1.9.4 브랜치에 맞춰 실행하지만,
1842 계속해서 변하고 있기 때문에 아무 것도 보장할 수는 없다.
1843 1.9.4p0와 2.0.0p0가 지원되길 기대한다.
1844
1845 Sinatra는 선택한 루비 구현체가 지원하는 어떠한 운영체제에서도 작동해야 한다.
1846
1847 현재 Cardinal, SmallRuby, BlueRuby 또는 1.8.7 이전의 루비 버전에서는
1848 Sinatra를 실행할 수 없을 것이다.
1849
1850 == 최신(The Bleeding Edge)
1851
1852 Sinatra의 가장 최근 코드를 사용하고자 한다면,
1853 여러분 애플리케이션을 마스터 브랜치에 맞춰 실행하면 되지만, 덜 안정적일 것임에 분명하다.
1854
1855 또한 우리는 가끔 사전배포(prerelease) 젬을 푸시하기 때문에, 다음과 같이 할 수 있다
1856
1857 gem install sinatra --pre
1858
1859 최신 기능들을 얻기 위해선
1860
1861 === Bundler를 사용하여
1862
1863 여러분 애플리케이션을 최신 Sinatra로 실행하고자 한다면,
1864 {Bundler}[http://gembundler.com/]를 사용할 것을 권장한다.
1865
1866 우선, 아직 설치하지 않았다면 bundler를 설치한다:
1867
1868 gem install bundler
1869
1870 그런 다음, 프로젝트 디렉터리에서, +Gemfile+을 하나 만든다:
1871
1872 source :rubygems
1873 gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git"
1874
1875 # 다른 의존관계들
1876 gem 'haml' # 예를 들어, haml을 사용한다면
1877 gem 'activerecord', '~> 3.0' # 아마도 ActiveRecord 3.x도 필요할 것
1878
1879 이 속에 애플리케이션의 모든 의존관계를 나열해야 함에 유의하자.
1880 그렇지만, Sinatra가 직접적인 의존관계에 있는 것들 (Rack과 Tilt)은
1881 Bundler가 자동으로 추출하여 추가할 것이다.
1882
1883 이제 여러분은 다음과 같이 앱을 실행할 수 있다:
1884
1885 bundle exec ruby myapp.rb
1886
1887 === 직접 하기(Roll Your Own)
1888
1889 로컬 클론(clone)을 생성한 다음 <tt>$LOAD_PATH</tt>에 <tt>sinatra/lib</tt> 디렉터리를 주고
1890 여러분 앱을 실행한다:
1891
1892 cd myapp
1893 git clone git://github.com/sinatra/sinatra.git
1894 ruby -Isinatra/lib myapp.rb
1895
1896 이후에 Sinatra 소스를 업데이트하려면:
1897
1898 cd myapp/sinatra
1899 git pull
1900
1901 === 전역으로 설치(Install Globally)
1902
1903 젬을 직접 빌드할 수 있다:
1904
1905 git clone git://github.com/sinatra/sinatra.git
1906 cd sinatra
1907 rake sinatra.gemspec
1908 rake install
1909
1910 만약 젬을 루트로 설치한다면, 마지막 단계는 다음과 같이 해야 한다
1911
1912 sudo rake install
1913
1914 == 버저닝(Versioning)
1915
1916 Sinatra는 {시맨틱 버저닝Semantic Versioning}[http://semver.org/]을 준수한다.
1917 SemVer 및 SemVerTag 둘 다 해당된.
1918
1919
1920 == 더 읽을 거리(Further Reading)
1921
1922 * {프로젝트 웹사이트}[http://www.sinatrarb.com/] - 추가 문서들, 뉴스, 그리고 다른 리소스들에 대한 링크.
1923 * {기여하기}[http://www.sinatrarb.com/contributing] - 버그를 찾았나요? 도움이 필요한가요? 패치를 하셨나요?
1924 * {이슈 트래커}[http://github.com/sinatra/sinatra/issues]
1925 * {트위터}[http://twitter.com/sinatra]
1926 * {Mailing List}[http://groups.google.com/group/sinatrarb/topics]
1927 * {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] http://freenode.net
1928 * {Sinatra Book}[http://sinatra-book.gittr.com] Cookbook 튜토리얼
1929 * {Sinatra Recipes}[http://recipes.sinatrarb.com/] 커뮤니티가 만드는 레시피
1930 * http://rubydoc.info에 있는 {최종 릴리스}[http://rubydoc.info/gems/sinatra]
1931 또는 {current HEAD}[http://rubydoc.info/github/sinatra/sinatra]에 대한 API 문서
1932 * {CI server}[http://ci.rkh.im/view/Sinatra/]
Something went wrong with that request. Please try again.