bizside 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +7 -0
  2. data/app/assets/images/jquery-treeTable/images/toggle-collapse-dark.png +0 -0
  3. data/app/assets/images/jquery-treeTable/images/toggle-collapse-light.png +0 -0
  4. data/app/assets/images/jquery-treeTable/images/toggle-expand-dark.png +0 -0
  5. data/app/assets/images/jquery-treeTable/images/toggle-expand-light.png +0 -0
  6. data/app/assets/javascripts/bizside.js +1 -0
  7. data/app/assets/javascripts/jquery-treeTable/jquery.treeTable.js +400 -0
  8. data/app/assets/stylesheets/bizside.css +3 -0
  9. data/app/assets/stylesheets/jquery-treeTable/jquery.treeTable.css.erb +55 -0
  10. data/lib/bizside/acl/access_control_utils.rb +36 -0
  11. data/lib/bizside/acl/available_helper.rb +65 -0
  12. data/lib/bizside/acl/controller_helper.rb +15 -0
  13. data/lib/bizside/acl.rb +6 -0
  14. data/lib/bizside/active_record_logger.rb +3 -0
  15. data/lib/bizside/audit/job_logger.rb +14 -0
  16. data/lib/bizside/audit/logger.rb +35 -0
  17. data/lib/bizside/audit_log.rb +158 -0
  18. data/lib/bizside/cache/entry.rb +112 -0
  19. data/lib/bizside/cache/file_store.rb +167 -0
  20. data/lib/bizside/cache/store.rb +237 -0
  21. data/lib/bizside/cache_util.rb +47 -0
  22. data/lib/bizside/cache_utils.rb +10 -0
  23. data/lib/bizside/carrierwave.rb +48 -0
  24. data/lib/bizside/config.rb +63 -0
  25. data/lib/bizside/configurations/mail.rb +37 -0
  26. data/lib/bizside/configurations/prefix.rb +25 -0
  27. data/lib/bizside/configurations/storage.rb +28 -0
  28. data/lib/bizside/coverage/launch.rb +25 -0
  29. data/lib/bizside/coverage/rcov_formatter.rb +13 -0
  30. data/lib/bizside/cron_validator.rb +62 -0
  31. data/lib/bizside/cucumber.rb +1 -0
  32. data/lib/bizside/engine.rb +4 -0
  33. data/lib/bizside/file_converter.rb +56 -0
  34. data/lib/bizside/file_uploader.rb +71 -0
  35. data/lib/bizside/gengou.rb +46 -0
  36. data/lib/bizside/gengou.yml +5 -0
  37. data/lib/bizside/hanaita_conf.rb +88 -0
  38. data/lib/bizside/implicit_ftps.rb +29 -0
  39. data/lib/bizside/itamae_conf.rb +186 -0
  40. data/lib/bizside/job_utils.rb +285 -0
  41. data/lib/bizside/log_analyzer.rb +122 -0
  42. data/lib/bizside/mailer.rb +56 -0
  43. data/lib/bizside/query_builder.rb +78 -0
  44. data/lib/bizside/railtie.rb +80 -0
  45. data/lib/bizside/record_has_warnings.rb +4 -0
  46. data/lib/bizside/resque.rb +141 -0
  47. data/lib/bizside/rsync.rb +40 -0
  48. data/lib/bizside/safe_pty.rb +17 -0
  49. data/lib/bizside/shib_utils.rb +18 -0
  50. data/lib/bizside/show_exceptions.rb +18 -0
  51. data/lib/bizside/sql_utils.rb +45 -0
  52. data/lib/bizside/string_io.rb +39 -0
  53. data/lib/bizside/string_utils.rb +157 -0
  54. data/lib/bizside/task_helper.rb +263 -0
  55. data/lib/bizside/tasks.rb +6 -0
  56. data/lib/bizside/test_help.rb +17 -0
  57. data/lib/bizside/uploader/content_type_validator.rb +39 -0
  58. data/lib/bizside/uploader/default_extensions.yml +15 -0
  59. data/lib/bizside/uploader/exif.rb +43 -0
  60. data/lib/bizside/uploader/extension_whitelist.rb +26 -0
  61. data/lib/bizside/uploader/filename_validator.rb +31 -0
  62. data/lib/bizside/user_agent/action_view/action_view_4.rb +56 -0
  63. data/lib/bizside/user_agent/action_view/use_variant.rb +4 -0
  64. data/lib/bizside/user_agent/action_view.rb +10 -0
  65. data/lib/bizside/user_agent/controller_helper.rb +51 -0
  66. data/lib/bizside/user_agent.rb +108 -0
  67. data/lib/bizside/validations.rb +8 -0
  68. data/lib/bizside/version.rb +3 -0
  69. data/lib/bizside/view_helper.rb +10 -0
  70. data/lib/bizside/warning.rb +24 -0
  71. data/lib/bizside/yes.rb +16 -0
  72. data/lib/bizside.rb +96 -0
  73. data/lib/cron_validator.rb +3 -0
  74. data/lib/gengou.rb +3 -0
  75. data/lib/job_utils.rb +3 -0
  76. data/lib/query_builder.rb +3 -0
  77. data/lib/record_has_warnings.rb +3 -0
  78. data/lib/sql_utils.rb +3 -0
  79. data/lib/string_utils.rb +3 -0
  80. data/lib/user_agent.rb +3 -0
  81. data/lib/yes.rb +3 -0
  82. data/rails/locales/ja.yml +12 -0
  83. data/validations/collection_presence_validator.rb +15 -0
  84. data/validations/email_validator.rb +1 -0
  85. data/validations/ip_address_validator.rb +22 -0
  86. data/validations/tel_validator.rb +28 -0
  87. data/validations/url_validator.rb +30 -0
  88. data/validations/zip_validator.rb +35 -0
  89. metadata +467 -0
