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.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.vimproject +52 -11
  3. data/Rakefile +5 -0
  4. data/VERSION +1 -1
  5. data/bin/scout-camp +46 -0
  6. data/doc/terraform.md +188 -0
  7. data/lib/scout/aws/s3.rb +6 -4
  8. data/lib/scout/offsite/resource.rb +110 -5
  9. data/lib/scout/offsite/step.rb +21 -14
  10. data/lib/scout/offsite/sync.rb +38 -10
  11. data/lib/scout/offsite.rb +1 -0
  12. data/lib/scout/render/engine.rb +119 -0
  13. data/lib/scout/render/helpers.rb +92 -0
  14. data/lib/scout/render/resource.rb +54 -0
  15. data/lib/scout/render.rb +3 -0
  16. data/lib/scout/sinatra/auth.rb +158 -0
  17. data/lib/scout/sinatra/base/assets.rb +245 -0
  18. data/lib/scout/sinatra/base/favicon.rb +43 -0
  19. data/lib/scout/sinatra/base/headers.rb +77 -0
  20. data/lib/scout/sinatra/base/helpers.rb +14 -0
  21. data/lib/scout/sinatra/base/parameters.rb +147 -0
  22. data/lib/scout/sinatra/base/post_processing.rb +18 -0
  23. data/lib/scout/sinatra/base/session.rb +72 -0
  24. data/lib/scout/sinatra/base.rb +253 -0
  25. data/lib/scout/sinatra/entity.rb +259 -0
  26. data/lib/scout/sinatra/finder.rb +9 -0
  27. data/lib/scout/sinatra/fragment.rb +275 -0
  28. data/lib/scout/sinatra/htmx.rb +68 -0
  29. data/lib/scout/sinatra/knowledge_base.rb +14 -0
  30. data/lib/scout/sinatra/tool.rb +11 -0
  31. data/lib/scout/sinatra/workflow.rb +129 -0
  32. data/lib/scout-camp.rb +1 -1
  33. data/scout-camp.gemspec +39 -3
  34. data/scout_commands/find +83 -0
  35. data/scout_commands/glob +90 -0
  36. data/share/aws/lambda_function.rb +53 -30
  37. data/share/terraform/aws/efs_host/data.tf +1 -2
  38. data/share/terraform/aws/efs_host/main.tf +1 -1
  39. data/share/terraform/aws/efs_host/variables.tf +5 -1
  40. data/test/scout/render/test_engine.rb +88 -0
  41. data/test/scout/render/test_resource.rb +29 -0
  42. data/test/scout/sinatra/base/test_headers.rb +125 -0
  43. data/test/scout/sinatra/base/test_parameters.rb +88 -0
  44. data/test/scout/sinatra/test_base.rb +27 -0
  45. data/test/scout/sinatra/test_entity.rb +44 -0
  46. data/test/scout/sinatra/test_render.rb +44 -0
  47. data/test/scout/sinatra/test_workflow.rb +157 -0
  48. data/test/test_helper.rb +26 -0
  49. metadata +103 -2
