時空を超え始めた
今日は記念すべき日かも知れない。というのは、こんなことを真顔で言うととうとうあいつも発狂したかと思われそうなんだけど、今日ようやく潜在能力を覚醒させ時間と空間を瞬時に移動することに成功した。
どういうことか、少し詳しく話したい。
私はいま、普段8階建のビルの7階のオフィスで仕事しているんだけど、小さいビルなのでエレベーターが1つしかない。そして古い建物だからエレベーターも遅く、7階から降りる際に適当な階でもたついてるとけっこうな時間を食う。
今日も例によってそんなわけで、私は7階から薄暗い屋内階段を使って降りた。 ちょうどそのとき非常に複雑で、面倒なことを考えていた。というか、そういうことで頭がいっぱいになったときに気分転換にコンビニに行く。
7階から降りると電気がついていない6階になる。 6階から降りると、当然5階になる。
しかし今日は違った。 頭の中を質めんどくさいことが駆け巡った状態で、歩き慣れた階段を脱力状態で降りていた。 7階の次は6階だった。6階の次は・・・、2階だったのである!
一瞬マジで5階の人がふざけて「2」のマークを壁の「5」の上に貼っつけたんだと思って確認したが、やはりこれは2階だった。このビルは2階にゆうちょのATMがある。そして昼の時間帯でATMには列ができていた。 そんなバカなと思いながら2階から降りると、、、、、、やはり1階だった。
ははーんそういうことか、と。私はいかなる超常現象も宗教も迷信も、付け加えれば科学も政治も経済も信じてないのでこれは単に頭の中が考え事でいっぱいの状態で、階段を降りるといった半無意識的で負担のかからない運動だと案外階段も一瞬で感じるということかと理解した。例えば歩きスマホをやってる人が熱中しすぎて自分が思ったよりも歩いてしまって、柱か何かにぶつかるようなものなんだと。
ということで、この日はそのあとも何度か「マジでめんどくさいXXさんを避けるにはどうすればいいか」「2000万くらいほしいけどどうすればいいか」「地上で一番始めに動き始めたものはどうやって動いたか」など難問を考えながら階段を降りてみた。ところが、もう二度とワープ現象は発生しなかった。
そこで最後に、ダメでもともとで「7階の次は1階にワープしろ!」と頭で念じながら降りてみた。 するとどうであろうか。7階の次は1階になったのである。
というわけで何が言いたいかというと、意志の力はどうでもいい現実は捻じ曲げられる(現実歪曲空間)という話と、私が超能力キャラに唐突にはまりだしたという2点である。
カンマ入ったり全角入った数字(の文字列)を数値にしたいやつ
タイトルのやつ自体はrubyだとカンタンなんだけど、適当にこういうクラス作って
module Bacchus class IntegerFilter def initialize(num_string) @num_string = num_string end def convert unless @num_string.is_a?(String) return @num_string end @num_string = @num_string.tr("0-9", "0-9").gsub(/,|、|,/, '') n = @num_string.to_i if n == 0 && !['0', 0].include?(@num_string) @num_string else n end end end end
数字っぽい文字列なら数値にして、それ以外はそのまま戻すコードを用意しておく。 (なんか汚いな、最近エレガントな興味書こうみたいな意欲が全くなくなった気がする...。'hoge'.to_iが0になるの、チェックの仕方これしかないのかな)
問題はウェブアプリケーションでこれをどこに置きますかっていう話。
某クラウド家計簿サービスはこういうのとか、空文字列をnilに変換する処理をわりとRackなのかスーパークラスなのか、とにかく低層の共通処理においてしまったがためにわりと標準の仕組み的にうまくいかないことが増えて負債化してしまったらしい。
一見モデルのbefore_save
あたりに置けば良さそうだけど、Int型のItem#price
とかのカラムに文字列'12,345'を突っ込んだ時点でRailsが12にしちゃうのでそれはできない。
というわけでcontrollerで
def hoge_params _params = params.require(:hoge).permit(*Hoge::ACCESSIBLE_ATTRIBUTES) [:price, :item_number].each do |attr| _params[attr] = Bacchus::IntegerFilter.new(_params[attr]).convert end _params end
が無難かなぁ・・・と思いつつこれが頻出するのは嫌だよね。 なんか良いやり方思いついたら教えて下さい。
簡潔かつセキュアなログインフォーム
ログインフォームは一般的に「メール」「パスワード」の組み合わせをSSLで送受信して適当にセッションを付与するのが基本なのだろうけど、以下のような問題点がある。
スマホ時代にパスワード入力は鬱陶しい
最大のポイント。一般論として英数字記号などが適度に含まれててある程度の長さを持っているパスワードのほうが安全だけど、スマホはキーボード切り替えが面倒なのでそんなパスワードを設定してもらうのを期待するのは厳しいのでは。
どうせ使い回すのでユーザのパスワードが他サービスから漏れたら不正アクセスされる
「ぱす」とかで入力候補に使い回しパスワードが出るようにしてる人もいるし、「他サービスから漏れたのが原因だからうちのサービスで被害が出たのはこっちの責任じゃない」というのは法的には成立しても親切ではない。
復旧手段が脆弱
パスワードを忘れた場合、メールアドレス宛に再発行URLを送るのが一般的実装だけど、メールは盗聴可能であるという前提を持つと再発行URLは脆弱。このあたりはいろんなサイトで判断が分かれてるところだけど、再発行URLからさらに秘密の質問的なものを踏ませないと安全ではない。それもどこまで効果があるか疑問。 だいいち秘密の質問にまともに回答してますか? 僕「test」とか適当に入れてるけど。
というわけで対抗策をいくつか考えてみた。
セッションを破棄せず一つのブラウザでしか使わせない
ログイン・ログアウトの概念をなくす。個人のスマホでサービスが使われるという前提があるならわりと安全だと思う。ブラウザをまたぎたい場合は最初に使ったブラウザからソーシャルアカウントと連携させる。
まぁソーシャル連携は案外敷居が高いので、クロスブラウザでは使えないサービスになるけど。
批判
セッションを破棄しないのは危険。なぜなら個人の端末で使われるとは限らないため。またシークレットモードとかの扱いも面倒(シークレットモードで使ってもらうと困るから)だし、そもそもクッキー消されたら一生ログインできなくなっちゃう。
SMS
メール・パスワードの組み合わせではなく電話番号・ワンタイムパスワードにする。 SMSって盗聴されるんですかね?(ちょっとよくわかってない) これならパスワードいらないし、別メール飛ばすよりもiPhoneだと上からぴょっと出るからけっこう楽だと思うけど。
批判
SMSが届く電話番号がないとサービスが使えない=PCのユーザビリティ低下+MVNOでSMS使えないケースもある。 またSMS実装はコストがかかる。
個人的にはこれLINEに送れるといいと思う。そのままLINEで問い合わせ対応できるようにとか・・・。ないのかな?
安全かつシンプルがいいとも限らない
ただ、最大の問題はユーザが電話番号ログインとかワンタイムパスワード慣れしてないので、いま「電話番号だけでログインできます」というサービスを作っても付いてこれない気がする。
ハナマサの神対応
最近引っ越したのだが以前の家にも今の家にも近所にハナマサ(花正)がある。 ハナマサは独り身には量が多かったり業務用のものが多いので少し不便な点もあるのだけど、安いので助かっている。
ただ、残念なことが一つあった。 引越前の店舗には「鶏ヤゲン軟骨」が置いてあったのだけど、今の近所の店舗には置いてないのだ。
これは鶏一羽から一欠しか取れない、貴重なわりに安い軟骨。調理法は極めて簡単でフライパンで適当に焼いて塩コショウをかけるだけでサイコーのつまみになるので前の店舗時代から相当量を買っていた。
しかし引越し先には存在しない。そこでダメ元でハナマサのHPから要望を出してみた。
いつも大変お世話になっております。 赤坂店の近所から池袋店の近所に引っ越したのですが、赤坂店で販売しており 大変好物だった鶏ヤゲン軟骨が池袋店で取り扱っておらず、非常に残念に思っております。 池袋店で鶏軟骨の販売を心待ちにしておりますので仕入れのご参考にしていただければと思います。
翌日すぐに返信が来た(返信いらなかったけど)。
平素は花正をご愛顧頂き誠に有難う御座います。メールを 拝見しました。ご不便をお掛けし申し訳御座いません。 赤坂店では解凍販売させて頂いておりますが池袋店では 冷凍販売をさせて頂いております。ご要望を池袋店に報告 した結果明日より解凍販売もさせて頂くとの報告を受けて おります。何卒御利用頂けます様お願い致します。
神対応ですね。ハナマサ。ちなみに僕は1年に1,2回くらい、どうしても食べたいものにはわりとこうやって直接問い合わせで要望出してるんですが、以前「光◯」というラーメン屋が醤油味を辞めた時に「どうしても醤油味が食べたいから復活してほしい」と送った時は何の反応もなかったです。ちなみに「光◯」は最近破産したらしいんですが、破産するからいちいち対応できないのか、はたまたその逆か、興味深いものです。
ドキュメントを書けば?
これ。当然私は「いいこと言ってるなぁ」派。
これに寄せられるいくつかのコメントについて。
新規サービスをテスト書かずに立ち上げた奴らが創業者利益でがっぽり勝ち逃げして後から入った真面目な若者が負債による圧迫で「こんな機能追加するのにこんなにかかるの?」みたいに詰められてる姿が目に浮かぶ
それはテストを書くかどうかの話より、技術的負債を積み上げ続けたまま勝ち逃げする人たちの批判ですかね。
テスト書いてあっても技術的負債が積み上がったプロジェクトだってあるし、テストはないけどプロジェクトのメンテナンスちゃんとしてるプロジェクトもあるし。
一般論としてテスト書いてる現場のほうがメンテナンスちゃんとしてる傾向にはあるだろうけど、直接の論点とは違うんじゃないですかね。
インフラとかライブラリ系はテスト書きまくっとくと安心感が凄い。簡単なテスト書いてるだけでも結構自分の思考の漏れが見つかる事多いしね。フロントはあんま関わらないからノーコメント。
そうそう、ライブラリは書いたほうがいいし書くべきなんですよ。読みづらいコードも増えるし。
でも、フロントはそんなにいらない。
テストを書くか書かないかは自由かもしれんけど動く事を保証するための動作確認をテストと言っているなら他人に説明するためのシナリオは必須だと思う
シナリオとは? TDDのシナリオですかね。私はシナリオとかスペックという概念(仕様とテスト実装、両方を表すようで両方ともと違う)は無駄な中間概念だと考えているので嫌いです。
普通にドキュメント書いて、あとminitest書けばと思う。
「よくできたRSpecはテスト自体が英語で仕様を表す」みたいなのって水素水とか、声がけで水の結晶が〜みたいなレベルのカルト思想だと思う。
共感してる人は元記事の矛盾に気づいてるのだろうか?"ユーザーに価値を届ける"事には"ユーザーの価値を壊さない"事も含まれる筈なのだが;多分これが許されるのって最初から完璧なコードが書けて修正不要な場合だけ
テストを書けば確かに価値を壊すリスクは減るとは思う。これはビジネス側の解釈なのでケースによるけど、壊すだけの価値がある時点でテスト書けばよい。たいていのスタートアップは誰も使わないとかのレベルで停滞してるわけで。
自称出来る人のドキュメントなしテストコードなしのソースを引き継いだ経験のある人はまったく賛同できないだろうな。それやるならお前が一人で開発して一人で墓まで持ってけよって。
そう、これなんだけど、「テストが書かれていてドキュメントがない」状況とその逆だったら、ドキュメントがあってテストがないほうが全然よくない? つまり、テスト書かなくてもドキュメントがちゃんと用意されてればこの問題はある程度緩和されると思う。実際、ここの仕様の意味がわからないというときにテスト読むって少し無理な注文だと思う。(少なくても教科書的に、「あぁこのメソッドはこういう振る舞いが期待されてるのか」と納得した経験はかなり少ない)
いいエンジニアはコメントを書かない、というかコメントにはBad Codeの臭いがあるのは確かにそうなのだけど、コメントを書かないにしてもドキュメントは書いたほうが良い。
例えば本番データを開発環境に投入するコマンドとか、キャッシュクリア、デプロイ手順(capistranoで自動化されてるにせよ普通にrollbackしようとしたら変なエラーが起きたりとかけっこうある)、ビジネスロジック上わかりづらいが重要な違いがあるメソッド(`Article.published`と`Article.visible`とか)。
ちなみに、昔自分がやったプロジェクトでRSpecでゴリゴリテスト書きまくってカバレッジも優秀なRailsアプリがあったのだけど、紆余曲折あって運営元が変わって、2年後くらいに見に行ったら一切実行されてないテストと「ドキュメントが一切ないクソプロジェクトなんですよ」という現場の声にショックを受けたので、今やドキュメント85, テスト15くらいの割合で考えてる。
2年連続でインフルエンザになる
確か去年はA型だったんですが、今年はB型になりました。節約のため高速バスで仙台に帰った翌日くらいから体調が悪くなったんで、バスの中で感染ったのだと思います。
しかし、今回のインフルエンザはけっこう辛かったです。去年ももちろん辛かったは辛かったんですが、高熱が出て翌日に病院に行ったらすぐにインフルエンザ陽性が出たのでイナビルという鼻から吸引するお薬をキメたら8時間くらいで回復したんですが、
今回のは熱が出て翌日に病院に行ったらインフルエンザ陰性。ただの風邪かと思いそのまま外で仕事してたらどんどん悪化したので2日後にまた病院に行ってようやく陽性反応が出ました。これは運が悪かったらしい。
で、今回はイナビルじゃなくて例のタミフル。これを5日間飲むらしいんですが、回復がけっこうゆっくりで未だ気持ち悪いんですよね・・・。
金曜 朝にバスから降りる(この時点で感染?)
土曜 なんともなし
日曜 38度くらい熱が出る
月曜 インフル陰性
火曜 仕事するも相当悪化
水曜 ずっと寝てる+夜間の救急病院に行ってインフル陽性
木曜 ずっと寝てる
金曜 だいたい寝てる
というわけでまだ指先の動きが悪かったり足元がふらついたりします。
これ去年も思ったんですが、病院代と薬代、電気毛布買ったりで軽く1万超えてるし仕事しなかった損害分も考慮したらけっこうな打撃なので、高速バス・新幹線って時間的なものだけでなく乗るにしても季節考えるとか重要ですね。
Rails初心者に知ってほしい本当の脱初心者の方法
この記事読みました。まぁ感想を一言で言えば
まぁ言いたいことを順を追って言っていきます。
ロジックで用いる具体的な数字は定数に格納する
これは半分正しいですが半分間違いです。マジックナンバー死すべしみたいな人が短絡的にあらゆる数値をそのクラスの定数にしちゃったりしてますが、程度問題です。
例えば
@articles = Article.where(...).page(params[:page]).per(PAGINATE_ITEM_LENGTH)
みたいなやつ。こういうの、結局ここでしか使わないから修正箇所1個だけなんですよね。だからベタ書きでもいいと思います。 定数にしたほうがいいやつってむしろこういうやつで
$li_margin_top : 50px; ul { margin-top: -$li_margin_top; } li { margin-top: $li_margin_top: }
まぁ変更する頻度が高い、どこかの数字と連動している、とか。
CRUDとルーティング
ある時期まではとりあえずresourcesが使えないか考えるべしというのは姿勢としてはいいのですが、現実問題としてわりと厳しいです。 例えばあるレストランの詳細ページの中に、地図を表示するページとかメニューを表示するページとかいろいろ作る時
resources :restaurants, only: %w[show] do resource :maps, only: %w[show] resource :menus, only: %w[index] resource :reviews, only: %w[index] end
みたいな。まぁreviewsとかはいいんですけどmapみたいに明らかに1画面しか作らないなら
resources :restaurants, only: %w[show] get 'restaurants/:id/map', as: :restaurant_map・・・①
か
resources :restaurants, only: %w[show] do get 'map' => 'restaurants#map', as: :restaurant_map・・・② end
で十分ですよね。とくに①の方法は:idの部分を:nameとか:uuidとか変えられるのでオススメです。その際named_routesがそれっぽくなるようにちゃんとasオプションを付けましょう。
ちなみにshallowオプションは使うべきではない派です。
resources :restaurants, only: %w[show], shallow: true do resources :reviews, only: %w[show] #これでレストランのレビュー一覧ページはマッピングできるけど、ユーザのレビュー一覧は? # それならRestaurant#ReviewsControllerとUser#ReviewsControllerにしたほうが良くない? end
まぁas, namespace, pathあたりのオプションをちゃんと使えば良い話です。
クラスを継承してsuperメソッドを使う...
まずDevise使うのやめましょう。 あと、iOS使ってるとsuper.viewDidLoad()とか出てくるけどRailsでこれが出てきたら使うライブラリが間違ってるか設計がおかしいかのどちらかだと思う。
クラスをまたぐ処理はconcernに
concerns使うシーンがピンとこなかったんですが、モデルからデコレータに変換するメソッドだけを含むモジュールとかをconcernsにするとけっこう便利です。あとはpublished_atだけでModel#draft?
, Model#published?
, Model.published
, Model#publish!
などを付け足してくれるモジュールとか。
ただ、たまたま共通してるレベルのものをconcernsにするのは危険かなと思います。
scope
昔保守した案件で @article.visible.user_authenticated.high_quolity.(以下略)
みたいに定義して、結局Article.published
が抜けてたみたいな事故があり、案外scope定義しまくるのはリスクがあるというか、scopeがそんなに大量になる時点で何かがおかしい気がします。
後はアプリケーションの画面に強く関係するビジネスロジックをscopeにすべきではないです。
Helperメソッド
ページ数によってtitleタグを動的にとる程度であれば
- if @article.current_page > 1 title "記事一覧 #{@article.current_page} ページ" - else title "記事一覧"
とかで十分。どうしても欲しいなら@articleのデコレータ。これをヘルパーにしてるとメソッド名が枯渇する。
考え方として、Railsが提供してくれるlink_toとかの亜種を作りたいときにはいいと思う。
def noindex_nofollow_tag "<meta content='noindex,nofollow' name='robots' />".html_safe end
とか。
Test
minitest使ってるのはいいですね!
おわりに
いろんな現場、いろんなプロジェクト、規模やセキュリティの重要性、リリース後の動きとかによって書くべきコードは変わると思います。 私もまだまだ勉強中です。
批判あればご指摘ください