Class: Alone

Inherits:
Object
  • Object
show all
Defined in:
lib/al_main.rb

Overview

Note:

Aloneメインクラス

Aloneのベースとなる機能を、まとめている。

Defined Under Namespace

Classes: OutputTrap

Constant Summary collapse

CHAR_ENT_REF =

escape_html用定数テーブル

{'<'=>'&lt;', '>'=>'&gt;', '&'=>'&amp;', '"'=>'&quot;', '\''=>'&#39;', "\r\n"=>'<br>', "\r"=>'<br>', "\n"=>'<br>' }
@@headers =

Returns httpヘッダ.

Returns:

  • (Array)

    httpヘッダ

[ "Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0" ]
@@cookies =

Returns クッキー.

Returns:

  • (Hash, NilClass)

    クッキー

nil
@@flag_redirect =

Returns リダイレクトするかのフラグ.

Returns:

  • (Boolean)

    リダイレクトするかのフラグ

false
@@flag_send_http_headers =

Returns httpヘッダを送ったかのフラグ.

Returns:

  • (Boolean)

    httpヘッダを送ったかのフラグ

false
@@ctrl =

Returns コントローラ名.

Returns:

  • (String)

    コントローラ名

""
@@action =

Returns アクション名.

Returns:

  • (String)

    アクション名

""
@@log =

Returns ロガーオブジェクト.

Returns:

  • (Logger)

    ロガーオブジェクト

nil

Class Method Summary collapse

Class Method Details

._startObject

初期化



476
477
478
479
480
481
482
483
484
485
486
487
# File 'lib/al_main.rb', line 476

def self._start()
  if ENV.has_key?("GATEWAY_INTERFACE")
    query_string = ENV["QUERY_STRING"] || ""
    /(^|&)ctrl=([a-zA-Z0-9_\-\/]*)/ =~ query_string
    @@ctrl = $2.to_s.dup
    /(^|&)action=([a-zA-Z0-9_\-\/]*)/ =~ query_string
    @@action = $2.to_s.dup
    $stdout = OutputTrap.new
    $stderr = $stdout
  end
  Encoding.default_external = AL_CHARSET
end

.actionString

Getter アクション名

Returns:

  • (String)

    アクション名



57
58
59
# File 'lib/al_main.rb', line 57

def self.action()
  return @@action
end

.add_http_header(header) ⇒ Object

Note:

httpヘッダの追加

実際にhttpヘッダが送られる前に、使用する必要がある。 httpヘッダは、send_http_headers() メソッドで送られる。 典型的には、最初にユーザコードで文字列などが表示される直前に、 ヘッダが出力されるので、それまでに使用する。

Parameters:

  • header (String)

    ヘッダ文字列。“Tag: message.” 形式



173
174
175
# File 'lib/al_main.rb', line 173

def self.add_http_header( header )
  @@headers << header.chomp.gsub( /\r?\n[^ \t]|\r[^\n \t]/, ' ' )
end

.ctrlString

Getter コントローラ名

Returns:

  • (String)

    コントローラ名



48
49
50
# File 'lib/al_main.rb', line 48

def self.ctrl()
  return @@ctrl
end

.decode_uri_component(s) ⇒ String

URIデコード

Parameters:

  • s (String)

    ソース文字列

Returns:

  • (String)

    デコード済み文字列



314
315
316
317
318
319
# File 'lib/al_main.rb', line 314

def self.decode_uri_component( s )
  a = s.to_s.dup.force_encoding( Encoding::ASCII_8BIT )
  a.gsub!( /%([0-9a-fA-F]{2})/ ) { $1.hex.chr }
  a.force_encoding( AL_CHARSET )
  # TODO: UTF-8(あるいはその他の漢字コード)として無効なコードが来たらどうする?
end
Note:

クッキーの消去

httpヘッダに出力する関係上、ヘッダ出力前にコールする必要がある。

Parameters:

  • name (String)

    クッキー名

  • path (String) (defaults to: nil)

    パス



267
268
269
270
271
272
273
274
275
# File 'lib/al_main.rb', line 267

def self.delete_cookie( name, path = nil )
  # 既にヘッダへ出力予約されているものがあれば削除
  target = "Set-Cookie: #{name}="
  @@headers.delete_if { |h| h.start_with?( target ) }

  cookie = "Set-Cookie: #{name}=; expires=Thu, 08-Jun-1944 00:00:00 GMT"
  cookie << "; path=#{path}"  if path
  @@headers << cookie
end

.delete_http_header(header) ⇒ Object

Note:

httpヘッダの削除

実際にhttpヘッダが送られる前に、使用する必要がある。 デフォルトで送信されるヘッダ Cache-Control: を止めることができる。

Parameters:

  • header (String)

    ヘッダ文字列。“Cache-Control”等

See Also:

  • add_http_header()


187
188
189
# File 'lib/al_main.rb', line 187

def self.delete_http_header( header )
  @@headers.delete_if { |a| a.start_with?( header ) }
end

.encode_uri_component(s) ⇒ String

