Rubyのsort_byを理解する

Rubyのsort_byとsortでどう違うのかわかっていないので調べた。

Rubyのリファレンスマニュアルを読む

リファレンスマニュアルをみると下記のように書いていた。

sort_byの説明

Enumerable#sort と比較して sort_by が優れている点として、比較条件が複雑な場合の速度が挙げられます。 sort_by を使わない以下の例では比較を行う度に downcase が実行されます。 従って downcase の実行速度が遅ければ sort の速度が致命的に低下します。

p ["BAR", "FOO", "bar", "foo"].sort {|a, b| a.downcase <=> b.downcase }

一方、次のように sort_by を使うと downcase の実行回数は要素数と同じです。 つまり、その部分の実行時間は O(n) のオーダーです。

p ["BAR", "FOO", "bar", "foo"].sort_by {|v| v.downcase }

sort_byの挙動

sort_by を使うと downcase の実行回数は要素数と同じというのがわからなかった。
リファレンスには、以下のコードとほぼ同じ動作をします。と書いてあったのでsort_byのコードを読むことにした。 (ユニさんの解説も参考にしています。ありがとうございます。)

class Array
  def sort_by
    self.map {|i| [yield(i), i] }.
       sort {|a, b| a[0] <=> b[0] }.
       map {|i| i[1]}
  end
end

以下のコードを実行して、sort_byのコードを分解しながらみていくと理解できた。

p ["BAR", "FOO", "bar", "foo"].sort_by {|v| v.downcase }
# =>  ["BAR", "bar", "FOO", "foo"]

self.map {|i| [yield(i), i] }まで

self.map {|i| [yield(i), i] }では、yield(i)の値とiの値は以下のようになる。

# yield(i)の値
"bar", "foo", "bar", "foo" # (downcaseした値)
# iの値
"BAR", "FOO", "bar", "foo" # (元の配列の値)で

上記を二次元配列にしている。ここまでの返す値

# => [["bar", "BAR"], ["foo", "FOO"], ["bar", "bar"], ["foo", "foo"]] 

sort {|a, b| a[0] <=> b[0] }まで

sort {|a, b| a[0] <=> b[0] }では、
["bar", "foo", "bar", "foo"](downcaseした配列)の値を比べてソートしている。 ここまでの返す値

# => [["bar", "BAR"], ["bar", "bar"], ["foo", "FOO"], ["foo", "foo"]]

map {|i| i[1]}まで

map {|i| i[1]}では、
[["bar", "BAR"], ["bar", "bar"], ["foo", "FOO"], ["foo", "foo"]](ソート済みの二次元配列)から、元の要素だけを取り出している。 ここまでの返す値

# => ["BAR", "bar", "FOO", "foo"]

コードをひとつひとつ分解してみると、downcaseの実行回数は要素数と同じというのがわかった。

じゃあsortはいつのタイミングで使うのかという疑問が残ったけどそれはまた今度調べる。

XserverでPHPのバージョンを5.6から7.2にあげました

はじめに

Xserverで動かしているWordpressにて、PHPのバージョンアップを行ったのでその手順を紹介します。

ざっくりと上記の流れで対応しました。今回はPHPのバージョンあげただけで、WordPressプラグインWordPressのバージョンはあげていません。 テスト環境がない場合は手元で環境をつくるよいと思います。 Local by Flywheelなどがお手軽に環境を作れて便利です。 自分はテスト環境で上記の作業をして問題がないことを確認してから本番で作業を行いました。

1. プラグインおよびテーマファイルがPHP7に対応しているかチェック

PHP Compatibility CHeckerを使いました。 プラグインがPHP7.2に対応しているか、テーマがPHP7.2に対応しているかのチェックを行ってくれます。 はまりどころとしては、テスト環境でPHP Compatibility Checker が動かない問題がありました。

  • wp-cronが動かないと動かない
  • wp-cronが動かない理由はベーシック認証がかかっているため動かない

解決策は、htaccessに特定のサーバーのIPアドレスを許可することで解決できました。(XserverとかであればそのサーバーのIPアドレス)

書き方

Satisfy any →これがあると特定のIPアドレスが正しい or BASIC認証できたユーザーのどちらかという意味。

order deny,allow
allow from 111.22.333.44 ← サーバーのIPアドレス

deny from all

