P2P地震情報14周年: 個人サービスを長く続けるための技術
個人開発 #2 Advent Calendar 2018 8日目の記事です.
せっかく作った個人サービスを長く続けていくための技術についてお話ししようと思います.
3行でまとめると
- Dogfooding … 使い続ける
- Maintenance-free … とにかく省力化する
- Write Code Every Day … 小さい変更を積み重ねる
P2P地震情報
P2P地震情報というサービスを開発・運用しています.
気象庁の地震情報やユーザ同士の「揺れた!」という地震感知情報を,P2Pネットワークによって共有するサービスです.
現在のサービス構成は下記の通りです.本体はP2Pですが,サーバの役割は多岐にわたります.なお,耐災害性の観点から,一部機能は有志の方々のご協力により全国数ヶ所に分散しています.
祝14周年🎉 (なお理想と現実は異なる)
2004年12月に公開し,今年で丸14年です.思った以上に長く続いています.
残念ながら,やりたいことは十分に実現出来ていません.GUIアプリケーションは未だにVB6製で,なかなかのレガシーです.*1
それでも,14年間続けることができました.なんだかんだで良かったと思っています.どうして続けられたのかというと,これから話す3つの技術が大事なのではと感じています.
サービスを続けていくための技術
Dogfooding: 使い続ける
サービスを続けるには,モチベーションを維持し続けることが重要です.
その最も簡単な方法が,自分自身がモチベーションの源泉となるように,サービスを自ら使い続けることだと感じています.
昔,LaTeXのオンラインコンパイラやPDFファイルの画像化といったサービスを公開していましたが,自分が使わなくなった途端にメンテナンスしなくなり,外部からアクセスは続いていたものの公開を終了したことがあります.
Maintenance-free: とにかく省力化する
モチベーション(と時間)を無駄にしないために,維持するためにかかるコストを最小限に抑えます.
色々ありますが,一番やってよかったのはひとりSlackでした.
サービスに関わるあらゆるメール,通知,実行ログをSlackに集約し,「とりあえずSlackの新着を見ればよい」という状態に辿り着きました.メールの未読消化と比べて圧倒的な省力化です.
Write Code Every Day: 小さい変更を積み重ねる
学生から社会人になり,「まとまった時間が取れない」「しかし週末に一気にやる気持ちにもなれない」という矛盾する悩みを抱えていました.数年間悩み続けた末,John Resigの記事に出会いました.
John Resig - Write Code Every Day (日本語訳参考: 毎日コードを書くこと - snowlongの日記)
学生時代の開発スタイルではいけないと気付かされました.
それから,100日以上毎日コードを書き続けました.1~2行しか書かない日もありましたが,それでも効果は絶大でした.
C#版のGUIアプリケーションも徐々に実装が進んでいて,一部機能は既に動作します.レガシーをそろそろ捨てられる日が来そうです.
楽しい個人開発ライフを!
世の中の便利な個人サービスが,少しでも長く続きますように…!
明日は @ktrkmk さんです.お楽しみに!
*1:余談ですが,Windows 10でもVisual Basic 6.0は動作しますし,IDEでコンパイルもできます
Raspberry Piでダッシュボードを作る(7) -時刻表-
Raspberry Piとミニディスプレイ,各種センサを使ってダッシュボードを作ります.
- Raspberry Piでダッシュボードを作る(1) -準備-
- Raspberry Piでダッシュボードを作る(2) -真っ白ダッシュボード-
- Raspberry Piでダッシュボードを作る(3) -デザイン-
- Raspberry Piでダッシュボードを作る(4) -天気予報-
- Raspberry Piでダッシュボードを作る(5) -温度・湿度(センサ)-
- Raspberry Piでダッシュボードを作る(6) -鉄道遅延情報-
- Raspberry Piでダッシュボードを作る(7) -時刻表- ←イマココ
今回のゴール
- 通勤に使っている最寄り駅の時刻表を表示させる.
いちいち最寄り駅の時刻表を調べるのは面倒です.ダッシュボードに常に表示させておきましょう.
時刻表の抽出
今回は Yahoo!路線情報 を利用します(※個人利用に留めましょう).例として 日比谷駅(東京メトロ日比谷線)の時刻表 を使ってみます.
情報の抽出には, Nokogiri
ライブラリを用います(スクレイピングとも言います).使い方は他所に譲るとして,下記スクリプトを parse.rb
といった名前で用意します.
#!/usr/bin/env ruby require 'net/http' require 'nokogiri' data = Nokogiri::HTML.parse(Net::HTTP.get(URI.parse(ARGV[0]))) data.css('table.tblDiaDetail > tr').each do |node| hour = node.css('td:first-child').text.rjust(2,"0") node.css('td:last-child > ul > li').each do |train| next if train.text !~ /(\d+)/ minute = $1.rjust(2,"0") puts "#{hour}:#{minute}" end end
実行してみましょう.
$ gem install nokogiri # Nokogiriライブラリをインストール $ chmod +x ./parse.rb # 実行権限を付与 $ ./parse.rb "https://transit.yahoo.co.jp/station/time/22951/?gid=3301&kind=1&done=time" 05:07 05:26 05:38 05:46 05:54 06:02 (省略)
無事に取得できました.ダッシュボードで表示するために,ファイルに出力しておきます.
$ ./parse.rb "https://transit.yahoo.co.jp/station/time/22951/?gid=3301&kind=1&done=time" > hibiya.txt
ダッシュボードへの表示
今回はかなりゴリ押しです.「むこう60分間の時刻表をいい感じに表示する」ようにします.
<div class="card"> <h4 class="card-header">時刻表</h4> <div class="card-body"> <% data = File.read("hibiya.txt") train_data = data.split(/\n/).map{|e| e.chomp}.select{|e| e>Time.now.strftime("%H:%M") && e<(Time.now+60*60).strftime("%H:%M")} .group_by{|e| e[0,2]}.map{|k,v| "<strong>#{k}</strong>#{" "*3}#{v.map{|e| e[3,2]}.join(" ")}"}.join("<br>") %> <p><%= train_data %></p> </div> </div>
これで,「次の電車何分だっけ?」というときにスマートフォンを操作せずに確認出来るようになりました.
補足
今回,土休日や種別(急行など)は考慮していません.
今日はここまで.
Xamarin.iOS: Storyboard上のアイテムとC#のクラスとをリンクさせる
Xamarin.iOSの日本語文献が少ないので,記録も兼ねてアウトプットしていこうと思います.
Storyboard上のアイテムをC#のクラスとリンクさせる
結論: Storyboardのプロパティ Class
にクラス名を書けば,対応する.cs
ファイルが自動的に作成される.
Storyboard上に配置したViewControllerや他アイテムに対してコードを書きたいとき,C#のクラスとリンクさせる必要があります.
このとき,Storyboard上でクラス名を指定すると,対応する .cs
ファイルが自動的に作成されます.
例
例えば,右側の Page View Controller をリンクさせたいときは,Page View Controller を選択した状態でクラス名を入力します.
クラス名に対応する.cs
ファイルが自動的に作成されます.
環境
- Visual Studio for Mac (Community) 7.6.3
- Xcode 9.4.1
参考
RF-killされたBluetoothデバイスをオンにする
Raspberry Pi Zero WでBluetoothを使って遊ぼうとしたものの,RF-kill
,つまり電波を出さないようになっているご様子.
$ sudo hciconfig hci0: Type: Primary Bus: UART BD Address: B8:27:EB:xx:xx:xx ACL MTU: 1021:8 SCO MTU: 64:1 DOWN RX bytes:654 acl:0 sco:0 events:33 errors:0 TX bytes:419 acl:0 sco:0 commands:33 errors:0 $ sudo hciconfig hci0 up Can't init device hci0: Operation not possible due to RF-kill (132)
うーん,CUIでなんとかしたいな?
と思ったら,rfkill
というそのまんまのコマンドがありました.
bluetooth hci0 soft blocked if not turned on before arch boot / Laptop Issues / Arch Linux Forums
$ rfkill list 0: phy0: Wireless LAN Soft blocked: no Hard blocked: no 1: hci0: Bluetooth Soft blocked: yes Hard blocked: no $ sudo rfkill unblock bluetooth $ rfkill list 0: phy0: Wireless LAN Soft blocked: no Hard blocked: no 1: hci0: Bluetooth Soft blocked: no Hard blocked: no $ sudo hciconfig hci0 reset $ sudo hciconfig hci0 hci0: Type: Primary Bus: UART BD Address: B8:27:EB:xx:xx:xx ACL MTU: 1021:8 SCO MTU: 64:1 UP RUNNING PSCAN RX bytes:5025 acl:0 sco:0 events:293 errors:0 TX bytes:5265 acl:0 sco:0 commands:291 errors:0
機内モードのオンオフ的なイメージですね.
manコマンドで調べると分かるのですが,unblock hci0
とデバイス名で指定するのではなく,wifi
bluetooth
wimax
などと種類を指定する必要があります.ちょっと分かりにくい.
「Visual Studio IntelliCode」早速使ってみた.
Visual Studio IntelliCodeが発表されました.
詳細は上記記事に譲るとして,既にVisual Studio 2017でC#向けにプレビュー版が使えるようになっています.早速使ってみます.
インストール
- Visual Studio IntelliCodeのサイトを開きます.
- ページ下部の「Try it out today for C#」のリンクを選択し,Visual Studio Marketplaceに飛びます.
- 「Download」からダウンロード.
- ダウンロードしたファイルを開くと,Visual Studioにインストールされます.
使用方法
特に設定などせず,普通にコーディングをするだけです. サジェストで「★」が出てきます.
使用例(アニメーション)
参考:IntelliCode不使用時は単にアルファベット順だった
アニメーションだと分かりづらいのですが,オーバーロードのあるメソッド(ここだとConsole.WriteLine
)でも,最適と思われる定義が選ばれるようです.
プレビュー版はここまで.
プレビュー版の機能はこれだけ.正直なところ「使う頻度の少ないクラスに触れるときは嬉しいかも?」という程度です.
ただ,IntelliCodeのサイトには,潜在的なバグの検出・修正だったり,コーディングスタイルをソースコードから学習したり,といったことも記載されています.
潜在的なバグの検出に関しては,従来の静的コード解析よりも良いものが出せるのかどうか気になります.大いに期待したいところです.
Spring Fest 2017に参加しました&資料リンク
(11/29追記: 「これから始めるSpringのWebアプリケーション」「Yahoo! JAPANのコンテンツプラットフォームを支えるSpring Cloud Streamによるマイクロサービスアーキテクチャ」の資料リンクを追加)
2017/11/24に開催されたSpring Fest 2017に参加しました.
冷めないうちに,発表資料のリンクと感想をアウトプットしておきます.
(発表者敬称略)
発表資料リンク
Twitter #jsugで見つけたものを可能な限りリンクしました.
時刻 | KFC Hall | KFC Hall Annex | Room 113 | Room 115 |
---|---|---|---|---|
10:00-10:45 | What's New in Spring - Dave Syer (Pivotal) | - | - | - |
11:00-11:45 | エンタープライズ開発で利用するSpring Boot - 廣末 丈士(株式会社ビッグツリーテクノロジー&コンサルティング) | Introduction to Spring WebFlux - 槙 俊明 (Pivotal) | これから始めるSpringのWebアプリケーション - 土岐 孝平(日本Springユーザ会) | SpringFrameworkを用いた社内ライブラリ開発について - 朏島 一樹(リクルートライフスタイル) |
12:00-12:45 | - | Wagby R8 と Spring の関係 - 贄 良則(株式会社ジャスミンソフト) | - | - |
13:15-14:00 | Spring Security 5 解剖速報 - 岩塚 卓弥、堅田 淳也(NTTソフトウェアイノベーションセンタ) | Spring Data RESTを利用したAPIの設計と、作り直しまでの道のり - 高橋 勲(楽天株式会社) | これから始めるSpringのWebアプリケーション 〜ハンズオン編〜 - 大野 渉 (Starlight & Storm) | Yahoo! JAPANのコンテンツプラットフォームを支えるSpring Cloud Streamによるマイクロサービスアーキテクチャ - 鴨志田 智弥(ヤフー株式会社) |
14:15-15:00 | ドメイン駆動設計のためのSpringの上手な使い方 - 増田 亨(有限会社システム設計 / ギルドワークス株式会社) | 事例で知る!Spring/Angularによる大規模エンタープライズ開発 - 岩佐 歩(株式会社野村総合研究所) | Struts -> Spring 移植のテクニックとノウハウを公開 - 鈴木 健夫(株式会社スタイルズ) | 脆弱性の探し方 ~発見と対応のノウハウ in NTTDATA~ - 浅原舜平(株式会社NTTデータ) |
15:30-16:15 | Quick start Spring Boot on OpenShift (demo sources) - Kamesh Sampath (Red Hat's) | 日本一やさしく説明する予定のマイクロサービス入門 - 長谷川 裕一(Starlight&Storm / 日本Springユーザ会) | Pivotal認定講師が教える!Spring Data JPAによるデータアクセス徹底入門 - 多田 真敏(株式会社カサレアル) | 2017年のLINEのマイクロサービスを支えるSpring - 井出真広(LINE株式会社) |
16:30-17:15 | The Road to Serverless - Dave Syer (Pivotal) | Spring と TDD - 小川岳史、古家優(株式会社タグバンガーズ) | ついに来たリアルタイムSpark~ビッグデータ処理の新常識・SnappyDataの実力~ - 山河 征紀(ウルシステムズ株式会社) | Spring エンジニアが理解すべきフロントエンド入門 - Angular編 - 佐川 夫美雄(アシラス株式会社) |
17:30-18:15 | Spring BootとSpring Cloudで始めるマイクロサービス - 谷本 心(Acroquest Technology株式会社 / 日本Javaユーザーグループ) | - | 実践JHipster - こざけ(株式会社第一コンピュータリソース) | SpringでOAuth 2.0・OpenID Connect 1.0を使う - うらがみ |
以下,感想
10:00~ What's New in Spring? (Dave Syer (Pivotal))
- Thinkpad + Linux + Jekyll + Google Chrome でプレゼンテーション.完成されている
- 英語+同時通訳だったが,通訳前にリアクションする参加者が多かった.英語力が試される
- 「これJavaなの?」と思うくらいの隠蔽具合
- Spring Initializr (またはSpring Tool Suite)で簡単に新規プロジェクトが作成できるうえ,わずかなコーディングで動作する
- 正直,Ruby on Rails最強だと思っていたので怯えている
- 「サーバレスに向かっていくのかな?」と感じた
- WebFlux, Router Functionsなどを用いた "Reactive", "Non-blocking" の説明が強かった
11:00~ Spring Frameworkを用いた社内ライブラリ開発について (朏島 一樹(リクルートライフスタイル))
- 標準化はどこも苦労しているな~ と思った.地味だけど難しい活動
- 方針がはっきりしていて良いと感じた
- 『フレームワークではなく,あくまでライブラリ』(小分け)
- 『「汎用性」「実装難易度」の2軸を指標としてライブラリ化』
- プロダクト(開発プロジェクト)から「これライブラリ化しない?」という話が上がってくる,という話は正直うらやましい
- 「ボイラープレートコード」(Boilerplate code - Wikipedia (英語版) 初めて聞いた
13:15~ Spring Data RESTを利用したAPIの設計と、作り直しまでの道のり (高橋 勲(楽天株式会社))
- 貴重な失敗談.「Spring Data RESTは使わない」というオチに笑ってしまった
- 「簡単なモノを簡単に作るのには向いているが,実際に適用するのは大変」
- ちょっと触って「これ便利じゃん!」というのは結構危ないなと反省
15:30~ 2017年のLINEのマイクロサービスを支えるSpring (井出真広(LINE株式会社))
- "Recent apps are Java/Spring applications",意外.勝手な偏見でRubyやらPHPやらゆるふわWeb系なのだと思っていた
- 技術要素としてHTTP/2が(違和感なく)入っているあたりが眩しい
- 良いものは使っていく,使ってみる,という空気感がありそうな気がした
- 『みなさんもぜひ地雷を踏んでいってください』 (ツールのバグに対して)
- 非同期サービス化するのは大変そう.でも既にだいぶ洗練されている様子だった
16:30~ ついに来たリアルタイムSpark~ビッグデータ処理の新常識・SnappyDataの実力~ (山河 征紀(ウルシステムズ株式会社))
- Springと全く関係なさそうだ,という理由だけでセッション参加
- ビッグデータ処理,「要素が多くてとっつきにくそう」という懸念を見事に破ってくれそうな感触
- 『テーブルとSQLなら出来ると思うんです』 確かに出来そうな気がしてしまう
- とりわけSynopsis Data Engineが興味深かった
- 精度を抑える代わりに高速なクエリ処理を実現.信頼度も指定ができる
- まっったく無関係だが Apache Spark のロゴがスパークかわいい
17:30~ Spring BootとSpring Cloudで始めるマイクロサービス (谷本 心(Acroquest Technology株式会社 / 日本Javaユーザーグループ))
- やはり使い所は相当限られる,という感覚は間違っていない様子
- 「学ぶと視野は広がるので,学んでおくことは重要」.どうせ使わないと思っていたけど触ってみてもいいかなと思えた
- "Start simple, not small"
- 小さすぎると苦しみは味わえない.確かに,サンプルページ表示しただけでは「ふーん」で終わる…
- Spring Cloud Netflix Eureka (ユリーカと発音されていた)
- わずかな設定で勝手にマイクロサービスを管理・探索・ロードバランシングされる.とても気持ち悪い(いい意味で)
- 「トランザクションをマイクロサービスにまたがって行うのは難しい(実質不可能)」
- 非同期処理で流す,サービスのインスタンス増減に自然に対応できる,のがポイントのよう
Raspberry Piでダッシュボードを作る(6) -鉄道遅延情報-
Raspberry Piとミニディスプレイ,各種センサを使ってダッシュボードを作ります.
- Raspberry Piでダッシュボードを作る(1) -準備-
- Raspberry Piでダッシュボードを作る(2) -真っ白ダッシュボード-
- Raspberry Piでダッシュボードを作る(3) -デザイン-
- Raspberry Piでダッシュボードを作る(4) -天気予報-
- Raspberry Piでダッシュボードを作る(5) -温度・湿度(センサ)-
- Raspberry Piでダッシュボードを作る(6) -鉄道遅延情報- ←イマココ
今回のゴール
- 通勤に使っている路線の遅延情報を表示させる.
"鉄道遅延情報のjson"
今回は,鉄道遅延情報のjsonというサイトを利用します.このサイトでは,Tetsudo.com(鉄道コム)が提供する運行情報のRSSフィードをJSON形式で返してくれます.
JSONは第4回の天気予報でも扱いましたが,Rubyから簡単に読み取ることが出来ます.
まずはirbで解析
まずはirb
を使って,対話型でデータを解析しましょう.
$ irb irb(main):001:0> require 'uri' => true irb(main):002:0> require 'net/http' r=> true irb(main):003:0> require 'yaml' => true irb(main):004:0> data = Net::HTTP.get(URI.parse("https://tetsudo.rti-giken.jp/free/delay.json")) => "[{\"name\":\"\xE4\xB9\x85\xE5\xA4\xA7\xE6\x9C\xAC\xE7\xB7\x9A\",\"company\":\"JR\xE4\xB9\x9D\xE5\xB7\x9E\",\"lastupdate_gmt\":1510449602,\"source\":\"\xE9\x89\x84\xE9\x81\x93com RSS\"}, (省略) irb(main):005:0> json = YAML.load(data) => [{"name"=>"久大本線", "company"=>"JR九州", "lastupdate_gmt"=>1510449602, "source"=>"鉄道com RSS"}, {"name"=>"豊肥本線", "company"=>"JR九州", "lastupdate_gmt"=>1510449602, "source"=>"鉄道com RSS"}, (省略)
第4回の天気予報と同じやり方で,JSONを解釈しました.company
に会社,name
に路線が入っています.
そのままダッシュボードに載せると不要な情報も入ってしまうため,欲しい路線だけに絞り込みます.Enumerableモジュールのselectメソッドを使うと,条件にマッチする要素だけに絞り込めます.
irb(main):006:0> json.select { |item| item["company"] == "JR東日本" } => [{"name"=>"東北本線", "company"=>"JR東日本", "lastupdate_gmt"=>1510449902, "source"=>"鉄道com RSS"}, {"name"=>"羽越本線", "company"=>"JR東日本", "lastupdate_gmt"=>1510449902, "source"=>"鉄道com RSS"}, {"name"=>"白新線", "company"=>"JR東日本", "lastupdate_gmt"=>1510449902, "source"=>"鉄道com RSS"}] irb(main):007:0> json.select { |item| item["name"] =~ /京浜東北線|山手線/ } => []
最初に「会社: JR東日本」,次に「路線: 京浜東北線か山手線(※正規表現を使用)」で絞り込んでみました.何もマッチするものがなければ,空っぽの配列が返ってきます.
データの操作がイメージ出来たところで,ダッシュボードに載せましょう.
ダッシュボードへ
views/index.erb
のカードを編集しましょう.ざっとこんな感じです.
<div class="card"> <% require 'uri' require 'net/http' require 'yaml' data = Net::HTTP.get(URI.parse("https://tetsudo.rti-giken.jp/free/delay.json")) json = YAML.load(data) json = json.select { |item| item["name"] =~ /京浜東北線|山手線/ } output_message = json.map { |item| item["name"] }.join("<br>") %> <h4 class="card-header text-white bg-danger">電車遅延情報</h4> <div class="card-body"> <p class="card-text"><%= output_message %></p> </div> </div>
表示ができました.
遅延がないときの表示をいい感じにする
表示はできましたが,このままですと,遅延情報がないときの表示が残念なことになります.
そこで,遅延情報がないときは「タイトルを赤くしない」「『遅延はありません』と表示する」としてみます.
(省略) json = json.select { |item| item["name"] =~ /京浜東北線|山手線/ } output_message = "遅延情報はありません。" output_class = "" if !json.empty? output_message = json.map { |item| item["name"] }.join("<br>") output_class = "text-white bg-danger" end %> <h4 class="card-header <%= output_class %>">電車遅延情報</h4> <div class="card-body"> <p class="card-text"><%= output_message %></p> </div>
視覚的にわかりやすくなりました.
今回はここまで.