Blogaomu

WEBアプリケーション開発とその周辺のメモをゆるふわに書いていきます。

Kanazawa.rb meetup #75 に参加しました #kzrb

今回はDMM.com 金沢事業所をお借りして開催!

kzrb.doorkeeper.jp

当初は自分含めて2人しか参加表明してなくて不安でしたが最終的には11人の参加になりました。わいわい。

Scala関西Summit 2018に行ってきた話

www.blogaomu.com

↑の話をベースに写真も追加したスライドを作って発表しました。コンセプトとしてはScala関西Summitに行った気になれる、だったんですが、いろいろ詰め込みすぎてだいぶ時間を取ってしまいました(すいません)。Scalaは可愛いを広めることができてよかったです。

Bufferを操作する

Kanazawa.rb告知用のSNS投稿をBufferで行っているとのことで、これをCLIでやれたらいろいろ捗るのでは?という考えから今回のもくもくタイムではBuffer APIを触ってみました。

APIを触る前にBuffer自体を使ったことが無かったのでアカウントを作っていろいろ試して概念を理解するところから始めました。

  • 1アカウントに対して複数のSNSアカウントをひもづけられる。ひもづいたSNSアカウントのことをProfileと呼んでいる
  • 投稿はQueueというものに格納され、特定の時刻になったらSNSアカウントを通して接続先のSNSに投稿される
    • 投稿時にQueueに入れる(Add to Queue)、即時にSNS投稿する(Share now)、任意の日時に投稿する(Schedule post)、の3つから選択できる
    • Queueに入った投稿は1件ずつ時刻(Posting times)が割り振られ、その時刻になると接続先のSNSに投稿される

投稿に関する概念はざっとこんな感じだと思います。詳しくはこの辺を見ると良さそう。 Managing your Queue - Buffer FAQ

概念を理解したところで、プログラムからBuffer APIを呼ぶ場合には公式のgemがあるのでこれを利用すると良さげです。

github.com

Buffer APIを利用する場合、先にアプリケーションの登録をサイト上で行う必要があります。画面に従って入力していけばOKです。ここで発行されたAccess tokenを使用します。

buffer.com

先ほど発行されたAccess tokenを環境変数 BUFFER_ACCESS_TOKEN に入れておいて、replでいろいろ操作してみます。

> require 'buffer'
> client = Buffer::Client.new(ENV['BUFFER_ACCESS_TOKEN'])
# Profileの取得
> profiles = client.profiles
=> [{"_id"=>"5bef956a4b1be7784607d225",
  "avatar"=>
   "http://pbs.twimg.com/profile_images/835761987728502784/QMKQ3UYI_normal.jpg",
  "avatar_https"=>
   "https://pbs.twimg.com/profile_images/835761987728502784/QMKQ3UYI_normal.jpg",
  "can_see_content_library"=>false,
  "counts"=>
   {"sent"=>4, "drafts"=>0, "daily_suggestions"=>25, "reminders"=>0, "pending"=>0},
  "cover_photo"=>
   "https://d3ijcis4e2ziok.cloudfront.net/default-cover-photos/blurry-blue-background-iii_facebook_timeline_cover.jpg",
  "created_at"=>1542428010,
  "default"=>true,
  "disabled"=>false,
  "disabled_features"=>[],
  "disconnected"=>false,
  "formatted_service"=>"Twitter",
  "formatted_username"=>"@TAKAyuki_atkwsk",
  "id"=>"5bef956a4b1be7784607d225",
  "is_analyze_enabled"=>false,
  "is_on_business_v2"=>false,
  "locked"=>false,
  "paused"=>false,
  "paused_schedules"=>[],
  "preferences"=>{},
  "reports_logo"=>nil,
  "schedules"=>
   [{"days"=>["sun"], "times"=>["09:55", "14:25", "16:44", "19:32"]},
    {"days"=>["mon"], "times"=>["09:55", "14:25", "16:44", "19:32"]},
    {"days"=>["tue"], "times"=>["09:55", "14:25", "16:44", "19:32"]},
    {"days"=>["wed"], "times"=>["09:55", "14:25", "16:44", "19:32"]},
    {"days"=>["thu"], "times"=>["09:55", "14:25", "16:44", "19:32"]},
    {"days"=>["fri"], "times"=>["09:55", "14:25", "16:44", "19:32"]},
    {"days"=>["sat"], "times"=>["09:55", "14:25", "16:44", "19:32"]}],
  "service"=>"twitter",
  "service_id"=>"77184067",
  "service_type"=>"profile",
  "service_username"=>"TAKAyuki_atkwsk",
  "shortener"=>{"domain"=>"buff.ly"},
  "statistics"=>{"followers"=>785},
  "timezone"=>"Asia/Tokyo",
  "timezone_city"=>"Tokyo - Asia",
  "user_id"=>"5bef953a4b1be777074c2b05",
  "utm_tracking"=>"disabled",
  "verb"=>"tweet"},
  ...]

