表参道.rb #31 で LINE でお天気bot をつくった話をしてきた

LT してきました。
今回もご飯がとても美味しすぎた…。

LINE でお天気bot をつくった話

https://osyo-manga.github.io/slide-omotesandorb-31-otenki-bot/index.html#/

と、いうことで今回は珍しく生産性のある話をしてきました。
年末に LINE でお天気 bot を作ったので、それで利用した API や Ruboty の紹介なんかをしてきました。
Ruboty のプラグインとして LINE のアダプタお天気 bot のハンドラをつくったので API キーさえ用意してもらえればすぐに使える(はず…。
何か問題があれば Issues まで教えてください。


そんな感じで今回で5回目のLTをしてきましたー。
運営の方々、発表を聞いてくださった皆様、ありがとうございました。
今回はちょっと参加人数が少なくていつもとは違う趣向でしたが、これぐらいの人数のほうがみんなで色々と意見をいいながら話せるのでこれはこれでよかったかな?と思いました。
みんなでワイワイいいながらコードを見るのは楽しいですね!
そろそろ話すのにも慣れていきたい。

Ruby で (a ==1 && a== 2 && a==3) の結果を真にする

と、いうのが JavaScript 界隈で流行っているので Ruby でもやってみた

== メソッドを定義する

多分一番簡単なやり方。 比較演算子そのものの結果を変えます。

a = Object.new
def a.== other
    true
end

p a == 1 && a == 2 && a == 3
# => true

a メソッドを定義する

Ruby ではメソッド呼び出しの際に () を省略する事が出来るので a() というメソッド呼び出しを a とだけで呼び出すことが出来ます。

def a
    @a = (@a || 0) + 1
end

p a == 1 && a == 2 && a == 3
# => true

こっちは Ruby らしいですね。

おまけ:== ではなくて === を使う

主題からは外れますが Ruby では === 演算子は特別な呼び出しなので == の変わりに === を使うといろいろな手段が使えます。

Proc#=== を使う

Proc#===Proc#call と同じなのでブロックの中身をそのまま返します。

a = proc { true }

# a.call 1 と同じ
p a === 1 && a === 2 && a === 3
# => true

Method#=== を使う

Method#===Proc#=== と同様に #call を呼び出します。

a = [1, 2, 3].method(:include?)

# [1, 2, 3].include? 1 を呼び出しているのと同じ
p a === 1 && a === 2 && a === 3
# => true

Range#=== を使う

Range#===Range#include? を呼び出します。

a = (1..3)

# (1..3).include? 1 を呼び出しているのと同じ
p a === 1 && a === 2 && a === 3
# => true

Set#=== を使う

Range と同様に Set#===Set#include? を呼び出します。

require "set"

a = Set[1, 2, 3]

# Set[1, 2, 3].include? 1 を呼び出しているのと同じ
p a === 1 && a === 2 && a === 3
# => true

Ruby たのしい

Ubuntu で Dropbox の同期が出来なかったときの対処方法

最近 Ubuntu を入れ直したんですが、その時に Dropbox の同期がうまくいかなかったので覚書。
Linux 版の Dropbox アプリの場合、デフォルトの設定では『1 万件以上のフォルダを監視すること』が出来ないため、以下のようなコマンドを端末から実行して回避する必要がある。

# 10 万件までのフォルダを監視するように指示する
$ echo fs.inotify.max_user_watches=100000 | sudo tee -a /etc/sysctl.conf; sudo sysctl -p

参照

Ruby の Hash のキーを Symbol に変更する

Ruby の Hash のキーを Symbol に変更したい場合、Ruby 2.5 で追加された Hash#transform_keys を使うとよさそう。

hash = { "name" => "homu", "age" => 14 }.transform_keys(&:to_sym)
# => {:name=>"homu", :age=>14}

こういうのをやりたいことは稀によくあるのでメソッド1つで出来るのはよさそう。
ただ、#transform_keys って名前がちょっと長いので transform_keys(&:to_sym) を呼び出すヘルパメソッドが欲しくなる。
ちなみに Hash#transform_keys! だと自身を破壊的に変更します。

Toyama.rb に参加してきた

タイミングよくちょっと近くへ行く用事があったの Toyama.rb に参加してきました。
勉強会の内容はみんなで雑談しながらもくもくして最後にやったことを各自で発表するような会です。

やったこと

  • Ruby 2.5 の導入
  • Ruby 本体をビルド
  • ファイルローカルなトップレベルメソッドを定義する gem の作成
    • もの自体は30分ぐらいでできたんですがちょっと問題があったので修正してから公開する予定

どんな場面で必要なのか?

成果発表の時に「どういう場面で必要なのか?」と質問されたので補足 わたし自身は以下のような場面で使いたいと思うことが多いですかね。

  • 長いメソッドから処理を切り出したい時
  • DRY を行う際に重複している処理を別メソッド定義したい時


まあこの辺りは Ruby に限らずどの言語でも言えるかと思います。

private メソッドやモジュール関数ではダメ?

private メソッドもダメではないんですが、Ruby の private は簡単に外部から呼び出すことができるので完全に隠蔽できないという問題があります(そもそも Ruby の private はそういう目的の機能ではないというのは置いておいて。
また、個人的には DRY を行うという目的でそのクラスに「余計なメソッド」を定義したくないというのもありますね。
モジュール関数も同様に「トップレベルで定数(モジュール)を定義すること」になってしまうので隠蔽化が不完全であったり、そのファイル以外でも影響してしまうという問題があるのでそれを避けたいです。

以上の理由から「ファイルローカルメソッドを定義する」専用の機構がほしいなーという感じです。
あと口頭で解答していた時は「Rails みたいなフレームワークではなくてライブラリとか小さい範囲のプロジェクトで使うことが多いかなー」みたいな話もあったと思うんですが、これ自体は別に大小は関係ないと思います(Rails でも上記で挙げたようなケースでは利用することはあると思いすし…多分…。

参加してみて

と、いうことで Toyama.rb に参加してきましたー。
都内だと地域.rb は結構頻繁に行われていますが、こういう地方の Ruby の勉強会は少ないので貴重ですね。
みんなでワイワイ言いながら作業していたので割といい刺激になりました。
いい意味で人数も多くなく(物理的な意味でも)他の参加者と距離が近くて話しやすかったです。
そういう意味では何かコードとかをスクリーンに映して、それを見ながらみんなで意見を言い合ったりして議論をしてみるのもいいんじゃないかなーと思いました。

Ruby の勉強会と言いつつ内容は Ruby に限らない(実際参加した今回も Ruby 以外の作業を行っている人は何人かいました)ので近くに住んでいてきになる方は参加してみるとよいのではないでしょうか。
さすがに毎回参加することは難しいですが、機会があればまた参加してみたいと思います。
運営の方々ありがとうございましたー。

あと、これは今回の勉強会に限ったことはないんですが、もくもく会でみんな作業しているとなかなかわからないことなんかを相談するのが難しいので「つらいときにあげる札」なんかがほしいなーと思いましたまる。
そしてもうちょっとしゃべりが上手くなりたい…

C++ で each_split

C++ で split というと『戻り値型とかどうするの』みたいな問題があるんですが、『それなら戻り値ではなくて each みたいに関数オブジェクトを渡せばいいじゃん』みたいな感じでやってみた。

#include <iostream>
#include <sstream>
#include <string>

auto each_split = [](auto src, char del, auto f){
    std::stringstream input(src);
    std::string output;
    while(std::getline(input, output, del)){
        f(output);
    }
};

int
main(){
    each_split("homu,mami,mado", ',', [](auto str){
        std::cout << str << std::endl;
    });

    return 0;
}
/*
output:
homu
mami
mado
*/

関数テンプレートを定義するのがめんどくさかったのでラムダ式を使っていますが、実装には関係ないです。

rbenv をインストールしたので覚書

今まで めんどくさかったので rbenv を使っていなかったのですが Ruby 2.5 をインストールする為に入れたのでその覚書。
rbenv とは?みたいなことは以下の記事を参照してください。

事前準備

すでに system の gem で bundle などをインストールしている場合は削除しておくと混乱が少ない。

$ gem uninstall bundler
$ gem uninstall bundle

あと ~/.bundle なども削除しておく。

rbenv のダウンロード

rbenv 本体は ~/.rbenv に保存

$ git clone git://github.com/sstephenson/rbenv.git ~/.rbenv

ruby-build のダウンロード

rbenv 以外に Ruby 本体をインストールするための ruby-build が必要なのでそれの保存。

$ mkdir -p ~/.rbenv/plugins
$ cd ~/.rbenv/plugins
$ git clone git://github.com/sstephenson/ruby-build.git

.bashrc に rbenv を読み込む設定を追記

rbenv をダウンロードしただけでは PATH などが設定されていないので .bashrc で設定を初期化するように追記。

export RBENV_ROOT="${HOME}/.rbenv"
if [ -d "${RBENV_ROOT}" ]; then
  export PATH="${RBENV_ROOT}/bin:${PATH}"
  eval "$(rbenv init -)"
fi

Ruby のインストール

rbenv installRuby のインストールし、rbenv global で使用する Ruby を設定する。

$ rbenv install 2.5.0
# rbenv intall や gem install した後に呼び出す
$ rbenv rehash
# global で使用する Ruby の設定
$ rbevn global 2.5.0
$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-linux]

rbevn rehash を忘れないように。

ruby -v で system の Ruby を参照してる場合に試すこと

rbenv versions の確認

rbenv versions で現在インストールしてる Ruby と設定されている Ruby を確認する。

$ rbenv versions
  system
* 2.5.0 (set by /home/worker/.rbenv/version)

rbenv local をリセット

可能性は低いんですが rbenv local --unset でローカルのバージョンを削除してみる。

$ rbenv local --unset
$ rbenv rehash

bundle install でハマった

bundle install した際に rbenv ではなくて system 側の gem を参照しておりはまった。

bundle の PATH を確認

bundle コマンドが rbenv 側のものを参照しているか確認

$ which bundle
~/.rbenv/shims/bundle

~/.bundle を削除

手元だとこれを削除したら改善しました。

参照