OmniAICreator/Qiita-1.07M huggingface dataset:
【98%のサイバー攻撃が依拠する手口】ソーシャルエンジニアリングとは何か

こんにちは！AironWorks Hacker Teamです。今回は「ソーシャルエンジニアリングとは何か」と題して、サイバー攻撃の手法の一つであるソーシャルエンジニアリングについて取り扱います。これまでの記事で当たり前のように使用していたソーシャルエンジニアリングについて改めて深く解説しております！

#とりあえずWikipedia
早速ですが、議論の出発点としてWikipediaの解説を見てみましょう。
Wikipediaでは次のように紹介されています。
 > - In the context of information security, social engineering is the psychological manipulation of people into performing actions or divulging confidential information. 

これを邦訳すると次のようになります。

 > - サイバーセキュリティの文脈では、ソーシャルエンジニアリングは心理学的に人を操って、特定の行動をさせたり、機密情報を漏らさせたりする行為です。
心理学的と言われるといささか仰々しいですが、何らかの方法を用いて不安を煽ったりすることを指しています。つまり、不安や動揺、緊急性などを喚起して、普段ではありえないようなことを被攻撃者にさせたりすることです。

#ハッカーの侵入の方法は2通り
ここからは攻撃相手を個人ではなく、会社などの組織に限って説明していきます。そもそも攻撃者が組織内に侵入し、悪意のあるソフトウェア（マルウェア）やウイルスをインストールさせたり、機密情報を盗んだりする方法は2通りです。

![截屏2021-06-18 16.58.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645867/c605bfef-cbc4-0aa1-beba-12efd4e5804c.png)

1つ目はアプリケーションなどを通じて侵入する方法です。技術的な脆弱性を使用する場合もあれば、コーディングのミスやアクセス管理の設定ミスを使用する場合もあります。

2つ目は組織の人間を通じて侵入する方法です。代表的な方法としては、組織内の人間にメールやSNS、SMSなどでメッセージを送ることで、特定のリンクをクリックさせてマルウェアをインストールさせたり、似たようなデザインの偽サイトに誘導したりするものがあります。さらには物理的に盗み見たり、公衆Wi-Fiを遮断することによって攻撃用のWi-Fiに巧みに誘導する例もあります。

ソーシャルエンジニアリングはまさにこの2つ目の方法です。つまり、組織内の人間の不安を煽って、普段では到底考えられないような行動をとらせることなのです。

#具体的な事例とともに
具体的な事例として、日本経済新聞社で起こったインシデントをご紹介しましょう。米国支社のNikkei Americaが経営幹部に成り済ました第三者に約32億円を誤送金しました。

![截屏2021-06-18 16.58.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645867/bd1198b3-4cf1-79a1-e9fd-97248e802c3f.png)

> - 参考：Japanese media giant Nikkei says $29 million lost in BEC scam - CyberScoop
Scammers fleeced the publishing conglomerate Nikkei out of $2

詳しい手口については定かではありませんが、単なるメールによって近づいたのではないかと推測されています。この種の手口はBECスキャンあるいはビジネスメール詐欺と呼ばれており、ソーシャルエンジニアリングの代表的な手法です。

#なぜ今！？
なぜ今ソーシャルエンジニアリングが問題となっているのでしょうか。それはテレワークやリモートワークの増加で情報やその処理が社内ネットワークの外に染み出し、ソーシャルエンジニアリングがより簡単になったからです。実際、98%のサイバー攻撃はソーシャルエンジニアリングに依拠しており（出典：https://purplesec.us/resources/cyber-security-statistics）、

その脅威はCOVID-19の影響で増しています。（2020年でフィッシングが4.8倍に）

![截屏2021-06-18 17.03.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645867/454f1181-a2a9-fd87-1934-a387f07f9728.png)


#どのような目的で攻撃してくるのか
特定の思想を伝えるハクティビスト、特別な事由なく攻撃する愉快犯なども確かにいますが、最も大きな目的は2つ。金と情報です。

心理学的手口で誤送金を引き出したり、不正に得た個人情報をダークウェブに売り捌いたり、そもそも決済情報を盗んだりすることで、攻撃者は多額のお金を手にします。標的が公開企業であれば、情報流出などに起因する株価下落に依拠してお金を稼ぐケースもあります。また、敵対企業の機密情報を盗んだり、敵対企業の信用低下を狙って攻撃するケースもあります。

このように攻撃者は目的のためなら企業の規模を問わず攻撃を試みます。

#どのように防ぐべき？
ここまでソーシャルエンジニアリングの概要とその脅威について取り上げてきました。それでは結局ソーシャルエンジニアリングをどのように防げば良いのでしょうか。技術的に完全にこれを防ぐ方法はありません。しかし、人間生来のインテリジェンスで攻撃が成功する確率を格段に引き下げることができます。

そのために最も効果的なのは、実際に疑似的な攻撃を受けてみることです。そのようなトレーニングを経て実攻撃について深く理解できる、また、注意レベルを引き上げることができます。単なるセミナーや勉強会ではやはり他人事として終わってしまう可能性が高いです。

#最後に
最後までお読みいただきありがとうございました！ソーシャルエンジニアリングを考える一つの契機になれば幸いです。

これから先は2021年6月24日（木）実施予定の下記無料イベントで取り上げることにしたいと思います！
続きが気になる方は、ぜひ下記リンクからお申し込みください！
![aniwo engineer meetup-3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645867/21ed11b5-da2e-a075-8a46-0a8f5ca6d54a.png)

####エンジニアからプロダクトマネジャーへ：エンジニアの生存戦略を経験者が語る
日　程：2021年6月24日(木) 18:00 - 19:00
申　込：https://aniwo-engineermeetup-1.peatix.com/
会　場：Zoom Webinar
主　催：Aniwo
参加費：無料

また、毎週金曜に記事をアップしておりますので、よければフォローもお願いします>人<！
Zend Server for IBMi 標準ポート10080のブラウザブロックについて

# Zend Server標準ポート10080のブラウザブロックについて

Chrome 91より、Zend Server デフォルトポートの`10080`が、ブロック（ERR_UNSAFE_PORT）されるようになりました。企業の多くで使われるであろう、**Edge (Chromiumベース)** についても同様です。
FireFoxでは、既に対応されておりましたが、今回のChorme 91より、騒ぎが大きくなったようです。

## なぜブロック対象のポートとなったか？

