v-forでkey属性を指定する意味
vueのコードを見ていて v-for
などで key
属性が何かを忘れて調べるをすること3回目。
覚えるための記事ということで。。自分用のメモ
key属性の仕様
まず仕様はこちら。
キーがない場合、Vue は要素の移動を最小限にするアルゴリズムを使い、できる限り同じ種類の要素をその場でパッチや再利用しようとします。 キーがある場合、キーの変更順序に基づいて要素の順番を変更して、存在しなくなったキーを持つ要素は常に削除や破棄されます。
key属性なしで動かしてみる
See the Pen key属性無しv-for by azukineko (@azukineko) on CodePen.
この色のテキストボックスに色を入力する
その後に3番のレモンを削除すると、項目がズレてしまう
これはkey属性を指定しない場合にできる限り同じ種類の要素を再利用しようとする仕様のため。
key属性ありで動かしてみる
See the Pen by azukineko (@azukineko) on CodePen.
こちらで同じ動作をさせるとkey属性を指定したtr要素ごと削除などが適用される。
Railsでページネーションを自作する
Railsでページネーションをgemを使わないで実装した事について書きます。
「ページネーションを使える様にしましょう」という事は良くあり、、というかWEB開発では必ずある。
今まではRailsでは kaminari とか will_paginate を使ってきて、便利だし個人的には使えば良いと思っている。
今回は特殊な事情から自作する事になった。
何故自作したか
- アプリの構成が特殊で、kaminariを使おうとしたけど何故かkaminariが正常にロードされなかった。
1.
の問題を深掘りしたい気持ちもあったものの、作ってしまった方が早いとなった。- カスタマイズしやすいし、アプリの影響範囲もわかるので自作の良い点も多い。
ページネーションの処理をmodelによせたかった
初めはコントローラ側にページネーションの処理を書いていたのですが、
コントローラにこんな感じのコードが複数発生してきて、共通化できる様にしたかった。
@page = params[:page].to_i < 1 ? 1 : params[:page].to_i @offset = PER_PAGE * (@page - 1) relation = Huga.where(coditions) @hugas = relation.offset(@offset).limit(PER_PAGE).order(id: :desc) @total_count = relation.count
modelに移した
modelに移す上で、kaminariのコード を見てみると、ActiveRecord::QueryMethods#extending というメソッドが使われている。
ActiveRecord::Relation
にメソッドを追加したり拡張ができるみたいで便利!
ドキュメントの例もページネーションだし、これ使ってみようという気持ちになる。
という事で実装してみた所、以下の感じになった。簡単・・・。
module Hoge module Concerns module Paginate extend ActiveSupport::Concern included do scope :page, ->(page, per_page) do all.extending(Hoge::Concerns::Paginate::Extends).paginate(page, per_page) end end module Extends def paginate(page, per_page) @current_page = page.to_i < 1 ? 1 : page.to_i offset = per_page * (@current_page - 1) offset(offset).limit(per_page) end def current_page @current_page end def limit_value self.values[:limit] end def offset_value self.values[:offset] end def total_count except(:limit, :offset).count end end end end end
使い方
class Huga < ApplicationRecord include Hoge::Concerns::Paginate end hugas = Huga.page(3, 30) # 1ページ30件の3ページ目を取得する hugas.current_page # 3 hugas.limit_value # 30 hugas.offset_value # 60 hugas.total_count # 全件の件数
RSpecによるRailsテスト入門を読んだ
「テスト経験が少なくて・・・」みたいな話をした時に、
色々な方から「everyday rails読むといいよ!」と勧めて頂いた。
3人の方から勧められたので、最早自分が読んでいないことに焦りを感じたレベル。
なので「everyday Rails RSpecによるRailsテスト入門」を読んだ話。
本の概要
書籍名
Everyday Rails - RSpecによるRailsテスト入門
URL
https://leanpub.com/everydayrailsrspec-jp
本から学びたかったこと
RSpecはともかく触れてこなかったので、1から知りたかった。
読んでみてよかったこと
- 本を順番通りに読み勧めて行くだけで開発の流れが一通り掴める作りになっていた
- マニュアル的な要素は少なく、よく使う機能に関して使い方や理由がしっかり書かれている
- RSpecだけでは苦労する点などは実現する為のgemの紹介、使い方の説明がある
- RSpec3.6 ✖️ Rails 5.1 と読んだ時点で新しいバージョン対応であり、不安がなかった
- サンプルコードがしっかりとあり(githubに公開)、解りにくい所は試すことができる
- 書き手の言葉が強制感がなくとても優しく・共感が持てる
次からのTry
- リポジトリで勉強の記録を残す
自分で試したコードや印象に残ったコードなどは後から見れる様にしたら良かったと思った。
自分のgithubにプッシュしておけば良いので、次から技術書を読むときは一回リポジトリを作ってみる。
次何読もうかな
- 一旦は家に届いてまだ読んでいなかったWeb+DBを読む
2018年年越し前の日記
久しぶりに実家の群馬に帰ってきて年越しすることになった。
実家に帰るとVBAやスマホ周りのお仕事が結構溜まっていたりする。
PCとタブレット使いこなして仕事を依頼してくるうちの親は結構凄いのだろう・・・。
今年は本当に多くのことがあった!
良かったことと悪かったことを3つぐらいずつ書こう!と思ったのだけど、
悪かったことが離婚だったり・・・お酒なしには辛い事が多く、まとまらなかったので
「書けることから」ってことで良かったことだけ書く!
良かったこと
良い転職先に巡り会えたこと
9月から新しい職場でお世話になっている。
転職直後にCTOとの1on1で転職した理由をお話した際に、
「君は一発で素晴らしい会社を選んだよ」と言われて泣きそうになった。
約4ヶ月新しい環境に身を置いて、思う今の職場の素晴らしいことは、
* 皆が「こういう事がやりたいから、こういう努力をしている」を共有しあっている事
* 失敗してもトライした人に責任を押し付けることなく、「ナイストライ!」と褒める風土
* 技術力が高く、打ち合わせ等があると知らない事が必ず出てくる
良い環境に身をおくからにはその環境を構成する1人である為に悩む事も本当に多い・・・。
が、来年のこの記事を書く頃には胸を張ってこの会社の一員と言える様に頑張っていきたい。
前の職場の方と良い関係で離職できたこと
去年の今頃、周りの人たちも30歳をこえて仕事・家庭と悩みが増える。
一方で時間が取れずに人間関係が薄くなりやすく、来年は人間関係は大事にしたいと思っていた。
そんな中で恐らく恨まれる事なく前の職場を辞められ、
前職の方とも時々飲みに行ったりして、、
仕事が別になってもそんな関係でいさせて貰えたのは個人的に本当に良かった。
EvoJapanというゲームのイベントに参加した
格闘ゲームが好きで、今年の上旬はKingOfFighters14というゲームをやっていた。
そんな格闘ゲーム好きには夢の様な世界大会であるEvoJapanというイベントが1月に開催され、それに参加した。
世界中から様々なゲームのプレイヤーが東京に集結する一大イベントとなった。
結果は65位とふるわない結果となったものの、素晴らしいイベントに参加できた事が自分に取ってとても嬉しい事だった。
転職前後は優先順位からゲームを避けていたのですが、
計画的に時間作って来年はゲームも真剣にやっていきたい。
PlantUMLでER図を書いてみる
プロジェクトでPlantUMLで図を書いている人が居るんです。
これが自分が過去感じて来た課題を解決してくれる物に見えたので自分でも書いてみる。
解決してくれそうと思っている課題
- ER図やシーケンス図のバージョン管理
- 他の仕組みとの連携
- ER図からRailsのmigrationファイルを自動生成とか、その逆とか
Macで環境を作る
brew install graphviz # brew cask install java brew install plantuml
エラーになって 以下のメッセージが出たので、自分は上の brew cask install java
を挟む事になった。
% brew install plantuml Updating Homebrew... plantuml: Java is required to install this formula. JavaRequirement unsatisfied! You can install with Homebrew Cask: brew cask install java You can download from: https://www.oracle.com/technetwork/java/javase/downloads/index.html Error: An unsatisfied requirement failed this build.
とりあえず作ってみる
1. ディレクトリ作る
mkdir work/test_plant_uml
2. ソースを書いてみる
vi work/test_plant_uml/recipes.uml -------- @startuml package "cock" as cock { entity "recipes" { + id [PK] == name:string } entity "recipe_ingredients" { + id [PK] == #recipe_id:references #ingredient_id:references quantity:integer } entity "ingredients" { + id [PK] == name:string } } recipes --{ recipe_ingredients ingredients --{ recipe_ingredients @enduml
3. 図を生成してみる
plantuml
コマンドに引数でファイルを渡すとpngを生成してくれる。
% plantuml work/test_plant_uml/recipes.uml
% ls work/test_plant_uml/
recipes.png recipes.uml
4. 出来た図
それっぽいのがとりあえずできた!
デザインも変えられる
デザインも変えられる。 (参考: http://plantuml.com/skinparam )
例えば以下のコードをumlファイルに追加して、図を生成し直す。
skinparam roundcorner 20 skinparam class { BackgroundColor PaleGreen ArrowColor SeaGreen BorderColor SpringGreen } skinparam stereotypeCBackgroundColor YellowGreen
素敵!・・・とはこれはならなそうだけど、いい感じにできたら良さそう。
ActiveRecord::RecordNotUniqueが起きるケースのRSpecを書いた
Ruby経験は多いけど、テストコードの経験が少ない・・・そんなRuby技術者です。
最近テストコード(RSpecなど)を大事にするプロジェクトに携わらせて頂く事になりました。
経験が少ない自分には涙が出るほどありがたいお話なのですが、、わからないことが多い!
テスト書くのに時間が掛かりコミットしていけないなんて事に・・・なりましたorz
これはすぐ解決できる問題ではない認識で、少しずつ学んでいくしかないとは思うのですが、
少しでも早く改善していく為にハマッたポイントは振り返る。
なのでRSpecについて書きます!
書くこと
RSpecのreceive
、 return_to
、 and_raise
によるモックで ActiveRecord::RecordNotUnique
の例外が起きるパターンのテストコードを書く。
解決したかった課題
ボタン連打により発生する ActiveRecord::RecordNotUnique
をRSpecでまず再現し、
再現した後に改善していきたい。
前提
例として扱うアプリ
- レシピに材料と個数を指定し、材料を登録する機能を想定
- レシピに材料が未登録の場合は中間テーブルを作成する
- レシピにその材料が既に登録されている場合は中間テーブルの個数を更新する
- ボタン連打などでほぼ同時に2リクエストが来た時、
2.
の動作が2回動き、
DB側でDuplicate Entry
エラーとなる。
ActiveRecord
としてはActiveRecord::RecordNotUnique
の例外をraiseする。
コードにすると以下のイメージ
Recipe#add_ingredient!
が今回のテスト対象
class Recipe < ApplicationRecord has_many :recipe_ingredients def add_ingredient!(ingredient, quantity) recipe_ingredient = self.recipe_ingredients.find_or_initialize_by(ingredient: ingredient) recipe_ingredient.increment(:quantity, quantity) recipe_ingredient.save! end end class Ingredient < ApplicationRecord end class RecipeIngredient < ApplicationRecord belongs_to :recipe belongs_to :ingredient validates :ingredient_id, presence: true, uniqueness: { scope: :recipe_id } end
どう改善したいか?
ActiveRecord::RecordNotUnique
がraiseされた場合は、リトライしたい。
自分のハマったポイントと書いたRSpec
- リトライ処理をさせた時に1回目と2回目でメソッドに異なる動作をさせたい
and_return
に引数を複数指定すると呼び出しごとに返り値を変える事ができる- 引数を使い切った後は最後の引数を返す動作となる
# find_or_initialize_byに固定で1回目は新規レコードを返させ、2回目は登録済みのオブジェクトを返させる new_recipe_ingredient = recipe_ingredients.new(ingredient_id:ingredient.id) allow(recipe_ingredients).to receive(:find_or_initialize_by).and_return(new_recipe_ingredient, recipe_ingredient)
ActiveRecord::RecordNotUnique
が起きる状況を再現させる事が出来ない- 状況揃えて起こさせる事は難しいのでモックに置き換えて無理やり例外を起こさせる
# 1回目の新規レコードのみ固定でActiveRecord::RecordNotUniqueをraiseする様にする allow(new_recipe_ingredient).to receive(:save!).and_raise(ActiveRecord::RecordNotUnique)
書いたテストコード全体
require 'rails_helper' RSpec.describe Recipe, type: :model do describe '#add_ingredient!' do # recipesに目玉焼きレコードを登録 let(:recipe) { FactoryBot.create(:recipe) } # ingredientsに卵レコードを登録 let(:ingredient) { FactoryBot.create(:ingredient) } context '同じ材料が同時に登録されたとき' do # 目玉焼きの材料に卵を1個登録しておく let(:recipe_ingredient) { FactoryBot.create(:recipe_ingredient, recipe: recipe, ingredient: ingredient, quantity: 1) } before do # 目玉焼きの材料のCollectionProxyに固定のオブジェクトを返させる用に変更 recipe_ingredients = recipe.recipe_ingredients allow(recipe).to receive(:recipe_ingredients).and_return(recipe_ingredients) # find_or_initialize_byに固定で1回目は新規レコードを返させ、2回目は登録済みのオブジェクトを返させる new_recipe_ingredient = recipe_ingredients.new(ingredient_id:ingredient.id) allow(recipe_ingredients).to receive(:find_or_initialize_by).and_return(new_recipe_ingredient, recipe_ingredient) # save!時にvaidationによりActiveRecord::RecordInvalidに弾かれるので、 # 1回目の新規レコードのみ固定でActiveRecord::RecordNotUniqueをraiseする様にする allow(new_recipe_ingredient).to receive(:save!).and_raise(ActiveRecord::RecordNotUnique) end it 'リトライされて指定された個数分の材料が追加登録されること' do expect { recipe.add_ingredient!(ingredient, 1) }.to change {recipe.recipe_ingredients.find_by(ingredient_id: ingredient.id).quantity}.by(1) end end end end
結果
やったー!テストに失敗した。
% bundle exec rspec spec/models/recipe_spec.rb (git)-[master] F Failures: 1) Recipe#add_ingredient! 同じ材料が同時に登録されたとき リトライされて指定された個数分の材料が追加登録されること Failure/Error: recipe_ingredient.save! ActiveRecord::RecordNotUnique: ActiveRecord::RecordNotUnique # ./app/models/recipe.rb:18:in `add_ingredient!' # ./spec/models/recipe_spec.rb:43:in `block (5 levels) in <top (required)>' # ./spec/models/recipe_spec.rb:42:in `block (4 levels) in <top (required)>' Finished in 0.19459 seconds (files took 5.26 seconds to load) 1 example, 1 failure Failed examples: rspec ./spec/models/recipe_spec.rb:41 # Recipe#add_ingredient! 同じ材料が同時に登録されたとき リトライされて指定された個数分の材料が追加登録されること
ブログ書けるようになるぞ
実は「継続的にブログを書くぞ!」という目標を3ヶ月間掲げていた。
しかし3ヶ月間で書いた記事は1つ・・・これはなんとかしなければ・・・
という事で自分なりの方針決めをやろう。
何故ブログ書くんだっけ?
- 自分の技術者の価値を証明出来るようにしておきたい
- 外向けに発信する事で組織の価値を高められる技術者でありたい
ブログを書く為に必要なことはなんだろう?
とりあえず1つのブログ記事を書く為に必要なことをリストアップ
- ブログの登録
- 記事を書き方(マークダウンなど)の知識
- 題材
- 書く為の時間
あまり思いつかなかった・・・
それぞれ出来ているだろうか?
ブログの登録
記事を書き方(マークダウンなど)の知識
ブログは登録済み!
1つは記事を書けているのでマークダウンもOK
題材
ここで3ヶ月間の反省として思ったのは以下。
- 題材に対して不足している知識の調査や検証に時間が掛かって結局書かなかったりした
- 書く事と書かない事をしっかり決めていないと広がりすぎてまとまらず、書かなかったりした
書く為の時間
- 時間がない事はない。例えば日曜日の夜2時間やるとか決めればやれる。
当面の間の対策
- 習慣になるまでは内容は気にせず簡単な題材でとにかく書く
- 題材を決めの時間と書く時間を分け、題材決めをしっかりやる
- 題材決めと書く時間を一旦カレンダー管理でしっかりやる