# 投稿をQueueに入れる
> client.create_update(
*   body: {
*     text: 'お腹吹田',
*     profile_ids: [profiles.find {|profile| profile.service == "twitter" }.id],
*   }
* )
=> {"updates"=>
  [{"_id"=>"5befaff6dd5072291b2a9b93",
    "client_id"=>"5befa27c4b1be72ec04037b3",
    "created_at"=>1542434806,
    "day"=>"Today",
    "due_at"=>1542450720,
    "due_time"=>"7:32 pm",
    "id"=>"5befaff6dd5072291b2a9b93",
    "is_video_processing"=>false,
    "library_update_id"=>"",
    "needs_approval"=>false,
    "organization_id"=>"5bef953a4b1be777074c2b07",
    "perm_approvable"=>false,
    "perm_editable"=>true,
    "pinned"=>false,
    "profile_id"=>"5bef956a4b1be7784607d225",
    "profile_service"=>"twitter",
    "shared_now"=>false,
    "status"=>"buffer",
    "text"=>"お腹吹田",
    "text_formatted"=>"お腹吹田",
    "text_md5"=>"e01909236cf97670e3f23a95e52c7072",
    "type"=>"text",
    "updated_at"=>1542434806,
    "user_id"=>"5bef953a4b1be777074c2b05",
    "via"=>"api"}],
 "buffer_percentage"=>20,
 "buffer_count"=>2,
 "success"=>true,
 "message"=>"One more tweet in your Buffer. Keep it topped up!",
 "code"=>false}

# 複数のProfileで投稿する
> client.create_update(body: {text: "こんにちは", profile_ids: profiles.map(&:id)})
=> (略)

# 特定の日時に投稿されるようにする
> client.create_update(body: {text: 'hogehoge', profile_ids: [profiles.find {|profile| profile.service == "twitter" }.send(:id)], scheduled_at: "20181117T19:00:00+09:00"})
=> (略)

ひとまず複数の投稿形式を試して思った通りに動作しました。 profile_ids を取ってくるところで少しハマりましたが、後はAPIパラメーターを調べつついけました。このあとはCLIとして操作できれば良いなと考えているのでthorと組み合わせて実装してみようと思っています。

懇親会

www.hotpepper.jp

www.hotpepper.jp

一次会は手羽先食べ放題コース!過去にいくつかの食べ放題に勝ってきたkzrb勢も手羽先には勝てなかった...。

二次会は安定の丸二。唐揚げがでかいぜ!

懇親会で、作業部屋のCO2濃度を計測して可視化およびアラートを出す仕組みを作りたいと相談したところ、まともな濃度センサーはお高いらしく中国から輸入するのが良さそう?とのことでした。また、Withingsの体重計でCO2濃度も測れるやつがあるらしく、可視化ならこれでもいいのではという話も。

次回のKanazawa.rbは年末恒例のLT大会です!

Scala関西Summit 2018に参加しました #scala_ks

大阪にて行われたScala関西Summit 2018の1日目に参加してきました。私自身にとっては初の参加で、Scalaの集まりとしても久しぶりに顔を出しました。

2018.scala-kansai.org

プラチナスポンサーがZOZOテクノロジーズだったので一番大きなホールの名称がZOZOホールになっていました。

私が聞いたトークセッションについて軽くメモ

http4sとcats-effectで可愛らしい、関数型らしいアプリケーションを書こう!٩(๑^o^๑)۶

