Calmery.me

みっかぼうずにならないようがんばる

ElmとElectronでデスクトップアプリを作ってみた

これは Elm Advent Calendar 2017Electron Advent Calendar 2017 の 15 日目の記事です.

Qiita にも同じ内容の記事を投稿しています.
ElmとElectronでデスクトップアプリを作ってみた - Qiita

はじめに

ElmElectron を使って Twitterハッシュタグである #elm をストリーミングするアプリを作ってみました.完成したものは GitHub - calmery/elm-advent-calendar-2017 で見ることができます.
f:id:calmery:20171214230543p:plain

実装

コミット毎にまとめていきます.

Hello World

GitHub - calmery/elm-advent-calendar-2017 at 6490533e51c1fb4afea9e03aa9562e4762f52207
f:id:calmery:20171215004356p:plain

Elm が undefined になった

早速というか.Electron から Elm を参照すると undefined となってしまいました.

Elm.Main.fullscreen() // Uncaught ReferenceError: Elm is not defined

実際に生成されたコードを見ると module.exports が優先されてしまうようです.

if (typeof module === "object")
{
  module['exports'] = Elm;
  return;
}

var globalElm = this['Elm'];
if (typeof globalElm === "undefined")
{
  this['Elm'] = Elm;
  return;
}

なので module.exports から直接参照するようにしました.

const Elm = module.exports

// require を使って読み込むこともできる
const Elm = require( './app.js' )

この問題は webpack を使用することで気にならなくなりました.

Twitter から取得したツイートをプロセス間通信と Port で送る

GitHub - calmery/elm-advent-calendar-2017 at 45fff5fea0170ef369d8554a4dbcbe0f020abe81
Electron はメインプロセスとレンダラプロセスが別れています.なので,その間でやりとりを行うためにプロセス間通信を行う必要があります.ここは ipcMain | Electron を見るといいかなと思います.

// src/entry.js

// In main process.
stream.on( 'data', event => {
  const tweet = event.text
  window.webContents.send( 'newTweet', tweet )
} )
// src/public/entry.js

// In renderer process.
ipcRenderer.on( 'newTweet', ( _, tweet ) => {
  // do something ...
} )

ひとまずツイートの本文だけをレンダラプロセスに渡すようにしました.ここから,さらに受け取ったデータを Elm に Port を使って渡します.ここは JavaScript · An Introduction to Elm の Step 2: Talk to JavaScript とか ElmのPortでJSを使う。 - Qiita が参考になります.

ipcRenderer.on( 'newTweet', ( _, tweet ) => {
  app.ports.newTweet.send( tweet )
} )
port newTweet : (String -> msg) -> Sub msg

type Msg
    = NewTweet String

subscriptions : Model -> Sub Msg
subscriptions model =
    newTweet NewTweet

JSON をデコードする

GitHub - calmery/elm-advent-calendar-2017 at 39ab6b701fcfbbf29a46ec706286c2f4c76a5f1f
ツイートの本文だけでは物足りないのでユーザの情報なども一緒に渡すようにしました.

// src/entry.js
stream.on( 'data', event => {
  const tweet = {
    text: event.text,
    created_at: event.created_at,
    user: {
      profile_image_url: event.user.profile_image_url,
      name: event.user.name,
      screen_name: event.user.screen_name,
    }
  }

  window.webContents.send( 'newTweet', JSON.stringify( tweet ) )
} )

Elm で JSON をデコードします.JSON · An Introduction to Elm[Elm] Decoder a からいろいろ理解ってしまおう - Qiita が参考になります.

-- src/public/elm/Main.elm
type alias Tweet =
    { user : User
    , text : String
    , created_at : String
    }

decodeTweet : String -> Result String Tweet
decodeTweet response =
    decodeString tweetDecoder response

tweetDecoder : Decoder Tweet
tweetDecoder =
    map3 Tweet
        (field "user" userDecoder)
        (field "text" string)
        (field "created_at" string)

通知する

GitHub - calmery/elm-advent-calendar-2017 at 5ee82825bd10cd5c3b0705f21b0804ba33455ca3
Elm から Port を通して Notification - Web API インターフェイス | MDN を呼び出すことでツイートを受け取った際に通知を表示するようにしました.

-- src/public/elm/Main.elm
port notification : String -> Cmd msg

...

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
  ...
    Ok tweet ->
      ( List.append [ tweet ] <| List.take 100 model, notification tweet.text )
