まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2021/02

IBM Cloud から提供されている NoSQL のマネージドデータベースである Cloudant (実体は Apache CouchDB)を便利に使っています。最大の特徴の一つが「無料でも一定条件下で使える」点ですが、今回は無料である以外の便利な使い方を紹介します。

この記事の中で紹介するのはこのような使いかたです:
(1) Cloudant の CORS を有効に設定する(特定ドメインのページからの REST API リクエストを受け付けて読み書きできるようにする)
(2) Github ページに用意したウェブページから (1) で有効に設定した Cloudant のデータを読み書きする


2021022401


最終的に実現したいのは、世の中の SaaS(といっていいのかな)の中でもかなり可用性高く、安定して動いている印象の Github ページを使ったウェブアプリケーションを作ることです。ただ静的ページそのものは安定して稼働していてもデータの読み書きが出来なければウェブアプリケーションとしては不十分で、そこを REST API で読み書きできる IBM Cloud の Cloudant データベースで賄えないか、と考えました。ただそのままでは CORS の制約もあって API が実行できないのですが、Cloudant 側の設定で CORS を正しく設定して Github ページからの読み書きを可能な状態にする、そのための Cloudant の設定手順を確認する、というのが目標です。

以下では Github のアカウントを持っている方を対象に、Github ページを使って作成するウェブページから Cloudant にアクセスするサンプルを紹介します。Github ページでなくても(静的ページのホスティング環境があれば)同様に可能なはずですが、その場合は適宜読み直してください(特に CORS 設定時のドメインなど)。


【CORS】
"Cross-Origin Resource Sharing" 、つまり「オリジン間リソース共有」の略語です。ここでの「オリジン」は「ドメイン」と読み替えたほうが理解しやすいかもしれまえん。

例えば A というウェブサーバーが https://aaa.com/ 上で、また B というデータベース・サーバーが https://bbb.net/ 上で動いていると仮定します(つまり全く別々に管理されている2つのサーバーです。この状態を Cross-Origin と呼びます)。A 上のウェブページで表示される画面の中で B から取得したデータを表示しようと思っています。

例えば A が Java などのプログラミング言語によって作られたアプリケーションサーバーであった場合、A は B からのデータ取得に Java を使うことになります。A の中で Java が実行され、B にアクセスしてデータを取り出し、その結果を使って A のページの画面を作成することができます。A の内部でプログラミング言語が使われている場合は、Cross-Origin であってもこのような方法でデータを取得することができます。

ところが A がアプリケーションサーバーではなく、ただの(既存の HTML ページを表示するだけの)ウェブサーバーであった場合、A から B にアクセスしてデータを取得する手段はかなり限られてしまいます。その限られた手段の1つが AJAX などに代表される JavaScript 処理です。A のサーバーにプログラミング言語が用意されていなくても、ウェブブラウザ自体が持つ JavaScript 実行環境を使って B サーバーにアクセスしてデータを取得する、という考え方です。ただし Cross-Origin の場合、A にアクセスしたウェブブラウザから B のサーバーに JavaScript で HTTP アクセスすることは原則できないことになっています(Cross-Origin でなければ、つまり A = B であれば可能です)。これが CORS の制約です。

CORS の制約はウェブブラウザが持っている制約なので、ウェブブラウザを使わない HTTP 通信(上述の Java 言語によるプログラミングによる通信など)には関係ありませんが、(ウェブブラウザの)利用者側でこの制約を解除する方法はありません。唯一の方法がリソース提供側(この場合だと B サーバー)による許可のみです。

今回のブログエントリは IBM Cloud のデータベースの1つである Cloudant の CORS をうまく設定することで、代表的な静的ウェブページの1つである Github ページから(Cloudant 内の)外部データを読み書きする方法について紹介する、というものです。


【Cloudant の用意】
まず IBM Cloud のアカウントを取得します。IBM Cloud アカウントを新規に取得した時点では「ライトアカウント」と呼ばれる無料の制約のあるアカウントとなります。今回紹介する作業を実行するだけであれば(ライトアカウントだとデータは 1GB までの格納となりますが、この容量などに問題なければ)ライトアカウントのまま実行いただいても構いません。

改めて取得したアカウントで IBM Cloud にログインし、「リソースの作成」ボタンクリック後に「Cloudant」を選択します:
2021022501

2021022502


インスタンス作成前にいくつか設定箇所があります。主なものは以下になりますが、"Authentification Method" だけはデフォルトの "IAM" ではなく "IAM and legacy credentials" を選択してください:
・Available Regions: インスタンスを作成するエリア、お好きな場所でいいが「東京」あたりが無難
・Authentification Method: 今回は必ず "IAM and legacy credentials" を選択
・Plan: 料金プラン。「Lite」であれば無料

最後に "Create" ボタンで作成します:
2021022503


Authentification Method について補足しておくと、デフォルトの "IAM" だといわゆる API キーを使った認証/認可を行います。ただ今回はアプリケーションサーバーからではなく(GitHub ページの)静的なページから Basic 認証を使ってデータベースの REST API を使うことを想定しています。この場合は "IAM" ではなく "IAM and legacy credentials" (API キーまたは Basic 認証を使う)を選択しておく必要があるためです。


少し待つと Cloudant インスタンスが起動済みになります。インスタンスを開いて「サービス資格情報」タブを選び、「新規資格情報」ボタンを押して資格情報を作成します。作成後に作成した資格情報(「サービス資格情報-1」のような名前になっていると思います)を選択・展開して、中身を確認します:
2021022504