AuthUserFile "/home/.htpasswd"
AuthName "Input ID and Password"
AuthType BASIC
require valid-user

2. DBのバックアップを取る

バックアップをとりましょう。Xserverなら管理画面からphpmyadminにアクセスして取得できます。 無理ならsshでサーバーに接続してdumpファイルを取得します。

3. サイトのバックアップを取る。

zipコマンドでバックアップを取っておきます。

zip -rq  ZIPファイル名 バックアップしたいフォルダ/* -x "*.png*" "*.jpg*" 

pngやjpgファイルがあるとバックアップ量が膨大になるので、-xオプションで除外しています。

4. エラーログを記録できるようにしておく。

実はこの作業は本番環境では、行いませんでした。というのもWordPress エラーログ 記録などで検索すると、 wp-configファイルに下記を記述するようにするといった記事が多かったのですが、WordPress Codexで、WP_DEBUGについて調べると、通常は WordPress の開発環境の wp-config.php ファイル内で true に設定します。と書いてあったからです。

https://wpdocs.osdn.jp/WordPress%E3%81%A7%E3%81%AE%E3%83%87%E3%83%90%E3%83%83%E3%82%B0

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_DISPLAY', false );
define( 'WP_DEBUG_LOG', true );

5. PHPをバージョンアップ

Xserverの管理画面から行えます。バージョンアップに時間がかかるのではと懸念がありましたが、数秒で切り替わったので問題なく行えました。

6. 動作確認

PHP Compatibility CHeckerでテーマファイル以下のファイルが、PHP7.2に対応しているかどうかのチェックは通っていたのですが、動作確認してみるとcount関数を使用している箇所でいくつかの箇所でwarningが出ていました。適切に変更していくだけなのでそこまで手間ではなかったです。 cruw.co.jp

おわりに

PHP5系は2018年でサポートが切れてしまっているみたいなので、早めにバージョンアップすることをおすすめします。

stocker.jp

PHP7にすると速度もあがるはずです。実際にバージョンアップ前とバージョンアップ後ではPage Speed Insightのスコアも上昇していました。パフォーマンス面においても変わってくるので、バージョンアップやっていきましょう!

マネーフォワードさんの京都オフィスをお借りして、kyoto.rb開催しました。

毎回コアーキングスペースを借りて、ゆるくやっているkyoto.rbですが、今回はマネーフォワードさんのオフィスをお借りして、勉強会を開きました。

kyotorb.doorkeeper.jp


入り口が、京都の旅館みたいな感じで、テンションあがりました。

f:id:taca10:20190317142903j:plain


kyoto.rbでは、毎回もくもく会をしているのですが、参加者の人たちともっと話す機会があったら楽しいのではという意見があったので、今回から、自己紹介の時に気になるテーマや、キーワードを発表してもらい、その中で、良さそうなものを参加者の人達と議論するようにしてみました。
今回はこんな感じのテーマがでてきました。

f:id:taca10:20190317145838j:plain

シェアハウス募集してます。といった、全く技術と関係ない話もありましたw

人数が、18人集まったので、初心者と、中級者以上のグループに別れて話しました。
自分は、初心者グループにいたのですが、やってよかったことが、初心者グループのところでは、Scrapboxにページを作って、聞きたいことをたくさん書いてもらって、それについて、わかる人が答えていくっていうのをしました。ちょっと聞きにくいなってことでも、Scrapboxに書くのであれば抵抗感なくかけて、結構よかったなと思いました。
個人的にも良かったのが、オブジェクト指向の勉強を最近していて、自分でオブジェクト指向で書いたと思っているコードを、上級者の人にみてもらってFBしてもらえたので、かなり学びがありました。
悪かったところが、初心者グループが、12人いて、対話ができない人がちらほらでてきてしまっていたり、初参加の人や知識が十分にないひとからの質問を引き出せなかったことが課題でした。
次回は、6人ぐらいにグループわけして、グループ毎に進行役みたいな人を置いてやっていこうかなという感じでした。
懇親会でも、そのままマネーフォワードさんのオフィスをお借りしていて、お酒やジュースがマネーフォワードさんから提供されて最高でした。本当にありがとうございました。

次回は4月13日(土曜)に同じくマネーフォワードさんの京都オフィスで開催予定なので、気になる人は遊びにきてください!

kyotorb.doorkeeper.jp





IP電話を導入しました

半年ほど前に、IP電話を導入しました。
これから、導入する人がいれば、参考になるかと思い、ブログに書いておきます。
ip電話でも色々あると思いますが、今回はwebブラウザ、アプリケーション上で電話設定をしたかったのでクラウドPBXのみに絞っています。

biztel.jp



  • オペレーターさんが使用している電話が廃盤になっていてこれ以上電話回線を増やせない
  • 営業時間外も電話がなってしまう。
  • 電話しながらPC操作が不便(片手がふさがるので)

 

使用している電話が廃盤になっていて、電話回線を増やせないことはけっこう問題だったので、なんとかしないといけませんでした。

導入したサービスはCall Connectというものです。 

www.callconnect.jp


プランが複数ありますが、僕たちは、starterプラン(一番安いプラン)で始めました。
startemrプランでも、Google拡張機能を開発すれば、電話番号をキーにして、僕たちのCRMにリンクをとばせそうだったので、starterプランから始めることにしました。


何ができるようになるのか?

  • 回線をいくらでも増やせる。(お金はかかる)
  • 営業時間外に留守電を入れることが可能
  • PCで通話可能(両手が空く)
  • 僕たちが開発しているCRMに登録しているお客さんであれば、何を注文しているかすぐに調べることができる。
  • 番号の確認。転送
  • 着信ポップアップ

できないこと(プランを変更すればできるようになる)

  • 顧客情報登録、管理
  • 外部サービス連携(slack連携など)
  • キューイング機能(電話待ちの人にアナウンスを流す機能)
  • 通話メモ
  • etc

starterプランでは、顧客情報を登録することができない。つまり電話がかかってきたときにどんなお客さんか、わからないのですが、Google拡張機能を開発すれば、電話番号をキーにして、自分たちのCRMにリンクでとばせそうなので、starterで十分だと感じました。

料金( 3アカウントの場合で計算しています。)

初期費用
¥108円 (電話番号代) + ¥3,682×5(ヘッドセット予備も含め5個)

¥18,518

月々のランニングコスト
= ¥1,800×3アカウント +  (携帯への発信料金が全体の85%くらいを占めていたので、1ヶ月で携帯に発信する通話のみで計算しています 1083分×16.45(携帯に発信) +  3000分×1.25 (着信電話))
¥26,965

他社との料金比較
(きちんと調べたつもりですが、間違っていたらすいません。)

クラウドテレコール
初期費用
= ¥50,000(初期導入費) + ¥380 (電話番号代) + ¥3,682×5(ヘッドセット予備も含め5個)
¥68,790

月々のランニングコスト
= ¥10,000 × 3アカウント + ¥17,220 (発信)
¥80,220
楽天コミュニケーション社とのIP電話回線契約が必要

INNOVERA PBX
初期費用
= ¥100,000(初期導入費) + ¥2,500×3アカウント + ¥1,000 (電話番号代) + ¥3,682×5(ヘッドセット予備も含め5個)
¥126,910

月々のランニングコスト
= ¥1,000×3アカウント +1083分×15円(携帯に発信) + 20,000 (API連携)
¥39,245

機能が揃っていいて、日々のランニングコストも割と安いのはINNOVERA PBXでしたが、弊社のCRMと連携できるか微妙でした。かつUIが使いにくそうだなと感じたので、Call Connectを導入しました。



before_validation コールバック関数に引数を持たせる

やりたいこと

電話番号入力欄で後ろに半角、全角スペースがあった時に空白を取り除いてからvalidationを行いたい時に、before_validationを使って書きました。
電話番号だけじゃなくメール入力欄など他の場面でも利用できると思いmoduleを使って書いた時に詰まった部分があったのでどうすればいいか共有します。

とりあえずapp/models/user.rbに下記のように最初は書いていました。

app/models/user.rb
class User < ActiveRecord::Base
# ... 省略 ...

  before_validation :trim_space

  def trim_space
    self.phone.gsub!(/[ \p{blank}\r\t\n\f]/, "")
  end

end

gsub!なのですが!を使ってオブジェクトの状態を変更しておかないと返り値が全角、半角を取り除いた状態にならないので!を使うかもしくは下記のような書き方にしてみてください

app/models/user.rb
  self.phone = self.phone.gsub(/[ \p{blank}\r\t\n\f]/, "")
  return self.phone


これでまずは電話番号の入力欄だけは全角、半角を取り除けるのですが、他にも使いたい時に何度も同じコードを書かなければならないのでmoduleに書いて呼び出すようにしてみました。

app/model/validate_common.rb

module ValidateCommon
  extend ActiveSupport::Concern
  included do
    def trim_space
      self.phone.gsub!(/[ \p{blank}\r\t\n\f]/, "")
    end
  end
end
app/model/user.rb
class User < ActiveRecord::Base
  include ValidateCommon
  # ... 省略 ...

  before_validation :trim_space
end

ただこのままだと電話番号にしか使用出来ない状態なので、app/model/user.rbの:trim_spaceに引数を持たそうとした時につまりました。

app/model/user.rb
class User < ActiveRecord::Base
  include ValidateCommon
  # ... 省略 ...

   before_validation -> {trim_space("phone", "email")}
end

このようにして書けば引数をもたせる事ができるので、DRYなコードにできるかと思います。
app/model/validate_common.rb側も少し編集します。

module ValidateCommon
  extend ActiveSupport::Concern
  included do
    def trim_space(*target_columns)
      target_columns.each do |target_column|
        if self.send(target_column)
          self.send(target_column).gsub!(/[ \p{blank}\r\t\n\f]/, "")
        end
      end
    end
  end
end

条件分岐の部分なのですが、 self.send(target_column)がnilの場合つまり今回だと電話番号、メール入力欄が何も入ってこない場合だとNoMethodError Exception: undefined method `gsub!' for nil:NilClassになるので書いています。

before_validationで書く時は基本シンボルの書き方でメソッドを書いていたので結構詰まりました。
後、sendメソッドのレシーバの持っているメソッドを呼び出すというのも知らなかったので非常に学びがありました。
他にも良い書き方あれば教えて欲しいです。

2017年振り返り

2017年は色々あった。特に仕事を辞めてプログラミングを始めた事が1番大きな事だった。今回はプログラミングを始めてどうだったか今の思いと振り返りをしていこうと思う。

なぜ仕事を辞めてプログラミングを始めたか?

自分は流れのままに生きてきてしまっていた気がする。学生時代も学びたい事もなく、とりあえず周りが大学進学してるし大学行こうって行ったし、そんな訳だから就職も特にしたい事もなく、なんとなく就活やってまあ受かったし行くかっていうよくありがちなダメな若者だった。ただ、就職してから、仕事も全く楽しくないし、この人生でいいのか?このまま流れのまま生きて行っていいのか?って自問自答していて、やっぱり自分のやりたいこと、楽しめることがしたいってなって前からプログラミングに興味があったのでそっちの道に進もうと思った。

やってみてどうだったか

プログラミングを始めたのは本当に良くて、勉強とか好きではなかったけど、今は休みがあったら自分の勉強としてコード書ける事が楽しいと思えるようになった。
ただ、挫折もあった。3ヶ月くらいかかってとりあえず動くものを作って、当たり前だけど、全然だなーって感じで、これ使う人いないよなって感じだった。そこから特に作りたいものなかったので、どうしようって感じだったし、2ヶ月くらい経ってまた作ろうってなったけど、今度は機能が多くなりすぎて、無理だ、、ってなって途中でやめてしまった。
反省点として本当に必要な機能からつくるべきだったし、最低限の機能を作って公開すれば良かったなと思う。

現在

今はアルバイトでエンジニアの仕事を2箇所でやっていて、フロントエンド中心に開発をしている。転職活動したけど、内定が出たのはそこまで行きたい企業じゃなかったし、行きたかった企業は落ちたので、アルバイトから始めて社員になるのが理想かなって思い今は技術力つけるために頑張っている。
ネットとか見てると、未経験で半年勉強してエンジニアなったとか、勉強始めて10ヶ月でエンジニアになった記事が結構あってそれに比べると自分はまだまだだなって思って辛い気持ちになったけど、今年は学ぶって事は楽しいって思えた事が収穫でエンジニアで生きて行く上で1番大事な事だと思うので、来年はもっと技術を学んでいこうと思っている。
最後に来年の目標として4つ

  • コードをしっかり書く時間を取る。
  • webアプリケーションを作って運用する。
  • アウトプット(ブログやLT)をする。
  • 定職につく。

これからはてなブログで技術の事も書いていこうと思うのでよろしくお願いします。