@@ -0,0 +1,78 @@
1
+ module Bizside
2
+ class QueryBuilder
3
+
4
+ attr_reader :sql, :params
5
+
6
+ def initialize
7
+ @sql = ''
8
+ @params = []
9
+ @indent = 0
10
+ end
11
+
12
+ def append(*args)
13
+ if args.size == 1
14
+ if args[0].is_a?(Array)
15
+ append(*args[0])
16
+ elsif args[0].is_a?(QueryBuilder)
17
+ append(args[0].to_array)
18
+ else
19
+ append_sql(args[0])
20
+ end
21
+ else
22
+ args.each_with_index do |arg, i|
23
+ if i == 0
24
+ append_sql(arg)
25
+ else
26
+ @params << arg
27
+ end
28
+ end
29
+ end
30
+
31
+ self
32
+ end
33
+
34
+ def and(*args)
35
+ append_sql('and (')
36
+
37
+ if block_given?
38
+ @indent += 1
39
+ yield self
40
+ @indent -= 1
41
+ else
42
+ append(*args)
43
+ end
44
+
45
+ append_sql(')')
46
+ end
47
+
48
+ def or(*args)
49
+ append_sql('or (')
50
+
51
+ if block_given?
52
+ @indent += 1
53
+ yield self
54
+ @indent -= 1
55
+ else
56
+ append(*args)
57
+ end
58
+
59
+ append_sql(')')
60
+ end
61
+
62
+ def to_array
63
+ [@sql] + @params
64
+ end
65
+
66
+ def to_a
67
+ to_array
68
+ end
69
+
70
+ private
71
+
72
+ def append_sql(sql)
73
+ @indent.times { @sql << ' ' }
74
+ @sql << sql
75
+ @sql << "\n"
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,80 @@
1
+ require_relative 'coverage/launch'
2
+
3
+ module Bizside
4
+ class Railtie < ::Rails::Railtie
5
+
6
+ # ロケールファイル
7
+ initializer 'bizside-i18n' do |app|
8
+ Bizside::Railtie.instance_eval do
9
+ pattern = pattern_from(app.config.i18n.available_locales)
10
+ add("rails/locales/#{pattern}.yml")
11
+ end
12
+ end
13
+
14
+ # ビューのヘルパーメソッド
15
+ initializer 'view_helper' do
16
+ require_relative 'view_helper'
17
+
18
+ ActiveSupport.on_load(:action_view) do
19
+ include Bizside::ViewHelper
20
+ end
21
+ end
22
+
23
+ # 警告バリデーション
24
+ if Bizside.config.warning_validation.enabled?
25
+ require_relative 'warning'
26
+
27
+ initializer 'warning_validation' do
28
+ ActiveSupport.on_load :active_record do
29
+ include Bizside::Warning
30
+ end
31
+ end
32
+ end
33
+
34
+ if Bizside.config.user_agent.enabled?
35
+ require_relative 'user_agent'
36
+
37
+ initializer 'user_agent' do
38
+ require_relative 'user_agent/action_view'
39
+
40
+ ActiveSupport.on_load(:action_controller) do
41
+ include Bizside::UserAgent::ControllerHelper
42
+ end
43
+ end
44
+ end
45
+
46
+ if Bizside.config.acl.enabled?
47
+ require_relative 'acl'
48
+
49
+ initializer 'acl' do
50
+ ActiveSupport.on_load(:action_controller) do
51
+ include Bizside::Acl::ControllerHelper
52
+ end
53
+ ActiveSupport.on_load(:action_view) do
54
+ include Bizside::Acl::AvailableHelper
55
+ end
56
+ end
57
+ end
58
+
59
+ unless Bizside.config.active_record_logger.enabled?
60
+ initializer 'active_record_logger' do
61
+ ActiveSupport.on_load(:active_record) do
62
+ require_relative 'active_record_logger'
63
+ end
64
+ end
65
+ end
66
+
67
+ protected
68
+
69
+ def self.add(pattern)
70
+ files = Dir[File.join(File.dirname(__FILE__), '../..', pattern)]
71
+ I18n.load_path.concat(files)
72
+ end
73
+
74
+ def self.pattern_from(args)
75
+ array = Array(args || [])
76
+ array.blank? ? '*' : "{#{array.join ','}}"
77
+ end
78
+
79
+ end
80
+ end
@@ -0,0 +1,4 @@
1
+ module Bizside
2
+ class RecordHasWarnings < Exception
3
+ end
4
+ end
@@ -0,0 +1,141 @@
1
+ module Bizside
2
+ module Resque
3
+ end
4
+ end
5
+
6
+ require 'resque'
7
+ require 'resque/worker'
8
+ require 'resque/failure/base'
9
+ require 'resque/failure/multiple'
10
+ require 'resque/failure/redis'
11
+ require_relative 'audit/job_logger'
12
+
13
+ {
14
+ yaml: ['config/resque.yml', 'config/redis.yml'],
15
+ json: ['config/resque.json', 'config/redis.json']
16
+ }.each do |format, file_candidates|
17
+ file_candidates.each do |file|
18
+ resque_file = File.join(File.expand_path(ENV['RAILS_ROOT'] || '.'), file)
19
+ next unless File.exist?(resque_file)
20
+
21
+ resque_config = ERB.new(File.read(resque_file), 0, '-').result
22
+
23
+ case format
24
+ when :yaml
25
+ Resque.redis = YAML.load(resque_config)[Bizside.env]
26
+ break
27
+ when :json
28
+ Resque.redis = ActiveSupport::JSON.decode(resque_config)[Bizside.env]
29
+ break
30
+ else
31
+ raise "不正なResque設定ファイルです。#{file}"
32
+ end
33
+ end
34
+ end
35
+
36
+ Resque.redis.namespace = "resque:#{Bizside.config.add_on_name}:#{Bizside.env}"
37
+
38
+ if defined?(Resque::Scheduler)
39
+ Resque::Scheduler.dynamic = true
40
+ end
41
+
42
+ # tmp/stop.txt が存在する場合は一時停止する
43
+ module Resque
44
+ class Worker
45
+
46
+ alias_method :reserve_without_stop_txt, :reserve
47
+
48
+ def reserve
49
+ stop_file_path = File.join('tmp', 'stop.txt')
50
+ if File.exist?(stop_file_path)
51
+ puts "#{Resque.redis.namespace} #{stop_file_path} が存在するため一時停止します。"
52
+ nil
53
+ else
54
+ reserve_without_stop_txt
55
+ end
56
+ end
57
+
58
+ end
59
+ end
60
+
61
+ # resque-webのエラーメッセージ文字化けに対するパッチ
62
+ module Resque
63
+ module Failure
64
+ class Redis
65
+
66
+ def save
67
+ data = {
68
+ :failed_at => UTF8Util.clean(Time.now.strftime("%Y/%m/%d %H:%M:%S %Z")),
69
+ :payload => payload,
70
+ :exception => exception.class.to_s,
71
+ :error => exception.to_s, #UTF8Util.clean(exception.to_s), UTF8Util.cleanを呼ぶと文字化けする
72
+ :backtrace => filter_backtrace(Array(exception.backtrace)),
73
+ :worker => worker.to_s,
74
+ :queue => queue
75
+ }
76
+ data = Resque.encode(data)
77
+ Resque.redis.rpush(:failed, data)
78
+ end
79
+
80
+ end
81
+ end
82
+ end
83
+
84
+ # エラーをログに出力
85
+ module Resque
86
+ module Failure
87
+ class LogOutput < Base
88
+ def save
89
+ Bizside.logger.error [
90
+ "[FATAL] Resque #{queue}:#{worker}",
91
+ "#{payload}",
92
+ "#{exception.class} #{exception.to_s}",
93
+ "#{Array(exception.backtrace).join("\n")}"
94
+ ].join("\n")
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ # エラーをLTSV形式で専用ログに出力
101
+ module Resque
102
+ module Failure
103
+ class JobAuditLog < Base
104
+
105
+ def save
106
+ info = build_loginfo
107
+ return info if Bizside.rails_env&.test?
108
+
109
+ logger.record(info)
110
+
111
+ info
112
+ end
113
+
114
+ private
115
+
116
+ def logger
117
+ @logger ||= Bizside::Audit::JobLogger.logger
118
+ end
119
+
120
+ def build_loginfo
121
+ info = {
122
+ time: Time.now.strftime('%Y-%m-%dT%H:%M:%S.%3N%z'),
123
+ add_on_name: Bizside.config.add_on_name,
124
+ class: payload['class'],
125
+ args: payload['args'].to_s,
126
+ queue: queue,
127
+ worker: worker.to_s,
128
+ exception: exception.class,
129
+ exception_message: exception.to_s,
130
+ exception_backtrace: Array(exception.backtrace)[0..10].join("\n") # Get only the top 10 because there are many traces.
131
+ }
132
+ info
133
+ end
134
+
135
+ end
136
+ end
137
+ end
138
+
139
+ Resque::Failure::Multiple.configure do |multi|
140
+ multi.classes = [Resque::Failure::Redis, Resque::Failure::LogOutput, Resque::Failure::JobAuditLog]
141
+ end
@@ -0,0 +1,40 @@
1
+ module Bizside
2
+ module Rsync
3
+
4
+ def self.cleanup_old_dirs(backup_dirs, generations)
5
+ if backup_dirs.size > generations
6
+ puts '--------------------'
7
+ while backup_dirs.size > generations
8
+ old_backup_dir = backup_dirs.shift
9
+ puts "古いバックアップを削除します。#{old_backup_dir}"
10
+ FileUtils.rm_r(old_backup_dir, :force => true)
11
+ end
12
+ puts '--------------------'
13
+ end
14
+ end
15
+
16
+ def self.next_generation(backup_dir)
17
+ running_dir = backup_dir + '.running'
18
+ failure_dir = backup_dir + '.failed'
19
+
20
+ begin
21
+ FileUtils.ln_s(backup_dir, running_dir)
22
+ yield
23
+ rescue => e
24
+ FileUtils.ln_s(backup_dir, failure_dir)
25
+ raise e
26
+ ensure
27
+ FileUtils.rm_f(running_dir)
28
+ end
29
+ end
30
+
31
+ # YYYYMMDDHHMMSS形式
32
+ # .failed や .running は除外する
33
+ def self.get_backup_dirs(backup_base_dir)
34
+ ret = Dir::glob(File.join(backup_base_dir, '2*')).reject{|dir| File.basename(dir).index('.') }
35
+ ret = ret.sort
36
+ ret
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,17 @@
1
+ require 'pty'
2
+
3
+ module Bizside::SafePty
4
+
5
+ def self.spawn command, &block
6
+ PTY.spawn(command) do |r, w, p|
7
+ begin
8
+ yield r, w, p
9
+ rescue Errno::EIO
10
+ ensure
11
+ Process.wait p
12
+ end
13
+ end
14
+
15
+ $?.exitstatus
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ # Shibboleth環境に関するユーティリティ
2
+ class Bizside::ShibUtils
3
+ LOGOUT_URL = '/Shibboleth.sso/Logout'
4
+
5
+ def self.get_bizside_user(request)
6
+ ret = _get_bizside_user(request.env)
7
+ ret ||= _get_bizside_user(request.headers)
8
+ ret
9
+ end
10
+
11
+ def self._get_bizside_user(hash)
12
+ ret = hash['mail']
13
+ ret ||= hash['HTTP_X_BIZSIDE_USER']
14
+ ret ||= hash['X-BIZSIDE-USER']
15
+ ret
16
+ end
17
+
18
+ end
@@ -0,0 +1,18 @@
1
+ module Bizside
2
+ class ShowExceptions
3
+
4
+ BIZSIDE_EXCEPTION_ENV_KEY = 'bizside.exception'
5
+
6
+ def initialize(app)
7
+ @app = app
8
+ end
9
+
10
+ def call(env)
11
+ @app.call(env)
12
+ rescue Exception => exception
13
+ env[BIZSIDE_EXCEPTION_ENV_KEY] = exception # 発生した例外を保持
14
+ raise exception
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,45 @@
1
+ module Bizside
2
+ class SqlUtils
3
+
4
+ # LIKE検索用に検索文字列をエスケープします。
5
+ def self.escape_search(str)
6
+ str.gsub(/\\/, '\\\\\\\\').gsub(/%/, '\%').gsub(/_/, '\_')
7
+ end
8
+
9
+ def self.like(columns, query_string, options = {})
10
+ target_columns = columns
11
+ unless target_columns.is_a?(Array)
12
+ target_columns = [target_columns]
13
+ end
14
+
15
+ sql = []
16
+ sql[0] = '('
17
+
18
+ query_string.gsub(/ /, ' ').split.each_with_index do |s, i|
19
+ like = self.escape_search(s)
20
+
21
+ sql[0] << ' or ' if i > 0
22
+ sql[0] << '('
23
+
24
+ target_columns.each_with_index do |column, j|
25
+ sql[0] << ' or ' if j > 0
26
+ sql[0] << "#{column} like ?"
27
+
28
+ if options.fetch(:backward_match, false)
29
+ sql << like + '%'
30
+ elsif options.fetch(:forward_match, false)
31
+ sql << '%' + like
32
+ else
33
+ sql << '%' + like + '%'
34
+ end
35
+ end
36
+
37
+ sql[0] << ')'
38
+ end
39
+
40
+ sql[0] << ')'
41
+ sql
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,39 @@
1
+ module Bizside
2
+ class StringIO < StringIO
3
+ attr_accessor :base_dir
4
+ attr_accessor :fullpath
5
+ attr_accessor :content_type
6
+ attr_accessor :file_size
7
+ attr_accessor :md5
8
+
9
+ def initialize(string = '', mode = 'r+')
10
+ super(string, mode)
11
+ begin
12
+ self.md5 = Digest::MD5.hexdigest(self.read)
13
+ rescue
14
+ #失敗しても何もしない。
15
+ ensure
16
+ self.rewind
17
+ end
18
+ end
19
+
20
+ def original_filename
21
+ fullpath ? File.basename(fullpath) : ''
22
+ end
23
+
24
+ def original_dirname
25
+ fullpath ? File.dirname(fullpath) : ''
26
+ end
27
+
28
+ def relative_dirname
29
+ if fullpath
30
+ relpath = fullpath.sub(/^#{Regexp.quote(base_dir)}/,'').sub(/^\//,'')
31
+ File.dirname(relpath) == '.' ? '' : File.dirname(relpath)
32
+ else
33
+ ''
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,157 @@
1
+ require 'nkf'
2
+
3
+ module Bizside
4
+ class StringUtils
5
+ Characters = ('a'..'z').to_a + ('A'..'Z').to_a + ('0'..'9').to_a
6
+
7
+ def self.create_random_alpha_string length, case_sensitive = false
8
+ chars = ('a'..'z').to_a
9
+ chars += ('A'..'Z').to_a if case_sensitive
10
+ chars.sample(length).join
11
+ end
12
+
13
+ def self.create_random_string(number)
14
+ (Array.new(number.to_i) do
15
+ Characters[rand(Characters.size)]
16
+ end
17
+ ).join
18
+ end
19
+
20
+ def self.to_hiragana(s)
21
+ return s if is_empty(s)
22
+ NKF::nkf('-Ww --hiragana', s)
23
+ end
24
+
25
+ def self.to_katakana(s)
26
+ return s if is_empty(s)
27
+ NKF::nkf('-Ww --katakana', s)
28
+ end
29
+
30
+ def self.to_zen(s)
31
+ return s if is_empty(s)
32
+ NKF::nkf('-WwXm0', s)
33
+ end
34
+
35
+ def self.to_han(s)
36
+ return s if is_empty(s)
37
+ NKF::nkf('-Wwxm0Z0', s)
38
+ end
39
+
40
+ def self.sjis_to_utf8(s)
41
+ NKF.nkf('-Swx', s)
42
+ end
43
+
44
+ def self.utf8_to_sjis(s)
45
+ NKF.nkf('-Wsx', s)
46
+ end
47
+
48
+ def self.is_empty(s)
49
+ s.nil? or s.empty?
50
+ end
51
+
52
+ def self.equal(s1, s2)
53
+ if is_empty(s1) and is_empty(s2)
54
+ true
55
+ else
56
+ s1 == s2
57
+ end
58
+ end
59
+
60
+ def self.current_time_string(now = nil)
61
+ now = Time.now unless now
62
+ now.instance_eval { '%s%03d' % [strftime('%Y%m%d%H%M%S'), (usec / 1000.0).round] }
63
+ end
64
+
65
+ def self.is_user_password(value,minimum,maximum)
66
+ value =~ /^[!-~]{#{minimum},#{maximum}}$/u
67
+ end
68
+
69
+ def self.is_not_user_password(value,minimum,maximum)
70
+ ! is_user_password(value,minimum,maximum)
71
+ end
72
+
73
+ def self.is_login_id(value)
74
+ return nil if value =~ /\.{2,}/
75
+ value =~ /^[0-9a-z_\.\-]{1,50}$/u
76
+ end
77
+
78
+ def self.is_not_login_id(value)
79
+ ! is_login_id(value)
80
+ end
81
+
82
+ # HTMLタグを除去した文書を返す
83
+ def self.remove_html_tag(str)
84
+ return nil unless str
85
+ str=str.dup
86
+ str.sub!(/<[^<>]*>/,"") while /<[^<>]*>/ =~ str
87
+ str
88
+ end
89
+
90
+ # リッチテキストの改行を保ったまま、HTMLタグを除去した文書を返す
91
+ def self.remove_rich_html_tag(str)
92
+ return nil unless str
93
+
94
+ ret = str.gsub(/<div[^>]*>/, "\n")
95
+ ret = remove_html_tag(ret)
96
+ ret
97
+ end
98
+
99
+ # 指定した長さのランダム英数字文字列を返す。a-zA-Z0-9。62種類。
100
+ def self.rand_string(length = 8)
101
+ ret = []
102
+ length.times do |i|
103
+ ret[i] = Characters[rand(Characters.size)]
104
+ end
105
+ ret = ret.join
106
+
107
+ return ret if ret =~ /^(?=.*?[a-z])(?=.*?\d).*+$/iu
108
+
109
+ # 英字と数字が最低1文字を含むランダム英数字文字列を返すようにする
110
+ alphabets = ('a'..'z').to_a + ('A'..'Z').to_a
111
+ numerics = (0..9).to_a
112
+ ranges = (0...length).to_a
113
+ ret[ranges.slice!(rand(ranges.size))] = alphabets[rand(alphabets.size)]
114
+ ret[ranges.slice!(rand(ranges.size))] = numerics[rand(numerics.size)].to_s
115
+
116
+ ret
117
+ end
118
+
119
+ # URLからクエリストリングを除外して文字列を返す。
120
+ # https://www.example.com?foo=bar #=> https://www.example.com
121
+ def self.exclude_query_string(string)
122
+ return string unless string.include?("?")
123
+ string[/^.*(?=\?)/]
124
+ end
125
+
126
+ # 複数個の全角スペースを1個に圧縮する
127
+ def self.compress_doble_byte_space(string)
128
+ return nil unless string
129
+ string.gsub(/ +/, ' ')
130
+ end
131
+
132
+ # 文字列中に含まれる全角ハイフンっぽい文字を、指定した文字に一括置換する
133
+ def self.replace_char_like_hyphen_to(string, to: "\u30FC")
134
+ # U+002D Hyphen-Minus
135
+ # U+00AD Soft Hyphen
136
+ # U+02D7 Modifier Letter Minus Sign
137
+ # U+2010 Hyphen
138
+ # U+2011 Non-Breaking Hyphen
139
+ # U+2012 Figure Dash
140
+ # U+2013 En Dash
141
+ # U+2014 Em Dash
142
+ # U+2015 Horizontal Bar
143
+ # U+2043 Hyphen Bullet
144
+ # U+2212 Minus Sign
145
+ # U+2796 Heavy Minus Sign
146
+ # U+30FC Katakana-Hiragana Prolonged Sound Mark
147
+ # U+FE58 Small Em Dash
148
+ # U+FE63 Small Hyphen-Minus
149
+ # U+FF0D Fullwidth Hyphen-Minus
150
+ # U+FF70 Halfwidth Katakana-Hiragana Prolonged Sound Mark
151
+ string.gsub(
152
+ /[\u002D\u00AD\u02D7\u2010\u2011\u2012\u2013\u2014\u2015\u2043\u2212\u2796\u30FC\uFE58\uFE63\uFF0D\uFF70]/,
153
+ to
154
+ )
155
+ end
156
+ end
157
+ end