資格情報の中身は以下のような内容の JSON 文字列になっているはずです(この中身は他人に見せないように注意してください):
{
  "apikey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "host": "xxxxxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud",
  "iam_apikey_description": "Auto-generated for key xxxxxxxxxxxxxxxxxxx",
  "iam_apikey_name": "サービス資格情報-1",
  "iam_role_crn": "crn:v1:bluemix:public:iam::::serviceRole:Manager",
  "iam_serviceid_crn": "crn:v1:bluemix:public:iam-identity::a/xxxxxxxxxxxxxxxxxxxx::serviceid:ServiceId-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
  "password": "パスワード",
  "port": 443,
  "url": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud",
  "username": "ユーザー名"
}

この情報のうち、後で必要になるのが以下の(赤字の)4つです。メモするか、コピペできるようにしておいてください:
キー意味補足
hostnameホスト名*******-bluemix.cloudantnosqldb.appdomain.cloud といった感じの文字列になっています(******* 部分が可変)
portポート番号443 で固定のはずです
usernameユーザー名ログイン時にこの2つの値が必要です
passwordパスワード


認証情報を確認したら、実際に Cloudant データベースのダッシュボードを見てみましょう。画面左のメニューから「管理」タブを選択し、右上の「Launch Dashboard」ボタンをクリックします:
2021022505


(初期状態では空の)ダッシュボード画面が表示されます。最初にアクセスした時点ではまだ何のデータベースも作成されていないため、下図のような画面になります。後述の作業のためのデータベースをここで1つ作成しておくことにします。画面右上の「Create Database」ボタンをクリックします:
2021022506


データベースを作成するメニューが画面右に表示されます。ここでは Database name に「mydb(異なる名前を指定しても構いませんが、その場合は後述のコマンド等を作成した名前に読み替えて実行してください)」、Partitioning は「Non-partitioned」を選択し、「Create」ボタンをクリックします:
2021022507


作成した mydb データベースが出来上がりました。このデータベースもまだデータが1件もないので、このような画面になります。一旦元の画面に戻るため、画面左上の "<" マークをクリックします:
2021022508


一つ前のデータベース一覧画面に戻りました。先程は空でしたが、作成したデータベースが1つ追加された画面になっているはずです:
2021022509


Cloudant 側の準備はいったんここまでできていればOKとします。 次に CORS のための設定を行います。


【Cloudant に CORS を設定する】
次に Cloudant に CORS の設定をして外部からのアクセスを許可します。ダッシュボードのメニューから「アカウント」(下から3番め)を選び、「CORS」タブを選択します。以下のような画面(「CORS は有効になっていて、外部のどこからのリクエストも受け付けない」という内容)になるはずです:
2021022501


画面下部の「Restrict to specific domains」欄にアクセスを許可するドメインの URL を指定して「Add Domain」ボタンで追加します。ここでは "https://(github のアカウント名).github.io/" を指定することで自分の Github ページからのアクセスを許可することになるので、自分の github アカウントに合わせて設定してください:
2021022502


このような画面になれば自分の github ページ用の CORS 設定ができたことになり、自分の github ページからは JavaScript で Cloudant データを読み書きできることになります:
2021022503


【Cloudant に初期データを入力する】
今回の紹介では Github ページから Cloudant のデータを REST API 経由で表示する(CORS の設定をしていれば取得できるはず)、ということを試してみます。そのための「表示するデータ」をあらかじめ Cloudant に入力しておきます。

もちろん(ダッシュボードから)自分で好きにデータを入力いただいても構いませんが、比較的簡単に動作を確認できるサンプルを用意しており、そこで対象とするデータを curl 一発でまとめて入力できるように準備しているのでこちらの方法を紹介します。

まず以下の URL からサンプルデータをダウンロード(表示後に右クリック→名前を付けて保存で "prefs.json" という名前で保存)してください:
https://raw.githubusercontent.com/dotnsf/cloudant_cors/main/prefs.json

2021022701


このファイルの中身は以下のようなフォーマットになっており、各都道府県庁所在地と、その位置情報が格納されています:
{
  "docs": [
    { "code": 1, "prefecture": "北海道", "capital": "札幌市", "geometry": { "type": "Point", "coordinates": [ 141.34694, 43.06417 ] } },
    { "code": 2, "prefecture": "青森県", "capital": "青森市", "geometry": { "type": "Point", "coordinates": [ 140.74, 40.82444 ] } },
    { "code": 3, "prefecture": "岩手県", "capital": "盛岡市", "geometry": { "type": "Point", "coordinates": [ 141.1525, 39.70361 ] } },
    { "code": 4, "prefecture": "宮城県", "capital": "仙台市", "geometry": { "type": "Point", "coordinates": [ 140.87194, 38.26889 ] } },
    { "code": 5, "prefecture": "秋田県", "capital": "秋田市", "geometry": { "type": "Point", "coordinates": [ 140.1025, 39.71861 ] } },
    { "code": 6, "prefecture": "山形県", "capital": "山形市", "geometry": { "type": "Point", "coordinates": [ 140.36333, 38.24056 ] } },
    { "code": 7, "prefecture": "福島県", "capital": "福島市", "geometry": { "type": "Point", "coordinates": [ 140.46778, 37.75 ] } },
    { "code": 8, "prefecture": "茨城県", "capital": "水戸市", "geometry": { "type": "Point", "coordinates": [ 140.44667, 36.34139 ] } },
    { "code": 9, "prefecture": "栃木県", "capital": "宇都宮市", "geometry": { "type": "Point", "coordinates": [ 139.88361, 36.56583 ] } },
    { "code": 10, "prefecture": "群馬県", "capital": "前橋市", "geometry": { "type": "Point", "coordinates": [ 139.06083, 36.39111 ] } },
    { "code": 11, "prefecture": "埼玉県", "capital": "さいたま市", "geometry": { "type": "Point", "coordinates": [ 139.64889, 35.85694 ] } },
    { "code": 12, "prefecture": "千葉県", "capital": "千葉市", "geometry": { "type": "Point", "coordinates": [ 140.12333, 35.60472 ] } },
    { "code": 13, "prefecture": "東京都", "capital": "新宿区", "geometry": { "type": "Point", "coordinates": [ 139.69167, 35.68944 ] } },
    { "code": 14, "prefecture": "神奈川県", "capital": "横浜市", "geometry": { "type": "Point", "coordinates": [ 139.6425, 35.44778 ] } },
      :
      :
  ]
}

