bizside 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|