概要
Railsアプリを作っている際、特定のコマンドを知っている人だけが辿り着けるページみたいなものがあれば面白いなと思い調べてみたところ、JavaScriptを上手く使えば実装できる事がわかったので試してみました。
実運用できるレベルかどうかは別として完全に遊び心みたいな感じです。(URLを直打ちすれば普通に飛べちゃうみたいな野暮なツッコミは無しでお願いします(笑))
完成イメージ
今回はかの有名な コナミコマンド (上上下下右左右左BA)を入力した場合、隠しページにつながる導線をアラートメッセージで表示するような仕組みにしてみました。
仕様
- Ruby3
- Rails6
- MySQL8
- Docker
再現性を考慮しDockerで環境構築を行っていきます。
実装
前置きはそこそこに実装に入ります。
ディレクトリ&各種ファイルを作成
$ mkdir rails-secret-command && cd rails-secret-command
$ touch Dockerfile
$ touch docker-compose.yml
$ touch entrypoint.sh
$ touch Gemfile
$ touch Gemfile.lock
FROM ruby:3.0
RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - \
&& echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs yarn
ENV APP_PATH /myapp
RUN mkdir $APP_PATH
WORKDIR $APP_PATH
COPY Gemfile $APP_PATH/Gemfile
COPY Gemfile.lock $APP_PATH/Gemfile.lock
RUN bundle install
COPY . $APP_PATH
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000
CMD ["rails", "server", "-b", "0.0.0.0"]
version: "3"
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: password
command: --default-authentication-plugin=mysql_native_password
volumes:
- mysql-data:/var/lib/mysql
- /tmp/dockerdir:/etc/mysql/conf.d/
ports:
- 3306:3306
web:
build:
context: .
dockerfile: Dockerfile
command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
volumes:
- .:/myapp
- ./vendor/bundle:/myapp/vendor/bundle
environment:
TZ: Asia/Tokyo
RAILS_ENV: development
ports:
- 3000:3000
depends_on:
- db
volumes:
mysql-data:
# !/bin/bash
set -e
# Remove a potentially pre-existing server.pid for Rails.
rm -f /myapp/tmp/pids/server.pid
# Then exec the container's main process (what's set as CMD in the Dockerfile).
exec "$@"
# frozen_string_literal: true
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
gem "rails", "~> 6"
# 空欄でOK
rails new
おなじみのコマンドでプロジェクトを作成します。
$ docker-compose run web rails new . --force --no-deps -d mysql
Gemfileが更新されたので再ビルド。
$ docker-compose build
「.config/database.yml」を編集
default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password: password # デフォルトだと空欄になっているはずなので変更
host: db # デフォルトだとlocalhostになっているはずなので変更
development:
<<: *default
database: myapp_development
test:
<<: *default
database: myapp_test
production:
<<: *default
database: <%= ENV["DATABASE_NAME"] %>
username: <%= ENV["DATABASE_USERNAME"] %>
password: <%= ENV["DATABASE_PASSWORD"] %>
host: <%= ENV["DATABASE_HOST"] %>
データベースを作成
$ docker-compose run web rails db:create
動作確認
$ docker-compose up -d
localhost:3000 にアクセスしていつもの画面が表示されればOK。
トップページを作成
$ docker-compose run web rails g controller home index
コントローラー
class HomeController < ApplicationController
def index
end
end
ビュー
<%= javascript_pack_tag "home/index" %>
<h1>次のコマンドを入力してください</h1>
<p>上上下下右左右左BA</p>
ルーティング
Rails.application.routes.draw do
root "home#index" # 追記
end
隠しページを作成
$ docker-compose run web rails g controller secret_pages index
コントローラー
class SecretPagesController < ApplicationController
def index
end
end
ビュー
<h1>ここは隠しページです</h1>
ルーティング
Rails.application.routes.draw do
root "home#index"
resources :secret_pages, only: %i[index] # 追記
end
隠しコマンドを実装
Rails6でJavaScriptを使用する場合、「app/javascript/packs/」以下にjsファイルを配置する必要があるのでちゃちゃっと作成します。
$ mkdir app/javascript/packs/home
$ touch app/javascript/packs/home/index.js
var secretCommands = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65] // 隠しコマンド
var inputtedCommands = [] // 入力されたコマンド
document.onkeydown = function(e) {
inputtedCommands.push(e.keyCode)
// 入力されたコマンドの順番と値が隠しコマンドと全一致した場合はアラートメッセージを表示
if (inputtedCommands[inputtedCommands.length - 1] === secretCommands[inputtedCommands.length - 1]) {
if (inputtedCommands.length === secretCommands.length) {
if (window.confirm("隠しページへ飛びますか?")) {
location.href = "http://localhost:3000/secret_pages" // 隠しページへ飛ぶ
} else {
window.alert("キャンセルしました") // キャンセル時の処理
}
inputtedCommands.length = 0
}
} else {
inputtedCommands.length = 0 // 少しでも違った場合はリセット
}
console.log(inputtedCommands)
}
JavaScriptには
- keydown(キーが押された瞬間)
- keypress(キーが押されて文字が入力された瞬間)
- keyup(キーを放した瞬間)
といったキーボードの操作を検知して発火するイベントがありますが、今回はキーが押された瞬間を検知したいので「keydown」を採用しました。
また、キーボードにはそれぞれのキーに「キーコード」という数字が割り当てられています。
参照: キーコード一覧
そのルールに従うと「上上下下右左右左BA」は「38, 38, 40, 40, 37, 39, 37, 39, 66, 65」に変換できるため、コンピューターに対してはコイツが隠しコマンドであると認識させます。
動作確認
あとがき
以上、Railsアプリに隠しコマンド・隠しページを実装してみました。
実用性という点ではちょっと不明ですが、こういう面白い仕掛けを組み込む事もできるという内容です。
今回作成したコードの完成品を記載しておくので、もし記事通りに進めて動かない部分などはそちらと照らし合わせてみてください。