このデータを先程作成した Cloudant 内の mydb データベースに格納します。curl コマンドを使うと以下のコマンド(※)で格納できます:
$ curl -u "username:password" -XPOST "https://host/mydb/_bulk_docs" -H "Content-Type: application/json" -d @prefs.json

username, password, host の値は上述で確認した以下の値を指定して入力してください:
 username: 上述の資格情報で確認した username の値
 password: 上述の資格情報で確認した password の値
 host: 上述の資格情報で確認した host の値


このコマンドが成功すると Cloudant の mydb データベースに47都道府県のデータが挿入されます。ダッシュボードから mydb データベースを選択すると以下のように 47 件のデータが表示されるようになります:
2021022702


試しにどれか1つ選択してみると、個々の内容を確認することも可能です(下図は北海道の例、"Cancel" で一覧に戻ります):
2021022703


ここまでの作業で Cloudant に Github ページからアクセスするための CORS を設定し、表示用のデータも格納できました。ではこのデータを Github ページから REST API 経由で取得して表示してみましょう。


【Github ページを用意する】
では実際に Github ページからクロスオリジンな Cloudant データベースに REST API 経由でアクセスできることを確認してみます。

まず Github にログインし、その後で以下のページにアクセスし、画面右上の "Fork" をクリックしてください:
https://github.com/dotnsf/cloudant_cors

2021022706


Fork 処理が成功すると、
 https://github.com/(あなたの github アカウント名)/cloudant_cors
というリポジトリができるはずです。

このリポジトリの Github ページを有効にします。リポジトリページの右上にある "Settings" をクリックします:
2021022707


