Railsで検索条件を指定するときはプレースホルダーではなくHashにしたほうがいい

文字列の場合

下記の場所で タイムゾーン考慮処理と、 DateTime#to_s を呼び出しているので時間まで残る

def quoted_date(value)
  if value.acts_like?(:time)
    zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal

    if value.respond_to?(zone_conversion_method)
      value = value.send(zone_conversion_method)
    end
  end

  result = value.to_s(:db)
  if value.respond_to?(:usec) && value.usec > 0
    "#{result}.#{sprintf("%06d", value.usec)}"
  else
    result
  end
end

rails/quoting.rb at e53430fa9af239e21e11548499d814f540d421e5 · rails/rails · GitHub で呼ばれているメソッドより抜粋

Hashの場合

こちらは DateTime#to_date を呼び出しているので、時間は切り落とされて日付だけが返却される

def cast_value(value)
  if value.is_a?(::String)
    return if value.empty?
    fast_string_to_date(value) || fallback_string_to_date(value)
  elsif value.respond_to?(:to_date)
    value.to_date
  else
    value
  end
end

rails/date.rb at e53430fa9af239e21e11548499d814f540d421e5 · rails/rails · GitHubより抜粋

いつから入ったのか?

どうもこれっぽい。

github.com

本来はどうあるべきなのだろうか

MySQL5.7で検証しているが 2019-02-132019-02-13 00:00:00 にしても検索結果はヒットする。しかし、しかし、MySQL5.6時点の MySQL :: MySQL 5.6 リファレンスマニュアル :: 9.1.3 日付リテラルと時間リテラル を見ると 2019-02-13 == 2019-02-13 00:00:00 とはどこにも書いてない。Date型は日付だけを持っており時間を持っていないので当たり前だが。

DBという固く設計・実装すべき方に合わせるほうが良いので、プレースホルダーの方を日付型にすれば良いと思ったが、下記で話をされている通り、そちらはカラムの型を持っていないのでDatetimeなのかDateなのかわからないため変換ができない。

github.com

アプリケーションエンジニアができることはなんだろうか?

テストかけ

Classi Angular Night #1 を開催しての感想

connpass.com

Angular日本ユーザー会との共催で開催しました。関係者の皆様改めてお礼申し上げます。

身内だからというのも若干あるんですが わたる (@kasaharu) | Twitter が事例を話してくれて、派手さはないけど、正しく、奇をてらわずに作ってくれていることへ対してみんなが反応してくれていたのが一番印象に残りました。

難しいのはわかるけどそれをやらないと何年後かの自分たちが絶対に苦労するのは目に見えている(実際現在進行系で苦労してるし...)ので、自分たちがやっていることが決して間違っていないということを再認識しました。そういうのがきちんと評価したいし評価されたいですね。

帰り道にlacoさんとお話していて(酔っていたので若干異なってるかもしれないですが...) 仕事でAngularがこれだけ使われていて安心して使える。そしてどういうケースはAngularが適しているのかという事例を出していきたいですねーという話をしてくださいました。

仕事という実戦の中でエンジニアの技術力は磨かれていくと思っているので(もちろんそれだけではないけど)、仕事で個人の技術力上げる→ よいプロダクトを提供する→その知見をコミュニティに還元する みたいな循環ができれば、個人、企業、コミュニティともにwin-win-winになると思っています。

一例だと今のリプレイスプロジェクトはmultiple projects を採用してるのですがこれはng-japan 2018(at Google)で知ったものです。今はmonorepoで作るというのと将来的にはマイクロサービスとして分割していく可能性は非常に高いのでそこを意識して機能ごとにパッケージは別けたいというのはシステム作成時の制約として決めていました。

しかし、当時は最良の解決策を持っていませんでした。発表を聞いた直後にGoogleの食堂で二人で「いける?」「いけそう」というのを試してたのは今でも覚えています。

また、そのときに初めてAngular日本ユーザー会へ会社がスポンサーをしてくれたのですが、そこでうちに興味を持ってくれた学生が4月から社員として入ってくれる事になりました。もちろんAngular以外で作っているシステムもあるのでそういうのも知った上で入ってくれますし、大変なところも承知した上で入ってくれます(多分)。

そういう事例を作って循環の輪を作っていければいいなぁとは思います。

Angularでのローカル開発時に便利なProxyConfig

前提条件

ローカル環境での開発時にはMockAPIを返すサーバと実際のAPIサーバの2つを作ることが多い。そして小規模〜中規模のモノリシックなアプリの場合はユーザがアクセスするFQDNAPIFQDNが同じであることも多い。

課題

ただ、実際に開発するときにはAngularはWebpackのDevServerが4200ポートを使っていて、Rails,NodeなどのAPIサーバは他のポートを使わざるを得ない。これまで自分が作ったWebサービスではMockAPIと実際のAPIサーバのアクセスをどう切り替えていたかというと、environment.mock.ts とかのファイルにURLのフルパスを書いていた。npmから起動するときに ng serve -c=mock とかでmockのconfigurationを読むようにしていた。

export const environment = {
  production: false,
  // api server
  api: {
    url: 'http://127.0.0.1:3000/api'
  }
};