URIエンコード

Parameters:

  • s (String)

    ソース文字列

Returns:

  • (String)

    エンコード済み文字列



301
302
303
304
305
# File 'lib/al_main.rb', line 301

def self.encode_uri_component( s )
  a = s.to_s.dup
  a.force_encoding( Encoding::ASCII_8BIT )
  a.gsub( /[^a-zA-Z0-9!'\(\)*\-._~]/ ) { |c| "%#{c.unpack('H2')[0]}" }
end

.escape_backslash(s) ⇒ String

Note:

クォート文字をバックスラッシュでエスケープする

対象は、シングルクォート、ダブルクォート、バックスラッシュ、NULL

Parameters:

  • s (String)

    対象文字列

Returns:

  • (String)

    変換後文字列



405
406
407
# File 'lib/al_main.rb', line 405

def self.escape_backslash( s )
  s.to_s.gsub( /['"\\\x0]/ ) { |q| "\\#{q}" }
end

.escape_html(s) ⇒ String

html特殊文字のエスケープ

Parameters:

  • s (String)

    対象文字列

Returns:

  • (String)

    変換後文字列



379
380
381
# File 'lib/al_main.rb', line 379

def self.escape_html( s )
  return s.to_s.gsub( /[<>&"']/, CHAR_ENT_REF )
end

.escape_html_br(s) ⇒ String

Note:

html特殊文字のエスケープ with 改行文字

html特殊文字のエスケープに加え、改行文字を
タグへ変更する。

Parameters:

  • s (String)

    対象文字列

Returns:

  • (String)

    変換後文字列



392
393
394
# File 'lib/al_main.rb', line 392

def self.escape_html_br( s )
  return s.to_s.gsub( /([<>&"']|\r\n|\r|\n)/, CHAR_ENT_REF )
end

クッキーの取得

Parameters:

  • name (String, Symbol)

    クッキー名

Returns:

  • (String)

  • (NilClass)

    引数で与えられたクッキーが定義されていない場合 nil



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/al_main.rb', line 221

def self.get_cookie( name )
  if ! @@cookies
    @@cookies = {}
    http_cookie = ENV['HTTP_COOKIE']
    return nil  if ! http_cookie

    cookies = http_cookie.split( ';' )
    cookies.each do |c|
      (k,v) = c.split( '=', 2 )
      next if ! v
      @@cookies[k.strip.to_sym] = decode_uri_component( v )
    end
  end

  return @@cookies[name.to_sym]
end

.handle_error(ex) ⇒ Object

エラーハンドラ



89
90
91
92
93
94
95
96
97
98
# File 'lib/al_main.rb', line 89

def self.handle_error( ex )
  if ex.class == SystemExit
    send_http_headers()
  else
    __send__( AL_ERROR_HANDLER, ex )
    log( ex )
  end

  exit
end

.handle_error_display(ex) ⇒ Object

エラーハンドラ:エラー詳細表示



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/al_main.rb', line 134

def self.handle_error_display( ex )
  add_http_header( "Status: 500 Internal Server Error" )
  send_http_headers()

  puts AL_TEMPLATE_HEADER
  puts AL_TEMPLATE_BODY
  puts '<h1 class="al-error-display">Alone: Error detected.</h1>'
  puts "<h2 class=\"al-error-display\">#{ex.class} occurred.</h2>"
  puts '<pre class="al-error-display">'
  puts '    ' + escape_html( ex.message )
  puts '</pre>'

  puts '<h2 class="al-error-display">Backtrace</h2>'
  puts '<pre class="al-error-display">'
  ex.backtrace.each do |bt|
    puts '    ' + escape_html( bt )
  end
  puts '</pre>'

  puts '<h2 class="al-error-display">Environment</h2>'
  puts '<pre class="al-error-display">'
  ENV.each {|k,v|
    puts "    #{k} = #{escape_html(v)}"
  }
  puts '</pre>'
  puts AL_TEMPLATE_FOOTER
end

.handle_error_static_page(ex) ⇒ Object

エラーハンドラ:静的ページを表示



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/al_main.rb', line 104

def self.handle_error_static_page( ex )
  status_code = nil
  status_message = nil

  # check status code in @@headers.
  @@headers.each do |h|
    if /^Status: (\d+)(.*)$/ =~ h
      status_code = $1
      status_message = $1 + $2
    end
  end
  if ! status_code
    status_code = "500"         # default 500 Internal Server Error
    status_message = "Internal Server Error"
    Alone::add_http_header( "Status: 500 Internal Server Error" )
  end

  # display error page.
  send_http_headers()
  begin
    print File.read( "#{AL_BASEDIR}/templates/#{status_code}.html" )
  rescue
    print status_message
  end
end

.log(*args) ⇒ Logger

ログ出力

Parameters:

  • msg (String, Object)

    エラーメッセージ

  • severity (Symbol)

    ログレベル :fatal, :error …

  • progname (String)

    プログラム名

Returns:

  • (Logger)

    Loggerオブジェクト



418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
# File 'lib/al_main.rb', line 418

def self.log( *args )
  return nil  if ! defined? AL_LOG_DEV
  require "logger"

  if !@@log
    @@log = Logger.new( AL_LOG_DEV, AL_LOG_AGE, AL_LOG_SIZE )
    @@log.level = Logger::INFO
  end
  return @@log  if args.empty?

  msg,severity,progname = *args
  s = { :fatal=>Logger::FATAL, :error=>Logger::ERROR, :warn=>Logger::WARN,
        :info=>Logger::INFO, :debug=>Logger::DEBUG }[ severity ]

  case msg
  when String
    @@log.add(s || Logger::INFO, msg, progname)

  when Exception
    @@log.add(s || Logger::ERROR, "#{msg.class} / #{msg.message}", progname)
    @@log.add(s || Logger::ERROR, "BACKTRACE: \n  " + msg.backtrace.join("\n  ") + "\n", progname)

  else
    @@log.add(s || Logger::INFO, msg.inspect, progname)
  end

  return @@log
end

.mainObject

Note:

メイン

ユーザコードがブロックで渡されるので、それを実行する。 例外はすべてキャッチして、正当なhtmlで表示する。



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
# File 'lib/al_main.rb', line 69

def self.main()
  # リダイレクトするなら、ヘッダ出力のみで終了(htmlコンテンツは必要ない)
  if @@flag_redirect
    send_http_headers()
    exit
  end

  begin
    yield()
    send_http_headers()

  rescue Exception => ex
    handle_error( ex )
  end
end

.make_uri(arg = {}) ⇒ String

Note:

リンク用のURIを生成する

このメソッドは、escape_html()した値を返さない。 生成した値は、アトリビュート値にもテンプレートにも使われる。 少々危険な気もするが、エスケープはテンプレートエンジンの仕事にしなければ アプリケーションが破綻する。

Parameters:

  • arg (Hash) (defaults to: {})

    引数ハッシュ

Options Hash (arg):

  • :ctrl (String)

    コントローラ名

  • :action (String)

    アクション名

  • :(oters) (String)

    その他のパラメータ

Returns:

  • (String)

    生成したURI



354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/al_main.rb', line 354

def self.make_uri( arg = {} )
  uri = "#{ENV['SCRIPT_NAME']}?ctrl=#{arg[:ctrl] || @@ctrl}"
  uri << "&action=#{arg[:action]}"  if arg[:action]

  arg.each do |k,v|
    next if k == :ctrl || k == :action
    if v.class == Array
      v.each do |v1|
        uri << "&#{k}=#{encode_uri_component(v1)}"
      end
    else
      uri << "&#{k}=#{encode_uri_component(v)}"
    end
  end

  return uri
end

.redirect_to(uri) ⇒ Object

リダイレクト設定

Parameters:

  • uri (String)

    リダイレクト先のURI



283
284
285
286
287
288
289
290
291
292
# File 'lib/al_main.rb', line 283

def self.redirect_to( uri )
  if @@flag_send_http_headers
    raise "HTTP header was already sent."
  end
  raise "Invalid URI"  if /[\r\n]/ =~ uri

  @@headers << "Location: #{uri}"
  @@headers << "Status: 302 Found"
  @@flag_redirect = true
end

.request_uriString

リクエストされたURIを返す

Returns:

  • (String)

    リクエストされたURI



327
328
329
330
331
332
333
334
335
336
337
# File 'lib/al_main.rb', line 327

def self.request_uri()
  if ENV['REQUEST_URI']
    return ENV['REQUEST_URI']
  end

  if ENV['QUERY_STRING']
    return "#{ENV['SCRIPT_NAME']}?#{ENV['QUERY_STRING']}"
  end

  return ENV['SCRIPT_NAME']
end

.send_http_headersObject

Note:

httpヘッダの送信

送信済みなら何もしないので、何度よんでも問題ない。



198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/al_main.rb', line 198

def self.send_http_headers()
  return  if @@flag_send_http_headers
  @@flag_send_http_headers = true

  flag_content_type = false
  @@headers.each do |h|
    puts h
    flag_content_type = true  if h.start_with?( "Content-Type:" )
  end
  if ! flag_content_type
    print "Content-Type: text/html; charset=#{AL_CHARSET}\n"
  end
  print "\n"
end
Note:

クッキーの設定

httpヘッダに出力する関係上、ヘッダ出力前にコールする必要がある。

Parameters:

  • name (String)

    クッキー名

  • value (String)

  • expire (String) (defaults to: nil)

    有効期限

  • path (String) (defaults to: nil)

    パス

  • other (String) (defaults to: nil)

    その他フラグ



250
251
252
253
254
255
256
# File 'lib/al_main.rb', line 250

def self.set_cookie( name, value, expire = nil, path = nil, other = nil )
  cookie = "Set-Cookie: #{name}=#{encode_uri_component( value )}"
  cookie << "; expires=#{expire.to_s}"  if expire
  cookie << "; path=#{path}"  if path
  cookie << "; #{other}"  if other
  @@headers << cookie
end