画面を下に "GitHub Pages" と書かれている箇所までスクロールします。"Source" に "main" ブランチを指定して保存すると、"main" ブランチの内容がそのまま Github ページとして公開され、この中にある CORS の動作確認用に作った index.html を参照するための URL(https://(あなたの github アカウント名).github.io/cloudant_cors/)とそのリンクが表示されます(リンク先が有効になるまで1分程度かかります):
2021022708


少し待ってからリンク先にアクセスします。以下のような画面が表示されるはずです(Github ページなので、****.github.io というドメインのページになっていることを再確認してください):
2021022704


改めて資格情報から取得した値を画面内の各該当箇所に入力します。上にある横幅の大きなフィールドには host の値、その下は左から作成した DB 名(mydb)、username、password の値を入力します。最後に "Refresh" ボタンを押すと入力された情報を使ってこのページから AJAX で Cloudant へ REST API を実行して文書一覧を取得し、表形式で出力します。成功すると以下のように47都道府県の情報が(クロスオリジンの制約を乗り越えて)表示されます:
2021022705


以上、正しく設定することでクロスオリジンの壁を超えて Github ページから Cloudant のデータを利用することができました。Github も Cloudant も(一定制限の中で)無料で利用できるので、安定したウェブページを Github で運用しつつ、表示データを Cloudant から提供する、といった形で利用することができそうです。


なお、Cloudant(CouchDB) の REST API についての詳しくは、こちらのリファレンスを参照してください:
https://docs.couchdb.org/en/stable/api/index.html


なお、今回使った「特定データベース内の全文書を取得する」 REST API はこちらです:
GET /{db}/_all_docs

また準備段階で都道府県データをバルクインサートしましたが、その REST API はこちらで紹介されています:
POST /{db}/_bulk_docs


ある意味、先日のこの記事の続きです:
「チームでアプリケーション開発を体験したい」、どうやる?


1つの案として(候補案4の)「オンラインエディタを使う」方法を紹介しました。ここで紹介したオープンソースのオンラインエディタ Eclipse Orion を Linux サーバーに、特に docker 環境下で簡単に導入する方法を紹介します。なお、ここでの「 docker 環境下」は正確には「x86_64 チップの docker 環境下」とさせてください(後述する docker イメージが linux/amd64 アーキテクチャ向けのため)。


まずは docker 環境を用意します。既に手元にあれば飛ばしていただいて構いませんが、こちらを参考いただくなどして環境にあった方法で docker エンジンが起動している状態にしておいてください:
Docker のインストール

今回紹介する方法ではこちらの Eclipse Orion 用 docker イメージを使わせていただきます:
https://hub.docker.com/r/cloudeity/orion


早速 docker pull して、・・・の前に、Eclipse Orion が参照する対象となるフォルダを自分の手元に用意しておきます。フォルダが空の状態から始めるのであれば空のフォルダを用意すればいいのですが、サンプルファイルが含まれた状態で始めるのであれば、対象ファイルがコピーされた状態のフォルダを用意しておく必要があります。

今回は以下のような index.html ファイル1つだけが用意されたフォルダを /tmp/web/ 以下に準備することにします(別のフォルダでも構いませんが、以下の内容を読み替えてください)。まず /tmp/web というフォルダを作ります(後でこのフォルダを Eclipse Orion の作業フォルダとします):
$ mkdir -p /tmp/web


そして以下の内容の index.html を作って、/tmp/web/ フォルダにコピーしておきます(/tmp/web/index.html ファイルを作ります):
<html>
Hello World.
</html>

改めて docker を使って Eclipse Orion をインストールします。まずは docker pull でイメージをダウンロードしておきます(初回のみ):
$ docker pull cloudeity/orion

次にコンテナ化して起動するのですが、その際に -v オプションで作業フォルダをボリューム指定します。今回のように /tmp/web を作業フォルダとする場合は以下のコマンドを実行します:
$ docker run -d --name orion -v /tmp/web:/opt/orion.client/modules/orionode/.workspace -p 8081:8081 cloudeity/orion

コマンドの実行に成功したらウェブブラウザで 8081 番ポートにアクセスします。成功していると /tmp/web フォルダがプロジェクトフォルダとなって、ウェブブラウザ画面から既存の index.html ファイルを編集したり、新規にフォルダやファイルを作成したり、編集したりができるようになります:

(同じマシンから http://localhost:8081 にアクセスした時の画面。他マシンからアクセスする場合は localhost 部分を IP アドレス指定にします)
2021022301

(index.html ファイルを選択するとエディタが開き、直接編集できます)
2021022302


Eclipse Orion 自体を終了するには docker コンテナを止めます:
$ docker stop orion




このたび、ご縁あって浜名湖競艇のとある単レースの冠スポンサーとなる機会をいただきました:



開催日時: 令和日(水)
開催場所: 浜名湖競艇 第
レース名: .nsf@dominoforever記念


リリースから今年で33年目を迎えたグループウェア「ノーツ・ドミノ」。管理会社も社目となり、なんとなくに縁がありそうですが、信じるか信じないかはあなた次第。。


boatrace



に縁のありそうな日に、同じくに縁のありそうなグループウェア「ノーツ・ドミノ」の記念レースを開催させていただくことになりました。

僕を個人的にご存知でない方のために補足しておくと、gmail やツイッター、ポケモン Go などの各種アカウントで "dotnsf" を使っています。これは ".nsf" のことで、もともとは「ノーツ・ドミノ」システムで使うデータベースファイルの拡張子(.nsf)を由来にしています。もともとはロータス株式会社で Lotus 1-2-3 やノーツを含めたソフトウェア製品の開発を担当しており、その頃から自分の製品には愛着があって今に至ります。そんなノーツ・ドミノは 1989 年(平成元年!)のバージョン1リリースから今年で 33 年目を迎えました(余談ですが、これだけ長く業務に使われているアプリケーションソフトって他に一太郎くらいしか思いつきません)。管理会社は Lotus 、IBM を経て、現在は社目となる HCL に移っています。こういった背景&経緯&愛着もあって、今回この滅多にない機会でのレースのネーミングライツに応募し、当選させていただきました。

※名前の "dominoforever" 部分はコミュニティのハッシュタグとしてよく使われるものですが、・・詳しくはググってみてください(笑)

dotnsf_logo_200x200


この個人スポンサーの冠レース、売上の利益はスポンサーには入りません。みなさんが賭けて外れてもそのぶん僕の懐が潤ったりはしませんので(苦笑)、興味ありましたら(今回、このような機会をいただいた浜名湖競艇様を応援する意味で)視聴したり、舟券を購入してみたりいただけると嬉しいです。

#ちなみに僕は当日は抜けられないお仕事があるので、結果を楽しみにしております。 d(ToT)



ところで今回、この冠レースに個人で申し込みを行って知ったのですが、このような公営競技に個人が冠レーススポンサーになることができるレースがいくつかあります(企業スポンサーほどは多くありません)。またスポンサーになった上での特典もまちまちです。今回の浜名湖競艇様はそんな個人スポンサーを募っている数少ない公営競技の1つです。どんな特典があるのか、といったことも含めて興味を持った方はこちらを参照ください(ここから協賛を申し込むこともできます):
個人向け応募概要

※2021 年2月申し込み時点では新型コロナウィルス感染拡大防止のため、無観客開催が行われており、花束プレゼンターとしての表彰式参加ができません。残念とも言えるし、それで抽選のライバルが減るのはラッキーとも言えるような・・・

この浜名湖競艇の場合は「レース名が20文字以内」、「PR文書が80文字以内」という制限が結構厳しく、色々考えた末にこのようなレース名&PR文書となりました(僕のはどちらもギリギリです)。


「複数人でプログラミングを体験する」ための環境を作ることになった場合、その環境をどう実現すればいいかを考えました。
computer_mob_programming


より具体的にはこんな状況・前提だと思ってください(余談ですが DX の影響なのか、業務でこういう需要を耳にする機会が増えているように感じてます):
- 複数人でアプリ開発(プログラミング)体験を行いたい
  - 参加者は1チーム5~6人程度
  - 参加者は普段 Windows を使っている。Mac, Linux の経験はほぼゼロ
  - 参加者はコマンドプロンプトなどの CLI は普段使っていない
  - 参加者はプログラミングに関してはほぼ初心者
- チームで1つのアプリを作って動かす、という体験をしたい
- 体験期間は半日、長くて1日
- 参加者の PC に何かをインストールしたり、アカウントを作ったり、設定したりする場合、作業できるのは当日
  - 参加者 PC 以外の Linux サーバーなどに主催者が事前準備するのは自由
- 全員オンライン参加、zoom などのオンラインツールの利用に問題はない


最後の前提は必須ではないですが、昨今だとどうしてもこの条件になるかなあ、ということで加えています。


この状況下でどういう仕組を用意すれば、目的に会ったプログラミング体験ができるかを考えます。


候補案1
「とりあえず正攻法でやってもらう」方法です。ソースコードは git などでリポジトリ管理して(必要であれば事前にサンプルを用意して)、各自はリポジトリから clone したソースコードを画面共有を併用しながら手元で編集して動かしてもらう。環境が許せば Visual Studio Code の LiveShare なども使って共同編集する。。

・・・これが本来のスジであることは重々承知しているし、将来的に役立てることを意識するならこの形を学ぶべきだとは思っています。ただプログラミング初心者相手に半日~1日でここまでやるには少しハードルが高すぎる気がしています。環境準備段階でのハードルの高さもありますし、git も教えないといけないし、LiveShare 使うならアカウントから準備しないといけないし、その上でほぼ未経験のプログラミングをオンラインで・・・ 何を作るかにもよりますが、Hello World 程度すら厳しいかも。。

#おまけとして、業務においてはこの手の「管理者権限が必要な作業」自体が実施上のリスクとなる可能性があることを経験談として触れておきます。


候補案2
参加者がそこそこ Linux に詳しい人だったら、Linux サーバーにサンプルを含むソースコードを集める形にして、全員が個別に SSH や VNC でログインして vi でプログラミングすればよい、です(同時編集によるコンフリクトはいったん目をつぶりますw)。同じサーバーでアプリを動かせば、それぞれがウェブブラウザで動作確認もできます。

でも今回、この形で行うにはハードルが高すぎます。普段から SSH やコマンドプロンプトを使うような人ではなく、ましてやキーバインドにクセのある vi を使わせるのは厳しそうです。

手段の1つとして「SSH でログインして nano エディタを使う」ことや「Linux に X Window まで導入した上で、VNC でログインして簡易テキストエディタを使う」も考えられます。これらはありっちゃあり、懸念があるとすれば慣れない CLI や Linux での操作でしょうか。nano エディタも vi や Emacs ほどクセはありませんが、Linux コマンドを無視することもできませんし、CTRL キーや ALT キーと組み合わせてのメニュー操作は慣れるまでは苦労するかも、という印象です。

その辺りの苦労も体験の一部とみなしてやってもらう、という案は相手次第ではあり、かな。。


ここまで書いておいてアレですが、個人的にはここまでの案1&2は現実的ではないかな、と考えています。オンラインでなければまだしも、オンラインでこの慣れてないはずの作業のサポートをするのはかなり難しそうという印象を持っています。理想どおりに実施するのことがかなり厳しそう・・・


候補案3
いわゆるローコード・ノーコード環境を事前に Linux サーバーに用意して、この環境を使う方法です。

これは目的に対する解答になっていると思います。Linux に例えば Node-REDScratch などをインストール&起動しておいて、参加者は画面共有しながらウェブブラウザで(CLI ではない、ここがでかい!)アクセスしてコーディング作業を行う、というものです。なんといっても参加者の PC に何かを事前にインストールする必要がない(=準備段階のリスクが低い)点がポイントです。ローコードなのでプログラミング開始時の敷居が低く、1日でもそこそこ学べるものだと思っています。

懸念が2つあるとすれば、この環境が1つは共同作業に適したツールかどうかの判断や対応が必要になる点と、もう1つはやはりどうしてもできることの制限があることです。ローコードであるが故に「ループ」や「条件分岐」といったコーディングの基礎のようなことを行う選択肢が少なかったりするわけです(ループができないとは言わないけど「整数配列をその数だけループさせながら加算する」とかは Node-RED では難しい)。動くものは作れるかもしれないけど、プログラミング体験という当初の目的を達成できるものになるかどうか、が鍵だと感じました。


候補案4
実はいま個人的にはこれが一番いいかも、と考えているのが、この候補案4です。概要はこんな感じ:
(1) サンプルを含むソースコードを事前に Linux サーバーに用意する
(2) 同サーバーに Eclipse Orion をインストールする
(3) 参加者はウェブブラウザで Eclipse Orion にアクセスして、サーバーのソースコードを直接編集
(4) 誰か一人(主催者でもよい)がサーバーでアプリを起動して動作確認

(1) のサンプルはあらかじめ最低限動くものを用意しておきます。目的にもよりますが、Hello World 表示だけのウェブアプリでもかまわないと思ってます。

(2), (3) の Eclipse Orion は「オンラインテキストエディタ」です。サーバー上で起動し、サーバー内の特定フォルダ以下のテキストファイル(ソースコード)をオンラインで編集できるようになります。テキストエディタとしての基本機能があるので、便利にコーディング作業をすすめることができます。オンラインエディタは必ずしも Orion エディタでなくてもいいと思ってますが、オープンソースであることや docker 環境下で使える便利さもあっておすすめです。

(4) そして編集したソースコードを使って実際に動かし、可能であれば全員がウェブブラウザで動作も確認する、というものです。

この方法も候補案3同様に、参加者 PC 側での事前準備が不要です。CLI 操作もなく、全て GUI 作業です。Java なり JavaScript なりの実行環境もサーバーだけに事前に用意しておけばいいので参加の負担はありません。実際にコーディングも行うので、内容も(分岐やループから、外部API へアクセスしての AI 体験みたいなことまで)自由度高く設計可能です。

プログラミング環境も、Eclipse Orion インスタンスを1つだけ起動して、全員で同じインスタンスに接続して共同プログラミングしてもいいし、Eclipse Orion インスタンスを複数起動して別々に接続させることで同じテーマで個別にプログラミングすることもできます。純粋なプログラミング環境を比較的容易に準備する方法と考えます。


こちらの懸念はあらかじめ用意しておくサンプルをどうするかと、候補案3と異なり「実際にプログラミングを行う(しかも半日~1日で)」ことになる点です。データベースを使うかどうかなど、サンプルに合わせたカリキュラムの検討が必要だと思っています。その代わり「プログラミング体験」という目的に合っていて、「まず一度体験してもらう」ための案としては自由度も高くて悪くない、と思っています。

 

今更感がありますが、「ハンバーガーメニュー」と呼ばれるメニューがあります。三本線の見た目がハンバーガーのように見えることから名付けられたメニューで、ここをクリック(タップ)すると隠れていたメニューアイテムが表示されて選択できるようになる、というものです。スマートフォンなどの、メニューを常時表示するには小さい画面でメニューを実現するためのメジャーな方法です。

このハンバーガーメニューを(ウェブアプリの)CSS だけで実現する方法がいくつか紹介されています。まあコピペすれば動くし、自分も以前はそうしていたのですが、ちゃんと理屈を理解してはいませんでした。そこで今回改めて自分で理解しながら作って公開してみました。まずは一度動かしてみて、ハンバーガーメニューの挙動を確認してみてください:
https://dotnsf.github.io/css_hamburgermenu/

(ハンバーガーメニュー)
2021022001

(タップすると左からメニュー本体が表示され、自分は×印に。×をタップで元に戻る)
2021022002


ハンバーガーメニューに限りませんが、このような動きのある処理を実現するには、一般的には JavaScript を使います。またメニューの見た目である三本線や、メニューが開いた時の×印の表現は一般的には画像を使って表現することが考えられます。上図のページではどちらも使わず、純粋な HTML と CSS だけで実現しています。

同じようなコード(HTML & CSS)のサンプルは数多く存在していて、コピー&ペーストすれば動くようにはなっています。ただコードの中身の解説がされていない場合が多く、「なんでこのコードでハンバーガーメニューになるのか」の理解が難しいのでした。

というわけで、どうやって画像も JavaScript を使わずにハンバーガーメニューを実現しているかを、上図の例を使って解説します。


まず、このページの HTML と CSS は以下のようになっています(よかったらコピペするかダウンロードして、手元の HTML ファイルに保存して動かしてみてください):
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8"/>

<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<meta name="apple-mobile-web-app-status-bar-style" content="black"/>
<meta name="apple-mobile-web-app-title" content="CSS Hamburger Menu"/>

<style>
/* ハンバーガーメニューのサイズ(32x32) */
.hamburger-menu{
  vertical-align: center;
  width: 32px;
  height: 32px;
}
/* ハンバーガーメニューの位置と色 */
.menu-btn{
  top: 20px;
  left: 10px;
  display: flex;
  height: 32px;
  width: 32px;
  justify-content: center;
  align-items: center;
  z-index: 90;
  background-color: #005B00;
}
/* メニュー線(本体と before と after で3本表示する) */
.menu-btn span,
.menu-btn span:before,
.menu-btn span:after{
  /* 20x3 の白線 */
  content: '';
  display: block;
  height: 3px;
  width: 20px;
  border-radius: 3px;
  background-color: #ffffff;
  position: absolute;
}
/* before を少し上にずらして描画 */
.menu-btn span:before{
  bottom: 8px;
}
/* after を少し下にずらして描画 */
.menu-btn span:after{
  top: 8px;
}

/* メニューをオープンしたら三本線を×にする */
#menu-btn-check:checked ~ .menu-btn span{
  /* メニューオープン時は三本線の真ん中の線を透明にする */
  background-color: rgba( 255, 255, 255, 0 ); 
}
#menu-btn-check:checked ~ .menu-btn span::before{
  /* メニューオープン時は三本線の上の線を 45 度傾ける */
  bottom: 0;
  transform: rotate( 45deg ); 
}
#menu-btn-check:checked ~ .menu-btn span::after{
  /* メニューオープン時は三本線の下の線を -45 度傾ける */
  top: 0;
  transform: rotate( -45deg ); 
}
/* チェックを非表示にする */
#menu-btn-check{
  display: none;
}