https://speakerdeck.com/dysangelist/tagless-final

  • 発表者のダーシャは仕事でお世話になってるのと「可愛らしい」ってどういうこと?というのが気になって聞くことにした
  • Scalaの特徴を可愛さに例えているのが良いポイントでした
    • 簡潔さ、高レベルでの抽象化、論理的、型システムで安心などなど
  • Tagless Finalという手法?の紹介
    • うまく理解できなかったけど、実装を良い感じに分離できるとかそういう良さがあるのだろうか?

アニメーションで理解するAkka StreamsとAkka HTTP

https://youtu.be/NBKpr7bL22M

  • これまでAkka Streamsを触る機会があってドキュメントを読んだりもしたけどふわっとした理解だったので聞くことにした
  • アニメーションで概念が理解しやすかった
    • 爆発シーンが最高
  • Akka HTTPがAkka Streamsで構成されてるのは興味深かった

Format things with scalafmt

https://speakerdeck.com/tanishiking/format-things-with-scalafmt

  • 共感を覚えた意見
    • 人によって暗黙的にしたがっているスタイルが存在しているので、人が増えるとカオスになる
    • 瑣末な議論を減らして、本質的なものの議論に重きを置きたい
  • ScalafmtはScalametaというものでScalaを解析しているそう(コンパイラとは別の実装)、すごい
  • 既存のコードベースからdiffの少ない設定を生成するやつ

Scala-erにも影響ありそうなJavaの現状まとめ

https://www.slideshare.net/linecorp/summary-of-the-state-of-java-that-will-affect-scalaers

  • JavaはOpenJDKとしてオープンソースコミュニティで開発
  • Javaのビルドはいろいろなプロバイダーが提供している(Oracle, AdoptOpenJDK, クラウドベンダー等)
  • リリースサイクルが変わったことは知っていたけども、サポートポリシーや各種ビルドについて知らないことばかりだった

Akkaを分散トレーシングで見てみよう

http://nbviewer.jupyter.org/format/slides/github/grimrose/scala-kansai-summit-2018/blob/master/slide.ipynb#/

  • Scalaに限らずマイクロサービスにも通ずる話題だったし、分散トレーシングという概念は今後ものにしておきたいトピックだった
  • Kamonを使っているけどもJVMメトリクスの取得に使っていたので、トレーシングの方も調査したい
  • OpenTracingという統一した仕様を策定している(CNCFプロジェクト)
    • 実装はそれぞれのライブラリで異なる
  • Jaegerのロゴが可愛い(gopherっぽい)
  • 途中で発表者のドリンクタイムを入れていたのが良かった(Pyconの発表であったのを取り入れたらしい)
    • 聞く側も一段落できて良い

DatabricksとSparkではじめる [データ分析/機械学習] 実践入門

https://speakerdeck.com/smdmts/databrick-and-spark-with-data-analyze-and-ml-for-newbie

  • Databricks便利っぽい
    • AWS上に実行基盤を構築可能で、Web UIもある
    • Web UI上のNotebookで開発できる
  • ここからはScalaの世界観で、ここからはPythonの世界観という風な言い回しが特徴的だった
  • Sparkが基盤になっているとScalaを使う場面も出てくるのか、なるほどと思った

Json codec を楽しもう、現場で役立つ circe

http://labs.septeni.co.jp/entry/2018/09/18/090727

  • 「キルケ」派が多い?(「サース」って呼んでた)
  • library versionの書き方面白かった .map(_ % version) 的な
  • 私も使っていますが今のところJsonライブラリとしては最も使い勝手が良い感じ
    • Encoder/Decoderの自動導出(サポートされている型の多さ、case classにも対応)
  • forProductN知らなかった

実践GraphQL on Scala

https://speakerdeck.com/petitviolet/real-world-graphql-on-scala

  • 思い切ってGraphQLを導入(フロントエンドはElmだそうです)
  • 型システムを持っているものとScalaは相性がよいと再認識
  • 参照系と更新系でアプリケーション内の層を分けているのは興味深かった

