jpmobile 0.0.8 → 0.1.0.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. data/.gitignore +0 -3
  2. data/{spec/spec.opts → .rspec} +1 -2
  3. data/README.rdoc +122 -128
  4. data/Rakefile +23 -101
  5. data/VERSION.yml +4 -3
  6. data/jpmobile.gemspec +130 -182
  7. data/lib/jpmobile.rb +37 -24
  8. data/lib/jpmobile/docomo_guid.rb +2 -1
  9. data/lib/jpmobile/email.rb +3 -4
  10. data/lib/jpmobile/emoticon.rb +2 -6
  11. data/lib/jpmobile/encoding.rb +29 -0
  12. data/lib/jpmobile/filter.rb +42 -134
  13. data/lib/jpmobile/helpers.rb +5 -18
  14. data/lib/jpmobile/hook_action_controller.rb +24 -16
  15. data/lib/jpmobile/hook_action_view.rb +28 -89
  16. data/lib/jpmobile/mobile/abstract_mobile.rb +21 -4
  17. data/lib/jpmobile/mobile/android.rb +10 -0
  18. data/lib/jpmobile/mobile/au.rb +28 -3
  19. data/lib/jpmobile/mobile/ddipocket.rb +12 -0
  20. data/lib/jpmobile/mobile/display.rb +29 -26
  21. data/lib/jpmobile/mobile/docomo.rb +38 -12
  22. data/lib/jpmobile/mobile/iphone.rb +26 -0
  23. data/lib/jpmobile/mobile/smart_phone.rb +21 -0
  24. data/lib/jpmobile/mobile/softbank.rb +16 -41
  25. data/lib/jpmobile/mobile/vodafone.rb +17 -0
  26. data/lib/jpmobile/mobile/willcom.rb +1 -10
  27. data/lib/jpmobile/mobile/windows_phone.rb +10 -0
  28. data/lib/jpmobile/position.rb +4 -3
  29. data/lib/jpmobile/rack.rb +43 -0
  30. data/lib/jpmobile/rack/filter.rb +51 -0
  31. data/lib/jpmobile/rack/mobile_carrier.rb +33 -0
  32. data/lib/jpmobile/rack/params_filter.rb +47 -0
  33. data/lib/jpmobile/rails.rb +22 -0
  34. data/lib/jpmobile/request_with_mobile.rb +8 -12
  35. data/lib/jpmobile/trans_sid.rb +44 -26
  36. data/lib/jpmobile/util.rb +68 -0
  37. data/{tasks → lib/tasks}/jpmobile_tasks.rake +32 -13
  38. data/spec/rack/jpmobile/android_spec.rb +20 -0
  39. data/spec/rack/jpmobile/au_spec.rb +206 -0
  40. data/spec/rack/jpmobile/docomo_spec.rb +237 -0
  41. data/spec/rack/jpmobile/emoticon_spec.rb +215 -0
  42. data/spec/rack/jpmobile/filter_spec.rb +181 -0
  43. data/spec/rack/jpmobile/iphone_spec.rb +32 -0
  44. data/spec/rack/jpmobile/mobile_by_ua_spec.rb +39 -0
  45. data/spec/rack/jpmobile/params_filter_spec.rb +193 -0
  46. data/spec/rack/jpmobile/softbank_spec.rb +123 -0
  47. data/spec/rack/jpmobile/willcom_spec.rb +67 -0
  48. data/spec/rack/jpmobile/windows_phone.rb +20 -0
  49. data/spec/rack_helper.rb +86 -0
  50. data/spec/spec_helper.rb +3 -2
  51. data/spec/unit/detect_by_email_spec.rb +21 -27
  52. data/spec/unit/is_carrier_spec.rb +56 -84
  53. data/spec/unit/spec_helper.rb +1 -2
  54. data/spec/unit/util_spec.rb +2 -2
  55. data/spec/unit/valid_ip_spec.rb +43 -52
  56. data/test/legacy/autoload_test.rb +1 -3
  57. data/test/legacy/emoticon_test.rb +5 -6
  58. data/test/legacy/helper.rb +0 -71
  59. data/test/rails/overrides/.rspec +5 -0
  60. data/test/rails/overrides/Gemfile +32 -0
  61. data/test/rails/overrides/app/controllers/docomo_guid_always_controller.rb +3 -0
  62. data/test/rails/overrides/app/controllers/docomo_guid_base_controller.rb +5 -0
  63. data/test/rails/overrides/app/controllers/docomo_guid_docomo_controller.rb +3 -0
  64. data/test/rails/overrides/app/controllers/filter_controller.rb +0 -34
  65. data/test/rails/overrides/app/controllers/filter_controller_base.rb +19 -0
  66. data/test/rails/overrides/app/controllers/hankaku_filter_controller.rb +3 -0
  67. data/test/rails/overrides/app/controllers/links_controller.rb +28 -0
  68. data/test/rails/overrides/app/controllers/mobile_spec_controller.rb +1 -1
  69. data/test/rails/overrides/app/controllers/template_path_controller.rb +3 -0
  70. data/test/rails/overrides/app/controllers/trans_sid_always_and_session_off_controller.rb +4 -0
  71. data/test/rails/overrides/app/controllers/trans_sid_always_controller.rb +3 -0
  72. data/test/rails/overrides/app/controllers/trans_sid_base_controller.rb +18 -0
  73. data/test/rails/overrides/app/controllers/trans_sid_mobile_controller.rb +3 -0
  74. data/test/rails/overrides/app/controllers/trans_sid_none_controller.rb +3 -0
  75. data/test/rails/overrides/app/views/links/au_gps.html.erb +1 -0
  76. data/test/rails/overrides/app/views/links/au_location.html.erb +1 -0
  77. data/test/rails/overrides/app/views/links/docomo_foma_gps.html.erb +1 -0
  78. data/test/rails/overrides/app/views/links/docomo_openiarea.html.erb +1 -0
  79. data/test/rails/overrides/app/views/links/docomo_utn.html.erb +1 -0
  80. data/test/rails/overrides/app/views/links/link.html.erb +1 -0
  81. data/test/rails/overrides/app/views/links/show_all.html.erb +1 -0
  82. data/test/rails/overrides/app/views/links/softbank_location.html.erb +1 -0
  83. data/test/rails/overrides/app/views/links/willcom_location.html.erb +1 -0
  84. data/test/rails/overrides/app/views/template_path/_partial_smart_phone.html.erb +1 -0
  85. data/test/rails/overrides/app/views/template_path/_partial_smart_phone_iphone.html.erb +1 -0
  86. data/test/rails/overrides/app/views/template_path/index_smart_phone.html.erb +1 -0
  87. data/test/rails/overrides/app/views/template_path/index_smart_phone_iphone.html.erb +1 -0
  88. data/test/rails/overrides/app/views/template_path/show_mobile.html.erb +1 -0
  89. data/test/rails/overrides/app/views/template_path/show_mobile_docomo.html.erb +1 -0
  90. data/test/rails/overrides/autotest/discover.rb +2 -0
  91. data/test/rails/overrides/config/initializers/jpmobile_generator.rb +1 -0
  92. data/test/rails/overrides/config/routes.rb +58 -0
  93. data/test/rails/overrides/db/migrate/001_add_sessions_table.rb +16 -0
  94. data/test/rails/overrides/spec/helpers/helpers_spec.rb +2 -2
  95. data/test/rails/overrides/spec/{controllers → requests}/docomo_guid_spec.rb +37 -14
  96. data/test/rails/overrides/spec/{controllers → requests}/docomo_spec.rb +11 -8
  97. data/test/rails/overrides/spec/{controllers → requests}/emobile_spec.rb +24 -8
  98. data/test/rails/overrides/spec/requests/filter_spec.rb +171 -0
  99. data/test/rails/overrides/spec/requests/helpers_spec.rb +187 -0
  100. data/test/rails/overrides/spec/requests/pc_spec.rb +22 -0
  101. data/test/rails/overrides/spec/requests/softbank_emulator_spec.rb +21 -0
  102. data/test/rails/overrides/spec/requests/template_path_spec.rb +234 -0
  103. data/test/rails/overrides/spec/requests/trans_sid_spec.rb +142 -0
  104. data/test/rails/overrides/spec/spec_helper.rb +17 -48
  105. data/test/sinatra/guestbook.rb +40 -0
  106. data/test/sinatra/test/filter_test.rb +39 -0
  107. metadata +185 -312
  108. data/lib/jpmobile/hook_request.rb +0 -5
  109. data/lib/jpmobile/rack/auth.rb +0 -55
  110. data/lib/jpmobile/rack/combined_logger.rb +0 -54
  111. data/lib/jpmobile/rack/request.rb +0 -7
  112. data/lib/jpmobile/rack/trans_sid.rb +0 -14
  113. data/lib/jpmobile/version.rb +0 -9
  114. data/spec/unit/detect_by_ua_spec.rb +0 -40
  115. data/test/legacy/au_test.rb +0 -173
  116. data/test/legacy/docomo_test.rb +0 -187
  117. data/test/legacy/emoticon_functional_test.rb +0 -195
  118. data/test/legacy/filter_test.rb +0 -34
  119. data/test/legacy/helpers_test.rb +0 -217
  120. data/test/legacy/softbank_test.rb +0 -183
  121. data/test/legacy/willcom_test.rb +0 -56
  122. data/test/rails/2.3.2/spec/controllers/trans_sid_spec.rb +0 -99
  123. data/test/rails/overrides/app/controllers/docomo_guid_controller.rb +0 -13
  124. data/test/rails/overrides/app/controllers/trans_sid_controller.rb +0 -28
  125. data/test/rails/overrides/lib/tasks/rspec.rake +0 -165
  126. data/test/rails/overrides/spec/controllers/filter_spec.rb +0 -159
  127. data/test/rails/overrides/spec/controllers/pc_spec.rb +0 -13
  128. data/test/rails/overrides/spec/controllers/softbank_emulator_spec.rb +0 -42
  129. data/test/rails/overrides/spec/controllers/template_path_spec.rb +0 -117
  130. data/test/rails/overrides/spec/controllers/trans_sid_spec.rb +0 -117
  131. data/test/rails/overrides/spec/rspec.rake +0 -165
  132. data/test/rails/overrides/spec/spec.opts +0 -4
