29.hotfix|Git
致命的な不具合が見つかった際に行うhotfix。
今の会社の開発においてはよく聞くけど、自分でリリースをしてないので流していた。
改めて正確な意味を確認しようと思う。
リリース後、不具合が見つかったらhotfixブランチを用いて修正を行うことができる。
流れとしては、master→hotfixブランチを切って緊急の修正。
修正完了後、下記のようにそれぞれのブランチにマージ。
hotfix→master
hotfix→develop
↑
このそれぞれのブランチにマージする必要があるというのが、少々手間ではある。
その後リリースタグを打ち、hotfixタグは削除する。
以上がhotfixの流れ。
28.Rails|binding.pry
これまで余り使ってこなかったbinding.pry
を使いこなせるようにしていきたいので、整理。
使い方①
例えば今の開発環境では、処理を止めたい箇所にbinding.pry
と記述すれば、
Dcokerを立ち上げているConsole上で、その処理が止められた位置の前後で定義されている変数の中身などを確認できる。
使い方としては、エラーが発生していると思われる前の箇所にbinding.pry
と記述するだけ。
使い方②
またbinding.pry
を操作して、前後の処理をより詳細に確認したいなら。
Railsでは事前準備としてpry-byebug
というGemが必要となるのでインストールし、
require 'pry'
で呼び出す。
更にDockerを立ち上げた状態で、下記を実行してpry console
を立ち上げる。
docker attach [コンテナ名]
- サンプルコード
6: a = 1 7: b = 2 8: c = 3 9: binding.pry => 10: sample = a * b * c 11: p sample
- コマンド実行例
[1] pry(#~略~)> next
6: a = 1 7: b = 2 8: c = 3 9: binding.pry 10: sample = a * b * c => 11: p sample
- 変数名を指定して、中身を確認
[2] pry(#~略~)> sample => 6
よく使うコマンド
- 現在のメソッド内で一行処理を進める
next
- 次のメソッド内部に入る
step
- pry(処理の中断状態)を終了し、プログラムの続きを実行
continue
- 現在のメソッドを抜ける
finish
コマンド一覧(help)
Byebug backtrace Display the current stack. break Set or edit a breakpoint. continue Continue program execution and end the pry session. finish Execute until current stack frame returns. next Execute the next line within the current stack frame. step Step execution into the next line or method.
Binding
とは
そもそもBinding
メソッドは、Kernel(OS上の中核処理を担うソフトウェア)上に定義されている。
Rubyではそれを組み込んで使用している。
27.TypeScript|Optional Chaining
前回に引き続きTypeScriptの?
関連について。
Optional Chaining
は、?.
という構文のこと。
最も基本的な用法はプロパティアクセスの.
の代わりに?.
を使うもの。
You might find yourself using ?. to replace a lot of code that performs repetitive nullish checks using the && operator.
// Before if (foo && foo.bar && foo.bar.baz) { // ... } // After-ish if (foo?.bar?.baz) { // ... }
- Nullish Coalescing(Null合体演算子)
The nullish coalescing operator is another upcoming ECMAScript feature that goes hand-in-hand with optional chaining, and which our team has been involved with championing in TC39.
You can think of this feature - the ?? operator - as a way to “fall back” to a default value when dealing with null or undefined. When we write code like
let x = foo ?? bar();
this is a new way to say that the value foo will be used when it’s “present”; but when it’s null or undefined, calculate bar() in its place.
Again, the above code is equivalent to the following.
let x = (foo !== null && foo !== undefined) ? foo : bar();
注意点としては、【null:明示的に未設定/undefined:valueが存在しないこと】はチェックしているが、【空文字や0】は考慮されていない
こと。
26.TypeScript|Optional Property
TypeScript上でよく?
が付けられているのを見掛けるので、きちんと理解する為に整理していく。
TypeScriptには、Objectの持つpropertyにOptional Property
という機能がある。
propertyの末尾に?
を付けることで、そのpropertyが無い場合もあるということを表現することができる。
Not all properties of an interface may be required. Some exist under certain conditions or may not be there at all. These optional properties are popular when creating patterns like “option bags” where you pass an object to a function that only has a couple of properties filled in.
Here’s an example of this pattern:
interface SquareConfig { color?: string; width?: number; } function createSquare(config: SquareConfig): {color: string; area: number} { let newSquare = {color: "white", area: 100}; if (config.color) { newSquare.color = config.color; } if (config.width) { newSquare.area = config.width * config.width; } return newSquare; } let mySquare = createSquare({color: "black"});
Interfaces with optional properties are written similar to other interfaces, with each optional property denoted by a ? at the end of the property name in the declaration.
The advantage of optional properties is that you can describe these possibly available properties while still also preventing use of properties that are not part of the interface. For example, had we mistyped the name of the color property in createSquare, we would get an error message letting us know:
interface SquareConfig { color?: string; width?: number; } function createSquare(config: SquareConfig): { color: string; area: number } { let newSquare = {color: "white", area: 100}; if (config.clor) { // Error: Property 'clor' does not exist on type 'SquareConfig' newSquare.color = config.clor; } if (config.width) { newSquare.area = config.width * config.width; } return newSquare; } let mySquare = createSquare({color: "black"}); Readonly properties #
つまり?
を必須でないpropertyに付けることで、「必須ではないが指定するならこの型」といった宣言ができる。
必須でないpropertyを普通に定義した場合、そのpropertyが指定されないとエラーになってしまう。
25.Ruby-Grape|declared
RubyのAPIの処理中でよく出てくる。
ぼんやりした理解だったのでおさらい。
Grape allows you to access only the parameters that have been declared by your params block. It filters out the params that have been passed, but are not allowed. Consider the following API endpoint:
format :json post 'users/signup' do { 'declared_params' => declared(params) } end
If you do not specify any parameters, declared will return an empty hash.
- Request
curl -X POST -H "Content-Type: application/json" localhost:9292/users/signup -d '{"user": {"first_name":"first name", "last_name": "last name"}}'
- Response
{ "declared_params": {} }
APIの定義時に指定するparams
に基づいて、そこで指定されていないものは落とす。
params do requires :title, type: String requires :content, type: String optional :published, type: Boolean end … post do article = Article.new(declared(params, include_missing: false)) end
例えば上記の例で、params
の中でもしtitle
が定義されていなければ、
declared(params)
の中でtitle
のキーとバリューはレスポンスの中に含まれなくなる(落とされる)。
その場合、declared(params)[:title]
はnil
で返ってくる。
24.React|Mount/Unmount
Unmount…って何やったっけ?ってなったのでおさらい…。
Mount
- ReactコンポーネントがDOMツリーに追加されること。
Unmount
- ReactコンポーネントがDOMツリーから削除されること。
UNSAFE_componentWillMount()
- コンポーネントがDOMにマウントされる直前に呼び出される。
componentWillMount
という名前から変更となった(componentWillMount
はバージョン17まで機能し続ける)。
なお現在ではこのライフサイクルメソッドは非推奨となっている。
ES6以前のconstructorが存在しなかった時には、初期化処理を行うのに適していた。
下記のようにconstructor
を使用するように修正すると良い。
- 変更前
class Hello extends React.Component { componentWillMount() { doSomething(); } render() { return <div>{this.state.name} </div> } }
- 変更後
class Hello extends React.Component { constructor(props) { super(props); doSomething(); } render() { return <div>{this.state.name} </div> } }
componentDidMount()
- コンポーネントがDOMにマウントされた直後に呼び出される。
DOMに関わる初期化処理を行いたい時に便利。
23.React/Redux|async/await
asyncの処理の中で呼び出している関数の頭にawaitが抜けていて、他のawaitの処理と順番が入れ変わってしまうという問題があった。
今回はJavaScriptにおけるasync/awaitについて、React/Reduxでの書き方も含めて確認したいと思う。
まずはasync/awaitと密接に結び付いている、Promiseの処理から確認していく。
Promise
JavaScriptの非同期処理の仕組みであり、成功時と失敗時にそれぞれコールバック関数を渡しておくことで、
非同期処理の終了時にそれらを実行してくれる。
- 下記のように非同期処理を実装することができる。
const promise = new Promise((resolve, reject) => { // 処理 if (/* 成功したかどうかの判定 */) { resolve(/* 処理結果 */); } else { reject(/* エラーオブジェクト */); } });
- しかし複数の非同期処理を組み合わせる際に、このコールバック処理は複雑化しやすくバグを生みやすくなる。
fetch("/api/hoge") .then(response => { // responseからidを抽出する const hogeId = translateResponse(response); // hogeIdを使って別のAPIを呼び出す return fetch(`/api/fuga?hogeId=${hogeId}`); }) .then(response => { // responseからidを抽出する const fugaId = translateResponse(response); // hogeIdとfugaIdを使って別のAPIを呼び出したいが、 // hogeIdがこのスコープにないのでエラーが起きる return fetch(`/api/piyo?hogeId=${hogeId}&fugaId=${fugaId}`); })
そこで、async/awaitを用いる方法を見ていきたい。
async/await構文
JavaScriptのベース(技術標準)となっているECMASCript。
その中でも2017年に公開されたECMAScript 2017(ES8)以降、JavaScriptでこのasync/await構文が使えるようになった。
asyncとは、非同期関数を定義する関数宣言を指す。
async function sample() { return 1; }
awaitは、async function内でPromiseの結果(resolve/reject)が返されるまで待機する(処理を一時停止する)演算子のこと。
async関数の中でのみ動作する。
async function sample() { let promise = new Promise((resolve, reject) => { setTimeout(() => resolve("done!"), 1000) }); let result = await promise; // promise が解決するまで待機 alert(result); // "done!" } sample();
上記のように複数の非同期処理も組み合わせて簡潔に書くことができる。
React/Redux:async/await
React/Reduxの中で使う場合にも考え方は同じ。
下記のように書くことができる。
import React, { useState } from 'react' import { useDispatch, useSelector } from 'react-redux' import { unwrapResult } from '@reduxjs/toolkit' import { addNewPost } from './postsSlice' export const AddPostForm = () => { const [title, setTitle] = useState('') const [content, setContent] = useState('') const [userId, setUserId] = useState('') const [addRequestStatus, setAddRequestStatus] = useState('idle') // omit useSelectors and change handlers const canSave = [title, content, userId].every(Boolean) && addRequestStatus === 'idle' const onSavePostClicked = async () => { if (canSave) { try { setAddRequestStatus('pending') const resultAction = await dispatch( addNewPost({ title, content, user: userId }) ) unwrapResult(resultAction) setTitle('') setContent('') setUserId('') } catch (err) { console.error('Failed to save the post: ', err) } finally { setAddRequestStatus('idle') } } } // omit rendering logic }