scout-camp 0.1.13 → 0.1.14
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 +4 -4
- data/.vimproject +52 -11
- data/Rakefile +5 -0
- data/VERSION +1 -1
- data/bin/scout-camp +46 -0
- data/doc/terraform.md +188 -0
- data/lib/scout/aws/s3.rb +6 -4
- data/lib/scout/offsite/resource.rb +110 -5
- data/lib/scout/offsite/step.rb +21 -14
- data/lib/scout/offsite/sync.rb +38 -10
- data/lib/scout/offsite.rb +1 -0
- data/lib/scout/render/engine.rb +119 -0
- data/lib/scout/render/helpers.rb +92 -0
- data/lib/scout/render/resource.rb +54 -0
- data/lib/scout/render.rb +3 -0
- data/lib/scout/sinatra/auth.rb +158 -0
- data/lib/scout/sinatra/base/assets.rb +245 -0
- data/lib/scout/sinatra/base/favicon.rb +43 -0
- data/lib/scout/sinatra/base/headers.rb +77 -0
- data/lib/scout/sinatra/base/helpers.rb +14 -0
- data/lib/scout/sinatra/base/parameters.rb +147 -0
- data/lib/scout/sinatra/base/post_processing.rb +18 -0
- data/lib/scout/sinatra/base/session.rb +72 -0
- data/lib/scout/sinatra/base.rb +253 -0
- data/lib/scout/sinatra/entity.rb +259 -0
- data/lib/scout/sinatra/finder.rb +9 -0
- data/lib/scout/sinatra/fragment.rb +275 -0
- data/lib/scout/sinatra/htmx.rb +68 -0
- data/lib/scout/sinatra/knowledge_base.rb +14 -0
- data/lib/scout/sinatra/tool.rb +11 -0
- data/lib/scout/sinatra/workflow.rb +129 -0
- data/lib/scout-camp.rb +1 -1
- data/scout-camp.gemspec +39 -3
- data/scout_commands/find +83 -0
- data/scout_commands/glob +90 -0
- data/share/aws/lambda_function.rb +53 -30
- data/share/terraform/aws/efs_host/data.tf +1 -2
- data/share/terraform/aws/efs_host/main.tf +1 -1
- data/share/terraform/aws/efs_host/variables.tf +5 -1
- data/test/scout/render/test_engine.rb +88 -0
- data/test/scout/render/test_resource.rb +29 -0
- data/test/scout/sinatra/base/test_headers.rb +125 -0
- data/test/scout/sinatra/base/test_parameters.rb +88 -0
- data/test/scout/sinatra/test_base.rb +27 -0
- data/test/scout/sinatra/test_entity.rb +44 -0
- data/test/scout/sinatra/test_render.rb +44 -0
- data/test/scout/sinatra/test_workflow.rb +157 -0
- data/test/test_helper.rb +26 -0
- metadata +103 -2
data/lib/scout/offsite/sync.rb
CHANGED
|
@@ -1,14 +1,39 @@
|
|
|
1
1
|
require_relative 'ssh'
|
|
2
2
|
class SSHLine
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
|
|
4
|
+
def self.locate(server, paths, map: :user, source: false)
|
|
5
|
+
paths = paths.collect do |path|
|
|
6
|
+
next path unless path.located?
|
|
7
|
+
relocated = Resource.identify(File.expand_path(path)).find_all.
|
|
8
|
+
select{|file| Open.same_file(file, path) }.
|
|
9
|
+
first
|
|
10
|
+
next path unless relocated
|
|
11
|
+
relocated.identify
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
script = <<-EOF
|
|
15
|
+
map = #{map.nil? ? "nil" : ":#{map}" }
|
|
16
|
+
paths = #{paths}
|
|
7
17
|
located = paths.collect{|p| Path.setup(p).find(map) }
|
|
8
|
-
|
|
9
|
-
|
|
18
|
+
EOF
|
|
19
|
+
|
|
20
|
+
script += <<-EOF if source
|
|
21
|
+
located = located.collect{|p| p.exists? ? p : nil }
|
|
22
|
+
EOF
|
|
23
|
+
|
|
24
|
+
script += <<-EOF
|
|
25
|
+
identified = paths.collect{|p| Resource.identify(p) if p }
|
|
26
|
+
located = located.each{|path| path << "/" if path && path.directory? && ! path.end_with?('/') }
|
|
10
27
|
[located, identified]
|
|
11
28
|
EOF
|
|
29
|
+
|
|
30
|
+
located, identified = SSHLine.scout server, script
|
|
31
|
+
|
|
32
|
+
identified.zip(paths).each do |ip,p|
|
|
33
|
+
ip.pkgdir = p.pkgdir if Path === p
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
[located, identified]
|
|
12
37
|
end
|
|
13
38
|
|
|
14
39
|
def self.rsync(source_path, target_path, directory: false, source: nil, target: nil, dry_run: false, hard_link: false)
|
|
@@ -24,11 +49,14 @@ located = located.collect{|path| path << "/" if path.directory? }
|
|
|
24
49
|
Open.mkdir(File.dirname(target_path))
|
|
25
50
|
end
|
|
26
51
|
|
|
52
|
+
source_path = File.expand_path(source_path) unless source
|
|
53
|
+
target_path = File.expand_path(target_path) unless target
|
|
54
|
+
|
|
27
55
|
cmd = 'rsync '
|
|
28
56
|
cmd << rsync_args
|
|
29
57
|
cmd << '-nv ' if dry_run
|
|
30
|
-
cmd << (source ? [source, source_path] * ":" : source_path) << " "
|
|
31
|
-
cmd << (target ? [target, target_path] * ":" : target_path) << " "
|
|
58
|
+
cmd << (source ? [source, "'" + source_path + "'"] * ":" : "'" + source_path + "'") << " "
|
|
59
|
+
cmd << (target ? [target, "'" + target_path + "'"] * ":" : "'" + target_path + "'") << " "
|
|
32
60
|
|
|
33
61
|
CMD.cmd_log(cmd, :log => Log::HIGH)
|
|
34
62
|
end
|
|
@@ -38,14 +66,14 @@ located = located.collect{|path| path << "/" if path.directory? }
|
|
|
38
66
|
target = nil if target == 'localhost'
|
|
39
67
|
|
|
40
68
|
if source
|
|
41
|
-
source_paths, identified_paths = SSHLine.locate(source, paths)
|
|
69
|
+
source_paths, identified_paths = SSHLine.locate(source, paths, source: true)
|
|
42
70
|
else
|
|
43
71
|
source_paths = paths.collect{|p| Path === p ? p.find : p }
|
|
44
72
|
identified_paths = paths.collect{|p| Resource.identify(p) }
|
|
45
73
|
end
|
|
46
74
|
|
|
47
75
|
if target
|
|
48
|
-
target_paths = SSHLine.locate(target, identified_paths, map: map)
|
|
76
|
+
target_paths, identified_paths = SSHLine.locate(target, identified_paths, map: map)
|
|
49
77
|
else
|
|
50
78
|
target_paths = identified_paths.collect{|p| p.find(map) }
|
|
51
79
|
end
|
data/lib/scout/offsite.rb
CHANGED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
require_relative 'resource'
|
|
2
|
+
require_relative 'helpers'
|
|
3
|
+
|
|
4
|
+
require 'tilt'
|
|
5
|
+
|
|
6
|
+
class TemplateNotFoundException < StandardError; end
|
|
7
|
+
class FragmentNotFound < StandardError; end
|
|
8
|
+
|
|
9
|
+
module ScoutRender
|
|
10
|
+
def self.render(template_file = nil, params = {}, &block)
|
|
11
|
+
exec_context = IndiferentHash.process_options params,
|
|
12
|
+
:exec_context,
|
|
13
|
+
exec_context: self
|
|
14
|
+
|
|
15
|
+
if block && template_file.nil?
|
|
16
|
+
params = params.values if Hash === params
|
|
17
|
+
block.call *params
|
|
18
|
+
else
|
|
19
|
+
Log.debug "Render #{template_file}"
|
|
20
|
+
begin
|
|
21
|
+
Tilt.new(template_file).render(exec_context, params, &block)
|
|
22
|
+
ensure
|
|
23
|
+
exec_context.add_checks template_file if exec_context.respond_to?(:add_checks)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.render_step(template_file = nil, options = {}, &block)
|
|
29
|
+
exec_context = IndiferentHash.process_options options, :exec_context
|
|
30
|
+
|
|
31
|
+
persist_options = IndiferentHash.pull_keys options, :persist
|
|
32
|
+
|
|
33
|
+
persist_options = IndiferentHash.add_defaults persist_options,
|
|
34
|
+
dir: ScoutRender.cache_dir,
|
|
35
|
+
other: options,
|
|
36
|
+
name: "Step"
|
|
37
|
+
|
|
38
|
+
step_name = IndiferentHash.process_options persist_options, :step
|
|
39
|
+
|
|
40
|
+
if step_name
|
|
41
|
+
dir = persist_options[:dir]
|
|
42
|
+
path = Path === dir ? dir[step_name] : File.join(dir, step_name)
|
|
43
|
+
persist_options[:path] = path
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
path, extension, update = IndiferentHash.process_options persist_options,
|
|
47
|
+
:path, :extension, :update,
|
|
48
|
+
path: Persist.persistence_path(persist_options[:name], persist_options),
|
|
49
|
+
extension: 'html',
|
|
50
|
+
update: update
|
|
51
|
+
|
|
52
|
+
path = path.set_extension extension if extension and ! path.end_with?(".#{extension}")
|
|
53
|
+
|
|
54
|
+
if block
|
|
55
|
+
step = Step.new path, options, exec_context: exec_context, &block
|
|
56
|
+
else
|
|
57
|
+
step = Step.new path, options, exec_context: exec_context do
|
|
58
|
+
ScoutRender.render(template_file, options.merge(exec_context: self))
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
step.exec_context = step unless exec_context
|
|
63
|
+
|
|
64
|
+
step.type = :text
|
|
65
|
+
|
|
66
|
+
step.extend ScoutRenderHelpers if exec_context.nil?
|
|
67
|
+
|
|
68
|
+
step.exec_context.instance_variable_set(:@step, step)
|
|
69
|
+
|
|
70
|
+
step
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.render_template(template = nil, options = {}, &block)
|
|
74
|
+
options = IndiferentHash.add_defaults options, persist_name: template
|
|
75
|
+
|
|
76
|
+
extension, update, run, cache, check = IndiferentHash.process_options options,
|
|
77
|
+
:extension, :update, :run, :cache, :check,
|
|
78
|
+
extension: %w(slim haml erb),
|
|
79
|
+
run: true,
|
|
80
|
+
cache: true
|
|
81
|
+
|
|
82
|
+
template_file = ScoutRender.find_resource(template, extension: extension) if template
|
|
83
|
+
|
|
84
|
+
raise TemplateNotFoundException, template unless template_file.nil? || template_file.exists?
|
|
85
|
+
|
|
86
|
+
return ScoutRender.render(template_file, options, &block) if ! cache
|
|
87
|
+
|
|
88
|
+
step = ScoutRender.render_step(template_file, options, &block)
|
|
89
|
+
|
|
90
|
+
#checks = step.info[:checks] || []
|
|
91
|
+
#checks << check if check
|
|
92
|
+
#checks << [template_file]
|
|
93
|
+
#checks = checks.flatten.uniq
|
|
94
|
+
#step.set_info :checks, checks
|
|
95
|
+
|
|
96
|
+
case update
|
|
97
|
+
when :false, 'false', false, :wait, 'wait'
|
|
98
|
+
when :true, 'true', true, :reload, 'reload'
|
|
99
|
+
step.clean
|
|
100
|
+
when nil
|
|
101
|
+
if checks = step.info[:checks]
|
|
102
|
+
step.clean if (step.done? || step.error?) && step.path.outdated?(checks)
|
|
103
|
+
end
|
|
104
|
+
else
|
|
105
|
+
step.clean if step.error? && step.recoverable_error?
|
|
106
|
+
step.clean unless step.running?
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
if run
|
|
110
|
+
step.run unless step.done?
|
|
111
|
+
else
|
|
112
|
+
step
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def self.render_partial(template, options = {})
|
|
117
|
+
render_template(template, options)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module ScoutRenderHelpers
|
|
2
|
+
def escape_html(text)
|
|
3
|
+
Rack::Utils.escape_html(text)
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def hash_to_html_tag_attributes(hash)
|
|
7
|
+
return "" if hash.nil? or hash.empty?
|
|
8
|
+
hash.collect{|k,v|
|
|
9
|
+
case
|
|
10
|
+
when (k.nil? or v.nil? or (String === v and v.empty?))
|
|
11
|
+
nil
|
|
12
|
+
when Array === v
|
|
13
|
+
[k,"'" << v * " " << "'"] * "="
|
|
14
|
+
when String === v
|
|
15
|
+
[k,"'" << v << "'"] * "="
|
|
16
|
+
when Symbol === v
|
|
17
|
+
[k,"'" << v.to_s << "'"] * "="
|
|
18
|
+
when TrueClass === v
|
|
19
|
+
[k,"'" << v.to_s << "'"] * "="
|
|
20
|
+
when Numeric === v
|
|
21
|
+
[k,"'" << v.to_s << "'"] * "="
|
|
22
|
+
else
|
|
23
|
+
nil
|
|
24
|
+
end
|
|
25
|
+
}.compact * " "
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def html_tag(tag, content = nil, params = {})
|
|
29
|
+
attr_str = hash_to_html_tag_attributes(params)
|
|
30
|
+
attr_str = " " << attr_str if String === attr_str and attr_str != ""
|
|
31
|
+
html = if content.nil?
|
|
32
|
+
"<#{ tag }#{attr_str}/>"
|
|
33
|
+
else
|
|
34
|
+
"<#{ tag }#{attr_str}>#{ content.to_s }</#{ tag }>"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
html
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def remove_GET_param(url, param)
|
|
41
|
+
if Array === param
|
|
42
|
+
param.each do |p|
|
|
43
|
+
url = remove_GET_param(url, p)
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
url = url.gsub(/(\?|&)#{param}=[^&]+/,'\1')
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
url = url.sub(/\?&/, '?')
|
|
50
|
+
url = url.sub(/&&*/, '&')
|
|
51
|
+
url = url.sub(/&$/, '')
|
|
52
|
+
url = url.sub(/\?$/,'')
|
|
53
|
+
url
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def add_GET_param(url, param, value)
|
|
57
|
+
url = remove_GET_param(url, param)
|
|
58
|
+
if url =~ /\?.+=/
|
|
59
|
+
url + "&#{ param }=#{ value }"
|
|
60
|
+
else
|
|
61
|
+
url + "?#{ param }=#{ value }"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def add_GET_params(url, params)
|
|
66
|
+
params.each do |k,v|
|
|
67
|
+
url = add_GET_param(url, k, v)
|
|
68
|
+
end
|
|
69
|
+
url
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def add_checks(checks)
|
|
73
|
+
return unless Step === @step
|
|
74
|
+
checks = [checks] unless Array === checks
|
|
75
|
+
current_checks = @step.info[:checks] || []
|
|
76
|
+
|
|
77
|
+
new_files = checks - current_checks
|
|
78
|
+
@step.set_info :checks, new_files if new_files.any?
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def outdated?
|
|
82
|
+
return false unless Step === @step
|
|
83
|
+
return false unless @step.path.exists?
|
|
84
|
+
current_checks = @step.info[:checks] || []
|
|
85
|
+
@step.path.outdated?(current_checks)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def log(...)
|
|
89
|
+
return unless Step === @step
|
|
90
|
+
@step.log(...)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
require 'scout'
|
|
2
|
+
module ScoutRender
|
|
3
|
+
extend Resource
|
|
4
|
+
|
|
5
|
+
self.subdir = 'share/views'
|
|
6
|
+
|
|
7
|
+
def self.find_resource(name, extension: %w(slim erb haml) )
|
|
8
|
+
return name.find_with_extension(extension) if Path === name && name.find_with_extension(extension).exists?
|
|
9
|
+
ScoutRender.root[name].find_with_extension(extension)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.exists?(name, extension: %w(slim erb haml) )
|
|
13
|
+
find_resource(name, extension: extension).exists?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.find_haml(name)
|
|
17
|
+
find_resource(name, extension: 'haml')
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.find_slim(name)
|
|
21
|
+
find_resource(name, extension: 'slim')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.find_js(name)
|
|
25
|
+
find_resource(name, extension: 'js')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def self.find_css(name)
|
|
29
|
+
find_resource(name, extension: 'css')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def self.find_sass(name)
|
|
34
|
+
find_resource(name, extension: 'sass')
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
class << self
|
|
38
|
+
undef :method_missing
|
|
39
|
+
|
|
40
|
+
attr_accessor :app_dir, :cache_dir, :files_dir
|
|
41
|
+
|
|
42
|
+
def app_dir
|
|
43
|
+
@app_dir ||= Scout.var.render[self.to_s]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def cache_dir
|
|
47
|
+
@cache_dir ||= app_dir.cache
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def files_dir
|
|
51
|
+
@files_dir ||= app_dir.files
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
data/lib/scout/render.rb
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
require "sinatra"
|
|
2
|
+
require "omniauth"
|
|
3
|
+
require "omniauth-google-oauth2"
|
|
4
|
+
|
|
5
|
+
module SinatraScoutAuth
|
|
6
|
+
def self.registered(app)
|
|
7
|
+
|
|
8
|
+
app.use OmniAuth::Builder do
|
|
9
|
+
client_id = Scout::Config.get :client, :google, :google_oauth, env:"GOOGLE_CLIENT_ID"
|
|
10
|
+
client_secret = Scout::Config.get :secret, :google, :google_oauth, env:"GOOGLE_CLIENT_SECRET"
|
|
11
|
+
provider :google_oauth2, client_id, client_secret,
|
|
12
|
+
access_type: 'offline',
|
|
13
|
+
prompt: 'select_account',
|
|
14
|
+
scope: 'email',
|
|
15
|
+
setup: lambda { |env|
|
|
16
|
+
script_name = env['HTTP_SCRIPT_NAME']
|
|
17
|
+
if script_name
|
|
18
|
+
referer = env['rack.session']['return_to']
|
|
19
|
+
uri = URI.parse(referer)
|
|
20
|
+
# Keep only scheme + host + (optional port)
|
|
21
|
+
base = "#{uri.scheme}://#{uri.host}"
|
|
22
|
+
base += ":#{uri.port}" if uri.port && ![80, 443].include?(uri.port)
|
|
23
|
+
|
|
24
|
+
# Construct full redirect URI
|
|
25
|
+
redirect_uri = "#{base}#{script_name}/auth/google_oauth2/callback"
|
|
26
|
+
|
|
27
|
+
strategy = env['omniauth.strategy']
|
|
28
|
+
|
|
29
|
+
strategy.options[:redirect_uri] = redirect_uri
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
puts "OmniAuth session: #{env['rack.session'].inspect}"
|
|
33
|
+
}
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
app.before do
|
|
37
|
+
load_preferences
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
app.get "/auth/login" do
|
|
41
|
+
session[:return_to] = to(request.referer || "/")
|
|
42
|
+
|
|
43
|
+
render_or 'auth/login', <<~HTML
|
|
44
|
+
<form method='get' action='/auth/google_oauth2'>
|
|
45
|
+
<input type="hidden" name="authenticity_token" value='#{request.env["rack.session"]["csrf"]}'>
|
|
46
|
+
<button type='submit'>Login with Google</button>
|
|
47
|
+
</form>
|
|
48
|
+
HTML
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
app.get "/auth/:provider/callback" do
|
|
52
|
+
auth = request.env["omniauth.auth"]
|
|
53
|
+
|
|
54
|
+
# Store relevant info in session
|
|
55
|
+
session[:user] = {
|
|
56
|
+
uid: auth.uid,
|
|
57
|
+
name: auth.info.name,
|
|
58
|
+
email: auth.info.email,
|
|
59
|
+
image: auth.info.image
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return_to = session[:return_to]
|
|
63
|
+
redirect return_to || "/"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
app.get "/auth/logout" do
|
|
67
|
+
return_to = session[:return_to]
|
|
68
|
+
session.clear
|
|
69
|
+
render_or 'auth/logout', nil, return_to: return_to do
|
|
70
|
+
redirect return_to || "/"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
app.get "/auth/failure" do
|
|
75
|
+
render_or 'auth/fail', "Authentication failed: #{params[:message]}"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
app.helpers do
|
|
79
|
+
def current_user
|
|
80
|
+
session[:user]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def authenticated?
|
|
84
|
+
!current_user.nil?
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def protected!
|
|
88
|
+
render_or 'auth/missing', "Not authorized" unless authenticated?
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def user_name
|
|
92
|
+
case current_user
|
|
93
|
+
when Hash
|
|
94
|
+
current_user[:name] || current_user['name'] || current_user[:email] || current_user['email']
|
|
95
|
+
when String
|
|
96
|
+
current_user
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def user_dir
|
|
101
|
+
Scout.var.sinatra.users[user_name]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def user_file(file)
|
|
105
|
+
user_dir[file.to_s]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def user_save(file, content)
|
|
109
|
+
Open.sensible_write(user_file(file), content, force: true)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def user_load(file)
|
|
113
|
+
return nil unless user_file(file).exists?
|
|
114
|
+
user_file(file).read
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def save_preferences
|
|
118
|
+
Log.debug "save #{preferences.to_json}"
|
|
119
|
+
user_save(:preferences, preferences.to_json)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def load_preferences
|
|
123
|
+
return unless user_file(:preferences).exists?
|
|
124
|
+
session['preferences'] = JSON.parse(user_load(:preferences))
|
|
125
|
+
session['preferences_time'] = Open.mtime(user_file(:preferences))
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def updated_preferrences?
|
|
129
|
+
return true if session['preferences_time'].nil?
|
|
130
|
+
session['preferences_time'] < Open.mtime(user_file(:preferences))
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def preferences_changed
|
|
134
|
+
@preferences_changed ||= []
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def record_preference(key, value)
|
|
138
|
+
preferences_changed << key
|
|
139
|
+
preferences[key] = value
|
|
140
|
+
save_preferences if current_user
|
|
141
|
+
value
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def get_preference(key)
|
|
145
|
+
load_preferences if current_user && updated_preferrences?
|
|
146
|
+
preferences[key]
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def preference(key, default= nil, &block)
|
|
150
|
+
current = get_preference(key)
|
|
151
|
+
return current unless current.nil?
|
|
152
|
+
default = block.call if default.nil? and block_given?
|
|
153
|
+
record_preference(key, default) if default
|
|
154
|
+
default
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|