/* メニュー装飾 */
.menu-content{
  width: 60%;
  height: 100%;
  position: fixed;
  top: 80;
  left: -100%; /* メニューを画面外へ */
  z-index: 80; /* 下のコンテンツの上にかぶせて表示する */
  background-color: #005B00;
  transition: all 0.5s; /* 0.5秒かけてアニメーションで出し入れする */
}
.menu-content ul{
  padding: 70px 10px 0;
}
.menu-content ul li{
  border-bottom: solid 1px #ffffff; /* メニューアイテムの区切り線 */
  list-style: none;
}
.menu-content ul li a{
  display: block;
  width: 100%;
  font-size: 15px;
  box-sizing: border-box;
  text-decoration: none;
  color: #ffffff;
  padding: 9px 15px 10px 0;
  position: relative;
}

/* メニューの出し入れ */
#menu-btn-check:checked ~ .menu-content{
  left: 0;  /* チェックされたら画面内へ */
}
</style>

<body>

<span class="header">
  <div class="hamburger-menu">
    <!-- ここからメニューボタン -->
    <input type="checkbox" id="menu-btn-check"/>
    <label for="menu-btn-check" class="menu-btn"><span></span></label>
    <!-- ここまでメニューボタン -->

    <!-- ここからメニュー本体 -->
    <div class="menu-content">
      <ul>
        <li><a href="#">メニューアイテム1</a></li>
        <li><a href="#">メニューアイテム2</a></li>
        <li><a href="#">メニューアイテム3</a></li>
      </ul>
    </div>
    <!-- ここまでメニュー本体 -->
  </div>