良かった点

  • 写真公開NGの人を明示的に区別できていた(名札の色で判断できる仕組み)
  • 写真撮影用スタッフがいた
  • フリーコーヒー
  • 快適なwifi
  • 椅子のみではなく机がある
  • 学生スタッフが結構いた
  • アンカンファレンスのScala酒場(参加できなかったけけど、ツイッターで流れてきた画像を見て良さそうと思った)

懇親会

懇親会は会場変わって、大阪駅から直ぐのさくらインターネット。オープンスペースにはバーカウンターがあってオシャレ空間だった。お料理も美味しかったです!

発表者に質問してみようかなとか思ってたんですが、話に割って入るのが個人的にはハードル高いので聞けずじまいに終わってしまいました。でも初めましての方とも話すことができて良かったです。金沢から来ましたーと言うとかなりの確率でびっくりされましたw (関西勢と東京勢が多かった)

その他の様子

スタッフの皆様、参加者の皆様、楽しい空間をありがとうございました!はるばる金沢から参加して良かったです!

あと、早朝のサンダーバード乗るのしんどかったので来年は前乗りしようかなと思いますw

Kanazawa.rb meetup #74 に参加しました #kzrb

kzrb.doorkeeper.jp

今回は和室での開催。入り口からして開発合宿感が漂っていました(合宿ではない)。金沢勤労者プラザというところが会場だったのですがインターネットが無いのだけ除けば満足できる施設でしたよ!

ブログ記事を書いた

前日に参加していたJAWS-UG金沢もくもく会の記事を書きました。とりあえず進捗出せた。

www.blogaomu.com

プログラミングElixir 20章

「マクロとコードの評価」というテーマの章でした。

  • 関数が使えるなら、マクロは使ってはならない、という教訓
  • defmacro quote unquote
  • あるモジュールでマクロを定義して、使う側ではそのモジュールを requrie する
  • マクロに渡されたパラメータは評価されない
  • quote に渡されたもの(ブロック)は評価されずに内部表現を返してくれる
  • unquoteしたコードは quote ブロックが実行されてコードが生成されたときに実行される
  • unquote_splicing はリストを展開してくれる
Code.eval_quoted(quote do: [1, 2, unquote([3, 4])])
#=> {[1, 2, [3, 4]], []}
Code.eval_quoted(quote do: [1, 2, unquote_splicing([3, 4])])
#=> {[1, 2, 3, 4], []}

if をマクロで実装してみようという例でしたが、まだしっくり理解できず。練習問題もやりながら理解していきたいですね。。

プログラミングElixir

プログラミングElixir

懇親会

居酒屋 さかなや道場 金沢駅店
〒920-0031 石川県金沢市広岡1-9-25 アクロスキューブ金沢駅西口2F
3,000円(平均)850円(ランチ平均)
r.gnavi.co.jp

retty.me

2件目は安くて程よく雑な雰囲気で穴場開拓した感がありましたね。「本日の男汁」というメニューがあったのが印象的でした。

JAWS-UG金沢 #35 に参加しました(Terraform独習メモ4日目) #jawsug

jawsug-kanazawa.doorkeeper.jp

今回はもくもく会で6人参加でした。私はTerraform独習の続きを、その傍ら次回の発表ネタ出しなどをみんなで行いました。

www.blogaomu.com

2018-10-19

  • 今回はALBを作ってみようと思う
  • 久しぶりなのでいろいろ忘れている
    • terraform version したらprovider.awsバージョンも出てきたので、そもそもどうやってインストールしたんだっけ?となった
$ terraform version
Terraform v0.11.8
+ provider.aws v1.6.0
resource "aws_lb" "memo_lb" {
  name               = "memo-lb"
  internal           = false
  load_balancer_type = "application"
  subnets            = ["${aws_subnet.public-a.id}", "${aws_subnet.public-b.id}"]

  enable_deletion_protection = true
}
  • ALB用のセキュリティグループを作る
  • 現状一つのtfファイルで管理しているのでごちゃっとしてきた
    • まとまり毎にファイル分割したいなあ
  • 〆のグラフ
    • terraform graph > graph/$(git rev-list --max-count 1 HEAD).dot && dot -T png graph/$(git rev-list --max-count 1 HEAD).dot -o graph/$(git rev-list --max-count 1 HEAD).png
    • 中央付近にlbが増えている f:id:TAKAyuki_atkwsk:20181020135808p:plain

