bizside 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/app/assets/images/jquery-treeTable/images/toggle-collapse-dark.png +0 -0
- data/app/assets/images/jquery-treeTable/images/toggle-collapse-light.png +0 -0
- data/app/assets/images/jquery-treeTable/images/toggle-expand-dark.png +0 -0
- data/app/assets/images/jquery-treeTable/images/toggle-expand-light.png +0 -0
- data/app/assets/javascripts/bizside.js +1 -0
- data/app/assets/javascripts/jquery-treeTable/jquery.treeTable.js +400 -0
- data/app/assets/stylesheets/bizside.css +3 -0
- data/app/assets/stylesheets/jquery-treeTable/jquery.treeTable.css.erb +55 -0
- data/lib/bizside/acl/access_control_utils.rb +36 -0
- data/lib/bizside/acl/available_helper.rb +65 -0
- data/lib/bizside/acl/controller_helper.rb +15 -0
- data/lib/bizside/acl.rb +6 -0
- data/lib/bizside/active_record_logger.rb +3 -0
- data/lib/bizside/audit/job_logger.rb +14 -0
- data/lib/bizside/audit/logger.rb +35 -0
- data/lib/bizside/audit_log.rb +158 -0
- data/lib/bizside/cache/entry.rb +112 -0
- data/lib/bizside/cache/file_store.rb +167 -0
- data/lib/bizside/cache/store.rb +237 -0
- data/lib/bizside/cache_util.rb +47 -0
- data/lib/bizside/cache_utils.rb +10 -0
- data/lib/bizside/carrierwave.rb +48 -0
- data/lib/bizside/config.rb +63 -0
- data/lib/bizside/configurations/mail.rb +37 -0
- data/lib/bizside/configurations/prefix.rb +25 -0
- data/lib/bizside/configurations/storage.rb +28 -0
- data/lib/bizside/coverage/launch.rb +25 -0
- data/lib/bizside/coverage/rcov_formatter.rb +13 -0
- data/lib/bizside/cron_validator.rb +62 -0
- data/lib/bizside/cucumber.rb +1 -0
- data/lib/bizside/engine.rb +4 -0
- data/lib/bizside/file_converter.rb +56 -0
- data/lib/bizside/file_uploader.rb +71 -0
- data/lib/bizside/gengou.rb +46 -0
- data/lib/bizside/gengou.yml +5 -0
- data/lib/bizside/hanaita_conf.rb +88 -0
- data/lib/bizside/implicit_ftps.rb +29 -0
- data/lib/bizside/itamae_conf.rb +186 -0
- data/lib/bizside/job_utils.rb +285 -0
- data/lib/bizside/log_analyzer.rb +122 -0
- data/lib/bizside/mailer.rb +56 -0
- data/lib/bizside/query_builder.rb +78 -0
- data/lib/bizside/railtie.rb +80 -0
- data/lib/bizside/record_has_warnings.rb +4 -0
- data/lib/bizside/resque.rb +141 -0
- data/lib/bizside/rsync.rb +40 -0
- data/lib/bizside/safe_pty.rb +17 -0
- data/lib/bizside/shib_utils.rb +18 -0
- data/lib/bizside/show_exceptions.rb +18 -0
- data/lib/bizside/sql_utils.rb +45 -0
- data/lib/bizside/string_io.rb +39 -0
- data/lib/bizside/string_utils.rb +157 -0
- data/lib/bizside/task_helper.rb +263 -0
- data/lib/bizside/tasks.rb +6 -0
- data/lib/bizside/test_help.rb +17 -0
- data/lib/bizside/uploader/content_type_validator.rb +39 -0
- data/lib/bizside/uploader/default_extensions.yml +15 -0
- data/lib/bizside/uploader/exif.rb +43 -0
- data/lib/bizside/uploader/extension_whitelist.rb +26 -0
- data/lib/bizside/uploader/filename_validator.rb +31 -0
- data/lib/bizside/user_agent/action_view/action_view_4.rb +56 -0
- data/lib/bizside/user_agent/action_view/use_variant.rb +4 -0
- data/lib/bizside/user_agent/action_view.rb +10 -0
- data/lib/bizside/user_agent/controller_helper.rb +51 -0
- data/lib/bizside/user_agent.rb +108 -0
- data/lib/bizside/validations.rb +8 -0
- data/lib/bizside/version.rb +3 -0
- data/lib/bizside/view_helper.rb +10 -0
- data/lib/bizside/warning.rb +24 -0
- data/lib/bizside/yes.rb +16 -0
- data/lib/bizside.rb +96 -0
- data/lib/cron_validator.rb +3 -0
- data/lib/gengou.rb +3 -0
- data/lib/job_utils.rb +3 -0
- data/lib/query_builder.rb +3 -0
- data/lib/record_has_warnings.rb +3 -0
- data/lib/sql_utils.rb +3 -0
- data/lib/string_utils.rb +3 -0
- data/lib/user_agent.rb +3 -0
- data/lib/yes.rb +3 -0
- data/rails/locales/ja.yml +12 -0
- data/validations/collection_presence_validator.rb +15 -0
- data/validations/email_validator.rb +1 -0
- data/validations/ip_address_validator.rb +22 -0
- data/validations/tel_validator.rb +28 -0
- data/validations/url_validator.rb +30 -0
- data/validations/zip_validator.rb +35 -0
- metadata +467 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
require_relative 'audit/logger'
|
|
2
|
+
|
|
3
|
+
module Bizside
|
|
4
|
+
class AuditLog
|
|
5
|
+
|
|
6
|
+
def initialize(app)
|
|
7
|
+
@app = app
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def call(env)
|
|
11
|
+
start = Time.now.strftime('%Y-%m-%dT%H:%M:%S.%3N%z')
|
|
12
|
+
@status, @headers, @response = @app.call(env)
|
|
13
|
+
stop = Time.now.strftime('%Y-%m-%dT%H:%M:%S.%3N%z')
|
|
14
|
+
exception = env[Bizside::ShowExceptions::BIZSIDE_EXCEPTION_ENV_KEY]
|
|
15
|
+
|
|
16
|
+
if env['BIZSIDE_SUPPRESS_AUDIT']
|
|
17
|
+
return @status, @headers, @response
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
if Bizside.rails_env&.development?
|
|
21
|
+
return @status, @headers, @response if env['REQUEST_URI'] =~ /\/[^\/]+\/assets\/.*/
|
|
22
|
+
elsif Bizside.rails_env&.test?
|
|
23
|
+
return @status, @headers, @response
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
info = build_loginfo(env, start, stop, @status, exception)
|
|
27
|
+
logger.record(info)
|
|
28
|
+
|
|
29
|
+
return @status, @headers, @response
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def logger
|
|
35
|
+
@logger ||= Bizside::Audit::Logger.logger
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def build_loginfo(env, start, stop, status, exception)
|
|
39
|
+
info = {
|
|
40
|
+
time: start,
|
|
41
|
+
add_on_name: detect_add_on_name(env),
|
|
42
|
+
feature: detect_feature(env),
|
|
43
|
+
action: detect_action(env),
|
|
44
|
+
data: detect_data(env),
|
|
45
|
+
interactive: detect_interactive(env),
|
|
46
|
+
company: detect_company(env),
|
|
47
|
+
user: detect_user(env),
|
|
48
|
+
employee_code: detect_employee_code(env),
|
|
49
|
+
department: detect_department(env),
|
|
50
|
+
project: detect_project(env),
|
|
51
|
+
server_name: env['SERVER_NAME'],
|
|
52
|
+
server_address: ENV['HOSTNAME'] || env['SERVER_ADDR'], # nginx と apache で異なる
|
|
53
|
+
server_port: env['SERVER_PORT'],
|
|
54
|
+
scheme: env['rack.url_scheme'],
|
|
55
|
+
path: env['PATH_INFO'],
|
|
56
|
+
referrer: env['HTTP_REFERER'],
|
|
57
|
+
request_method: env['REQUEST_METHOD'],
|
|
58
|
+
request_uri: env['BIZSIDE_REQUEST_URI'].presence || env['REQUEST_URI'],
|
|
59
|
+
remote_address: env['REMOTE_ADDR'],
|
|
60
|
+
status: status,
|
|
61
|
+
started_at: start,
|
|
62
|
+
finished_at: stop,
|
|
63
|
+
device: detect_device(env),
|
|
64
|
+
user_agent: detect_user_agent(env),
|
|
65
|
+
exception: detect_exception(exception),
|
|
66
|
+
exception_message: detect_exception_message(exception),
|
|
67
|
+
exception_backtrace: detect_exception_backtrace(exception)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
info
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def detect_company(env)
|
|
74
|
+
env['BIZSIDE_COMPANY']
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def detect_user(env)
|
|
78
|
+
ret = env['BIZSIDE_USER']
|
|
79
|
+
ret ||= Bizside::ShibUtils._get_bizside_user(env)
|
|
80
|
+
|
|
81
|
+
if ret.to_s.empty?
|
|
82
|
+
if defined?(Devise)
|
|
83
|
+
warden = env['warden']
|
|
84
|
+
if warden.authenticate?
|
|
85
|
+
auth_user = warden.send(warden.config[:default_scope])
|
|
86
|
+
ret = auth_user.email
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
ret
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def detect_add_on_name(env)
|
|
95
|
+
ret = env['BIZSIDE_ADD_ON_NAME']
|
|
96
|
+
ret ||= Bizside.config.add_on_name
|
|
97
|
+
ret
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def detect_feature(env)
|
|
101
|
+
env['BIZSIDE_FEATURE']
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def detect_action(env)
|
|
105
|
+
env['BIZSIDE_ACTION']
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def detect_data(env)
|
|
109
|
+
env['BIZSIDE_DATA']
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def detect_interactive(env)
|
|
113
|
+
env['BIZSIDE_INTERACTIVE']
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def detect_department(env)
|
|
117
|
+
env['BIZSIDE_DEPARTMENT']
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def detect_project(env)
|
|
121
|
+
env['BIZSIDE_PROJECT']
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def detect_device(env)
|
|
125
|
+
env['BIZSIDE_DEVICE']
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def detect_user_agent(env)
|
|
129
|
+
ret = env['BIZSIDE_USER_AGENT']
|
|
130
|
+
ret ||= env['HTTP_USER_AGENT']
|
|
131
|
+
ret
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def detect_employee_code(env)
|
|
135
|
+
ret = env['BIZSIDE_EMPLOYEE_CODE']
|
|
136
|
+
ret
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def detect_exception(exception)
|
|
140
|
+
return '' unless exception
|
|
141
|
+
|
|
142
|
+
exception.class.name
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def detect_exception_message(exception)
|
|
146
|
+
return '' unless exception
|
|
147
|
+
|
|
148
|
+
exception.to_s
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def detect_exception_backtrace(exception)
|
|
152
|
+
return '' unless exception
|
|
153
|
+
|
|
154
|
+
exception.backtrace.join("\n")
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
end
|
|
158
|
+
end
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
module Bizside
|
|
2
|
+
module Cache
|
|
3
|
+
class Entry
|
|
4
|
+
DEFAULT_COMPRESS_LIMIT = 16.kilobytes
|
|
5
|
+
|
|
6
|
+
def initialize(value, options = {})
|
|
7
|
+
if should_compress?(value, options)
|
|
8
|
+
@value = compress(value)
|
|
9
|
+
@compressed = true
|
|
10
|
+
else
|
|
11
|
+
@value = value
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
@created_at = Time.now.to_f
|
|
15
|
+
@expires_in = options[:expires_in]
|
|
16
|
+
@expires_in = @expires_in.to_f if @expires_in
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def value
|
|
20
|
+
convert_version_4beta1_entry! if defined?(@v)
|
|
21
|
+
compressed? ? uncompress(@value) : @value
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def expired?
|
|
25
|
+
convert_version_4beta1_entry! if defined?(@v)
|
|
26
|
+
@expires_in && @created_at + @expires_in <= Time.now.to_f
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def expires_at
|
|
30
|
+
@expires_in ? @created_at + @expires_in : nil
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def expires_at=(value)
|
|
34
|
+
if value
|
|
35
|
+
@expires_in = value.to_f - @created_at
|
|
36
|
+
else
|
|
37
|
+
@expires_in = nil
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def size
|
|
42
|
+
if defined?(@s)
|
|
43
|
+
@s
|
|
44
|
+
else
|
|
45
|
+
case value
|
|
46
|
+
when NilClass
|
|
47
|
+
0
|
|
48
|
+
when String
|
|
49
|
+
@value.bytesize
|
|
50
|
+
else
|
|
51
|
+
@s = Marshal.dump(@value).bytesize
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def dup_value!
|
|
57
|
+
convert_version_4beta1_entry! if defined?(@v)
|
|
58
|
+
|
|
59
|
+
if @value && !compressed? && !(@value.is_a?(Numeric) || @value == true || @value == false)
|
|
60
|
+
if @value.is_a?(String)
|
|
61
|
+
@value = @value.dup
|
|
62
|
+
else
|
|
63
|
+
@value = Marshal.load(Marshal.dump(@value))
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def should_compress?(value, options)
|
|
71
|
+
if value && options[:compress]
|
|
72
|
+
compress_threshold = options[:compress_threshold] || DEFAULT_COMPRESS_LIMIT
|
|
73
|
+
serialized_value_size = (value.is_a?(String) ? value : Marshal.dump(value)).bytesize
|
|
74
|
+
|
|
75
|
+
return true if serialized_value_size >= compress_threshold
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
false
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def compressed?
|
|
82
|
+
defined?(@compressed) ? @compressed : false
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def compress(value)
|
|
86
|
+
Zlib::Deflate.deflate(Marshal.dump(value))
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def uncompress(value)
|
|
90
|
+
Marshal.load(Zlib::Inflate.inflate(value))
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def convert_version_4beta1_entry!
|
|
94
|
+
if defined?(@v)
|
|
95
|
+
@value = @v
|
|
96
|
+
remove_instance_variable(:@v)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
if defined?(@c)
|
|
100
|
+
@compressed = @c
|
|
101
|
+
remove_instance_variable(:@c)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
if defined?(@x) && @x
|
|
105
|
+
@created_at ||= Time.now.to_f
|
|
106
|
+
@expires_in = @x - @created_at
|
|
107
|
+
remove_instance_variable(:@x)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
require 'bizside/cache/store'
|
|
2
|
+
|
|
3
|
+
module Bizside
|
|
4
|
+
module Cache
|
|
5
|
+
class FileStore < Store
|
|
6
|
+
|
|
7
|
+
attr_reader :cache_path
|
|
8
|
+
|
|
9
|
+
DIR_FORMATTER = "%03X"
|
|
10
|
+
FILENAME_MAX_SIZE = 228 # max filename size on file system is 255, minus room for timestamp and random characters appended by Tempfile (used by atomic write)
|
|
11
|
+
FILEPATH_MAX_SIZE = 900 # max is 1024, plus some room
|
|
12
|
+
EXCLUDED_DIRS = ['.', '..'].freeze
|
|
13
|
+
|
|
14
|
+
def initialize(cache_path, options = nil)
|
|
15
|
+
super(options)
|
|
16
|
+
@cache_path = cache_path.to_s
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def clear(options = nil)
|
|
20
|
+
root_dirs = Dir.entries(cache_path).reject {|f| (EXCLUDED_DIRS + [".gitkeep"]).include?(f)}
|
|
21
|
+
FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)})
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def cleanup(options = nil)
|
|
25
|
+
options = merged_options(options)
|
|
26
|
+
search_dir(cache_path) do |fname|
|
|
27
|
+
key = file_path_key(fname)
|
|
28
|
+
entry = read_entry(key, options)
|
|
29
|
+
delete_entry(key, options) if entry && entry.expired?
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def increment(name, amount = 1, options = nil)
|
|
34
|
+
modify_value(name, amount, options)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def decrement(name, amount = 1, options = nil)
|
|
38
|
+
modify_value(name, -amount, options)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def delete_matched(matcher, options = nil)
|
|
42
|
+
options = merged_options(options)
|
|
43
|
+
instrument(:delete_matched, matcher.inspect) do
|
|
44
|
+
matcher = key_matcher(matcher, options)
|
|
45
|
+
search_dir(cache_path) do |path|
|
|
46
|
+
key = file_path_key(path)
|
|
47
|
+
delete_entry(key, options) if key.match(matcher)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
protected
|
|
53
|
+
|
|
54
|
+
def read_entry(key, options)
|
|
55
|
+
file_name = key_file_path(key)
|
|
56
|
+
if File.exist?(file_name)
|
|
57
|
+
File.open(file_name) { |f| Marshal.load(f) }
|
|
58
|
+
end
|
|
59
|
+
rescue => e
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def write_entry(key, entry, options)
|
|
64
|
+
file_name = key_file_path(key)
|
|
65
|
+
return false if options[:unless_exist] && File.exist?(file_name)
|
|
66
|
+
ensure_cache_path(File.dirname(file_name))
|
|
67
|
+
File.atomic_write(file_name, cache_path) {|f| Marshal.dump(entry, f)}
|
|
68
|
+
true
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def delete_entry(key, options)
|
|
72
|
+
file_name = key_file_path(key)
|
|
73
|
+
if File.exist?(file_name)
|
|
74
|
+
begin
|
|
75
|
+
File.delete(file_name)
|
|
76
|
+
delete_empty_directories(File.dirname(file_name))
|
|
77
|
+
true
|
|
78
|
+
rescue => e
|
|
79
|
+
raise e if File.exist?(file_name)
|
|
80
|
+
false
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
private
|
|
86
|
+
|
|
87
|
+
def lock_file(file_name, &block)
|
|
88
|
+
if File.exist?(file_name)
|
|
89
|
+
File.open(file_name, 'r+') do |f|
|
|
90
|
+
begin
|
|
91
|
+
f.flock File::LOCK_EX
|
|
92
|
+
yield
|
|
93
|
+
ensure
|
|
94
|
+
f.flock File::LOCK_UN
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
else
|
|
98
|
+
yield
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def key_file_path(key)
|
|
103
|
+
if key.size > FILEPATH_MAX_SIZE
|
|
104
|
+
key = Digest::MD5.hexdigest(key)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
fname = URI.encode_www_form_component(key)
|
|
108
|
+
hash = Zlib.adler32(fname)
|
|
109
|
+
hash, dir_1 = hash.divmod(0x1000)
|
|
110
|
+
dir_2 = hash.modulo(0x1000)
|
|
111
|
+
fname_paths = []
|
|
112
|
+
|
|
113
|
+
begin
|
|
114
|
+
fname_paths << fname[0, FILENAME_MAX_SIZE]
|
|
115
|
+
fname = fname[FILENAME_MAX_SIZE..-1]
|
|
116
|
+
end until fname.blank?
|
|
117
|
+
|
|
118
|
+
File.join(cache_path, DIR_FORMATTER % dir_1, DIR_FORMATTER % dir_2, *fname_paths)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def file_path_key(path)
|
|
122
|
+
fname = path[cache_path.to_s.size..-1].split(File::SEPARATOR, 4).last
|
|
123
|
+
URI.decode_www_form_component(fname, Encoding::UTF_8)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def delete_empty_directories(dir)
|
|
127
|
+
return if File.realpath(dir) == File.realpath(cache_path)
|
|
128
|
+
if Dir.entries(dir).reject {|f| EXCLUDED_DIRS.include?(f)}.empty?
|
|
129
|
+
Dir.delete(dir) rescue nil
|
|
130
|
+
delete_empty_directories(File.dirname(dir))
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def ensure_cache_path(path)
|
|
135
|
+
FileUtils.makedirs(path) unless File.exist?(path)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def search_dir(dir, &callback)
|
|
139
|
+
return if !File.exist?(dir)
|
|
140
|
+
Dir.foreach(dir) do |d|
|
|
141
|
+
next if EXCLUDED_DIRS.include?(d)
|
|
142
|
+
name = File.join(dir, d)
|
|
143
|
+
if File.directory?(name)
|
|
144
|
+
search_dir(name, &callback)
|
|
145
|
+
else
|
|
146
|
+
callback.call name
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def modify_value(name, amount, options)
|
|
152
|
+
file_name = key_file_path(namespaced_key(name, options))
|
|
153
|
+
|
|
154
|
+
lock_file(file_name) do
|
|
155
|
+
options = merged_options(options)
|
|
156
|
+
|
|
157
|
+
if num = read(name, options)
|
|
158
|
+
num = num.to_i + amount
|
|
159
|
+
write(name, num, options)
|
|
160
|
+
num
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
require 'bizside/cache/entry'
|
|
2
|
+
|
|
3
|
+
module Bizside
|
|
4
|
+
module Cache
|
|
5
|
+
class Store
|
|
6
|
+
|
|
7
|
+
attr_reader :options
|
|
8
|
+
|
|
9
|
+
def initialize(options = nil)
|
|
10
|
+
@options = options ? options.dup : {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def fetch(name, options = nil)
|
|
14
|
+
if block_given?
|
|
15
|
+
options = merged_options(options)
|
|
16
|
+
key = namespaced_key(name, options)
|
|
17
|
+
|
|
18
|
+
cached_entry = find_cached_entry(key, name, options) unless options[:force]
|
|
19
|
+
entry = handle_expired_entry(cached_entry, key, options)
|
|
20
|
+
|
|
21
|
+
if entry
|
|
22
|
+
get_entry_value(entry, name, options)
|
|
23
|
+
else
|
|
24
|
+
save_block_result_to_cache(name, options) { |_name| yield _name }
|
|
25
|
+
end
|
|
26
|
+
else
|
|
27
|
+
read(name, options)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def read(name, options = nil)
|
|
32
|
+
options = merged_options(options)
|
|
33
|
+
key = namespaced_key(name, options)
|
|
34
|
+
instrument(:read, name, options) do |payload|
|
|
35
|
+
entry = read_entry(key, options)
|
|
36
|
+
if entry
|
|
37
|
+
if entry.expired?
|
|
38
|
+
delete_entry(key, options)
|
|
39
|
+
payload[:hit] = false if payload
|
|
40
|
+
nil
|
|
41
|
+
else
|
|
42
|
+
payload[:hit] = true if payload
|
|
43
|
+
entry.value
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
payload[:hit] = false if payload
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def read_multi(*names)
|
|
53
|
+
options = names.extract_options!
|
|
54
|
+
options = merged_options(options)
|
|
55
|
+
results = {}
|
|
56
|
+
names.each do |name|
|
|
57
|
+
key = namespaced_key(name, options)
|
|
58
|
+
entry = read_entry(key, options)
|
|
59
|
+
if entry
|
|
60
|
+
if entry.expired?
|
|
61
|
+
delete_entry(key, options)
|
|
62
|
+
else
|
|
63
|
+
results[name] = entry.value
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
results
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def fetch_multi(*names)
|
|
71
|
+
options = names.extract_options!
|
|
72
|
+
options = merged_options(options)
|
|
73
|
+
results = read_multi(*names, options)
|
|
74
|
+
|
|
75
|
+
names.each_with_object({}) do |name, memo|
|
|
76
|
+
memo[name] = results.fetch(name) do
|
|
77
|
+
value = yield name
|
|
78
|
+
write(name, value, options)
|
|
79
|
+
value
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def write(name, value, options = nil)
|
|
85
|
+
options = merged_options(options)
|
|
86
|
+
|
|
87
|
+
instrument(:write, name, options) do
|
|
88
|
+
entry = Bizside::Cache::Entry.new(value, options)
|
|
89
|
+
write_entry(namespaced_key(name, options), entry, options)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def delete(name, options = nil)
|
|
94
|
+
options = merged_options(options)
|
|
95
|
+
|
|
96
|
+
instrument(:delete, name) do
|
|
97
|
+
delete_entry(namespaced_key(name, options), options)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def exist?(name, options = nil)
|
|
102
|
+
options = merged_options(options)
|
|
103
|
+
|
|
104
|
+
instrument(:exist?, name) do
|
|
105
|
+
entry = read_entry(namespaced_key(name, options), options)
|
|
106
|
+
(entry && !entry.expired?) || false
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def delete_matched(matcher, options = nil)
|
|
111
|
+
raise NotImplementedError.new("does not support delete_matched")
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def increment(name, amount = 1, options = nil)
|
|
115
|
+
raise NotImplementedError.new("does not support increment")
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def decrement(name, amount = 1, options = nil)
|
|
119
|
+
raise NotImplementedError.new("does not support decrement")
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def cleanup(options = nil)
|
|
123
|
+
raise NotImplementedError.new("does not support cleanup")
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def clear(options = nil)
|
|
127
|
+
raise NotImplementedError.new("does not support clear")
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
protected
|
|
131
|
+
|
|
132
|
+
def key_matcher(pattern, options)
|
|
133
|
+
prefix = options[:namespace].is_a?(Proc) ? options[:namespace].call : options[:namespace]
|
|
134
|
+
if prefix
|
|
135
|
+
source = pattern.source
|
|
136
|
+
if source.start_with?('^')
|
|
137
|
+
source = source[1, source.length]
|
|
138
|
+
else
|
|
139
|
+
source = ".*#{source[0, source.length]}"
|
|
140
|
+
end
|
|
141
|
+
Regexp.new("^#{Regexp.escape(prefix)}:#{source}", pattern.options)
|
|
142
|
+
else
|
|
143
|
+
pattern
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def read_entry(key, options)
|
|
148
|
+
raise NotImplementedError.new
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def write_entry(key, entry, options)
|
|
152
|
+
raise NotImplementedError.new
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def delete_entry(key, options)
|
|
156
|
+
raise NotImplementedError.new
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
private
|
|
160
|
+
|
|
161
|
+
def merged_options(call_options)
|
|
162
|
+
if call_options
|
|
163
|
+
options.merge(call_options)
|
|
164
|
+
else
|
|
165
|
+
options.dup
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def expanded_key(key)
|
|
170
|
+
return key.cache_key.to_s if key.respond_to?(:cache_key)
|
|
171
|
+
|
|
172
|
+
case key
|
|
173
|
+
when Array
|
|
174
|
+
if key.size > 1
|
|
175
|
+
key = key.collect{|element| expanded_key(element)}
|
|
176
|
+
else
|
|
177
|
+
key = key.first
|
|
178
|
+
end
|
|
179
|
+
when Hash
|
|
180
|
+
key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"}
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
key.to_param
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def namespaced_key(key, options)
|
|
187
|
+
key = expanded_key(key)
|
|
188
|
+
namespace = options[:namespace] if options
|
|
189
|
+
prefix = namespace.is_a?(Proc) ? namespace.call : namespace
|
|
190
|
+
key = "#{prefix}:#{key}" if prefix
|
|
191
|
+
key
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
def instrument(operation, key, options = nil)
|
|
195
|
+
payload = { :key => key }
|
|
196
|
+
payload.merge!(options) if options.is_a?(Hash)
|
|
197
|
+
ActiveSupport::Notifications.instrument("cache_#{operation}.active_support", payload){ yield(payload) }
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def find_cached_entry(key, name, options)
|
|
201
|
+
instrument(:read, name, options) do |payload|
|
|
202
|
+
payload[:super_operation] = :fetch if payload
|
|
203
|
+
read_entry(key, options)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def handle_expired_entry(entry, key, options)
|
|
208
|
+
if entry && entry.expired?
|
|
209
|
+
race_ttl = options[:race_condition_ttl].to_i
|
|
210
|
+
if (race_ttl > 0) && (Time.now.to_f - entry.expires_at <= race_ttl)
|
|
211
|
+
entry.expires_at = Time.now + race_ttl
|
|
212
|
+
write_entry(key, entry, :expires_in => race_ttl * 2)
|
|
213
|
+
else
|
|
214
|
+
delete_entry(key, options)
|
|
215
|
+
end
|
|
216
|
+
entry = nil
|
|
217
|
+
end
|
|
218
|
+
entry
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def get_entry_value(entry, name, options)
|
|
222
|
+
instrument(:fetch_hit, name, options) { |payload| }
|
|
223
|
+
entry.value
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def save_block_result_to_cache(name, options)
|
|
227
|
+
result = instrument(:generate, name, options) do |payload|
|
|
228
|
+
yield(name)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
write(name, result, options)
|
|
232
|
+
result
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Bizside
|
|
2
|
+
module CacheUtil
|
|
3
|
+
|
|
4
|
+
def fetch(key, options = {})
|
|
5
|
+
if block_given?
|
|
6
|
+
action = cache.exist?(key) ? 'READ' : 'WRITE'
|
|
7
|
+
output_log "#{action} CACHE: #{key}"
|
|
8
|
+
cache.fetch(key, options) do
|
|
9
|
+
yield
|
|
10
|
+
end
|
|
11
|
+
else
|
|
12
|
+
output_log "READ CACHE: #{key}"
|
|
13
|
+
cache.fetch(key, options)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def delete(key)
|
|
18
|
+
output_log "CLEAR CACHE: #{key}"
|
|
19
|
+
cache.delete(key)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def delete_matched(matcher)
|
|
23
|
+
output_log "CLEAR CACHE: #{matcher}"
|
|
24
|
+
cache.delete_matched(matcher)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def clear(options = nil)
|
|
28
|
+
output_log "CLEAR ALL CACHE"
|
|
29
|
+
cache.clear(options)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def cache
|
|
33
|
+
raise 'サブクラスで実装'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def output_log message
|
|
39
|
+
if defined?(Rails) && Rails.logger
|
|
40
|
+
Rails.logger.info message
|
|
41
|
+
else
|
|
42
|
+
puts message
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
end
|