一直想做一个在线交互式编程的学习平台,最近因为教育创业的缘故,所以按捺不住内心的冲动,终于开始撸了~
(和我一样激动的小伙伴别激动,目前只是花了一上午搞的个 Demo 而已)
我用的是Rails 6.1 版本,ruby 2.7.4。
不知道有没有人一样和我发现在 Mac Big Sur 上安装Ruby 3.0+ 困难重重?
首先,请出好久不用的 rails new 命令:
$ rails new demo create create README.md create Rakefile create .ruby-version create config.ru create .gitignore create .gitattributes create Gemfile run git init from "." .... ├─ webpack-dev-middleware@5.0.0 ├─ webpack-dev-server@4.0.0 ├─ websocket-driver@0.7.4 ├─ websocket-extensions@0.1.4 └─ ws@8.2.0 Done in 4.98s. Webpacker successfully installed 🎉 🍰
建议新手别用
Rails 6, 我碰到过很多工作很多年的前端都玩不转前端那一套打包工具。。。。
再次吐槽一下,Rails 6 一点也不 Rails...
如果有必要可以再执行一下,默认会自动执行的:
$ rails webpack:install
随便生成一个scaffold
$ rails g scaffold casts title url [WARNING] The model name 'casts' was recognized as a plural, using the singular 'cast' instead. Override with --force-plural or setup custom inflection rules for this noun before running the generator. invoke active_record create db/migrate/20210823080552_create_casts.rb create app/models/cast.rb invoke test_unit create test/models/cast_test.rb create test/fixtures/casts.yml invoke resource_route route resources :casts invoke scaffold_controller create app/controllers/casts_controller.rb invoke erb create app/views/casts create app/views/casts/index.html.erb create app/views/casts/edit.html.erb create app/views/casts/show.html.erb create app/views/casts/new.html.erb create app/views/casts/_form.html.erb invoke resource_route invoke test_unit create test/controllers/casts_controller_test.rb create test/system/casts_test.rb invoke helper create app/helpers/casts_helper.rb invoke test_unit invoke jbuilder create app/views/casts/index.json.jbuilder create app/views/casts/show.json.jbuilder create app/views/casts/_cast.json.jbuilder invoke assets invoke scss create app/assets/stylesheets/casts.scss invoke scss identical app/assets/stylesheets/scaffolds.scss
然后运行数据迁移,创建数据库和表:
$ rails db:migrage == 20210823080552 CreateCasts: migrating ====================================== -- create_table(:casts) -> 0.0020s == 20210823080552 CreateCasts: migrated (0.0021s) =============================
======= 划重点啦 =======
引入xterm.js
$ yarn add xterm yarn add v1.22.10 [1/4] 🔍 Resolving packages... [2/4] 🚚 Fetching packages... [3/4] 🔗 Linking dependencies... [4/4] 🔨 Building fresh packages... success Saved lockfile. success Saved 1 new dependency. info Direct dependencies └─ xterm@4.13.0 info All dependencies └─ xterm@4.13.0 ✨ Done in 2.54s.
引入xterm-addon-attach
$ yarn add xterm-addon-attach yarn add v1.22.10 [1/4] 🔍 Resolving packages... [2/4] 🚚 Fetching packages... [3/4] 🔗 Linking dependencies... [4/4] 🔨 Building fresh packages... success Saved lockfile. success Saved 1 new dependency. info Direct dependencies └─ xterm-addon-attach@0.6.0 info All dependencies └─ xterm-addon-attach@0.6.0 ✨ Done in 2.47s.
在app/javascript/packs/application.js 加入:
require("xterm/css/xterm.css") // 偶个人觉得不太rails way... import { Terminal } from "xterm" import { AttachAddon } from 'xterm-addon-attach';
再在app/views/layouts/application.html.erb中添加以下一行代码:
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
完整的代码如下:
<!DOCTYPE html> <html> <head> <title>Demo</title> <meta name="viewport" content="width=device-width,initial-scale=1"> <%= csrf_meta_tags %> <%= csp_meta_tag %> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %> </head> <body> <%= yield %> </body> </html>
然后再在app/javascript/packs/application.js 加入:
document.addEventListener("turbolinks:load", () =>{ var term = new Terminal(); term.open(document.getElementById('terminal')); const socket = new WebSocket('ws://localhost:5020/containers/b6bb364f6d06/attach/ws?stream=1&stdout=1'); const attachAddon = new AttachAddon(socket); // Attach the socket to term term.loadAddon(attachAddon); term.write("Hello from \x1B[1;3;31mxterm.js\n\x1B[0m $ "); })
======== UPDATE:采用更通用的方法 ========
WebSocket的地址这样获得:
之前分享过一篇Caddy 相关的文章,为了让这个例子,更具有通用性,特修改一下,不用socat, 改用Caddy
首先安装 socat
$ brew install socat
其次运行以下命令:
$ socat TCP-LISTEN:5020,reuseaddr,fork UNIX-CLIENT:$HOME/Library/Containers/com.docker.docker/Data/docker.raw.sock
安装Caddy
$ brew install caddy
touch 一个 Caddyfile
$ touch Caddyfile
在 Caddyfile 文件中输入以下代码
:8080 { reverse_proxy unix//Users/i/Library/Containers/com.docker.docker/Data/docker.raw.sock }
WebSocket的地址中的b6bb364f6d06是 container 的 ID。
理论上来说,Docker 和 host 的共用的是
/var/run/docker.sock,但是 Mac 上读取不出数据,有知道的可以解释一下?
总之,这里走的唯一的弯路就是这个sock的地址。
然后我们再编辑一下app/views/casts/index.html.erb
在最下面添加一行:
<div id="terminal" style="width:800px;height:600px"></div>
重点是添加一个 id 是
terminal的 div
此时运行rails s
$ rails s
这时候我们还需要一个启动 docker 镜像,启动的命令里面要加入-i -t的参数,具体参数的意义最好参考一下官方文档。
这个对容器是有要求的,具体可以参考:https://docs.docker.com/engine/reference/commandline/attach/
到这里可以得出结论,xterm.js attach 到容器,最终能不能进行交互,和容器的 ENTRYPOINT/CMD 的命令有很大的关系。如果在创建容器时没指定 -it 或者 ENTRYPOINT/CMD 不是 bash 之类的可交互进程,xterm.js 虽然可以连接到容器,但都不可以与容器进行交互的。 而实际情况是,绝大多数的容器事务,这两个条件都没有。
我们这里贴出参考命令:
$ docker run --name test -d -it debian
参考文档:https://unihon.github.io/2019-05/use-xterm-js-attach-to-docker-container/
说明:这种教程,相比国外的同类教程来说已经写的非常简约了。我希望后续我基础类教程的分享甚至包括我解决小 bug 的过程。
大牛请绕道~