その他

@_kentaro_m さんのLT練習(AWS運用周りの話)をみんなで聞いていて、サービスや組織の規模によってこういう構成をとるのかーと新たな発見があって面白かった。小規模ならそれに適した構成も取れるし、なんだかんだ言うてAWSサービス全体としても柔軟なんだなあと思いました。

懇親会

がブリチキン。 金沢武蔵店 - 北鉄金沢/居酒屋 [食べログ]

3人でDB設計やらコンテナやらの話をしていました。コンテナを使ったアプリケーション運用の話を聞きたいということだったので、そのうち喋ることになりそうです。唐揚げ美味しかった。

次回

2018-11-23(金・祝)にOpsJAWSとの共同イベントを行います(私は参加できない)。みんな悩みがちな運用周りの話がいろいろ出てくるようなのでぜひご参加くださいませ〜〜。

jawsug-kanazawa.doorkeeper.jp

Kanazawa.rb meetup #73 に参加しました #kzrb

kzrb.doorkeeper.jp

7年目に突入して最初のもくもく会です。今回はいつものElixir本ではなく、違うことをしていました。

告知用文言生成のスクリプト

Kanazawa.rb では meetup を開催するときに予め各種 SNS で告知を行うのですが、毎回手で文言を作成するという話をメンバーから聞いたので、ある程度自動的にできればいいなあと思いスクリプト化の検討を行いました。文言の構成は毎回同じようなのでテンプレートを作成して変数を流し込むという処理で良い感じにできそうです。rbの名が付いているので erb 使う?と思ったのですが、そこまで複雑なことは行わないので logic-less と謳っている mustache を使ってプロトタイプを作ることにしました。一通りのプログラミング言語に対応しています。まずは手っ取り早くシェルでお試し。

github.com

macOSなら以下のようにインストール可能です。

curl -sSL https://git.io/get-mo -o mo
chmod +x mo
mv mo /usr/local/bin/

以下のようなテンプレートを用意して

Kanazawa.rb meetup {{SERIES_NO}} を{{DATE}}に開催します。内容は「{{TITLE}}」。{{DESCRIPTION}}場所は{{PLACE}}。詳細はこちら。

https://kzrb.org/meetup/{{SERIES_NO}}/

#kzrb

環境変数を渡して実行すると以下のように出力されました。

$ SERIES_NO=73 DATE=2018-09-15 TITLE=意識高いもくもく会 DESCRIPTION=各自で目標を設定する自主勉強会です。 PLACE="ITビジネスプラザ武蔵 研修室2" mo template.txt

Kanazawa.rb meetup 73 を2018-09-15に開催します。内容は「意識高いもくもく会」。各自で目標を設定する自主勉強会です。場所はITビジネスプラザ武蔵 研修室2。詳細はこちら。

https://kzrb.org/meetup/73/

#kzrb

まあ、なんか良さそう。今後は実用化に向けて、GitHub Issueと絡めて上手くmeetup準備フローに組み込めないかなと考えております。以下当日書いていたメモ。

LTした

以下の内容で話しました。今回は Dropbox Paper でばーっと書いてプレゼンテーションモードで見せる、という発表方法にしたのですがブログに載せるときにはスライドとして作るという作業が必要そうですね...


batコマンドのご紹介

https://github.com/sharkdp/bat

A cat(1) clone with wings.


  • catコマンドのクローン
    • バッチファイルの類いではない
    • Rustで実装されている
  • チームメイトから教えてもらって良さげだと思ったので紹介
    • brew install bat

  • bat README.md


  • catにない特徴
    • シンタックスハイライト
    • gitとの連携(差分表示)
    • ページング
    • デフォルトで行番号表示(catには行番号表示オプションあり)

  • ヘルプがカラー対応、ナウい
    • bat --help

  • シンタックスハイライトは多くの言語、ファイルタイプに対応
    • bat --list-languages

  • テーマを変えることができる
    • bat --list-themes

  • プレーンな表示だと見づらいファイルを見ながら作業したいとき
    • 例えばREADME, JSON, yaml, Dockerfile
    • 今まではvimを開いて見ていた
    • batで十分な場面がほとんどなので役立ちそう

