web-console 0.1.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of web-console might be problematic. Click here for more details.

Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.markdown +121 -0
  4. data/Rakefile +48 -0
  5. data/app/assets/javascripts/web_console/application.js +4 -0
  6. data/app/assets/javascripts/web_console/console_sessions.js +24 -0
  7. data/app/assets/stylesheets/web_console/application.css +13 -0
  8. data/app/assets/stylesheets/web_console/console_sessions.css +8 -0
  9. data/app/controllers/web_console/application_controller.rb +12 -0
  10. data/app/controllers/web_console/console_sessions_controller.rb +23 -0
  11. data/app/helpers/web_console/application_helper.rb +4 -0
  12. data/app/helpers/web_console/console_session_helper.rb +4 -0
  13. data/app/models/web_console/console_session.rb +107 -0
  14. data/app/views/layouts/web_console/application.html.erb +14 -0
  15. data/app/views/web_console/console_sessions/index.html.erb +4 -0
  16. data/config/routes.rb +5 -0
  17. data/lib/web-console.rb +1 -0
  18. data/lib/web_console.rb +7 -0
  19. data/lib/web_console/engine.rb +38 -0
  20. data/lib/web_console/fiber.rb +48 -0
  21. data/lib/web_console/repl.rb +59 -0
  22. data/lib/web_console/repl/dummy.rb +38 -0
  23. data/lib/web_console/repl/irb.rb +61 -0
  24. data/lib/web_console/stream.rb +27 -0
  25. data/lib/web_console/version.rb +3 -0
  26. data/test/controllers/web_console/console_sessions_controller_test.rb +57 -0
  27. data/test/dummy/README.rdoc +28 -0
  28. data/test/dummy/Rakefile +6 -0
  29. data/test/dummy/app/assets/javascripts/application.js +13 -0
  30. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  31. data/test/dummy/app/controllers/application_controller.rb +5 -0
  32. data/test/dummy/app/helpers/application_helper.rb +2 -0
  33. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  34. data/test/dummy/bin/bundle +3 -0
  35. data/test/dummy/bin/rails +4 -0
  36. data/test/dummy/bin/rake +4 -0
  37. data/test/dummy/config.ru +4 -0
  38. data/test/dummy/config/application.rb +15 -0
  39. data/test/dummy/config/boot.rb +5 -0
  40. data/test/dummy/config/database.yml +25 -0
  41. data/test/dummy/config/environment.rb +5 -0
  42. data/test/dummy/config/environments/development.rb +29 -0
  43. data/test/dummy/config/environments/production.rb +80 -0
  44. data/test/dummy/config/environments/test.rb +36 -0
  45. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  46. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  47. data/test/dummy/config/initializers/inflections.rb +16 -0
  48. data/test/dummy/config/initializers/mime_types.rb +5 -0
  49. data/test/dummy/config/initializers/secret_token.rb +12 -0
  50. data/test/dummy/config/initializers/session_store.rb +3 -0
  51. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  52. data/test/dummy/config/locales/en.yml +23 -0
  53. data/test/dummy/config/routes.rb +2 -0
  54. data/test/dummy/db/development.sqlite3 +0 -0
  55. data/test/dummy/db/schema.rb +16 -0
  56. data/test/dummy/db/test.sqlite3 +0 -0
  57. data/test/dummy/log/development.log +26591 -0
  58. data/test/dummy/log/test.log +78368 -0
  59. data/test/dummy/public/404.html +58 -0
  60. data/test/dummy/public/422.html +58 -0
  61. data/test/dummy/public/500.html +57 -0
  62. data/test/dummy/public/favicon.ico +0 -0
  63. data/test/dummy/tmp/cache/assets/development/sprockets/0280bb38c5058cc31c4fcd8c392a5ec4 +0 -0
  64. data/test/dummy/tmp/cache/assets/development/sprockets/05bd7b24c0a86010ebb28b50ac7cad52 +0 -0
  65. data/test/dummy/tmp/cache/assets/development/sprockets/2a48be7daff14cf56911b263cbe017c7 +0 -0
  66. data/test/dummy/tmp/cache/assets/development/sprockets/3a37adb5ebd079cf67dae597b8c2e4f8 +0 -0
  67. data/test/dummy/tmp/cache/assets/development/sprockets/4f79e5f341043e081becefe4952395c5 +0 -0
  68. data/test/dummy/tmp/cache/assets/development/sprockets/508ab3c25833ea537a5e3fc90df33595 +0 -0
  69. data/test/dummy/tmp/cache/assets/development/sprockets/5a8ab22e707dfc7ba00691f90e054d7e +0 -0
  70. data/test/dummy/tmp/cache/assets/development/sprockets/5adcb7569cdd03204d650e285f19351f +0 -0
  71. data/test/dummy/tmp/cache/assets/development/sprockets/654a1bde557d359b957dc0aa12b0dfa0 +0 -0
  72. data/test/dummy/tmp/cache/assets/development/sprockets/71f57313d6c92f5483915a6c2d79a506 +0 -0
  73. data/test/dummy/tmp/cache/assets/development/sprockets/7ec0041a47c34b44e52e836c01454a1a +0 -0
  74. data/test/dummy/tmp/cache/assets/development/sprockets/a1534e0c08b73ad82c2edb8c364caa66 +0 -0
  75. data/test/dummy/tmp/cache/assets/development/sprockets/a841a3af20a321912acfed87036438fb +0 -0
  76. data/test/dummy/tmp/cache/assets/development/sprockets/ad8eea7f774b674c29708ca90952764f +0 -0
  77. data/test/dummy/tmp/cache/assets/development/sprockets/b0741545b4917192ba7b5e803c2e323d +0 -0
  78. data/test/dummy/tmp/cache/assets/development/sprockets/b3159b06164dd474ff8efc3c84aefbba +0 -0
  79. data/test/dummy/tmp/cache/assets/development/sprockets/b40b7c5b2003544010f30f0bd3fb81b2 +0 -0
  80. data/test/dummy/tmp/cache/assets/development/sprockets/bbbe6a3ce382662666a355d708c83d2d +0 -0
  81. data/test/dummy/tmp/cache/assets/development/sprockets/c4e26d8dbebb3afd7013acfefa564dd1 +0 -0
  82. data/test/dummy/tmp/cache/assets/development/sprockets/ca53fb2717d5aac2f1c3939d9444fe3b +0 -0
  83. data/test/dummy/tmp/cache/assets/development/sprockets/d6b37d10680a997662c379d0ff7cad27 +0 -0
  84. data/test/dummy/tmp/cache/assets/development/sprockets/e1d89809967e81220dca66770c50aa67 +0 -0
  85. data/test/dummy/tmp/cache/assets/development/sprockets/e3d5cafc071e8f9a8efc88fddf721947 +0 -0
  86. data/test/dummy/tmp/cache/assets/development/sprockets/f44e2a41bd51a92a84f99847ea675ba3 +0 -0
  87. data/test/dummy/tmp/cache/assets/development/sprockets/f4a21ed9cebe2c83a9d988504dc6720b +0 -0
  88. data/test/dummy/tmp/cache/assets/development/sprockets/f4d45273ff5b44879dab0a16805f4309 +0 -0
  89. data/test/dummy/tmp/cache/assets/development/sprockets/f5deea0ae9671fdb035aefc6c4ba1109 +0 -0
  90. data/test/dummy/tmp/cache/assets/test/sprockets/0280bb38c5058cc31c4fcd8c392a5ec4 +0 -0
  91. data/test/dummy/tmp/cache/assets/test/sprockets/05bd7b24c0a86010ebb28b50ac7cad52 +0 -0
  92. data/test/dummy/tmp/cache/assets/test/sprockets/2a48be7daff14cf56911b263cbe017c7 +0 -0
  93. data/test/dummy/tmp/cache/assets/test/sprockets/3824a9e25e846a4916b3ac1d67060782 +0 -0
  94. data/test/dummy/tmp/cache/assets/test/sprockets/3a37adb5ebd079cf67dae597b8c2e4f8 +0 -0
  95. data/test/dummy/tmp/cache/assets/test/sprockets/4f79e5f341043e081becefe4952395c5 +0 -0
  96. data/test/dummy/tmp/cache/assets/test/sprockets/508ab3c25833ea537a5e3fc90df33595 +0 -0
  97. data/test/dummy/tmp/cache/assets/test/sprockets/5a8ab22e707dfc7ba00691f90e054d7e +0 -0
  98. data/test/dummy/tmp/cache/assets/test/sprockets/5adcb7569cdd03204d650e285f19351f +0 -0
  99. data/test/dummy/tmp/cache/assets/test/sprockets/632346eda030b596f513fff2de181743 +0 -0
  100. data/test/dummy/tmp/cache/assets/test/sprockets/654a1bde557d359b957dc0aa12b0dfa0 +0 -0
  101. data/test/dummy/tmp/cache/assets/test/sprockets/71f57313d6c92f5483915a6c2d79a506 +0 -0
  102. data/test/dummy/tmp/cache/assets/test/sprockets/9b9aee85f29dc573732fbb4001ceda00 +0 -0
  103. data/test/dummy/tmp/cache/assets/test/sprockets/a841a3af20a321912acfed87036438fb +0 -0
  104. data/test/dummy/tmp/cache/assets/test/sprockets/bbbe6a3ce382662666a355d708c83d2d +0 -0
  105. data/test/dummy/tmp/cache/assets/test/sprockets/ca53fb2717d5aac2f1c3939d9444fe3b +0 -0
  106. data/test/dummy/tmp/cache/assets/test/sprockets/d6b37d10680a997662c379d0ff7cad27 +0 -0
  107. data/test/dummy/tmp/cache/assets/test/sprockets/e1d89809967e81220dca66770c50aa67 +0 -0
  108. data/test/dummy/tmp/cache/assets/test/sprockets/e3d5cafc071e8f9a8efc88fddf721947 +0 -0
  109. data/test/dummy/tmp/cache/assets/test/sprockets/f44e2a41bd51a92a84f99847ea675ba3 +0 -0
  110. data/test/dummy/tmp/cache/assets/test/sprockets/f4a21ed9cebe2c83a9d988504dc6720b +0 -0
  111. data/test/dummy/tmp/cache/assets/test/sprockets/f4d45273ff5b44879dab0a16805f4309 +0 -0
  112. data/test/dummy/tmp/cache/assets/test/sprockets/f5deea0ae9671fdb035aefc6c4ba1109 +0 -0
  113. data/test/dummy/tmp/pids/server.pid +1 -0
  114. data/test/helpers/web_console/console_session_helper_test.rb +6 -0
  115. data/test/models/console_session_test.rb +110 -0
  116. data/test/test_helper.rb +15 -0
  117. data/test/web_console/repl/dummy_test.rb +54 -0
  118. data/test/web_console/repl/irb_test.rb +108 -0
  119. data/test/web_console/repl_test.rb +15 -0
  120. data/test/web_console_test.rb +91 -0
  121. data/vendor/assets/javascripts/jquery.console.js +727 -0
  122. metadata +303 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 852f83191e70586981bb3ead966b52a03a69d98e