`NAT Slipstreaming v2攻撃`への対策として、ブロックの対象となったようです。リソースは、[Fetch Standard](https://fetch.spec.whatwg.org/)の[bad port](https://fetch.spec.whatwg.org/#port-blocking)扱いになった事が理由の様です。

## IBM iユーザーの対応として

対応として、Chromeの起動パラメータ引数（-explicitly-allowed-ports = xxx）を指定するなど、対処療法的な回避策はあるようですが、長い目で見ると良い対応とは言えませんので、ポートを変える事が良いでしょう。

## Zend Serverのポートの変更方法

Zend Serverのポートの変更は以下の手順になります。

- Zend Serverインスタンスの停止
- 設定ファイルの変更
- Zend Serverインスタンスの再開
- クライントがアクセスするURLの指定ポートを変更を周知する

※これ以降の手順は、全てジョブ＆セッションのCCSID `5035`か`1399`環境で行う事を前提としおります。さらに、Zend Serverインストール時に特殊な事をせず、標準的なインストール作業を行った場合の状況について説明をしています。

### Zend Server インスタンスの停止方法

停止方法は、HTTP管理画面を利用するか、コマンドを実行します。今回はコマンド例で行います。
`QSECOFR`相当のユーザーで、5250エミュレータを開始し、以下のコマンドを実行します。

- `ENDTCPSVR SERVER(*HTTP) HTTPSVR(ZENDPHP7)`

`ZENDPHP7`の部分は、Zend Server9以降の標準インスタンス名で、お使いのバージョンにより適宜変更する必要があります。古いバージョンをお使いの場合は、`ZENDSVR6`,`ZENDSVR`の可能性があります。

### 設定ファイルの変更

5250での編集方法は、以下の通りになります。SSH接続等で編集しても構いません。

`EDTF STMF('/www/zendphp7/conf/httpd.conf')` 
`/www/zendphp7`の部分は、バージョンにより異なります。`/www/zendsvr`,`/www/zendsvr6`など。
以下の例では`10080`ポートを`55580`に変更しています。

`zend-default-vhost-10080.conf`に関しては、ただのファイル名ですので、**変えてしまわない**様に注意が必要です。もし変える場合は、実際にパス上に存在するファイル名も変えなければいけません。

```txt
〜〜〜中略〜〜〜
NameVirtualHost *:10080
Listen *:10080

↓
NameVirtualHost *:55580
Listen *:55580

〜〜〜中略〜〜〜

<VirtualHost *:10080>
   Include /usr/local/zendphp7/etc/sites.d/zend-default-vhost-10080.conf
</VirtualHost>

↓
<VirtualHost *:55580>
   Include /usr/local/zendphp7/etc/sites.d/zend-default-vhost-10080.conf
</VirtualHost>

```

### Zend Server インスタンスの再開方法

- `STRTCPSVR SERVER(*HTTP) HTTPSVR(ZENDPHP7)`

### クライントがアクセスするURLの指定ポートを変更を周知する

ポート変更は完了しましたので、各クライントが、アクセスしているURLを変更する旨を伝える必要があります。

- 旧URLの例：`http://hostname:10080/foo/bar`
- 新URLの例：`http://hostname:55580/foo/bar`

以上で完了です。
Azure App ServiceへのZipデプロイで任意のPythonライブラリをインストールする方法

### 背景
FlaskアプリをApp Serviceに(デフォルトで作成されたAzure Pipelinesを実行して)Zipデプロイした際、
requirements.txtに書いたPythonライブラリがApp Serviceに正しくインストールされなかった
これを正しくインストールされるようにするための解決方法を記載

### 解決方法
App Serviceに以下のアプリケーション設定をする

```json:アプリケーション設定
  {
    "name": "SCM_DO_BUILD_DURING_DEPLOYMENT",
    "value": "true",
    "slotSetting": false
  }
```

az webapp upコマンドを使用してアプリケーションをApp Serviceにデプロイすると上記アプリケーション設定が自動で設定される

### 事象を確認した環境
- App Service
  - ランタイムスタック：Python3.9
- Azure Pipelines
  - 以下情報でデフォルトで作成されたazure-pipelines.ymlを使用
     - リポジトリ：GitHub
     - リポジトリ内容：Python Flaskアプリケーション
     - デプロイ：App Service
プログラミングをこれから始めようとする人向けのチュートリアル

#初めに
　「プログラミングをしよう！」と思う人はそんなに少なくないと思います。ですが、何から始めたらいいのかわからない人は多いです。プログラミングしたいなあと言っていた著者友人はスタートの仕方がわからず電子回路関連に行きました（それもそれですごいけど）。
　というわけで、無事スタートを切っただけの初心者だからこそかける、まったくの初心者向けの記事を以下に書いていきます。
#pythonって何？何ができるの？
　pythonはプログラミング言語です。コンピューターに話しかけるための言葉です。しかるべきところにしかるべき方法でこの言葉で書いたファイルを実行すると、コンピューターが書いた通りに動きます。
　
　プログラミング言語は多数存在しますが、pythonは**インデント**と呼ばれる、改行や空白をしっかり入れないと、コンピューターが正しく実行してくれません。他の言語では横続きでも実行できてしまう場合があるので、pythonから始めることで綺麗に、人間の目にもわかりやすいコード（プログラミング言語を書いた文章）を書くいい練習となります。

　また、pythonは「コンパイル」と呼ばれる、プログラミング言語をよりコンピューターに理解しやすい文に変換する、という工程を踏まずとも実行できるので、他の言語より初心者の悩みの種を生む原因が一つ少ないのです。
#鉄則
　環境をそろえたり、実際に書いていくうえで一番頼りにすべきなのは**グーグル先生**です。ググればすべてが出てきます。エラーが出て先に進めなくなっても、エラー文を翻訳したりそのまま検索するだけで解決することが多いです。
その検索結果からサイトを選ぶとき、
・今表示されているQiitaというサイト
・https://teratail.com/
がおすすめとなります。

また学習に関してですが、本屋で本を買うのもいいですが、初めから意気込むと躓きやすいと思うので、ネット記事を利用しノーコストで行きましょう。
・https://note.nkmk.me/
　＝＞難しいが、情報が豊富
・https://www.javadrive.jp/python/
　＝＞基本的なことが知りたいときに。教科書替わり。
がおすすめです。
#環境を整える
　本当は色々整えるべきですが、とりあえず触りたい！という場合は以下の二つを行ってください。

1・[vscode](https://azure.microsoft.com/ja-jp/products/visual-studio-code/)を[こちらの記事](https://breezegroup.co.jp/201904/visualstudiocode/)で紹介されているように、ダウンロード→インストールを済ませてください。コードはここに書き込みます。

2・pythonを[こちら](https://www.python.jp/install/windows/install.html)で紹介されている通りにインストールしてください。

　vscodeは、コードを書くノートです。このノートにはプログラミングを助ける様々な機能が存在します。pythonをインストールした環境でpythonで書いたコードを実行（コードを書いたファイルを開く）することで、初めてコンピューターに伝えることができます。
#コードを実行してみる
　では、書き込む準備をしましょう。
　vscodeを開き、左上のファイルタブから、新規ファイルを選択します。開いた画面に

```py
print("やったぜ")
```
と書いてください。
その後、左上のファイルタブから名前を付けて保存を選択し、ファイル名を「.py」が最後に来るようにして保存してください。`.py`を最後に着けて保存することで、「このファイルはpythonで書いています」とコンピューターに宣言していることとなります。

　それではお待ちかね、右上の緑の矢印を押して初めてのコードを実行してみてください。やったぜ、と画面下部に表示されたら成功です。違う場合はコードがあってるかを見直してください。
　画面下に出てきたものは「ターミナル」と呼ばれるもので、厳密には違いますがざっくり言うと、実行結果やエラーが出力されるところと思っておいてください。

次に、vscodeを介さずに実行してみましょう。

```py
a = input()
```
　と一段下げて書き足してから、ファイルタブからの保存を選びvscodeをいったん閉じて、ファイルを保存した場所を開いてください。それから、保存したファイルをダブルクリックで実行しましょう。おそらくさっきと同じ言葉が黒い画面に映し出されたと思います。
#コードの解説
　「print」は、その後のカッコ内に渡したものを表示させる文です。
　また、inputはカッコ内をprintした後に待機し、その後入力された内容を受け取る役割があります。コードは上から下へ実行されるので、今回の場合はprintが終わった後にキーボードに何が入力されるかをinput文は待機していた、ということになります。また、

```py
a =
```
　と書いたので、待機中に入力された文字はaに保存されています。数学におけるx,yみたいなもので、=で結ぶことによって様々な値を様々な文字列に保存できます。
　ですが今回は本来の使い方と違ってinput文には、printが実行された後、何か入力するまでinput文は実行できないことを利用して、実行結果を見るまでウインドウをあけておいてもらう役割を担ってもらいました。

　まあ詳しいことは最初に提示したサイトを読んだ方がわかるかと。
#その後何をしたらいいのか
　初めてプログラミングで文を書きました。

「次は何をしますか？」

と聞かれてもわからないと思います。わからなくていいんです。まず、ある程度文法的なものを覚えるために序盤に示したサイトを閲覧し、実用性皆無でいいのでそれを生かした何か作りましょう。そして何か覚えるたびに、それを繰り返します。基本的なことをだんだん覚え始めたら、LINEbotの作り方や、実行時にユーザーインターフェースを表示させる方法などをグーグル先生に聞きましょう。
そこまでいけば、脱初心者なんじゃないでしょうか？
#最後に
ここまでが、脱初心者を果たしたかはたしていないかの自分が言える限界です。
少し知識のある人が読むと欠陥や、笑ってしまうような幼稚な表現もあったかと思います（笑）
しかし、この記事が右も左もわからない人たちの助けになることを願ってます。
[Rails]date_validatorで日付に関するバリデーションを。

# date_validatorとは
簡単に日付(date)に関するバリデーションを設定出来るgemのことです。

# 使い方
## 基本形

```ruby
validates :expiration_date, date: true
```

### 範囲を指定したい場合
afterには`指定した日付より後`のみ許可し、beforeには`指定した日付より前`のみ許可します。

```ruby
  validates :birthday, date: {
    # 1900年1月1日より後で現在の日付までの範囲のみ許可する
    after: Date.new(1900, 1, 1),
    before: ->(obj) { Date.today }
  }
```

after_or_equal_toだと、`指定した日付以降`のみ許可し、before_or_equal_toでは`指定した日付以前`のみ許可します。

```ruby
validates :due_date,  date: {
  # 1900年1月1日以降で現在の日付以前の範囲のみ許可する
  after_or_equal_to: Date.new(1900, 1, 1),
  before_or_equal_to: ->(obj) { Date.today }
}
```

# 参考
[date_validator]
(https://github.com/codegram/date_validator)
ruby 演算子

###　演算子
様々な数値に対して、計算や加算、減算、比較などを行う場合に使用。

###　代数演算子
```
a = 1 + 2       #足し算 3
a = 5 - 2       #引き算 3
a = 3 * 1       #掛け算 3
a = 9 / 3       #割り算 3
a = 9 % 2       #余り算(割った後の余りを表示する)　1
```
四則演算と余剰を求めるもので、以上の4つ。
 + に関しては文字列の連結に使用できる。
ただし、数値としての 1 と文字列としての 1 は別のものであり、数値と文字列を連結するとエラーになる。
そのため数値を文字列に変換するために　整数値.to_s を使用する。
また、割り算を少数の値まで出したい場合は　整数値.to_f を使用する。

```
a = 1 + 2
puts a.to_s + "です"　　　　　#3です　と表示される

a = 9.to_f % 2
puts a.to_s + "です"　　　　　#1.0です　と表示される
```

###比較演算子
```
a = 3 == 3　　　　　#等しい場合にはtrue。等しくない場合にはfalse。
a = 3 != 3　　　　　#等しい場合にはfalse。等しくない場合にはtrue。
a = 3 < 4　　　　　 #大きい場合にはtrue。小さいか等しい場合にはfalse。
a = 3 > 4　 　　　　#小さい場合にはture。大きいか等しい場合にはfalse。
a = 3 <= 4　　　　　#大きいか等しい場合にはtrue。
a = 3 >= 4　　　　　#小さいか等しい場合にはtrue。
```
代入演算子の = と ==　は全くの別物。
条件分岐のif文などで使用。


### 論理演算子
```
(1 == 1) && (3 == 3)　　　　　#条件がどちらもtureならture。
(1 == 1) || (3 == 3)　　　　　#条件のどちらかがtureならture。
!(3 == 3)　　　　　　　　　　　#条件がfalseならture。
```
Flaskでワクチン余剰分の情報を掲示できるサイトを作ってみた

#背景
コロナワクチンは１つの瓶を希釈することで６回分を作成し数時間のうちに使い切らなければ廃棄になってしまうらしく、その余ってしまったワクチン情報をシェアして、コロナに不安を感じている人が一人でも安心できるようになるといいなと思って、サイトを作成してみました。

#使ったもの
Python　Flask
Cloud SQL
LINE Login API
GAS
HTML＆CSSは[テンプレート](https://template-party.com/)をそのまま使いました。

#ワクチン接種者側
トップページにアクセスすると、下記ページが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/739747/be66d8dc-eeac-3000-7101-9ebd8c16a128.png)
ここでは現在ワクチン接種が可能な病院情報が表示されます。都道府県ごとに表示させることもできます。
病院情報の最後に表示されている予約するリンクをクリックすると下記ページに遷移されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/739747/d0006b01-599e-67f1-41d0-e0246a5a65d0.png)
LINEアカウントを使って申し込めます。不正防止のため１アカウントにつき１日１回だけ申し込むことができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/739747/0150e272-26ac-5401-e027-d6840fcb46d2.png)
ログインをクリックするとLINEIDを取得し、このサイトにログインログアウトすることができます。そして無事にログインもしくは会員登録が完了すると下記画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/739747/bd2587b0-d9ee-82a1-0e93-e293b6778b3c.png)
この画面を通じ、自分の情報を入力し送信します。この情報はCloud SQL上のDBに記録されます。
そしてこの情報を送信するとトップページにリダイレクトされるので、そこでLogoutタブからログアウトを行います。

#病院側
**病院情報登録ページ**
病院側はトップページがLINE Login画面です。こちらもLINEアカウントを使います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/739747/2371a5c6-85f9-ba8c-5dc8-27894a33e098.png)
一度もログインしたことがないユーザーは病院情報登録のページが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/739747/910aeb04-2840-0fe1-5c6a-d9dc7ef3ea8a.png)
そして情報登録が終わると、下記の管理者ページが表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/739747/3e081afb-8400-d5f0-0a06-98fea0a1eb32.png)
ここには現在予約しており、来院していない患者のリストが表示されます。病院側からは患者の申し込みに対し、ワクチンの会社・ロット番号・来院の有無　の情報を追加することができ、この情報を追加しない限りは患者一覧から非表示にはなりません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/739747/846f7d2d-bfdf-25c2-106e-c5dd6538a6be.png)
上部のタブはこのようになっていますが、この中央のワクチン情報掲載フォームをクリックすると下記のGoogle Formに遷移します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/739747/581419fb-afa0-d62e-1d62-4a4b28bc2e82.png)
こちらから新たなワクチン余剰分のデータを掲載することができます。このGoogleformはスプレッドシートと結びついており、そのスプレッドシートに新たな情報の記入があると、CloudSQL上のDBに書き込みが行われるようになっています。そしてフォームを入力し終えると病院管理トップページへリダイレクトされます。