@@ -0,0 +1,10 @@
1
+ # -*- coding: utf-8 -*-
2
+ # =WindowsPhone
3
+
4
+ module Jpmobile::Mobile
5
+ # ==WindowsPhone
6
+ class WindowsPhone < SmartPhone
7
+ # 対応するUser-Agentの正規表現
8
+ USER_AGENT_REGEXP = /Windows Phone/
9
+ end
10
+ end
@@ -1,9 +1,10 @@
1
+ # -*- coding: utf-8 -*-
1
2
  #= 位置情報
2
3
 
3
- # plugins/以下にgeokitがインストールされている場合は読み込む
4
+ # Rack 化にあわせて gem を見るように
4
5
  begin
5
- require RAILS_ROOT + '/vendor/plugins/geokit/lib/geo_kit/mappable'
6
- rescue MissingSourceFile, NameError
6
+ require 'geokit'
7
+ rescue LoadError
7
8
  end
8
9
 
9
10
  module Jpmobile
@@ -0,0 +1,43 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'rack/utils'
3
+
4
+ module Jpmobile
5
+ module Rack
6
+ module_function
7
+ def mount_middlewares
8
+ # 漢字コード・絵文字フィルター
9
+ ::Rails.application.middleware.insert_before('ActionDispatch::ParamsParser', Jpmobile::Rack::ParamsFilter)
10
+ ::Rails.application.middleware.insert_before('ActionDispatch::ParamsParser', Jpmobile::Rack::Filter)
11
+ end
12
+ end
13
+
14
+ class Configuration
15
+ def mobile_filter
16
+ ::Jpmobile::Rack.mount_middlewares
17
+ end
18
+ end
19
+ end
20
+
21
+ module Rack
22
+ class Request
23
+ def params
24
+ self.GET.merge(self.POST)
25
+ end
26
+ end
27
+
28
+ # UTF-8 で match させるようにする
29
+ module Utils
30
+ def escape(s)
31
+ s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/) {
32
+ '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
33
+ }.tr(' ', '+')
34
+ end
35
+ module_function :escape
36
+ def unescape(s)
37
+ s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/){
38
+ [$1.delete('%')].pack('H*')
39
+ }
40
+ end
41
+ module_function :unescape
42
+ end
43
+ end
@@ -0,0 +1,51 @@
1
+ # -*- coding: utf-8 -*-
2
+ # 出力変換
3
+ module Jpmobile
4
+ module Rack
5
+ class Filter
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ # 入力を保存
12
+ mobile = env['rack.jpmobile']
13
+
14
+ status, env, response = @app.call(env)
15
+
16
+ if mobile
17
+ if content_type = env['Content-Type']
18
+ type, charset = content_type.split(/;\s*charset=/)
19
+ else
20
+ type = nil
21
+ charset = nil
22
+ end
23
+
24
+ response, charset = mobile.to_external(response_to_body(response), type, charset)
25
+
26
+ if type and charset
27
+ env['Content-Type'] = "#{type}; charset=#{charset}"
28
+ end
29
+ end
30
+
31
+ new_response = ::Rack::Response.new(response, status, env)
32
+ new_response.finish
33
+ end
34
+
35
+ private
36
+ def response_to_body(response)
37
+ if response.respond_to?(:to_str)
38
+ response.to_str
39
+ elsif response.respond_to?(:each)
40
+ body = []
41
+ response.each do |part|
42
+ body << response_to_body(part)
43
+ end
44
+ body.join("\n")
45
+ else
46
+ body
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,33 @@
1
+ # -*- coding: utf-8 -*-
2
+ # UserAgent から生成すべき class 名を判定する
3
+ module Jpmobile
4
+ module Rack
5
+ class MobileCarrier
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ env['rack.jpmobile'] = carrier(env)
12
+
13
+ @app.call(env)
14
+ end
15
+
16
+ def carrier(env)
17
+ ::Jpmobile::Mobile.carriers.each do |const|
18
+ c = ::Jpmobile::Mobile.const_get(const)
19
+ if c.check_carrier(env)
20
+ res = ::Rack::Request.new(env)
21
+ return c.new(env, res)
22
+ end
23
+ end
24
+
25
+ nil
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ class Rack::Request
32
+ include Jpmobile::RequestWithMobile
33
+ end
@@ -0,0 +1,47 @@
1
+ # -*- coding: utf-8 -*-
2
+ # リクエストパラメータの変換
3
+ module Jpmobile
4
+ module Rack
5
+ class ParamsFilter
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ # 入力
12
+ if @mobile = env['rack.jpmobile']
13
+ # パラメータをkey, valueに分解
14
+ # form_params
15
+ if env['REQUEST_METHOD'] == 'POST'
16
+ env['rack.input'] = StringIO.new(parse_query(env['rack.input'].read))
17
+ end
18
+
19
+ # query_params
20
+ env['QUERY_STRING'] = parse_query(env['QUERY_STRING'])
21
+ end
22
+
23
+ status, env, body = @app.call(env)
24
+
25
+ [status, env, body]
26
+ end
27
+
28
+ private
29
+ def to_internal(str)
30
+ ::Rack::Utils.escape(@mobile.to_internal(::Rack::Utils.unescape(str)))
31
+ end
32
+ def parse_query(str)
33
+ return nil unless str
34
+
35
+ new_array = []
36
+ str.split("&").each do |param_pair|
37
+ k, v = param_pair.split("=")
38
+ k = to_internal(k) if k
39
+ v = to_internal(v) if v
40
+ new_array << "#{k}=#{v}" if k and v
41
+ end
42
+
43
+ new_array.join("&")
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,22 @@
1
+ ActiveSupport.on_load(:action_controller) do
2
+ # -*- coding: utf-8 -*-
3
+ require 'jpmobile/docomo_guid'
4
+ require 'jpmobile/filter'
5
+ require 'jpmobile/helpers'
6
+ require 'jpmobile/hook_action_controller'
7
+ require 'jpmobile/hook_action_view'
8
+ require 'jpmobile/trans_sid'
9
+
10
+ # MobileCarrierのみデフォルトで有効
11
+ ::Rails.application.middleware.insert_before('ActionDispatch::ParamsParser', Jpmobile::Rack::MobileCarrier)
12
+
13
+ module Rails
14
+ class Application
15
+ class Configuration
16
+ def jpmobile
17
+ @jpmobile ||= ::Jpmobile::Configuration.new
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,8 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  #
3
3
  # jpmobile の各機能を提供するモジュール
