Class: Alone

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

Overview

Note:

Aloneメインクラス

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

Defined Under Namespace

Classes: OutputTrap

Constant Summary

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

+ (Object) _start

初期化



465
466
467
468
469
470
471
472
473
474
475
476
# File 'lib/al_main.rb', line 465

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
    /(^|&)action=([a-zA-Z0-9_\-\/]*)/ =~ query_string
    @@action = $2.to_s
    $stdout = OutputTrap.new
    $stderr = $stdout
  end
  Encoding.default_external = AL_CHARSET
end

+ (String) action

Getter アクション名

Returns:

  • (String)

    アクション名



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

def self.action()
  return @@action
end

+ (Object) add_http_header(header)

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

+ (String) ctrl

Getter コントローラ名

Returns:

  • (String)

    コントローラ名



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

def self.ctrl()
  return @@ctrl
end

+ (String) decode_uri_component(s)

URIデコード

Parameters:

  • s (String)

    ソース文字列

Returns:

  • (String)

    デコード済み文字列



312
313
314
315
316
317
# File 'lib/al_main.rb', line 312

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)

    パス



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

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

+ (Object) delete_http_header(header)

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

+ (String) encode_uri_component(s)

URIエンコード

Parameters:

  • s (String)

    ソース文字列

Returns:

  • (String)

    エンコード済み文字列



299
300
301
302
303
# File 'lib/al_main.rb', line 299

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

+ (String) escape_backslash(s)

Note:

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

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

Parameters:

  • s (String)

    対象文字列

Returns:

  • (String)

    変換後文字列



403
404
405
# File 'lib/al_main.rb', line 403

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

+ (String) escape_html(s)

html特殊文字のエスケープ

Parameters:

  • s (String)

    対象文字列

Returns:

  • (String)

    変換後文字列



377
378
379
# File 'lib/al_main.rb', line 377

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

+ (String) escape_html_br(s)

Note:

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

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

Parameters:

  • s (String)

    対象文字列

Returns:

  • (String)

    変換後文字列



390
391
392
# File 'lib/al_main.rb', line 390

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

+ (Object) handle_error(ex)

エラーハンドラ



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

+ (Object) handle_error_display(ex)

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



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`.split("\n").each do |e|
    puts '    ' + escape_html( e )
  end
  puts '</pre>'
  puts AL_TEMPLATE_FOOTER
end

+ (Object) handle_error_static_page(ex)

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



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

+ (Logger) log(arg, severity = nil, progname = nil)

ログ出力

Parameters:

  • arg (String, Object)

    エラーメッセージ

  • severity (Symbol) (defaults to: nil)

    ログレベル :fatal, :error …

  • progname (String) (defaults to: nil)

    プログラム名

Returns:

  • (Logger)

    Loggerオブジェクト



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

def self.log( arg, severity = nil, progname = nil )
  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

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

  if arg.class == String
    @@log.add( s || Logger::INFO, arg, progname )

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

  elsif arg != nil
    @@log.add( s || Logger::INFO, arg.to_s, progname )
  end
  return @@log
end

+ (Object) main

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

+ (String) make_uri(arg = {})

Note:

リンク用のURIを生成する

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

Parameters:

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

    引数ハッシュ

Options Hash (arg):

  • :ctrl (String)

    コントローラ名

  • :action (String)

    アクション名

  • :(oters) (String)

    その他のパラメータ

Returns:

  • (String)

    生成したURI



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

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

+ (Object) redirect_to(uri)

リダイレクト設定

Parameters:

  • uri (String)

    リダイレクト先のURI



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

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

+ (String) request_uri

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

Returns:

  • (String)

    リクエストされたURI



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

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

+ (Object) send_http_headers

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)

    パス



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

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