</span>

</body>
</html>

画像も JavaScript も使っていません。CSS もすべてこの中に含まれていて外部ファイルを参照していません。本当に「これだけ」です。この内容を index.html として保存し、ローカルのブラウザで開けば同じ挙動を確認できるはずです。

HTML の <body> 部分だけを抜き出すとこのようになっています:
<body>

<span class="header">
  <div class="hamburger-menu">
    <!-- ここからメニューボタン -->
    <input type="checkbox" id="menu-btn-check"/>
    <label for="menu-btn-check" class="menu-btn"><span></span></label>
    <!-- ここまでメニューボタン -->

    <!-- ここからメニュー本体 -->
    <div class="menu-content">
      <ul>
        <li><a href="#">メニューアイテム1</a></li>
        <li><a href="#">メニューアイテム2</a></li>
        <li><a href="#">メニューアイテム3</a></li>
      </ul>
    </div>
    <!-- ここまでメニュー本体 -->
  </div>
</span>

</body>

<body> はハンバーガーメニューのボタン部分と、ボタンをタップした時に表示されるメニュー本体部分に分かれています。メニュー本体はそれほど難しくないので、このブログエントリではボタン部分の CSS を中心に、以下の4つの観点から詳しく紹介します:

(1) JavaScript なしでどうやってタップされる前と後を識別しているのか?
(2) 「三本線」の見た目を画像を使わずにどうやって実現しているのか?
(3) (タップ後の)×印の見た目を画像を使わずにどうやって実現しているのか?
(4) JavaScript なしでどうやってメニュー本体の出し入れを実現しているのか?