// src/public/entry.js
app.ports.notification.subscribe( message => {
  new Notification( message )
} )

見た目を整える

GitHub - calmery/elm-advent-calendar-2017 at c72da54e9bfb6d0aff18446d4313bfd31af246c5
Elm にも elm-styled とか style-elements とかあるようですが,今回は普通に CSS を使いました.

まとめ

躓いたところもいくつかあったけれど,思っていたよりすんなりできたかなと思います.Elm はまだわからないことが多いので色々と作りながら試していけたらと思います.

LINEの40万インターンに参加してきた

LINE Summer Internship 2017 のエンジニア就業コース,通称 40 万インターンに参加してきた.詳しくは LINE株式会社 | インターンシップ 2017 エンジニア就業コース を見ればわかると思う.

linecorp.com

技術テスト

コーディングの問題が 3 問あり,それぞれを 1 時間くらいで解く.全完はできなかった.1 問目に至ってはテストケースが 1 つも通っていない(とても悲しい).聞いた限りだと他のインターン生も全完は出来ていないようだった.

面接

Skype で面接を行った.面接では今までどんなことをやってきたのか,インターンシップではどんなことをやりたいのか,興味のある技術は何かなどを話した.自分の場合は雑談混じりでラフな感じだったのだけど,しっかり面接だったというところもあったみたい.

インターンシップについて

初日は新宿駅から出るのに苦労したり,お洒落なオフィスに緊張したり.自分が配属されたのは LINE ゲームに関係するチームで,自分を含めて 2 人が配属された.

f:id:calmery:20170903104338j:plain

出社は 10 時とゆっくりで定時は 18 時半だった.定時になったら帰らせられる.帰りたくないよ.

あと,オフィスに席は用意されていたけど,基本作業をする場所は自由で,絶対にオフィスの中でということはなかった.自分はソファでまったり音楽を聴きつつ進めて,行き詰まったときに自分の席に戻り,メンターの方に相談しつつ進めていた.かなり自由にやらせてもらったと思う.

社内はお洒落.撮影スタジオや保育園,カフェまである.カフェでは毎朝,軽い朝ご飯が出るし最高.毎日 2 回は行ってました.

f:id:calmery:20170903104402j:plain

内容

簡単に言えば,配属されたチームの中の課題を解決するということ.

自分は配属先で使われているモニタリングツールの改良を課題にした.改良を行う上での技術の選択は自由だったので,前々から興味のあった技術を使って開発を行った.ほぼ初心者だったというのもあるけど,どこかで詰まると無限に時間が溶ける.そして最終的に完璧とは言い難いものが出来た.

最終日の講評で限られた時間内に作り上げること云々,時間が足りないのは実力不足云々と言われて,まさにその通りだなと思った.普通に時間足りませんでした.

インターンシップを終えて

感想としては楽しかったというのが一番かなと思う.

もはやインフラと化している LINE の内部のことを知れたり,実際のプロダクトがどのように作られているか,そのソースコードまで自由に見ることができた.

メンターの方にもコードのレビューなどサポートして頂いて本当に感謝しかない(ちなみにメンターの方も以前このインターンシップに参加していたとのことでした!)

運が良かっただけかもしれないけど,とても良い経験ができたし,参加することができて本当に良かった.ありがとうございました.

f:id:calmery:20170903104432j:plain

他のインターン生の記事

観測した範囲でメモ.

ak1t0.hatenablog.com
www.kagemiku.com
chigichan24.hatenablog.com

色々と

自分は CodeSprint を経由して申し込んだのだが,元々はエンジニア就業コースではなくサービス開発コースに申し込むつもりだった.だが CodeSprint を経由して申し込むとエンジニア就業コースに申し込んだことになってしまうらしく,それを知らずにサービス開発コースに申し込んだものだと思い込んでいた.面接後にこれはエンジニア就業コースの面接だよと言われて「?」となった.

あ…ありのまま 今 起こった事を話すぜ!
「おれは サービス開発コースの面接を受けていると思ったら いつのまにかエンジニア就業コースの面接を受けていた」
な… 何を言っているのか わからねーと思うが おれも 何をされたのか わからなかった…

それ以外にも面談だと聞いて行ったら面接だったとか何だかんだあったけど,色々なインターンシップを紹介していただいたり,企業の方と繋いでいただいたりとサポートも手厚くてすごく良かったです.