#Google Formを選択した理由
最初はセキュリティを考慮し、個人情報登録部分のすべてをGoogle Formで記入→スプレッドシート→DB接続にしようと思っていました。ですがSQLインジェクションについて調べると、入力フォームへのこだわりは不要だとわかり他の2つはHTMLで作成しました。最後に一つ残したのは、POSTで情報送信しなくともDBに書き込みができるだけの情報量しかなかったからです

#難しかった事
*Cloud sqlのDBを接続してローカルで使えるようにするための設定
*LINEのトークンの発行
→これらの手法をしっかり明記していきたい

#次にしたいチャレンジ
ちゃんと需要のあるサービスのサイトをresponderで作ってみたい！
pandas.dataframeからBigQuery用のtable_schemaを生成する

#1.意図
python（だいたいjupyterlab）からBigQueryを操作(データ取得)して、pythonの方が得意なことをやらせてから
加工後のデータをBigQueryに保存する、という操作をよくやる。
BigQueryへのデータ登録の際にいつも困るのがtable_schemaの定義。
pythonだとあまり型を意識することがなくふわーっと書いてて立ち止まってしまい、結局ウェブ上に探しに行く羽目になるので、備忘録として。

* だいたいSTRINGであろう。
* 例外的にあるとしたNUMERICだろう

という前提のもと書いてみた。DATE系のも適宜入れていけばいい感じになってくれるか。


#2.こんな感じ

```python
table_schema = []
  for col in df.columns:
    schm_dic = {}
    dtype = df[col].dtype
    schm_dic["name"] = col
    if 'int' in str(dtype):
        schm_dic["type"] = "NUMERIC"
    else:
        schm_dic["type"] = "STRING"
    table_schema.append(schm_dic)

table_schema
```

出力結果：

```python
[{'name': 'family_id', 'type': 'STRING'},
 {'name': 'appday', 'type': 'STRING'},
 {'name': 'applicants', 'type': 'STRING'},
 {'name': 'ipc4', 'type': 'STRING'},
  ・・・（略）
 {'name': 'tag', 'type': 'STRING'}]
```


これを使って

```python
project_id = '***'
dataset = '***'
tablename = '****'
dest_table = "{}.{}".format(dataset,tablename)

df.to_gbq(destination_table=dest_table,project_id=project_id,table_schema=table_schema)
```

で無事に登録できた！
STM32F303K8で温度,S-5851Aをシリアル出力


目的
温度をシリアルに出力したい

1.SCLとSDAを接続、プルアップも忘れずに
2.電源の接続
3.下記のソースコードを書き込む
4.コンパイル実行で表示されたら終了
5.おわり

xプログラムは、STM32F767と全く同じ

D4 I2C1_SDA
D5 I2C1_SCL



```c++

#include "mbed.h"

//10の割り算 0から1028までは、正しい。主に0から999
#define DVI10(n) ((n*205)>>11)

//#define S5851A 0x48
#define ADDR        ((0x48)<<1)   //  address

Serial pc(SERIAL_TX, SERIAL_RX); //767
//RawSerial pc(PA_2, PA_3); //010

I2C i2c(I2C_SDA, I2C_SCL); //767
//I2C i2c(dp5, dp27); //1114
//I2C i2c(PA_10, PA_9); //010

int     tempval;        //温度
char    data_read[6];   //i2cバッファー
int     n10;            //10の桁

int main()
{
    
    pc.printf("\r\n767\r\n"); //767
    
    while (1) {

        //初期化
        //set address 0
        i2c.write(ADDR, "\000", 1); //addres 0

        //温度の読み込み
        // Read temperature register
        i2c.read(ADDR, data_read,1,false);

        //温度の保存
        // Calculate temperature value in Celcius
        tempval =  data_read[0];
    
        //tempval = 12; //debug
    
        //計算
        // Calculation temp
        //tempval = 12;
        n10 = DVI10(tempval);             //10の桁
        tempval = tempval - ( n10 * 10 ); //1の桁

        //液晶に出力
        // Display result
        data_read[0] = '0' + n10;
        data_read[1] = '0' + tempval;
        data_read[2] = '\r';
        data_read[3] = '\n';
        data_read[4] = 0;

        //温度の出力
        //puts(data_read); //010
        pc.printf(data_read); //767

    //1秒待つ
        wait_ms(1000);
    }//while

}//main

//容量削減
//void error(const char* format, ...){}



```


![i2c_s5851a_1114_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/a8ca3f14-5262-ebe5-7304-1a598733be49.jpeg)
バリデーションの種類について

電話番号のバリデーションについて

```
# 電話番号(ハイフンなし10桁)
/\A\d{10}\z/

# 携帯番号(ハイフンなし11桁)
/\A\d{11}\z/

# 携帯番号(ハイフンなし10桁or11桁)
/\A\d{10,11}\z/
```

実際の記述はこんな感じ。

