mega-clean-box 0.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/mega-clean-box.gemspec +12 -0
- data/web-console-4.3.0/CHANGELOG.markdown +201 -0
- data/web-console-4.3.0/MIT-LICENSE +20 -0
- data/web-console-4.3.0/README.markdown +193 -0
- data/web-console-4.3.0/Rakefile +29 -0
- data/web-console-4.3.0/lib/web-console.rb +3 -0
- data/web-console-4.3.0/lib/web_console/context.rb +45 -0
- data/web-console-4.3.0/lib/web_console/errors.rb +9 -0
- data/web-console-4.3.0/lib/web_console/evaluator.rb +42 -0
- data/web-console-4.3.0/lib/web_console/exception_mapper.rb +56 -0
- data/web-console-4.3.0/lib/web_console/extensions.rb +34 -0
- data/web-console-4.3.0/lib/web_console/injector.rb +32 -0
- data/web-console-4.3.0/lib/web_console/interceptor.rb +17 -0
- data/web-console-4.3.0/lib/web_console/locales/en.yml +15 -0
- data/web-console-4.3.0/lib/web_console/middleware.rb +137 -0
- data/web-console-4.3.0/lib/web_console/permissions.rb +42 -0
- data/web-console-4.3.0/lib/web_console/railtie.rb +88 -0
- data/web-console-4.3.0/lib/web_console/request.rb +46 -0
- data/web-console-4.3.0/lib/web_console/session.rb +80 -0
- data/web-console-4.3.0/lib/web_console/source_location.rb +12 -0
- data/web-console-4.3.0/lib/web_console/tasks/extensions.rake +62 -0
- data/web-console-4.3.0/lib/web_console/tasks/templates.rake +50 -0
- data/web-console-4.3.0/lib/web_console/template.rb +24 -0
- data/web-console-4.3.0/lib/web_console/templates/_inner_console_markup.html.erb +8 -0
- data/web-console-4.3.0/lib/web_console/templates/_markup.html.erb +5 -0
- data/web-console-4.3.0/lib/web_console/templates/_prompt_box_markup.html.erb +2 -0
- data/web-console-4.3.0/lib/web_console/templates/console.js.erb +1024 -0
- data/web-console-4.3.0/lib/web_console/templates/error_page.js.erb +69 -0
- data/web-console-4.3.0/lib/web_console/templates/index.html.erb +12 -0
- data/web-console-4.3.0/lib/web_console/templates/layouts/inlined_string.erb +1 -0
- data/web-console-4.3.0/lib/web_console/templates/layouts/javascript.erb +5 -0
- data/web-console-4.3.0/lib/web_console/templates/main.js.erb +1 -0
- data/web-console-4.3.0/lib/web_console/templates/regular_page.js.erb +24 -0
- data/web-console-4.3.0/lib/web_console/templates/style.css.erb +182 -0
- data/web-console-4.3.0/lib/web_console/testing/erb_precompiler.rb +27 -0
- data/web-console-4.3.0/lib/web_console/testing/fake_middleware.rb +44 -0
- data/web-console-4.3.0/lib/web_console/testing/helper.rb +11 -0
- data/web-console-4.3.0/lib/web_console/version.rb +5 -0
- data/web-console-4.3.0/lib/web_console/view.rb +58 -0
- data/web-console-4.3.0/lib/web_console/whiny_request.rb +33 -0
- data/web-console-4.3.0/lib/web_console.rb +37 -0
- metadata +82 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WebConsole
|
|
4
|
+
# Injects content into a Rack body.
|
|
5
|
+
class Injector
|
|
6
|
+
def initialize(body, headers)
|
|
7
|
+
@body = "".dup
|
|
8
|
+
|
|
9
|
+
body.each { |part| @body << part }
|
|
10
|
+
body.close if body.respond_to?(:close)
|
|
11
|
+
|
|
12
|
+
@headers = headers
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def inject(content)
|
|
16
|
+
# Set content-length header to the size of the current body
|
|
17
|
+
# + the extra content. Otherwise the response will be truncated.
|
|
18
|
+
if @headers[Rack::CONTENT_LENGTH]
|
|
19
|
+
@headers[Rack::CONTENT_LENGTH] = (@body.bytesize + content.bytesize).to_s
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
[
|
|
23
|
+
if position = @body.rindex("</body>")
|
|
24
|
+
[ @body.insert(position, content) ]
|
|
25
|
+
else
|
|
26
|
+
[ @body << content ]
|
|
27
|
+
end,
|
|
28
|
+
@headers
|
|
29
|
+
]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module WebConsole
|
|
2
|
+
module Interceptor
|
|
3
|
+
def self.call(request, error)
|
|
4
|
+
backtrace_cleaner = request.get_header("action_dispatch.backtrace_cleaner")
|
|
5
|
+
|
|
6
|
+
# Get the original exception if ExceptionWrapper decides to follow it.
|
|
7
|
+
Thread.current[:__web_console_exception] = error
|
|
8
|
+
|
|
9
|
+
# ActionView::Template::Error bypass ExceptionWrapper original
|
|
10
|
+
# exception following. The backtrace in the view is generated from
|
|
11
|
+
# reaching out to original_exception in the view.
|
|
12
|
+
if error.is_a?(ActionView::Template::Error)
|
|
13
|
+
Thread.current[:__web_console_exception] = error.cause
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
en:
|
|
2
|
+
errors:
|
|
3
|
+
unavailable_session: |
|
|
4
|
+
Session %{id} is no longer available in memory.
|
|
5
|
+
|
|
6
|
+
If you happen to run on a multi-process server (like Unicorn or Puma) the process
|
|
7
|
+
this request hit doesn't store %{id} in memory. Consider turning the number of
|
|
8
|
+
processes/workers to one (1) or using a different server in development.
|
|
9
|
+
|
|
10
|
+
unacceptable_request: |
|
|
11
|
+
A supported version is expected in the Accept header.
|
|
12
|
+
|
|
13
|
+
connection_refused: |
|
|
14
|
+
Oops! Failed to connect to the Web Console middleware.
|
|
15
|
+
Please make sure a rails development server is running.
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/string/strip"
|
|
4
|
+
|
|
5
|
+
module WebConsole
|
|
6
|
+
class Middleware
|
|
7
|
+
TEMPLATES_PATH = File.expand_path("../templates", __FILE__)
|
|
8
|
+
|
|
9
|
+
cattr_accessor :mount_point, default: "/__web_console"
|
|
10
|
+
cattr_accessor :whiny_requests, default: true
|
|
11
|
+
|
|
12
|
+
def initialize(app)
|
|
13
|
+
@app = app
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def call(env)
|
|
17
|
+
app_exception = catch :app_exception do
|
|
18
|
+
request = create_regular_or_whiny_request(env)
|
|
19
|
+
return call_app(env) unless request.permitted?
|
|
20
|
+
|
|
21
|
+
if id = id_for_repl_session_update(request)
|
|
22
|
+
return update_repl_session(id, request)
|
|
23
|
+
elsif id = id_for_repl_session_stack_frame_change(request)
|
|
24
|
+
return change_stack_trace(id, request)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
status, headers, body = call_app(env)
|
|
29
|
+
|
|
30
|
+
if (session = Session.from(Thread.current)) && acceptable_content_type?(headers)
|
|
31
|
+
headers["x-web-console-session-id"] = session.id
|
|
32
|
+
headers["x-web-console-mount-point"] = mount_point
|
|
33
|
+
|
|
34
|
+
template = Template.new(env, session)
|
|
35
|
+
body, headers = Injector.new(body, headers).inject(template.render("index"))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
[ status, headers, body ]
|
|
39
|
+
end
|
|
40
|
+
rescue => e
|
|
41
|
+
WebConsole.logger.error("\n#{e.class}: #{e}\n\tfrom #{e.backtrace.join("\n\tfrom ")}")
|
|
42
|
+
raise e
|
|
43
|
+
ensure
|
|
44
|
+
# Clean up the fiber locals after the session creation. Object#console
|
|
45
|
+
# uses those to communicate the current binding or exception to the middleware.
|
|
46
|
+
Thread.current[:__web_console_exception] = nil
|
|
47
|
+
Thread.current[:__web_console_binding] = nil
|
|
48
|
+
|
|
49
|
+
raise app_exception if Exception === app_exception
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def acceptable_content_type?(headers)
|
|
55
|
+
headers[Rack::CONTENT_TYPE].to_s.include?("html")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def json_response(opts = {})
|
|
59
|
+
status = opts.fetch(:status, 200)
|
|
60
|
+
headers = { Rack::CONTENT_TYPE => "application/json; charset = utf-8" }
|
|
61
|
+
body = yield.to_json
|
|
62
|
+
|
|
63
|
+
[ status, headers, [ body ] ]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def json_response_with_session(id, request, opts = {})
|
|
67
|
+
return respond_with_unavailable_session(id) unless session = Session.find(id)
|
|
68
|
+
|
|
69
|
+
json_response(opts) { yield session }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def create_regular_or_whiny_request(env)
|
|
73
|
+
request = Request.new(env)
|
|
74
|
+
whiny_requests ? WhinyRequest.new(request) : request
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def repl_sessions_re
|
|
78
|
+
@_repl_sessions_re ||= %r{#{mount_point}/repl_sessions/(?<id>[^/]+)}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def update_re
|
|
82
|
+
@_update_re ||= %r{#{repl_sessions_re}\z}
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def binding_change_re
|
|
86
|
+
@_binding_change_re ||= %r{#{repl_sessions_re}/trace\z}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def id_for_repl_session_update(request)
|
|
90
|
+
if request.xhr? && request.put?
|
|
91
|
+
update_re.match(request.path) { |m| m[:id] }
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def id_for_repl_session_stack_frame_change(request)
|
|
96
|
+
if request.xhr? && request.post?
|
|
97
|
+
binding_change_re.match(request.path) { |m| m[:id] }
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def update_repl_session(id, request)
|
|
102
|
+
json_response_with_session(id, request) do |session|
|
|
103
|
+
if input = request.params[:input]
|
|
104
|
+
{ output: session.eval(input) }
|
|
105
|
+
elsif input = request.params[:context]
|
|
106
|
+
{ context: session.context(input) }
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def change_stack_trace(id, request)
|
|
112
|
+
json_response_with_session(id, request) do |session|
|
|
113
|
+
session.switch_binding_to(request.params[:frame_id], request.params[:exception_object_id])
|
|
114
|
+
|
|
115
|
+
{ ok: true }
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def respond_with_unavailable_session(id)
|
|
120
|
+
json_response(status: 404) do
|
|
121
|
+
{ output: format(I18n.t("errors.unavailable_session"), id: id) }
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def respond_with_unacceptable_request
|
|
126
|
+
json_response(status: 406) do
|
|
127
|
+
{ output: I18n.t("errors.unacceptable_request") }
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def call_app(env)
|
|
132
|
+
@app.call(env)
|
|
133
|
+
rescue => e
|
|
134
|
+
throw :app_exception, e
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ipaddr"
|
|
4
|
+
|
|
5
|
+
module WebConsole
|
|
6
|
+
class Permissions
|
|
7
|
+
# IPv4 and IPv6 localhost should be always allowed.
|
|
8
|
+
ALWAYS_PERMITTED_NETWORKS = %w( 127.0.0.0/8 ::1 ::ffff:127.0.0.0/104 )
|
|
9
|
+
|
|
10
|
+
def initialize(networks = nil)
|
|
11
|
+
@networks = normalize_networks(networks).map(&method(:coerce_network_to_ipaddr)).uniq
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def include?(network)
|
|
15
|
+
@networks.any? { |permission| permission.include?(network.to_s) }
|
|
16
|
+
rescue IPAddr::InvalidAddressError
|
|
17
|
+
false
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_s
|
|
21
|
+
@networks.map(&method(:human_readable_ipaddr)).join(", ")
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def normalize_networks(networks)
|
|
27
|
+
Array(networks).concat(ALWAYS_PERMITTED_NETWORKS)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def coerce_network_to_ipaddr(network)
|
|
31
|
+
if network.is_a?(IPAddr)
|
|
32
|
+
network
|
|
33
|
+
else
|
|
34
|
+
IPAddr.new(network)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def human_readable_ipaddr(ipaddr)
|
|
39
|
+
ipaddr.to_range.to_s.split("..").uniq.join("/")
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/railtie"
|
|
4
|
+
|
|
5
|
+
module WebConsole
|
|
6
|
+
class Railtie < ::Rails::Railtie
|
|
7
|
+
config.web_console = ActiveSupport::OrderedOptions.new
|
|
8
|
+
|
|
9
|
+
initializer "web_console.initialize" do
|
|
10
|
+
require "bindex"
|
|
11
|
+
require "web_console/extensions"
|
|
12
|
+
|
|
13
|
+
ActionDispatch::DebugExceptions.register_interceptor(Interceptor)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
initializer "web_console.development_only" do
|
|
17
|
+
unless (config.web_console.development_only == false) || Rails.env.development?
|
|
18
|
+
abort <<-END.strip_heredoc
|
|
19
|
+
Web Console is activated in the #{Rails.env} environment. This is
|
|
20
|
+
usually a mistake. To ensure it's only activated in development
|
|
21
|
+
mode, move it to the development group of your Gemfile:
|
|
22
|
+
|
|
23
|
+
gem 'web-console', group: :development
|
|
24
|
+
|
|
25
|
+
If you still want to run it in the #{Rails.env} environment (and know
|
|
26
|
+
what you are doing), put this in your Rails application
|
|
27
|
+
configuration:
|
|
28
|
+
|
|
29
|
+
config.web_console.development_only = false
|
|
30
|
+
END
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
initializer "web_console.insert_middleware" do |app|
|
|
35
|
+
app.middleware.insert_before ActionDispatch::DebugExceptions, Middleware
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
initializer "web_console.mount_point" do
|
|
39
|
+
if mount_point = config.web_console.mount_point
|
|
40
|
+
Middleware.mount_point = mount_point.chomp("/")
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if root = Rails.application.config.relative_url_root
|
|
44
|
+
Middleware.mount_point = File.join(root, Middleware.mount_point)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
initializer "web_console.template_paths" do
|
|
49
|
+
if template_paths = config.web_console.template_paths
|
|
50
|
+
Template.template_paths.unshift(*Array(template_paths))
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
initializer "web_console.deprecator" do |app|
|
|
55
|
+
app.deprecators[:web_console] = WebConsole.deprecator if app.respond_to?(:deprecators)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
initializer "web_console.permissions" do
|
|
59
|
+
permissions = web_console_permissions
|
|
60
|
+
Request.permissions = Permissions.new(permissions)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def web_console_permissions
|
|
64
|
+
case
|
|
65
|
+
when config.web_console.permissions
|
|
66
|
+
config.web_console.permissions
|
|
67
|
+
when config.web_console.allowed_ips
|
|
68
|
+
config.web_console.allowed_ips
|
|
69
|
+
when config.web_console.whitelisted_ips
|
|
70
|
+
WebConsole.deprecator.warn(<<-MSG.squish)
|
|
71
|
+
The config.web_console.whitelisted_ips is deprecated and will be ignored in future release of web_console.
|
|
72
|
+
Please use config.web_console.allowed_ips instead.
|
|
73
|
+
MSG
|
|
74
|
+
config.web_console.whitelisted_ips
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
initializer "web_console.whiny_requests" do
|
|
79
|
+
if config.web_console.key?(:whiny_requests)
|
|
80
|
+
Middleware.whiny_requests = config.web_console.whiny_requests
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
initializer "i18n.load_path" do
|
|
85
|
+
config.i18n.load_path.concat(Dir[File.expand_path("../locales/*.yml", __FILE__)])
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WebConsole
|
|
4
|
+
class Request < ActionDispatch::Request
|
|
5
|
+
cattr_accessor :permissions, default: Permissions.new
|
|
6
|
+
|
|
7
|
+
def permitted?
|
|
8
|
+
permissions.include?(strict_remote_ip)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def strict_remote_ip
|
|
12
|
+
GetSecureIp.new(self, permissions).to_s
|
|
13
|
+
rescue ActionDispatch::RemoteIp::IpSpoofAttackError
|
|
14
|
+
"[Spoofed]"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
class GetSecureIp < ActionDispatch::RemoteIp::GetIp
|
|
20
|
+
def initialize(req, proxies)
|
|
21
|
+
# After rails/rails@07b2ff0 ActionDispatch::RemoteIp::GetIp initializes
|
|
22
|
+
# with a ActionDispatch::Request object instead of plain Rack
|
|
23
|
+
# environment hash. Keep both @req and @env here, so we don't if/else
|
|
24
|
+
# on Rails versions.
|
|
25
|
+
@req = req
|
|
26
|
+
@env = req.env
|
|
27
|
+
@check_ip = true
|
|
28
|
+
@proxies = proxies
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Used by rails <= 8.1
|
|
32
|
+
def filter_proxies(ips)
|
|
33
|
+
ips.reject do |ip|
|
|
34
|
+
@proxies.include?(ip)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Used by rails > 8.1.
|
|
39
|
+
def first_non_proxy(ips)
|
|
40
|
+
ips.find do |ip|
|
|
41
|
+
!@proxies.include?(ip)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WebConsole
|
|
4
|
+
# A session lets you persist an +Evaluator+ instance in memory associated
|
|
5
|
+
# with multiple bindings.
|
|
6
|
+
#
|
|
7
|
+
# Each newly created session is persisted into memory and you can find it
|
|
8
|
+
# later by its +id+.
|
|
9
|
+
#
|
|
10
|
+
# A session may be associated with multiple bindings. This is used by the
|
|
11
|
+
# error pages only, as currently, this is the only client that needs to do
|
|
12
|
+
# that.
|
|
13
|
+
class Session
|
|
14
|
+
cattr_reader :inmemory_storage, default: {}
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
# Finds a persisted session in memory by its id.
|
|
18
|
+
#
|
|
19
|
+
# Returns a persisted session if found in memory.
|
|
20
|
+
# Raises NotFound error unless found in memory.
|
|
21
|
+
def find(id)
|
|
22
|
+
inmemory_storage[id]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Create a Session from an binding or exception in a storage.
|
|
26
|
+
#
|
|
27
|
+
# The storage is expected to respond to #[]. The binding is expected in
|
|
28
|
+
# :__web_console_binding and the exception in :__web_console_exception.
|
|
29
|
+
#
|
|
30
|
+
# Can return nil, if no binding or exception have been preserved in the
|
|
31
|
+
# storage.
|
|
32
|
+
def from(storage)
|
|
33
|
+
if exc = storage[:__web_console_exception]
|
|
34
|
+
new(ExceptionMapper.follow(exc))
|
|
35
|
+
elsif binding = storage[:__web_console_binding]
|
|
36
|
+
new([[binding]])
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# An unique identifier for every REPL.
|
|
42
|
+
attr_reader :id
|
|
43
|
+
|
|
44
|
+
def initialize(exception_mappers)
|
|
45
|
+
@id = SecureRandom.hex(16)
|
|
46
|
+
|
|
47
|
+
@exception_mappers = exception_mappers
|
|
48
|
+
@evaluator = Evaluator.new(@current_binding = exception_mappers.first.first)
|
|
49
|
+
|
|
50
|
+
store_into_memory
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Evaluate +input+ on the current Evaluator associated binding.
|
|
54
|
+
#
|
|
55
|
+
# Returns a string of the Evaluator output.
|
|
56
|
+
def eval(input)
|
|
57
|
+
@evaluator.eval(input)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Switches the current binding to the one at specified +index+.
|
|
61
|
+
#
|
|
62
|
+
# Returns nothing.
|
|
63
|
+
def switch_binding_to(index, exception_object_id)
|
|
64
|
+
bindings = ExceptionMapper.find_binding(@exception_mappers, exception_object_id)
|
|
65
|
+
|
|
66
|
+
@evaluator = Evaluator.new(@current_binding = bindings[index.to_i])
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Returns context of the current binding
|
|
70
|
+
def context(objpath)
|
|
71
|
+
Context.new(@current_binding).extract(objpath)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def store_into_memory
|
|
77
|
+
inmemory_storage[id] = self
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :ext do
|
|
4
|
+
rootdir = Pathname("extensions")
|
|
5
|
+
|
|
6
|
+
desc "Build Chrome Extension"
|
|
7
|
+
task chrome: "chrome:build"
|
|
8
|
+
|
|
9
|
+
namespace :chrome do
|
|
10
|
+
dist = Pathname("dist/crx")
|
|
11
|
+
extdir = rootdir.join(dist)
|
|
12
|
+
manifest_json = rootdir.join("chrome/manifest.json")
|
|
13
|
+
|
|
14
|
+
directory extdir
|
|
15
|
+
|
|
16
|
+
task build: [ extdir, "lib:templates" ] do
|
|
17
|
+
cd rootdir do
|
|
18
|
+
cp_r [ "img/", "tmp/lib/" ], dist
|
|
19
|
+
`cd chrome && git ls-files`.split("\n").each do |src|
|
|
20
|
+
dest = dist.join(src)
|
|
21
|
+
mkdir_p dest.dirname
|
|
22
|
+
cp Pathname("chrome").join(src), dest
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Generate a .crx file.
|
|
28
|
+
task crx: [ :build, :npm ] do
|
|
29
|
+
out = "crx-web-console-#{JSON.parse(File.read(manifest_json))["version"]}.crx"
|
|
30
|
+
cd(extdir) { sh "node \"$(npm bin)/crx\" pack ./ -p ../crx-web-console.pem -o ../#{out}" }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Generate a .zip file for Chrome Web Store.
|
|
34
|
+
task zip: [ :build ] do
|
|
35
|
+
version = JSON.parse(File.read(manifest_json))["version"]
|
|
36
|
+
cd(extdir) { sh "zip -r ../crx-web-console-#{version}.zip ./" }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
desc "Launch a browser with the chrome extension."
|
|
40
|
+
task run: [ :build ] do
|
|
41
|
+
cd(rootdir) { sh "sh ./script/run_chrome.sh --load-extension=#{dist}" }
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
task :npm do
|
|
46
|
+
cd(rootdir) { sh "npm install --silent" }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
namespace :lib do
|
|
50
|
+
templates = Pathname("lib/web_console/templates")
|
|
51
|
+
tmplib = rootdir.join("tmp/lib/")
|
|
52
|
+
js_erb = FileList.new(templates.join("**/*.js.erb"))
|
|
53
|
+
dirs = js_erb.pathmap("%{^#{templates},#{tmplib}}d")
|
|
54
|
+
|
|
55
|
+
task templates: dirs + js_erb.pathmap("%{^#{templates},#{tmplib}}X")
|
|
56
|
+
|
|
57
|
+
dirs.each { |d| directory d }
|
|
58
|
+
rule ".js" => [ "%{^#{tmplib},#{templates}}X.js.erb" ] do |t|
|
|
59
|
+
File.write(t.name, WebConsole::Testing::ERBPrecompiler.new(t.source).build)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
|
|
5
|
+
namespace :templates do
|
|
6
|
+
desc "Run tests for templates"
|
|
7
|
+
task test: [ :daemonize, :npm, :rackup, :wait, :mocha, :kill, :exit ]
|
|
8
|
+
task serve: [ :npm, :rackup ]
|
|
9
|
+
|
|
10
|
+
workdir = Pathname(EXPANDED_CWD).join("test/templates")
|
|
11
|
+
pid = Pathname(Dir.tmpdir).join("web_console_test.pid")
|
|
12
|
+
runner = URI.parse("http://#{ENV['IP'] || '127.0.0.1'}:#{ENV['PORT'] || 29292}/html/test_runner.html")
|
|
13
|
+
rackup = "rackup --host #{runner.host} --port #{runner.port}"
|
|
14
|
+
result = nil
|
|
15
|
+
|
|
16
|
+
def need_to_wait?(uri)
|
|
17
|
+
Net::HTTP.start(uri.host, uri.port) { |http| http.get(uri.path) }
|
|
18
|
+
rescue Errno::ECONNREFUSED
|
|
19
|
+
retry if yield
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
task :daemonize do
|
|
23
|
+
rackup += " -D --pid #{pid}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
task :npm do
|
|
27
|
+
Dir.chdir(workdir) { system "npm install --silent" }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
task :rackup do
|
|
31
|
+
Dir.chdir(workdir) { system rackup }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
task :wait do
|
|
35
|
+
cnt = 0
|
|
36
|
+
need_to_wait?(runner) { sleep 1; cnt += 1; cnt < 5 }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
task :mocha do
|
|
40
|
+
Dir.chdir(workdir) { result = system("npx mocha-headless-chrome -f #{runner} -r dot") }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
task :kill do
|
|
44
|
+
system "kill #{File.read pid}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
task :exit do
|
|
48
|
+
exit result
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module WebConsole
|
|
4
|
+
# A facade that handles template rendering and composition.
|
|
5
|
+
#
|
|
6
|
+
# It introduces template helpers to ease the inclusion of scripts only on
|
|
7
|
+
# Rails error pages.
|
|
8
|
+
class Template
|
|
9
|
+
# Lets you customize the default templates folder location.
|
|
10
|
+
cattr_accessor :template_paths, default: [ File.expand_path("../templates", __FILE__) ]
|
|
11
|
+
|
|
12
|
+
def initialize(env, session)
|
|
13
|
+
@env = env
|
|
14
|
+
@session = session
|
|
15
|
+
@mount_point = Middleware.mount_point
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Render a template (inferred from +template_paths+) as a plain string.
|
|
19
|
+
def render(template)
|
|
20
|
+
view = View.with_empty_template_cache.with_view_paths(template_paths, instance_values)
|
|
21
|
+
view.render(template: template, layout: false)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|