codesprint.jp

インターン中に連れて行ってもらったご飯はどれも豪華でおいしかった.

f:id:calmery:20170903104448j:plain
f:id:calmery:20170903104450j:plain
f:id:calmery:20170903104521j:plain
f:id:calmery:20170903104452j:plain

VirtualBox上のUbuntuでDockerを動かしてみる

セキュリティさくら 分科会(仮称) 第2回 : ATND のまとめ.

事前準備

VirtualBox 上で Ubuntu を動かせる状態にしておく.自分は Ubuntu 17.04 を使用した.

コンテナと VM の違い

VM はハードウェアシュミュレーションであってコンテナはあるプロセスに専用の実行空間を用意すること.注目を浴びる「Dockerコンテナ」、従来の仮想化と何が違うのか? | コラム | 東京エレクトロン デバイス株式会社 とかを読んだら雰囲気はわかると思う.

Docker がしてくれるもの

  • コンテナ管理
  • 環境構築
  • イメージ管理

Docker の使い方

  • Image を作成する
    • docker build
  • コンテナを実行する
    • docker run

インストー

Get Docker for Ubuntu - Docker Documentation を見る.インストールする対象ごとにドキュメントが用意されているので参考にする.

$ sudo apt-get remove docker docker-engine
$ sudo apt-get update
$ sudo apt-get install \
    linux-image-extra-$(uname -r) \
    linux-image-extra-virtual
$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo apt-key fingerprint 0EBFCD88
$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
$ sudo apt-get update
$ sudo apt-get install docker-ce
$ apt-cache madison docker-ce
$ sudo docker run hello-world
$ sudo usermod -aG docker $(whoami)

usermod を実行後,ログインし直す必要がある.

Ubuntu の 17.04 では add-apt-repository の $(lsb_release -cs) を xenial に変更する必要がある.ここを変更していないと apt-get install docker-ce したときにそんなものないよって言われる.

$ sudo apt-get install docker-ce
  Readning package lists... Done
  Building dependency tree
  Reading state information... Done
  E: Unable to locate package docker-ce

Note: The lsb_release -cs sub-command below returns the name of your Ubuntu distribution, such as xenial.

Sometimes, in a distribution like Linux Mint, you might have to change $(lsb_release -cs) to your parent Ubuntu distribution. For example: If you are using Linux Mint Rafaela, you could use trusty.
Get Docker for Ubuntu - Docker Documentation

$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   xenial \
   stable"

Hello World

試しに動かしてみる.

$ sudo docker run hello-world
  Unable to find image 'hello-world:latest' locally
  latest: Pulling from library/hello-world
  78445dd45222: Pull complete 
  Digest: sha256:c5515758d4c5e1e838e9cd307f6c6a0d620b5e07e6f927b07d05f6d12a1ac8d7
  Status: Downloaded newer image for hello-world:latest

  Hello from Docker!
  This message shows that your installation appears to be working correctly.

  To generate this message, Docker took the following steps:
   1. The Docker client contacted the Docker daemon.
   2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
   3. The Docker daemon created a new container from that image which runs the
      executable that produces the output you are currently reading.
   4. The Docker daemon streamed that output to the Docker client, which sent it
      to your terminal.

  To try something more ambitious, you can run an Ubuntu container with:
   $ docker run -it ubuntu bash

  Share images, automate workflows, and more with a free Docker ID:
   https://cloud.docker.com/

  For more examples and ideas, visit:
   https://docs.docker.com/engine/userguide/

Struts2 を動かしてみる

何かと話題となった Struts2 を Docker で動かしてみる.

$ mkdir workspace
$ cd workspace
$ wget https://dist.apache.org/repos/dist/release/struts/2.5.10.1/struts-2.5.10.1-apps.zip
$ unzip struts-25.10.1-apps.zip
$ vim Dockerfile
$ cat Dockerfile
  FROM tomcat:7.0-jre8
  ADD struts-2.5.10.1/apps/struts2-rest-showcase.war /usr/local/tomcat/webapps/
  CMD ["catalina.sh", "run"]
$ ls
  struts-25.10.1 Dockerfile
$ docker build -t struts/st .
$ docker run -it --rm -p 8080:8080 struts/st
  ...
  May 13, 2017 5:10:29 AM org.apache.catalina.startup.Catalina start
  INFO: Server startup in 8858 ms