```app/models/purchase_shipping.rb
    validates :phone_number, format: { with: /\A\d{10,11}\z/, message: "PhoneNumber must be 10or11 digit Half-width numbers" }
```

その他正規表現を使用したバリデーションの種類について下記リンクの記事がすごいわかりやすかった。
https://gist.github.com/nashirox/38323d5b51063ede1d41
【SwiftUI】NeumorphismButton作ってみた

#環境

【Xcode】11.7
【Swift】5.2.4
【iOS】13.7
【macOS】Catalina バージョン 10.15.7

#作り方
##ベースの色を作成

```
extension Color {
    static let offWhite = Color(red: 245 / 255, green: 245 / 255, blue: 255 / 255)
}
```

アプリ全体で使っていくので、extentionで定義しておきます。

##ViewModifierとして設定

```
struct NeumorphismStyle: ViewModifier {
    var height: CGFloat
    var ratio: CGFloat
    
    func body(content: Content) -> some View {
        content
            .padding(10)
            .background(
                GeometryReader(){ geometry in
                    RoundedRectangle(cornerRadius: 30,
                                     style: .continuous)
                        .fill(Color.offWhite)
                        .shadow(color: Color.black.opacity(0.2), radius: 10, x: 10, y: 10)
                        .shadow(color: Color.white.opacity(0.5), radius: 10, x: -5, y: -5)
                        .frame(width:geometry.size.width * self.ratio,
                               height: self.height)
                }
            )
    }
}
```
高さ→値で指定
幅→親要素の幅に対する倍率

汎用的に使うために設定したViewのサイズを参照し幅を決めていきます。

##設定方法

```
NavigationLink(destination: SecondView()) {
                    Text("NextView")
                        .modifier(NeumorphismStyle(height: 50, ratio: 1.5))
                }
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643790/4f28cf61-15ac-4958-5026-d5b887cb965e.png)
##参考記事

https://www.hackingwithswift.com/articles/213/how-to-build-neumorphic-designs-with-swiftui
BastilleBSDでVNET

複数のjailでtftpサーバを動かして外からアクセスしたいのですが、通常のIPv4とrdrでは1つのポートを一つのjailしか転送できません。

仕方が無いのでVNETを試して見ます。中身はもっと複雑ですが、大体の構成は以下のような感じです。

![vnet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/e00dc705-e3a3-01e8-fa2f-a32f0c3f7098.png)

ドキュメントに書かれているように/etc/devfs.rulesに以下を書きます。この効用はよくわかりません。

```
[bastille_vnet=13]
add path 'bpf*' unhide
```

とりあえず、VNETなjailを作ります。

```
% sudo bastille create -V vnetjail 13.0-RELEASE 192.168.0.55/24 alc0
```

アドレスはalc0と同じネットワークで設定します。デフォルトルートが設定されないようなので、設定します。

```
% sudo bastille sysrc vnetjail defaultrouter="192.168.0.1"
% sudo bastille restart vnetjail
```

IPv4の時と同じようにinetdを動かしtftpを使えるようにしようとしましたが、うまく動きません。いろいろ試した結果pf.confのblock in allを外してpfで引っかからないようにして、IPv4で設定しているtftpポートのrdrをclearしたら動くようになりました。

rdrはclearすると一時的に消えますが、再起動すると元に戻ります。該当のjailを消すとrdrも消えますが、rdrだけ消す方法が分かりません。

IPv4とVNETは共存できるようなのですが、上のように微妙に干渉しています。またVNETのjailはIPアドレスが空で表示されたりしていて、0.8(20210115)ではVNETサポートは完全ではないのかもしれません。

VNETの設定は手動でやると結構複雑みたいですが、簡単にできるのは良い事だと思います。

IPv4で作ったjailをVNETに移すのはbastilleのsysrcとeditでできますが、ちょっと面倒です。

2024年2月にはいって突然IPv4なjailでgithubへのアクセスが不安定になり、別のホストでvnetだと大丈夫だったので設定変更しました。

editで以下を消して

```
  interface = bastille0;
  ip4.addr = 192.168.10.50;
  ip6 = disable;
```

以下を追加します。

```
  vnet;
  vnet.interface = e0b_bastille0;
  exec.prestart += "jib addm bastille0 re0";
  exec.poststop += "jib destroy bastille0";
```

sysrcで以下を設定します。

```
ifconfig_e0b_bastille0_name="vnet0"
ifconfig_vnet0="inet 192.168.0.56/24"
defaultrouter="192.168.0.1"
```

/usr/local/bastille/jailの下にrdr.confが残ってるとrestartしたときにerrorになるので消しました。
Modalで遷移した画面からUnwindSegueで戻る

## はじめに
- 自分のメモとして記事を書いています。

## 参考文献
[https://youtu.be/V3ippqWGH3o?list=PLQ5rERkGSxF9_soz3Ns-SpURWsy0WmbJQ](https://youtu.be/V3ippqWGH3o?list=PLQ5rERkGSxF9_soz3Ns-SpURWsy0WmbJQ)

### PresentModallyで接続する
1. まず練習のための画面を用意します。今回は初めの画面と次の画面を
用意し、間にナビゲーションコントローラを挟んでいます。
1. 実際に画面をモーダルでつなぎます。初めの画面のボタンをドラックアンドドロップでナビゲーションコントローラに(PresentModallyで)つなぎます。

![Unwind1.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1379778/dc18e0af-f6c8-9154-54d3-8cc184c55c74.jpeg)![Unwind1.002.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1379778/c8d5411a-35e7-e04d-8496-e34ce0d530ec.jpeg)

### FullScreenにする
1. segueを選択してFullScreenを選択します。
![Unwind1.003.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1379778/33a4b6d2-b569-bc70-104b-aa91e4a53e6c.jpeg)

### UnwindSegueのコードを書く
1. 初めの画面のコントローラに移動してコードを書きます。
1. 今回はViewController.swiftにコードを書きます。
![Unwind1.005.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1379778/9781511f-33ba-254d-e01e-da056ef88117.jpeg)

```swift
@IBAction func exit(segue: UIStoryboardSegue) {
    }
```
### 遷移先の画面をUnwindSegueに接続
1. ボタンをドラックアンドドロップでExitとつなぎます。
1. コードで作成したUnwindSegueがあるのでそれを選択します。![Unwind1.006.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1379778/0146d3a5-8b22-0a22-b5d6-79c85e8da695.jpeg)

![Unwind1.007.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1379778/4fc67b4c-0069-214a-aee0-36cdc3eac7bf.jpeg)

### 動画
![unwindSegue.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1379778/77ecda93-9d9d-c62d-48d2-947913e19194.gif)

##　おまけ1
### UnwindSegueのidentifierを設定して遷移した画面から戻る
#### UnwindSegueを接続する
1. SecondViewControllerからドラックアンドドロップで、作成したUnwindSegueに接続する
![unwindSegue.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1379778/ba3d5593-db96-227c-94b7-54c15c34c30c.jpeg)
![unwindSegue.002.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1379778/94965d18-7954-521a-0522-a237d78c0eb2.jpeg)

#### identifierを設定する
1. UnwindSegueを選択し、identifierを設定します。
1. 今回はexitに設定しています。

![名称未設定.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1379778/6ba19d69-fa2f-94ec-a6d1-4d7df830528a.jpeg)


#### ボタンを接続する
1. ボタンをドラックアンドドロップして、@IBAction接続する

![unwindSegue.003.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1379778/ddb73981-8579-574f-65c4-1451150d5f79.jpeg)

#### ボタンが押された時の処理
1. performSegueのwithIdentifierに設定したものをString型で入れます。

```swift
 @IBAction func tappedBtn(_ sender: Any) {
        performSegue(withIdentifier: "exit", sender: nil)
    }
```

![unwindSegue.004.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1379778/de72a1af-83bd-6544-21ff-f1d14b0262e2.jpeg)

## おまけ2
### 画面を戻った時に遷移先のコントローラーを取り出す
1.今回はViewController内でSecondViewControllerを取り出しています。
※ UnwindSegueでは、SourceViewControllerが遷移先（今回ではSecondViewController）になる

```swift
 @IBAction func exit(segue: UIStoryboardSegue) {
        let secondViewController = segue.source as! SecondViewController
    }
