rails-playground 0.1.0
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/MIT-LICENSE +20 -0
- data/README.md +67 -0
- data/Rakefile +8 -0
- data/app/assets/javascripts/playground/application.js +17 -0
- data/app/assets/javascripts/playground/controllers/codemirror_controller.js +49 -0
- data/app/assets/javascripts/playground/controllers/hello_controller.js +7 -0
- data/app/assets/javascripts/playground/controllers/playground_controller.js +12 -0
- data/app/assets/javascripts/playground/controllers/search_scripts_controller.js +20 -0
- data/app/assets/javascripts/playground/controllers/select_script_controller.js +8 -0
- data/app/assets/stylesheets/playground/application.scss +28 -0
- data/app/controllers/playground/application_controller.rb +4 -0
- data/app/controllers/playground/consoles_controller.rb +7 -0
- data/app/controllers/playground/scripts_controller.rb +49 -0
- data/app/helpers/playground/application_helper.rb +4 -0
- data/app/helpers/playground/consoles_helper.rb +4 -0
- data/app/helpers/playground/scripts_helper.rb +34 -0
- data/app/jobs/playground/application_job.rb +4 -0
- data/app/mailers/playground/application_mailer.rb +6 -0
- data/app/models/playground/application_record.rb +5 -0
- data/app/views/layouts/playground/application.html.erb +14 -0
- data/app/views/playground/consoles/show.html.erb +20 -0
- data/app/views/playground/scripts/_script.html.erb +5 -0
- data/app/views/playground/scripts/_script_select_box.html.erb +3 -0
- data/app/views/playground/scripts/index.html.erb +1 -0
- data/app/views/playground/scripts/index.turbo_stream.erb +1 -0
- data/app/views/playground/scripts/show.html.erb +1 -0
- data/app/views/playground/scripts/show.turbo_stream.erb +1 -0
- data/config/routes.rb +6 -0
- data/lib/playground/engine.rb +14 -0
- data/lib/playground/templates/index.html.erb +22 -0
- data/lib/playground/version.rb +3 -0
- data/lib/playground.rb +20 -0
- data/lib/tasks/playground_tasks.rake +4 -0
- data/vendor/javascripts/playground/application.js +41 -0
- data/vendor/stylesheets/playground/application.css +57 -0
- data/vendor/stylesheets/playground/application.css.map +1 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: df198165430aba4c303eda2c7ea181c913f58bfa8314b51ed3d456341746689a
|
4
|
+
data.tar.gz: e25a29c31228b830b9a40890e89e796741407287a9178f8cde7c266d32a1e817
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 04b3f859d616b122bce51a4c624693a6b3cd784f1a398b69b524845f83ec6d01171e5905f6589e2da021b4a006d7f40f955ba21ab03c7314d8c6a1c8ccaac3b6
|
7
|
+
data.tar.gz: 6408f5d0f1b941ae0912fc5404cce2d6722c795990ffeb6f3300722412862fa94f75dbeea2e935d5d3c22476883bb986448c0776adeb8a2461c8e4972dbdeed0
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2022 Alexandre Barret
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
# Playground
|
2
|
+
|
3
|
+
Back-and-forths from your editor and your rails console is a thing of the past!
|
4
|
+
|
5
|
+