4
- # envメソッドと、parameter あるいは params メソッドが実装されている必要がある。
5
- # 今のところはRack::RequestとActionController::AbstractRequestに対応しているはず。
4
+ # envメソッドが実装されている必要がある。
6
5
 
7
6
  module Jpmobile
8
7
  module RequestWithMobile
@@ -27,21 +26,18 @@ module Jpmobile
27
26
 
28
27
  # 携帯電話からであれば +true+を、そうでなければ +false+ を返す。
29
28
  def mobile?
30
- mobile != nil
29
+ mobile and not mobile.smart_phone?
30
+ end
31
+
32
+ # viewの切り替えをするかどうか
33
+ def smart_phone?
34
+ mobile and mobile.smart_phone?
31
35
  end
32
36
 
33
37
  # 携帯電話の機種に応じて Mobile::xxx を返す。
34
38
  # 携帯電話でない場合はnilを返す。
35
39
  def mobile
36
- @__mobile ||= nil
37
- return @__mobile if @__mobile
38
-
39
- Jpmobile::Mobile.carriers.each do |const|
40
- c = Jpmobile::Mobile.const_get(const)
41
- return @__mobile = c.new(self) if c.check_request(self)
42
- end
43
- nil
40
+ env['rack.jpmobile']
44
41
  end