```

![名称未設定.001.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1379778/6eac8699-ee5c-caa2-7f4b-3466c48a4bb1.jpeg)
[Android]思考停止で使っていたFragmentを少し掘り下げる

#粋がりのFragment
最初は、Activityにだけ処理を書いていた自分も、いつからかFragmentも使うようになりました。
ちょっとAndroid詳しくなっちゃった感じ？
と、肩に風を切るように開発を進めていますが、正直、Fragmentのことをそんなにわかっていません。
ということで、掘り下げていきましょう。

#Fragmentとは？
>Fragment は、FragmentActivity でのユーザー インターフェースの挙動や部位を表すものです。

相変わらず、ドキュメント先輩の威圧感半端ない。
何を言っているのか、ぱっと見わかりにくいです。

>1 つのアクティビティに複数のフラグメントを組み合わせてマルチペイン UI をビルドしたり、複数のアクティビティでフラグメントを再利用したりできます。

簡単に説明すると、Fragmentとは、「ピース」(パズルのね)みたいな画面です。
Activityという、画面の「枠」にはめて使います。

>フラグメントとは、アクティビティのモジュラー セクションのようなもので、独自のライフサイクルを持ち、独自の入力イベントを受信します。

Fragmentにも、Activityと同じようにライフサイクルがあるみたいです。

>フラグメントはアクティビティの実行中に追加したり削除したりできます（別のアクティビティで再利用できる「サブ アクティビティ」のようなものです）。

どのActivityという「枠」だろうが、Fragmentという「ピース」をはめ込んでは、外すことができるという意味ですね。

>フラグメントは常にアクティビティでホストされている必要があり、フラグメントのライフサイクルはホストのアクティビティのライフサイクルの影響を直接受けます。たとえば、アクティビティが一時停止しているとき、その中のすべてのフラグメントも一時停止し、アクティビティが破棄されると、すべてのフラグメントも破棄されます。

うんうん！
こっちの方が、「枠」と「ピース」の関係性がよく表現されていますね。
「枠」が破棄されたら、「ピース」も同じように破棄されますからね。

>ただし、アクティビティの実行中（ライフサイクルで再開された状態）は、追加や削除といった操作を各フラグメントで独立して行うことができます。

Activityという「枠」が、あればFragmentという「ピース」を自由に生成(onCreateView),破棄（onDetach）できるというわけです。

>フラグメントのトランザクションを実行するとき、アクティビティで管理されているバックスタックにそれを追加することもできます。アクティビティの各バックスタック エントリは、発生したフラグメントのトランザクションの記録です。バックスタックでは、[戻る] ボタンを押すとフラグメントのトランザクションを戻す（前に戻る）ことができます。

Activityには、バックスタックという「後入先出」の「まとめ処理」として記録していてくれてます。
つまり、aFragment -> bFragment -> cFragment
と、遷移したとき、戻るボタンをぽちぽち押すと、
cFragment -> bFragment -> aFragment
と戻ることができるわけです。

###Fragment誕生秘話
どうして、このような画面が生まれたのでしょうか？

>Android では、タブレットなどの大画面でのよりダイナミックで柔軟な UI デザインに対応するため、Android 3.0（API レベル 11）でフラグメントが採用されました。

ふむふむ。

> たとえばニュース アプリケーションでは、1 つのフラグメントを使って左側に記事の一覧を表示し、別のフラグメントを使って右側に記事の内容を表示できます。両方のフラグメントが 1 つのアクティビティに横並びに表示され、それぞれのフラグメントには自身のライフサイクル コールバック メソッドがあり、それぞれのユーザー入力イベントを処理します。つまり、1 つのアクティビティで記事を選択して、別のアクティビティで記事を閲覧するのではなく、図 1 のタブレットのレイアウトのように 1 つのアクティビティ内で記事を選択して閲覧できるようになります。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599059/b72eaa60-3647-6283-07d2-0c60dbaffc01.png)

ほー。
確かに、ipadでLINEやるとこんな感じの画面構成だよね。
左側にトーク一覧があって、右側にトーク画面があるみたいな。
タブレットでの有意義な画面構成を目的として生まれたっぽいですね。

>各フラグメントは独自のライフサイクル コールバックを使用して自身のレイアウトと挙動とを定義するため、1 つのフラグメントを複数のアクティビティに含めることができます。このことから、再利用可能なデザインを用いることに加えて、1 つのフラグメントを他のフラグメントから直接操作しないようにする必要があります。

Activityという「枠」があれば、自由にFragmentという「ピース」を当てはめることができるので、競合が起きないように注意ってことですね。

>引き続きニュース アプリケーションの例を使うと、アプリケーションがタブレット サイズの端末で実行中は、2 つのフラグメントをアクティビティ A に埋め込むことができます。しかし、ハンドセット サイズの画面では両方のフラグメントを表示する領域が足りないため、アクティビティ A には記事の一覧のフラグメントだけが含まれます。ユーザーが記事を選択すると、記事を閲覧するための 2 つ目のフラグメントが含まれるアクティビティ B が開始されます。そのため、アプリケーションは図 1 のようにフラグメントを異なる組み合わせで再利用することで、タブレットとハンドセットの両方に対応できるようになります。

タブレットの場合、画面サイズ、つまり「枠」に余裕があるので、二つの「ピース」をはめることができると。
逆に、スマホサイズだと一つというわけか。
面白いなぁ。
つまり、LINEの画面はFragmentで遷移している感じなのね。

#終わりに
一旦ここまでにします。
Activityという「枠」にはめるFragmentという「ピース」という説明しかしてないと思いますが笑。
機会があれば、FragmentManagerとライフサイクルについても深掘りできたらと思います。
【Swift】アルバムから動画だけを抽出する方法

#アルバムから動画だけを抽出
動画投稿の際、アルバムから動画を選択したい時があるかと思います。
そういった実装をされる時に参考にしていただけると幸いです。
#コード
```swift
import UIKit

final class FirstViewController: UIViewController {

    @IBAction func openAlbum(_ sender: Any) {
        
        album()
    }
}