(1) JavaScript なしでどうやってタップされる前と後を識別しているのか?

<body> のハンバーガーメニューボタン部分に以下のコードが含まれています:
    <input type="checkbox" id="menu-btn-check"/>

この部分はチェックボックスですが CSS で非表示に設定されているので、画面上には現れていません:
/* チェックを非表示にする */
#menu-btn-check{
  display: none;
}

メニューをタップするとチェックが入り、もう一度タップするとチェックが外れます(ただし画面には表示されません)。このチェックの有無でタップされる前と後を識別しているのでした。

また HTML ではこの部分の直後に <label for="menu-btn-check" class="menu-btn"><span></span></label> があります。後述しますがこの <span></span> 部分で三本線や×印を描画します。つまり三本線部分をタップすることで、このチェックボックスのチェックが入ったり消えたりするようになっていて、このチェックの有無で三本線か×印か、どちらを描画するかを切り替えられるような仕組みを実現しています。

以下の説明をわかりやすくするために、ここで一時的にチェックボックスの非表示スタイルを解除してみましょう。以下は該当 CSS 部分にこのようなコメントが入っている前提で説明を続けます:
/* チェックを非表示にする */
#menu-btn-check{
/*  display: none; */
}

この時点でハンバーガーメニューや×印の上部に(今まで非表示だった)チェックボックスが表示されているはずです:

2021022003

2021022004


(2) 「三本線」の見た目を画像を使わずにどうやって実現しているのか?

今回のブログを書くために色々調べる前は、ここが最大の謎でした。この三本線が画像でないとしたら何? SVG で描画しているのかと思ったけどそういう記述はないし・・・ と興味津々だった謎でした。

これを実現している HTML 及び CSS 部分は以下です:
(HTML)
<label  class="menu-btn" for="menu-btn-check"><span></span></label>

(CSS)
/* メニュー線(本体と before と after で3本表示する) */
.menu-btn span,
.menu-btn span:before,
.menu-btn span:after{
  /* 20x3 の白線 */
  content: '';
  display: block;
  height: 3px;
  width: 20px;
  border-radius: 3px;
  background-color: #ffffff;
  position: absolute;
}
/* before を少し上にずらして描画 */
.menu-btn span:before{
  bottom: 8px;
}
/* after を少し下にずらして描画 */
.menu-btn span:after{
  top: 8px;
}