@@ -0,0 +1,253 @@
1
+ require_relative 'base/helpers'
2
+ require_relative 'base/headers'
3
+ require_relative 'base/parameters'
4
+ require_relative 'base/assets'
5
+ require_relative 'base/session'
6
+ require_relative '../render/engine'
7
+
8
+ module SinatraScoutBase
9
+ def self.registered(app)
10
+ class << app
11
+ def post_get(...)
12
+ get(...)
13
+ post(...)
14
+ end
15
+ end
16
+
17
+ app.helpers ScoutRenderHelpers
18
+ app.register SinatraScoutHelpers
19
+
20
+ app.register SinatraScoutHeaders
21
+ app.register SinatraScoutParameters
22
+ app.register SinatraScoutPostProcessing
23
+ app.register SinatraScoutAssets
24
+ app.register SinatraScoutSession
25
+
26
+ app.helpers do
27
+ def json_halt(status, object = nil)
28
+ status, object = 200, status if object.nil?
29
+ content_type 'application/json'
30
+ halt status, (object.is_a?(String) ? {message: object}.to_json : object.to_json)
31
+ end
32
+
33
+ def html_halt(status, text = nil)
34
+ status, text = 200, status if text.nil?
35
+ content_type 'text/html'
36
+ halt status, text
37
+ end
38
+
39
+ def return_json(object)
40
+ json_halt 200, object
41
+ end
42
+
43
+ def charset_meta
44
+ '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">'
45
+ end
46
+
47
+ def html(content, layout = false, extension: %w(slim haml erb))
48
+ return charset_meta + content unless _layout
49
+ return charset_meta + content unless layout
50
+ layout = 'layout' if TrueClass === layout
51
+ layout_file = ScoutRender.find_resource('layout', extension: extension )
52
+ ScoutRender.render(layout_file, exec_context: self) do
53
+ content
54
+ end
55
+ end
56
+
57
+ def initiate_step(step, layout = nil, http_status = 200)
58
+ step.clean if _update == :clean
59
+ step.recursive_clean if _update == :recursive_clean
60
+ #step.clean if step.recoverable_error? && _update
61
+
62
+
63
+ return step if step.done?
64
+
65
+ case _cache_type
66
+ when :synchronous, :sync
67
+ step.run
68
+ when :asynchronous, :async
69
+ step.fork unless step.started?
70
+ when :exec
71
+ halt http_status, html(step.exec, layout)
72
+ end
73
+
74
+ Open.wait_for(step.path, timeout: 0.5)
75
+
76
+ step.join if step.done?
77
+
78
+ step
79
+ end
80
+
81
+ def serve_step(step, layout = nil, http_status = 200, &block)
82
+ layout = _layout if layout.nil?
83
+ status = step.status
84
+ step.status = :done if step.done? && ! status == :done
85
+ status = :done if step.done?
86
+ case status
87
+ when :error, 'error'
88
+ Log.exception step.exception if Exception === step.exception
89
+ raise step.exception
90
+ when :done, 'done'
91
+ step.join
92
+ if block_given?
93
+ block.call step
94
+ else
95
+ halt http_status || 200, html(step.load, layout)
96
+ end
97
+ else
98
+ render_or('wait', "Waiting on #{step.path}", cache: false, step: step, http_status: 202)
99
+ end
100
+ end
101
+
102
+ def serve_step_info(step)
103
+ case step.status
104
+ when 'done'
105
+ json_halt 200, step.info
106
+ when 'error'
107
+ json_halt 500, step.info
108
+ else
109
+ json_halt 202, step.info
110
+ end
111
+ end
112
+
113
+ def serve_step_json(step)
114
+ case step.status
115
+ when 'done'
116
+ json_halt 200, step.load.to_json
117
+ when 'error'
118
+ json_halt 500, step.info
119
+ else
120
+ json_halt 202, step.info
121
+ end
122
+ end
123
+
124
+ def serve_step_raw(step)
125
+ case step.status
126
+ when 'done'
127
+ status 200
128
+ mime_file step.path.find
129
+ when 'error'
130
+ json_halt 500, step.info
131
+ else
132
+ json_halt 202, step.info
133
+ end
134
+ end
135
+
136
+
137
+ def render_template(template, options = {}, &block)
138
+ layout, http_status = IndiferentHash.process_options options, :layout, :http_status,
139
+ layout: _layout
140
+ options = IndiferentHash.setup(clean_params).merge(options) if defined?(clean_params)
141
+ options = IndiferentHash.add_defaults options,
142
+ update: _update,
143
+ persist_step: _step,
144
+ cache: _cache_type != :none
145
+
146
+ step = ScoutRender.render_template(template, options.merge(exec_context: self, run: false, current_user: current_user), &block)
147
+ if String === step
148
+ status http_status if http_status
149
+ return html step, layout
150
+ end
151
+
152
+ @step = step
153
+
154
+ initiate_step step, layout, http_status
155
+
156
+ post_processing @step
157
+
158
+ serve_step step, layout, http_status
159
+ end
160
+
161
+ def render_partial(template, options = {}, &block)
162
+ render_template(template, options.merge(layout: false, cache: false), &block)
163
+ end
164
+
165
+ def render_or(template, alt=nil, params = {}, &block)
166
+ begin
167
+ render_template(template, params)
168
+ rescue TemplateNotFoundException
169
+ if block_given?
170
+ if block.arity == 0
171
+ block.call
172
+ else
173
+ block.call params
174
+ end
175
+ else
176
+ status = IndiferentHash.process_options params, :http_status, http_status: 200
177
+ halt status, alt
178
+ end
179
+ end
180
+ end
181
+ end
182
+
183
+ #{{{ HOOKS
184
+
185
+ app.before do
186
+ $script_name = script_name
187
+
188
+ if request_method == 'POST' && request.content_type.include?('json')
189
+ begin
190
+ post_text = request.body.read
191
+ post_data = JSON.parse(post_text)
192
+ params.merge!(post_data)
193
+ rescue
194
+ Log.exception $!
195
+ end
196
+ end
197
+
198
+ method_color = case request_method
199
+ when "GET"
200
+ :cyan
201
+ when "POST"
202
+ :yellow
203
+ end
204
+
205
+ Log.medium{ "#{Log.color method_color, request_method} #{Log.color(:blue, request.ip)}: " << path_info.gsub('/', Log.color(:blue, "/")) << ". Params: " << Log.color(:blue, Log.fingerprint(params))}
206
+
207
+ process_common_parameters
208
+
209
+ headers 'Access-Control-Allow-Origin' => '*'
210
+ end
211
+
212
+ app.after do
213
+ if @step
214
+ headers 'SCOUT_RENDER_STEP' => @step.name
215
+ end
216
+
217
+ if _update == :reload
218
+ redirect to(fullpath)
219
+ end
220
+ end
221
+
222
+ app.error do
223
+ error = env['sinatra.error']
224
+ case _format
225
+ when :json
226
+ json_halt 500, {class: error.class.to_s, error: error.message, backtrace: error.backtrace}
227
+ else
228
+ render_or "error", error: error.message, backtrace: error.backtrace, http_status: 500 do
229
+ <<-EOF
230
+ <pre>#{error.class}<pre/>
231
+ <pre>#{error.message}<pre/>
232
+ <pre>#{error.backtrace * "\n"}<pre/>
233
+ EOF
234
+ end
235
+ end
236
+ end
237
+
238
+ #{{{ ROUTES
239
+
240
+ app.post_get "/" do
241
+ render_template('main', clean_params)
242
+ end
243
+
244
+ app.post_get "/main/*" do
245
+ $title = "Scout"
246
+ splat = consume_parameter :splat
247
+ splat.unshift 'main'
248
+ template = splat * "/"
249
+ render_template(template, clean_params)
250
+ end
251
+
252
+ end
253
+ end
@@ -0,0 +1,259 @@
1
+ require 'sinatra/base'
2
+ require_relative 'base' # SinatraScoutHelpers: requested_format, consume_parameter
3
+ require_relative 'knowledge_base' # SinatraScoutHelpers: requested_format, consume_parameter
4
+
5
+ module SinatraScoutEntity
6
+ def self.registered(app)
7
+ app.register SinatraScoutKnowledgeBase
8
+
9
+ # utilities local to the component
10
+ app.helpers do
11
+ def clean_element(elem)
12
+ elem.to_s.gsub('&', '--AND--').
13
+ gsub('/', '-..-').
14
+ gsub("|", '-...-').
15
+ gsub('%', 'o-o').
16
+ gsub('[','(.-(').
17
+ gsub(']',').-)')
18
+ end
19
+
20
+ def restore_element(elem)
21
+ CGI.unescape(CGI.unescape(
22
+ elem.gsub('--AND--', '&').
23
+ gsub('-..-', '/').
24
+ gsub('-...-', '|').
25
+ gsub('o-o', '%').
26
+ gsub('(.-(','[').
27
+ gsub(').-)',']')
28
+ ))
29
+ end
30
+
31
+ def setup_entity(type, entity, params)
32
+ return Entity.prepare_entity entity, type, params
33
+
34
+ base_type, format = type.split ":"
35
+ entity_class = case
36
+ when Entity.formats.include?(base_type)
37
+ Entity.formats[base_type]
38
+ when Entity.formats.include?(format)
39
+ Entity.formats[format]
40
+ else
41
+ nil
42
+ end
43
+
44
+ raise "Unknown entity type: #{ type }" if entity_class.nil?
45
+
46
+ entity_class.setup(entity, params)
47
+ entity_class.annotations.each{|annotation| params.delete annotation }
48
+
49
+ entity.format = format if format and entity.respond_to? :format
50
+
51
+ entity
52
+ end
53
+
54
+ def entity_template(entity, type = :entity, other = nil)
55
+ file = case type
56
+ when :entity
57
+ File.join('entity', entity.base_type.to_s)
58
+ when :action, :property
59
+ File.join('entity', entity.base_type.to_s, other.to_s)
60
+ end
61
+ return file if ScoutRender.exists?(file)
62
+
63
+ file = case type
64
+ when :entity
65
+ File.join('entity', "Default")
66
+ when :action, :property
67
+ File.join('entity', "Default", other.to_s)
68
+ end
69
+ return file if ScoutRender.exists?(file)
70
+
71
+ raise TemplateNotFoundException, "Template not found for entity type #{entity.base_type} action #{other}" if other
72
+ raise TemplateNotFoundException, "Template not found for entity type #{entity.base_type}"
73
+ end
74
+
75
+ def entity_list_template(list, type = :entity, other = nil)
76
+ file = case type
77
+ when :entity
78
+ File.join('entity_list', list.base_type.to_s)
79
+ when :action
80
+ File.join('entity_list', list.base_type.to_s, other.to_s)
81
+ end
82
+ return file if ScoutRender.exists?(file)
83
+
84
+ file = case type
85
+ when :entity
86
+ File.join('entity_list', 'Default')
87
+ when :action
88
+ File.join('entity_list', 'Default', other.to_s)
89
+ end
90
+ return file if ScoutRender.exists?(file)
91
+
92
+ raise "Template not found for entity list type #{list.base_type} action #{other}" if other
93
+ raise "Template not found for entity list type #{list.base_type}"
94
+ end
95
+
96
+ def render_entity_template(entity, type = :entity, other = nil, params = {})
97
+ template = entity_template(entity, type, other)
98
+ render_template(template, params.merge(entity: entity))
99
+ end
100
+
101
+ def entity_list_template(list, type = :entity, other = nil, params = {})
102
+ template = entity_list_template(list, type, other)
103
+ render_template(template, params.merge(list: list))
104
+ end
105
+
106
+ def render_entity_partial(entity, type = :entity, other = nil, params = {})
107
+ template = entity_template(entity, type, other)
108
+ render_partial(template, params.merge(entity: entity))
109
+ end
110
+
111
+ def entity_list_partial(list, type = :entity, other = nil, params = {})
112
+ template = entity_list_template(list, type, other)
113
+ render_partial(template, params.merge(list: list))
114
+ end
115
+
116
+ def entity_url(entity, type = :entity, other = nil, params = {})
117
+ url = '/' + case type
118
+ when :entity
119
+ File.join('entity', entity.base_type.to_s, clean_element(entity))
120
+ when :action
121
+ File.join('entity_action', entity.base_type.to_s, clean_element(other), clean_element(entity))
122
+ when :property
123
+ File.join('entity_property', entity.base_type.to_s, clean_element(other), clean_element(entity))
124
+ end
125
+ url = add_GET_params(url, params)
126
+ url
127
+ end
128
+
129
+ def entity_list_url(list, type = :list, other = nil, params = {})
130
+ url = '/' + case type
131
+ when :list
132
+ File.join('entity_list', list.base_type.to_s, clean_element(list))
133
+ when :action
134
+ File.join('entity_list_action', list.base_type.to_s, clean_element(other), clean_element(list))
135
+ when :property
136
+ File.join('entity_list_property', list.base_type.to_s, clean_element(other), clean_element(list))
137
+ end
138
+ url = add_GET_params(url, params)
139
+ url
140
+ end
141
+
142
+ def entity_link(entity, type = :entity, other = nil, params = {})
143
+ link_options = IndiferentHash.pull_keys params, :link
144
+ url = entity_url(entity, type, other)
145
+ link_options = IndiferentHash.add_defaults link_options, class: "entity_#{type}", href: url
146
+ text = IndiferentHash.process_options link_options, :text,
147
+ text: entity.respond_to?(:name) ? entity.name : entity
148
+
149
+ html_tag('a', text, link_options)
150
+ end
151
+
152
+ def entity_list_link(list, type = :entity, other = nil, params = {})
153
+ link_options = IndiferentHash.pull_keys params, :link
154
+ url = entity_list_url(entity, type, other)
155
+ link_options = IndiferentHash.add_defaults link_options, class: "entity_list_#{type}", href: url
156
+ text = IndiferentHash.process_options link_options, :text,
157
+ text: list
158
+
159
+ html_tag('a', text, link_options)
160
+ end
161
+
162
+ def entity
163
+ return nil unless entity_type
164
+ return nil unless splat
165
+ @entity ||= begin
166
+ entity_id = restore_element(splat*"/")
167
+
168
+ if entity_type
169
+ setup_entity(entity_type, entity_id, params)
170
+ else
171
+ entity_id
172
+ end
173
+ end
174
+ end
175
+
176
+ def entity_render(entity, type = :entity, other = nil, params = {})
177
+ template = entity_template(entity, type, other)
178
+
179
+ params = params.merge(entity: entity)
180
+ params = params.merge(check: entity.check) if entity.respond_to?(:check)
181
+ render_template(template, params)
182
+ end
183
+
184
+ def entity_list_render(list, list_id, type = :entity, other = nil, params = {})
185
+ template = entity_template(list, type, other)
186
+ render_template(template, params.merge(list: list, list_id: list_id))
187
+ end
188
+ end
189
+
190
+ #{{{ SINGLE ENTITIES
191
+
192
+ # Entity report
193
+ app.post_get '/entity/:entity_type/*' do
194
+ if _format == :json
195
+ json_halt(200, { id: entity.to_s, info: entity.annotation_hash })
196
+ else
197
+ entity_render(entity, :entity, nil, params)
198
+ end
199
+ end
200
+
201
+ app.post_get '/entity_action/:entity_type/:entity_action/*' do
202
+ if _format == :json
203
+ json_halt(501, message: "Action JSON not implemented yet")
204
+ else
205
+ entity_render(entity, :action, entity_action, params)
206
+ end
207
+ end
208
+
209
+ app.post_get '/entity_property/:entity_type/:entity_property/*' do
210
+ entity
211
+ entity_property
212
+ args = consume_parameter(:args)
213
+
214
+ if args
215
+ prop_params = JSON.parse(args) rescue [args]
216
+ else
217
+ param_names = consume_parameter(:params)
218
+ if param_names
219
+ prop_params = param_names{|param| consume_parameter(param) }
220
+ else
221
+ prop_params = params.values
222
+ end
223
+ end
224
+
225
+ value = entity.send(entity_property, *prop_params)
226
+
227
+ if Step === value
228
+ job = value
229
+ initiate_step job, _layout
230
+
231
+ case _format
232
+ when :job
233
+ redirect to(add_GET_params(job_url(job), _layout: _layout))
234
+ when :json
235
+ serve_step_json job
236
+ else
237
+ serve_step job, _layout do
238
+ entity_render(entity, :property, entity_property, params)
239
+ end
240
+ end
241
+ end
242
+
243
+ if _format == :json
244
+ json_halt(200, value)
245
+ else
246
+ begin
247
+ entity_render(entity, :property, entity_property, params)
248
+ rescue TemplateNotFoundException
249
+ json_halt(200, value)
250
+ end
251
+ end
252
+ end
253
+
254
+ app.register_common_parameter(:entity_type, :escaped)
255
+ app.register_common_parameter(:entity_action, :escaped)
256
+ app.register_common_parameter(:entity_property, :escaped)
257
+ app.register_common_parameter(:_format, :symbol) do entity_property ? :json : :html end
258
+ end
259
+ end
@@ -0,0 +1,9 @@
1
+ module SinatraScoutFinder
2
+ def self.registered(app)
3
+ app.helpers do
4
+ def finder
5
+ nil
6
+ end
7
+ end
8
+ end
9
+ end