extension FirstViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    
    func album() {
        //動画のみが閲覧できるアルバムを起動。
        let imagePickerController = UIImagePickerController()
        //デリゲートを指定する。
        imagePickerController.delegate = self
        //タイプはアルバム。
        imagePickerController.sourceType = .savedPhotosAlbum
        //動画だけを抽出。
        imagePickerController.mediaTypes = ["public.movie"]
        //編集不可にする。
        imagePickerController.allowsEditing = false
        //インスタンスを出力。
        self.present(imagePickerController, animated: true, completion: nil)
    }
    
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        //アルバムを閉じる。
        picker.dismiss(animated: true, completion: nil)
    }
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        
        let mediaURL = info[.mediaURL] as? URL
        videoURL = mediaURL
        
        picker.dismiss(animated: true, completion: nil)
        
        //値を渡しながら画面遷移。
        let storyboard = UIStoryboard(name: "Second", bundle: nil)
        let secondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
        secondViewController.url = videoURL
        self.present(secondViewController, animated: true, completion: nil)
    }
}
```
rubyについて

###　この記事を書いた理由
rubyの学習を始めるにあたって、自分の知識を確認するため。

###　rubyとは
まつもとゆきひろ氏という方によって作られたプログラミング言語。
開発者が日本人ということで、日本語の参考資料も豊富である。

###　rubyのメリット
・オブジェクト指向のプログラミング言語。
・多言語よりも記述量が少なくプログラマーの負担を減らしているため、初心者でも学びやすい。
・記述の自由度が高い。

###　rubyのデメリット
・記述の自由度が高い反面、記述者によって記述の差が出やすい。
自分向けに作ったDockerの忘備録をここに公開する

# 構成

Dockerを構成するのは、次の2つです。
Docker Engine ・・・コンテナ仮想化技術であり、アプリケーションの構築からコンテナ化に至るワークフローを連結する
Docker Hub ・・・ アプリケーション群を共有・管理するSaaSサービス


## Docker Engine

わからん


## Docker Compose

Compose とは、複数のコンテナを使う Docker アプリケーションを、定義・実行するツールです。Compose はアプリケーションのサービスの設定に、Compose ファイルを使います。そして、コマンドを１つ実行するだけで、設定した全てのサービスを作成・起動します。

## Docker Hub

[Docker Hub](https://hub.docker.com/) はクラウド上のレジストリ・サービスであり、アプリケーションやサービス・コンテナの構築と配信を行います。コンテナ化されたイメージを見つけたり、配布したり、変更の管理、ユーザやチームのコラボレーション、開発パイプラインを通したワークフローの自動化といった集約されたリソースを提供します

## Docker Machine

Docker Machine は仮想マシン上に Docker Engine をインストールするツールであり、 `docker-machine` コマンドを使ってホストを管理します。Mac や Windows 上では、Docker Machine を使うのが Docker を実行する唯一の方法です。

## Docker Toolbox

Windows と Mac の両方で利用可能な Docker Toolbox は、Docker クライアント、Machine、Compose、Kitematic をインストールします

## Docker Registry（Docker Distribution に統合予定）

レジストリ（Registry）とは、ステートレス（処理状態を把握しない）であり、サーバ・サイドのアプリケーションを大きくスケールさせるため、Docker イメージを保管・提供をします

## Docker Swarm

Docker Swarm は Docker に対応するネイティブなクラスタリング用ツールです。Docker Swarm は標準 Docker API で操作できます。そのため、Docker ホスト群を集め、１つの仮想 Docker ホストとして扱えます。既に Docker デーモンと通信可能なツールであれば、Swarm を使うだけで、意識せずに複数のホストにスケール可能になります



# ◎ Docker のコマンド集 ◎
## docker images

ローカル環境に保存されているDockerイメージの一覧を取得する

## docker pull <Dockerイメージ名>( : <タグ>)

DockerイメージをDocker Hubからダウンロードだけする。

## docker rmi <Dockerイメージ名・イメージID>

Dockerイメージをローカル環境から削除する


## docker ps

実行中のDockerコンテナのみを表示する。-a オプションで停止中のコンテナも表示する。

## docker create <Dockerイメージ名・イメージID>

Dockerコンテナを作成する（起動はしない）

## docker run <Dockerイメージ名>

Dockerコンテナを作成して起動する。はじめはローカル環境のイメージを探し、無ければDocker Hubに探しに行く。
**有名なオプション**

- -d  // バックグラウンドで起動
- --name <名前> // コンテナを識別するための名前を設定する
- -p <ホスト側ポート>:<コンテナ側ポート> // ポートフォワーディングの設定
- -it // コンテナ内でインタラクティブなbashシェルを作成する
## docker start <Dockerコンテナ名・コンテナID>

作成されたDockerコンテナを起動する

## docker stop <Dockerコンテナ名・コンテナID>

起動中のDockerコンテナを停止する

## docker rm <Dokcerコンテナ名・コンテナID>

Dockerコンテナを削除する

## docker exec <Dockerコンテナ> <コマンド>

Dockerコンテナ内でコマンドを実行する
**応用編？**
docker exec -it <Dockerコンテナ> bash // Dockerコンテナ内に新しいbashセッションを作成する


## docker build (-t <イメージ名>(:<タグ名>)) <Dockerfileのディレクトリ>

dockerfileの情報からDockerイメージの作成をする


## docker-compose up

docker-compose.yml が置かれているディレクトリで実行する。Dockerコンテナを作成、起動

## docker-compose ps

docker-compose によるDockerコンテナの起動状況を確認する？


## docker inspect

コンテナ・イメージ・タスクの低レベルのイメージを表示

# ◎ Dockerfile とは ◎
**通常**
1. 何らかのDockerイメージからコンテナを作成
2. コンテナ内でソフトをインストール＆環境変数の設定 etc…
**トラブル**
2の作業をどうやって引継ぐ？どうやって他の開発環境でも同様のものを作り上げる？
**アイデア**
2の一連の作業をDockerfileという形でコード化してしまおう！

# ◎ Docker Compose とは ◎
**通常**
1. 1つのサービスにつき、複数のDockerイメージから複数のDockerコンテナを作成
2. コンテナ内でポートフォワーディング＆ネットワークの設定 etc…

**トラブル**
2の作業をどうやって引継ぐ？どうやって他の開発環境でも同様のものを作り上げる？

**アイデア**
2の一連の作業を docker-compose.yml という形でコード化してしまおう！

# 参考文献

Docker公式リファレンス
さくらのナレッジ Docker入門

# おわりに
この記事は1年前に自分向けに書いたDockerの忘備録をqiitaに移植したものです。
{other_than: 1} のエラーメッセージを日本語にする

#はじめに

![スクリーンショット 2021-06-18 12.45.08（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264460/26b08346-4441-b464-112b-042f87cbd6ea.png)


このような形でエラーメッセージを日本語化しました。

しかし、職業の所はActiveHashをしており、エラーメッセージが「1以外の値にして下さい」と、ユーザーからは何を言ってるか分からない日本語になっています。

ここをどうやって直したかをアウトプットしたいと思います。

###前提

- `gem 'rails-i18n'`がインストールされている状況

- config>locals>devise.ja.ymlに日本語メッセージを記述してある状況

#試した事

ActiveHashし、id:1は選択できないようにバリデーションをかけています。

```app>models>profession.rb
class Profession < ActiveHash::Base
  self.data = [
    { id: 1, name: '--' },
    { id: 2, name: '小学校教諭' },
    { id: 3, name: '中学校教諭' },
    { id: 4, name: '高校教諭' },
    { id: 5, name: '特別支援学校教諭' },
  ] 
    include ActiveHash::Associations
    has_many :users
end
```

```user.rb
class User < ApplicationRecord
  extend ActiveHash::Associations::ActiveRecordExtensions
  belongs_to :profession

  with_options presence: true do
    #他バリデーション省略
    validates :profession_id, numericality: { other_than: 1}
  end
end
```

**`numericality: { other_than: 1}`**このバリデーションを記述しました。

試した事は`devise.ja.yml`内に記述しました。

```devise.ja.yml
other_than: 'を選択して下さい'
```

表示は変わらずでした。

他にも`ja.yml`に同じように記述しましたが結果はできず。。。

#結論

調べた所、直接モデルに記述できる事を知りました。

```user.rb
validates :profession_id, numericality: { other_than: 1, message: 'を選択して下さい'}
```

![スクリーンショット 2021-06-18 15.42.00（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1264460/74acb41d-1a9b-72e0-b3b1-0b9e417bc640.png)

このように選ばなかったら日本語でエラーメッセージを出す事ができました。

#参考

https://railsguides.jp/active_record_validations.html#message
WSL2上への OpenFOAM-v1806 の インストール

# はじめに
OpenFOAMによる熱流体解析 入門と初級 を受講することになりましたので、Windows10 PC上の`WSL2`へ OpenFOAM-v1806 をインストールします。
# Microsoft Store
![20210618a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538241/aad8656d-d55c-62ba-9349-1b2aa5b047cf.png)
`Ubuntu 20`の方は既に使用していますので、OpenFOAM用に18の方をインストールしました。
![20210618b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538241/f1d7e26e-2498-eb95-fd8d-84e0a6897c86.png)
お約束である`apt update`と`apt upgrade`も実施しています。
![20210618c.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538241/55b45c92-8c40-afae-2e7c-aa2df7e4e192.png)
Ubuntuが２つ並んでいます。
# OpenFOAM
こちらのガイドに沿ってインストールします。
[openfoam-installation-on-windows-10](https://www.openfoam.com/download/openfoam-installation-on-windows-10)
`OpenFOAM-v1806`が無いので、代わりに`OpenFOAM-v1812`をダウンロードします。
[OpenFOAM-v1812-windows10.tgz](https://sourceforge.net/projects/openfoam/files/v1812/OpenFOAM-v1812-windows10.tgz/download)

```cmd.exe
superrino130@DESKTOP-FFECJ57:/mnt/c/Users/baka/tutorial/cavity$ touch cavity.foam
superrino130@DESKTOP-FFECJ57:/mnt/c/Users/baka/tutorial/cavity$ ls
0  0.1  0.2  0.3  0.4  0.5  cavity.foam  constant  system
```
`.foam`ファイルまで来ました。
# ParaView
こちらの記事に沿ってインストールします。
[ubuntu18.04LTSでターミナルからParaviewを起動する](https://qiita.com/smae/items/47b4f10d1d9edf0c2a80)
今回は、`ParaView-5.9.1-MPI-Linux-Python3.8-64bit.tar.gz`を使用します。
[ParaView-5.9.1-MPI-Linux-Python3.8-64bit.tar.gz](https://www.paraview.org/paraview-downloads/download.php?submit=Download&version=v5.9&type=binary&os=Linux&downloadFile=ParaView-5.9.1-MPI-Linux-Python3.8-64bit.tar.gz)

```cmd.exe
superrino130@DESKTOP-FFECJ57:~$ paraview
/usr/local/ParaView-5.9.1-MPI-Linux-Python3.8-64bit/bin/paraview-real: error while loading shared libraries: libXcursor.so.1: cannot open shared object file: No such file or directory
```
ぐぬぬ

```apt.install
sudo apt install paraview
```
天下の宝刀`apt`さんにお願いしました。
![20210618e.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538241/9666f39f-70c9-2879-92b7-367e17b08e0a.png)
はい、paraviewのバージョンが`5.4.1`とやや古いものの表示に成功。
# まとめ
+ OpenCAE さんありがとう
+ OpenFOAM さんありがとう
+ ParaView さんありがとう
+ apt さんありがとう
AWS EC2 サーバー構築(Git編)

## 概要
サーバーへデプロイする方法は本当にたくさんありますが、とりあえずGitを使ったシンプルな形でのデプロイの方法をご紹介します。
ちなみに、この方法はAWSに全く依存していません。（良くも悪くもねw）

## 手順

### 1. Gitインストール
Gitをサーバーにインストールします。

```bash
$ sudo dnf install -y git