4
+ data.tar.gz: cdaf295bc404be9d8c83d7749a06b81036349f52
5
+ SHA512:
6
+ metadata.gz: b8709b25d8e845212ccf470c10ac4ccbca756ebe7491dbf4a1e468c9a7b3ae4b9459ed6ae3c976fe1c02303124f47cd3008827bd5d264117680828966c576cae
7
+ data.tar.gz: 8707a5e85486c63ff7c7e79e23943333fb8c53d21b4549f2c7cb1b557600c9c85bd6ddf095c66eb84ecc081841373f73bfc72f6e186a006f3da2c2d46362cf76
@@ -0,0 +1,20 @@
1
+ Copyright 2013 Genadi Samokovarov and Guillermo Iguaran
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,121 @@
1
+ [![Travis](https://travis-ci.org/gsamokovarov/web-console.png)](https://travis-ci.org/gsamokovarov/web-console)
2
+
3
+ Web Console
4
+ ===========
5
+
6
+ There is no doubt that `rails console` is one of the most useful commands,
7
+ Rails has to offer. However, sometimes you can't easily access it, or maybe
8
+ you want to share it with a coworker without configuring remote desktop
9
+ server.
10
+
11
+ This is where _Web Console_ comes to the rescue. It gives you the same
12
+ `rails console` experience, right in the browser. It's not just a tool that
13
+ let's you evaluate Ruby code, there are a lot of those. It's your IRB session,
14
+ the way you configured it.
15
+
16
+ ![web-console-demo](http://f.cl.ly/items/1b2E2C052g1v1A233N0g/web-console-demo.png)
17
+
18
+ Requirements
19
+ ------------
20
+
21
+ To run _Web Console_ you need to be running _Rails 4_ and _MRI Ruby 1.9.3_ and
22
+ above. It may run on _Rubinius_ and _JRuby_, but we haven't tested those yet.
23
+
24
+ Installation
25
+ ------------
26
+
27
+ To install it in your current application, add the following to your `Gemfile`.
28
+
29
+ ```ruby
30
+ group :development do
31
+ gem 'web-console', '~> 0.1.0'
32
+ end
33
+ ```
34
+
35
+ After you save the `Gemfile` changes, make sure to run `bundle install` and
36
+ restart your server for the _Web Console_ to take affect.
37
+
38
+ By default, it should be available in your development environment under
39
+ `/console`. The route is not automatically mounted in a production environment
40
+ and we strongly encourage you to keep it that way.
41
+
42
+ Configuration
43
+ -------------
44
+
45
+ > Today we have learned in the agony of war that great power involves great
46
+ > responsibility.
47
+ >
48
+ > -- <cite>Franklin D. Roosevelt</cite>
49
+
50
+ _Web Console_ is a powerful tool. It allows you to execute arbitrary code on
51
+ the server, so you should be very careful, who you give access to it.
52
+
53
+ ### config.web_console.whitelisted_ips
54
+
55
+ By default, only requests coming from `127.0.0.1` are allowed.
56
+
57
+ `config.web_console.whitelisted_ips` lets you control which IP's have access to
58
+ the console.
59
+
60
+ Let's say you want to share your console with just that one roommate, you like
61
+ and his/her IP is `192.168.0.100`.
62
+
63
+ ```ruby
64
+ class Application < Rails::Application
65
+ config.web_console.whitelisted_ips = %w( 127.0.0.1 192.168.0.100 )
66
+ end
67
+ ```
68
+
69
+ From the example, you can guess that `config.web_console.whitelisted_ips`
70
+ accepts an array of ip addresses, provided as strings. An important thing to
71
+ note here is that, we won't push `127.0.0.1` if you manually set the option!
72
+
73
+ Now let's assume you like all of your roommates. Instead of enumerating their
74
+ IP's, you can whitelist the whole private network. Now every time their IP's
75
+ change, you'll have them covered.
76
+
77
+ ```ruby
78
+ class Application < Rails::Application
79
+ config.web_console.whitelisted_ips = '192.168.0.0/16'
80
+ end
81
+ ```
82
+
83
+ You can see that `config.web_console.whitelisted_ips` accepts plains strings
84
+ too. More than that, they can cover whole networks.
85
+
86
+ Again, note that this network doesn't allow `127.0.0.1`. If you want to access
87
+ the console, you have to do so from it's external IP or add `127.0.0.1` to the
88
+ mix.
89
+
90
+ ### config.web_console.default_mount_path
91
+
92
+ By default, the console will be mounted on `/console`.
93
+
94
+ _(This happens only in the development and test environments!)_.
95
+
96
+ Say you want to mount the console to `/debug`, so you can more easily remember
97
+ where to go, when your application needs debugging.
98
+
99
+ ```ruby
100
+ class Application < Rails::Application
101
+ config.web_console.default_mount_path = '/debug'
102
+ end
103
+ ```
104
+
105
+ Restart your server and you are done!
106
+
107
+ Test Drive
108
+ ----------
109
+
110
+ If you just want to try the web-console, without having to go through the
111
+ trouble of installing it, we provide a [Docker] container that does that for
112
+ you.
113
+
114
+ To try it, install [Docker], clone the project and run the following snippet in
115
+ the git root directory.
116
+
117
+ ```bash
118
+ docker build -t gsamokovarov/web-console . && docker run -i -t !#:3
119
+ ```
120
+
121
+ [Docker]: http://www.docker.io/
@@ -0,0 +1,48 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'socket'
8
+ require 'active_support/core_ext/string/strip'
9
+ require 'rake/testtask'
10
+
11
+ EXPANDED_CWD = File.expand_path(File.dirname(__FILE__))
12
+
13
+ Rake::TestTask.new(:test) do |t|
14
+ t.libs << 'lib'
15
+ t.libs << 'test'
16
+ t.pattern = 'test/**/*_test.rb'
17
+ t.verbose = false
18
+ end
19
+
20
+ # Just ignore this if rake is not runned from the current directory, as is the
21
+ # case with docker's container. BUNDLE_GEMFILE won't do for our case, since the
22
+ # Gemfile references gemspec.
23
+ Bundler::GemHelper.install_tasks if defined? Bundler::GemHelper
24
+
25
+ namespace :docker do
26
+ task :sync do
27
+ Dir.chdir(EXPANDED_CWD) do
28
+ sh "git fetch && git reset --hard origin/master", verbose: false
29
+ end
30
+ end
31
+
32
+ task run: :sync do
33
+ # Generally, the first ipv4 private address is how we would access the
34
+ # docker container from the current machine.
35
+ container_ip = Socket.ip_address_list.find(&:ipv4_private?).ip_address
36
+
37
+ puts <<-NOTICE.strip_heredoc
38
+ Go to http://#{container_ip}:3000/console to preview web-console.
39
+ If it's not showing, please give a few seconds for the server to start.
40
+ NOTICE
41
+
42
+ Dir.chdir("#{EXPANDED_CWD}/test/dummy") do
43
+ sh 'rails server', verbose: false
44
+ end
45
+ end
46
+ end
47
+
48
+ task default: :test
@@ -0,0 +1,4 @@
1
+ //= require jquery
2
+ //= require jquery_ujs
3
+ //= require jquery.console
4
+ //= require_tree .
@@ -0,0 +1,24 @@
1
+ $(function() {
2
+ var ERROR_CLASS = 'jquery-console-message-error';
3
+
4
+ var $console = $('#console');
5
+ var instance = $console.console({
6
+ autofocus: true,
7
+ promptLabel: $console.data('initial-prompt'),
8
+ commandHandle: function(line, report) {
9
+ $.ajax({
10
+ url: $console.data('remote-path'),
11
+ type: 'PUT',
12
+ dataType: 'json',
13
+ data: { input: line },
14
+ success: function(response) {
15
+ instance.promptLabel(response.prompt);
16
+ report([{msg: response.output}]);
17
+ },
18
+ error: function(xhr) {
19
+ report([{msg: xhr.responseJSON.error, className: ERROR_CLASS}]);
20
+ }
21
+ });
22
+ }
23
+ });
24
+ });
@@ -0,0 +1,13 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ *= require_tree .
13
+ */
@@ -0,0 +1,8 @@
1
+ body, div, textarea { margin: 0; padding 0 }
2
+
3
+ #console { font-family: Consolas, "Liberation Mono", Courier, monospace; font-size: 13px; position: fixed; width: 98%; height: 98%; left: 1%; top: 1% }
4
+ #console div.jquery-console-inner { height: 100%; background: #333; color: #fff; overflow: auto }
5
+ #console div.jquery-console-focus span.jquery-console-cursor { font-weight: bold; background: #fff }
6
+ #console div.jquery-console-message { white-space: pre-wrap }
7
+ #console div.jquery-console-message-error { font-weight: bold; color: #ff530d }
8
+ #console span.jquery-console-prompt-label { font-weight: bold }
@@ -0,0 +1,12 @@
1
+ module WebConsole
2
+ class ApplicationController < ActionController::Base
3
+ before_action :prevent_unauthorized_requests!
4
+
5
+ private
6
+ def prevent_unauthorized_requests!
7
+ unless request.remote_ip.in?(WebConsole::Engine.config.web_console.whitelisted_ips)
8
+ render nothing: true, status: :unauthorized
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,23 @@
1
+ require_dependency "web_console/application_controller"
2
+
3
+ module WebConsole
4
+ class ConsoleSessionsController < ApplicationController
5
+ rescue_from ConsoleSession::NotFound do |exception|
6
+ render json: exception, status: :gone
7
+ end
8
+
9
+ def index
10
+ @console_session = ConsoleSession.create
11
+ end
12
+
13
+ def update
14
+ @console_session = ConsoleSession.find(params[:id])
15
+ render json: @console_session.save(console_session_params)
16
+ end
17
+
18
+ private
19
+ def console_session_params
20
+ params.permit(:input)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,4 @@
1
+ module WebConsole
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module WebConsole
2
+ module ConsoleSessionHelper
3
+ end
4
+ end
@@ -0,0 +1,107 @@
1
+ module WebConsole
2
+ class ConsoleSession
3
+ include Mutex_m
4
+
5
+ include ActiveModel::Model
6
+ include ActiveModel::Serializers::JSON
7
+
8
+ # In-memory storage for the console sessions. Session preservation is
9
+ # troubled on servers with multiple workers and threads.
10
+ INMEMORY_STORAGE = {}
11
+
12
+ # Store and define the available attributes.
13
+ ATTRIBUTES = [ :id, :input, :output, :prompt ].each do |attr|
14
+ attr_accessor attr
15
+ end
16
+
17
+ # Raised when trying to find a session that is no longer in the in-memory
18
+ # session storage.
19
+ class NotFound < Exception
20
+ def to_json(*)
21
+ {error: message}.to_json
22
+ end
23
+ end
24
+
25
+ class << self
26
+ # Finds a session by its id.
27
+ #
28
+ # Raises WebConsole::ConsoleSession::Expired if there is no such session.
29
+ def find(id)
30
+ INMEMORY_STORAGE[id.to_i] or raise NotFound.new('Session unavailable')
31
+ end
32
+
33
+ # Creates an already persisted consolse session.
34
+ #
35
+ # Use this method if you need to persist a session, without providing it
36
+ # any input.
37
+ def create
38
+ INMEMORY_STORAGE[(model = new).id] = model
39
+ end
40
+ end
41
+
42
+ def initialize(attributes = {})
43
+ @repl = WebConsole::REPL.default.new
44
+
45
+ super
46
+ ensure_consequential_id!
47
+ populate_repl_attributes!(initial: true)
48
+ end
49
+
50
+ # Saves the model into the in-memory storage.
51
+ #
52
+ # Returns false if the model is not valid (e.g. its missing input).
53
+ def save(attributes = {})
54
+ self.attributes = attributes if attributes.present?
55
+ populate_repl_attributes!
56
+ store!
57
+ end
58
+
59
+ # Returns true if the current session is persisted in the in-memory storage.
60
+ def persisted?
61
+ self == INMEMORY_STORAGE[id]
62
+ end
63
+
64
+ # Returns an Enumerable of all key attributes if any is set, regardless if
65
+ # the object is persisted or not.
66
+ def to_key
67
+ super if persisted?
68
+ end
69
+
70
+ protected
71
+ # Returns a hash of the attributes and their values.
72
+ def attributes
73
+ return Hash[ATTRIBUTES.zip([nil])]
74
+ end
75
+
76
+ # Sets model attributes from a hash.
77
+ def attributes=(attributes)
78
+ attributes.each do |attr, value|
79
+ next unless ATTRIBUTES.include?(attr.to_sym)
80
+ public_send(:"#{attr}=", value)
81
+ end
82
+ end
83
+
84
+ private
85
+ def ensure_consequential_id!
86
+ synchronize do
87
+ self.id = begin
88
+ @@counter ||= 0
89
+ @@counter += 1
90
+ end
91
+ end
92
+ end
93
+
94
+ def populate_repl_attributes!(options = {})
95
+ synchronize do
96
+ # Don't send any input on the initial population so we don't bump up
97
+ # the numbers in the dynamic prompts.
98
+ self.output = @repl.send_input(input) unless options[:initial]
99
+ self.prompt = @repl.prompt
100
+ end
101
+ end
102
+
103
+ def store!
104
+ synchronize { INMEMORY_STORAGE[id] = self }
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>WebConsole</title>
5
+ <%= stylesheet_link_tag "web_console/application", media: "all" %>
6
+ <%= javascript_include_tag "web_console/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,4 @@
1
+ <div id="console"
2
+ data-remote-path="<%= web_console.console_session_path(@console_session) %>"
3
+ data-initial-prompt="<%= @console_session.prompt %>">
4
+ </div>
@@ -0,0 +1,5 @@
1
+ WebConsole::Engine.routes.draw do
2
+ root 'console_sessions#index'
3
+
4
+ resources :console_sessions
5
+ end
@@ -0,0 +1 @@
1
+ require 'web_console'