懇親会

www.hotpepper.jp

retty.me

5人でこじんまりとわいわい飲みました。今回初めて参加した方は、東京の会社に属して金沢でリモートワークしているとのこと。リモートワーク事例じわじわ増えてきてますね。二次会ではガリ酎飲んでガリ〆鯖を食べるというガリ三昧を体験してきました。

ScalaTest の OptionValues が良さげ

ScalaTestを使っていて便利な機能があったので小ネタとして紹介してみます。

OptionValues

Option 型の値を検証したいときに、 OptionValues を使うとOption値が定義されているかとその値についての検証を同時に行うことができます。

http://www.scalatest.org/user_guide/using_OptionValues

// やりがちな例
opt.get should be > 9 // opt が Noneの場合 NoSuchElementException が起きてテストが失敗する(後述)

// より安全な方法
opt should be ('defined) // opt が None の場合 TestFailedException が発生。どこで失敗したかの情報が得られる
opt.get should be > 9

// OptionValuesを使う
import org.scalatest.OptionValues._

opt.value should be > 9 // Option型の値に対して `value` メソッドを使えるようになる
// opt が None の場合 TestFailedException が発生

// テストクラスに対しては OptionValues trait を mix-in するとよい
class ExampleSpec extends WordSpec with Matchers with OptionValues {
  // この中のテストコードで `value` メソッドを使えるようになる
}

実際やってみるとこんな感じです。

import org.scalatest.{Matchers, OptionValues, WordSpec}

class ExampleSpec extends WordSpec with Matchers with OptionValues {

  "Option value is defined" in {
    val opt: Option[Int] = Some(10)
    opt.value should be > 9
  }

  "Option value is defined but is not match condition" in {
    val opt: Option[Int] = Some(1)
    opt.value should be > 9
  }

  "Option value is not defined" in {
    val opt: Option[Int] = None
    opt.value should be > 9
  }

  "Option value is not defined and access with get" in {
    val opt: Option[Int] = None
    opt.get should be > 9
  }
}

以下はテスト実行時の出力。注目してほしい点は3番目と4番目のテストの違いで、 value を使った場合はテストの失敗内容が簡潔にまとめられて出力されていますが、 get を使った場合は通常の例外における出力(Exceptionのメッセージ + スタックトレース)となっています。

[info] ExampleSpec:
[info] - Option value is defined
[info] - Option value is defined but is not match condition *** FAILED ***
[info]   1 was not greater than 9 (ExampleSpec.scala:12)
[info] - Option value is not defined *** FAILED ***
[info]   The Option on which value was invoked was not defined. (ExampleSpec.scala:17)
[info] - Option value is not defined and access with get *** FAILED ***
[info]   java.util.NoSuchElementException: None.get
[info]   at scala.None$.get(Option.scala:349)
[info]   at scala.None$.get(Option.scala:347)
[info]   at ExampleSpec.$anonfun$new$4(ExampleSpec.scala:22)
[info]   at ExampleSpec$$Lambda$5658/224979600.apply(Unknown Source)
[info]   at org.scalatest.OutcomeOf.outcomeOf(OutcomeOf.scala:85)
[info]   at org.scalatest.OutcomeOf.outcomeOf$(OutcomeOf.scala:83)
[info]   at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
[info]   at org.scalatest.Transformer.apply(Transformer.scala:22)
[info]   at org.scalatest.Transformer.apply(Transformer.scala:20)
[info]   at org.scalatest.WordSpecLike$$anon$1.apply(WordSpecLike.scala:1078)
[info]   ...
[info] ScalaTest
[info] Run completed in 436 milliseconds.
[info] Total number of tests run: 4
[info] Suites: completed 1, aborted 0
[info] Tests: succeeded 1, failed 3, canceled 0, ignored 0, pending 0
[info] *** 3 TESTS FAILED ***
[error] Failed: Total 4, Failed 3, Errors 0, Passed 1
[error] Failed tests:
[error]     ExampleSpec
[error] (Test / testOnly) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 2 s, completed 2018/08/26 11:35:09

EitherValues, TryValues, PartialFunctionValues

OptionValues と同じような働きをするtraitは他にもあって EitherValues TryValues PartialFunctionValues というものがあります。これも中の値の定義と検証を同時に行い、失敗した場合は TestFailedException を起こしてくれます。

http://www.scalatest.org/user_guide/using_EitherValues

http://www.scalatest.org/user_guide/using_PartialFunctionValues

(なぜかTryValuesについてのページは存在しない😅)

// TryValues
import org.scalatest.TryValues._

val try1: Try[Int] = Try { 100 / 10 }
val try2: Try[Int] = Try { 1 / 0 }
// enable `success` and `failure`
try1.success.value should be > 9
try2.failure.exception should have message "/ by zero"

// EitherValues
import org.scalatest.EitherValues._

val either1: Either[String, Int] = Right(10)
val either2: Either[String, Int] = Left("Muchas problems")
// enable `value`
either1.right.value should be > 9
either2.left.value should be ("Muchas problemas")

// PartialFunctionValues
import org.scalatest.PartialFunctionValues._

val pf: PartialFunction[String, Int] = Map("I" -> 1, "II" -> 2, "III" -> 3, "IV" -> 4)
// enable `valueAt`
pf.valueAt("IV") should equal (4)

Kanazawa.rb meetup #72 に参加しました #kzrb #ふくもく会

kzrb.doorkeeper.jp

fukumoku.connpass.com

今回はKanazawa.rb 6周年記念回ということで恒例のLT大会が行われました。また、同時刻にふくもく会も開催されており拠点をzoomで繋いで発表し合うというスタイルでした。

6周年記念のカラビナ。普段使いにも良さそう。(雑に接続した模様です...)

話したこと

Webアプリケーションを開発するに当たって意識していることを雑に発表しました。チーム内で言われていることだったり、meetupで聞いたことだったり影響を受けているものは結構あります。

初めの2つに関しては前々から言われていることではありますが、チーム内でフォーカスが当たることが多かったので採り上げてみました。そういえばリーダブルコードでそんな話あったなと思い調べてみたらやっぱり書いてありました。テストコードの話については、異常系のテストケースは完全になることはなくて漏れることは多々あるので生じたバグに対して積み上げていくのがいいんじゃないかなと最近思ってます。発表では触れてませんがProperty Based Testingについても調べてみたいなと思ってます。

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)