$ git version
git version 2.40.1
```

### 2. キーペアの作成及びGithubへ登録
#### 2-1. 秘密鍵を置く場所に移動

```sh
cd ~/.ssh
```

#### 2-2. 秘密鍵作成

```sh
ssh-keygen -t rsa -b 4096
```

色々聞かれますが、こだわりなければ全部`Enter`でOKです。

秘密鍵の権限も制限しておきましょう。

```sh
chmod 600 id_rsa
```

#### 2-3. 公開鍵の中身をコピー

```sh
cat ~/.ssh/id_rsa.pub
```

#### 2-4. Githubのリポジトリに公開鍵を登録する
Githubにアクセスして、リポジトリを開きます。
メニューにある「Setting」を選択してください。

「Add deploy key」をクリックします。

![スクリーンショット 2021-06-18 14.32.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290840/74944896-dc81-c986-2334-990890597b22.png)

2-3.でコピーした公開鍵の中身を貼り付けます。
「Title」はわかりやすい名前を付けておくと良いです。

![スクリーンショット 2021-06-18 14.39.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/290840/ff93279a-e50e-31ba-7769-5a7db57335fd.png)

#### 2-5. サーバーから接続確認

```sh
ssh -T git@github.com
```

`You've successfully authenticated`というメッセージが確認できたらOKです。

### 3. リポジトリをクローン
```sh
# テストディレクトリを作成して、権限と所有者を変更します。
sudo chown ec2-user:ec2-user /srv
cd /srv
git init
git clone git@github.com:test/test-repository.git
```

### おまけ
毎回手動でSSH接続してデプロイするなんて面倒なそこのアナタへ

シェルスクリプトを用意して、シェルの実行だけでデプロイできるようにしておきましょう。

#### 構成
ローカル環境に以下の２つのシェルファイルを用意します。
`deploy.sh`はローカルで実行するシェルファイル、`exec.sh`はリモートサーバーで実行するシェルファイルになります。

```
.
├── deploy.sh
└── exec.sh
```

#### deploy.sh
SSH接続して、接続先で`exec.sh`を実行するようになります。

```sh:deploy.sh
#!/bin/sh

HOST=XXX.XXX.XXX.XXX
USER=ec2-user
PRIVATE_KEY="~/.ssh/private.pem"

ssh -i ${PRIVATE_KEY} ${USER}@${HOST} < exec.sh
```

#### exec.sh
リモートサーバーで実行したいことを書いちゃってください。
今回は`git pull`だけ行うようにしています。

```sh:exec.sh
# git pull
cd /test/test-repository
git checkout master
git pull origin master
```

#### 実行
ローカルから`deploy.sh`を実行するだけです。

```sh
sh deploy.sh
```
SQL コマンド　備忘録

自分用にまとめ

# SQLの種類
- DDL
    - データ定義言語。
    - データを格納する入れ物であるDBやテーブルなどを製作したり削除したりする。
- DML
    - データ操作言語。
    - テーブルの行を検索したり変更したりする。
- DCL
    - データ制御言語。
    - データベースに対して行った変更を確定したり、取り消したりする。その他RDBMSのユーザがDBにあるものを操作する権限の設定も行う

## 記述ルール
- SQL文はセミコロン（;）で終わる
- キーワードの大文字小文字は区別させない
- 文字列と日付の定数はシングルクォーテーションで囲む。数値の定数は囲まない
- 単語の間を半角スペース、または改行で区切る

## DDL
### CREATE
テーブルの作成

```sql:例
CREATE TABLE テーブル名 （ 列名　データ型, ...）;

/* 作成したテーブルの確認 */
SHOW TABLES;
```

### ALTER
テーブル変更

```sql:例
AlTER TABLE テーブル名 変更操作;

/* よく使う変更操作 */
ADD （列名　データ型）　　--列を追加
ADD 制約　--制約を追加
MODIFY 列名　データ型　--データ型や制約を変更
DROP COLUM 列名 --列を削除
DROP 制約　--制約を削除
```

### DROP
テーブルの削除

```sql:例
DROP TABLE テーブル名;
```

## DML
### SELECT
データの検索

```sql:例
SELECT 列名[表示用列名]　FROM テーブル名;　--テーブルから列を抜き出す。表示用の列名は省略可能
SELECT * FROM テーブル名;　--テーブルから全て選んで表示する

/* 条件指定 */
SELECT 列名 FROM テーブル名 WHERE 条件式;

/* 重複を排除 */
SELECT DISTINCT 列名 FROM テーブル名;

/* ソート */
ORDER BY 列名 --昇順
ORDER BY 列名 DESC --降順

/* よく使う */
列名 IS NULL --空欄を選択
列名 IS NOT NULL --空欄以外を選択
列名 IS BETWEEN 値 AND 値 --2つの値の間に含む
列名 IS IN (値, 値, ...) --列挙した中のどれかと一致する
```
### UPDATE
データの更新

```sql:例
/* 条件と一致する列の値を更新 */
UPDATE テーブル名 SET 列名 = 値 WHERE 条件式;
```

### INSERT
データの挿入


```sql:例
/* 行をテーブルの一番下に挿入する */
INSERT INTO テーブル名 VALUES （値, 値, ...）;
/* 列名を指定して行を挿入 */
INSERT INTO テーブル名 （列名, 列名, ...） VALUES （値, 値, ...）;
```

### DELETE
データの削除

```sql:例
/* 条件と一致する列の値を削除 */
DELETE FROM テーブル名 WHERE 条件式;
```
## DCL
### COMMIT
確定

```sql:例
COMMIT;
```

### ROLLBACK
取り消し

```sql:例
ROLLBACK;
```

### セーブポイント

```sql:例
/* 新しいセーブポイントの設定 */
SAVEPOINT 新規セーブポイント名
/* 指定したセーブポイントまで戻る */
ROLLBACK TO セーブポイント名
```
std_srvs/Empty型のサービス・クライアント作成 (Python/C++)

std_service/Empty型のサービス・クライアントのサンプルプログラムを記載します。

- サーバー(Python)

```python:empty_service_server.py
#!/usr/bin/env python

from std_srvs.srv import Empty
from std_srvs.srv import EmptyResponse
import rospy

def handle(req):
    print("service call")
    return EmptyResponse()

def empty_server():
    rospy.init_node('empty_service_server')
    s = rospy.Service('empty_service', Empty, handle)
    rospy.spin()

if __name__ == "__main__":
    empty_server()
```

- クライアント(Python)

```python:empty_service_client.py
#!/usr/bin/env python

from std_srvs.srv import Empty
import rospy

rospy.init_node('empty_service_client')

service_name = 'empty_service'
rospy.wait_for_service(service_name)
empty_service = rospy.ServiceProxy(service_name, Empty)

r = rospy.Rate(20)

while not rospy.is_shutdown() :
  print("call")
  empty_service()
  r.sleep()
```

- サーバー(C++)

```c++:empty_service_server.cpp
#include "ros/ros.h"
#include "std_srvs/Empty.h"
#include "std_srvs/EmptyResponse.h"

bool handle(std_srvs::Empty::Request &req, std_srvs::Empty::Response &res)
{
  std::cout << "service call" << std::endl;
  return true;
}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "empty_service_server");
  ros::NodeHandle n;

  ros::ServiceServer service = n.advertiseService("empty_service", handle);

  ros::spin();
}
```

- クライアント(C++)

```c++:empty_service_client.cpp
#include "ros/ros.h"
#include "std_srvs/Empty.h"

int main(int argc, char **argv)
{
  ros::init(argc, argv, "empty_service_client");

  ros::NodeHandle n;

  ros::ServiceClient client = n.serviceClient<std_srvs::Empty>("empty_service");

  ros::Rate r(10);

  std_srvs::Empty srv;
  
  while (ros::ok())
  {
    client.call(srv);   
    r.sleep();
  }

  return 0;
}
```

- 上記c++プログラムの2つのcmakeファイル

```cmake:CMakeLists.txt
cmake_minimum_required(VERSION 3.0.2)
project(empty_service)

find_package(catkin REQUIRED roscpp std_srvs)

catkin_package(
)

include_directories(
  ${catkin_INCLUDE_DIRS}
)

add_executable(empty_service_server src/empty_service_server.cpp)
target_link_libraries(empty_service_server
  ${catkin_LIBRARIES}
)

add_executable(empty_service_client src/empty_service_client.cpp)
target_link_libraries(empty_service_client
  ${catkin_LIBRARIES}
)
```
watchをaliasに対して実行する


aliasを設定していると、ついwatchでそのまま使ってエラーになることが多いんじゃなかろうか。

```shell
$ watch k top pod
/bin/sh: 1: wget: not found
Every 2.0s: k top pod                                                                                                              oss: Fri Jun 18 15:10:31 2021

sh: 1: k: not found
```

そういう時は以下をaliasに設定する。

```shell
$ alias watch="watch "
```
実行すると以下のように取れる。

```
$ watch k top pod
Every 2.0s: kubectl top pod                                                                                                        oss: Fri Jun 18 15:12:08 2021

NAME                        CPU(cores)   MEMORY(bytes)
centos                      4m           1Mi
```

普段から使うなら.bashrcに書いておけばよいと思われ。
kubectl、-wが効かないものとかもあるので、kubectl使ってる方とかにはオススメ。
[ActiveRecord]dependentオプションの使い分け方

# dependentオプションとは？
モデルのレコードがdestroyされたときの、関係付けされたモデルに対する挙動を定義するものです。

## :destroy
関連付けられたモデルに対してもdestroyを実行することが出来る。

