こんにちは、宇賀神です。
みなさんはバリデーション、書いてますでしょうか。
フレームワークとかライブラリの都合で仕様が固定されることも多く、設計とかあまり意識しない&意識したところで選択肢がない、的な状況もあると思うんですが、最近はフロントの自由度が上がっている影響で、考えられることが増えてきた感があります。
そんなバリデーションを設計するときに、UIUX的な観点も含めて自分なりに意識しているポイントを書いていこうと思います。
わりと設計思想の話なのでケースバイケースで正解は違うだろうし、あくまでいまの段階で自分が思うことなので、これが絶対だとも思ってないです。このあたりを前提として、生暖かく読んでください。
たまに聞くのが「バリデーションの処理はAPI側に書いて、フロントは422レスポンス受け取って結果を表示すれば良いじゃない」みたいなのがあります。処理の内容が同じになることが多いので結果として2重管理になったりする、みたいな観点ですね。
後述しますが、APIとフロントが行うバリデーションは厳密には役割が違う、というのが今のとこ自分の考えです。なので、基本的にはどちらにもバリデーションが実装されているのが理想かなと思います。
フロント側のバリデーションの役割はざっくり「入力内容がinvalidかどうか、なぜinvalidなのかを伝えること」だと思っています。
極端な例だけど、全てのバリデーションエラーを「エラーが起きました」という表示しかしないフォームがあったとして。入力必須のフィールドが空でsubmitされてエラーになったとき、ユーザーはなぜエラーになったのかが分からず、手当たり次第に色んな方法を試すんだと思います。これがあんまり良くなさそうと。
サービス内で使う文言については色々な箇所で気をつけるべきですが、エラーメッセージの文言とかは特にシビアに考えるようにしています。さっきの例だと「xxxを入力してください」みたいなメッセージを表示できると、ユーザーが次に何をするかが分かりやすくなるんだと思います。
また、その手のフィードバックはなるべく早くユーザーに伝えるべきだし、もっと言うとエラーになることが分かっているのにわざわざサーバーと通信させることもないかなと。フロントでバリデーションをしているとそのあたりのstate管理ができるので、「invalidな入力がある場合submitボタンをdisabledにする」みたいなこともやれる。
まとめると、結果として以下のような触り心地になってると良さそうと思ってます。
前述した「フロントとAPIのバリデーションは役割が違う」についてもう少し。
フロントのバリデーションの役割はさっき書いた通りですが、API側のバリデーションの役割はもうシンプルに「データ不整合を起こさせない」に尽きると思ってます。データ不整合って本当に厄介で、原因を見つけるのも大変だし、不整合を正すのもサービスが大きくなればなるほど大変になります。
ただ、API側だけでバリデーションをすると前述したような「ユーザーに適切にフィードバックを返す」がやれないケースが出てくるので、フロントでもバリデーションをした方が使いやすいサービスになるかなと思ってます。
ざっとまとめると、こんなふうにフォームが出来ているといいよね、ということでした。
いざ書いてみると結構当たり前なことを書いてる気がするけども、こういうことをちゃんと意識するのって意外と難しくてかなり大事なことかなとも思うのです。
昨今は質の高いWebサービスが世に溢れていて、ユーザーの見る目もどんどん厳しくなる中なので、こうした細かいところに気をつけることは忘れずに開発していたいなーと思うのでした。
また書きます〜
]]>すごくお久しぶりです、宇賀神です。
あれから色々ありまして、一度会社員になったりしたのですが結局またフリーランスやっています。近頃は割と足元も固まってきたところもあり、さすがに放置しすぎ&あまりアウトプットをしていなかったのでまたちゃんと書いていこうかなと思っています。ちょっとこのブログも3年くらい更新していなかった気がするんですが、また更新をする前に今回はその間何をしていたかをちょっと書いておこうと思います。
新卒の会社を辞めてからフリーランスをしていたのですが、2020年の2月にLINE GrowthTechnology(以下、LINE GT)に入社していました。元々フリーランスやるのはちょっと早かったかな、というのも自覚としてあった中で、当時の自分にまだなかった経験を多く得られそうな場所に就職を希望したのが理由だったりします。あとはシンプルに家で仕事をし続けるのが辛かった、オフィスが欲しかった。
晴れて入社が決まって、「やったー綺麗なオフィスで働くぞー」などと浮かれていたところにコロナが直撃しました。一年間のリモートを経て出社することを希望したのに、再び自宅で仕事することになってしまいました。人生うまくいかねえなぁと思いました。
とまあそんな話は割とどうでも良くて、LINE GTでは環境に恵まれたこともあり今までにない経験がたくさん得られました。
技術的な部分ではGoやJavaなど静的型付け言語での開発、Dockerを始めとしたコンテナ技術を使ったCI/CD構築、AWSを使ったインフラ環境や監視を含めた運用周りを考慮しつつ、TerraformでのIaC化などをやらせてもらいまして、Rails一辺倒だった技術スタックに随分と幅ができたかなと思っています。代わりにそれまでのスキルで役に立たないものも多く、辛い部分もありましたがそこを含めての狙いだったこともあり、頑張った甲斐はあったと思います。
そもそも扱うサービスのユーザー数が多いので、適切に最適化を行うことが自然と強制されていたことも、今まで随分と適当なコードを書いてしまっていたなーとか思っ自分の知見を広げる上でとても貴重な経験になりました。
そんなこんなで2年半ほど前職のお世話になった後、一年前くらいからまたフリーランスをやっています。今回の転職に際しても、たくさんの方に相談に乗っていただきました。ありがとうございます。皆さんのおかげで宇賀神はエンジニアとして存在出来ていると思います。
インフラ周りや他言語での開発を経験したので、お受けできる仕事の幅が広がったなーというのは特に感じているプラスの部分です。最近はフロントエンド周りに触れることも多く、個人的な弱点をさらに補強できている手応えもあります。
基本的には人づてで仕事をいただいているのですが、リソースの都合上お断りしてしまっているものもありまして、申し訳ない気持ちと、もうちょい上手いことやれんか、というのを自分に思っています。
相変わらず、これ!というほどの人生の目標はないまま進んでいるんですが、気づけばこんなところまで来たなーという感じにもなってきました。エンジニアとしては一応10年ほどになるのですが、ここまで生きてこれた上に、独立までして生活が成り立っているのは9割くらいみなさんのおかげです。
いまの立ち位置は一つの到達点でもある気がしていて、つまりは次にどこを目指すかを考えなきゃいけない状態かなーと思っています。昨今は何かとAIが話題だったり、相変わらず流れが早い業界ですが、ちょっとまた3〜5年後の未来を考えようと思います。
「技術的なことしか書かねえ」とか言って始めたブログにこんなことを書いてしまいました。次はちゃんと技術的なことを書きます。
それではまた〜
]]>はいどうもお久しぶりです。宇賀神です。
少し前のことになるのですが、私のPCで難解な問題が発生していたので、そのときにやった対処法について書きます。
仕事でお客さんのとこのサービスを開発していた際、立ててもらった環境にアクセスしようとすると、以下のような画面が表示される状態になってしまいました。
詳細なドメイン名は伏せておりますが、どうやら .dev
がついたサイトにアクセスしようとするとこうなっておりました。
ローカルマシンの /etc/resolver/dev
を確認してみるとこんな記述が。
# Lovingly generated by Pow
nameserver 127.0.0.1
port 20560
どうやらPowというアプリをインストールした際に勝手に書き換えられてしまったようです(またお前か。。)。
というわけで、下の2行をコメントアウトしました。
# Lovingly generated by Pow
# nameserver 127.0.0.1
# port 20560
試しにアクセスしたところ、問題なく表示されるようになりました。
またしてもPawにやられるという自体(前回のやらかしは こちら を参照)。
なんか変な入れ方してしまったのかなぁ。
とりあえず無事に直って良かったです。
また更新します〜。
こんにちは。宇賀神です。 今回は Qiitaに投稿した記事 をこちらにも転載したものになります。
Rubyの2.4以降のバージョンに対してセキュリティリリースが出たそうで。
https://www.ruby-lang.org/ja/news/2019/10/01/ruby-2-6-5-released/
使っているrubyのバージョンを更新しようとしたところ、brew upgrade ruby-build
したのに2.6.5がない。
$ brew update && brew upgrade ruby-build
$ rbenv install --list
...(中略)...
2.6.3
2.6.4
2.7.0-dev
2.7.0-preview1
...(中略)...
どうやら brew upgrade ruby-build
でインストールできるのは 2019/08/28
リリースのバージョンまでらしい(2019/10/02現在)。
https://formulae.brew.sh/formula/ruby-build
というわけで、gitのHEADから拾ってきてruby-build
を更新する必要が出てきました。
インストールコマンドにオプションを付けるだけ
$ brew install ruby-build --HEAD
が、既に ruby-build
が入った状態でこれをやると怒られます。
Error: ruby-build 20190828 is already installed
To install HEAD,first run `brew unlink ruby-build`.
Warning: Skipping (old) /usr/local/Cellar/ruby-build/20190828 due to it being linked
まあそりゃそうだ、ということで、一旦 unlink
して外してあげてから再インストールします。
$ brew unlink ruby-build
$ brew install ruby-build --HEAD
Rubyのインストールをしたらバージョン切り替えて確認
$ rbenv install 2.6.5
$ rbenv local 2.6.5
$ ruby -v ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin18]
大丈夫そうっすね。
というわけで、ちょっと邪道なHomebrewのプラグイン更新のやり方でした。セキュリティリリース自体は迅速な対応が必要になるものなので、今後も使う機会はありそうな気がしています。 また更新します〜〜。
おはようございます。腰の痛みが本格化してきました宇賀神です。
今日はいわゆるGoF本にて紹介されているデザインパターンのお話。
GoF本で紹介されるデザインパターンは23種類あります。自分への備忘録も兼ねて記事にしたいなーと思った次第。
本当は全部について詳しく解説していきたいのですが、多い。なんせパターンの数が多い。
そこで、開き直って全部のパターンを1行だけで解説していきたいと思います。
ここからは各パターンについて解説します。
ループ用クラスを作る。
ラッパークラスを挟んで使いやすくする。
抽象クラスで形だけ決めてサブクラスで実装する。
Template Method パターンをインスタンスの生成に適用する。
コンストラクタをプライベートにしてインスタンスを1つしか作れなくする。
インスタンスを原型からクローンして作る。
パーツごとにクラスを作って分解する。
大規模な Factory Method。
「機能」と「実装」でクラスを分ける
サブクラス単位で実装を切り替える。ガンガンいこうぜ。
枝葉の上に抽象クラス被せて同一視する。ファイルシステム。
飾り付けをクラスで切り替える。ストラテジーに近い。
Iteratorで回しながら個別に処理する。
クラスごとの役割に徹してたらい回しにする。
窓口を作って呼び出すクラスとメソッドを1つに絞る。
状態管理(など)を1つのクラスに任せる
通知を受けてアクションを起こす観察者(Observer)を立てる。
状態を保存するクラスを作る。undoやredoがやりやすい。
「昼」や「夜」などの状態ごとにクラスを作る。コード内の分岐を減らせる。
共有できるものはなるべく共有する。
使うクラスと使われるクラスの間に1つクラスを挟んで使いやすくする。
「命令」の内容と「実装」の内容を分離する。
正規表現とか文法の異なる言語を共通の文法で使えるようにする。
以上、デザインパターン1行解説でした。
こうしてみても、正直理解しきったとは言い難い感じだなーと思います。
もちろんこれだって万能ではないだろうし、このあたりをやんわりと頭に入れながら、あとはひたすら実戦で磨いていくのがいいんだろうなぁ。
また更新します〜〜
]]>おはようございます。早起きチャレンジに成功した宇賀神です。
最近AWSの文字起こしサービスである AmazonTranscribe
を少し触る機会があったので、今日はそのあたりのお話。
ruby 2.6.2
rails 5.2.3
この辺読んでもらうのが早そうですが、簡単に解説。
2017年11月29日に発表された、 Speech to Text
と呼ばれる機能を提供する自動音声認識サービスです。
mp3ファイルなどを投げると文字起こしされたjsonが返ってくる、といった感じです(ざっくり)。
aws側の準備をしましょう。
transcribeの実行にはS3が必要になるのでサクッとバケットを作っておきましょう。バケットを作成したら、元となるmp3ファイルを置いておきましょう。
また、ruby側でsdkから使用するIAMも用意する必要があります。
S3のリージョンはtranscribeのリージョンと揃える必要がありますが、transcribeは2019年9月現在、東京リージョンに対応していません。他のアジア圏のリージョンを使用するか、料金の安いバージニア辺りを使うのが良さそうです。
手順としては以下の通り
ではいってみましょう。
下記をGemfileに記述して、bundle installをしましょう。
S3のアップロードからやる場合は aws-sdk-s3
も追加しておきましょう。
gem 'aws-sdk-transcribeservice'
.envに以下を設定します。
AWS_S3_BUCKET="my-s3-bucket-name"
AWS_REGION="us-east-1" # バージニア
AWS_ACCESS_KEY_ID="XXXXXXXXXXXXXXXXX"
AWS_SECRET_ACCESS_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
今回は簡単にrails runnerで実行できるよう実装します。
アプリケーション内で実行して使う場合は、いい感じに応用してください。
lib/runners/amazon_transcribe.rb
的なファイルを用意して、以下のような感じに
job_name = ARGV[0]
media_file_uri = ARGV[1]
client = Aws::TranscribeService::Client.new(
region: ENV['AWS_REGION'],
access_key_id: ENV['AWS_ACCESS_KEY_ID'],
secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
)
response = client.start_transcription_job(
transcription_job_name: job_name,
language_code: 'en-US',
media_format: 'mp3',
media: {
media_file_uri: media_file_uri
},
output_bucket_name: ENV['AWS_S3_BUCKET']
)
puts response
job_name(ARGV[0])
には作成するjobの名前を、 media_file_uri(ARGV[1])
にはS3に置いたmp3ファイルのuriをそれぞれ渡してあげます。実行コマンドは以下のような感じ。
$ rails runner lib/runners/amazon_transcribe.rb test-job s3://my-s3-bucket-name/media.mp3
実行してからtranscribeのダッシュボードを確認すると、以下のようなjobが作成されているかと思います。
status
が Complete
なったのちに、S3を確認してみてjsonが保存されていれば、実験成功です。
ちょっと駆け足でしたが、Amazon Transcribeの紹介でした。
クラウドサービスで文字起こしってちょっと前まで選択肢にもないような手段でしたが、だいぶ簡単にできるようになってましたね。awsすごい。
また更新します〜〜
]]>以下のような形のモデルがありまして、 Parent1
または Parent2
のレコードを消した時に、関連するレコードも同時に消したいなぁと思って dependent
オプションを設定した時です。
class Parent1 < ApplicationRecord
has_many :child, dependent: :destroy
end
class Parent2 < ApplicationRecord
has_many :child, dependent: :destroy
end
class Child < ApplicationRecord
belongs_to :parent1, dependent: :destroy
belongs_to :parent2, dependent: :destroy
end
この状態で parent1.destroy!
などを実行すると以下のようになって削除が失敗します。
1. parent1.destroy!
2. Parent1 が Child を消しにいく
3. Child が Parent2 を消しにいく
4. Parent2 が Child を消しにいく
以下3〜4で無限ループ
そもそも子から親に対してdependentオプションを設定するのはバッドパターンぽいので基本的にやめましょう。横着してはいけません(教訓)。
また更新します〜〜
]]>今回テーマのコーディングやデザインを知り合いのデザイナーに頼んだのですが、WordPressに触れるのが初だったらしく、環境構築がネックだなーと思いまして。
こんなときのためのDockerだろう、ということで、使うことにしました。
構築の手順としてはこれだけ。さすがDocker。
Docker for Macなどはインストール済みだったようなので、そのあたりはすっ飛ばしています。
中々サクッといけそうですね。というわけで、いってみましょう。
まずやることは docker-compose.yml
の準備です。
今回はこのブログに合わせて、以下のように設定しています。なお、wordpressとmysqlは公式イメージを使用するようにしています。
version: '3.3'
services:
db:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: somewordpress
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
ports:
- "4306:3306"
wordpress:
depends_on:
- db
image: wordpress:5.2.2
ports:
- "8000:80"
volumes:
- "$PWD/wp:/var/www/html"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
volumes:
db_data:
mysqlとwordpressのバージョンは作成する環境と同じものを設定してください。特にこだわりがなければ latest
を指定しておくといいでしょう。
DBのパスワードは .env
に切り出していたりする記事もありますが、今回は開発環境用なので適当な設定でやっています。
docker-compose.yml
の準備ができたら、以下のコマンドを実行しましょう。
$ docker-compose up -d
-d
は付けても付けなくても良いです。ログを流しておきたいときなど、コンテナをバックグラウンドで起動したくない場合は外しましょう。
無事に起動できたら localhost:8000
にアクセスしてみます。
これで開発環境の構築が完了しました。
こうして使ってみるとやっぱりコンテナは便利だなーという感じ。
デザイナーと協業、みたいになったときも、こちらで用意した Dockerfile
とか docker-compose.yml
を渡すだけで済むので、とっても助かります。
また何かあれば更新します〜
https://qiita.com/dnrsm/items/1143517240d178b60d8e
https://qiita.com/nanakenashi/items/180941699dc7ba9d0922
https://tech.recruit-mp.co.jp/infrastructure/post-11266/
お久しぶりです。
先日Dockerを触っていたらちょっと特異性のありそうなエラーに遭遇したので、その対処法を記事にしようかと思います。
nginxイメージを元にして webserver
というコンテナを起動しようとしたときのことです。
おそらく、入門書的な本ではよくあるコマンドなのではないかと思います。
docker container run --name webserver -d -p 80:80 nginx
期待に胸を踊らせて http://localhost
にアクセスしたところ以下の画面が表示されていました。
想定していたものと全然違うものが表示されました。
なぜ宇賀神のPCが既知の情報を伝え続けるようなマシンになってしまったのか。
Pawをインストールすると、内包されているrackサーバーが80番ポートを使用するようになってしまいます。それが今回起動したコンテナと競合してしまったようです。
起動コマンドを少しいじって、ポートフォワーディングをするようにしてあげれば大丈夫です。
docker container run --name webserver -d -p 8080:80 nginx
そうです、これが見たかった。
以上、Dockerいじってみて詰まった点でした。
StackOverflowとか覗くと、「アンインストールしなければならない」的な物騒なこと言われていたりするのですが、もし同じような症状の方はアンインストールする前にこちらを試してみると良いかもです。(せっかくの有料ソフトですし。。)
また更新します〜
]]>というわけで、前回の記事で構築した実行環境を使いながら、Python tutorialを進めてみました。
内容としては比較的経験や知識の多いRuby
などと比べたりしながら、個人的に思ったことなどをまとめています。
ただ、触ってるうちに「これ実際にアプリケーション作る方向に倒した方が良いんじゃね」と思ってしまい、7章以降はざっと流し読みした程度になっています。
タイトルが「前編」となっているのはそのためですが、後編の更新予定は今のところありません。今後にご期待ください。
ここからは実際に動かしながら思ったことをまとめていきます。
Pythonでは一度変数に代入された文字列を編集することが出来ないそうです。
>>> word = "Python"
>>> word[2] = "a"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
新たな文字列が必要な場合は新たな変数を用意する必要があるそう。
確かにそういうコードは汚れがちなので、仕様としてはわりと助かるのかな。
長さチェックの関数は len()
。しかし word.len()
ではなく len(word)
>>> word = "Python"
>>> word.len()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'len'
>>> len(word)
6
どうやら len()
は組み込み関数らしい。rubyだとArrayやらStringやらがそれぞれ size
とか持ってたので、このあたりには設計思想の違いがありそうな気配がする。
Rubyで言うeach_with_index
はちょっとスッキリしそう。
とは言っても、Railsやってるとそもそもそんなに使わないのはweb開発だからかな。
Pythonは数学的な使われ方が多いそうなので、そうなるとこういうのよく使うんだろうか。
a = ["Mary", "had", "a", "little", "lamb"]
for i in range(len(a)):
print(i, a[i])
------- 出力結果 --------
0 Mary
1 had
2 a
3 little
4 lamb
Pythonでは空のクラスや変数を作ることが出来ないらしく、何がしか書かなければいけない時には pass文
なるものを置いておけるそうな。
>>> class MyEmptyClass:
... pass
...
また、こっちの使い道は開発してる時にめっちゃ使えそう。
pass のもう 1 つの使い道は、新しいコードを書いているときの関数や条件文の仮置きの本体としてです。こうすることで、より抽象的なレベルで考え続けられます。 pass は何事も無く無視されます
>>> def initlog(*args):
... pass # Remember to implement this!
...
関数をオブジェクトとして変数に代入できるのは少しjsチックな感じ
>>> fib # フィボナッチ数列を求める関数
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89
引数のデフォルト値は一回しか評価されないらしい。
下記みたいな感じで関数内で引数への再代入とかやるとやばそうなんだけど、どうやって折り合いつけているのだろう。
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
----------------
[1]
[1, 2]
[1, 2, 3]
rubyで必要だったような module Hoge
という構文はいらないっぽい。
メソッド群を書いたファイル名がそのままモジュール名になる感じか。
ざっと大まかにこんな感じです。
他にもコメントの書き方の文化とか気になったところもあったりしました。
なんとなく構文の雰囲気とかは掴めたので、次回は少しアプリケーションを作る方向にチャレンジしていきたいな〜と思っています。