最大の問題はブラウザ上に表示されているFQDNAPIサーバのFQDNが異なっていた。可能な限り実際のサーバ構成とローカル開発の環境は同じであるほうがいらないトラブルを減らせる。あとはMock用の設定とローカル開発用のAPIサーバの設定とかで個別でファイルを作っていた。

解決策

https://angular.io/guide/build#proxying-to-a-backend-server に書いてあることがすべて。 proxy.conf.jsonを作ってからangular.jsonに読み込ませることで設定終了という非常にお手軽。

{
  "/api": {
    "target": "http://127.0.0.1:3000",
    "secure": false
  }
}

in-memory-web-apiをつかわないのか?

自分はあまりAngularのチュートリアルでも出てくる GitHub - angular/in-memory-web-api を積極的に導入していない。(社内でやっているところはある) 理由はいくつかあって、

  1. Chrome Dev ToolでもわかるようにHTTPリクエストを走らせたい
  2. 本番でフロントエンドのコードとAPIのコードが別れているのであれば開発時でも別けたいし、開発のためだけにコードを入れると問題の切り分けが面倒
  3. Mockを1つ追加したいだけなのに毎回コンパイル走るのは遅いし、Angularに慣れてない人にもMockは触ってほしいのでAngularをあまり意識させたくない
  4. swaggerや agreed などドキュメント+Mockサーバで管理したほうが便利だと思う(この辺はまだ手を付けれていない)

技術書とか以外で2018年に買ってよかった(気がする)もの

何気なく去年のクレジットカードの利用状況を見てたのでまとめる。

バタフライボードA4

ミーティング行くときには大体携帯している。A4サイズでそこそこ書けるし、複数人でやっててもみんなで書くとかもできて汎用性は高い。Office Lensとの相性もいいので使い勝手はよい。会社のロゴ入りのホワイトボードも買ってもらった。赤字0.5mmのペンも出たので更に嬉しい。ホワイトボードの宿命なのか他のペンで書くとうまく消えなくてそろそろ買い替えたいなぁというお気持ちはある。ホワイトボードにこれを貼ったり、広げてA3ぐらいの大きさにしたりと汎用性は高い

単 −HITOE long wallet

もともと財布の中身を減らしたいなと思っていたので購入。iPhone8の幅より薄くてカード10枚ぐらいは入る。コンビニとかはApplePayでいいけどそれ以外はクレカ/現金が必要なので財布自体はまだまだ持ち歩くことが多い。カバンとかを持ってないときはズボンのポケットに財布をいれるのでズボンの傷み防止にもなってる。デザインもシンプルだしちょうどいい分量が入るので結構重宝してる。多分今の財布が壊れたら同じものを選択すると思う。確かAmazonでも買えるはず。

www.makuake.com

フィリップス ソニッケアー ダイヤモンドクリーン スマート 電動歯ブラシ ルナーブルー HX9964/55

2017年後半から今まで以上に健康に気を使うようになった。3ヶ月ごとに歯医者の定期検診とかにも行ってる。歯ブラシの種類によって自動でコースを設定してくれたりとモノグサな自分にはちょうどいいと思う。スマホアプリと連携しているが洗面台にスマホを持っていく習慣がないのでそこは使ってない。

東京西川エアーSX

朝起きると体が痛いことがしばしば起きててそろそろベッド買い替え時かなーと思っていたところに会社の人と睡眠の話になったので購入。今まではマットレス一体型のベッドだったので、ベッドフレームからまるごと買い替えで結構な出費(Macbook Proが買える値段ぐらい)だった。今までのベッドと違いすぎたため慣れるまでに2週間ぐらいかかったので、買ってしばらくは「いやーこれ失敗したかも」と思っていたが慣れると結構楽な気がする。Mi bandで睡眠時間を図ってみたら、確か深い睡眠が30分ぐらい延びていたはずなので数字にはしっかり出てるし、携帯のバイブ音でも起きれるのが今では起きれないことも増えた(単に歳のせいかもしれないが)。枕も合わせて買い直した。東京西川で軽く図ってもらって購入。一般的に睡眠時間は長いと言われているのでボーナスとかが出てる人は結構早めに投資しても元が取れるかもしれないがものによっては部屋のサイズを圧迫するので注意。

www.airsleep.jp

shop.nishikawasangyo.co.jp

スタバのアンケートの体験がイケてない話

今日スタバに行ったときにレシートを貰ったらアンケートに答えるとドリンク1杯無料というのをやってたのでまぁいいか。とやってみたら体験がよくなかった。

アンケート
スタバのアンケート

手順は

  1. QRコードでアンケートサイトに遷移する ← わかる
  2. ログイン後カスタマー番号 17桁を自分で 入力する。ここで利用規約とか読んでもらう ← えー
  3. アンケートに回答すると 手書きでレシートに引き換え番号を記載して スタバへ行く ← ふぁっ!?
  4. スタバでレシートに記載してあるバーコードをスキャンしてドリンクゲット

