OmniAICreator/Qiita-1.07M huggingface dataset:
Twitterメタタグの画像キャッシュを消す方法

既存のサイトのTwitterカード用メタタグimageを、新規画像に変更し、いざリリース！

```
<meta property="og:title" content="サイトタイトル名" />
<meta property="og:type" content="website" />
<meta property="og:url" content="サイトURL" />
<meta property="og:image" content="ツイートする際に出るTwitterサムネ画像" />
<meta property="og:site_name" content="サイト名>
<meta property="og:description" content="サイト紹介文など" />
```

といきたいところだが、結構Twitter側のキャッシュが強烈でなかなか切り替わらない。。
そんな場合は、

```
<meta name="twitter:image:src" content="（imageタグの画像と同じアドレス）" />
```

を入れてキャッシュを強制的にセット！

同時に、ここからTwitter側のキャッシュを消す依頼も出しておきましょう！（数秒～数分ラグがある）

```
https://cards-dev.twitter.com/validator
```

少しでも、悩める人を救えれば…！（私がそうだった:rolling_eyes:
Unityで Review機能の実装をして嵌ったお話。

UnityアプリにReview機能を入れてほしいという事なので実装してて嵌ったお話。

# ＜実装するよ＞
とても有難い記事があったため、ここを見ながら実装

https://nobushiueshi.com/unity%E3%82%B9%E3%83%88%E3%82%A2%E3%83%AC%E3%83%93%E3%83%A5%E3%83%BC%E6%A9%9F%E8%83%BD%E3%81%AE%E5%AE%9F%E8%A3%85%E6%96%B9%E6%B3%95ios%E3%80%81android%E4%B8%A1%E5%AF%BE%E5%BF%9C/

そしてちょっと詰まったところ

```
1.External Dependency Manager
　→2021/5/19日に使えなくなってる為
　https://developers.google.com/unity/archive
　ここから
　Play In-App Review　のunitypackage をダウンロードして利用
```

残りはソースコードをそのまま流用

# ＜いざ実行＞

・iPhoneはシステムダイアログが問題なく表示された
・Androidで表示されなかった。:dizzy_face:

・https://workspace.google.co.jp/intl/ja/pricing.html


# ＜解決策をネットで探す＞
・開発者モードだと__表示されない__
・レビューを書き込こんだら__表示されない__
・正しいアカウントを使ってください　←正しいってなんやねん:imp:
・iOS
　　Xcode：直接インストールした実機では表示されます
　　TestFlight：実機では表示__されません__
　　Deploygatee：実機では表示された
・Android
　　apk：直接インストールした実機では表示__されません__
　　Google Playからインストール(内部テスト版等含む)：実機では表示されます
　　Deploygate →　？？（記事見つからず）

等々
上記の条件は満たしているのに表示されないぞ？:thinking:

# ＜自分の解決策を発見＞

PlayStoreアプリ
・右上の自分のアイコンをクリック
・アプリとデバイスの管理
・評価とレビュー
・自分が開発している以外のアプリのレビューを行う
・DF-DFERH-01とかいうエラー発生:persevere:
・__ここで評価できることが勝利のカギ！__:v::japanese_ogre:

#＜今回のケース＞
・DF-DFERH-01のケース調べてAndroidのOS Versionが古いのかなとかとか思ったが症状変わらず。
・新しく適当なgmailアカウントを作成したら動いた。
・[GooglwWorkSpace](URL "https://workspace.google.com/intl/ja/products/gmail/") ←これのアカウントだとどうもエラーになる模様？
・本当に上記メアドを使っているのだけが原因かはわからないが
　ほかのアプリに対してレビューが書き込めない状態だとだめなのは間違いない。
・ちなみにアプリで何も発生しないのでわかりにくい。（エラーにもならずに進む。）
ZeroMQを使ってPython-Node.js間でPUB/SUBする。

#ZeroMQとは
公式サイトはこちら

https://zeromq.org/

grpcとかddsとかと同じメッセージングミドルウェアです。
TCPベースで、他言語でのプロセス間でREQ-REPやPUB-SUBやPUSH-PULLなど様々な通信方式が手軽に実装でき、分散システムの構築に向いています。

#実装
Windows10と、WSL2のUbuntu18.04上で相互に通信してみた。
思ったよりしっかり(お堅く)作られていて、変にハマってしまったので解決策と実装を共有する。
Node側はあっさり動いたがPython側がややこしい。

今回試したのは、サンプルによって"チャンネル"だったり"キー"だったり呼び方が様々だが、
所謂***トピック通信***で、`kitty cats`というトピックにメッセージをパブリッシュしている。

##Node.js

- Node.js 12.18.2
- npm 6.14.6

Node.js側はこれを使った。npmでインストールできる。

https://github.com/zeromq/zeromq.js/tree/5.x

最新版は6.xらしいが、とりあえず安定板の5.xで。
WSL2側でしか動かしていないが、意外とちゃんと動く。

サンプルのままだと、Bufferの形式のまま受け取って出力してしまうので、toString()で文字列にして出力する。

```pub.js
var zmq = require('zeromq')
  , sock = zmq.socket('pub');

sock.bindSync('tcp://127.0.0.1:3000');
console.log('Publisher bound to port 3000');
sock.send(['kitty cats', 'meow!']);
var counter = 1;

setInterval(function(){
  console.log('sending a multipart message envelope');
  sock.send(['kitty cats', counter]);
  counter += 1;
}, 500);
```

```sub.js
var zmq = require('zeromq')
  , sock = zmq.socket('sub');

sock.connect('tcp://127.0.0.1:3000');
sock.subscribe('kitty cats');
console.log('Subscriber connected to port 3000');

sock.on('message', function(topic, message) {
  console.log('received a message related to:', topic.toString(), 'containing message:', message.toString());
});
```

##Python

- Python 3.9.5

Pythonで使ったのはこれ。pipで普通に入れられた。

https://pyzmq.readthedocs.io/en/latest/api/zmq.html

Pythonの方がややこしく、まず、普通に組むとWindows環境ではSubscriberのプロセスがCtrl-C(Keyboard Interrupt)で止められなくなる。
これはデフォルトでその仕様らしいが、使いにくいので色々調べると、`allow_interrupt()`というのを使えば止められるようだったのでそれを使うようにした。
これで実装すれば、エラーを吐きながらではあるがCtrl-Cでプロセスを止めることができる。
(きれいに止める必要はないのでこれで十分)

あと、time.sleep()で0.1秒待っているのは、PythonでのZeroMQが、ソケットをbind直後にメッセージを送信すると、***必ず一度目は取りこぼす***からである。
これもどうやら仕様らしく、本当はもっとちゃんとした書き方がありそうだが、今のところそこまでシビアな実装をする予定もないので、これで間に合わせた。
Node.jsの方はライブラリ内で一度目の送信に対して何か処理がされているのか、そのまま使っても一度目からメッセージは到達する。

おそらく、PUBを先に開始して、あとからSUBを起動するのが正しい手順(?)なんだと思うので、自分の使い方がイレギュラーなのかもしれない。


```pub.py
import zmq
import time

ctx = zmq.Context()
sock = ctx.socket(zmq.PUB)
sock.bind("tcp://127.0.0.1:3000")

print("Starting loop...")
topic = "kitty cats"

i = 1
time.sleep(0.1)
while True:
    msg = "Hi for the %d:th time..." % i
    sock.send_string(f"{topic} {msg}")
    print("Sent string: %s ..." % msg)
    i += 1
    time.sleep(1)

sock.close()
ctx.term()
```

```sub.py
import zmq
from zmq.utils.win32 import allow_interrupt
import time

ctx = zmq.Context()
sock = ctx.socket(zmq.SUB)
sock.setsockopt_string(zmq.SUBSCRIBE, "kitty cats")
def interrupt_polling():
    """Fix CTRL-C on Windows using "self pipe trick"."""
    # ctx.send_multipart(['', 'quit'])
    sock.close()
    ctx.term()

with allow_interrupt(interrupt_polling):
    sock.connect("tcp://127.0.0.1:3000")
    sock.subscribe("kitty cats")

    print("Starting receiver loop ...")
    while True:
        msg = sock.recv_string()
        print("Received string: %s ..." % msg)

    sock.close()
    ctx.term()
```

#参考

https://www.cuspy.org/diary/2015-05-07-zmq/zguide-ja.pdf
Suica残金から行ける場所を判断Bot～改良版～

コロナ禍で**出勤先が増えた人、営業で複数の場所に行く人**におすすめのLINEBotです！
以前の記事でSuica残金から行ける場所を判断するBotを作成しました。
こちらのBotを改良した記事になります。

https://qiita.com/kkyosuke17/items/23f3e41f410e024ec83b

##Botの概要
LINEBotに現在チャージされている金額を送信するだけで以下のようなメッセージが返ってきます！

![traincost1 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1269684/15e71429-35fb-8891-2a7a-c9c9370d791e.png)

![traincost2 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1269684/de36bb36-c08d-d59f-a04b-a7b7b9713ce8.png)

##改良点
|改良場所|改良点|
|----|------|
|インプット方法（Suica残金）|enebular起点をLINEのメッセージ入力に変更する|

まずは前回からenebularを変更しました。
フロー→[-traincost-](https://enebular.com/discover/flow/c290ee76-998e-42ec-b5fd-548741eb2083)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1269684/20df0116-0041-2ae0-b42a-586dfa647391.png)

まずはhttp inの設定です。
ここで特定のLINEBotにメッセージを受信したときメッセージをインプットとしてフローが流れます。

enebularの設定はこんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1269684/a4e2cb62-848f-27a3-b8f9-75365577d2ff.png)

