OmniAICreator/Qiita-1.07M huggingface dataset:
【VBA / ADO】ExcelへのDB接続に OLEDBプロバイダ のほかに ODBCドライバー も使えることについて

# 1. 概要

VBAからMySQLに接続する方法を調べていたら、`Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)`というものがあることに気づきました（無知がゆえ）。
![2021-02-14 234553.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551412/67f0791b-c9aa-8101-5dee-4c3ab2bd4f62.png)
調べてみると、このドライバでも[ADOによる接続ができる](https://docs.microsoft.com/ja-jp/office/troubleshoot/excel/fails-accessing-excel-through-odbc)ことが分かったので、試しに使ってみました。
基本的なところは、普段使用している`Microsoft.ACE.OLEDBプロバイダ`と大差はないのですが、
**データ型の読込み内容に違い**がありました。
この点についても、既に**[丁寧に検証されているサイト](http://blog.livedoor.jp/tea_cocoa_cake/archives/7510828.html)**がありましたので、もはや書くことがないのですが、せっかく調べたので書いておきます。

|Excelの書式|OLEDBのデータ型|ODBCのデータ型|
|---|---|---|
|文字列|adVarWChar(202)|adVarChar(200)|
|数値|adDouble(5)|adDouble(5)|
|通貨|adCurrency(6)|adCurrency(6)|
|日付|adDate(7)|adDBTimeStamp(135)|


Microsoftが公式に提供しているものなので**知っている方にとっては当たり前**かもしれません。

# 2. ソースコード例

## 2-1. Connectionオブジェクトの接続部分

### 一般的に使用されているADODB.Connectionの接続方法

```vb
    Dim cn As New ADODB.Connection
    cn.Provider = "Microsoft.ACE.OLEDB.12.0"
    cn.Properties("Extended Properties") = "Excel 12.0"
    cn.Open ThisWorkbook.Path & "\" & "TestTable.xlsx"
```

### Microsoft Excel Driverを使用したADODB.Connectionの接続方法

```vb
    Dim cn As New ADODB.Connection
    Dim filePath As String: filePath = ThisWorkbook.Path & "\" & "TestTable.xlsx"
    cn.ConnectionString = "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DBQ=" & filePath & ";ReadOnly=1"
    cn.Open
```

## 2-2. 全体のサンプルコード

```vb
'【ODBCドライバー】を使用したアクセス方法
Sub ADOSample()
    'インスタンスを作成
    Dim cn As New ADODB.Connection
    Dim rs As New ADODB.Recordset
    Dim filePath As String: filePath = ThisWorkbook.Path & "\" & "TestTable.xlsx"
    
    'ADO接続
    cn.ConnectionString = "Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DBQ=" & filePath & ";ReadOnly=1"
    cn.Open
    
    'SQL文の実行
    rs.Open "SELECT * FROM [Sheet1$] WHERE 区分 = '野菜'", cn
    
    'Recordset
    Do Until rs.EOF
        Debug.Print rs("品名") & ", " & rs("単価") & ", " & rs("購入日")
        rs.MoveNext
    Loop
    
    'メモリの解放
    rs.Close: Set rs = Nothing
    cn.Close: Set cn = Nothing
End Sub
```

**参考サイト**
・[PRB: ODBC を使用して Excel にアクセスする場合、"操作は更新可能なクエリを使用する必要があります"](https://docs.microsoft.com/ja-jp/office/troubleshoot/excel/fails-accessing-excel-through-odbc)
・[ExcelファイルへのADOでのCREATE/DROP/ALTER TABLE](http://blog.livedoor.jp/tea_cocoa_cake/archives/7510828.html)
・[DAO、ADO、ODBC、OLE DBの違いを簡単にまとめる](https://mat0401.info/blog/dao-ado-odbc-oledb/)
Pythonでリブログやスキを一括取得する方法

ここではTumblrのリブログなどを一括取得できるPyTumblrを紹介します。

まず、コマンドラインで以下のようにしてインストールします。

```
pip install pytumblr
```

Tumblrを外部から操作する場合はアプリを登録しAPIを使う必要があります。 https://www.tumblr.com/oauth/apps からアプリを登録してください。

コンシューマーキーやコンシューマーシークレットを取得して、以下のようにすると初期化できます。

```python
import pytumblr

client = pytumblr.TumblrRestClient(
    TUMBLR_CONSUMER_KEY,
    TUMBLR_CONSUMER_SECRET,
    user.tumblr_access_token,
    user.tumblr_secret_key
)
```
`TUMBLR_CONSUMER_KEY`, `TUMBLR_CONSUMER_SECRET`,`user.tumblr_access_token`, `user.tumblr_secret_key`は自分のものに変更しておいてください。Webサービスなど不特定多数のユーザーのアクセストークンを必要とする場合は[DjangoでTwitterなどのOAuth 1.0認証を行う方法](https://qiita.com/lamrongol/items/551c931e04400e30ea80)を見て取得してください。

使い方は[公式ドキュメント](https://pypi.org/project/PyTumblr/)を見てもらうこととして、以下ではリブログやスキを一括取得する方法を紹介します。
とりあえずコードです。

```python
offset = 0

def fetch_tumblr_reblogs(client):
    while True:
        response = client.posts("blog_name", limit=20, offset=offset, reblog_info=True)

        # Get the 'posts' field of the response
        posts = response['posts']

        #それ以上投稿がない場合
        if not posts:
            break

        for post in posts:
            #isReblog
            if 'reblogged_from_id' in post:
                print(post)

        offset += len(posts)
```

`blog_name`の部分はリブログを取得したいブログのURL(`https://www.tumblr.com/blog/***`)の***の部分を入力してください。
TumblrのAPIは「最新20件を取得」などはできず一番古い投稿から順に取得していきます。`offset`を設定すると一番古い投稿からoffset件飛ばして投稿を取得できます。

スキを取得するには以下のように少し変更します。

```python
offset = 0

def fetch_tumblr_reblogs(client):
    while True:
        response = client.likes(limit=20, offset=offset)

        # Get the 'posts' field of the response
        posts = response['liked_posts']

        #それ以上投稿がない場合
        if not posts:
            break

        for post in posts:
            print(post)

        offset += len(posts)
```

# 宣伝
以上のコードを使って[Remind Archives](https://remind-archives.herokuapp.com/main/getting_started)というWebサービスを作りました。これは過去のいいねやRTをランダムに表示して発見とセレンディピティを生み出すことを目的とするサービスです。
Pythonでツイートやいいねを一括取得する方法

Twitter APIを使用するためのPythonライブラリはいくつもありますが、ここではツイートなどを一括取得できるTweepyを紹介します。

まず、コマンドラインで以下のようにしてインストールします。

```
pip install tweepy
```

Twitterを外部から操作する場合はアプリを登録しAPIを使う必要があります。以下の記事を参考に登録してください。
[2021年度版 Twitter API利用申請の例文からAPIキーの取得まで詳しく解説 \| 新宿のホームページ制作会社 ITTI（イッティ）](https://www.itti.jp/web-direction/how-to-apply-for-twitter-api/)

コンシューマーキーやコンシューマーシークレットを取得して、以下のようにすると初期化できます。

```python
import tweepy

auth = tweepy.OAuthHandler(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET)
auth.set_access_token(user.twitter_access_token, user.twitter_secret_key)
api = tweepy.API(auth)
```
`TWITTER_CONSUMER_KEY`, `TWITTER_CONSUMER_SECRET`,`user.twitter_access_token`, `user.twitter_secret_key`は自分のものに変更しておいてください。Webサービスなど不特定多数のユーザーのアクセストークンを必要とする場合は[DjangoでTwitterなどのOAuth 1.0認証を行う方法](https://qiita.com/lamrongol/items/551c931e04400e30ea80)を見て取得してください。

```python
api.update_status("Hello World")
```
のように投稿などができるようになります。他にも色々できますがそれは検索してもらうか[公式ドキュメント（英語）](https://docs.tweepy.org/en/latest/)を読んでもらうとして、この記事ではツイートやいいねを最大限取得する方法を解説します。

とりあえずコードです。

```python
FIFTEEN_MINUTES = 15 * 60

latest_id = 1

def limit_handled(cursor):
    while True:
        try:
            yield cursor.next()
        except tweepy.error.TweepError:
            print('Twitter rate limit')
            time.sleep(FIFTEEN_MINUTES)
        except StopIteration:
            print("fetch end")
            return None

def fetch_twitter_likes(api):
    first = True
    for status in limit_handled(tweepy.Cursor(api.favorites, since_id=latest_id).items()):
        if first:
            latest_id = status.id_str
            first = False
        print(status.text)
```

`for status in limit_handled(tweepy.Cursor(api.favorites, since_id=latest_id).items()):`の部分でいいねを取得しています。`api.favorites`の部分を変えることで検索結果などのツイート一覧を取得できます。
問題は`limit_handled`の部分です。ここでツイートを順次取得しているのですが、TwitterのAPI制限にかかった場合`tweepy.error.TweepError`が発生し、ここでは15分待機しています。すべてのツイートを取得した場合`StopIteration`が発生しループを終了します。

# 宣伝
以上のコードを使って[Remind Archives](https://remind-archives.herokuapp.com/main/getting_started)というWebサービスを作りました。これは過去のいいねやRTをランダムに表示して発見とセレンディピティを生み出すことを目的とするサービスです。
Naming in programming - プログラミングにおけるネーミング

OpenStreetMap のエディターや、Mapbox の製品開発を率いた著名なエンジニア Tom MacWright ([@tmcw](https://twitter.com/tmcw/)) さんが、Gists に書いたプログラミングにおけるネーミングに関するメモを翻訳しました。

https://gist.github.com/tmcw/35849b7e9b86bb0c125972b2bb275bc7

ある程度のスキルの人には常識的な内容とも言えますが、こういうレイヤーの話はなかなか文書化されることが無いなと思ったので、もし皆さんのお役に立てれば幸いと思います。

一部ニュアンスがわかりにくいところもあったので、もしもっといい役があるよ～という方はぜひ編集リクエストをお願いします。

---

プログラミングの中におけるネーミングという作業はとても難しいものです。でも不可能ではありません。そこで、私がいつも守っているガイドラインをいくつか紹介しましょう。

### 曖昧な言葉（weasel words）を避ける

ウィキペディアの [weasel words に関する記事](https://en.wikipedia.org/wiki/Weasel_word) がすごい。この文脈ではこんな言葉を指しています。

- Data
- Process
- Run
- Do
- Setup

これらの言葉は通常、全く意味がありません。どの変数でもデータと呼ぶことができます。どのメソッドもプロセスと呼ばれるかもしれません。これらの単語は、情報を与えたり、区別したりするものではありません。これらの名前を含むほとんどのメソッドや変数は誤ったネーミングです。よく考えて、何が良いのか考えてみてください。

- `processData` のようなメソッドは、おそらく単にデータを処理しているだけではないでしょう。たぶん `processCsv` ではないでしょうか？
- それは本当にそのCSVを「処理」しているだけなのでしょうか。数値であるべき文字列を強制的に処理（coercing）しているのかもしれません。たぶん `coerceCsvRows`?

たぶんこのほうがいいですよね！

- `data` は、たぶん単なるデータではないですよね。たぶんそれは `recentPosts` （最近の記事）のようなものでは？

### パターンに忠実に従う

データの型を示す何らかのグループがあるとしましょう。たとえば `Commentable` （コメント可能）や `Archivable` （アーカイブ可能）などです。これらは正確にいうと形容詞です。これらに続く部分については、たとえやっかいなケースがあったとしてもパターンに従ってください。

多くの場合、ルールに例外があるよりも、やっかいなケースを受け入れるほうが良いでしょう。例えば `XMLHttpRequest` は、頭字語を保持しないキャメールケース（XmlHttpRequest) でも、保持するキャメルケース (XMLHTTPRequest) でもない名前です。ルールは一つに統一しましょう！

### 文字数をケチらないようにしましょう

不必要な略語は、一文字の変数名より微妙な形です。何かが `document` であるならば、`doc` ではなく `document` と呼びましょう。国際化のことを `i18n` と呼ぶように、特別な例でのみ用いいるようにしましょう。

### 同じものを同じであることがわかるように

`rows`という変数を持っていて、それを行データを扱うメソッドに渡したい場合、そのメソッドのパラメータも同様に `rows` という名前にするべきです。同じ種類のデータやインスタンスに対して異なる名前を使う理由がない限り、異なる名前を使わずに同じ名前を使いましょう。

同様に、不要な変数の代入を行わないようにしましょう。`let theData = input` のような代入でわかりやすくなったり、なんらかの目的を果たすことはありません。`input`の型や値が変更され、別の名前を付ける必要がある場合にのみ再代入してください。

### 社内プロジェクトに名前をつけない

原則として、あなたのインフラの名前にスターウォーズに出てくる惑星の名前やロシア語で青や鳥の種類を表す言葉を使用するのではなく、それが何をするのかがわかるように名前をつけてください。社内プロジェクトのための洒落たネーミングは、今ではちょっとした楽しみとなっていますが、その一方で、わかりにく名前であることによって、プロジェクトに新しいメンバーを迎える際の終わりのない煩わしさがあります。メールを送信するマイクロサービスであれば、`mail-service`と呼んで終わりにしましょう。

### 物事が変わったら、名前を変えよう。

ネーミングの趣旨は常に問題です。たとえば `sendMail` と呼ばれてメールを送信していたメソッドが、単にメールのフォーマットだけを行い、実際にメールを送信する別のメソッドに渡すようにリファクタリングされた等です。コードをリファクタリングするときは、メソッドの名前も変更しましょう。

同様に、コードを書いているときも、「それが何をするのか？」と「それが何を呼んでいるのか？」をよく見て、ネーミングの趣旨とそれらが同じであることを確認してください。メソッドが `renderPage` と呼ばれていても、実際にはページをレンダリングするためのデータ構造をセットアップしているだけなら、別の名前にしましょう。

---

ネーミングが強いと、名前に頼ってしまいます。メソッド名を見れば、それが何をしているのか推測できます。まともな名前を選ぶことは有益な時間となります。
GitHubのコミットログ等をWebhookでDiscordに流す

# GitHubとDiscordを連携する方法
自分のための備忘録。ちょくちょく忘れてはググるのでこの際書いて覚えようと思います。
ついでに、自分みたいな忘れちゃう人向けにめっちゃわかりやすい記事を作ろうと思って書いてみました。

## 連携して何ができるの？
早い話、コミットのログや Issue のログなどを表示できる。
細かく言えばこれ以外にも色々表示できる（ブランチ操作だったり・・・）けど、
そこまで表示させる人はいないんじゃね？と思ったのでこの辺は割愛します。

# 連携するには？
最低限必要な権限は「チャンネルの管理」「Webhookの管理」の2つです。
自分が所有していない（他人のサーバーなど）場合は事前に管理者の方などに連絡し、WebhookのURLを渡してもらうなどしてください。
またコミットログがデフォルトでは垂れ流されるので、コミットメッセージには気をつけましょう。
以降の説明は「自分が管理者である」ことと「言語が英語である」ことを前提としています。そうでない場合は適宜いい感じに読み替えたりしてね。

## 連携する方法
### Discord側の操作
* まず連携させたい目的のチャンネルの設定画面を開いてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120587/e658f4fe-774d-2a50-7ef6-cd34154588ee.png)
* 左の「Integrations」をクリックして、<br>
「Webhooks」の横にある「Create Webhook」のボタンを押してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120587/48aba225-4113-40e1-7570-29cd7110d1e1.png)
* ここの「Copy Webhook URL」をクリックし、Webhook URLを控えてください。<br>
名前やアイコンは GitHub によってオーバーライドされるので、特に変える必要はないです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120587/466bbfbc-e9ad-3747-3ab6-70c4d418dbc8.png)

### GitHub側の操作
* 次に GitHub のリポジトリの設定を変更します。まだ作成してない方は作成してください。<br>
  公開状態は何でもいいです。プライベートリポジトリでもちゃんと流せますよ。<br>
  目的のリポジトリを開いたら「Settings」を開き、「Webhooks」をクリックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120587/e88e8669-e0f6-2953-832e-f1427ed1f124.png)
* 横の「Add webhook」を押してください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120587/5e643194-573d-c279-7c5d-c1ec566021e5.png)
* 色々出てきますが、「Payload URL」を一旦打ちましょう。<br>
  先程控えた「Webhook URL」を「Payload URL」の所にうち、**末尾に/github**をつけます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120587/a333233a-a1a5-b003-575f-b62243b0ac64.png)
* あとは「Content type」を「application/json」にして色々打つのは終了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120587/af68a961-72e1-d0ee-65b5-51b9a9e92f81.png)
* 最後に「Which events would you like to trigger this webhook?」を設定して終わりです。<br>
  基本的に「Send me everything」か「Just the push event」で問題ないですが、<br>
  個別で設定したい場合は「Let me select individual events」で設定可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120587/a626d7ca-a398-820c-1a00-c7f4010a4dbf.png)

設定内容は流石に全部紹介するとつらすぎるので DeepL翻訳 に投げるなりしながら色々ポチポチして設定しましょう。
あとは「Active」にチェックが入ってることを確認して「Add webhook」を押せば完了です！お疲れさまでした！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120587/d0b8ba44-4836-4a1b-a670-4fad946f81c5.png)
## 確認
README.md を最初から作成する設定にしてる方はブラウザ上などで「README.md」を開き、編集してコミットするだけでコミットログが流れると思います。
そうでない方は適当なファイル（txtなど）をアップロードするだけでもコミットログが流れます。
↓こんな感じの。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120587/f31c759b-7566-af12-8aa4-fde716f4d4b6.png)

# 最後に
完全備忘録に近いので、読みづらかったり分かりづらかったりしたらごめんなさい。~~他のサイトを見てください~~
こういった、GitHubとDiscordを連携させる方法はウェブサイトではよく見かけるのですが、Qiitaでは見たことがないので書いてみました。
次なにか書くことがあるかどうかはわかりませんが、もし書いてたらぜひそっちも見てね。
迷路を抜けるアルゴリズム

迷路を"S"からスタートし"G"へゴールする様な脱出アルゴリズムの作成

# システム設計
- mapにユーザー定義型[wall][road]を設定
- ゴールまでたどり着くよう、[save][reset]メソッドを作成
- [road]型に通った道を記す[gone]フラグをセット
- [road]型にリセット前に通った道を示す[art_gone]フラグをセット
- 基本処理として、周囲のマスの中で[road]の[gone],[alt_gone]フラグの着いていないマスに移動
- マスを移動する際に2つ以上進んだ事ない道があった際、saveアクションを実行
- saveアクションが実行されると、現在の座標位置を無制限配列?で記録<br>
※セーブスロットが1つだと、「分岐の先の分岐」があった場合詰むため
- ゴールの座標に一番近い座標の値を優先的に移動
- 行き止まりになった際はresetアクションを実行し、一番新しいsaveアクションが実行された座標に戻る
- 以上の処理を繰り返し、ゴール座標へ向かう

# 基本処理
1. 空いているマスへ移動
2. 分岐がある場合はセーブし、ゴールに近いマス(or ランダム)に移動
3. 行き止まりが起きた場合はリセットし、セーブ座標へ戻る
bscscan API で binance smart chain のトランザクションを取得する

https://bscscan.com/

bscscan は BSC(Binance Smart Chain) 用でほぼ etherscan のコピーです :smiley: . アカウント登録で API key 取得してブロックチェーン情報を取得できます(結果は JSON).

1 秒あたり 5 回 per IP の制限がありますが, 普通に使う分には制限にはかからないでしょう.

例えば curl ですと以下のようにして取得できます.

```bash
APIKEY=XXXXXX

# POST の方がいいのかも?
curl -X GET https://api.bscscan.com/api -d "module=account&action=txlist&address=0x000000&startblock=0&endblock=99999999&sort=asc&apikey=${APIKEY}"
```

トランザクション数が多くて 10000 を超える場合は paging で処理することになります(bscscan の API 利用のドキュメント参照)
BigQueryの番号付け関数が返す値の比較

BigQueryの[番号付け関数](https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators#numbering_functions)が返す値の違いが、いつも「あれ、RANKとROW_NUMBERってどう違うんだっけ」ってドキュメント見ることになるので、実際にクエリ書いて整理してみた。

```sql
SELECT 
  value,
  ROW_NUMBER() OVER (ORDER BY value) row_number,
  RANK() OVER (ORDER BY value) rank,
  DENSE_RANK() OVER (ORDER BY value) dense_rank,
  PERCENT_RANK() OVER (ORDER BY value) percent_rank,
  CUME_DIST() OVER (ORDER BY value) cume_dist,
  NTILE(2) OVER (ORDER BY value) ntile2,
  NTILE(5) OVER (ORDER BY value) ntile5,
  NTILE(10) OVER (ORDER BY value) ntile10,
FROM
(
  SELECT 0 AS value
  UNION ALL
  SELECT 1 AS value
  UNION ALL
  SELECT 1 AS value
  UNION ALL
  SELECT 2 AS value
  UNION ALL
  SELECT 3 AS value
  UNION ALL
  SELECT 20 AS value
)
```

| value | row_number | rank | dense_rank | percent_rank | cume_dist | ntile2 | ntile5 | ntile10 |
| --: | --: | --: | --: | --: | --: | --: | --: | --: |
| 0 | 1 | 1 | 1 | 0.0 | 0.16666666666666666 | 1 | 1 | 1 |
| 1 | 2 | 2 | 2 | 0.2 | 0.5 | 1 | 1 | 2 |
| 1 | 3 | 2 | 2 | 0.2 | 0.5 | 1 | 2 | 3 |
| 2 | 4 | 4 | 3 | 0.6 | 0.6666666666666666 | 2 | 3 | 4 |
| 3 | 5 | 5 | 4 | 0.8 | 0.8333333333333334 | 2 | 4 | 5 |
| 20 | 6 | 6 | 5 | 1.0 | 1.0 | 2 | 5 | 6 |

`NTILE`は、指定した数字のバケットに、値を均等に分割する。値自体は見ず、その順序のみでグルーピングして、可能な限り同じ数ずつに分けようとする。
[Pythonデバッグ]Icecreamで変数名と値を一発表示

Pythonでデバッグをしていて、print()で値を出力する時に、それがなんの変数に入ってるものなのか一見してわからないのがちょっと歯痒いなと思ってました。

例:

```python
str1 = "Apple"
str2 = "Orange"

print(str1)
print(str2)
```
出力:

```
Apple
Orange
```

こんな感じで、これがもっとたくさんの変数を扱う場合地味に分かりにくくてノイズ。print()内に変数名をマニュアルに書き込んでもいいのですが、それもあまり効率的じゃないなと思い調べてみたら
[Icecream](https://github.com/gruns/icecream)というライブラリに出会いました。print()をic()に置き換えるだけで変数名と値を一発表示してくれるというスグレモノ。

導入は

```
pip install icecream
```
これだけ。

最初の例を使ってデモしてみます。

```python
from icecream import ic

str1 = "Apple"
str2 = "Orange"

ic(str1)
ic(str2)
```
出力:

```
ic| str1: 'Apple'
ic| str2: 'Orange'
```
一見してどの値がどの変数のものなのかすぐに分かります。これほんと便利。
ぜひ試してみてください。
なぜAWSみたいなクラウドコンピューティングが必要か

AWSについてはあまり知識がまだないが、なぜこのようなクラウドコンピューティングと呼ばれるものが必要かを簡単にまとめておく。

## まず、クラウドコンピューティングとは
クラウドコンピューティングとは、```クラウド```と呼ばれるもの。
例えば、icloudなんかが代表的なもの。

icloudは、写真などのデータを保存しておくことができる。
これは特に物理的ななにかが存在するわけではなく、インターネットを経由してAppleのストレージを使用することができるというもの。

## クラウドを利用するメリット
例えば、スマホに写真や動画を保存しておくとして、このスマホの容量がいっぱいになってしまえば、さらに写真や動画を保存しておくことは不可能。もしくは、データを削除する必要がある。
しかし、クラウドは料金を払えば容量を買うことができる。さらには、物理的なものを所有する必要がないため、もしその容量が不要になれば料金の支払いをやめればいい。

## クラウドが存在しなかったとき
クラウドが存在しなかった時は、例えばフロッピーやUSBのようなものをいちいち購入してその物にデータを移動させる必要があった。保存する物体を購入する必要があった。

## 本当にしたいことは
しかし、ぼくらが本当にしたいことはそのフロッピーやUSBを購入するということではない。大切なデータを保存することだ。この目的を果たすだけなら特にUSBとか買わなくても問題ない。そこで、このクラウドがあれば、大切なデータを保存するストレージ自体を購入し、大切なデータを保存するという目的を果たすことができるという事になる。
新しいAndroidランタイムパーミッションのAPIを使ってみる

Android 6.0で導入されたランタイムパーミッションですが、定型文が多い上に結果の受け取りが`onRequestPermissionsResult`のオーバーライドによる非同期処理となるため非常に使いづらく、ややこしくなりがちでした。昔記事を書いていたのがこちら。
https://qiita.com/niusounds/items/f8852534bd3135d89dcc

そんなランタイムパーミッションのAPIが、`androidx.activity 1.2.0`でより使いやすくシンプルなAPIになりました！

たとえば`startRecordButton`というボタンを押すと端末のマイクから音声入力を録音する処理`startRecording()`が実行されるアプリを作っているとしたら、以下のような感じのコードになります。

```kotlin
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 最初にregisterForActivityResultしておく(onStartまでにやっておく必要があるらしい)
        val requestPermission = registerForActivityResult(ActivityResultContracts.RequestPermission()) { result ->
            // パーミッションリクエストの結果を処理する
            // パーミッションが許可されたらresult=true
            if (result) {
                startRecording()
            }
        }

        startRecordButton.setOnClickListener {
            checkPermission(requestPermission)
        }
    }

    // 引数でrequestPermissionを渡してみたが、フィールドに保持するでも良いと思う
    private fun checkPermission(requestPermission: ActivityResultLauncher<String>) {
        // パーミッション許可済みかどうかの判定。これは今まで通り変わらず。
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED) {
            startRecording()
        } else {
            // ランタイムパーミッションのダイアログを出す
            requestPermission.launch(Manifest.permission.RECORD_AUDIO)
        }
    }
}
```

※丁寧にやるならランタイムパーミッションのダイアログを出す前に`ActivityCompat.shouldShowRequestPermissionRationale`を問い合わせて以前に拒否したことがあるかどうかを調べた方が良いと思います。

`requestCode`を自分で定義したり`onRequestPermissionsResult`をオーバーライドして定型文的なコードを書いたりしなくて良くなったので、だいぶスッキリしました。
なんでもっと早く導入されなかったんですかね〜！
GoogleCodeJamの登録方法

# 概要
GoogleCodeJam2021に登録しようと思ったのですが、登録方法（特に住所）がわかりにくいので書いてみました。

# 登録手順
### 1. GoogleCodeJam2021のページへアクセス  
https://codingcompetitions.withgoogle.com/codejam/schedule  
上部の「RegisterNow」と書いてあるところをクリックし、登録画面へ移りましょう。
（googleアカウントの認証が走るので、アカウントがない人は作りましょう。）

### 2. Create your profileする  
*マークがついているところは入力必須です。
登録後も修正は可能なようです。
氏名住所などの情報はフィクションです。

![profile_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241832/f5fa42e6-6faf-2e5a-2e08-b7e96e63cbec.png)

+ 「ニックネーム」
+ 「国籍」
  
![profile_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241832/0aec2f52-3472-e399-9a27-81397c550738.png)

+ 「名前」  
+ 「苗字」  
+ 「居住国」  
+ 「ジェンダー情報提供の同意（YES/NO）」  
+ 「ジェンダー（YESにした場合のみ表示）」  

![profile_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241832/8d5fd6bb-32bb-4b05-e985-5d809c10d588.png)

+ 「人種/民族情報提供の同意（YES/NO）」  
+ 「人種/民族（YESにした場合のみ表示）」  
+ 「よく使う言語」  
+ 「学生or社会人orその他」  
+ 「会社名」  
+ 「役職」  
+ 「最終学歴」  
+ 「卒業年」  

![profile_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241832/ddb46947-99a6-4cc0-c06b-cb43213c68f3.png)

+ 「ジョブオファーしてもよいか（YES/NO）」
+ 「レジュメのリンク先（YESにした場合のみ表示）」
+ 「LinkedInのアカウント（YESにした場合のみ表示）」
+ 「他のコーディングコンテストのメール通知希望（YES/NO）」
+ 「他のプログラムやイベントのメール通知希望（YES/NO）」

![profile_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241832/a3204551-db64-5fff-7394-003774f15d1b.png)

+ 「商品受け取りを希望するか（YES/NO）」
+ 「受取人の名前（YESにした場合のみ表示）」

（次は商品受け取りYESの場合のみ）
![profile_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241832/7bc5b307-4d9f-bf73-a9b2-da12c3a40567.png)

+ 「会社や学校の住所かどうか確認(YES/NO)」 →　YESの場合は会社名・学校名の記載欄が追加
+ 「丁目・番地、町名」
+ 「建物名・部屋番号」
+ 「都道府県」
+ 「市町村・区」
+ 「郵便番号」
+ 「カントリーコード＝日本は”81”（左） / 電話番号→市外局番から記入し、頭の"0"を取る　（右）」
+ 「Tシャツのサイズ」

入力例は埼玉県庁の住所から拝借（一部変更）しました。
> 埼玉県庁　
> 〒330-9301
> 埼玉県さいたま市浦和区高砂三丁目15番1号 埼玉県庁101号室
> 電話番号：048-824-2111

※ASCII文字以外を使うなと書いてあるのでひらがな、漢字などは使ってはいけません。
　また、電話番号はハイフン不要です。

GCJではRound 2 を突破した1000名に商品としてTシャツが送られます。
このTシャツが目当ての方はここでYESを選択し、受取人と住所の情報を忘れず入力しましょう。

![profile_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241832/0c8583de-0ed8-e067-7153-2c7dfffdba83.png)

+ 「16歳以上であることの確認」
+ 「規約同意の確認」

「Create Profile」ボタンをクリックします。  

### 3. 「2021 Registration」する

![register_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241832/4d3dbf1e-0625-4fb8-683b-82e1300726bd.png)

「About You」→ profileの確認
「Rules」

+ 「コンテストルールの同意」
+ 「コンテスト連絡のメール通知希望(YES/NO)」

「Register」ボタンをクリックします。
以上です。お疲れさまでした。
（何か誤りがあればコメントいただけると助かります。）

# 修正したいとき
サイト右上のアイコン（イニシャルが書いてあるやつ）をクリックし、表示された「ViewProfile」をクリックした先で修正できます。
【GoogleSpreadSheet】怪しげなサイトを回避するために

最近Google検索をしていると、怪しげなサイトやまとめサイト、同じドメインのサイトばかり表示されていて、欲しい情報が中々手に入らないことが多い。

「いくつか好きなサイトを、好きな順番で、好きな数だけ表示できないか。」

この問題をGooleSpreadSheetを用いて解決してみました。

#使い方
![FireShot Capture 1136 - メタ検索エンジン - Google スプレッドシート - docs.google.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120085/3f5189d9-fa97-4e03-78e2-d690be1b3875.png)

E1に検索文字を入力すると、各サイトに関連する見出しが表示されます。
それをクリックすることで、新規タブで該当ページが開きます。

下記URLにて共有していますので、実際に試してみてはいかがでしょうか。
ダウンロード・コピー可能ですが、悪用はしないでください。
https://docs.google.com/spreadsheets/d/1IpPStG2POCiOHxpKrUrroCz-4lpQo6T8j28qaFutYas/edit?usp=sharing

#作り方
![FireShot Capture 1139 - メタ検索エンジン - Google スプレッドシート - docs.google.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120085/dc36d130-017e-210d-8e14-a0b95e4a8fb7.png)
エクセル!sheetについて解説します。

A2
=ENCODEURL(E1)

B2:B12
="https://search.yahoo.co.jp/search?p="&A$2&"+site%3A"&D2

D2では、気になるサイトのドメイン名を入力します。
www.example.com

E2:E12
=IFERROR(HYPERLINK(G4,H4))

![FireShot Capture 1142 - メタ検索エンジン - Google スプレッドシート - docs.google.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1120085/2de5024c-9fa5-af7f-bc23-c1bd64dd4129.png)

G2
=IMPORTXML(B2,"//a/@href")

H4
=IF(G4="","",CLEAN(IMPORTXML(G4,"//title")))

これらの列名を変えながら、I・J列、K・L列と横に延長します。

#まとめ
まとめサイトなど、自分の嫌いなサイトを検索結果に表示させないためにいくつかの方法があります。

・Googleが提供している[Programmable Search(プログラム可能な検索エンジン)](https://programmablesearchengine.google.com/intl/ja_jp/about/)については、無料版だとドメインを10個しか登録できず、無理があった。

・[クリーンサーチ](https://burnworks.com/docs/clean-search/#gsc.tab=0)や[ノイズレスサーチ](http://pasokatu.com/nsearch#gsc.tab=0)を使うことで、一部のサイトを非表示にできる。しかし、ブラックリストやホワイトリストを自分で変えることはできない。

・uBlacklistを用いることで、Googleの検索結果から嫌なサイトを消すことができるが、消しても消してもすぐに新たな偽サイト等は出現するため無理があった。

・ublockoriginを用いることで、ビジターアンケートなど怪しげなサイトにアクセスすることを防いだり、検索結果から嫌なサイトを消すことができるが、これも限界があった。

このスプレッドシートを用いれば、好きなサイトの検索結果のみが表示され、好きな順番に並び替えることもできます。とはいえ、これを使うと新しいサイトに出会うことは少なくなるかもしれません。しかし、「ググれ〇ス」から「ググっても〇ス」と言われるようになった現在ではこちらを使うべきだと考えています。

#改善すべき点
・見出しの後ろに邪魔なものがくっついてくる。(右から最初の空白とその一字前までを抜き出せばいいのでは)
・PDFだと見出しが表示されない("-pdf"をB2:B12に追加する)
・見出しが文字化けするサイトがある(そのようなサイトは諦める or 我慢する)
・site:の条件なしで検索した時に出てくる、好きなサイト以外の見出しも下の方に表示されたほうがいい

初投稿ということもあり、わかりにくかったかもしれません。
その他ご意見・ご感想お待ちしております。


#注意点
・スクレイピングのやりすぎはサーバーに負荷をかけてしまいます。
・著作権の問題等もありますので、実行する前にサイトの利用規約等を確認してください。

#参考にしたサイト
https://blog.members.co.jp/article/37782#how-to-scrape
Rubyで早期リターンを使用してif文のネストから解放される方法（備忘録）

## はじめに
if文が何重にもネストされると頭がこんがらがる闇から解放されるべく
早期リターン（early return）という手法を使用する。

＊ 初学者のただの備忘録です。

## 環境
- Ruby: 2.6.6
- Rails: 6.0.3.5

## 簡単な例
コードにあまり意味はありません。

```ruby
  def example(hoge)
    if hoge.nil? # hogeがnilならtrue、nilでなければfalseを返す
      true
    else
      false
    end
  end
```

これを早期リターンで書くと

```ruby
  def example(hoge)
    return true if hoge.nil? # returnが必須
    false
  end
```
こう書けます。

## 実装
先ほどは簡単なif文でしたが少しだけややこしくなると

```ruby
  def self.existing_ideas(category_name)
    if category_name.present?
      category = Category.find_by(name: category_name)
      if category.present? 
        category.ideas
      else
        []
      end
    else
      Idea.all
    end
  end
```

まずは内側から早期リターン

```ruby
  .
  . 
  .
  if category.present? 
    category.ideas
  else
    []
  end
```

```ruby
  # def self.existing_ideas(category_name)
  #   if category_name.present?
  #     category = Category.find_by(name: category_name)
        return category.ideas if category.present?
        []
  #   else
  #     Idea.all
  #   end
  # end
```

さらに早期リターンで完成
ifをunlessに変更して実装

```ruby
  # def self.existing_ideas(category_name)
      return Idea.all unless category_name.present?
      category = Category.find_by(name: category_name)
      return category.ideas if category.present?
      []
  # end
```

## ちなみに
私はunlessがあまり好きじゃないのでpresent?をblank?に変更して
ifのまま実装

```ruby
  def self.existing_ideas(category_name)
    return Idea.all if category_name.blank?
    category = Category.find_by(name: category_name)
    return category.ideas if category.present?
    []
  end
```
以上。
間違い等があればご指摘頂けると嬉しいです。
railsを使ってアプリを作る2

####テーブルの作成と操作方法

#####DBにテーブルを作成する時は2つの手順がある
1, データベースに変更を支持するファイルを作成する　(マイグレーションファイルの作成)
　rails g model Animal name:text
　Animalsテーブルを作成して、text型のnameカラムを作成
　rails g model　コマンドを実行すると、Animalsテーブルを操作する為のAnimalモデルも一緒に作成してくれる

2, 作成したファイルを読み込んでDBに反映する
　rails db:migrate

#####作ったテーブルにデータを作成する
1, newメソッドでAnimalモデルのインスタンスを作成
　animal = Animal.new(name: "らいおん")
　
2, animalsテーブルに保存
　animal.save

#####テーブルからデータの取り出し方
全部の取得　　Animal.all　対象がない時は空の配列が返ってくる
最初の1件　　Animal.first　対象がない時はnilが返ってくる
pkのみ取得　　Animal.find(1,2,3)　対象がない場合はエラーが返ってくる
指定したカラムで最初の1件　　Animal.find_by(id: 1)　対象がない時はnilが返ってくる　なんでもいける
指定した条件全部　　Animal.where(name: "inu")　対象がない時は空の配列が返ってくる
#####注意
マイグレーションファイルを作成した場合に、DBに反映させないと、画面アクセスした際に必ずエラーがおきる
ApplicationRecordを継承したクラスをモデルと言う
テーブル名は複数形でモデル名は単数形
自動でRemote WSLするVScodeのworkspaceを設定する

# 概要
VSCodeでWSLを扱うときに、ステータスバーから接続したりWSLから ```code .```を実行するのは面倒ですので、workspaceファイルでVSCodeを開いてその時にWSLにも自動で接続するようにしました。

# 対象
- Windows10以上
- Visual Studio Code 1.53.2 以上をインストール済
- WSL or WSL2 設定済み
- VScode内でRemote-WSLの拡張機能をインストール済

# コード
.code-workspaceファイルの中身にこれを書いたら終わりです。

```json
{
    "remoteAuthority": "wsl+ubuntu" ,

    "settings": {
        "terminal.integrated.cwd": "/home/username/aaa",
    },

    "folders": [
        {
            "path": "C:/Users/username/aaa/bbb"
        },
        {
            "path": "C:/Users/username/aaa/ddd"
        }
    ],
}
```

# 解説

### remoteAuthority

```json
"remoteAuthority": "wsl+ubuntu"
```

これを書くことでワークスペースを開いたときに自動で接続するようになります。```ubuntu```の部分はWSL用のディストリビューションを記述します。Debianの人は```wsl+debian```です。

### terminal.integrated.cwd

```
"terminal.integrated.cwd": "/home/username/aaa"
```

規定のターミナルを```settings``` 内で設定します。パスの部分はなんでも問題ないですが、WSLのルートから記述するようにしてください。Windowsの方で書くとエラーが発生する可能性があります。

### folders

```
    "folders": [
        {
            "path": "C:/Users/username/aaa/bbb"
        }
    ],

```
ワークスペース内で開きたいフォルダを好きなだけ書いてください。こちらは反対に，Windowsのルートからパスを書きます。

#おわりに
昔見たremoteAuthorityの記事が全然見つからなかったので備忘録として書きました。
その他いい方法や間違いがあればコメントでよろしくお願いします。
JAVA自習忘備録_2020/02/17

【概要】JAVAについて自分で勉強したことを残す
ド基礎知識なのであしからず


◆コードの基礎構造

```
//クラスブロック（classのあとクラス名を記載する）
　public class Main {
//メソッドブロック（ここに命令や指示を記載する）
	public static void main(String[] args) {
	}
}　
```

・行の終わりには「;」をつける
・コード内のコメント（指示や命令以外のテキスト）
　・１行のみのコメント：文頭に「//」をつける
　・複数行コメント：文頭に「/*」、文末に「*/」…


◆変数
　・変数宣言と代入

```
//hensuという名前で変数を作成する
inｔ hensu;

//変数「hensu」に１０を代入する
hensu = 10; 
```


・変数の初期化
　→変数宣言と代入をセットにしたもの

```
int hensu = 20;
```

◆テキスト表示
・「あああ」（テキストのベタ書き）を表示する場合

```
System.out.println("あああ");
```

・変数「hensu」の値を表示する場合

```
System.out.println(hensu);
```

・テキストのベタ書きと変数の値のミックス表示

```
System.out.println("あああ" + hensu);
```

→上記の場合、「あああ（変数「hensu」の値）」と表示される
Pythonのinput関数で再入力を促す方法

##if〜elseとwhileで書いてみた
###if〜elseの場合
Input関数を使って外部入力を設定した際に、数値以外が入力された場合に再入力を促す方法です。if〜elseを使い数値以外の場合はreturnで関数を再実行するやり方にしてみました。Trueの時はreturnで入力値が戻るので再利用が可能です。

以下、環境です。
__OS : macOSX Catalina 10.15.7__
__Python : python3.9.1 (pyenv）__


```py
#例：予算入力時に数字だけを入力してもらう （budget_input.py）

def budget_input():                                 #関数budget_inputを定義
    figure = input('ご予算を入力してください : ')   #input関数で入力された値を変数figureに代入
    if figure.isdigit():                            #.isdigit()で数値かどうかを判定
        budget = int(figure)                        #True(真):の場合は、intで数値化
        return budget                               #budget値を数値として返す (後で計算に使える）
    else:
        print('整数値を入力してください : ')        #False(偽)の場合は、再入力を促す表示をする実行し
        return budget_input()                       #関数budget_inputを再実行する。

input_budget = budget_input()
add_bounas_budget = input_budget * 1.05
print('入力されたご予算は' + str(input_budget) + 'になります。')
print('ポイントがご予算の5%分加算され合計で' + str(round(add_bounas_budget)) + '円になります。')
```

```
#出力結果：10000と入力

ご予算をご入力してください :10000
入力されたご予算は10000円になります。
ポイントがご予算の5%分加算され合計で10500円になります。
```

###whileの場合


```py
def budget_input():                                    #関数budget_inputを定義
    while True:                                        #Trueの間は繰り返す
        number = input('ご予算をご入力してください:')  #input関数で入力された値を変数numberに代入
        if number.isdigit():                           #.isdigit()で数値かどうかを判定
            budget = int(number)                       #True(真):の場合は、intで数値
            return budget                              #budget値を数値として返す (後で計算に使える）
            break                                      #breadkでループを終了
        print('整数値を入力して下さい。')


input_budget = budget_input()
add_bounas_budget = input_budget * 1.05
print('入力されたご予算は' + str(input_budget) + '円になります。')
print('ポイントがご予算の5%分加算され合計で' + str(round(add_bounas_budget)) + '円になります。')
```

```
#結果はif〜elseと同じ
#出力結果：10000と入力

ご予算をご入力してください :10000
入力されたご予算は10000円になります。
ポイントがご予算の5%分加算され合計で10500円になります。
```
GCPの備忘録

# 備忘録
いろいろ触ってると「あれどうやんだっけ」って毎回なって中々定着しないので、覚えがてらいろいろな備忘録

## VMインスタンス
Always Freeの条件

- リージョン  
us-central1, us-west1, us-east1
- マシンタイプ  
f1-micro
- ブートディスク  
標準永続ディスク/３０GBまで
- イメージ  
RHEL以外選んでおけば良さそう

### SSH接続
・gcp認証
```% gcloud auth login```
・SSH接続
```% gcloud compute ssh インスタンス名 --ssh-flag="-p ポート番号"```
・SSHログイン
```% ssh 外部IP -i ~/.ssh/key -p 変更後ポート番号```
・secureログ確認
```% sudo cat /var/log/secure```
・CentOSの場合はFWサービス停止

```
$ sudo systemctl disable firewalld
$ sudo vi /etc/selinux/config
一旦インスタンス再起動
```

・sshd.confの設定（バックアップ/ポート番号を変更）
```% sudo sed -i -e "s/#Port 22/Port 変更後ポート番号/g" /etc/ssh/sshd_config```
・sshd再起動
```% sudo systemctl restart sshd```
### ファイアーウォール設定
インスタンスの「ネットワーク詳細の表示」
 → ファイアーウォール ルール
　→ default-allow-ssh
macOS AWS CLI v2アンインストール

AWS CLI v2だと、以下のコマンドがエラーになる。

```
$ aws ecr get-login
```

既存のシステムで上記を含むシェルスクリプトがエラーになった。
それを直す時間などないので、aws-cliをバージョンダウンした。

[AWS CLI バージョン 2 のアンインストール](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-cliv2-mac.html#cliv2-mac-remove)

[macOS での AWS CLI バージョン 1 のインストール、更新、アンインストール](https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/install-macos.html)

```
curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip"
unzip awscli-bundle.zip
sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws
```

#### バージョンの確認

```
% aws --version
aws-cli/1.19.9 Python/2.7.16 Darwin/20.3.0 botocore/1.20.9
```

よしよし、ちゃんと1.19.9になってる。

#### aws ecr get-loginの動作確認

```
% aws ecr get-login
docker login -u AWS -p ey(略) -e none \
https://nnnnnnnnnnnn.dkr.ecr.ap-northeast-1.amazonaws.com
```

OK！
[JavaScript] turbolinksって実際どうなの？

今回は、必要か必要じゃないのかよく分からない
**turbolinks**について備忘録を書いていきます。


##turbolinksとは
画面遷移を高速化させるライブラリ(Gem)
「rails new」した時点から、標準で組み込まれている。
これが原因で、**Javascript**が正常に機能しなくなる場合がある。

デフォルトで組み込まれているライブラリなので、仕様は必ず把握する必要がある。

通常であればページが読み込まれたタイミングで、
**load イベント**が発生しますが、**turbolinks** によって画面が切り替わった場合は load イベントは発生しません。
その為、ページ読み込みを起点とした**Javascriptは機能しなくなります**。
　　
　
なので実際に
turbolinksに関する記述をコメントアウトしていきます。


```app/javascript/packs/application.js
// 省略
require("@rails/ujs").start()
// require("turbolinks").start() 　　// ここをコメントアウトする
require("@rails/activestorage").start()
require("channels")
// 省略
```
　　
多くの場合はこのようにコメントアウトや削除をする方がベターみたいです。
いつどこで役に立つかは、まだ恩恵を受けていないので分かりません。
クラス内における同一クラスに対するクラスメソッドのクラス省略について（備忘録）

## 概要
初学者のただの備忘録です。

## 環境
- Ruby: 2.6.6
- Rails: 6.0.3.5

## 実装
例えば下記のようなCategoryクラスでhogehogeというクラスメソッドを定義する。

```ruby:app/models/category.rb
class Category < ApplicationRecord
  
  def self.hogehoge
    category = Category.find_by(name: category_name)
    return category.ideas if category.present?
    []
  end

end
```
hogehogeメソッドの1行目Category.find_by(name: category_name)は
Categoryというクラス（class Category < ApplicationRecord）で
hogehogeメソッドの中で
Categoryクラスに対してfind_by(name: category_name)している。
だからクラスを省略できる。

```ruby
category = Category.find_by(name: category_name)
```
```ruby
category = find_by(name: category_name)
```
【Rails】 not a git repository (or any of the parent directories): .gitと怒られた話

はじめに
---
今回は学習中にとあるアプリケーションをgit cloneしたときの話です。
Rubyのバージョンを確認して、rails db:migrateでマイグレーションを実行をし、サーバーの起動を確認して、よし作業に取り組むぞ！と試みました。
ある程度作業が進んだので、一度GitHubに記録をしておこうと思い、git add .コマンドを入力すると、「fatal: not a git repository (or any of the parent directories): .git」と怒られてしまいます。

開発環境
---
ruby 2.6.3
Rails 5.2.4.4

エラーの内容と解決
---
そもそも今回の原因は、単純に初期のセットアップ不足が原因でした。
ネットで調べると、gitのディレクトリができていない時に出るエラーのようです。

```terminal.
$ git add .
fatal: not a git repository (or any of the parent directories): .git
```
つまり、Gitのリポジトリを作成すれば解決に至ります。


```terminal.
# 草を生やす設定を行う
$ git config --global user.name "アカウント名"
$ git config --global user.email "登録メールアドレス"

# gitのリポジトリを作成する
$ git init.
Initialized empty Git repository in /home/ec2-user/environment/アプリケーション名/.git/

# ブランチ名が表示されるように変わる
$ (master)
```


こちらで、「git add .」コマンドを受け付けてもらえるようになりました。
こんな初歩的なミスをしてしまい、お恥ずかしい限りです...

終わり
---
今回は以上になります。
私自身もプログラミング初心者ですが、同じ様な立場の方に少しでも参考になれば幸いです。
また、もし内容に誤りなどがございましたら、ご指摘いただけますと幸いです。
adbからスクショを連続で撮ったときに、画像ファイル名に自動で通し番号をつける

adbからスクショを連続で撮ったときに、画像ファイル名に自動で通し番号をつけるコマンド。

```sh
# 準備　最初に一回だけ実行
i=1

# スクショ撮影 & 取得
adb shell screencap -p /sdcard/screen_$i.png && adb pull /sdcard/screen_$i.png && i=$(($i+1))
```


## 参考
adb 経由の screenshot の取り方
https://qiita.com/TNaruto/items/b2407f5668e15e42bedd
JDK8アプリケーションでの確認/Amazon S3 and Amazon CloudFront migrating service certificates to Amazon Trust Services starting March 23rd 2021.

以下のようなメールが届きました｡近くなってきたので放置せずに対応します｡

[![Image from Gyazo](https://i.gyazo.com/630cd7d1dd75a49d020fb43e7d201f66.png)](https://gyazo.com/630cd7d1dd75a49d020fb43e7d201f66)

---

メール内に掲載されている[リンク先でAWSブログ記事](https://aws.amazon.com/jp/blogs/security/how-to-prepare-for-aws-move-to-its-own-certificate-authority/)を読むと､

全般的に｢よっぽど古くなければ大丈夫!!｣というニュアンス｡
弊社のアプリケーション（Java）はEC2上で動作しているので｢AWSがサポートしているはずだから､最新化してれば､たぶん大丈夫かなぁ｡｣と思いつつも｢対応を要するかどうかはアプリケーション次第｣とも書かれていてモヤモヤ｡

以下の証明書が全て揃っていればよい模様なので､とりあえずJavaの証明書ストアにインストールされているかをチェックしました｡

## 必要なルート証明書

* CN=Amazon Root CA 1,O=Amazon,C=US
* CN=Amazon Root CA 2,O=Amazon,C=US
* CN=Amazon Root CA 3,O=Amazon,C=US
* CN=Amazon Root CA 4,O=Amazon,C=US
* CN=Starfield Services Root Certificate Authority – G2,O=Starfield Technologies\, Inc.,L=Scottsdale,ST=Arizona,C=US

[※各証明書はAWSのリポジトリ](https://www.amazontrust.com/repository/)からダウンロードできます｡

## 確認結果

Amazon Linux（2ではない）のJava8（update 272）ではCA3とCA4がありませんでした｡涙 （Amazon Linux 2には存在してました｡）

```bash
# keytool -list -v -storepass changeit -keystore $JAVA_HOME/jre/lib/security/cacerts | grep amazon
別名: extra-amazonrootca1
別名: extra-amazonrootca2
★ca3がない!★
★ca4がない!★

# keytool -list -v -storepass changeit -keystore $JAVA_HOME/jre/lib/security/cacerts | grep starfield
別名: extra-starfieldrootcag2
別名: extra-starfieldservicesrootcag2
別名: starfieldclass2ca
```

> If your tests of any of the Test URLs failed, you must update your trust store.
> テストURLのいずれかのテストが失敗した場合は、トラストストアを更新する必要があります。

と書いてあるので､以下のスクリプトを使って追加しました｡

[lhriley/aws-cert-import.sh さんのスクリプト](https://gist.github.com/lhriley/d19ed92d1f09f7ae8edebab9eaf29ec3)を改良（=KEYSTOREを変数にしただけ）させていただきました｡感謝(thank you very much)｡

```bash:import_amazonca.sh
#!/bin/bash -e

# create a temp dir in which to work
OLDDIR="$PWD"
TMPDIR="/tmp/_aws-ca"
KEYSTORE=$JAVA_HOME/jre/lib/security/cacerts

mkdir "${TMPDIR}" && cd "${TMPDIR}"

AWS_CERTS=(
AmazonRootCA1.pem
AmazonRootCA2.pem
AmazonRootCA3.pem
AmazonRootCA4.pem
)

# download the bundle(s)
for cert in ${AWS_CERTS[*]}; do
    wget "https://www.amazontrust.com/repository/${cert}" -O "${TMPDIR}/${cert}"

    # extract a human-readable alias from the cert
    ALIAS=$(openssl x509 -noout -text -in "${TMPDIR}/${cert}" |
        perl -ne 'next unless /Subject:/; s/.*CN=//; print')
    echo "importing $ALIAS"
    # import the cert into the default java keystore
    keytool -import -keystore $KEYSTORE -storepass changeit -noprompt -alias "$ALIAS" -file "${TMPDIR}/${cert}"
done

# back out of the temp dir and delete the temp folder
cd "$OLDDIR"
rm -r "${TMPDIR}"

# list the imported certs as a sanity check
keytool -list -keystore $KEYSTORE -storepass changeit -noprompt | grep -i Amazon
```
App ExtensionのBundle Versionを自動でアプリのBundle Versionに合わせる

## はじめに
iOSアプリのアップデートをリリースするときにバージョン番号を更新しますが、
App Extensionがある場合はそちらのバージョンも更新する必要があるかと思います。

App Extensionのバージョン更新は、忘れがちなので自動で更新するようにしてみます。

## やること
メインのターゲットのアプリバージョンを更新して、ビルドすると
App Extensionのバージョンと設定アプリに表示するバージョンが更新されるようスクリプトを追加します。

※ 設定アプリに情報を表示するのに、[Settings Bundle](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/UserDefaults/Preferences/Preferences.html)を使用しますが、
　本記事ではSettings Bundleの詳細は割愛します（>人<）

## Run Scriptを作成

今回のサンプルプロジェクトの構成は、以下のとおりです。
![dir.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103695/a01a8fe1-6569-c46b-0aec-b4941af3ae18.png)

1. Build PhasesにRun Script Phaseを追加します。
![AddScript.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103695/69f78955-474c-fbd7-f32a-d03616f93b3d.png)

2. 追加した「Run Script」を分かりやすい名前にリネームします。
今回は、「Refresh App Versions」とします。
![Rename.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103695/4ea87068-0e4e-2eb1-2dde-2cbde0ca48cf.png)

3. スクリプトを書いていきます。
Run Scriptを開いて、スクリプトを書いていきます。
![WriteScript.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/103695/a76e0346-7cc7-4ae9-bca2-73467f339b78.png)

今回追加するスクリプトは以下のとおりです。

```shell

APP_VERSION=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" $PRODUCT_SETTINGS_PATH)
/usr/libexec/PlistBuddy -c "Set :PreferenceSpecifiers:1:DefaultValue ${APP_VERSION}" "${PRODUCT_NAME}/Resources/Settings.bundle/Root.plist"
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${APP_VERSION}" "${SRCROOT}/DemoAppNotificationServiceExtension/Info.plist"
```

これで、ビルドをするとInfo.plistのCFBundleShortVersionStringの値が設定アプリと、App ExtensionのInfo.plistに書き込まれるようになります \（^ω^）/

※ 2, 3行目には、出力先のplistのPATHを含んでいるので、プロジェクトの構成ごとに調整が必要ですm(_ _)m

## スクリプトの内容について
スクリプトの内容について簡単に説明します。

```
APP_VERSION=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" $PRODUCT_SETTINGS_PATH)
```
1行目は、Info.plistのCFBundleShortVersionStringの値を変数`APP_VERSION`に保持しています。
　`PlistBuddy`はplistを操作するプログラムです。（Macにデフォルトで入っています！）
　`$PRODUCT_SETTINGS_PATH` はplistのPATHを示す環境変数です。

```
/usr/libexec/PlistBuddy -c "Set :PreferenceSpecifiers:1:DefaultValue ${APP_VERSION}" "${PRODUCT_NAME}/Resources/Settings.bundle/Root.plist"
```
2行目は、`APP_VERSION`の値を`Settings.bundle/Root.plist`の
`PreferenceSpecifiers`の1番目のdictionaryの`DefaultValue`キーの値に書き込んでいます。
（PreferenceSpecifiersはdictionaryのarrayです。arrayなので、indexは0始まりです。）

```
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString ${APP_VERSION}" "${SRCROOT}/DemoAppNotificationServiceExtension/Info.plist"
```
3行目は、2行目とほとんど同じです。
書き込み先がApp ExtensionのInfo.plistに変わっているだけです（｀・ω・´）

## さいごに
メインのターゲットのアプリバージョンを更新してビルドしたら、
App Extensionのバージョンと設定アプリに表示するバージョンが更新されるようになりました！

App Extensionが複数ある場合でも、同様にスクリプトの行を追加していけばまとめて更新できます！
【Laravel】カラム型の変更

#はじめに
知識を整理するための個人的な備忘録です。

一度作成したカラム型の変更方法をまとめます。

#方法
まず`Laravel6.x`では、以降のカラムタイプのみ変更可能。
>bigInteger、binary、boolean、date、dateTime、dateTimeTz、decimal、integer、json、longText、mediumText、smallInteger、string、text、time、unsignedBigInteger、unsignedInteger and unsignedSmallInteger

###1.ライブラリのインストール
下記のコマンドで`doctrine/dbal`をインストール

```
$ composer require doctrine/dbal
```
###2.マイグレーションファイルの作成
ファイル名はなんでも良いが、何の内容の`migration`ファイルが実行されたかがわかる名前をつけるのが良い。

```
$ php artisan make:migration change_対象のカラム名_変更前カラム型_to_変更後カラム型_on_テーブル名_table --table=テーブル名
```

###3.マイグレーションファイルに追記
```php:database/migrations/YYYY_MM_DD_XXXXXX_change_カラム名_変更前カラム型_to_変更後カラム型_on_テーブル名_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class Changeカラム名変更前カラム型To変更後カラム型Onテーブル名Table extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('テーブル名', function (Blueprint $table) {
            //下記を追記
            $table->変更後カラム型('カラム名')->change();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('テーブル名', function (Blueprint $table) {
            //下記を追記
            $table->変更前カラム型('カラム名')->change();
        });
    }
}
```

###4.マイグレート
```
$ php artisan migrate
```
ここでエラーが出なければ良いが、以下のエラーが出た時の[解決方法](https://qiita.com/strrr/items/c5d2c50f27c73e6af601)

```
Symfony\Component\Debug\Exception\FatalThrowableError  : Class 'Doctrine\DBAL\Driver\PDOMySql\Driver' not found
```


#参考
*[Laravel 6.x データベース：マイグレーション](https://readouble.com/laravel/6.x/ja/migrations.html)
in-placeアルゴリズムとは何か調査してみた【例あり】

[Rotate Image - LeetCode](https://leetcode.com/problems/rotate-image/)を解いていたところ，**in-place** という馴染みのない単語が出てきたため調査してみた．

# in-place とは？
この問題は，

***入力***：n×nの二次元行列
***出力***：入力の行列を時計回りに90度回転したもの

というもの．

問題文にある **in-place** の説明は以下通りである：

> you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation.

つまり，追加のメモリを（ほとんど）使用せずに，入力を置換や入れ替えによって直接上書きすることで所望の結果を得るアルゴリズムのことであるといえる．

逆に， in-place でないアルゴリズムは **not-in-place** や out-of-place と呼ばれる．

# in-place と not-in-place アルゴリズムの比較

今回は，次の問題を例に in-place アルゴリズムと not-in-place アルゴリズムを比較する．

## 問題
> n個の要素を持つ配列の要素を逆順にした配列を返せ．
> ex) input : [1,2,3,4,5] → output : [5,4,3,2,1]

### not-in-place

```python
def reverse(array):
    n = len(array)
    res = [0] * n # 戻り値用に領域を追加

    for i in range(n):
        res[i] = array[(n-1) - i]

    return res
```

### in-place

```python
def reverse(array):
    n = len(array)

    for i in range(n//2):
        # swap（入力に直接上書き）
        array[i], array[n-1-i] = array[n-1-i], array[i]
```

## 考察
上記二つのアルゴリズムを見比べると，not-in-place は$O(n)$の配列`res`を**新たに割り当て**ているのに対して，in-place は新たに配列を割り当てていないことが分かる．

このことから，入力の情報を保持したい場合は入力を直接操作する in-place ではなく， not-in-place アルゴリズムを採用するべきであることが分かる．

また，in-place であることはメモリの制限が厳しかった時代において重要な性質であったが，近年では計算機の性能が上がっているため in-place 性の重要度は比較的低くなっていると推測される．

### 参考

- [Rotate Image - LeetCode](https://leetcode.com/problems/rotate-image/)
- [In-place Algorithm - Wikipedia](https://en.wikipedia.org/wiki/In-place_algorithm)

※こちらの記事は[私のブログ記事](https://kotaikehara.com/post/41ae5966b024f03602a1)の複製となっております。
※補足や間違いなどがありましたらコメント等でご指摘お願いいたします．
Macでatコマンドが実行できないときの対処法

`at` はデフォルトで無効になっており、有効にするためには `atrun` を起動し、フルディスクアクセスを許可する必要があります。

※ 現在は `launchd` が推奨されています。

## 1. atrun の起動

```sh
sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.atrun.plist
```

Mac を再起動すると再び無効になるので注意。

※ `com.apple.atrun.plist` の `Disabled` を `Enabled` にすれば起動時に自動でロードされるが、SIP (System Integrity Protection) で保護されているため、復元ユーティリティから `csrutil disable` が必要。

## 2. フルディスクアクセスの許可

`/usr/libexec/atrun` にシステム環境設定からフルディスクアクセスを許可します。

1. セキュリティとプライバシー　＞　プライバシー　＞　フルディスクアクセス
2. `+` ボタンをクリック
3. `Command + Shift + G` を押す
4. `/usr/libexec/atrun` と入力してEnter
5. `atrun` が選択されているのを確認してEnter

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/155587/58673de6-68c8-af7e-aee1-089231dcef02.png)

# 参考
`man at`
`man atrun`
https://unix.stackexchange.com/questions/478823/making-at-work-on-macos/478840#478840
https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/ScheduledJobs.html
for てごし

```seis.py
import obspy
from obspy import read
from matplotlib import pylab as plt
import numpy as np
import function

samplerate = 100 #サンプリング周波数

st = read('1912011200_N.MMOH_U.s', debug_headers=True)#このファイルはメールで送るます
data = st[0].data

#tは時間方向での離散値．stの長さをサンプルレートで切っているので，サンプル数分の配列になってます．
t = np.arange(0, len(data))/samplerate

plt.plot(t,data)
plt.show()
```
Next.jsでデバッグ実行をしてみた

自分用のメモです。
Next.jsを現在、勉強中で「デバッグってどうやるんだっけ？」という疑問から調べた内容をまとめてみました。
## エディタ
- VSCode

### 参考記事
[Next.js公式](https://nextjs.org/docs/advanced-features/debugging#step-1-start-nextjs-in-debug-mode)
[LCL Engineers' Blog](https://techblog.lclco.com/entry/2020/12/23/000000)

##１. Next.jsをデバッグモードで起動する
Next.jsを起動する前に、package.jsonのdevコマンドを書き換えます。

```json:package.json
"dev": "NODE_OPTIONS='--inspect' next dev"
```
設定後、Next.jsを起動するとログが出力されます。

```ログ:ターミナル
Debugger listening on ws://127.0.0.1:9229/5802dfbe-fc8a-4a3f-9643-97d8ff316ba7
For help, see: https://nodejs.org/en/docs/inspector
Browserslist: caniuse-lite is outdated. Please run:
npx browserslist@latest --update-db
ready - started server on http://localhost:3000
```

##２. VSCodeの設定をする
launch.jsonがない場合は、作成してください。

```CLI:ターミナル
$ mkdir .vscode
$ touch .vscode/launch.json
```

launch.jsonを次のようにします。

```json:launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
      "type": "node",
      "request": "attach",
      "name": "Launch Program",
      "skipFiles": ["<node_internals>/**"],
      "port": 9229
    }
  ]
}
```

##3. デバッグを行う
Next.jsを起動後、F5でデバッグ実行する。
ブレークポイントに設定した箇所で処理がストップする。
![debug.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68378/656068ce-6e9a-2133-4506-b4174dfd86d0.png)
HTMLタグリスト



＜!DOCTYPE＞ …… HTMLのバージョンを指定する
＜html＞ …… HTMLページであることを宣言する
＜head＞ …… ページ全体に関する設定情報を記述する
＜meta＞ …… headタグ内に記述し、ページの各種設定内容を指定する
＜title＞ …… headタグ内に記述し、ページのタイトルを指定する
＜link＞ …… headタグ内に記述し、スタイルシートなどの外部リソースを参照する
＜base＞ …… headタグ内に記述し、基準となるベースURLを指定する
＜style＞ …… スタイルを指定する

＜body＞ …… 実際にブラウザ上に表示されるページコンテンツを記述する
＜script＞ …… スクリプトを記述する
＜no script＞ …… scriptタグに対応していない、ブラウザに対する代替メッセージを記述する
＜!-- --!＞ …… HTMLソース上にコメントを記述する
＜main＞ …… ページ内にメインコンテンツエリアを設置する
＜section＞ …… ページ内にセクションエリアを設置する 
＜header＞ …… セクションのヘッダーエリアを設置する
＜footer＞ …… セクションのフッターエリアを設置する
＜nav＞ …… ページ内にナビゲーションエリアを設置する
＜div＞ …… ページ内に汎用的なブロックエリアを設置する
＜span＞ …… ページ内に汎用的なインラインエリアを設置する
＜canvas＞ …… ページ内に、図形を描画するキャンバスエリアを設置する
＜p＞ …… 段落を設置する
＜a＞ …… リンクを設置する
＜img＞ …… 画像を設置する
＜br＞ …… 改行を設置する
＜h＞ …… 見出しを設置する（h1〜h6の６段階で指定できる）
＜hgroup＞ …… 複数の見出しタグをグルーピングする
＜hr＞ …… 水平罫線を設置する
＜pre＞ …… 整形済みテキストを設置する
＜audio＞ …… オーディオファイルを設置する
＜video＞ …… ビデオファイルを設置する
＜picture＞ …… レスポンシブイメージを設置する
＜source＞ …… audio、video、pictureにて複数ソースを指定
＜track＞ …… video、audioタグにてトラック情報を指定する
＜embed＞ …… プラグインを必要とする音声や動画などを設置する
＜object＞ …… さまざまな形式のデータを埋め込むための汎用的なタグ
＜param＞ …… objectタグに渡すパラメーターを指定する
＜ifram＞ …… ページ内に別のページを埋め込む
＜figure＞ …… 写真や図表のエリアを設置する
＜article＞ …… 記事コンテンツを設置する
＜aside＞ …… 補足情報を設置する
＜template＞ …… スクリプトで使用するテンプレートを定義する
＜slot＞ …… テンプレート内でslot（枠）を定義する
＜data＞ …… コンピューターが判読可能なデータを指定する
＜meter＞ …… メーターを表示する
＜output＞ …… 計算結果を表示する
＜time＞ …… 日時を表示する
＜ol＞ …… 順序付きのリストを設置する
＜ul＞ …… 順序なしのリストを設置する
＜menu＞ …… メニューリストを設置する
＜li＞ …… ol、ul、menuタグ内に設置され、１つ１つのアイテムを設置する
＜dl＞ …… 定義リストを設置する
＜dt＞ …… dlタグ内に設置され、定義用語を設置する
＜dd＞ …… dlタグ内に設置され、定義用語の説明を設置する
＜table＞ …… 表を設置する
＜caption＞ …… 表のキャプションを設置する
＜thead＞ …… 表のヘッダ行を設置する
＜tbody＞ …… 表のボディ行を設置する
＜tfoot＞ …… 表のフッタ行を設置する
＜th＞ …… 表の見出しセルを設置する
＜tr＞ …… 表の行を設置する
＜td＞ …… 表のセルを設置する
＜colgroup＞ …… 表の列をグループ化する
＜col＞ …… colgroupタグ内に設置され、グループ化の指定をより細かく行う
＜form＞ …… 入力フォームを設置する
＜fieldset＞ …… 入力項目をグループ化する
＜legend＞ …… フィールドセットにタイトルを設置する
＜input＞ …… 入力項目を設置する
＜label＞ …… 入力項目にラベルを設置する
＜textarea＞ …… 複数行のテキスト入力項目を設置する
＜select＞ …… プルダウンリストを設置する
＜datalist＞ …… 入力項目付きプルダウンリストを設置する 
＜optgroup＞ …… プルダウンリストの選択肢をグループ化する
＜option＞ …… プルダウンリストの選択肢を設置する
＜button＞ …… ボタンを設置する

＜em＞ …… 囲んだテキストを斜体で強調する
＜strong＞ …… 囲んだテキストを太字で重要表示する
＜b＞ …… 囲んだテキストを太字で重要表示する
＜i＞ …… 囲んだテキストが斜体で表示され、他と性質が異なることを示す
＜u＞ …… 囲んだテキストが下線付きで表示され、本来とは異なる表記になっていることを示す
＜small＞ …… 囲んだテキストが小さく表示され、注釈的なものであることを示す
＜ins＞ …… 囲んだテキストが後から挿入されたものであることを示す
＜del＞ …… 囲んだテキストが削除されたものであることを示す
＜s＞ …… 囲んだテキストが正確ではないものであることを示す
＜dfn＞ …… 囲んだテキストが定義された用語であることを示す
＜abbr＞ …… 囲んだテキストが略語であることを示す
＜cite＞ …… 囲んだテキストが参照であることを示す
＜address＞ …… 囲んだテキストが連絡先であることを示す
＜q＞ …… 囲んだテキストがインライン要素として設置され、短い引用文であることを示す
＜code＞ …… 囲んだテキストがソースコードであることを示す
＜samp＞ …… 囲んだテキストがプログラムから出力されたサンプルであることを示す
＜var＞ …… 囲んだテキストがプログラムの変数であることを示す
＜kbd＞ …… 囲んだテキストがキーボードから入力されたものであることを示す
＜mark＞ …… 文章の中でハイライトしたい箇所を指定する
＜ruby＞ …… 指定したテキストにルビを振る
＜rt＞ …… ルビタグ内に設置され、ルビを設置する
＜rp＞ …… ルビタグ内に設置され、ルビ未対応ブラウザのための接続文字を設置する
＜bdi＞ …… 書式方向が異なる場合も固定表記にしたいテキストを指定する
＜wbr＞ …… 文章の中で改行可能な位置を指定する