|
6
|
+
|
7
|
+
Inspired by the Go playground and SQL Server Management Studio (SSMS) Query Editor, `playground` is a rails engine that makes it really easy to experiment with your application domain.
|
8
|
+
|
9
|
+
In addition to a web console, `playground` comes with a script management feature to help your personal and team development. It allows you to:
|
10
|
+
* use and save gitignored scripts for personal use.
|
11
|
+
* use and save version-controller scripts with your team.
|
12
|
+
|
13
|
+
This way, shared scripts stay close to the code and more maintainable while your personal script do not pollute the git history.
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
There are three ways to run ruby code in the console:
|
18
|
+
|
19
|
+
* Select your code in the editor and press cmd+Enter
|
20
|
+
* Put your cursor on any line (no selection) and press cmd+Enter
|
21
|
+
* Type your code straight in the terminal box just like in a rails console
|
22
|
+
|
23
|
+
## Installation
|
24
|
+
|
25
|
+
Playground is built on top of `web-console` gem. Just like `web-console`, `playground` is only meant to be used in development. Every `web-console` information still holds true for `playground`. Check `web-console` [repository for more information](https://github.com/rails/web-console)
|
26
|
+
|
27
|
+
|
28
|
+
You can require `playground` after `web-console` in your Gemfile if you already use it.
|
29
|
+
Add this line to your application's Gemfile:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
group :development do
|
33
|
+
gem "playground"
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
And then execute:
|
38
|
+
```bash
|
39
|
+
$ bundle
|
40
|
+
```
|
41
|
+
|
42
|
+
Then mount `playground` in your routes file and access the playground at `http://localhost:{PORT}/playground` on your local machine.
|
43
|
+
```ruby
|
44
|
+
Rails.application.routes.draw do
|
45
|
+
mount Playground::Engine => "/playground"
|
46
|
+
# ...
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
## Troubleshouting
|
51
|
+
|
52
|
+
### Session :id is no longer available in memory.
|
53
|
+
|
54
|
+
Example:
|
55
|
+
```
|
56
|
+
Session e96248aec85b3006488c276b9468a4d8 is no longer available in memory.
|
57
|
+
|
58
|
+
If you happen to run on a multi-process server (like Unicorn or Puma) the process
|
59
|
+
this request hit doesn't store e96248aec85b3006488c276b9468a4d8 in memory. Consider turning the number of
|
60
|
+
processes/workers to one (1) or using a different server in development.
|
61
|
+
```
|
62
|
+
|
63
|
+
This is a known `web-console` error which happened when you run a multi threaded server locally.
|
64
|
+
To solve this problem make sure to set the number of processes/worker to 1 on your local server.
|
65
|
+
|
66
|
+
## License
|
67
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
import "@hotwired/turbo-rails"
|
2
|
+
import "@rails/request.js"
|
3
|
+
import { Application } from "@hotwired/stimulus"
|
4
|
+
|
5
|
+
import HelloController from "./controllers/hello_controller"
|
6
|
+
import PlaygroundController from "./controllers/playground_controller"
|
7
|
+
import SearchScriptsController from "./controllers/search_scripts_controller"
|
8
|
+
import SelectScriptController from "./controllers/select_script_controller"
|
9
|
+
import CodemirrorController from "./controllers/codemirror_controller"
|
10
|
+
|
11
|
+
window.Stimulus = Application.start()
|
12
|
+
|
13
|
+
Stimulus.register("hello", HelloController)
|
14
|
+
Stimulus.register("playground", PlaygroundController)
|
15
|
+
Stimulus.register("search-scripts", SearchScriptsController)
|
16
|
+
Stimulus.register("select-script", SelectScriptController)
|
17
|
+
Stimulus.register("codemirror", CodemirrorController)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import { EditorView, keymap } from "@codemirror/view"
|
3
|
+
import { EditorState, Prec } from "@codemirror/state"
|
4
|
+
import { basicSetup } from "@codemirror/basic-setup"
|
5
|
+
import { indentWithTab } from "@codemirror/commands"
|
6
|
+
import { StreamLanguage } from "@codemirror/stream-parser"
|
7
|
+
import { ruby } from "@codemirror/legacy-modes/mode/ruby"
|
8
|
+
|
9
|
+
export default class extends Controller {
|
10
|
+
static values = { script: String }
|
11
|
+
|
12
|
+
connect() {
|
13
|
+
this.editor = new EditorView({
|
14
|
+
parent: this.element,
|
15
|
+
state: EditorState.create({
|
16
|
+
doc: this.scriptValue,
|
17
|
+
extensions: [
|
18
|
+
basicSetup,
|
19
|
+
StreamLanguage.define(ruby),
|
20
|
+
Prec.high(keymap.of([{key: "Mod-Enter", run: () => this.submitCodeSelected}])),
|
21
|
+
keymap.of([indentWithTab])
|
22
|
+
]
|
23
|
+
})
|
24
|
+
})
|
25
|
+
}
|
26
|
+
|
27
|
+
get submitCodeSelected() {
|
28
|
+
const event = new CustomEvent('codeSubmission', { detail: { selection: this.selectedBlock } });
|
29
|
+
this.element.dispatchEvent(event);
|
30
|
+
return true
|
31
|
+
}
|
32
|
+
|
33
|
+
get selection() {
|
34
|
+
return this.editor.state.selection.main
|
35
|
+
}
|
36
|
+
|
37
|
+
get doc() {
|
38
|
+
return this.editor.state.doc
|
39
|
+
}
|
40
|
+
|
41
|
+
get selectedBlock() {
|
42
|
+
const { from, to } = this.selection
|
43
|
+
if (from != to) {
|
44
|
+
return this.doc.toString().slice(from, to)
|
45
|
+
} else {
|
46
|
+
return this.doc.lineAt(from).text
|
47
|
+
}
|
48
|
+
}
|
49
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
|
3
|
+
export default class extends Controller {
|
4
|
+
connect() {
|
5
|
+
this.console = REPLConsole.installInto('console')
|
6
|
+
}
|
7
|
+
|
8
|
+
runCode(event) {
|
9
|
+
this.console.setInput(event.detail.selection)
|
10
|
+
this.console.onEnterKey()
|
11
|
+
}
|
12
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import { get } from "@rails/request.js"
|
3
|
+
|
4
|
+
export default class extends Controller {
|
5
|
+
static targets = ['input']
|
6
|
+
static values = { url: String }
|
7
|
+
|
8
|
+
debounce(callback, time) {
|
9
|
+
window.clearTimeout(this.debounceTimer);
|
10
|
+
this.debounceTimer = window.setTimeout(callback, time);
|
11
|
+
}
|
12
|
+
|
13
|
+
getScripts(event) {
|
14
|
+
let callback = () => get(this.urlValue, {
|
15
|
+
query: { name: event.target.value }
|
16
|
+
})
|
17
|
+
|
18
|
+
this.debounce(callback, 500)
|
19
|
+
}
|
20
|
+
}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
body#playground { max-width:900px; margin:auto; padding:0 1rem;
|
2
|
+
.console { margin-top:1rem;
|
3
|
+
position: relative; border: .5rem solid black;
|
4
|
+
.console-outer {
|
5
|
+
position:unset;
|
6
|
+
}
|
7
|
+
}
|
8
|
+
|
9
|
+
.console-actions { display: none;}
|
10
|
+
|
11
|
+
.code { flex-grow:1; height:550px; position: relative; }
|
12
|
+
.sidebar { margin-right:1rem;}
|
13
|
+
|
14
|
+
input, select, .console {
|
15
|
+
-webkit-box-sizing:border-box;
|
16
|
+
-moz-box-sizing:border-box;
|
17
|
+
box-sizing:border-box;
|
18
|
+
}
|
19
|
+
|
20
|
+
select { font-size:0.7em; padding:1rem; height:100%; max-height:500px; }
|
21
|
+
input { margin-bottom:0.5rem; padding:0.5rem;}
|
22
|
+
select, input { width:200px; }
|
23
|
+
|
24
|
+
[data-controller="codemirror"] { height:100%;
|
25
|
+
.cm-scroller { overflow: auto }
|
26
|
+
.cm-editor { position:absolute !important; top:0; right:0; bottom:0; left:0; }
|
27
|
+
}
|
28
|
+
}
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Playground
|
2
|
+
class ScriptsController < ApplicationController
|
3
|
+
def index
|
4
|
+
@scripts = script_paths
|
5
|
+
|
6
|
+
if params[:name]
|
7
|
+
@scripts = @scripts.select { |script| script.downcase.match? params[:name].downcase }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def show
|
12
|
+
@_script = File.read script_path(params[:name])
|
13
|
+
end
|
14
|
+
|
15
|
+
def new
|
16
|
+
@_script = <<~SCRIPT
|
17
|
+
# Access your application domain as if you were in a rails console.
|
18
|
+
# There are three ways to run code in the console:
|
19
|
+
|
20
|
+
# 1 - Select all the code from line 5 to 11 and press cmd+Enter
|
21
|
+
module HelloWorld
|
22
|
+
module_function
|
23
|
+
|
24
|
+
def hello
|
25
|
+
"Hello, World!"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# 2 - Put your cursor on line 14 (no selection) and press cmd+Enter
|
30
|
+
HelloWorld.hello
|
31
|
+
|
32
|
+
# 3 - Type "HelloWorld.hello" in the terminal box below and press cmd+Enter
|
33
|
+
SCRIPT
|
34
|
+
|
35
|
+
render :show
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def script_path(name)
|
41
|
+
script_paths.find { |path| path == name }
|
42
|
+
end
|
43
|
+
|
44
|
+
def script_paths
|
45
|
+
Dir[Rails.root.join("lib", "playground", "**", "*")]
|
46
|
+
.reject {|fn| File.directory?(fn) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Playground
|
2
|
+
module ScriptsHelper
|
3
|
+
MAX_SELECT_BOX_SIZE = 15
|
4
|
+
|
5
|
+
def script_size(scripts)
|
6
|
+
[MAX_SELECT_BOX_SIZE, scripts.length + 1].min
|
7
|
+
end
|
8
|
+
|
9
|
+
def script_options(scripts)
|
10
|
+
options = scripts
|
11
|
+
.map { |path| [script_label(path), url(path)] }
|
12
|
+
.unshift(["New script", new_script_path(format: :turbo_stream)])
|
13
|
+
|
14
|
+
options_for_select options
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def script_label(path)
|
20
|
+
Pathname(path)
|
21
|
+
.basename
|
22
|
+
.to_s
|
23
|
+
.truncate(30)
|
24
|
+
end
|
25
|
+
|
26
|
+
def script_pathname
|
27
|
+
Rails.root.join("lib").to_s
|
28
|
+
end
|
29
|
+
|
30
|
+
def url(path)
|
31
|
+
script_scripts_path(name: path, format: :turbo_stream)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>Playground</title>
|
5
|
+
<%= csrf_meta_tags %>
|
6
|
+
<%= csp_meta_tag %>
|
7
|
+
|
8
|
+
<%= javascript_include_tag "playground/application", skip_pipeline: true, nonce: true, media: "all" %>
|
9
|
+
<%= stylesheet_link_tag "/stylesheets/playground/application", media: "all" %>
|
10
|
+
</head>
|
11
|
+
<body id="playground">
|
12
|
+
<%= yield %>
|
13
|
+
</body>
|
14
|
+
</html>
|
@@ -0,0 +1,20 @@
|
|
1
|
+
<h1 data-controller="hello">Playground</h1>
|
2
|
+
|
3
|
+
<div style="display:flex;">
|
4
|
+
<div class="sidebar">
|
5
|
+
<div
|
6
|
+
data-controller="search-scripts"
|
7
|
+
data-search-scripts-url-value="<%= scripts_path(format: :turbo_stream) %>">
|
8
|
+
<%= text_field_tag :search, "",
|
9
|
+
placeholder: 'search for scripts',
|
10
|
+
data: { action: 'input->search-scripts#getScripts' }
|
11
|
+
%>
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<%= turbo_frame_tag :scripts, src: scripts_path(format: :turbo_stream) %>
|
15
|
+
</div>
|
16
|
+
|
17
|
+
<div class="code" data-controller="playground">
|
18
|
+
<%= turbo_frame_tag :script, src: new_script_path(format: :turbo_stream) %>
|
19
|
+
</div>
|
20
|
+
</div>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render partial: 'script_select_box', locals: { scripts: @scripts } %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= turbo_stream.update :scripts, partial: 'script_select_box', locals: { scripts: @scripts } %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= render partial: 'script', locals: { script: @_script } %>
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= turbo_stream.update :script, partial: 'script', locals: { script: @_script } %>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Playground
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace Playground
|
4
|
+
|
5
|
+
initializer "playground.web_console_templates" do |app|
|
6
|
+
template_paths = File.expand_path("../templates", __FILE__)
|
7
|
+
WebConsole::Template.template_paths.unshift(*Array(template_paths))
|
8
|
+
end
|
9
|
+
|
10
|
+
initializer "playground.assets" do |app|
|
11
|
+
app.middleware.insert_before(::ActionDispatch::Static, ::ActionDispatch::Static, "#{root}/vendor")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<% if playground_page? %>
|
2
|
+
|
3
|
+
<%= render 'markup' %>
|
4
|
+
|
5
|
+
<%= render_javascript 'console' %>
|
6
|
+
|
7
|
+
<% else %>
|
8
|
+
|
9
|
+
<%= render 'markup' %>
|
10
|
+
|
11
|
+
<%= render_javascript 'console' %>
|
12
|
+
<%= render_javascript 'main' %>
|
13
|
+
|
14
|
+
<% only_on_error_page do %>
|
15
|
+
<%= render_javascript 'error_page' %>
|
16
|
+
<% end %>
|
17
|
+
|
18
|
+
<% only_on_regular_page do %>
|
19
|
+
<%= render_javascript 'regular_page' %>
|
20
|
+
<% end %>
|
21
|
+
|
22
|
+
<% end %>
|
data/lib/playground.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "web-console"
|
2
|
+
|
3
|
+
WebConsole::View.class_eval do
|
4
|
+
def playground_page?
|
5
|
+
@playground_page ||= begin
|
6
|
+
# {:controller=>"playground/consoles", :action=>"show"}
|
7
|
+
path_parameters = @env["action_dispatch.request.path_parameters"]
|
8
|
+
path_parameters[:controller] == "playground/consoles" &&
|
9
|
+
path_parameters[:action] == "show"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
require "turbo-rails"
|
15
|
+
require "playground/version"
|
16
|
+
require "playground/engine"
|
17
|
+
|
18
|
+
module Playground
|
19
|
+
|
20
|
+
end
|