LINEBotの設定はenebularのhttpNodePathをコピー。
httpNodePathはデプロイボタン横のinformationボタンから入手できます。
※enebularのセッションは1時間で切れてしまうのでherokuにデプロイすることをお勧めします。今回のパスもデプロイしています([Heroku · enebular](https://docs.enebular.com/ja/deploy/deployflow/heroku/))

LINEBot側のWebhookを設定します。
MessageAPIタブのWebhook settingsのWebhook URLを設定します！
この時末尾に**/linebot**を入れるのを忘れないようにしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1269684/b04be395-ad8b-f487-3d5c-d04ab52d621e.png)

次は読み込んだメッセージから入力された文字列を取り出すfunction部分のコードです。
たったの2行！
メッセージに入力されたテキスト情報を取り出して数字変換しているだけです。

```javascript
var newMsg = { payload: Number(msg.payload.events[0].message.text) };
return newMsg;
```

これ以降のフローに関しては前回の記事を参照していただければと思います。

##終わりに
今回の改良でイケていない部分としてLINEBotでメッセージを受けているのに返信がLINENotifyから返信が来ているというところですね。
enebularの結果受け渡しをLINEBotにしてあげればいいだけなのですが前回の記事でノンプログラミングをうたっているのでここまでにしました。
前回はenebular起動でしたが今回はLINEBot起動でかつherokuにもデプロイしているので簡単にかつ永続的に動くものにできたと思います。
enebularのnode-red-contrib-line-messaging-apiを使えば送信したLINEBotから返答を受け取れそうだなと思いましたが結果の変換はまた今度にしたいと思います。
【kotlin】ジェネリック型　<T>とは

#■ジェネリック型とは
具体的なデータ型に依存しない,抽象的かつ汎用的なコードを書けるようにする手法

つまりString型とかInt型とか何の型でも使える様にする事

▼例：配列,コレクション
配列とかコレクションは何の型でも入ると思います。
ジェネリック型を使用しているからです。

逆に入る型を決める事もできました!!
場合によっては型を決めた方が安全性が高まるため。

``` kotlin
//Int型しか入れないよ。
val aaa: mutableList<Int> = mutableListOf(1,2,3) 
```

###■ジェネリックプログラミングが使われている例

▼Tはクラスの定義時には何でも入る型の事。 名前は任意　TやEが多い
型引数と呼ばれる。
引数の型
戻り値の型
として使える。

■インスタンス生成時にTに型を指定してクラス内部の型を制限する。
クラス内部のTは全て制限した型になる!!

``` kotlin
class Hoge<T>(var value: T) {
  
  fun aaa(): T {
    return value
  }
}

val hoge1 = Hoge<String>("こんにちわ")
val hoge2 = Hoge<Int>(2)

//型推論もできる。
val hoge3 = Hoge("よろしく")

println(hoge1.aaa()) //結果: こんにちわ
println(hoge2.aaa()) //結果: 2

hoge1.value = 3 //結果: エラー　インスタンス生成時にvalueはstring型っていいましたやん！むりでっせ！！となる。

hoge3.value = 3 //結果: エラー　型推論してるだけ。クラスのTは全てStringに制限されている。

```

#■渡せる型を制限したい。

▼T:クラス名
書いたクラスまたは派生クラスのみを渡せる。


``` kotlin
open class Hoge() {}
class Foo(): Hoge() {
 val hello = "こんにちわ"
}

//<T:クラス名指定>
class Generic<T: Hoge>(var aaa: T) {
  fun get(): T {
    return aaa
  }
}
//クラス名を指定されているので指定されたクラスかその親クラスのみ指定する。
val g1 = Generic<Foo>(Foo())
println(g1.get().hello) //結果: こんにちわ

val g2 = Generic<String>("はい") //結果: エラー
```
SVMによるmnistの学習・推論

#背景
SVMでmnistのデータを学習する研修はよく見るが、学習させたものに対して自分で手書きした文字の食べさせ方を説明しているものは見たことがなかった。
まぁ、自分がこの研修を以前受けたときは、仕組みは分かったから別にいいかとなったのですが、その場で試してみた方が定着が良いかと思うので、自分の備忘録もかねて投稿します。

#学習

google colaboratory上のドライブ準備

手順１　google driveにMy Drive\Colab Notebooks\dataのフォルダを作成。

```ruby:手順２　フォルダのマウント
from google.colab import drive
drive.mount('/content/drive')
%cd /content/drive/My Drive/Colab Notebooks/data
```

```ruby:手順３　mnistデータ取得と正規化
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split

%time X, y = fetch_openml('mnist_784', version=1, return_X_y=True)

X_train, X_test, y_train, y_test = train_test_split(X / 255,     # 正規化
                                   y.astype('int64'),            # 正解データを数値にする
                                   stratify = y,
                                   random_state=0)
```

```ruby:手順４　SVMによる学習と学習済みモデルの塩漬け
import pickle
from sklearn.svm import SVC

clf = SVC(kernel='rbf', gamma='auto', random_state=0, C=2)
clf.fit(X_train, y_train)

pickle.dump(clf, open("trained_model.pkl", 'wb'))         #trained model pickle
```


#推論
<font color="Red">上記の学習でtrained_model.pklが生成されているので、再学習は不要です。</font>

```ruby:手順１　学習済みモデルの読込
import pickle
clf = pickle.load(open('trained_model.pkl', 'rb'))        #trained model load
print(clf)
```

```ruby:手順２　予測
import matplotlib.pyplot as plt
import numpy as np

fname = "./tegaki1_28-28.jpg"

tegaki_0_255 = plt.imread(fname,0)   # read image.
tegaki_0_1   = tegaki_0_255/255      # Normalization.

tmp = tegaki_0_1.reshape(1,784)      # convert matrix to 1 line.
res = clf.predict(tmp)

print("prediction result: ",res)
plt.imshow(tegaki_0_255, cmap = "gray")
```
[推論の結果]
何の文字だったかと、その画像が出力される。

#番外編　画像生成リサイズ
手順１　google driveのMy Drive/Colab Notebooks/dataにtegaki0.jpgを配置。
　　　　画像はカラー・モノクロ問いません。サイズも適当で良いです。

```ruby:手順２　画像のリサイズ
%cd /content/drive/My Drive/Colab Notebooks/data

import cv2
from google.colab.patches import cv2_imshow

in_fname = "./tegaki0.jpg"
out_fname = "./tegaki0_28-28.jpg"

src = cv2.imread(in_fname,cv2.IMREAD_GRAYSCALE)
tgt = cv2.resize(src,dsize=(28,28))

cv2.imwrite(out_fname,tgt)
```

[番外編の結果]
google driveのMy Drive/Colab Notebooks/dataにtegaki0_28-28.jpgという名前で
28ピクセル×28ピクセルのグレースケール画像が作成される。
stack machineを愛でる。 その7

#概要

stack machineが、好きだ。
練習問題、やってみた。

#練習問題

九九を、表示せよ。

#考え方

変数iとjを１から９まで、回す。
i * j を　変数xに追加する。

#サンプルコード

```
  push  "x"
  push  ""
  set 
  push  "i"
  push  "1"
  set
loop1  push  "j"
  push  "1"
  set
loop0  push  "x"
  push  "x"
  get
  push  "i"
  get
  push  "j"
  get
  *
  add
  push  " "
  add
  set
  push  "j"
  push  "j"
  get
  ++
  set
  push  "j"
  get
  push  "10"
  >
  !
  if  skip0
  jp  loop0
skip0  push  "x"
  push  "x"
  get
  push  " / "
  add
  set
  push  "i"
  push  "i"
  get
  ++
  set
  push  "i"
  get
  push  "10"
  >
  !
  if  skip2
  jp  loop1
skip2  end


```

#成果物

https://embed.plnkr.co/plunk/IKdTbVVsF66uVQEy

以上。
Databricks on AWSでKeras ResNet50で画像判定


#背景
画像判定をDatabricksを用いて行おうとしたところ、
「そもそもS3上の画像をどうやって読み込ませるんだっけ？」「なんかエラーでる...」などで時間がかかったのでこの記事を書こうと思いました。

あくまで「こうやったらできた」の記録ですので、「Databricks的」「分散処理的」にはもっと良い書き方があるかもしれません。2021年現在Databricksに関する日本語の記事が少ないため記事を増やす目的で作成します。

keras部分は
https://keras.io/ja/applications/
を参考にしました。


### Databrcks clusterのRuntime version:
`7.3 LTS (includes Apache Spark 3.0.1, Scala 2.12)`

※2021/06/23 追記
[Databricks Runtime for Machine Learning](https://docs.databricks.com/runtime/mlruntime.html)を用いるとデフォルトで必要なライブラリがinstallされており、次項での`dbutils.library.installPyPI()`は不要のようです。


#実際のコードと補足説明

### library import
Databrikcsでは`dbutils.library.installPyPI()`がpythonでいう、`pip install`にあたります。


```py
dbutils.library.installPyPI('Pillow')
```
`keras`, `Tensorflow`より先にこの`Pillow`をinstallしましょう。
先にinstallしないと、
`ImportError: Could not import PIL.Image. The use of 'load_img' requires PIL.`
というエラーが出ます。(参考: https://qiita.com/YankeeDeltaBravo225/items/6968c376a491b6171671)


```py
dbutils.library.installPyPI('keras')
```

```py
dbutils.library.installPyPI('TensorFlow')
```

```py
from keras.applications.resnet50 import ResNet50
from keras.preprocessing import image
from keras.applications.resnet50 import preprocess_input, decode_predictions
import numpy as np
```

### パス指定
DatabricksでS3上のファイルを読み込むときは
`img_path = 's3://bucket-name/elephant.jpg'
image_df = spark.read.format("image").load(img_path)`
のようにして読み込みます。
しかし、今回はkerasの
`img = image.load_img(img_path, target_size=(224, 224))`
という形で読み込みます。
この`image.load_img()`を用いてDatabricksで読み込むためにはmountという処理が必要です。
(参考:https://docs.databricks.com/data/data-sources/aws/amazon-s3.html)
`bucket-name`はお使いのS3バケットの名称にしてください。



```py
dbutils.fs.mount("s3a://bucket-name/","/mnt/bucket-name/")
```

```py
img_path = '/dbfs/mnt/bucket-name/elephant.jpg'
```

なお、用いるelephantの画像はkerasのgithub repositoryのissueに取得方法が記載されてました。

https://github.com/keras-team/keras/issues/5534


### マニュアル通りに実行
あとは[参考マニュアル](https://keras.io/ja/applications/)通りのコードで判定ができました。

```py
model = ResNet50(weights='imagenet')
```




```py
# img_path = 'elephant.jpg' ### 前項で指定しているためコメントアウトしましょう

img = image.load_img(img_path, target_size=(224, 224))
```

```py
x = image.img_to_array(img)
x = np.expand_dims(x, axis=0)
x = preprocess_input(x)
preds = model.predict(x)
# decode the results into a list of tuples (class, description, probability)
# (one such list for each sample in the batch)
print('Predicted:', decode_predictions(preds, top=3)[0])
```
実行結果
`Predicted: [('n02504013', 'Indian_elephant', 0.81985754), ('n02504458', 'African_elephant', 0.11778806), ('n01871265', 'tusker', 0.05829753)]`


### 読み込んだ画像がどのようなものだったか確認
どのような画像を読み込んだかをDatabricks上で表示するにはmatplotlibを用いました。

```py
import matplotlib.pyplot as plt

plt.imshow(img)
plt.show()
```

### unmountしましょう (2021/06/23追記)
mountを行った場合、Databricks内の他のユーザーにも影響するため作業が終わったらunmoountしましょう。


```py
dbutils.fs.unmount("/mnt/bucket-name/")
```


# 参考記事
https://keras.io/ja/applications/
https://docs.databricks.com/data/data-sources/aws/amazon-s3.html
https://qiita.com/YankeeDeltaBravo225/items/6968c376a491b6171671
RHELでホームフォルダの拡張


**Azure**の**RHEL**イメージで、次のような「No space left on device」というエラーが発生したことありませんか。

たとえば、Linuxカーネルのソースコードのような大きなアーカイブを解凍する時です。

```bash
$ tar Jxf linux-5.12.10.tar.xz
... Cannot create symlink to ... : No space left on device
tar: Exiting with failure status due to previous errors
```

**Azureマーケットプレース**にある**RHEL7**または**RHEL8**イメージではホームフォルダに**1GB**しかありません。

```bash
$ df -Ph $HOME
Filesystem                 Size  Used Avail Use% Mounted on
/dev/mapper/rootvg-homelv 1014M 1014M   28K 100% /home
```

ホームフォルダを拡張する方法を紹介します。

最初に、**rootvg**ボリュームグループにまだスペースが残っているかどうかを確認します。

```bash
$ sudo vgdisplay -v rootvg | grep Free
  Free  PE / Size       10245 / <40.02 GiB
  Total PE / Free PE    16133 / 10245
```
約40GBが利用可能です。 

**homelv**を10GB拡張しましょう。 

```bash
sudo lvextend -L +10G /dev/mapper/rootvg-homelv
```

次に、ファイルシステムを拡張する必要があります。このイメージでは*xfs*が利用されているなので、次のコマンドを使用して、マウントされたままファイルシステムを拡張できます。 

```bash
sudo xfs_growfs -d /dev/mapper/rootvg-homelv
```

結果：

```bash
$ df -Ph /home
Filesystem                 Size  Used Avail Use% Mounted on
/dev/mapper/rootvg-homelv   11G  1.1G   10G  10% /home
```

さて、より多くのスペースがあり、ホームフォルダに大きなアーカイブを解凍できるようになりました。

以上です。
VSCodeの初期設定

#目次

1.インストールするために
2.設定
3.vimの設定


#1.インストールするために

Windowsの方は[こちらのサイト](https://code.visualstudio.com/)からダウンロードしよう

https://code.visualstudio.com/

Linuxの方はsnapが様々なディストリビューションを利用できるのでおすすめ
Ubuntuではデフォルトでinstallされていましたが、Arch Linuxではインストールされていないそうなので各自インストールしてください。


```terminal:terminal
sudo snap install --classic code
```

#2.拡張機能
私は普段vimのキーバインドを利用しているのでvimのプラグインを導入します。
Extensionsからvimと検索して画像のようにvimをinstallします。
ほかの拡張機能は必要の応じてインストールしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526293/e88e2aa3-0b06-6eab-2a6d-158a31737edd.png)

#3.vimの設定
普段nomalモードに入るときにjjを入力して入っているのでそのためのコードを記載します。
Ctl+shift+pを入力するとコマンドパレットが出現します。そこでsettingsと入力してPreference:Open Settings (JSON)を選択します。そこに以下の設定を記載することでjjでのnomalモードへ移動を実現します。

```json:settings.jason
{
   "vim.insertModeKeyBindings": [
        {
            "before": [
                "j",
                "j"
            ],
            "after": [
                "<Esc>"
            ]
        }
    ]
}
```
【AWS初心者でも分かりやすい】Lambdaで作成した関数をAPI Gatewayでアタッチする方法

今回はLambdaで作成した関数をAPI Gatewayを使用して、Lambdaで作成した関数をAPIとして使用する方法をお教えします。

特に細かい説明というよりかは、やり方だけを書いていくので、なにか気になる点があればご自身でお調べください。

また、そこまで設定も難しくないので、肩の力を抜いてご覧ください。

それでは早速説明していきます。

# はじめに

Lambdaで関数を作成している前提で話を進めていきます。

なので、まだLambdaで関数を作成していない方は作成してから本記事をご覧ください。

# API Gatewayの設定

まず、Lambdaで作成した関数をAPIとして使用するために、API Gatewayで設定を行わなければなりません。

まずはAPI Gatewayのコンソール画面へいきます。

・[https://ap-northeast-1.console.aws.amazon.com/apigateway/main/apis?region=ap-northeast-1](https://ap-northeast-1.console.aws.amazon.com/apigateway/main/apis?region=ap-northeast-1)

## APIの作成

そして、適当にAPIを作成します。

APIを作成する際、個人の用途に合わせタイプを選択してください。

ほとんどの場合、REST APIで問題ありません。

また、既存のAPIを使用しても構いませんので、お好きな方をお選びください。

作成したAPI、もしくは既存のAPIから「/」をクリック。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/a698bb0d-cc2c-5cde-d456-040bf106fa06.png)

そのまま「アクション」から「リソースの作成」をクリック。

リソース名、リソースパスは任意の名前で入力。

本記事は「example」とします。

「リソースの作成」をクリック。

「API Gateway CORS を有効にする」は、後で有効にしますのでチェックしないでください。

## メソッドの作成

そしたら、以下のように今設定したパスを選択し、「アクション」から「メソッドの作成」をクリック。

任意のメソッドを選択。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/a33350f6-29d8-5695-e37e-c34cb5a70a9f.png)

## Lambda関数と紐づけ

そしたら以下のような画面になると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/a88c9960-d0db-78cf-7524-ed7e7de8917e.png)

「Lambda関数」から、APIとして使用したいLambda関数名を選択。

「保存」をクリック。

## 総合リクエストの設定

そしたら、同じようにパスのメソッド部分をクリック。

すると、以下のような総合リクエストという項目があるのでクリック。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/8902f3e2-3ecf-d56d-6699-8f3bd67ce50c.png)

一番下のマッピングテンプレートのタブをクリックし、「Content-type」の設定。

ほとんどの場合は``application/json``でOKです。

テンプレートの生成で「メソッドリクエストのパススルー」を選択。

「保存」してください。

## CORSの有効化

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954754/b63e0aae-a4aa-00a7-3a08-22c49a5e8163.png)

ここから「アクション」→「CORSの有効化」を選択。

「CORSを有効にして既存のCORSヘッダーを置換」をクリック。

「はい、既存の値を置き換えます」をクリック。

## デプロイ

最後にAPIとして使用するためにデプロイします。

「アクション」→「APIのデプロイ」

これでLambda関数をAPIとして実行できます。

URLの確認は、左のメニューから「ステージ」→「先ほど指定したパス（本記事なら/example）」

任意のメソッドを選択し、ページ上部にURLが発行されているので、このURLを使えばAPIを実行できます。

## curlコマンドでAPIリクエスト

最後は、以下のcurlコマンドを例にリクエストを行ってみてください。

```powershell
curl -X POST -H "Content-Type: application/json" -d '{"message": "API request"}' https://example.com/dev/example
```

そこまで難しい作業ではなかったと思います。

ただ、忘れそうなので自分の備忘録も兼ねて記事として書きました。

また、APIのデプロイも時間が経つと忘れることもあるので、皆さんも忘れないように。

皆さんのお役にも立てれれば幸いです。

以上、「【AWS初心者でも分かりやすい】Lambdaで作成した関数をAPI Gatewayでアタッチする方法」でした！

Thank you for reading
[SwiftUI]Googleフォームをお問い合わせ機能として使う！

アプリ開発の中でお問い合わせ機能を実装したい場面があります。内容は不具合の報告などアプリの内容によって様々ですが、少なくとも以下の２点は注意しておきたい点です。

・できるだけ詳細な情報を送信してもらう
・ユーザーが入力、送信しやすいようにしておく

特にアプリやiOSのバージョンなどといった情報は開発側からすれば重要ですが、ユーザー側にとっても必ずしもそうだとは限りません。必要な情報はユーザー側に入力を促せるような仕組みを導入したいところですが、それをUIで作るのは大変です。そこでGoogleフォームの出番です。
#ブラウザを利用する

以下のようにアプリ側のコードとしては`SFSafariViewController`をSwiftUIで使えるようにします。後は作ったGoogleフォームのURLを指定するだけでOKです。

```swift:SafariView.swift
import SwiftUI
import SafariServices

struct SafariView: UIViewControllerRepresentable {
    var urlString: URL
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<SafariContent>) -> SFSafariViewController {
        let safariViewController = SFSafariViewController(url: urlString)
        safariViewController.dismissButtonStyle = .done //閉じるボタンの形式を変更できる
        return safariViewController
    }
    
    func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext<SafariContent>) {
    }
}
```

以下のViewを`NavigationLink`の遷移先や`.sheet`で表示されるように設定しておけばフォームを呼び出すことができます。

```swift:FormView.swift
struct FormView: View {
    var body: some View {
        if let urlString = URL(string: "https://forms.gle/sample") {
            SafariContent(urlString: urlString)
                .ignoresSafeArea()
        }
    }
}
```

これ以外にもWeb上のページを表示する方法はありますが、`SFSafariViewController`には拡大表示や、閉じるボタンのローカライズを自動で行ってくれるというメリットがあります。

Googleフォームを利用すれば、チェックボックスなど様々な入力型式が使えます。あらかじめ質問を用意しておいて手間を減らしておけばユーザーの負担も少なくて済みますし、作るのも簡単なので個人的にはこの方法がおすすめです。

#余談
今回この記事を作るにあたって有名なアプリのお問い合わせ機能を調査してみたのですが、やはり大手のものはアプリ内で完結させているものが多い印象です（Instagram、Google製アプリなど）。

それらは不具合の報告なのか、機能の追加要望なのかといった目的ごとに入力画面の出し分けが行われていました。もし上記のようにアプリ内UIとしてお問い合わせ機能を実装する場合はそちらを参考にするとよいと思います。
ユースケース図

ユースケース図で参考になった記事

#第1位
https://qiita.com/YUJIjs/items/aeec6c5bfb3ef421f68e

#第2位
https://qiita.com/tukine_T/items/7f45f31d8a927fc3c1df

#第3位
https://qiita.com/devopsCoordinator/items/bbd57df681844308fb5d

いつもわかりやすい記事ありがとうございます
MockeryでIlluminate\Database\Eloquent\Builderを利用しているメソッドのテストを書こうとしたら __clone method called on non-objectが発生した件のメモ



Illuminate\Database\Eloquent\Builderを利用しているメソッドのテストを書こうとしたら謎に引っかかったのでメモを残しておく。


```php:テスト対象のメソッド
    public function countSql(): string
    {
        // getBuilder() はIlluminate\Database\Eloquent\Builder を返す
        $tmpBuilder = clone $this->getBuilder();
        $tmpBuilder->selectRaw('count(0) as count');
        return $tmpBuilder->toSql();
    }
```

このメソッドに対して、以下様のテストを書いた。

```php:テストコード(だめ)
    public function test_countSql(){
        $builder = \Mockery::mock(EloquentBuilder::class)->makePartial();
        $builder->shouldReceive('selectRaw')->with('count(0) as count')->once();
        $builder->shouldReceive('toSql')->once()->andReturn('ResultOfCountSql');

        $targetClass = new $this->testClassName($builder);
        $actual = $targetClass->countSql();
        $this->assertIsString($actual);
        $this->assertSame('ResultOfCountSql',$actual);
    }

```

エラーメッセージは「__clone method called on non-object」で、なんでMockがcloneできないの？？？と悩んでいたが、Mockeryは呼び出し方によってコンストラクタを元クラスのコンストラクタを呼んだり呼ばなかったりすることを忘れていた。（よく忘れる）

全く同じ問題で困っている外人を見つけたので、下記URLと同様に対処をしてFix。
https://stackoverflow.com/questions/45623804/testing-a-method-that-clones-a-mocked-parameter

> Eloquent\Builder actually contains (as a member) an instance of Query\Builder, and its magic __clone method calls clone on this underlying Query\Builder object:

（適当訳）
Eloquent\Builderは、Query\Builderのインスタンスをメンバとして持ってんよ、んで、Eloquent\Builderの__clone()メソッドは、メンバであるunderlying Query\Builder(移譲先、内部の？的な意味か？)のインスタンスをcloneしてるよ。

> Since you're mocking Eloquent\Builder, it doesn't actually have an underlying $this->query member because that would be set in Eloquent\Builder's constructor, which never gets called in a fully mocked object.

（適当訳）
あんたEloquent\BuilderをMockingしてるけど、このMockは、実際には移譲先の$this->queryを持ってないでしょ。なぜなら、そのメンバはEloquent\Builderのコンストラクタでsetされるからやね。Mockするときにコンストラクタ呼ばれてねえよな？

> To get around this, you need to create a partial mock of Eloquent\Builder, and tell it to run its real constructor with a mocked instance of Query\Builder:

（適当訳）
要するに、あんたはパーシャルモックを作って、実際のコンストラクタを呼び出すようにせなあかんのよ。

おｋ了解

というわけで、Query\BuilderのMockを作って、Eloquent\Builderのコンストラクタにわたすように書き換えました。


```php:テスト（おｋ）
    use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
    use Illuminate\Database\Query\Builder as QueryBuilder;

    public function test_countSql(){
        $queryBuilder = \Mockery::mock(QueryBuilder::class);
        $eloquentBuilder = \Mockery::mock(EloquentBuilder::class, [$queryBuilder])->makePartial();

        $eloquentBuilder->shouldReceive('selectRaw')->with('count(0) as count')->once();
        $eloquentBuilder->shouldReceive('toSql')->once()->andReturn('ResultOfCountSql');

        $targetClass = new $this->testClassName($eloquentBuilder);
        $actual = $targetClass->countSql();
        $this->assertIsString($actual);
        $this->assertSame('ResultOfCountSql',$actual);
    }

```
既存のレイアウトの一部にJetpack Composeを組み込む その２

#はじめに
先日[こちらの記事](https://qiita.com/cw-matsuba/items/3f8094d534f755604d7b)にて、既存のレイアウトの中にJetpack Composeのレイアウトを組み込む方法を説明しました。 
今回はJetpack Composeで作られたレイアウトの中で既存のAndroid ViewをaddViewする方法について説明します。

#Jetpack Composeのレイアウト内で既存のAndroid ViewをaddViewする
###サンプル
```kotlin
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            Column {
                // Compose View
                Text("Hello world!")
                // Android View
                AndroidView(
                    factory = { context ->
                        // addViewしたい従来のView
                        TextView(context).apply {
                            text = "Hello AndroidView!"
                        }
                    }
                )
            }
        }
    }
}
```
Compose内で既存のViewを使いたい場合はAndroidView()を使います。
こちらのfactory内でaddViewしたいViewを返してあげるとCompose内に追加されます。
また、factoryの他にmodifierやupdateという引数も定義できて、こちらでレイアウトの修正や、
viewの状態や変数に合わせてviewを変えることも実装できます。

#おわりに
今回はJetpack Compose内に既存のAndroidのViewを使う方法を説明しました。
Jetpack Composeに移行したいもののこの部分はまだ既存のView使いたい問い時に便利だなと思いました。

#参考
[Compose をアプリに導入する](https://developer.android.com/jetpack/compose/interop?hl=ja)
[Rspec / system_spec]記事一覧ページの削除ボタンをテストする

記事一覧から削除ボタンを押して記事を削除する、
という機能をテストするのにちょっと突っかかった備忘録です。

| Left align | バージョン |
|:-----------|:------------|
|OS|macOS Big Sur 11.3|
|Ruby       | 3.0.0        |
|Rails     | 6.1.3.1     |
|Rspec      | 3.10       |


#capybaraさんに怒られた内容
```
1) Articles deleting deletes an article from index page
     Failure/Error: expect(page).to have_current_path local_articles_path(local)
     
     Selenium::WebDriver::Error::UnexpectedAlertOpenError:
       unexpected alert open: {Alert text : MyStringを削除します}
         (Session info: headless chrome=91.0.4472.101)
```

#そのわけ
ビューで

```app/views/articles/_article.html.erb
<li>
<!-- 略　-->
<span class="delete_button"><%= link_to '削除', article, method: :delete, data: {confirm: "'#{article.title}' を削除します"} %>
</li>
```

としていたうちの、
`{confirm: "'#{article.title}' を削除します"} `について
「そんなの聞いてないよー！」と怒られたわけです。

##それもそのはず

こうなってました。

```spec/system/articles_spec.rb
RSpec.describe 'Articles', js: true, type: :system do
  let(:local) { FactoryBot.create(:local) }
  let(:article) { FactoryBot.build(:article, local: local) }
  let(:another_article) { FactoryBot.create(:article, local: local)}
  before do
    local.confirm
    @number_of_articles = Article.count
    @number_of_tags = Tag.count
  end

...略...

  describe 'deleting' do
    it 'deletes an article from index page' do
      article.save
      sign_in local
      visit local_articles_path(local)
      　#------- ここから
          within "li#article-#{article.id}" do
            click_link '削除'
          end
        #------- ここまでの話
      expect(page).to have_current_path local_articles_path(local)
    end
  end
end
```

とりあえず「削除ボタン押して」しかcapybaraさんに指示出しできてませんでした。


#解
`accept_alert`を追記します。

```spec/system/articles_spec.rb
RSpec.describe 'Articles', js: true, type: :system do
  let(:local) { FactoryBot.create(:local) }
  let(:article) { FactoryBot.build(:article, local: local) }
  let(:another_article) { FactoryBot.create(:article, local: local)}
  before do
    local.confirm
    @number_of_articles = Article.count
    @number_of_tags = Tag.count
  end

...略...

  describe 'deleting' do
    it 'deletes an article from index page' do
      article.save
      sign_in local
      visit local_articles_path(local)
      accept_alert do　#------- ここから
        within "li#article-#{article.id}" do
          click_link '削除'
        end
      end              #------- ここまでの話
      expect(page).to have_current_path local_articles_path(local)
    end
  end
end
```

#参考
https://portalshit.net/2013/06/22/check-alert-with-capybara-plus-selenium

https://stackoverflow.com/questions/29359976/rails-capybara-unexpected-alert-open-for-selenium-driver
2021年のトレンド「ゼロトラスト」とは何か?

こんにちは！AniwoのAironWorks Hacker Teamです。
今回はサイバーセキュリティの2021年のトレンド「ゼロトラスト」に焦点を当てて、「ゼロトラストって何？」「ゼロトラストがなぜ必要？」「結局ゼロトラストってどうすれば良いの？」という点について取り上げたいと思います。

#目次
1. ゼロトラストって何？
2. ゼロトラストがなぜ必要？
3. 結局ゼロトラストってどうすれば良いの？
4. 最後に


#ゼロトラストって何？
ゼロトラストとは一言で表すなら「性悪説」です。あらゆるコンピュータ、デバイス、ユーザーをまず信頼せず、それらを一連の認証・認可プロセスにかけることで信頼するかどうかを決めるモデルです。これだけ聞くとあまりにも当たり前のように聞こえるかもしれませんが、従来との比較で考えてみるとわかりやすいでしょう。

これまでのセキュリティのコンセプトは社内ネットワークと社外ネットワークを分離し、社内の全員を信頼し、社外の全員をひとまず疑おうというものでした。いわゆる「境界型のセキュリティ」です。社外からアクセスして欲しくないデータベースサーバーなどを社内ネットワークの中に、社内・社外の両方からアクセスしたいWebサーバーなどは境界上（非武装地帯、DMZ）に配置することで、社外からの攻撃への防御をしてきました。

一方、ゼロトラストモデルは社内であろうが、社外であろうが全員をまず疑います。つまり、信頼（トラスト）がゼロの状態から出発する、それこそがゼロトラストです。その上で、アクセスに対して認証・認可処理を行うことで所望のリソースにアクセスできるようにします。

#ゼロトラストがなぜ必要？
なぜゼロトラストが必要なのでしょうか？端的に答えるなら「環境が変化したから」です。

最も大きな変化はクラウドの普及でしょう。これまで各種サーバーは社内のデータセンターなどに配置するのが一般的でした。つまり、各社が各々のサーバーを用意する形です。しかし、AWSやGCPなどのクラウドインフラが発達し、アプリケーションそのものは社外クラウド上に位置することになりました。そうすると、「社外クラウド」が「社内」となる矛盾した状況が発生します。

さらに、ゼロトラストの必要性を加速させたのはリモートワーク・テレワークの普及です。これまでは物理的に出社して、社用PCから社内ネットワークに接続してきました。この方法だと従来のような境界型のセキュリティは容易に実装できます。しかし、リモートワークが普及してPCやそれを使う社員が社外に出ると、その管理が非常に煩雑になります。公私混同しないと保証できないかもしれませんし、フィッシングやソーシャルエンジニアリングにより脆弱になるかもしれません。先日、GoogleがゼロトラストセキュリティソリューションBeyondCorp Enterpriseを発表しました。

BeyondCorp は、アクセス制御地点をネットワークの境界から個々のユーザーに移すことで、従来のように VPN を介さなくてもほぼどこからでも安全に働けるようにします。
このようにGoogleもVPNからへの移行を積極的に推し進めています。（しかも、そのプロジェクトが2011年から始まったということですから驚きです。）

#結局ゼロトラストってどうすれば良いの？
ここまでのセクションで「ゼロトラストが流行となる必然性」のようなものが分かったと思います。そうなると

``` 結局ゼロトラストに移行するにはどうすれば良いのでしょうか。それは... ```

と言いたいところですが、これから先は2021年6月24日（木）実施予定の下記無料イベントで取り上げることにしたいと思います！
続きが気になる方は、ぜひ下記リンクからお申し込みください！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645867/a62cea81-4728-a9ac-f5b1-8b1de98ebf14.png" width="600">
####エンジニアからプロダクトマネジャーへ：エンジニアの生存戦略を経験者が語る
日　程：2021年6月24日(木) 18:00 - 19:00
申　込：https://aniwo-engineermeetup-1.peatix.com/
会　場：Zoom Webinar
主　催：Aniwo
参加費：無料

#最後に
最後までお読みいただきありがとうございました！ゼロトラストを考える一つのきっかけになれば幸いです>.<
また、毎週に記事をアップしておりますので、よければフォローもお願いします！
Flutter で Android アプリを作ってみたい

今まで作り慣れているブラウザアプリに加え、 Android アプリを作れたら開発の幅がさらに広がるなぁと思ったのでやってみます。
Flutter を使ってみた系の記事はすでに山ほどありますが、勉強の意味を兼ねてやってみようと思います。

[Flutter 公式サイトの Get Started](https://flutter.dev/docs/get-started/) に従い、進めてみます。

## Flutter のインストール

[こちらのページ](https://flutter.dev/docs/get-started/install) からお使いの OS に合った Flutter をインストールします。

## エディタのインストールとセットアップ

今回は Android Studio というエディタを使用します。
[こちら](https://developer.android.com/studio) からダウンロード、インストールします。

インストールが終わったら、エディタに Flutter のプラグインをインストールします。
Android Studio を開き、 Welcome to Android Studio の画面で **Configure > Plugins** と進みます。
検索画面が表示されたら **flutter** と入力し、一番上に出てくる **Flutter** の **Install** をクリックします。

![ide_install_plugin_flutter.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486784/e188d857-03c9-c047-254e-4716c13200e6.png)

(このとき、 Dart のプラグインも合わせてインストールされます。)

インストールが終わったら IDE を再起動します。

## 新規プロジェクトの作成

Welcome to ... の画面が再度表示されたら、 **Create New Flutter Project** をクリックします。

**Flutter Application** が選択されていることを確認し、 **Next** をクリックします。

Flutter SDK Path に、インストールした Flutter のパスを指定します。

![configure_flutter_sdk_path.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486784/2b553f61-a2d8-e6ef-a088-f463653b54d4.png)

Project name と Description を指定し、 **Next** をクリックします。

次の画面は何もせず、 **Finish** をクリックします。

![configure_flutter_sdk_path_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486784/e6ac7ace-72bf-aa55-aabf-1963c70a3ead.png)

プロジェクトの作成が始まるのでしばらくお待ちください。

## プロジェクトの実行

エディタ上部のメニューから **Tools > AVD Manager**　を選択します。

![tools_avd_manager.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486784/c4a1e429-edb3-04cd-6397-ec74a35feb0f.png)

仮想デバイスの一覧が表示されますので、一覧があれば **Actions** の **起動** アイコンをクリックして、仮想デバイスを起動します。

![virtual_devices.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486784/bf3ac35a-0024-4290-8bd3-a005e2d922c4.png)

ない場合、下の **Create Virtual Device...** を選択して仮想デバイスを作成、起動します。

そうすると、画面右上のツールバーに先ほど指定したデバイスが表示されているはずです。
その状態で **起動** アイコンをクリックします。

![toolbar.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486784/2501b62a-8cf6-0bbd-b5dd-8bee7dde1432.png)

起動しました。右下の **+** を押すとカウントアップします。

![demo_page.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486784/e95b5a34-4f34-83b4-2a56-dc7762b622b1.png)

## ホットリロードを試す

Flutter では、アプリを再起動することなく、コードの修正を反映させることができるホットリロードを搭載しています。

ここでは `main.dart` の 97 行目を以下のように修正し、文言を変更してみます。

```diff
-             'You have pushed the button this many times:',
+             'You have clicked the button this many times:',
```

保存して画面を確認すると、文言が変わっているのがわかります。

![hot_reload.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486784/44bc143a-7f77-9e15-c6c0-6d9137bd533d.png)

## 続き

今回 Flutter を簡単に触ってみましたが、 Flutter 公式では、この後 Hello world を表示する処理を作成するものや、チュートリアルが何種類か用意されていますので、またあとでやってみたいと思います。
みなさんもぜひやってみてください。
【備忘録】サクラエディタで改行コードを変換する

▼ なんだかんだ 3 回くらいググってるのでメモ

1. 対象ファイルをサクラエディタで開く
2. 「名前を付けて保存」を押下（shift + ctrl + S）
3. 改行コードを選択して、保存
4. 既に存在するファイルの場合は上書き保存

![スクリーンショット 2021-06-16 17.14.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441218/60eb9a83-0f85-208d-3d42-5b08925fb578.png)
Config.ahk等を編集してbug.nをビルドする方法

# Config.ahk等を編集したbug.nを、ビルドする方法
# 概要
## bug.nとは？

WindowsのキーマッピングソフトであるAutoHotkeyを用いて作られた、タイル型ウィンドウマネージャです！:sparkles:
（なんでキーマッピングソフトでウィンドウマネージャ作った？）

- [bug.n: Tiling Window Manager for Windows - GitHub](https://github.com/fuhsjr00/bug.n)

カスタマイズ性がやばく、ArchLinuxで[重度にxmonadを改造し](https://qiita.com/aiya000/items/be55d899e9c92dd57c35)使用していた僕を受け入れてくれる、最高の相棒です

通常はConfig.iniで設定を行います

しかしながらConfig.iniではカスタマイズできない内容が[たまにあるようです](https://github.com/fuhsjr00/bug.n/issues/178)
その場合、ソースを直に弄る必要があります

# 解決

ソースを弄った場合、もちろん再ビルドが必要です

以下の手順でビルドができます:+1::sparkles:

1. まだ対象のbug.nリポジトリをbug.nリポジトリをcloneする
1. 既にbug.nを起動している場合は、終了させる
1. ./toolsディレクトリをexplorer.exeで開く
1. ./tools/build.ahkを実行する
    - 同ディレクトリにデバッグログが出力されるので、もし失敗した場合はそちらを見るとよい
1. 出力された./bugn.exeを実行する
    - なお公式から提供された未改造bugn.exeよりも、起動がなぜか**かなり遅い**。うちの環境だけ？

Or

```shell-session
$ /mnt/c/Program\ Files/AutoHotkey/Compiler/Ahk2Exe.exe /in src/Main.ahk /out bugn.exe \
    && ./bugn.exe
```

（これは簡単だけど、体感的には前述の方法よりもさらに起動が重い気がする。）

- - -

やったー！！
オブジェクト指向についての学習をまとめました

# オブジェクト指向とは
#####一言で説明すると**設計手法**のひとつです。

###設計とは**分割と結合を考える行為**
例えば、ランチェスターの2次法則　仕事の成果＝量²✕質というものがありますが
つまり同じ質を1とした場合、仕事の成果＝量²です。
単純に量をコストと考えると、、  
《100をそのまま作った場合》
**100作るにかかったコスト＝100²＝10,000**
《100を10に分割した場合》
**10を10個作るのにかかったコスト＝10²×10＝1,000**
  
物事を分ければその分コストを下げることができます。
**【設計工程とは分割と結合を考えること＝コストを削減するための作業】**

##オブジェクト指向の目的
#####オブジェクト指向設計とはランニングコストを下げるために考えられた設計
コストには**イニシャルコスト(初期工程)**と**ランニングコスト(保守工程)**があります。
設計工程においてどのように考えるかでどちらのコストが下げられるかが違います。

* **どうすれば簡単にコードが書けるか考える→イニシャルコストを下げるための設計**
* **どうすれば簡単に管理できるか考える→ランニングコストを下げるための設計**

|オブジェクト指向設計|構造化設計|
|:---|:--|
|データの扱い方→どんな処理をするのか|どんな処理をするのか→データの扱い方|
|メリット：仕様変更の際、処理の設計変更が最低限に抑えられる|メリット：処理から考えるため理解しやすく作りやすい|
|デメリット：必要な処理以外の部分まで考えながら作らないといけないので難しくなる|デメリット：仕様変更の際、処理側も変える必要がでてくるため仕様変更に弱い|
##仕様変更に強い設計
* 仕様変更の影響範囲がすぐに特定できること
* 仕様変更そのものの作業が楽であること

影響範囲の特定をしやすくするための考え方に**データ抽象**があります。
複数の関係するデータをまとめて、見分けを付けやすい単位をつける。
【名前、売価、卸値、説明、製造年月日、賞味期限】 →食品についての**データ群という想像**がつく。
仕様変更の際もどこを変更するか想像しやすい。

このデータを集めて、一つの**オブジェクト**としてまとめておくことを**カプセル化**といいます。

## オブジェクト指向4大原則
* **カプセル化**･･･関係するデータと、そのデータを必要とする処理を一つにまとめることで、影響範囲の特定を容易にし、仕様変更に強い設計の土台を作る。**単純にアクセスメソッドを作ることではないので注意**。
* **データ隠蔽**･･･カプセル化の維持が目的。フィールドをPrivateにする。
* **抽象化**･･･共通部分だけをひと一括りにして単純化して、全体を**シンプルに結合**していくこと。**多態性**の設計をするときにも必要な考え方。
    * 多態性
        * 扱い方と実装を分けておき、プログラムを使う側とプログラムとして使われてる側を分離することで、使う側が仕様変更の影響を受けないようにすること。 
* **情報隠蔽**･･･保守性を保つため、公開する情報と非公開の情報にわけて決められた変更しかできないようにすること。設計の劣化を防ぐ。
    * データ隠蔽のためのアクセス修飾子
        * public
        * protected
        * default(アクセス修飾子を使わない)
        * private
    * アクセス修飾子の設計では、**アクセス修飾子なし**をデフォルトとしている。これは保守性を保つためのデータ隠蔽の考えを基にしている。そのため**アクセス修飾子は必要時以外は使わないのが基本**となる。

## オブジェクト指向まとめ
データと、そのデータを必要とする処理を一つにまとめた**部品の最小単位をオブジェクト**といいます。
そのオブジェクトを組み合わせて、どのように**仕様変更に強いプログラムを設計**するか、どのようにその**設計を維持して保守性の高いもの**にしていくか。
それを考えるための**設計手法の一つの考え方をオブジェクト指向**といいます。

以上

> 参考にした動画が非常にわかりやすかったのでリンクを貼っておきます。
> [オブジェクト指向の基礎](https://www.youtube.com/playlist?list=PL0BiAlg8j4ZuDSu9GLU73-CVFgenU8T2P)
SQL Server: レプリケーションエラー発生時の原因調査方法を考えてみた

#背景
SQL Serverのトランザクションレプリケーションにおいて、エラーが発生する原因は複数存在します。
今回はその中で、「サブスクライバ側のテーブルを更新してしまった場合」の原因特定の方法について考えてみました。
自分で手動更新してしまった場合は、「どのクエリを更新したか」は分かりますが、誰がどのテーブルに対して更新を実施したか分からない状況を想定しています。

#方法
####１．サブスクライバ側で、エラーが発生したパブリケーションの最後に成功していたtransaction_timestamp(=xact_seqno)を取得
パブリケーション名も取得しておくと良いと思います。

```sql
select * from MSReplication_Subscriptions
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148310/e6049739-f65f-e506-de0c-0ff604c9e1df.png)

####２．該当のパブリケーションのアーティクルを含むxact_seqnoの中で、最後に成功していたxact_seqno以降のxact_seqnoを古い順に取得
取得できたxact_seqno＝失敗した可能性が高いxact_seqno＝レプリエラーの原因となったクエリ情報を取得できる可能性があると考えました。

```sql
--distributionDBで実行
--最後に成功していたxact_seqnoの次のxact_seqnoを取得 = 失敗していた可能性が高いxact_seqno
declare @error_publication_name varchar(1000) = 'my_publication'
select top 10 * from MSrepl_commands
where xact_seqno > 0x00653B43000150F0006B000000000000
and exists (
    select publication, * from distribution.dbo.MSarticles a join distribution.dbo.MSpublications b on a.publication_id = b.publication_id
    where a.article_id = MSrepl_commands.article_id
    and publication = @error_publication_name
)
order by xact_seqno
```

ポイントとしては、複数パブリケーションが混在している環境だと、「最後に成功したxact_seqnoの直後のxact_seqno=失敗したxact_seqno」とは限らないため、existsを使ってエラーが発生したパブリケーションのアーティクルを含むxact_seqnoに限定して抽出を行っている点です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148310/8af5b458-be4c-2c14-6ccd-234ec6b0c0b6.png)

#### ３．コマンドを解決
取得できた「怪しいxact_seqno」を使って、どういったコマンドを実行しようとしていたか解決します。
これにはsp_browsereplcmdsを使います。

```sql
exec sp_browsereplcmds @xact_seqno_start = '0x00653B490001A2B8009F', @xact_seqno_end = '0x00653B490001A2B8009F'
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148310/7c221ae0-61ab-1628-2c80-8fd860a64672.png)

この例だと、MyTableのDELETEを実行しようとしてエラーとなっていると推測できます。
この場合は、サブスクライバ側にPK＝55485のレコードが存在しない（＝誤って手動削除した可能性）ためにエラーとなっており、該当のPKを持つレコードをサブスクライバ側のテーブルにINESRTしてあげればOKです。

#### 複数レコードを更新してリカバリが困難な場合
この場合は、データを補正するよりも、エラーを起こしているテーブルが確認できた後は以下の手順をとるのもありだと思います。

１．該当のパブリケーションからエラーを起こしているテーブルを削除
２．パブリケーションに最後テーブルを追加
３．スナップショットエージェントを実行

この手順を踏むと、一時的にサブスクライバ側から追加したテーブルがDROPされることと、スナップショットエージェントの実行時にパブリケーションに含まれる全テーブルに一時的にSch-Mロックがかかるのでブロッキングの懸念がありますが、レプリ復旧の確実な手順となりますので覚えておいて損はないと思います。
Discovery Premiumやってみた　〜Answer finding編〜

Watson DiscoveryのPremiumプランで提供されている機能を使ってみました。
今回はAnswer findingというβ機能（as of 2021/06/14）です。

#Answer findingとは
マニュアルの記述によると
https://cloud.ibm.com/docs/discovery-data?topic=discovery-data-query-parameters#answer-finding

- 照会に対する**「短い答え」**とその確信度を返す

「答え検索に関する注記」として以下のようなことが書かれていました（詳細はマニュアルを参照してください）

- 複数の文が互いに近ければ、文を跨って答えを探す
    - 例えば、“When did IBM purchase Red Hat?”という質問に対して、“IBM acquired Red Hat for $34 billion. The deal closed in July of 2019." と書かれていても **July of 2019** と回答してくれる
- 暗黙的な質問にも対応している
    - 例えば：”What company developed the AS/400?”と質問しても“company that developed the AS/400”と質問しても回答を提示してくれる。
- 場合によっては長い文で答えを提示する時もある
    - 例えば、「どのように」「どうして」などの質問
- 一方、短い答えが期待される質問に対しては短く答える
    - 例えば、人や場所、日付を問う質問

ちょっと面白い注記としては以下のようなことも書かれていました（詳細はマニュアルを参照してください）

- 答えを見つけることはできますが、答えを作成することはできない。
    - 例えば、"2020年の売り上げは？"という質問に対して、データの中に各四半期の売上が記されていたとしても、合計額を計算して提示することはできない。
        - → さすがにこれができたらすごいですね
- 長い文で答える場合、要約はしない
- 意見を尋ねる質問もできるが、その意見の妥当性を評価するようなことはしない
    - 例えば、"アイシャドゥは青がいいかな"と質問した場合、文章内から"今年の**アイシャドゥの流行色は青**です。"というテキストを回答として提示するだけで、それが良いかどうかはあなた次第という感じ。


**でも、なんだか凄そう！**

#やってみた
##前提
DiscoveryのDocument Retrievalというタイプのプロジェクトを作り、[ここから](https://www.rondhuit.com/download.html)ダウンロードできるlivedoorニュースコーパスのデータを投入済みです。

- Sports Watch
- ITライフハック
- 家電チャンネル
- MOVIE ENTER
- 独女通信

それぞれ300件ずつ合計1500件のデータを投入。

##まずは、Answer findingを使わない場合
「リアル・スティールの主人公は誰？」と質問すると
以下が回答して返ってきました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/148302/7330c3f4-e2d8-c2e2-29a9-3c2c7c2cb51b.png)

Discoveryは全文検索なのでヒットしている文書として間違っているわけではないです。
「リアル・スティールの主人公は誰？」という質問を``リアル`` ``スティール`` ``主人公``などに分割して検索をしているので、キーワードがヒットした文書が回答して返ってくるのは自然だと思います。


##いよいよ、Answer finding！
では、Answer findingの機能を使うとどういう結果が返ってくるのでしょう。

Answer findingはβ機能なので現在はAPIのみ提供とのことで以下のように実行してみました。


```
curl -X POST -u "apikey:u2l*****4oP" -d @answerfindings.json -H "Content-Type: application/json" "https://api.us-south.discovery.watson.cloud.ibm.com/instances/9a****b09/v2/projects/26*****54/query?version=2019-11-29" | jq '.results[].document_passages[].answers[] | {answer_text:.answer_text, confidence:.confidence}'
```
***注意***：便宜上 Answer finding の「短い答え」が記載されているデータのみに出力を絞っています。

curlで指定している `answerfindings.json` の中身はこんな感じ。
ポイントは`passages`の各設定項目。

```
{
 "collection_ids" : [
  "6bb*****85"
 ],
 "natural_language_query" : "リアル・スティールの主人公は誰？",
 "return" : [
   "document_id"
 ],
 "passages" : {
   "enabled" : true,
   "per_document" : true,
   "find_answers" : true
 }
}
```



結果（上位3件）

```
{
  "answer_text": "ヒュー・ジャックマン",
  "confidence": 0.82901204
}
{
  "answer_text": "息子マックス",
  "confidence": 0.6774776
}
{
  "answer_text": "父と息子",
  "confidence": 0.17049855
}
{
  "answer_text": "チャーリー",
  "confidence": 0.14173211
}
```
お〜！ `ヒュー・ジャックマン` **大正解！！** すごいですねぇ〜！ :clap_tone2:

#まとめ

今はβ機能ですが早く本機能として搭載して欲しいですね！

#お断り
このサイトの掲載内容は私自身の見解であり、必ずしも所属会社の立場、戦略、意見を代表するものではありません。 記事は執筆時点の情報を元に書いているため、必ずしも最新情報であるとはかぎりません。 記事の内容の正確性には責任を負いません。自己責任で実行してください。
合体演算子でif文をなくす【PHP】

何か条件分岐をさせる際に、ついついifを使いがちでしたが、合体演算子でええやんということが多いので、備忘録として記事に残しておきます。

## inputが空か判定をしたい場合

````
if ($this->input('test')) {
    return $this->input('test');
 }
return [];
````
上記のコードを書いた場合、ifは一つだけだし、えんやん♪となるかもしれません。
しかし、これを一行にまとめることができます

````
return $this->input('test') ?? [];
````
この一行で同じ意味になります。

````
A ?? B（Aがnullの場合はB）;
````
という意味です。

今回の場合は、
input('test')がnullの場合、空配列をreturnするよ、という意味です。
if文でnullの場合はBのように書きたいとき、合体演算子を使うと良いでしょう^^
PHPで文字列に含まれている日本語(ひらがな、カタカナ、漢字）の文字数を取得

バリデーションなどで日本語の文字数をチェックするのに利用出来るかと思います。

```php
$count = preg_match_all('/[ぁ-んァ-ヶー一-龠]/u', $text);
```
Azure利用に関する資料

# 概要

Azure 利用、CSPに関する資料に関する情報のメモ

# 資料

## Microsoft資料

- [Azure利用ガイド](https://download.microsoft.com/download/4/e/4/4e43bee8-e65d-4935-9eb5-cf0627a2820b/Azure_usage_guide.pdf)
  
    本番でAzureを使うために、将来的な使い方も考慮した運用設計を行うための資料。テナント、アカウント、RBAC、ポリシー、管理グループ、サブスクリプションなどを考慮する必要がある

## SB C&S株式会社 [Azure 相談センター](https://licensecounter.jp/azure/)

- [Microsoft Azure ご紹介資料](https://licensecounter.jp/azure/download/azure01_outline.pdf)

    Azure を利用したサービスを提供するために必要な契約について記載されている

## 東京エレクトロンデバイス株式会社

- [CSPとは｜Microsoft Azureでビジネスを始める｜東京エレクトロンデバイス](https://esg.teldevice.co.jp/iot/azure/csp/)

- [Azure CSPよくあるご質問｜サポートサービス｜東京エレクトロンデバイス](https://esg.teldevice.co.jp/iot/azure/csp/faq.html)

- [サポートページTOP](https://cptechweb.teldevice.co.jp/hc/ja)

    ログインなしの状態でも、Azure AD、セキュリティ面も含め様々な情報を参照できます。
sec5 【Route53】ドメインを設定しよう

設置したwebサーバーにドメインを設定し、アクセスしやすくする。
ドメインの設定を通じてDNSに関する知識を深める。
IPアドレスに対して、ドメイン名というタグ付けする事で、アクセスしやすくなるイメージ、多分。

## ドメイン

ドメイン名はピリオドで区切られている。例えば下記に関しては、

www.example.co.jp

jp：トップレベルドメイン
co：第二レベルドメイン
など左に向かうにつれてレベル分けされている。

ICANN：ドメイン全体の管理
レジストリ：トップレベルドメイン（.jp, .com）の管理
レジストラ：リセラに卸す、ドメインを一般消費者に販売
リセラ：再販業者、ドメインを一般消費者に販売

## DNS

ドメインとIPアドレスを紐付ける必要がある。その関連付けを行う管理システムのことをDNSという。ネームサーバーとフルリゾルバで構成。

### ネームサーバー

イメージは電話帳。ドメインとそのIPアドレスが記載されている。

### フルリゾルバ

イメージは秘書。フルリゾルバに問い合わせれば、複数のネームサーバーから検索・案内してくれる。

1. スマホ「http://www.example.com/」
2. フルリゾルバ
3. フルリゾルバがルートネームサーバーへ問い合わせ
4. フルリゾルバがcomネームサーバーへ問い合わせ
5. フルリゾルバがwww.exampleネームサーバーへ問い合わせ → IPアドレスを提示
6. フルリゾルバがIPアドレスをスマホへ案内
7. スマホがIPアドレスを用いて、webサーバーへ接続

この一連の流れを「名前解決」という。またそれぞれのネームサーバーが管理しているドメイン情報を「ゾーン」という。

## Route53

AWSのDNSサービス。ネームサーバーの役割を担う。高可用性、高速、フルマネージドサービス。

### ホストゾーン

DNSのリソースレコードの集合。ゾーンファイルのようなもの。

### レコードセット

リソースレコードのようなもの

### ルーティングポリシー

Route53がRecord Setに対してどのようにルーティングを行うかを決める。
フルリゾルバから「〇〇のIPアドレスはどこ？」と尋ねられた時に、どのIPアドレスを返すかというルールを定めたもの。（シンプル, 荷重, レイテンシー, 位置情報, フェイルオーバーなど

### 実装【ハンヅオン】

1. スマホ「aws-and-infla-iwerks.com」
2. フルリゾルバ
3. フルリゾルバがルートネームサーバーへ問い合わせ
4. フルリゾルバがcomネームサーバーへ問い合わせ
5. フルリゾルバが**Route53のネームサーバー**へ問い合わせ → IPアドレスを提示
6. フルリゾルバがIPアドレスをスマホへ案内
7. スマホがIPアドレスを用いて、webサーバーへ接続

⑤の部分を変更したい。

作業内容

1. ドメインのネームサーバーをRoute53に変更
- Route53でホストゾーンを作成
- ネームサーバーをお名前.comからRoute53に変更
1. ドメインに紐づくIPアドレスを登録
- Route53でレコードセットを作成

ホストゾーンを作成 → NSレコード・SOAレコードが登録される。
NSレコード：ドメイン名を管理するネームサーバーとの紐付け
SOAレコード：ドメイン名を管理するゾーンの情報
Aレコード：ドメイン名とIPアドレスの関連付け
LaravelでJavaScriptが動作しないとき



##LaravelでJavaScriptが動作しないとき


いつも通りにJavaScriptを書いたのに、どこにもエラーはなく、Consoleでは正しく動作しているのにも関わらずblade上でJavaScriptが動かないとき。

なかなか無いとは思いますが、遭遇してしまい、時間を無駄にしたのでメモ。

結論。app.jsのせい。
body下部にapp.jsを書きますが、そのapp.jsより下に書けば動作しました。


```
<script src="{{ asset('/js/app.js') }}"></script> 
```
AWS Cloud Watch Logs のロググループ名を一覧で取得する方法

# ロググループ名を一覧で取得する方法
aws cli　にてロググループ名を一覧で取得できないかとなり、その時の内容を備忘録として記載します。
どなたかの役に立てば幸いです。

結果からお伝えするとコチラのコマンドで取得しました。

```
aws logs describe-log-groups | jq -r '.logGroups[] .logGroupName'
```

## jqコマンド
少し解説させてもらうとjqコマンドというjson形式で記載されているデータからデータの取得や整形したりするために使うコマンドだそうです。

## ちょっと解説
.が{}を表す。{"price"：2500}の場、.price で　2500　となるそうです。

##### ダブルクォーテーションが邪魔だったのでオプションの -r で除くことが出来ます。


# マニュアル
http://stedolan.github.io/jq/manual/

コチラを参考にさせていただきました。
SQL基本まとめ②（サブクエリ、JOIN、データの挿入・更新・削除）

#
#はじめに
Progateで学習したSQLの基本を自分なりにまとめておく。

##サブクエリ
2つ以上のクエリを1つにまとめることができる。
サブクエリを含むクエリの場合、サブクエリから実行される。

```sql
-- クエリ1 (     -- ()でサブクエリを囲む
--    クエリ2
-- );

SELECT name  -- ウィルより多いゴール数のプレイヤーの名前を取得するクエリ
FROM players
WHERE goals > (
  SELECT goals  -- ウィルのゴール数を取得するサブクエリ
  FROM players
  WHERE name = "ウィル"
);
```

##AS
カラム名に別名を定義できる。

```sql
-- カラム名 AS "名前"

SELECT name AS "身長180cm以上の選手"
FROM players
WHERE height >= 180;
```


##JOIN
テーブルを紐づけるために、外部キーと主キーが用意されたカラムを用意する。
同じ数字が入っているレコード同士を紐づける。

`JOIN`を用いて、複数のテーブルを1つに結合する。
`JOIN`を含んだクエリでは、はじめに`JOIN`が実行される。

```sql
-- SELECT *
-- FROM テーブルA
-- JOIN テーブルB
-- ON 結合条件
--
-- 結合条件は
-- ON テーブル名.カラム名(外部キー) = テーブル名.カラム名(主キー)
-- で指定する

SELECT *
FROM players
JOIN countries
ON players.country_id = countries.id;
```

###複数テーブルでのカラムの指定
テーブルごとにガラム名が重複している可能性がある。
`テーブル名.カラム名`
とすることで、同名のカラムでも個別に指定することができる。

```sql
SELECT countries.name, SUM(goals)
FROM players
JOIN countries
ON players.country_id = countries.id
GROUP BY countries.name;
```

###LEFT JOIN
通常、外部キーがNULLのレコードは実行結果に表示されない。
`LEFT JOIN`を用いると、`FROM`で指定したテーブルのレコードを全て取得し、外部キーが`NULL`でも`NULL`のまま実行結果に表示される。

```sql
SELECT players.name AS "選手名", teams.name AS "前年所属していたチーム"
FROM players
LEFT JOIN teams -- teamsテーブルにはNULLが含まれている
ON players.previous_team_id = teams.id;
```

###3つ以上のテーブルの結合
`FROM`は一つで、`JOIN`を複数使用して3つ以上のテーブルを結合する。

```sql
-- SELECT *
-- FROM テーブルA
-- JOIN テーブルB
-- ON 結合条件
-- JOIN テーブルC
-- ON 結合条件

SELECT *
FROM players
JOIN countries
ON players.country_id = countries.id
LEFT JOIN teams
ON players.previous_team_id = teams.id;
```

##全体の実行順序
1. テーブルの指定: `FROM`
2. 結合: `JOIN`, `ON`
3. 取得条件: `WHERE`
4. グループ化: `GROUP BY`
5. 関数: `COUNT`, `SUM`, `AVG`, `MAX`, `MIN`
6. HAVING: `HAVING`
7. 検索: `SElECT`, `DISTINCT`
8. 順序: `ORDER BY`
9. 制限: `LIMIT`


##データの取得・挿入・更新・削除
###SELECT（取得）
```sql
-- SELECT カラム名1, カラム名2
-- FROM テーブル名;

SELECT name
FROM purchases;
```

###INSERT（挿入）
多くの場合、`id`カラムにはAUTO INSERT(自動挿入)と呼ばれる機能が使われるため、その値は省略できる。

```sql
-- INSERT INTO テーブル名 (カラム名1, カラム名2, カラム名3)
-- VALUES (値1, 値2, 値3); -- 順番通りに挿入される

INSERT INTO students ( name, course)
VALUES ( "Kate", "Java");
```

###UPDATE（更新）
※注意
`WHERE`で指定しないと、カラム内の全データが更新されるので注意。
1. `SELECT` で操作するデータを確認する。
2. `UPDATE` でデータを更新する、実行後にデータを戻すことができない。

```sql
-- UPDATE テーブル名
-- SET カラム名1 = 値1, カラム名2 = 値2 -- 指定カラムを新しい値にする
-- WHERE カラム名 = 値; -- 更新するレコードをWHEREで指定する

UPDATE students
SET name = "Jordan", course = "HTML"
WHERE id = 6;
```

###DELETE（削除）
__レコード__ を削除する。
※注意
`WHERE`で指定しないと、カラム内の全データが削除されるので注意。
実行後はレコードを元に戻せない。

```sql
-- DELETE FROM テーブル名
-- WHERE カラム名 = 値;

DELETE FROM students
WHERE id = 7;
```

##おわりに
主なやつは大体まとめられたと思う。
次回からは現在勉強しているRuby on Railsチュートリアルについてまとめていきたいと思う。

[◀️ SQL基本まとめ① （基本形、集計関数等）](https://qiita.com/moromin/items/7f0c16c9fd239f051844)
【kotlin】オブジェクト宣言 コンパニオンオブジェクト

#■オブジェクト宣言とは
一つのインスタンスしか持たないようなクラスを用意したい時に使う

▼コードの書き方
classの場所がobjectに変わるだけ

```kotlin
object オブジェクト名 {オブジェクト本体}
```
▼インスタンスを作らずにそのままオブジェクト名を呼び出して使える

``` kotlin
object Hoge {

 var name = "吉田"
 var num =   3

 fun aaa() {
    println("${name}は${num}歳です。")
  }
}

Hoge.name = "佐藤"
Hoge.aaa() //結果: 佐藤は3歳です。
```

#■コンパニオンオブジェクト
kotlinでStaticメンバを利用するもの

■特徴
オブジェクト宣言と似ている。
インスタンスなしで値にアクセスできる。
コンパニオンオブジェクトでは決められたもののみをオブジェクト宣言のようにインスタンスなしでアクセスできる。
オブジェクトに紐付いているのでインスタンスによってアクセスできない

```kotlin
class Hoge {
 companion object Factory {
     fun aaa() = println("こんにちわ")
 }
}

Hoge.aaa() //結果: こんにちわ
```