http://localhost:8080 を開き,以下のような画面が表示されたら成功.docker run の -p 8080:8080 ではコンテナ側の 8080 番ポートを実行環境の 8080 番ポートに割り当てているのだとか.コンテナに外部からアクセス(ポートフォワード) - Qiita あたりを参考に.
f:id:calmery:20170514204146p:plain

共有フォルダを作成する

分科会では Ubuntu 16.04 LTS : Samba : フルアクセスの共有フォルダ作成 : Server World を見ながら進めたが,ここでは sambaサーバによるファイル共有環境の構築手順 を見ながら進めることにする.

sambaサーバの構築手順
上図のファイル共有環境の構築のためには以下の手順が必要になります。
1. sambaのインストー
2. sambaユーザの作成
3. sambaユーザのグループへの追加
4. グループ共有用ディレクトリの作成とアクセス権の設定
5. sambaの設定ファイルの編集
sambaサーバによるファイル共有環境の構築手順

まずは samba を apt-get を使ってインストールする.ついでに ifconfig を使うための net-tools と vim もインストールした.

$ sudo apt-get install samba
$ sudo apt-get install vim net-tools

samba で使用するユーザを追加する.

sambaを使用するユーザを作成する場合、sambaが動作するLinux上にsambaを使用するユーザと同名のLinuxユーザがすでに存在している必要があります。
sambaサーバによるファイル共有環境の構築手順

ということなのでユーザを作成してから pdbedit で samba にユーザを追加する.既にあるユーザを追加する場合は pdbedit だけ実行すればいい.

$ sudo useradd -m user1
$ sudo pdbedit -a user1
 new password:
 retype new password:

グループを作成し,グループごとにアクセス権を与える.

sambaでは、sambaが動作するLinux上に存在するグループ毎に、共有するファイルやディレクトリのアクセス権を変更できます。今回はubuntu上にgroup1という名前のグループを作成し、group1に属するユーザだけがアクセスできるディレクトリを作成します。
sambaサーバによるファイル共有環境の構築手順

$ sudo groupadd group1
$ sudo gpasswd -a user1 group1
 Adding user user1 to group group1

ディレクトリの作成を行い,先ほど追加したグループへアクセス権限の追加を行う.

$ sudo mkdir /home/public
$ sudo chgrp group1 /home/public
$ sudo chmod -R 774 /home/public

samba の設定を変更する.

$ sudo vim /etc/samba/smb.conf
$ cat /etc/samba/smb.conf
  ...
  unix charset = UTF-8
  dos charset = CP932
  ...
  map to guest = never
  ...
  [public]
     path = /home/public
     browseable = yes
     writable = yes
     valid users = @group1

ここでは valid users にグループ名を指定しているが pdbedit で追加したユーザを直接指定することもできる.

valid users = calmery, user1, user2

ホスト側で開く.ここで指定するアドレスは ifconfig で調べられる.
f:id:calmery:20170514205819p:plain
先ほど登録したユーザ名とパスワードを入力する.
f:id:calmery:20170514205319p:plain
マウントするボリュームを選択する.
f:id:calmery:20170514205748p:plain
開けた.ここで追加したファイルは Ubuntu 上で確認できる.
f:id:calmery:20170514205537p:plain
f:id:calmery:20170514210032p:plain
右クリックで取り出せる.
f:id:calmery:20170514210110p:plain

ハマったところ

自分は分科会で共有フォルダを開くことができなかった.調べてみると

また、NATの使用時に、SSH等でホストOSからゲストOSへの接続を行いたい場合、ネットワークのアダプターにホストオンリーアダプターを別途使用することで、ゲストOSへホストOSから接続可能とすることができます。
VirtualBox CentOS6.7 64bitでNAT、ホストオンリーアダプターを使用 | kakiro-web カキローウェブ

ということらしく,VirtualBoxVM の設定のネットワークから「ホストオンリーアダプター」を追加すると共有フォルダを開くことができるようになった.
f:id:calmery:20170514204231p:plain

まとめ

なるほど(?)
ほとんど環境を整えただけで終わってしまったが,簡単な使い方はわかったので色々と調べてみようと思う.

Dockerを使って、Apache Struts2の脆弱性S2-037のやられ環境を手軽に作る - DARK MATTER
Dockerで作る開発環境 - Qiita
Docker入門-基礎編 いまから始めるDocker管理【2nd Edition】

ちなみに共有フォルダでは会話も楽しむことができる.