45
42
  end
46
43
  end
47
-
@@ -8,11 +8,8 @@ module ParamsOverCookie
8
8
  # cookie よりも params を先に見るパッチ
9
9
  def load_session_with_jpmobile(env)
10
10
  request = Rack::Request.new(env)
11
- unless @cookie_only
12
- sid = request.params[@key]
13
- end
14
- sid ||= request.cookies[@key]
15
-
11
+ sid = request.cookies[@key]
12
+ sid ||= request.params[@key] unless @cookie_only
16
13
  sid, session = get_session(env, sid)
17
14
  [sid, session]
18
15
  end
@@ -21,35 +18,53 @@ module ParamsOverCookie
21
18
  end
22
19
  end
23
20
 
21
+ module ActionDispatch
22
+ module Session
23
+ class AbstractStore
24
+ include ParamsOverCookie
25
+ end
26
+ end
27
+ end
28
+
24
29
  module ActionController
25
- # cookie よりも params を先に見るパッチ
26
- Session::AbstractStore.send :include, ParamsOverCookie
30
+ module Redirecting
31
+ def redirect_to_with_jpmobile(options = {}, response_status = {})
32
+ if apply_trans_sid? and jpmobile_session_id
33
+ case options
34
+ when String
35
+ unless options.match(/#{session_key}/)
36
+ url = URI.parse(options)
37
+ if url.query
38
+ url.query += "&#{session_key}=#{jpmobile_session_id}"
39
+ else
40
+ url.query = "#{session_key}=#{jpmobile_session_id}"
41
+ end
42
+ options = url.to_s
43
+ end
44
+ when %r{^\w[\w+.-]*:.*}
45
+ when :back
46
+ else
47
+ unless options[session_key.to_sym]
48
+ options[session_key.to_sym] = jpmobile_session_id
49
+ end
50
+ end
51
+ end
52
+
53
+ redirect_to_without_jpmobile(options, response_status)
54
+ end
55
+
56
+ alias_method_chain :redirect_to, :jpmobile
57
+ end
27
58
 
28
59
  class Base #:nodoc:
29
60
  class_inheritable_accessor :trans_sid_mode
30
- alias :redirect_to_full_url_without_jpmobile :redirect_to_full_url
31
61
 
32
62
  def transit_sid_mode(*args)
33
63
  STDERR.puts "Method transit_sid is now deprecated. Use trans_sid instead."
34
64
  trans_sid_mode(*args)
35
65
  end
36
66
 
37
- def redirect_to_full_url(url, status)
38
- if apply_trans_sid? and !url.match(/#{session_key}/) and jpmobile_session_id
39
- uri = URI.parse(url)
40
- if uri.query
41
- uri.query += "&#{session_key}=#{jpmobile_session_id}"
42
- else
43
- uri.query = "#{session_key}=#{jpmobile_session_id}"
44
- end
45
- url = uri.to_s
46
- end
47
-
48
- redirect_to_full_url_without_jpmobile(url, status)
49
- end
50
-
51
67
  class << self
52
- # 2.3.x or higher
53
68
  def trans_sid(mode = :mobile)
54
69
  include Jpmobile::TransSid
55
70
  self.trans_sid_mode = mode
@@ -90,17 +105,20 @@ module Jpmobile::TransSid #:nodoc:
90
105
 
91
106
  protected
92
107
  # URLにsession_idを追加する。
93
- def default_url_options(options=nil)
108
+ def default_url_options
94
109
  result = super || {}
95
110
  return result unless request # for test process
96
111
  return result unless apply_trans_sid?
97
- return result.merge({ session_key => jpmobile_session_id })
112
+ return result.merge({session_key => jpmobile_session_id})
98
113
  end
99
114
 
100
115
  private
101
116
  # session_keyを返す。
102
117
  def session_key
103
- ActionController::Base.session_options.merge(request.session_options || {})[:key]
118
+ unless key = Rails.application.config.session_options.merge(request.session_options || {})[:key]
119
+ key = ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS[:key]
120
+ end
121
+ key
104
122
  end
105
123
  # session_idを返す
106
124
  def jpmobile_session_id
data/lib/jpmobile/util.rb CHANGED
@@ -17,5 +17,73 @@ module Jpmobile
17
17
  proc.call(obj)
18
18
  end
19
19
  end
20
+
21
+ def deep_convert(obj, &proc)
22
+ case obj
23
+ when Hash
24
+ new_obj = {}
25
+ obj.each_pair do |key, value|
26
+ new_obj[deep_convert(key.dup, &proc)] = deep_convert(value, &proc)
27
+ end
28
+ new_obj
29
+ when Array
30
+ new_obj = obj.map do |value|
31
+ deep_convert(value, &proc)
32
+ end
33
+ when Symbol
34
+ new_obj = proc.call(obj.to_s).to_sym
35
+ when NilClass, TrueClass, FalseClass, Tempfile, StringIO
36
+ new_obj = obj
37
+ else
38
+ obj = obj.to_param if obj.respond_to?(:to_param)
39
+ new_obj = proc.call(obj)
40
+ end
41
+
42
+ new_obj
43
+ end
44
+
45
+ def sjis(ascii_8bit)
46
+ if ascii_8bit.respond_to?(:force_encoding)
47
+ ascii_8bit.force_encoding("Shift_JIS")
48
+ end
49
+ ascii_8bit
50
+ end
51
+
52
+ def utf8(ascii_8bit)
53
+ if ascii_8bit.respond_to?(:force_encoding)
54
+ ascii_8bit.force_encoding("utf-8")
55
+ end
56
+ ascii_8bit
57
+ end
58
+
59
+ def ascii_8bit(str)
60
+ if str.respond_to?(:force_encoding)
61
+ str.force_encoding("ASCII-8BIT")
62
+ end
63
+ str
64
+ end
65
+
66
+ def utf8_to_sjis(utf8_str)
67
+ if utf8_str.respond_to?(:encode)
68
+ utf8_str.encode("Shift_JIS")
69
+ else
70
+ NKF.nkf("-m0 -x -Ws", utf8_str)
71
+ end
72
+ end
73
+
74
+ def sjis_to_utf8(sjis_str)
75
+ if sjis_str.respond_to?(:encode)
76
+ sjis_str.encode("UTF-8")
77
+ else
78
+ NKF.nkf("-m0 -x -Sw", sjis_str)
79
+ end
80
+ end
81
+
82
+ def hash_to_utf8(hash)
83
+ new_hash = {}
84
+ hash.each do |keu, value|
85
+ new_hash[utf8(key)] = utf8(value)
86
+ end
87
+ end
20
88
  end
21
89
  end