C#(.Net Framework)でStackExchange.Redisを扱うときの例外を想定する
単純に使うだけ
まずNugetでStackExchange.Redisをインストール。
using StackExchange.Redis;
Azureの記事だけどこれが参考になる。
IDatabase cache = ConnectionMultiplexer.Connect("localhost").GetDatabase(); cache.StringSet("key", "value"); string result = cache.StringGet("key");
例外
Redisを使う以上、あらゆる例外に向き合わなければいけない。
ここではString型にJSONが入っていることを想定し値をオブジェクトにマッピングするときに、想定しうる例外を各々キャッチする処理を書いてみた。
ここに挙げたコードを元に発生しうる例外を挙げてみる。
接続失敗
単純に接続情報が間違えていた時やRedisが落ちた時。
Redisの操作がある場合、一律でRedisConnectionExceptionをcatchしておかないと例外が発生し落ちる可能性がある。
try { cache = Connection.GetDatabase(); } catch (RedisConnectionException e) { WriteException("接続失敗", e); return; }
キーが存在しない場合
存在しないキーをStringGetメソッドに投げるとRedisValue型でNullを表すような値が返ってくる。
それをDeserializeObjectに投げると当然例外が発生する。
ここではArgumentExceptionになる。
try { var notExist = JsonConvert.DeserializeObject<EmployeeDto>(cache.StringGet(key)); } catch (ArgumentException e) { WriteException("キーが存在しない場合", e); }
String型ではない別の型で保存されていた場合
String型が欲しい時にハッシュ型などの違う型で値が格納されている場合は一律でRedisServerExceptionが発生する。
key = "other-type"; HashEntry[] hashEntries = { new HashEntry("a", 1), }; cache.HashSet(key, hashEntries); try { var hash = JsonConvert.DeserializeObject<EmployeeDto>(cache.StringGet(key)); } catch (RedisServerException e) { WriteException("String型じゃない場合", e); }
パースするJSONとマッピング対象のクラスが一致しない場合
下の例だとマッピングしたクラスのプロパティはNullで何もマッピングされない。
なのでしっかりNullチェックをしないとNullReferenceExceptionが発生する。
key = "anonymous"; var objDto = new { T = "a" }; cache.StringSet(key, JsonConvert.SerializeObject(objDto)); var eAnonymous = JsonConvert.DeserializeObject<EmployeeDto>(cache.StringGet(key)); try { // そのままオブジェクトのプロパティにアクセスするようなメソッド Write(key, eAnonymous); } catch (NullReferenceException e) { WriteException("パースするJSONとマッピング対象のクラスが一致しない場合", e); }
中身がJSONではなくただの文字列の場合
JSON形式で文字列が保存されていない場合Json.Net側でJsonReaderExceptionが発生する。
key = "string"; var stringDto = "test"; cache.StringSet(key, stringDto); EmployeeDto str = null; try { str = JsonConvert.DeserializeObject<EmployeeDto>(cache.StringGet(key)); Write(key, str); } catch (JsonReaderException e) { WriteException("中身がJSONではなくただの文字列の場合", e); }
中身が配列の場合
中身が配列の文字列を格納したString型の場合Json.NetのJsonSerializationExceptionが発生する。
key = "array"; var badJsonDto = "[1, 2, 3]"; cache.StringSet(key, badJsonDto); try { var arr = JsonConvert.DeserializeObject<EmployeeDto>(cache.StringGet(key)); Write(key, arr); } catch (JsonSerializationException e) { WriteException("中身が配列の場合", e); }
まとめ
こんな感じで数多の例外が発生する可能性がある。
防御的に各々全ての例外をcatchしてもいいのだが、格納されている型が違うだとかJSONの形式として正しい形式で値が入っていないとかは値を保存する側のプログラムのバグや仕様把握漏れになると思うので取得側では契約的に例外をcatchしなくてもいいんじゃないかと思っている。
でも間違えられた瞬間システムが落ちるので悩みどころ。
とりあえず最低限としては接続確認としてRedisConnectionExceptionと、キーが存在しないとき用に値のNullチェックあたりをして、そこからはシステムの要件と相談という形になりそう。
Vue.jsとか使って時間管理マトリックスのWEBアプリを作ってみた
作ってみた。リポジトリは下記。
Vue.jsとかSVG周りの勉強がてらなんか作るかーと思って作ってみた。
時間管理マトリックスってなんぞや?って人は下記を参照されたし。
概要
左下に入力項目の表示非表示ボタンがある。
ボタンを押すと左上に可変の入力欄があり、そこに項目名を入力、重要度と緊急度を数値の入力欄かスライダーで変更できるようにしている。
各値が変更されればそれに合わせてSVGのtext要素の値、座標も変更するといった感じだ。
今はLocalStorageに変更した値を保存するようにしているので再度開きなおしてもそのまま前回の画面のまま編集できるようにしている。
見た目はもっと良くしたいなーと思いながらも今の優先順位的にそこまで入れ込む気はないので手は付けず。
ぶつかった問題とかTipsとか
初めてGithub Pages使ってみた!
各リポジトリのSettingsからGitHub PagesのSourceを選択するだけでページを公開することが出来て便利。
基本的にはmasterブランチのファイルが公開されるが変更する方法もあるっぽい?(ここよく分かってない)
masterブランチにビルド後のJSファイルも含める形になる。
仕方ないとは思うけど何かいい方法がないかな。
あとmasterにマージしてからファイルが反映されるまで少し時間がかかる模様。
体感数分。
スプレッド演算子がSyntaxErrorになる
VuexのVueコンポーネントへのマッピング時の...mapState等がSyntaxErrorになった。
最終的にoptions取り除いて.babelrcを作成した。
// before { loader: "babel-loader", options: { presets: [ ['env', {'modules': false}] ] } } // after { loader: "babel-loader", }
{ "presets": ["es2015"], "plugins": ["transform-object-rest-spread"] }
webpack4のモード
webpack4からはmodeを指定することが出来るようになっていて、そこで開発用かプロダクション用か切り替えることが出来る。
もちろんwebpack.config.jsにmodeを追加することも出来るがwebpackコマンドの引数に追加することも出来る。
なので、下記のように指定してあげるとnpm run ***
で使い分けが出来て便利。
"scripts": { "dev": "webpack --mode development", "build": "webpack --mode production", }
LocalStorage
Local Storageには文字列で登録されるので、そのままオブジェクトを突っ込むと[object Object]となってしまう。
const obj = {x: 1, y: 2}; localStorage.setItem('items', obj); localStorage.getItem('items'); // [object Object]
なので適宜、設定時と取得時にJSONの変換を行う必要がある。
// 設定時 const obj = {x: 1, y: 2}; localStorage.setItem('items', JSON.stringify(obj)); // 取得時 JSON.parse(localStorage.getItem('items')); // {"x":1,"y":2}
Vue.js、Vuex
Vue.jsのwatch
監視する値が何回層かの配列やオブジェクトだったときにウォッチャではdeepというプロパティを定義してtrueにすると内部の値も監視対象としてくれます。
data() { return { items: [ {x: 0, y:0}, ], }; }, // 内部監視あり watch: { items: { handler() { // 処理 }, deep: true, } } // 内部監視なし watch: { items() { // 処理 }, }
SVGの描画時マウントされていないために参照できない問題。
ref の登録タイミングに関する重要な注意事項として、参照自体は、render 関数の結果として作成されているため、最初の描画においてそれらにアクセスすることができません。それらはまだ存在しておらず、$refs はリアクティブではなく、従ってデータバインディングのためにテンプレートでそれを使用すべきではありません。
地味に躓いた。
text要素の幅を取得して、背景にその幅分のrect要素を広げたかったのだけど、うまくいかなかった。
最終的にはVueのmouted()でフラグを有効にし、それの真偽で処理をするようにした。
data() { return { isMounted: false, }; }, mounted() { this.isMounted = true; }, // ~~~~~~~~ // if (this.isMounted) { // 特定要素の幅取ったり }
Vuexの各アクションなどのキー名
Vuexでミューテーションやアクションのキーに定数を使用することを提案している箇所がある。
これは本当に好みの問題かもしれない。
一応使って実装してみた。
こんな感じ。
// types.js export const set = 'set'; // mutations.js import * as types from './types'; export default { [types.set](state, items) { state.items = items; } } // app.vue this.$store.commit(types.set, items); // 文字列のままだと this.$store.commit('set', items);
定義名はmutationとaction、getterに適用。
gettersはプロパティに生えるから使わなくてもいいかもしれない。
this.$store.getters[types.initialItems]; this.$store.getters.initialItems;
VuexのmapStateとmapGettersを産出プロパティに
通常産出プロパティにスプレッド演算子の関係でmapStateとmapGettersは設定できないんだけど無理やりやってみた。
同じキー名のプロパティがあった場合、後方の値で上書きされるので注意。
computed: Object.assign(mapState([ 'items', ]), { ...mapGetters([ types.urgentPosition, types.importantPosition, ]), }), }
これからやってみたいこと
このアプリは遊びで試してみたかったのでそんな時間をかけるつもりはないもののやりたことは色々ある。
- 文字が枠外に出たりするのでちゃんと文字列が枠内に収まるように
- 型の導入
- LocalStorage以外にサーバーと連携してDBに保存するとか
- なんかもっとかっこいい見た目にしたい!!!
ぶっちゃけ見た目や使い勝手悪すぎて自分のUI周りの実装力の低さを思い知ってしまった。
ここはこれからどれだけ勉強するか悩みどころ。
今までの参加した勉強会のメモを公開した
タイトルの通り。
とあるところでキャリアの成長に関してもっと情報公開して恥をかいていこうよということを言われ、まずは今まで参加した勉強会のメモとかを公開していくことから始めようと思った次第。
かなり雑な部分もあるがそこらへん指摘貰えれば直していきます。
まとめ終わったので再度公開します。
- PHP7 Casual Talks #1に参加してきた - いがにんのぼやき
- Sansan&Eight技術責任者のリアルトークイベントに参加してきた - いがにんのぼやき
- Sansan × Sony アプリ勉強会 - いがにんのぼやき
- エンジニアが知っておくべき最低限のアルゴリズム《基礎》に参加してきた - いがにんのぼやき
- PHP BLT #3に参加してきた - いがにんのぼやき
- 第3回CodeIQ感謝祭「春のエンジニアまつり」に参加してきた - いがにんのぼやき
- 再演 ~ 組織にテストを書く文化を根付かせる戦略と戦術 +αに参加してきた - いがにんのぼやき
- 管理画面チラ見せ♡ナイト #3に参加してきた - いがにんのぼやき
- Laravel勉強会 InnoCAFE#21に参加してきた - いがにんのぼやき
- 第4回CodeIQ感謝祭「CodeIQ夏の陣」に参加してきた - いがにんのぼやき
- HTML5 Conference 2016に参加してきた - いがにんのぼやき
- Test Engineers Meetup #1に参加してきた - いがにんのぼやき
- 第5回CodeIQ感謝祭「食欲とプログラミングの秋」に参加してきた - いがにんのぼやき
- AbemaTV DEVELOPER CONFERENCE 2016に参加してきた - いがにんのぼやき
- Rubyエンジニアが語る、2016年の振り返りとこれからに参加してきた - いがにんのぼやき
- RakSul Meetup #05 ~RubyBizグランプリ大賞を受賞した「ハコベル」のすべて~に参加してきた - いがにんのぼやき
- マッチングサービスの運営で大切にしていること【Product Marketing Meetup】に参加してきた - いがにんのぼやき
- Railsを使ってサービスの成長を継続させるぞ!(FiNC×みんなのウェディング)に参加してきた - いがにんのぼやき
- JapanTaxi×エニグモ×一休のモダンな環境へのリプレイスに関する勉強会に参加してきた - いがにんのぼやき
- コード改善 meetup #1に参加してきた - いがにんのぼやき
- 【一休 × bitFlyer】C#を使ったサービス開発の裏側に参加してきた - いがにんのぼやき
- YAP(achimon)C::Asia Hachioji 2016mid(2日目)に参加してきた - いがにんのぼやき
- Vue.js Tokyo v-meetup #4に参加してきた - いがにんのぼやき
- Ultra Beer Bash 2017に参加してきた - いがにんのぼやき
- HTML5 Conference 2017に参加してきた - いがにんのぼやき
- PHPカンファレンス 2017に参加してきた - いがにんのぼやき
まとめてみて分かったが熱量のあるものとないものでメモの質に大きく差があるなあと思った。
これからは公開することも意識してメモの質を上げたい。
2018年の目標
2018年に入って早2カ月。
今更ですが今年の目標を考えたいなーと。
なんで今更?
実は年末年始の時からこの時期だと思ってた。
そう、転職先が決まった時だ。
無事、4カ月という長い無職期間を経て転職先を決めた。
年末年始、人々が目標を書いているのを横目に僕は転職先が決まったら書こうと思ってた。
理由としては、転職先によって達成したい目標、達成しやすさって変わると思う。
それは事業的なミッションだったり、技術的な成長だったり、個人の趣味や人生についてだったり。
職場によってそれを達成する土壌があったり色々な要素が絡み合った上で目標を決めようと思ってた。
目標
- 一週間に一回は技術系記事を書く
- 二カ月に一回は登壇
- Microsoft MVPを受賞する
- TOIEC 600点
一週間に一回は技術系記事を書く
一週間仕事なり趣味で開発していれば気づきやつまずいたりしたときの対処など書くことはあるだろう。
無ければ技術的なことが出来ていない可能性が高い。
そういう自戒も込めつつ一週間に一回は記事を書けるように励みたい。
二カ月に一回は登壇
アウトプットの機会を増やしたい。
これは前述の記事と同様に気づきなどの棚卸の面と、記事を書く技術とは別の発表技術を学んでいきたい面もある。
ある程度露出を増やしつつ技術的に人に触れ合う機会も作りたいという面もある。
あと今まで登壇をしたことはあったけど身内に近いコミュニティや初心者向けが多かった。
なのでカンファレンスなどに登壇も目指していきたい。
Microsoft MVPを受賞する
これは前述の二つのアウトプットを継続的に行って目指していきたい。
Microsoft MVPとはいわゆる技術発信を行ってる人に対する表彰制度みたいなものである。
自薦他薦問わずエントリーでき、受賞すればMicrosoftから特定のソフトの提供や限定イベントへの参加権などを特典として貰える。
気になったらググってほしい。
特典が目的というわけではないが一つのアウトプットしていった結果としての目標としたいと思う。
TOIEC 600点
これは個人的なコンプレックス。
エンジニアでありながら英語が全くできないのでこれを克服していきたい。
全くできないというのは具体的にどんなレベルかというと、2015年12月に受験したときのスコアは355点である。
本当に大学を卒業しているんだろうか。僕のキャンパスライフは夢だったのかもしれない。
英語のページを見るときには常にGoogle翻訳を横に並べている。
エンジニアとしては英語で書かれた第一ソースを読めるか読めないかは成長に大きく変わってくる。
ここは努力していきたい。
最後に
目標立ててみた。
年末だけじゃなくて時折見返してうまく達成していこうと思う。
転職したばかりは忙しくて大変そうだが頑張っていこう。
Webpack4ではCLIが切り離された
Webpackの4からCLIが別に切り出された。
なのでそれ以前のWebpackと同じように実行するとエラーが発生する。
> [project name]@1.0.0 dev [working directory] > webpack The CLI moved into a separate package: webpack-cli. Please install 'webpack-cli' in addition to webpack itself to use the CLI. -> When using npm: npm install webpack-cli -D -> When using yarn: yarn add webpack-cli -D npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! time-management-matrix@1.0.0 dev: `webpack` npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the time-management-matrix@1.0.0 dev script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR! C:\Users\[user name]\AppData\Roaming\npm-cache\_logs\2018-02-28T18_56_30_320Z-debug.log
言われた通りインストールしてみる。
npm install webpack-cli -D
ちなみに-Dは--save-devらしい。
npm installのhelpに出てこないので知らんかった。
これでまたwebpackコマンドを打つとビルドが実行できるようになる。
今までの参加した勉強会のメモを公開することにした
再度まとめて記事を上げなおしたので上記参照。
Swagger EditorとSwagger UIをDockerで動かす
Swagger Editor
docker pull swaggerapi/swagger-editor docker run -d -p 81:8080 --name swe swaggerapi/swagger-editor
一部generator.swagger.ioから外部リソースを取得してるみたい。
初期表示のAPI仕様は外部から取得したファイルを表示しているよう。
今のSwagger Editorはここら辺を変更できなくて、後述のSwagger UIだと変更できるようだ。
Swagger UI
docker pull swaggerapi/swagger-ui docker run -d -p 82:8080 --name swu swaggerapi/swagger-ui
中身を見ているとDocker起動時に環境変数を設定することで一部パラメータを変更できるようになっている。
例えば下記のようにAPI_URLを設定することで表示に使用するJSONを変更したりできる。
docker run -d -p 82:8080 --name swu -e API_URL=http://generator.swagger.io/api/swagger.json swaggerapi/swagger-ui
2018/02/21現在での最新コミットではポートやOAUTH関係の環境変数が設定できるようになっている。
Docker使用時に走るdocker-run.shというファイルで環境変数を使った設定を行っている。 この環境変数に関するドキュメントがどこかにあれば教えてほしい。
こんな風にSWAGGER_JSONという環境変数にコンテナ内で存在するファイル名を指定してあげるとそれをSwager UIに表示することができる。
docker run -d -p 82:8080 --name swu -e "SWAGGER_JSON=/test.json" -v d:/Docker/swagger/test.json:/test.json swaggerapi/swagger-ui
docker-compose
docker-composeだとこんな感じ。
swagger-editor: image: swaggerapi/swagger-editor container_name: "xxx-swagger-editor" ports: - "8081:8080" swagger-ui: image: swaggerapi/swagger-ui container_name: "xxx-swagger-ui" ports: - "8082:8080" volumes: - ./swagger/test.json:/test.json environment: SWAGGER_JSON: /test.json