Railsで検索条件を指定するときはプレースホルダーではなくHashにしたほうがいい
Ruby2.6 + Rails5.2の組み合わせでMySQLのdate型のデータに↓で実行したときにクエリが異なるのはなんでだろ?
— Eiji Hachiya (@hachi_eiji) February 12, 2019
d = DateTime.parse('2019-02-13')
<中略>.where("some_date=?",d).where(some_date: d)→<中略> WHERE (some_date = '2019-02-13 09:00:00') AND `some_dates`.`some_date` = '2019-02-13'
文字列の場合
下記の場所で タイムゾーン考慮処理と、 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より抜粋
いつから入ったのか?
どうもこれっぽい。
本来はどうあるべきなのだろうか
MySQL5.7で検証しているが 2019-02-13
を 2019-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なのかわからないため変換ができない。
アプリケーションエンジニアができることはなんだろうか?
テストかけ
Classi Angular Night #1 を開催しての感想
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つを作ることが多い。そして小規模〜中規模のモノリシックなアプリの場合はユーザがアクセスするFQDNとAPIのFQDNが同じであることも多い。
課題
ただ、実際に開発するときには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' } };
最大の問題はブラウザ上に表示されているFQDNとAPIサーバの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 を積極的に導入していない。(社内でやっているところはある) 理由はいくつかあって、
技術書とか以外で2018年に買ってよかった(気がする)もの
何気なく去年のクレジットカードの利用状況を見てたのでまとめる。
バタフライボードA4
ミーティング行くときには大体携帯している。A4サイズでそこそこ書けるし、複数人でやっててもみんなで書くとかもできて汎用性は高い。Office Lensとの相性もいいので使い勝手はよい。会社のロゴ入りのホワイトボードも買ってもらった。赤字0.5mmのペンも出たので更に嬉しい。ホワイトボードの宿命なのか他のペンで書くとうまく消えなくてそろそろ買い替えたいなぁというお気持ちはある。ホワイトボードにこれを貼ったり、広げてA3ぐらいの大きさにしたりと汎用性は高い
バタフライボード2 A4 ノート型ホワイトボード(0.5mm極細マーカーセット付)
- 出版社/メーカー: バタフライボード
- メディア: オフィス用品
- この商品を含むブログを見る
単 −HITOE long wallet
もともと財布の中身を減らしたいなと思っていたので購入。iPhone8の幅より薄くてカード10枚ぐらいは入る。コンビニとかはApplePayでいいけどそれ以外はクレカ/現金が必要なので財布自体はまだまだ持ち歩くことが多い。カバンとかを持ってないときはズボンのポケットに財布をいれるのでズボンの傷み防止にもなってる。デザインもシンプルだしちょうどいい分量が入るので結構重宝してる。多分今の財布が壊れたら同じものを選択すると思う。確かAmazonでも買えるはず。
フィリップス ソニッケアー ダイヤモンドクリーン スマート 電動歯ブラシ ルナーブルー HX9964/55
2017年後半から今まで以上に健康に気を使うようになった。3ヶ月ごとに歯医者の定期検診とかにも行ってる。歯ブラシの種類によって自動でコースを設定してくれたりとモノグサな自分にはちょうどいいと思う。スマホアプリと連携しているが洗面台にスマホを持っていく習慣がないのでそこは使ってない。
フィリップス ソニッケアー ダイヤモンドクリーン スマート 電動歯ブラシ ルナーブルー HX9964/55
- 出版社/メーカー: PHILIPS(フィリップス)
- 発売日: 2017/09/01
- メディア: ホーム&キッチン
- この商品を含むブログを見る
東京西川エアーSX
朝起きると体が痛いことがしばしば起きててそろそろベッド買い替え時かなーと思っていたところに会社の人と睡眠の話になったので購入。今まではマットレス一体型のベッドだったので、ベッドフレームからまるごと買い替えで結構な出費(Macbook Proが買える値段ぐらい)だった。今までのベッドと違いすぎたため慣れるまでに2週間ぐらいかかったので、買ってしばらくは「いやーこれ失敗したかも」と思っていたが慣れると結構楽な気がする。Mi bandで睡眠時間を図ってみたら、確か深い睡眠が30分ぐらい延びていたはずなので数字にはしっかり出てるし、携帯のバイブ音でも起きれるのが今では起きれないことも増えた(単に歳のせいかもしれないが)。枕も合わせて買い直した。東京西川で軽く図ってもらって購入。一般的に睡眠時間は長いと言われているのでボーナスとかが出てる人は結構早めに投資しても元が取れるかもしれないがものによっては部屋のサイズを圧迫するので注意。
スタバのアンケートの体験がイケてない話
今日スタバに行ったときにレシートを貰ったらアンケートに答えるとドリンク1杯無料というのをやってたのでまぁいいか。とやってみたら体験がよくなかった。
手順は
- QRコードでアンケートサイトに遷移する ← わかる
- ログイン後カスタマー番号 17桁を自分で 入力する。ここで利用規約とか読んでもらう ← えー
- アンケートに回答すると 手書きでレシートに引き換え番号を記載して スタバへ行く ← ふぁっ!?
- スタバでレシートに記載してあるバーコードをスキャンしてドリンクゲット
いやいやいや... スマホで17桁の番号を入力するなんてないでしょ今頃。間違えるし。更に手書きで番号を記入するって。なぜにこんな体験悪いんだろうなぁ。普通にやるんだったら
- QRコードでアンケートサイトに遷移する
- ログイン後には入力欄にカスタマー番号がプリセットされている。利用規約とか読んでもらう
- アンケートに回答すると引き換え用のバーコードが表示されているので画像を保管する
- 保管した画像をスタバでスキャンしてもらってドリンクゲット
これだったらスマホだけで全部完結するし。ペン持ってなくてももう1杯もらえるし楽だと思うんだけど、何かしらのシステム制約があったんだろうなぁ。
今回は体験が悪かったので、最後の自由記入欄で「アンケートの体験が悪かった」と書いてしまったんだけどそういうサービスが結構あるなぁと改めて思った。自分はあまりアンケートに答えたりしないのはそれも1つある。色々CS用の情報欲しいとかあるのはわかるんだけど、そこが悪いとどうしてもサービスを継続して使いたいとか、フィードバックしたいというモチベーションが失われる。ユーザは「まぁアンケートに答えてあげてもいいかなぁ」という感情になりやすいので、きちんとした体験を提供しないと逆効果になってしまうのが残念だ。
とはいえ、明日も同様のことをやっていたら多分答えてしまう。スタバ1杯無料という誘惑は大きいのである
Angularでpugを使ってみた
ng add ng-cli-pug-loader
を使えばOK.
なぜ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
- libruby.2.0.0.dylib が見つからないと言われる
CONFIGURE_OPTS="--disable-install-rdoc --enable-shared" rbenv install 2.4.5
として、.vimrc
にset rubydll=<path>
を書いてみる- vimで
ruby print RUBY_VERSION
を実行すると、ライブラリの関数 rb_cFixnum をロードできませんでした
と言われる - ググると Patch 8.0.0163 で対応されている模様
- brew から入れてみるかーと思い立ったがコンパイルエラーが起きる(下にメモ)
- 辛くなってきたので、
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