拾いきれなかった質問というかツイートがあったので補足すると、私は『The RSpec Book』を読んでこんな感じでテスト書いてけばいいのかーというのをきっかけにした感じです。実際はBDDのお話がメインにはなってくるのですが。あとは『Everyday Rails - RSpecによるRailsテスト入門』という電子書籍が出ていて、こちらは最新の環境に追随していてかつ実践的な内容なのでRailsを使っている|使おうとしているならばオススメです。実践の話で言うと、自分の実装部分からテストを書いてみるという手法があって、最初は最低限のもの(自分の触る単一のメソッドに対して等)だけ書いてって少しづつサイクルを回していくということは過去に経験しました。そのときは、プロジェクト自体にほとんどテストコードが無かったのでちょうど勉強してたし書いてみるかーと手を付けた流れでした。

カバレッジに関しては最初から数字を目的にするのは相応しくないなーと思ってまして、設計の元になる、ドキュメントになるというのが本質的にあって結果としてこのくらいカバーしてますよと。それと並行して、品質を担保するのにユニットテストをもっとやるのか、E2Eテストをやるのか、方法はいろいろあると思いますけどもどのようにしていくかというのを決めて、ユニットテストカバレッジはこれくらいあればいいよねと言う話になるのかなと思っています。あまりうまくまとまってませんが、こんな感じです。

The RSpec Book (Professional Ruby Series)

The RSpec Book (Professional Ruby Series)

leanpub.com

懇親会

www.hotpepper.jp

1次会はビアガーデン!こちらも毎年恒例になってますね😊 2次会は新天地のディラン、北dev勢にはおなじみのお店。運営の話や、地方でこの先生きのこるにはといったような話題が。また来月もやっていきましょう。