HTML の <span> 部分に線を描画します。そのため CSS で背景色(#ffffff = 白)、高さ(3px)、幅(20px)の塗りつぶし矩形を描画しています。実際は白い矩形ですが、20x3 と横に細長いので白線に見えるというわけです。親要素である .menu-btn の指定で縦横とのセンタリングされて(つまり 32x32 のメニューボタンの真ん中に)表示されます。

これだけだと線が1本表示されるだけですが、CSS では span, span:before, span:after すべてで同じ指定がされています。つまり before と after も合わせると3本の線が描画されることになります(まさかこんな方法で3本描いていたとは・・。この方法だと4本に増やすことはできないかも。。)。別途 before は bottom:8px; で本体よりも少し上に、after は top:8px; で本体よりも少し下に描画するよう指定しており、これらの結果3本の白線が縦に並んでハンバーガーメニューとして表示されているのでした。これ考えた人天才だな。。

2021022005



(3) (タップ後の)×印の見た目を画像を使わずにどうやって実現しているのか?

今回の調査をする前に一番の謎だったのが (2) とすると、調査し終わって一番感動したのがこの (3) の実現方法でした。前述の方法で三本線が表示できるとして、これをタップした時にどうやって表示内容を×印に(画像も JavaScript も使わずに)切り替えるのか、です。

上述したように、まずタップしたかどうかはチェックボックスのチェックの有無で判断できるようになっています。つまり #menu-btn-check:checked の時の .menu-btn の見た目を三本線から×印に変えればよい、ということになります。ここまではなんとなくわかります。

では今度はどのようにして×印の見た目を作るのか、その答えがこちらです。チェックが付いている時の span, span:before, span:after に適用しているスタイルです:
/* メニューをオープンしたら三本線を×にする */
#menu-btn-check:checked ~ .menu-btn span{
  /* メニューオープン時は三本線の真ん中の線を透明にする */
  background-color: rgba( 255, 255, 255, 0 ); 
}
#menu-btn-check:checked ~ .menu-btn span::before{
  /* メニューオープン時は三本線の上の線を 45 度傾ける */
  bottom: 0;
  transform: rotate( 45deg ); 
}
#menu-btn-check:checked ~ .menu-btn span::after{
  /* メニューオープン時は三本線の下の線を -45 度傾ける */
  top: 0;
  transform: rotate( -45deg ); 
}

まず三本線の真ん中の白線(span)については background-color: rgba( 255, 255, 255, 0 ); のスタイルを適用します。これはいわば「透明にする」ためのスタイルで、要するに真ん中の白線を透明にして見えなくします。

次に三本線の上の白線(span:before)は bottom:0; で真ん中の白線と同じ高さに移動した上で transform: rotate( 45deg ); を適用します。この指定は「45度回転させる」スタイルです。つまり水平線(ー)だったものをバックスラッシュ(\)のような線に変えています。

同様に三本線の下の白線(span:after)も top:0; で真ん中の白線と同じ高さに移動した上で transform: rotate( -45deg ); を適用してます。この指定は「-45度回転させる」スタイルであり、水平線(ー)だったものをスラッシュ(/)のような線に変えています。
2021022005


その結果が×印になっていたのでした。眼からウロコ、そんな方法だったのか・・・


(4) JavaScript なしでどうやってメニュー本体の出し入れを実現しているのか?

最後のこの部分は、ちゃんと勉強し直す前から自分で理解できていた、という意味で「一番簡単な仕組み」でした。大まかな仕組みとしてはメニューそのものを作った後、チェックボックスにチェックが入っていない時は表示されない位置に配置し、チェックが入ったら表示される位置まで transition 指定でアニメーションをかけてあげればよいだけのことです(そしてチェックが外れたら再び表示されない位置までアニメーションで戻す)。

上記のコード内では以下のように指定しています:
/* メニュー装飾 */
.menu-content{
  width: 60%;
  height: 100%;
  position: fixed;
  top: 80;
  left: -100%; /* メニューを画面外へ */
  z-index: 80; /* 下のコンテンツの上にかぶせて表示する */
  background-color: #005B00;
  transition: all 0.5s; /* 0.5秒かけてアニメーションで出し入れする */
}
  :
  :
/* メニューの出し入れ */
#menu-btn-check:checked ~ .menu-content{
  left: 0;  /* チェックされたら画面内へ */
}


メニュー本体は .menu-content クラスで定義された部分です。チェックがついていない時点ではここに left: -100%; を指定することで画面の幅1つぶん左の位置に表示しておきます。

そしてチェックが付いた時のスタイルで left: 0; を適用します。これでチェックが付くと画面の左端からメニューが表示されるようになります(チェックが再び外れると、また画面の左端の更に左へ移動して見えなくなります)。この時に .menu-content クラス内で transition: all 0.5s; を適用しておくことで、これらの移動を 0.5 秒かけたアニメーション処理で行うようになります。つまりチェックが付くと左端からすーっと表示され、チェックが外れると左端にすーっと吸い込まれて見えなくなる、という視覚効果をつけることができるのでした。


これら (1), (2), (3), (4) をあわせて適用することで、(画像も JavaScript も使わずに)CSS だけでハンバーガーメニューを実現することができる、というものでした。この「CSS だけで実現する」というのは、「スマホに適した処理として実現できる」とも言えます(簡単に言うと「GPU 併用で処理できるので、比較的貧弱なスマホの CPU に大きな負担をかけずに実現できる」ということです。詳しくはこちら)。この CSS だけの方法だとハンバーガーメニューの見た目を更に変更するのはなかなか難しいなどの制約もありますが、「工夫次第でここまでできる」というクラフトマンシップというか、ある意味天才的な発想を垣間見たような気分で、今後もこの方法を使っていきたいと思う内容でした。

で、動作を確認し終わったら最初にコメントしたチェックボックスの非表示設定はもとに戻しておきましょう。
/* チェックを非表示にする */
#menu-btn-check{
  display: none;
}






このページのトップヘ