```ruby
class User < ApplicationRecord
  # Userが削除された際に、それに関連付いているarticlesも削除される
  has_many :articles, dependent: :destroy
end
```

### 使い分け
- 削除時に、指定したモデルのレコードも同時に削除したい場合。
- `ActiveRecoreを介して`destroyに関連した処理も実行したい場合。
- 速度・サーバ負荷を気にする必要がない場合。

## :delete/:delete_all
destroy時に指定したモデルに対して、`ActiveRecordを介せずに直接SQLクエリ(DELETE)が実行`されます。

```ruby
class User < ApplicationRecord
  # has_manyの場合は:delete_allと記述
  has_many :articles, dependent: :delete_all

  # belobgs_to, has_oneの場合は:deleteと記述
  belongs_to :group, dependent: :delete
end
```


### 使い分け
- 削除時に、指定したモデルのレコードも削除したい。
- `ActiveRecordを介さず`、destroyに関連したフックを実行したくない。
- 速度・サーバ負荷が気になる場合(高速処理が可能なため)。

## :nullify
destroy時に指定したモデルの外部キーにnullが入れる。
※UPDATEのみなのでDELETEより負荷は軽いです。

```ruby
class User < ApplicationRecord
  # Userが削除された際に、それに関連付いているarticlesのuser_idがnilに更新される
  has_many :articles, dependent: :nullify
end
```

### 使い分け
- 削除されたレコードは残しておきたいが、参照先のない外部キーは防ぎたい場合。
- レコードが増え続けても問題がない場合。
- 自分のタイミングで削除を行いたい場合。
- DELETEする負荷に耐えられない場合。

## :restrict_with_exception
親レコードを削除するとき、子レコードがある場合はActiveRecord::DeleteRestrictionErrorを発生させる。
そのため、子レコードを先に削除しておく実装が必要になる。

```ruby
class Category < ApplicationRecord
  # itemを削除してからでないと、categoryを削除できない
  has_many :items, dependent: :restrict_with_exception
end
```

### 使い分け
- 子要素があるときに削除されては困る場合。

## :restrict_with_error
親レコードを削除するとき、子レコードがある場合は削除できず、親レコードにエラー情報が付加される。

```ruby
class Category < ApplicationRecord
  # itemが関連付いている場合は、categoryを削除できずにエラー情報が付加される
  has_many :items, dependent: :restrict_with_exception
end
```

### 使い分け
- 子要素があるときに削除されては困る場合。

# 参考
[【Rails】ActiveRecordの:dependent使い分けまとめ【:destroy, :delete, :nullify】]
(https://dorarep.page/articles/rails-dependent)
Error 409: Requested entity already exists, alreadyExists

## 解決方法
project_id にユニークなものを指定する
(絶対に正しいかはわからない)

## 起こったこと

```bash
$ terraform apply
.
.
.
google_project.gcp_project: Creating...
╷
│ Error: error creating project {PROJECT_ID} (PROJECT_ID): googleapi: Error 409: Requested entity already exists, alreadyExists. If you received a 403 error, make sure you have the `roles/resourcemanager.projectCreator` permission
│ 
│   with google_project.gcp_project,
│   on main.tf line 10, in resource "google_project" "gcp_project":
│   10: resource "google_project" "gcp_project" {
│ 
```

## 検証内容
```tf:main.tf
resource "google_project" "gcp_project" {
  name                = var.gcp_project
  project_id          = var.gcp_project
  # billing_account     = var.billing_id
  auto_create_network = false
}
```
最初、billing_account なしで project を作成しようとしてエラーが出て、指定後、再度 apply したらエラーが発生。
という流れだったので、最初に失敗したのが原因だと思って調べていた。
だが、検証するとエラーの発生と作成試行回数に関係はなさそう。
それよりも、project_id のユニークさでエラーの有無が変わることに気づく。
ブラウザの console に行って試してみると、やっぱりユニーク性で引っかかっていそうな気配がした。
ユニークなidでプロジェクト作成を行うようにしてから、`Error 409: Requested entity already exists, alreadyExists` このエラーは出なくなった。
【Xcode 12.5】ARCHS[@]: unbound variable ビルドできない

M1 Macを購入して会社で開発中のプロジェクトがビルドできなくなった。

`ARCHS[@]: unbound variable`
このようなエラーでビルドができない。
色々調べてみるが、なかなかエラーが取れないので、ビルドできた方法をまとめます。

### 1. PROJECTで`arm64`を設定する
PROJECT - Build Settings - Excluded Architectures に`Any iOS Simulator SDK`を追加し、`arm64`を追加する。
![TerraInspection_xcodeproj.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1628002/6d7d6ef0-2bf2-c57a-5820-34acd9fcb9f0.png)

### 2. Targetでarm64を設定する
Target - Build Settings - Excluded Architectures に`Any iOS Simulator SDK`を追加し、`arm64`を追加する。

### 3. Podsでarm64を設定する
Pods - Build Settings - Excluded Architectures に`Any iOS Simulator SDK`を追加し、`arm64`を追加する。

３番は必要ないアプリもあるかもしれないが、開発中のものでは必要だったため追加することで、ビルドできた。
そして、実機を接続しないとビルドできないというのも、Xcode12.5では解決されていた。

### それでもビルドできない場合
1. Podsのフォルダを削除
2. Podfile.lockを削除
3. `pod install` で解決できると思います。
【GitHub】.gitignoreを言語・FWごとのテンプレートで自動生成

#はじめに
タイトルの通りGithubでリポジトリの新規作成時に<br>.gitignoreファイルの自動生成ができます。

しかも、言語・FWごとにテンプレートがあるので<br>自分で定型文を調べて書かなくてもいいので便利です。

#作成手順
1. 新規リポジトリ作成のNewボタンをクリック
1. Add .gitignoreのチェックボックスを選択
1. .gitignore templateのドロップボックスから<br>言語・FWを選択
1. Create repositoryボタンをクリックしてリポジトリの新規作成

---
1.
![gitignore_tips1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1656973/e04cd04f-7e3c-0664-077f-bcff17036c18.png)

---
2.3.4.
![gitignore_tips3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1656973/da95e067-754d-8621-e657-7f0c84a41c59.png)


#結果
Javaのgradleプロジェクト向けのテンプレートでは
↓のような.gitignoreファイルが作成されました。

``` .gitignore
.gradle
/build/

# Ignore Gradle GUI config
gradle-app.setting

# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
!gradle-wrapper.jar

# Cache of project
.gradletasknamecache

# # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
# gradle/wrapper/gradle-wrapper.properties
```

#最後に

Javaのgradleプロジェクト以外にもたくさんの言語・FW向けのテンプレートがあるので
.gitignoreに記載するファイルを自分で調べる前に
githubでリポジトリを作る際に探してみてください。

最後まで読んでいただきましてありがとうございました。
[spreadsheet]オンラインスプレッドシートLuckysheet

# 概要

この記事は[ここ](https://zhuanlan.zhihu.com/p/266775529)を参照し、書きました。

# Luckysheetとは

>Luckysheet is an online spreadsheet like excel that is powerful, simple to configure, and completely open source.

[github](https://github.com/mengshukeji/Luckysheet)

オンラインスプレッドシートを実現するためのフロントエンドプラグインですね。

# かっこいいと思う機能

* マトリックス計算

![スクリーンショット 2021-06-18 14.43.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1184340/8b776299-e8a4-8410-b256-cfd301ffc60f.png)

* 画面キャプチャ(選定されている部分をキャプチャしダウンロードできる)

![スクリーンショット 2021-06-18 14.44.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1184340/9b1df932-18fd-2a2f-9eb6-6341e5f92859.png)


* データの形式の変換は簡単(csv→jsonなど、重複のデータの削除も簡単)

![スクリーンショット 2021-06-18 14.47.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1184340/1dd01368-9ab1-3405-b7b2-62267900ee35.png)

* excel、csv、txtなどのエクスポート(パスワード付きのエクスポートもできるらしい)

* データのバリデーションチェック

![スクリーンショット 2021-06-18 14.54.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1184340/3c3b2924-1a6f-d5e1-9364-b1afefaf64b6.png)


* セル内の改行など（一個セル内複数のスタイルの設定もできる）

![スクリーンショット 2021-06-18 14.48.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1184340/55138ac8-8d2b-ee1e-66d9-c4de68fbba05.png)

* 内部デフォルトの関数
　

```
SUMIFS, AVERAGEIFS, SUMIF, SUM
CONCATENATE, REGEXMATCH, MID
DATEVALUE, DATEDIF, NOW, WEEKDAY
PV, FV, IRR, NPV
IF, AND, OR, IFERROR
VLOOKUP, HLOOkUP, INDIRECT, OFFSET
SORT,FILTER,UNIQUE,RANDARRAY,SEQUENCE
and カスタマイズ関数
```

# ライセンス

MIT

# Demo

[demo](https://mengshukeji.github.io/LuckysheetDemo/)