LOCARI のテストで気をつけてること
この記事は Wondershake Advent Calendar 2日目の記事です。
先日分は間に合わなかったみたいですmm 代わりに謝罪しておきます 🙇
普段 Rails で LOCARI のサーバーサイドを主に書いている mmyoji です。やっていきます。
話すこと
- LOCARI のテストどーなってんの
- 自分がテスト書く時に気をつけてること
Wondershake(LOCARI)のテスト環境
現在 Wondershake で一番コードベースが大きいプロジェクトが LOCARI という女性向けのメディアサービスのサーバーサイドを含む Rails アプリになってます。自分は最近までフリーランスだったのですが、ほとんどの現場でテストが書かれていませんでした。が、こちらは Coverage 85% 強となってて、比較的安心して開発が進められる環境となってます。(「低い!」と思われる方、幸せですね 😇 )
現状 Rails アプリはいくつかありますが基本的にすべて RSpec を使っています。 LOCARI に関して言えば本当は MiniTest 使いたいなーという意見(主に自分)もありますが RSpec のコードが結構沢山あってなかなか踏ん切りがつかないという状況です。
あと LOCARI アプリに関しては独特で以下の例でも見られると思いますが、 MiniTest
に寄せたい思惑からか、expect
ではなく assert
を使って書いてたりします。このキモい感じが僕は割と気に入ってます 😈 ❤️ (読みづらいかもしれませんがご了承ください...)
で、本題に入りますが普段テスト書いてて気をつけてることとかを雑に書いていこうと思います。
こんなことを気をつけてます(個人的に)
賛否両論あるかと思いますがご容赦ください 🙇 あくまで 僕個人 が気をつけていることになりますmm
- vcr ガンガン使う
- 極力 example をまとめて test の実行速度を速くする
- 意味が被るようなテストコード書かない
let(!)
とbefore
を正しく使い分ける
vcr ガンガン使う
まず vcr の説明を簡単にしておくと、外部 API との通信を yaml ファイルにダンプしておいて、1回目以降はそちらのファイルを参照して実際に通信したかのようにテストを実行してくれる便利なライブラリです!
外部の API と通信を行う際に stub で済ませちゃうこともあると思うんですが、結構ガシガシ vcr に任せてしまいます(なので spec/cassettes
が結構膨れてます...)
ただそうすることで少なくとも一回は実際のリクエストをテストできるので、例えば「ドキュメントに書いてあるレスポンスと違った!」みたいなことが防げたり、安心感があります。
あと小手先ですが、生成されるテストデータが微妙に異なるせいで、リクエストに含むパラメターが変わり、 vcr がうまく動かない(「異なるリクエスト」として扱われてしまう)こともあるので、そういう時は match_requests_on
オプションを活用しています。このオプションに関しては以下を参考にしてみてくださいmm
以下のようなオプションの付け方してる箇所が多いイメージ
describe '#notify', vcr: { match_requests_on: [:host, :method] } do specify 'requests to external API' do # 処理 end end
極力 example をまとめて実行速度早くする
「どこでテストが落ちたのかわかりづらい!」と言う人もいるのでデメリットも多少ありますが、テストの初期化コストが大きく、且つまとめてしまえる場合などは特に example をまとめてしまう方がテスト実行時間減らせてオススメです。
API テストの例(あまりいい例ではないです。色々と省略)
# Before describe 'GET index' do before do api_request end # HTTP status code と response の中身をそれぞれ別 example でチェックしている specify do assert_resposne 200 end specify do assert_json_match(matcher, response.body) end end # After describe 'GET index' do before do api_request end specify do assert_resposne 200 assert_json_match(matcher, response.body) end end
実際にベンチマーク取ってどれだけ速くなってるか、みたいなの出したかったですすいません
意味が被るようなテストコード書かない
「A がチェックできてたら B は保証されるから無くていいよね」という話です。
ある程度テスト書いてる人からすれば当然のことかもしれませんが、プログラミング書き始めた頃はよく指摘されてたので今でも意識してます。なんでもテストしたがる病ですね。
let(!) と before を正しく使い分ける
これは RSpec 特有の話になってしまいますが、RSpec 使い始めだとなんでもかんでも let(!)
で書いちゃう人が一定数いると思ってて、テスト読む際に自分は意識して使い分けてます。
let!
: example などで1回以上参照されるbefore
: example などで参照されない
要するに let!
に書いてあれば example 内で参照されたり、値をチェックしてたりするのでテストコード読む際に判断がしやすくなります。逆にそうでなければ before
ブロックは読み飛ばして example だけ読めば大体わかるので無駄な時間使わずに済む、ということになりますね?(無理やり)
まとめ
テストはしっかり書きたいけどビルドが長いのは嫌だ、という気持ちを少しでも緩和するためこれからもやっていきます。
あと他の方のテストに関する気遣いを聞きたい今日この頃。
明日は Android エンジニア兼 PO/PM の shirokura です。ちゃんと書いてくれるかな? 😏
ESLint2からESLint3に上げて関連プラグインもアップデートした話
こんにちは。フロントエンドエンジニアの佐々木です。
先日、ESLintまわりを最新にアップデートしたのですが、結構修正することが多かったので備忘録も兼ねて残しておきます。
ちょっと古いですが、ESLintの導入に関してはこちらの記事でご紹介しました。
ESLint3へのアップデート
ESLint導入時には下記のバージョンを使っていました。
"eslint": "^2.9.0", "eslint-config-airbnb": "^9.0.1", "eslint-plugin-import": "^1.12.0", "eslint-plugin-jsx-a11y": "^1.2.0", "eslint-plugin-react": "^5.1.1", "babel-eslint": "^6.0.4",
それぞれ最新のバージョンを調べてインストールしました。
"eslint": "^3.8.1", "eslint-config-airbnb": "^12.0.0", "eslint-plugin-import": "^1.16.0", "eslint-plugin-jsx-a11y": "^2.2.2", "eslint-plugin-react": "^6.4.1", "babel-eslint": "^7.0.0",
この状態でESLintを走らせると ✖ 108 problems (99 errors, 9 warnings)
と、結構な数のエラーが発生してしまいました。
バージョンを上げる前は 0 errors
だったので、アップデートに伴ってルールが厳格になっているのがわかります。
.eslintrc を .eslintrc.yml に変換
ESLint3から設定をyamlで書けるようなので書き換えました。
これは好みの問題ですが、jsonよりもyamlの方がメンテしやすいです。
.eslintrc (変更前)
{ "parser": "babel-eslint", "extends": "airbnb", "env": { "browser": true, "node": true }, "globals": { "ga": true, "$": true, "I18n": true, "describe": true, "it": true, "before": true, "after": true }, "rules": { "new-cap": [2, { "capIsNewExceptions": ["Map", "List", "Set", "OrderedSet"] // これはImmutableJS用の記述 }] } }
.eslintrc.yml (変更後)
--- parser: babel-eslint extends: airbnb env: browser: true node: true globals: ga: true $: true I18n: true describe: true it: true before: true after: true rules: new-cap: [2, { capIsNewExceptions: [Map, List, Set, OrderedSet] }]
Lintをパスするよう修正
import/extensions
6:18 error Unexpected use of file extension "jsx" for "./views/base.jsx" import/extensions
このエラーは import Base from './views/base.jsx'
のように .jsx
を読み込んでいる箇所で起きてました。
eslint-plugin-import を見ると import/resolver が必要だとあるのでインストールします。
$ npm i -D eslint-import-resolver-node # もし webpack を使っていたら eslint-import-resolver-webpack が必要そうです
.eslintrc.yml に追記します。
# rules と同じインデントレベルに settings を追加 settings: import/resolver: node
これでESLintを走らせれば import/extensions のエラーは回避できます。
class-methods-use-this
34:22 error Expected 'this' to be used by class method 'comonentWillUnmount' class-methods-use-this
このエラーの当該箇所は次のようなコードになってました。
class Base extends PureComponent { componentDidMount() { $(window).on('resize', throttle(this.handleResize, 200)); } comonentWillUnmount() { // NG $(window).off('resize'); } // 以下略 }
class methodとして定義されたメソッドのスコープ内で this
への参照が無いとエラーになるようです。
componentDidMount でイベントを購読して、componentWillUnmount でイベントの購読を解除するコードなので、これ以上直しようが無いと思ったので warning を出すように変更しました。
.eslintrc.yml の rules に class-methods-use-this: 1
を追記するとエラーから警告に変わります。
根本的に解決するには、状態を持たないJSXは 関数として定義するといいです。
jsx-a11y/no-static-element-interactions
89:7 error Visible, non-interactive elements should not have mouse or keyboard event listeners jsx-a11y/no-static-element-interactions
このエラーは次のようなJSXを書くと発生します。
render() { return <div onClick="this.handleClick" /> }
div
要素などの本来ユーザーのインタラクションを想定していない要素に対して、onClick
などでハンドリングしようとすると発生します。
この場合はHTMLのマークアップ上でクリッカブルな要素ということを示さないといけないので、div
の代わりに button
などで代替するのがいいでしょう。
render() { return <button onClick="this.handleClick" /> }
react/forbid-prop-types
6:5 error Prop type `object` is forbidden react/forbid-prop-types
ここが一番修正箇所が多いエラーでした。
このエラーは ReactComponent の propTypes
で React.PropTypes.array
や React.PropTypes.object
を使っていると発生します。
props に React.PropTypes.array
と指定されても、Array型なのはわかりますが配列の中身が何の型なのかがわかりません。
何の型を持った配列なのかを明示的に示さないとエラーになるようになりました。
このLintをパスするには PropTypes.array
の代わりに PropTypes.arrayOf()
, PropTypes.object
の代わりに PropTypes.shape()
を使います。
このあたりの書き方は本家のドキュメントが参考になりました。
Typechecking With PropTypes - React
例えば次のようなコードがある場合、
// @file sites.jsx import React, { Component, PropTypes } from 'react'; export default class Sites extends Component { static propTypes = { sites: PropTypes.array.isRequired, // NG currentSite: PropTypes.object, // NG } // 以下略 }
このように直します。
// @file sites.jsx import React, { Component, PropTypes } from 'react'; export default class Sites extends Component { static propTypes = { sites: PropTypes.arrayOf(PropTypes.shape({ // OK name: PropTypes.string.isRequired, url: PropTypes.string, })).isRequired, currentSite: PropTypes.shape({ // OK name: PropTypes.string.isRequired, url: PropTypes.string, }), } // 以下略 }
currentSite
が site
型のオブジェクトを値に持つとすれば、sites
は site
型の配列を値に持つと言えそうです。
上記のコードは冗長なので、型定義をまとめたファイルを constants/prop_types.js
として作りました。
// @file prop_types.js import { PropTypes } from 'react'; export const site = PropTypes.shape({ name: PropTypes.string.isRequired, url: PropTypes.string, }); export sites = PropTypes.arrayOf(site);
型定義ファイルを参照するように変更するとすっきりします。
// @file sites.jsx import React, { Component } from 'react'; import * as pTypes from '../constants/prop_types'; export default class Sites extends Component { static propTypes = { sites: pTypes.sites.isRequired, currentSite: pTypes.site, } // 以下略 }
react/no-unused-prop-types
PropTypes.shape()
を全て外部のファイル(constants/prop_types.js
)で定義すればいいのですが、.jsx
ファイル内で PropTypes.shape()
を定義すると以下のようなエラーが出ます。
6:12 error 'item.key' PropType is defined but prop is never used react/no-unused-prop-types
このエラーを抑制するためのオプションが用意されているので、.eslintrc.yml の rules に下記を追記して無効化しましょう。
rules: react/no-unused-prop-types: [2, { skipShapeProps: true }]
Code Climate
ESLintのバージョンアップに伴い、CodeClimateの設定で少し悩んだので書いておきます。
CodeClimate上で実行するESLintのバージョンはデフォルトで1系が使われるようです。
Note:
If no channel is specified, ESLint v1.10.3 is used for analysis.
https://docs.codeclimate.com/docs/eslint
ということで、ESLint 3系を使って欲しい場合は .codeclimate.yml
に追記します。
--- engines: eslint: enabled: true channel: eslint-3 # ESLint3系 を使うようにする
ちなみにこの eslint-3
で使われる ESLint のバージョンは現時点では 3.6.1
でした。
これでうまく行くはずだったのですが、ローカルでESLintが通っていても CodeClimate 上では import/no-unresolved
のエラーが出る現象に悩まされました。
import/no-unresolved
が出ている箇所は import React from 'react';
のように、npm module を読み込んでいる箇所で、参照先が絶対パスで始まるとエラーになるようでした。
これはCodeClimate上での結果が正しくないので、.codeclimate.yml に下記を追加してチェックを無効化しました。
--- engines: eslint: enabled: true channel: eslint-3 checks: import/no-unresolved: # このチェックを無効化する enabled: false
まとめ
ESLintまわりを最新にアップデートして発生したエラーを無くしていく過程をご紹介しました。
記事が長くなるので紹介できなかったルールもあったのですが、React を使うなら eslint-config-airbnb
は入れておいたほうがいいと改めて思いました。
このLint設定で書いていけば最低限のコードの品質は保てると思います。
Wondershake では Web エンジニアや iOS、Android ディベロッパーを募集しています。 興味が湧いた方は是非こちらからご応募下さい!
非iOSエンジニアが、iOSDC Japan 2016に当日スタッフで参加してきた
去る2016年8月19日&20日、iOSDC Japan 2016が開催されました。
記念すべき第1回目で、TrackBのTK(タイムキーパー)&司会を担当しました、安藤です。
「ブログを書くまでがiOSDC」By@tomzohさん・・・ということで。
今回は、iOSDC Japan 2016に参加するメリットと、スタッフをやった感想について書きます。
iOSDC Japan 2016とは?
「iOSDC (iOS Developers Conference Japan) は、 iOSとその周辺技術に関するエンジニアのためのカンファレンスです。 iOS関連のカンファレンスを待っていた皆様、お待たせしました。ついにやってきました。 2016年8月20日はiOSエンジニアのお祭りです! 日本中、世界中から公募されたスピーカーがキレッキレのトークを繰り広げます。 トークは「iOSエンジニアが聞いて面白ければ何でもOK」という基準で選定されます。 iOSやSwiftといった王道テーマから、エモい話、デザインの話などもあるかもしれません。 そして、トーク終了後には同じ会場で懇親会が開催されます。 iOSDCはiOSエンジニアであれば誰でも楽しめるカンファレンスです。 皆様のご参加をお待ちしています。 カンファレンス未体験の方も、是非一度参加してみてください。 楽しく、エキサイティングな、忘れられない1日になるでしょう!」
(公式サイトより。)
実際、トークの内容は
- Swift誕生から現在までの軌跡を振り返る王道トーク
- 個人の経歴をネタにしたトーク
- アイコンデザインに関するトーク
さらにはコードに全く関係ない肉トーク(!)まで多岐に渡っていました。
※トークの内容は→こちらをどうぞ。
内容のバラエティさもさることながら、
イベント全体を包む空気がまさに「祭り」でした。
そもそもなぜiOSDCの当日スタッフやろうと思ったの?
ことの始まりは、2016年7月11日。
こんなツイートが。
今日はお知らせがたくさんあります。我々スタッフといっしょにiOSDCを作ってくれる当日スタッフを募集します。希望の方は応募フォームにて!17日(日)24:00までの応募です。 https://t.co/DEOXmPO59q
— iOSDC (@iosdcjp) July 11, 2016
「イベントの運営経験値を貯めたい。iOSのことを知りたい」
そう思っていた自分には最高の機会だな。ということで、参加表明しました。
締め切り後、当選のメールが来てiOSDCスタッフSlackに招待され
その後は基本Slack上でやりとりをして当日を迎えます。
(8月16日に事前スタッフ顔合わせがありましたが、私は台風の影響で行けませんでしたorz)
参加者のメリット
- 国内外で活躍されているエンジニアのトークが聞ける
- 聞くだけじゃなくて、一問一答も出来る
- 一問一答だけじゃなくて、会話もできる
(スピーカーに話しかけるタイミングは沢山あります。
普通に会場内をウロウロしています。懇親会なんて絶好のチャンス) - 充実したノベルティ、ビール←
結果
- 新しい知見が手に入る
- 悩んでいた問題が解決する
- 開発へのモチベーションが上がる
- 技術だけじゃなく働き方の指標にも出会える
- ごきげんになれる(ビールでなれる人は)
- 交流の輪が広がる
スタッフのメリット(主観)
- 楽しい。仲間ができる。
一言で言うと、これに尽きます。
実際に私がやったこと
1日目
時間の大半を机と椅子の設営に費やしました。
プロジェクターやカメラもほぼセッティングし終えたところで二手に分かれてみんなでランチ。
戻ってきてから、GitHubで配布されていたマニュアルを漁ります。
同じチームリーダーの@koogawaさんと打ち合わせしつつ
アナウンスの練習をしたら、あっというまに開場の時間。
この日はTrackBのTKが自分一人だったので、ずっとスピーカーと時間を気にしてました。
最後のトークセッションを終えたら、受付に出てたシャツ等を部屋の中に入れ、
そそくさとスピーカーディナー(という名の飲み会)へ。
2日目
TrackBのTKが2人になったので、昨日より余裕が出てきました。
ただ、冷暖房や電気の明るさ調整など、気は抜けません。
この日は、後半TrackAと部屋を結合してからずっと懇親会の設営のことで頭がいっぱい。
1日目に3-4時間かけて並べた机と椅子を、30分で片付けるスケジュールでしたから。。。
実際は・・・スタッフのみなさんの協力のもと、スイスイことは進みました。
懇親会では、ビールと料理を手に談笑する参加者&スピーカー&スタッフの皆さんを
端から見ては「みんな楽しそうだな。良い光景だな」と思ってました。
さて、当日スタッフをやると、持ち場によってはほぼトークが見れなかったり(※)します。
また、コアスタッフは前日までの準備に何十時間という時間を費やします。
頭も体も使うから、どっと疲れる2日間です。
※配信管理やHQをされていたiOSエンジニアスタッフさんが「この人のプレゼン参考になるわ〜」とか「あ、そういうやり方あるのね〜」と漏らしてたので、見てなかったのは私だけかもしれません\/(トークは後ほど動画で配信されるので、見れなくても大丈夫ですが!)
でも、この祭りの楽しさを一番享受していたのはコア&当日スタッフだったのではと思います。
某スタッフさんも「こんなに楽しかったのは本当に久しぶり」と言ってましたしね!
みんなでひとつひとつ創っていく様子は、学生時代の文化祭準備を想起させてくれました。
結果
もっとiOSのことが好きになりました。
(9/14の振り返りMTG後。ごきげん過ぎて画面に収まりきらない)
実際、私はiOSエンジニアではなかったのですが。この後、他のスタッフ主催の勉強会に参加したりして、iOSの世界に足を踏み入れかけています。
・・・iOSDC japan 2016が最高なイベントだったことは伝わりましたでしょうか。
少しでも楽しそうだと思った方は、来年のイベントスタッフ運営してみませんか?
スタッフになる方法?コアスタッフなら、@tomzohさんに聞いてみてください!
当日スタッフなら、きっと来年も公開応募があるかも?公式サイトとツイッターを要チェック!
まとめリンクたち
スピーカーや参加者、他のスタッフのブログはこちら
写真で振り返るiOSDC Japan 2016はこちら
トークセッションの映像はこちら
ここでちょっとお知らせ
11/11(金):ポッキーの日にエンジニア勉強会(もくもく会)を開催します。
もくもく内容は、何でもあり!前回は、お仕事する人や読書する人などもいらっしゃいました。
普段一人で家やカフェでもくもく作業してる人、11/11はみんなでもくもくやりませんか?
最初に今日何をもくもくするかを共有するので(任意)、きっと捗りますよ!
daikanyama-mokumoku.connpass.com
また、WondershakeではiOSエンジニアを絶賛募集中です。
(無理に「来年一緒にiOSDCのスタッフやろう」とは言いません。)
一緒に会社を盛り上げてくれる方、Wanted。
www.wantedly.com
最後に
@tomzohさん、リードありがとうございました&大変お疲れ様でした!
ProGuard Tips
こんにちは。Androidアプリを開発している代蔵です。 今回はアプリのサイズを削減するのに欠かせないツールProGuardに関する簡単なTipsを書いてみました。
アプリのサイズ
近頃、RxJavaやRetrofitなどAndroidアプリの開発に欠かせない便利なライブラリが増えてきています。こういったライブラリにより、開発速度・メンテナンス性は改善されている一方で、アプリの容量が大きくなってしまうという弊害が有ります。 この対策として、一般的にはProGuardを利用してアプリのサイズを小さくします。他にもredexといったツールを使うことでアプリの容量を小さくすることも可能です。
ProGuard
ProGuardを簡単に説明すると、使われていないクラスやメソッドを削除したり、クラス名やメソッド名などを難読化するためのツールです。これを利用することでコード量が削減されるので相対的にアプリケーションの容量が小さくなります。 ProGuardを利用する場合は以下のコードをbuild.gradleに追加し、proguard-rules.proに難読化したくないクラス名などを記述することで、ライブラリ内のクラス名などが削除・難読化されないようになっています。大抵のライブラリはProGuard用の設定を書いてくれています。
minifyEnabled true
ここで問題になるのは、利用するライブラリが増えれば増えるだけproguard-rules.proに記述する量が増えるので、可読性や管理コストが上がってしまいます。 例えば以下の様に一つのファイルに、様々なライブラリのルールを記述していくと管理するのが大変になっていきます。
# Retrofit -dontnote retrofit2.Platform -dontwarn retrofit2.Platform$Java8 -keepattributes Signature -keepattributes Exceptions # Picasso -dontwarn com.squareup.okhttp.** # OkHttp -keepattributes Signature -keepattributes *Annotation* -keep class com.squareup.okhttp.** { *; } -keep interface com.squareup.okhttp.** { *; } -dontwarn com.squareup.okhttp.** # RxJava -dontwarn sun.misc.** -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { long producerIndex; long consumerIndex; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { rx.internal.util.atomic.LinkedQueueNode producerNode; } -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { rx.internal.util.atomic.LinkedQueueNode consumerNode; } # ButterKnife -keep class butterknife.** { *; } -dontwarn butterknife.internal.** -keep class **$$ViewBinder { *; } -keepclasseswithmembernames class * { @butterknife.* <fields>; } -keepclasseswithmembernames class * { @butterknife.* <methods>; }
proguard-rules.proを分割する
Locariでは、ProGuard用のルールをそれぞれのライブラリに対して作成することで管理を楽にしています。具体的には、proguard-rulesというディレクトを作成し、その中に各種ライブラリ用のルールを記述し、gradleでそのディレクトリの中身を読み込むような形にすることで、ルールファイルを分割しています。
proguardFiles getDefaultProguardFile('proguard-android.txt') proguardFiles fileTree(dir: 'proguard-rules', include: ['*.pro']).asList().toArray()
雑感
最近のライブラリはProGuardでkeepしなくても良いものが増えてきていますが、ルールとして記述しないといけないものが依然としてあるなか、このように管理するのは意外と便利です。 みなさんもぜひお試し下さい!
加えて、毎回書いていますが弊社ではAndroid、iOS、サーバサイドエンジニアを絶賛募集しています。 ご興味の有るかた是非ご応募下さい! :)
Rails 5 のマイナー機能の紹介
こんにちは、バックエンドエンジニアのダイクストラです。
Rails 5 がやっと出ました!ActionCable やTurbolinks 3など大きな機能が色々追加されました。 加えて,Ruby 2.2.1以上を使うことが必要になりました。 これらに関する記事はたくさんあるので、今回はあまり知られていない機能を紹介します。
便利な機能
APIベースのアプリケーションが作成可能
人気のジェム Rails API は Rails 5 にマージされました。ActionController::API
で API のみのアプリケーションを作成することが出来ます。新しいアプリケーション作る時、フラグを追加するだけで出来ます。
rails new hogehoge --api
このタイプのアプリケーションは、ActionController::Base
ではなく、ActionController::API
を使います。middlewareも少なくなり、コントローラーがもっとシンプルになるので、JSベースのアプリ作る時は、このような作り方が便利です!
Migration に コメントを追加できる
データベースにはあった機能ですが、今まで直接SQLのクエリで書く必要がありました。Rails 5 から MySQL と PostgreSQL 使っていれば、コメントを追加することができます!
class CreatePosts < ActiveRecord::Migration[5.0] def change create_table :posts, comment: 'テーブルのコメント' do |t| t.string :title, comment: 'フィールドのコメント' end end end
DBAのツールにも、schema.rb
にもコメントでます。
create_join_table で UUID を使える
これはシンプルな機能ですが、今までは integer
を扱えませんでした。
class CreateJoinTableFavorites < ActiveRecord::Migration[5.0] def change create_join_table(:users, :posts, column_options: {type: :uuid}) end end
使わないで欲しい機能
ActiveSupport
で Array#second_to_last
と Array#third_to_last
のメソッドが増えた
よく .last
と .first
を使うと思ますが、 third_to_last
などは本当に必要ありますか?マジックナンバー)のようなものなので、先にリファクタリングして欲しいです。Array
使わず Hash
にすれば、コードが分かりやすくなる可能性があります。どうしてもこの使い方しかないと思ってたら、普通に Array
の [-2]
是非、使ってください。
ActiveSupport
で Enumerable#without
のメソッド増えた
Array
の場合は ary1 - ary2
でこれが実現できますし、Hash
の場合は色んなやり方があるので、不必要なメソッドが増えたと思います。
Rubyの標準ライブラリで簡単に書けることをわざわざRailsだけのコードで書く意味がないです!
> [1,2,3,4,5] - [3,4] => [1, 2, 5]
> {a: 1, b: 2, c: 3}.reject{|x| [:a].include? x} => {:b=>2, :c=>3}
ActiveRecord
のオブジェクト をsave
する時、(touch: false)
を入れれば、 updated_at
はアップデートされない
今まで save
した時、 updated_at
がアップデートされたのに、急に touch: false
をつけてしまうと、挙動が変わるので同じプロジェクトで働いてる人が困惑する可能性があります。save(touch: false)
を使いたい時は、updated_at
ではなく、新しいコラム作ったほうがいいでしょう。
その他の便利なアップデート
SQLのログにカラーが増えた
アプリからログをいい具合にBigQueryに突っ込む険しい道
千葉です。基盤と雑用をしています。
弊社のログの解析はBigQueryに丸投げなのですが、それ以外はAWSさんにおんぶにだっこなので、いわゆる
- Kinesis Firehoseからかんたんに Redshift にはいります!!
- Mobile Analyticsからボタンひとつで Redshift にはいります!!
というのを敢えて使わずに茨の道を歩まねばなりません。辛いです。 また、BigQueryのStreaming Insertで100%安心です\(^o^)/ となれば良いのですが、前に障害のメールもありましたし、デバッグもしにくいという理由からこちらも避けて通っています。
ということで弊社の構成です。
- アプリはMobile AnalyticsのSDKを使ってログをいい具合に転送する
- Mobile AnalyticsのS3連携(Kinesis Firehoseのよう)で定期的にS3に書き出す
- Lambda が S3 Put Event を拾って DynamoDB に記録しつつ BigQuery に突っ込むjobをつくる
最近は俄然Kinesisファミリーが流行していますが、Mobile Analyticsも同じようなことができます。 また Cookpadさんのpureeのようなものがアプリではやはり必要なので、AWSさんの開発力に乗ってしまえるのも利点です。
ところが、いきなりの障害
上記の運用でしばらくうまくいっていたのですが、急にログがBigQueryにはいらなくなりました/(^o^)\ よくよく見ると Put のイベントの数がいきなり増えています。
BigQueryはテーブル毎に1日に Insert する Job を作れる制限が決まっており、それが1,000件になっています。 それを超えてしまい、どうしようもなくなった!という状態でした。
仕方ないのでログを結合する
幸い、どのログが入ったのか入っていないのか、BigQueryのJobのIDがなんなのかというのはLambdaからDynamoDBに記録していたので、たどることができます。
なので、うまい具合にretryもしつつ、S3にたまったログを結合して(且つ、timestampなど特定のカラムをnormalizeして)BigQueryにいれるスクリプトを書きました。書きましたとも!
うまくいった
ということで、上記の対応でうまく対処できました。こうやって日々Locariのログを捌いています。
BigQueryはデータの投入もとても早いので(Steraming Insertの場合もすぐクエリーをうてます)かなりリアルタイムに近いデータにアクセスができるのは利点かなと思っています。もちろんBIのBackendとしても十分な速度とコストパフォーマンスがあると思います。
ちなみに汎用化すれば、BigQueryの普及に寄与できそうな Lambda(ES2015) を公開できそうなのですが、もうしばらく時間がかかりそうです...!
このスクリプトのメンテを中心とする基盤のメンテナンスやBigQueryに向けて一緒にクエリーを叩いてくれるような方を探しています! よかったらご応募ください!
アイコンフォントライブラリを使ってみる
こんにちは、iOS開発担当の藤井です。
iOSアプリの開発を行うときにちょっと面倒なのがアイコン画像ファイルの追加だったりします。画像を切り出して、Images.xcassetsに1x, 2x, 3xをそれぞれ追加して… 特にモックレベルのものを作るときは、そもそもアイコン画像がなかったりするので自分で用意するとなると相当大変です。
先日の記事でも紹介したIoniconsはiOS/Androidアプリ内で利用頻度が高そうなアイコンをカスタムフォントとして利用できるフォントライブラリです。
まずはインストール方法ですが、IonIconsはCocoapodsで配信されているのでいつものようにPodfileに以下を追加します。
pod 'ionicons'
UIImageとして利用したい場合は、
UIImage *icon = [IonIcons imageWithIcon:ion_ionic iconColor:[UIColor redColor] iconSize:60.0f imageSize:CGSizeMake(90.0f, 90.0f)];
という感じでUIImageとして生成します。色やサイズなども同時に指定可能です。 ただあくまでもアイコンフォントなので単色のみでしか指定できないので注意が必要です。 'ion_ionic'の部分はサイトから使いたいアイコンをホバーすると出てきます。'ion_ios_arrow_back'や'ion_ios_search'といったよく使いそうなアイコンから、'ion_social_twitter'や'ion_social_facebook'といった地味に便利なアイコンまでたくさんそろっています。
UIImageだけでなくUILabelとしても利用できます。
UILabel *label = [IonIcons labelWithIcon:ion_ionic size:20.0f color:[UIColor blackColor]];
という形で生成してあげればOKです。
また、カスタムフォントなので文中に混ぜるといったことも可能です。例えばLocariでは記事一覧画面でのお気に入り数の横にあるハートアイコンをその方法で追加しておます。
これは
NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@ %@", ion_ios_heart_outline, likeCount]]; [attributedText addAttributes:@{NSFontAttributeName:[IonIcons fontWithSize:13]} range:NSMakeRange(1, 1)]; cell.likeLabel.attributedText = attributedText;
という感じにstringWithFormatなどで連結してあげて、NSFontAttributeNameでIonIconsを該当箇所に指定するとうまい具合に反映されます。
文字内に含むのは若干レアケースかもしれないのですが、覚えておくと何かと便利かもしれません。
このようにLocariではIonIconsを利用しているのですが、もちろんこれ以外にもたくさんのフォントライブラリがあり、まとめて入れたい場合はFontAwesomeKit辺りを入れると便利なので気になる方はこちらも検討してみてください。
またSwiftの場合は別な方がSwift版をCocoapodsで配布しているので、そっちを利用すると良さそうです。
またWondershakeではアイコンフォントライブラリを使いこなせるiOSエンジニアも募集中です。 www.wantedly.com