いやいやいや... スマホで17桁の番号を入力するなんてないでしょ今頃。間違えるし。更に手書きで番号を記入するって。なぜにこんな体験悪いんだろうなぁ。普通にやるんだったら

  1. QRコードでアンケートサイトに遷移する
  2. ログイン後には入力欄にカスタマー番号がプリセットされている。利用規約とか読んでもらう
  3. アンケートに回答すると引き換え用のバーコードが表示されているので画像を保管する
  4. 保管した画像をスタバでスキャンしてもらってドリンクゲット

これだったらスマホだけで全部完結するし。ペン持ってなくてももう1杯もらえるし楽だと思うんだけど、何かしらのシステム制約があったんだろうなぁ。

今回は体験が悪かったので、最後の自由記入欄で「アンケートの体験が悪かった」と書いてしまったんだけどそういうサービスが結構あるなぁと改めて思った。自分はあまりアンケートに答えたりしないのはそれも1つある。色々CS用の情報欲しいとかあるのはわかるんだけど、そこが悪いとどうしてもサービスを継続して使いたいとか、フィードバックしたいというモチベーションが失われる。ユーザは「まぁアンケートに答えてあげてもいいかなぁ」という感情になりやすいので、きちんとした体験を提供しないと逆効果になってしまうのが残念だ。

とはいえ、明日も同様のことをやっていたら多分答えてしまう。スタバ1杯無料という誘惑は大きいのである

Angularでpugを使ってみた

ng add ng-cli-pug-loader を使えばOK.

github.com

なぜpug?

HTMLをそのまま書くのもいいかなぁと思ったけど、pugで楽にかけないかなと思って使ってみた。

これが

<table class="l-tasks">
  <thead>
  <tr>
    <td></td>
    <td>タイトル</td>
    <td>説明</td>
    <td>期限</td>
    <td>削除</td>
  </tr>
  </thead>
  <tbody>
  <ng-container *ngIf="(tasks$ | async) as t">
    <ng-container *ngIf="t.data.length; else notFound">
      <tr *ngFor="let task of t.data">
        <td class="m-done"><input type="radio"></td>
        <td class="m-title">{{task.title}}</td>
        <td class="m-description">{{task.description}}</td>
        <td class="m-due_date">{{task.due_date}}</td>
        <td class="m-delete">
          <app-button text="削除" (onClick)="delete(task.id)" buttonSize="small" buttonType="danger"></app-button>
        </td>
      </tr>
    </ng-container>
    <ng-template #notFound>
      <td colspan="4" class="no-task-message">data not found</td>
    </ng-template>
  </ng-container>
  </tbody>
</table>

こうなって比較的スッキリ

table.l-tasks
  thead
    tr
      td
      td タイトル
      td 説明
      td 期限
      td 削除
  tbody
    ng-container(*ngIf="(tasks$ | async) as t")
      ng-container(*ngIf="t.data.length; else notFound")
        tr(*ngFor="let task of t.data")
          td.m-done
            input(type="radio")
          td.m-title {{task.title}}
          td.m-description {{task.description}}
          td.m-due_date {{task.due_date}}
          td.m-delete
            app-button(text="削除", (onClick)="delete(task.id)", buttonSize="small", buttonType="danger")
    ng-template(#notFound)
      td.no-task-message(colspan="4") data not found

微妙だったところ

普段開発でIntellij IDEAを使っているが、リファクタリングとかで Find Usage ( option + F7) で使っている変数を探すときが多々あるが、pugにするとそこが動かない。まぁそうだよなぁというのはある。pugの便利機能をどこまで使うか?と言われるとうーん...と悩んでしまうので難しいところ。Angularの記法で書いたほうが健全な気がする

MacVim-kaoriyaでrubyが認識できないときの対応

mojaveのMacをゼロからセットアップしたときにハマった。

なお、使っていたバージョンは MacVim-KaoriYa 20180324

  1. libruby.2.0.0.dylib が見つからないと言われる
  2. CONFIGURE_OPTS="--disable-install-rdoc --enable-shared" rbenv install 2.4.5 として、.vimrcset rubydll=<path> を書いてみる
  3. vimruby print RUBY_VERSION を実行すると、 ライブラリの関数 rb_cFixnum をロードできませんでした と言われる
  4. ググるPatch 8.0.0163 で対応されている模様
  5. brew から入れてみるかーと思い立ったがコンパイルエラーが起きる(下にメモ)
  6. 辛くなってきたので、brew install ruby@2.0 && brew link ruby@2.0 --force で古いRubyを入れる

systemが変わるから正直イヤだけど...他で困ったら考えるか

コンパイルエラー内容

Last 15 lines from /Users/hachi/Library/Logs/Homebrew/macvim-kaoriya/03.make:
   
<長いので中略>
** BUILD FAILED **


The following build commands failed:
    CompileC /tmp/macvim-kaoriya-20181107-81261-1qv228j/src/MacVim/build/MacVim.build/Release/MacVim.build/Objects-normal/x86_64/MMPreferenceController.o /tmp/macvim-kaoriya-20181107-81261-1qv228j/src/MacVim/MMPreferenceController.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler
(1 failure)
make[1]: *** [macvim] Error 65
make: *** [first] Error 2