OmniAICreator/Qiita-1.07M huggingface dataset:
Microsoft Teamsで使えるプロジェクト管理アプリ一覧

#はじめに
Microsoft Teams はさまざまな外部サービスと接続することができます。
2019年7月時点では、約260種類のアプリを使うことができます。
今回はその中でもプロジェクト管理で使えるアプリをまとめてみました。
他のアプリについては近日公開予定です。(余裕があれば。笑)


#プロジェクト管理アプリ一覧
##Trello
カンバン方式でタスクを管理できるサービス。
使い方はとてもシンプルで、タスク内容が書かれた付箋をボードに貼り付けるようにチームで共有する。
Chrome の拡張機能が充実していて、カレンダーやタイムライン形式で表示してくれるものもある。
無料で基本的な機能を使うことができる。ただし、無料プランではプロジェクトが最大10までしか利用できない。
また、無料だとプラグインは1つしか使えないという制限がある。
https://trello.com/home

##Jira Cloud
アジャイルチームの利用実績No.1のソフトウェア開発ツール。
アジャイル開発で便利な機能が多く実装されていて、それぞれのチームに合わせて
機能をカスタマイズすることが可能。この製品を提供している Atlassian は Trelloなどの
プロジェクトで使えるさまざまなツールを提供していて、それらの機能をJira Cloudと連携させることができる。
https://www.atlassian.com/ja/software/jira

##Wrike
あらゆる業界のチームの活躍を導く、共同作業とプロジェクト管理のためのクラウドベースソフトウェア。
プロジェクトのレポートやステータスがリアルタイムで可視化でき、ガントチャートでリソースを効率よく活用することが可能。
また、ワークフローをプロセスに合わせてカスタマイズすることもできる。
5ユーザーまでのチームであれば無料で使うことができる。
https://www.wrike.com/ja/

##Asana
リスト形式とカンバン方式の好きな方でプロジェクトを管理することが可能。
チームメンバーとチャットができる機能がある。
ワークロード機能では、メンバーの仕事量を可視化することができる。
15ユーザーまでであれば無料で利用が可能。個人的に見た目が好き。
https://asana.com/ja

##Confluence Cloud
ミーティング議事録や製品要件から、マーケティング計画や人事ポリシーなどのページを作成できるツール。
作成したページをツリー構造により整理したり、ページに対してコメントでフィードバックを添えることができる。
https://www.atlassian.com/ja/software/confluence/features

##MeisterTask
直感的にプロジェクトやタスクを管理できるツール。
マインドマッピングアプリの MindMeister とシームレスに統合することができ、
ブレーンストーミングを行うのに役立つ。
https://www.meistertask.com/ja

##Wunderlist
Microsoft が提供しているタスク管理アプリケーション。
もうすぐサービスが終了するらしい。
https://www.wunderlist.com/ja/

##Jira Server
Jira Cloud のオンプレ版
https://www.atlassian.com/ja/software/jira/download

##Planner
Microsoft が提供しているタスク管理ツール。
Office 365ライセンスがあれば利用できる。Office 365 グループと紐付けることが可能。
https://products.office.com/ja-jp/business/task-management-software

##Jell
チームやメンバーの目標や作業を設定し、日々のパフォーマンスやステータスを定量的に評価するツール。
https://jell.com/

##Jibble
チームの作業時間や出席状況を管理できるツール。
給与計算を行うために使用する。また、レポートで作業生産性を監視できる。
https://www.jibble.io/

##Ruum
SAPが提供するプロジェクト管理ツール。
https://www.ruumapp.com/

##Aha!
世界No.1のロードマップソフトウェア。
6つのテンプレートからロードマップを選択することができる。
業務実績を可視化できるので、マーケティング活動で活躍するツール。
https://www.aha.io/

##Perfony
時間を効率良く活用することを目指しているツール。
会議の議事録を短時間で作成できる機能があるらしい。
https://www.perfony.com/fr/

##Suttna
非同期通信を使用してタスクを自動化することで、リモートチームの管理を支援するサービス。
チームメンバーの予定や質問などを管理してくれる。
https://suttna.com/

##Pivotal Tracker
アジャイルチームに適しているプロジェクト管理ツール。
https://pivotal.io/jp/tracker

##Beeminder
目標を設定し、その目標から軌道から外れた場合にお金を支払うサービス。
目標に向かってまっすぐ進めば無料で利用できる変わったサービス。
例えばダイエットすることを目標に設定した場合、予定通りに体重が落ちないと、
その分お金を支払わなければいけない。
目標を設定してもなかなか達成できない人におすすめのサービス。
https://www.beeminder.com/

##Axure Share
https://share.axure.com/

##Projectplace
カンバン方式とガントチャートが利用できる管理ツール。
チャット機能やカレンダー機能、オンライン会議やプロジェクポートフォリオ機能など、
様々な機能が充実している。
https://www.projectplace.com/

#さいごに
途中から全部が同じツールに見えてきたり、調べても情報がほとんどないようなツールもあってなかなか大変でした。
Trello や Asana、Plannerなどは馴染み深い人も多いと思いますが、これだけたくさんのツールがTeamsと連携できるので、
ぜひ色々活用してみて欲しいです。
VirtualBox上のゲストOS(CentOS)から外部ネットワークにつなぐ設定メモ(Windows10編)



自分用にメモを残しておきます

##環境
・Windows10 (64bit)
・VirtualBox 6.0
・CentOS-7-Mnimal

##手順

①VirtualBoxの[新規]からOS起動の直前までポチポチと設定をする。
　(私はいつも適当にデフォルトでOK連打しています)
※さっそくOSを起動したいところですがその前に少し設定をします！

②VirtualBoxの[環境設定]→[ネットワーク]で[NATネットワーク]のページまで来たら右上のプラスマーク(新しいNATネットワークを追加する)をクリックする。

③ホストOSのアダプターの設定から「VirtualBox Host-Only Ethernet Adapter」のIPアドレスとサブネットマスクを確認する。
初期値はIPアドレスが「192.168.56.1」、サブネットマスクが「255.255.255.0」らしい。

④VirtualBoxの右クリックメニューで[設定]→[ネットワーク]→[アダプター2]を選ぶ。
[ネットワークアダプタを有効化]にチェックを入れ、ドロップダウンメニューから[ホストオンリーアダプター]を選びOKボタンを押す。

⑤ここで初めてOSを起動！ポチポチと各種設定をしていきます。

⑥OSが立ち上がったら下記フォルダ内のifcfg-enp0s3とifcfg-enp0s8を編集する。

```
# cd /etc/sysconfig/network-scripts
```
```
# vi ifcfg-enp0s3
(略)
ONBOOT=yes 
BOOTPROTO=dhcp
(略)
```
```
# vi ifcfg-enp0s8
(略)
ONBOOT=yes 
BOOTPROTO=static
IPADDR=192.168.56.1 
NETMASK=255.255.255.0
(略)
```
⑥設定変更の反映

```
# systemctl restart network
```
##確認手順
①ホストOSのコマンドプロンプトから192.168.56.1 あてにpingを飛ばす。
　これがOKならホストOS→ゲストOS方向の通信はOKなはず。

```
> ping 192.168.56.1
```
②ゲストOSから外部サイトにpingを飛ばす。
　これがOKならゲストOS→ホストOS→外部ネットワーク方向の通信はOK。たぶん。

```
# ping google.com
```
どちらも正常に応答があればOK！
もしこの記事が誰かの助けになるのであれば嬉しいです…

##参考にさせていただいたページ
・[Windows上でVirtualBoxにCentOSを入れてネットワーク設定で詰まった話 - dorivenの日記](http://doriven.hatenablog.com/entry/2014/04/25/184930)
・[VirtualBox上のCentOS 7のネットワーク設定で死ぬほどつまづいた - Python改め、PHPを頑張るブログ](https://python-konbu.hateblo.jp/entry/2018/03/27/033253)
YAMAHA RTシリーズのACLをテストするPERL

自宅でyamahaのルータを使っていて、擬似FWとしてACLを入れている。通信の許可・拒否をシミュレートするPERLを作成した。

- 対応するACLの書式は一部限定となっている
- 会社のCiscoACLに対応したものとしたいが、まず初歩として作成


# 結果サンプル
検証パターン -> ヒットしたACLの番号：ACLの内容

```
lan2 in 192.168.0.1 192.168.0.1 tcp netbios_ns 99999 -> 130010:pass 192.168.0.0/16 192.168.0.0/16 tcp,udp 445,netbios_ns-netbios_ssn *
lan2 in  8.8.8.8 192.168.0.120 tcp * 2222 -> 160230:pass-log * 192.168.0.120 tcp * 2222
lan2 out 192.168.0.120 8.8.8.8 tcp 2222 * -> 260230:pass-log 192.168.0.120 * tcp 2222 *
lan2 in  8.8.8.8 192.168.0.1 tcp * 2222 -> 919999:reject * * * * *
lan2 out 192.168.0.1 8.8.8.8 tcp 2222 * -> 929999:pass * * * * *
```

# ソース
## Packege
 オブジェクト志向にはなっていません。試験が足りていません。
[Package](
https://github.com/harasakih/bin/blob/release/ip_ymhcommon.pl)

## ドライバ
 コンフィグファイル名を引数で、標準入力からテストパターンを入力してチェックする。

```bash:
./ymh_ut01_sotu.pl /path/to/yamaha.cf < test-pattern.txt
```

```perl:ymh_ut01_sotu.pl 
#!/usr/bin/perl
use strict;
require './ip_ymhcommon.pl';

$YmhCfg::Cfgfname = $ARGV[0];
die "die at Conf_Read()\n" if (YmhCfg::Conf_Read() == $YmhCfg::RtnERR);
$YmhCfg::DBG1 = 0;
$YmhCfg::DBG4 = 0;

# YmhCfg::dump_Mnemonic();
# YmhCfg::dump_SecFilter_all();
# YmhCfg::dump_SecFilter("lan2", "in");
# YmhCfg::dump_SecFilter("lan2", "out");

YmhCfg::checkmain();
```

```yamaha.cfg
ip lan2 secure filter in 130010 130011 139999 160010 160011 160020 160021 160030 160031 160041 160051 160210 160220 160230 160240 160250 160260 160270 160280 160290 160300 170010 170011 169999 919999
ip lan2 secure filter out 230010 230011 239999 260210 260220 260230 260240 260250 260260 260270 260280 260290 260300 270010 270011 929999

p filter 130010 pass 192.168.0.0/16 192.168.0.0/16 tcp,udp 445,netbios_ns-netbios_ssn *
ip filter 130011 pass 192.168.0.0/16 192.168.0.0/16 tcp,udp * 445,netbios_ns-netbios_ssn
ip filter 139999 pass 192.168.0.0/16 192.168.0.0/16 * * *
ip filter 160010 pass * * icmp
ip filter 160011 pass * * established
ip
....
ip filter 160210 pass-log * 192.168.0.103 tcp * https
ip filter 160220 pass-log * 192.168.0.120 tcp * www,8080
ip filter 160230 pass-log * 192.168.0.120 tcp * 2222
....
```

```test-pattern.txt
$ cat ymh_test01_ip01.txt 
# PORT FROM
lan2 in 192.168.0.1 192.168.0.1 tcp netbios_ns 99999
# PORT TO
lan2 in 192.168.0.1 192.168.0.1 udp 99999 netbios_ssn
```

# 検証パターンの書式
書式：IF DIRECTION FROM-IP TO-IP PROTOCOL FROM-PORT TO-PORT
IFとDIRECTIONは、コンフィグのこの箇所を一致します。

- IF:ip [lan2] secure filter in 
- DIRECTION:ip lan2 secure filter [in]
Ubuntu18.04にgoogle chromeをインストールする

#Ubuntu18.04にgoogle chromeをインストールする
##以下のコマンドを順に実行する
```bash:
sudo sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list'
sudo wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
sudo apt update
sudo apt install google-chrome-stable
```

####ネット上にあり失敗したコマンド
 /etc/apt/sources.list.d/google.list となっている部分が肝でした...

```bash:
sudo sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
sudo wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
sudo apt update
sudo apt install google-chrome-stable
```
####失敗した結果
```bash:
sudo apt update
```
↓
W: Target xxx Packages … is configured multiple times
のようなエラーがいっぱいに。

####失敗した原因
コマンド

sudo sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
が諸悪の根源。
google chromeのインストール後、
/etc/apt/sources.list.d/にgoogle-chrome.listが自動生成されるため
既存のgoogle.listと２重に読み込まれ、上記のエラーが生起される。

###解決法（失敗してしまった方へ）
インストール際に、google.listがなければsudo apt update しても
sudo apt install google-chrome-stable
でパッケージを見つけられないため
'google-chrome.list'として作成し最終的に上書きされるようにしてしまう。

```bash:
sudo sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google-chrome.list'
```

もし、すでに上記結果に示したようなエラーが出ていて困っている場合には、
/etc/apt/sources.list.d/にある、google.listをgoogle.list.saveにリネームして
sudo apt update すればなくなるかと思います。
(自分は、なくなったことを確認してから削除してしまいましたが...)
ファッションデータベースをTensorboardで次元圧縮（t-SNE）してtriplet lossの評価をしてみる

# 動機

ファッションサーチファンネル（ファッション画像検索エンジン）
https://qiita.com/mokoenator/items/6807132ce59913186a9b
https://funnel-service.com

で使っているtriplet lossの評価方法がよくわからないので困ってる。

* 「ハンドメイド３枚組画像」のアンカー、ポジティブ、ネガティブの順版評価。
* 目視で・・・:scream:。

くらいしか思いつかない。

ちなみに、同一商品に対して近い値がでていればよいわけではない。
検索したときの画像の順番がただしいか？だと思う。


# Tensorboardで特徴量を次元圧縮して可視化

t-sneで２次元に圧縮してみた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185049/21a6fbb5-ea74-645d-4733-225bf24db526.png)


こんな感じなる。

３次元は感動するけど見にくい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185049/4cce464f-43c1-2d7c-6b8b-80ea303dc9eb.png)



https://experiments.withgoogle.com/t-sne-map
こんな感じで見れるのが理想なんですが・・・

# 現実的な評価方法・・

画像の集まりから毎回同じ画像を選らんで、それをクエリ画像にして画像の集まりに検索して、HTMLにスコア順出力。
これが一番安心感があった。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185049/473b10bd-48af-cd74-cdf3-5295d08442d8.png)


行ごとに・・・一番左がクエリで2個目から検索結果を示している。
これって、ファンネルにバルク検索してるようなもの。:relaxed:


# 感想

* Tensorboardの操作がショボイ。2次元でPANはできるけどチルトができない。
* グリグリするのは楽しい。
* UMAPのほうが、イケてた:relaxed:
* 正しい評価方法があれば教えてほしい。:relaxed:
Java random,  処理いろいろ

- 単純な乱数

```java
new Random().nextXxx();
new Random().nextXxx(10);
```

- 重複なし乱数リスト

```java
new Random().ints(0, Integer.MAX_VALUE)
    .distinct()
    .limit(10)
    .boxed()
    .collect(Collectors.toList())
```

- リスト乱数取得

```java
list.get(random.nextInt(list.size()));
```
System.Random を継承して、自作の乱数生成器を実装する際の注意点


# まえがき（飛ばしてOK）

筆者が昔、軽い気持ちで自作乱数生成クラスを継承で作ったら意外に奥が深いと気付かされた。
扱うのが乱数となると、間違った値がいつも出力されるわけではないので簡単には気づけない。。。

# オーバーライドする必要のあるメソッドは？

1つ目の注意点
Random.Sample() メソッドは protected virtual なので、これ一つをオーバーライドすれば
すべてのメソッドの動作を、独自の乱数に置き換えれる雰囲気を出しているが、実はSample()が使用されるのは次のメソッドだけ

* Next(int minValue, int maxValue)
* Next(int maxValue)
* NextDouble()

実は以下のメソッドでは使用されていない、なので以下のメソッドもオーバーライドする必要がある。

* Next()
* NextBytes(byte[] buffer)

# double型に変換するときの注意点

2つ目の注意点
Sample() 内などで、自作乱数生成器で一旦整数を生成し double型に変換してから出力する場合、
Sample() や NextDouble() の出力の範囲の決まりは **0.0 以上 1.0 未満** となっているので、1.0 以上を出力しないように注意する必要がある

以下手段を検証（結論はこちら→ [long型から変換する場合2](#long型から変換する場合2)）

### int型から変換する場合

* int型の範囲は、0～2147483647(負数除く)
* **割られる数**を2147483646以下にすれば、一応1.0未満で出力される。
* しかし、出力の値が飛び飛びになる。（0.999999999534339～0.999999999068677間の値が出力されない）

以下、参考に式と出力結果

| 式 | 出力結果 |
|---|---|---|
| 2147483646 / 2147483647 | 0.999999999534339 |
| 2147483645 / 2147483647 | 0.999999999068677 |
| 2147483644 / 2147483647 | 0.999999998603016 |
| 2147483643 / 2147483647 | 0.999999998137355 |



### long型から変換する場合1

単純に最大値で割ってしまうと・・・

* long型の範囲は、0～9223372036854775807(負数除く)
* double型が正確に表現できる最大値を超えるため、**割られる数**が9223372036854775296以上のときに  1.0 になってしまう。

| 式 | 出力結果 | double値(16進数) |
|---|---|---|
| 9223372036854775807 / 9223372036854775807 | 1.0 | 0x3FF0000000000000 |
| 9223372036854775806 / 9223372036854775807 | 1.0 | 0x3FF0000000000000 |
| 9223372036854775805 / 9223372036854775807 | 1.0 | 0x3FF0000000000000 |
| 略 |  |  |
| 9223372036854775297 / 9223372036854775807 | 1.0 | 0x3FF0000000000000 |
| 9223372036854775296 / 9223372036854775807 | 1.0 | 0x3FF0000000000000 |
| 9223372036854775295 / 9223372036854775807 | 1.0 | 0x3FEFFFFFFFFFFFFF |
| 9223372036854775294 / 9223372036854775807 | 1.0 | 0x3FEFFFFFFFFFFFFF |
| 略 |  |  |
| 9223372036854771201 / 9223372036854775807 | 1.0 | 0x3FEFFFFFFFFFFFFC |
| 9223372036854771200 / 9223372036854775807 | 1.0 | 0x3FEFFFFFFFFFFFFC |
| 9223372036854771199 / 9223372036854775807 | 0.999999999999999 | 0x3FEFFFFFFFFFFFFB |
| 9223372036854771198 / 9223372036854775807 | 0.999999999999999 | 0x3FEFFFFFFFFFFFFB |


### long型から変換する場合2

上記の方法を対策した場合

* double型が正確に表現できる整数の最大値(9007199254740992)を**割る数**、生成した整数(0～9007199254740991)を**割られる数**にして計算する。
* **割られる数**の最大値は 9007199254740992 にすると、結果が 1.0 になるので 一つ小さくする。
 * 生成値を 0x1F_FFFF_FFFF_FFFF でビットマスクするか、11 ビット右シフトしてから、割り算すればよい。
* 下の表の2段目、出力結果では1.0になっているが、0x3FEFFFFFFFFFFFFF なので 値としては 0.999... なのでOK
* ulong型の場合も同様にすれば可能

| 式 | 出力結果 | double値(16進数) |
|---|---|---|
| 9007199254740992 / 9007199254740992 | 1.0 | 0x3FF0000000000000 |
| 9007199254740991 / 9007199254740992 | 1.0 | 0x3FEFFFFFFFFFFFFF |
| 9007199254740990 / 9007199254740992 | 1.0 | 0x3FEFFFFFFFFFFFFE |
| 9007199254740989 / 9007199254740992 | 1.0 | 0x3FEFFFFFFFFFFFFD |
| 9007199254740988 / 9007199254740992 | 1.0 | 0x3FEFFFFFFFFFFFFC |
| 9007199254740987 / 9007199254740992 | 0.999999999999999 | 0x3FEFFFFFFFFFFFFB |
| 9007199254740986 / 9007199254740992 | 0.999999999999999 | 0x3FEFFFFFFFFFFFFA |
Pocketに保存するリンク

まず[この記事](https://qiita.com/takuhito-h/items/891f3d53ad14a1182963)にも書かれてるように、多くのSNSサービスでは、シェアボタン(ウィジェット)を設置しなくてもリンク単体で投稿ページに内容を入れた状態で遷移できたりします

例えばTwitterならこんなリンクが使えます
https://twitter.com/intent/tweet?url=http://example.com/

そこでPocketにも似たようなリンクがないか探したのですが、いくら探しても公式が提供しているボタンの方しか出てこないので仕方なく検証してみたところ、リンクを見つけたので共有しておきます

https://getpocket.com/save?url=http://example.com/
Railsで関連に更新履歴を残しつつ1件ずつ登録する

# データに履歴を残したい

データの履歴はとっても大事だなと感じ始めた昨今、履歴を残しつつ、しかし普段は最新のデータのみ参照したい。
そして、データの更新時は既存のデータを更新ではなく、新しい履歴を作成したい。

## やってみる

### Modelの設定

モデルに`Foo`と`History`があって、1対多の関係であるとする。
`Foo`には、nameという属性があるとする。
`History`には、memoという属性があり、それの履歴が保存されていくとする。

fields_forで更新したいため、`accept_nested_attributes_for`を使う。ミソなのは、`limit: 1`にしておくことだ。

```ruby
class Foo < ApplicationRecord
  has_many :histories, inverse_of: :foo
  has_one :latest_history, -> { order(created_at: :desc) }, class_name: :History

  accepts_nested_attributes_for :histories, limit: 1
  
  validates :name, presence: true
end

class History < ApplicationRecord
  belongs_to :foo

  validates :foo, presence: true
  validates :memo, presence: true
end
```

### Viewの設定

Viewでは、`fields_for`を使って表現する。1件だけを対象にしたいので、`@foo.histories.last`を使う。
なお、Viewはslimで記述、simple_formを使っている前提で書く。

```slim
= simple_form_for @foo do |f|
  = f.input :name
  = f.simple_fields_for :histories, @foo.histories.last do |fh|
    = fh.input :memo
  = f.button :submit
```

### Controllerの設定

#### 登録の場合

Controller側では、actionがnewの場合は、`@foo.histories.build`するだけでよい。

```ruby
class FoosController < ApplicationController
  # ～ 略
  def new
    @foo = Foo.new
    @foo.histories.build
  end

  def create
    @foo = Foo.new(foo_params)
    if @foo.save
      redirect_to @foo, notice: '成功'
    else
      flash.now[:alert] = '失敗'
      render :new
    end
  end
  # ～ 略

  private

    def foo_params
      params.require(:foo).permit(
        :name,
        histories_attributes: [:memo]
      )
    end
end
```

#### 更新の場合

問題はeditの時である。単純に`@foo.histories.build`するとmemoが空になる。
editの場合は、`latest_history`からデータを取ってきて、それを使ってbuildする。

```ruby
class FoosController < ApplicationController
  before_action :set_foo, only: [:show, :edit, :update, :destroy]
  # ～ 略
  def edit
    @foo.histories.build(
      # 自動で値が入るカラムを除外してbuildのパラメータとして利用する
      @foo.latest_history.attributes.except('id', 'created_at', 'updated_at')
    )
  end

  def update
    if @foo.update(foo_params)
      redirect_to @foo, notice: '成功'
    else
      flash.now[:alert] = '失敗'
      render :edit
    end
  end
  # ～ 略

  private

    def set_foo
      @foo = Foo.find params[:id]
    end

    def foo_params
      params.require(:foo).permit(
        :name,
        histories_attributes: [:memo]
      )
    end
end
```

これで、普段は最新の情報のみが閲覧できるが、フォームを開いたらメモの履歴は取れる状態となった。

## 学び

* `fields_for`の引数にオブジェクトを渡すことで、数を制限できる
* `accept_nested_attributes`には件数制限のできる`limit`オプションがある
* `ActiveSupport`にあるHashの指定キーを取り除く`except`メソッドが便利
Visual Studio 2019 のインストール

# はじめに
今更ながら、Visual Studio 2019のインストール手順をまとめていなかったのでTipsとして記事にします。

## 注意
Visual Studio 2017からライセンス認証にMicrosoftアカウントを使用するようになりました。あらかじめMicrosoftアカウントを用意しておいてください。

## Visual Studio 2019について

Visual Studio には3つのエディションがあります。
Community、Professional、Enterprise。個人的な学習範囲や小規模な開発であればCommunityの利用が推奨されます。Professional、Enterpriseは中規模・大規模なアプリケーション開発を行う際に利用します。

Professionalからはチーム開発に向けたサブオプショナルなサービスが中心になる為、割愛します。

![Screenshot_NoName_2019-7-3_23-58-12_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184939/48731887-0905-1aeb-d965-ff48d5ff94e2.png)

## VisualStudioページ

https://visualstudio.microsoft.com/ja/downloads/?utm_medium=microsoft&utm_source=docs.microsoft.com&utm_campaign=navigation+cta&utm_content=download+vs2017&rr=https%3A%2F%2Fdocs.microsoft.com%2Fja-jp%2Fvisualstudio%2Finstall%2Finstall-visual-studio%3Fview%3Dvs-2019

## インストール方法

1. Visual Studioのページから Visual Studio Community 2019の説明にある[ダウンロード]をクリックします。
![Screenshot_NoName_2019-7-4_0-2-49_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184939/585a2525-374d-93eb-47b0-6df7b656010b.png)

2. ダウンロードしたVisual Studio Installerを実行します。ユーザーアカウント制御が有効だとデバイス変更許可を求められます。[はい]をクリックします。
![Screenshot_NoName_2019-7-4_0-4-57_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184939/4a23410e-eaa9-c345-bfe8-10cff920c0a7.png)

3. Visual Studioの変更許可を求めれます。[はい]をクリックします。
![Screenshot_NoName_2019-7-4_0-6-31_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184939/7e58121e-9153-55f0-7cf6-c85d5635d89c.png)

4. Visual Studio のダウンロードが始まります。
![Screenshot_NoName_2019-7-4_0-7-51_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184939/7768697e-e96d-3707-edd7-46fdb5b87560.png)

5. Visual Studioに実装させたい機能一覧が表示されます。必要な機能を選択し、[インストール]をクリックします。
<font color="red">※今回は.NETデスクトップ開発、ユニバーサルWindowsプラットフォーム開発、クラスデザイナーを選択してます。</font>
![Screenshot_NoName_2019-7-4_0-18-47_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184939/dd1f4a72-57c4-8ae4-bd03-e3e264396408.png)

6. Visual Studio 2019 Communityのダウンロードとインストールが開始します。
![screenshot._2019-07-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184939/1aecf4f9-86b9-3641-fb4d-861cc4ec2148.png)

7. Visual Studio 2019のライセンス認証をするため、サインインを要求されます。[サインイン]をクリックし、Microsoftアカウント認証を行います。
<font color="red">※Microsoftアカウント認証済みの場合はライセンス認証が自動的に行われます。</font>
![Screenshot_NoName_2019-7-4_1-8-26_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184939/fd3d9fc5-ffd1-0d27-d891-8bbbf65e7825.png)

8. 初めてVisual Studio 2019を実行すると画面の配色テーマの選択が求められます。配色テーマを選択し、[Visual Studioの開始]をクリックします。
![Screenshot_NoName_2019-7-4_1-8-54_No-00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/184939/6b5873cc-7675-d243-10a5-dd512fa837fa.png)

インストール方法は以上です。
[Tips] Typescript With MetaProgramming (Nativeをちょっと拡張しよ）

#背景
Native型の拡張として 有名な [lodash](https://lodash.com/) を使ってきたのだけど、
処理が重たいのでやめましょ、というコメントをもらいました。~~たしかに重たい。~~ lodash の実装は ~~メソッドチェインにしづらい~~ ので、シーケンシャルに処理を記述できないのが、前々からうーんとなっていた。

> lodash でもメソッドチェインはできる点、「lodashが重たい」という点について、コメントをいただきました。確かに lodash でも書きかたでカバーできる可能性がありそうです。


#実装
例として、Array型 自体をちょっと拡張してしまおう。

公式にもドキュメントがあります。Declaration-merging という手法を使います。
https://www.typescriptlang.org/docs/handbook/declaration-merging.html

以下は重複を取り除くuniqというメソッドを追加する例です。

**array.extensions.ts**

```
export {}

declare global {
    interface Array<T> {
        uniq<T> (comparer: (value1: T, valu2: T) => boolean): T[];
        contains<T> (value: T): boolean;
    }
}

/* eslint no-extend-native: ["error", { "exceptions": ["Array"] }] */
Array.prototype.uniq = function<T> (comparer: (value1: T, valu2: T) => boolean): T[] {
    return this.filter((value1, index1, array1): boolean => {
        const index2 = array1.findIndex((value2): boolean => comparer(value1, value2))
        return (index1 === index2)
    })
}
```

使うところ

```
import './array.extensions'

['aaa','bbb','ccc','bbb','ccc']
.filter(elem => elem === 'ccc')
.uniq((v1,v2) => (v1 === v2))

objectの配列でも
[｛ID:'aaa'}, {ID:'bbb'},{ID:'ccc'},{ID:'bbb'},{ID:'ccc'}]
.filter(elem => elem.ID === 'ccc')
.uniq((v1,v2) => (v1.ID === v2.ID))

```

#補足
eslint が効いている場合は、no-extend-native がデフォルト有効なので、OFFにしておきます。
無下に拡張すると、もともとのメソッドを上書きしてしまうので、よく検討した上で都度OFFにするコメントを記載しておくのがベターです。

Native型の拡張は、名前を侵食するリスクと隣合わせではありますが、うまく使えると余計なクラス、Interfaceを定義しなくて済むので、頭の片隅においてて損はないと思います。
Stan.rb で NATS streaming server に日本語を含む文字列を送信する

NATS streaming server に接続するためのライブラリ Stan.rb では、マルチバイト文字列を送信できない。

https://github.com/nats-io/stan.rb/issues/12

なので、下記の Issue のように `JSON.generate(payload, ascii_only: true)` する必要がある。

https://github.com/flori/json/issues/12#issuecomment-121199
【備忘録】各ファイルのサイズ計算方法について

## 1.はじめに
サーバ内の各ファイル名とファイルサイズについて出力させ、
ファイルサイズの合計サイズについて計算し結果を表示させたく練習しました。

## 2.結果
##### コマンドイメージ
```
$ ls -l ./TESTDIR/file* | awk '{print($5,",",$9)}' > /tmp/test.csv ; cat /tmp/test.csv ; cat /tmp/test.csv | awk '{a+=$1} END{print a;}'
```
##### 出力結果
```
6 , ./TESTDIR/file1
12 , ./TESTDIR/file10
6 , ./TESTDIR/file2
6 , ./TESTDIR/file3
6 , ./TESTDIR/file4
6 , ./TESTDIR/file5
12 , ./TESTDIR/file6
12 , ./TESTDIR/file7
12 , ./TESTDIR/file8
12 , ./TESTDIR/file9
90
```
うまくできました。

## 3.おわりに
awkコマンドで計算できるのを初めて知りました。
とても便利なのでもっと学習しようと思います。

以上
Laravelのtinkerを使えるとちょっと幸せになれる

# 概要
Laravelのtinkerでちょっと幸せになれたので、ここに残しておこうと思います。  

# tinker

## 説明
```
php artisan tinker
```
のことで、Laravelを対話的に動かすことができるコマンドです。  
Eloquentはもちろんのこと、自分で作成した独自クラスの読み込みも可能です。  

## 動作
```
$ php artisan tinker
```
を実行すると対話的に動かすことができるようになり、そこで動かしたいコマンドを実行します。

```
>>> Model::find($id)
```
```
>>> use 特定のクラス
>>> $hoge = new 読むこんだクラス
>>> $hoge->メソッド(引数)
```
などで簡単に動作を確認できます。

また、コンストラクタインジェクションなどで依存関係がある場合は下記で解決することができます。

```
resolve(クラス名)
```

このやり方なら、コンストラクタインジェクションで呼び出す先の依存関係も解決してもらえ、 `tinker` での確認が簡単になります。

# まとめ
Laravelで何らの動作確認をするときは、 `tinker` を使うとプログラムがちゃんと動作していることが確認することができ、デバッグの助けになってくれました。
Raspberry piで地デジサーバ構築:08_B-CASデコード用ライブラリのダウンロード・インストール

地上デジタル放送を復号するため、デコードに必要なパッケージをダウンロード・インストールする。

```terminal:ターミナル
# ビルドに必要なパッケージのインストール
sudo apt-get install cmake g++

# デコード用ライブラリのダウンロード
wget https://github.com/stz2012/libarib25/archive/master.zip unzip master.zip cd libarib25-master

# ビルド
cmake .
make

# インストール
sudo make install
```
Raspberry piで地デジサーバ構築:07_カードリーダとB-CASの準備

B-CASカードの読み取りに必要なパッケージをインストールする。

```terminal:ターミナル
# 必要なパッケージのインストール
sudo apt-get install build-essential git 
sudo apt-get install pcscd libpcsclite-dev libccid pcsc-tools
```
B-CASを挿入する際、ICチップの面が上になるようにしてセットしよう。正常に読み込めたらLEDが点灯する。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263905/99b654c9-5b75-632e-eaaa-620fecc09e2a.jpeg" alt="B-CASをセットする際の注意点" width="500">

インストール後に以下のコマンドを実行してB-CASカードが読み込めるか確認。

```terminal:ターミナル
sudo pcsc_scan
# 「Japanese Chijou Digital B-CAS Card (pay TV)」と表示されたら成功
```
MOONBLOCKでマリオジャンプ

# ゲーム説明
くまさんに向かってくるスライムをジャンプしてかわしましょう。
たまに出現するお助けナイトをつかってスライムを消すこともできますよ！
http://moonblock.jp/#s/5d1e2499296e71562256537
<img width="379" alt="2019-07-05 (2).png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/343913/0f44c442-07d5-4596-221b-344dcfacff66.png">
ジャンプアクションないのかなぁ・・・
Raspberry piで地デジサーバ構築:06_チューナのドライバのダウンロード・インストール

チューナの制御に必要なドライバをダウンロード・インストールする。
だいぶ更新されていない気がするが安定して動作した、流石国産。

```terminal:ターミナル
# ダウンロード
wget http://plex-net.co.jp/plex/px-s1ud/PX-S1UD_driver_Ver.1.0.1.zip
# 解凍
unzip PX-S1UD_driver_Ver.1.0.1.zip 
# インストール
sudo cp PX-S1UD_driver_Ver.1.0.1/x64/amd64/isdbt_rio.inp /lib/firmware/
```
Raspberry piで地デジサーバ構築:05_リモート環境の構築

# Sambaのインストール

録画したデータにアクセス可能にするため、ファイルサーバの「**Samba**」をインストールする。

```terminal:ターミナル
sudo apt-get install samba
```

# Webminのダウンロード・インストール

Raspberry Piのリソース等の使用状況をリモートで監視できる「**Webmin**」をダウンロード・インストールする。

ブラウザから[Webmin(SOURCEFORGE)](https://sourceforge.net/projects/webadmin/files/webmin/)にアクセスし、最新バージョンの~.debのファイルをダウンロードする。
執筆時点の最新版は1.910で、以下のコマンドを使用してダウンロードすることができる。

```terminal:ターミナル
wget https://sourceforge.net/projects/webadmin/files/webmin/1.910/webmin_1.910_all.deb
```

ダウンロードが完了したらインストールを行う。

```terminal:ターミナル
sudo apt install ./webmin_1.910_all.deb
```

動作確認のため、ブラウザで以下のURLを入力する。

```URL:URL
https://[IPアドレス]:10000
```

Webminのログインページが表示され、rootでログインできたら成功。

# Webminの日本語化

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263905/495b1723-f70f-f57d-7b39-b952f3307104.png" alt="Webminの日本語化設定" width="700">

Webminにログインした後、レフトナビの「**Webmin**」を選択し「**Change Language and Theme**」をクリック。
「**Personal choice..**」のラジオボタンを選択し、横にある言語を「**Japanese(JA_JP.UTF-8)**」を選択。

「**Make Changes**」ボタンをクリックして設定を反映し、ページを更新して日本語化されたら成功。

# rootのユーザ名とパスワードを変更

再びレフトナビから「**Webmin**」を選択し、「**Webminユーザ**」から「**root**」に移動する。
こちらでrootのユーザ名とパスワードを適当に変更できる。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263905/f4183eed-4690-6a58-898c-6288d9996a26.png" alt="Webminのユーザ名・パスワード変更" width="700">


# sanbaユーザの作成

```terminal:ターミナル
# 設定のバックアップアップを作成
sudo cp /etc/samba/smb.conf /etc/samba/smb.conf.bak
# ユーザ作成
sudo smbpasswd -a hoge
# パスワードを入力

# sambaの再起動
sudo service smbd restart
```

# ルートディレクトリの参照設定

```terminal:ターミナル
sudo nano /etc/samba/smb.conf
# [global]下に追加
unix charset = UTF-8
dos charset = CP932 

# 末尾に追加(hogeはsambaでログインするユーザ名)
[root]
comment = root
path = /
browseable = yes
writeable = yes
create mask = 0777
directory mask = 0777
guest ok = no
guest only = no
valid users = hoge

# sambaの再起動
sudo service smbd restart
```

# ファイルサーバに接続

MacOSではFinderから「**移動**」->「**サーバへ接続**」を選択し、下記のURLを入力する。

```URL:URL
smb://[ホストネーム].local
# または
smb://[IPアドレス]
```

先ほど変更したアカウントで認証し、フォルダを閲覧することができたら成功。
2つの結果整合性

「結果整合性」の意味として、2つの全然別の意味があるように感じます[^2]：

- a) 別々のサービス（マイクロサービス）間の、単なる「更新遅延」のズレ具合
- b) 1つのサービス内の、各ノードが保持するデータの「ばらつき」の落ち着き具合

これらは見え方が全く異なりそうです。

## 更新遅延のズレ具合の方は

あるサービスから（ストリーム処理などをワンクッション置いて）別のサービスにデータが伝搬するような状況で使われていて、下記の見え方になります：

- データの巻戻りは発生しない（消込はある）
- 最新の書き込みが見えるまでにズレがある

一貫性モデルでいうところのCausal ConsistencyやSequential Consistencyになります（分散システムとしては意外と強い一貫性）。

## データのばらつきの方は

いくつかの分散KVSにおける、各ノードの保持するデータのばらつきの落ち着き具合を指すときに使われていて、下記の見え方になります。

- 最新の書き込みが即座に読めることが多い（障害さえなければ） or そういうモードが用意されている
- データが巻き戻って見えてしまうことがある（特に障害時）

外見えではどの一貫性モデルでもなさそうに思えます。

## つまり...

同じ「結果整合性」という単語を使っているにもかかわらず...

- 最新のデータ[^1]が即座に見えるのか vs データが見えるまでにラグがあるのか
- データが巻き戻って見えるケースがあるのか vs 絶対にないのか

というように、外見えの見え方は全然違うということがあるので注意が必要な気がしました。
（と思いつつも、どこからどこまでを1つのサービスとみなすかで同じ意味になる気も...?）


[^1]: ちなみに蛇足ですが、 `最新のデータ` という単語を分散DBの人にうかつに使うと怒られるので注意が必要です。
[^2]: 似た感じで、これ以外にも全然別の意味で使われてそうなものに「分散システム」がある気がします。一つのサービスを分散させているのか（分散KVSなど）、様々なサービスのデプロイメントを分散させるのか（コンテナやCloud系）など。
Calendarクラスで、なぜgetメソッドが必要なのか

Java初心者です。結論は出ていない？ですが、一応メモのために、、

#はじめに
Calendarクラスを使っててふと疑問に思ったことがありました。
###getメソッドていらなくないか？

Calendarクラスには、年、月、日、曜日などを取り出したり、代入することができるgetterメソッドやsetterメソッドが存在します。
例えば、現在の年を取得したいときは、

```Java
import java.util.*;
class CalendarTest {
	public static void main(String args[])
	{
    	Calendar cld = Calendar.getInstance();
    	System.out.println("YEAR getterメソッド→"+cld.get(cld.YEAR));
    }
}
```
とgetterメソッドを利用することで、可能となります。

```:出力
YEAR getterメソッド→2019
```

ここで、疑問に思ったことがあります。
**`cld.YEAR`を呼び出せばよくないか？**と。APIリファレンスを見てみても、フィールド`YEAR`のアクセス制御レベルはpublicとなっているため、呼び出しは可能ですし（publicでないと上の`cld.get(cld.YEAR)`も使えなさそうだが）。
何はともあれ、確認してみます。

```Java
import java.util.*;
class CalendarTest {
	public static void main(String args[])
	{
    	Calendar cld = Calendar.getInstance();
    	System.out.println("YEAR getterメソッド→"+cld.get(cld.YEAR)+",フィールド→"+cld.YEAR);
    }
}
```

```:出力
YEAR getterメソッド→2019,フィールド→1
```

んー、？
YEARフィールドには2019ではなく、1という整数が入っているみたいです。
いろいろ憶測ができそうですがいろいろを確認してみます。

```Java
import java.util.*;
class CalendarTest {
	public static void main(String args[])
	{
    	Calendar cld = Calendar.getInstance();
    	for(int i = 2015; i<2020 ;i++){
    		cld.set(Calendar.YEAR,i);
    		System.out.println("YEAR("+i+") getterメソッド→"+cld.get(cld.YEAR)+",フィールド→"+cld.YEAR);
    	}
    }
}
```

```:出力
YEAR(2015) getterメソッド→2015,フィールド→1
YEAR(2016) getterメソッド→2016,フィールド→1
YEAR(2017) getterメソッド→2017,フィールド→1
YEAR(2018) getterメソッド→2018,フィールド→1
YEAR(2019) getterメソッド→2019,フィールド→1
```

なるほど、フィールド`YEAR`はつねに1という整数を持っているみたいです。
ということで、他のフィールドも確認してみたところ、フィールド`MONTH`は常に2、フィールド`DAY_OF_MONTH`は常に5であることが判明しました。

もしや、、と思いAPIリファレンスを確認してみたところ、、、
やはり、それぞれのフィールドは`final`型として、定数宣言されていました。確認不足、、

#Calendarクラスの中身を覗いてみた
とりあえずここで、タイトルの通りgetメソッドを使用する必要があることがわかりました。定数宣言されているフィールドは通し番号的なものでしょうか（ちゃんと調べろ

それではgetterメソッドはフィールドではなく何を返しているのでしょうか。
Calendarクラスの内部を確認してみました。
getterメソッドとsetterメソッドはこんな感じになっていました。

```Java:Calendar.java
class Calendar {
    public int get(int field)
    {
        complete();
        return internalGet(field);
    }

    protected final int internalGet(int field)
    {
        return fields[field];
    }
    
    final void internalSet(int field, int value)
    {
        fields[field] = value;
    }
    
    public void set(int field, int value)
    {
        if (areFieldsSet && !areAllFieldsSet) {
            computeFields();
        }
        internalSet(field, value);
        isTimeSet = false;
        areFieldsSet = false;
        isSet[field] = true;
        stamp[field] = nextStamp++;
        if (nextStamp == Integer.MAX_VALUE) {
            adjustStamp();
        }
    }
}
```

細かい部分は無視すると、`fields`という配列の添え字（インデックス）として`YEAR`などの定数フィールドがあり、フィールドによって特定された要素に値を代入したり取り出したりしてる感じです。
#感想
普通に、それぞれのフィールドに値を入れたり（`public int YEAR = 2001`のような感じ）してもよくないか？という気持ちは払拭できませんでした。

getterメソッド、setterメソッドなどがあることによる利点はどこにあるのだろうか、よくわからなくなってきました。（結局カプセル化とか、オブジェクト指向とかを理解できてない。

ただ、それぞれがバラバラに要素を持っているのではなく、一つの配列に｛［年］,［月］,［日］,…｝のようにまとまって入っていることは、扱いやすそうで良いなと感じました＾＾

Javaの勉強、仕方も分からずなかなか捗らない、、
Raspberry piで地デジサーバ構築:04_初期ユーザpiを任意のユーザ名に変更

私の環境では**Raspbian Buster**で行うことができなかったので注意すること。
そのため、1つ前の**Raspbian Stretch**を使用して検証を行った。

# 作業用のユーザtmpの作成

```terminal:ターミナル
sudo useradd -M tmp
sudo gpasswd -a tmp sudo
sudo passed tmp
# 適当なパスワードを入力 
```

後でユーザtmpからログインするのでパスワードを控えておくこと。

# 起動時に認証するユーザをtmpに変更

```terminal:ターミナル
sudo nano /etc/lightdm/lightdm.conf
# 一行目に「autologin-user=tmp」を追加

sudo nano /etc/systemd/system/autologin@.service 
# 「ExecStart」から始まっている行を見つけ出し、「ExecStart=-/sbin/agetty --autologin tmp --noclear %I $TERM」に変更

sudo reboot
```

再起動後に画面の描画がおかしくなるが、特に問題ない(私の環境ではOSロード時に表示されるラズベリーの上にCLIが表示された)。

画面上部にtmpと出ていたり、ログイン待ち状態に移っていたら成功。
ログインされていない場合は先ほど作成したユーザtmpでログインを行う。

今回はユーザpiを**ユーザhoge**に変更してみる。

# ユーザpiを任意のユーザ名に変更

hogeを任意のユーザ名に使用してコマンドを入力する。

```terminal:ターミナル
sudo usermod -l hoge
sudo usermod -d /home/hoge -m hoge
sudo groupmod -n hoge pi

sudo reboot
```

# ユーザ名の反映

先ほど設定したtmpの設定を元に戻す。

```terminal:ターミナル
sudo passwd hoge
# パスワードを入力
sudo nano /etc/lightdm/lightdm.conf
# 一行目を削除
sudo nano /etc/systemd/system/autologin@.service 
# 「ExecStart」から始まっている行を見つけ出し、「ExecStart=-/sbin/agetty --autologin hoge --noclear %I $TERM」に変更

sudo reboot
```

再起動後に画面表示がGUIに戻っていることを確認する。

# ユーザtmpの削除

ユーザ名を変更することができたため、変更する際に使用したユーザtmpは不要なので削除する。

```terminal:ターミナル
sudo userdel tmp
```

# ユーザrootのパスワード設定

設定は完了だが、この先のステップでroot権限を使用する機会があるためパスワードを設定しておく。

```terminal:ターミナル
sudo passed root
# 適当なパスワードを入力
```

再起動を行い、設定したユーザ名とパスワードでログインできたら成功。
【FeatureSpec】404 / 500ページを表示させる

まず`config/environments/test.rb`内の下記２つの設定を変更してください。
初期状態ではtrueとfalseが反対になっていると思われます。
現時点では、エラーが起きた時、開発用のエラー画面になりますので、テスト時に本番と同じエラー画面を表示するための設定です。

```ruby:config/environments/test.rb
config.consider_all_requests_local = false
config.action_dispatch.show_exceptions = true
```

次にFeatureSpecのテスト項目です。

```ruby
# 省略

  scenario '404エラーページに遷移し、ステータスコードが404であること' do
    # 存在しないパスにアクセスする
    visit '/tasks/404test'

    expect(page).to have_content '404 not found'
    expect(page.status_code).to eq 404
  end
  
  scenario '500エラーページに遷移し、ステータスコードが500であること' do
    # 一覧画面に遷移したら例外を発生させる
    allow_any_instance_of(TasksController).to receive(:index).and_throw(Exception)
    visit tasks_path

    expect(page).to have_content '500 Internal Server Error'
    expect(page.status_code).to eq 500
  end

# 省略
```

# 参考リンク
使えるRSpec入門・その3「ゼロからわかるモック（mock）を使ったテストの書き方」
https://qiita.com/jnchito/items/640f17e124ab263a54dd
　※例外発生方法を参考にしました（allow_any_instance_of）
dockerでdiscord bot開発環境構築(python)

#はじめに

**この記事がqiita初投稿となります。**

最近discord botの開発をwindows端末で行っていたのですが
mac側で開発しよーと思って環境立ち上げるのにdocker使ったんでその備忘録

docker等はインストールされている前提で話をすすめまーす

#概要
pythonとcode-serverのコンテナをdocker-composeを使って
開発環境をパパッと立ち上げます。

(今回code-serverいらないし、しかもdocker-network理解してなくて
　くそクオリティですがね・・・)

#内容
##準備
###docker file

```
FROM python:3
USER root

RUN apt-get update
RUN apt-get -y install locales && \
    localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm

RUN apt-get install -y vim less
RUN pip install --upgrade pip
RUN pip install --upgrade setuptools
RUN python -m pip install numpy
RUN python -m pip install discord.py
```

###docker-compose.yml

```
version: "3"
services:
  python:
    restart: always
    build: .
    container_name: "python3"
    working_dir: "/root/"
    tty: true
    volumes:
      - ./app:/root/opt
  code:
    image: codercom/code-server
    restart: always
    ports:
      - "8443:8443"
    volumes:
      - ./app:/home/coder/project
```
volumesで繋げるところは、なんかチョロっとみた記事とは違かったので
もしうまくデータ連携されない時は一度コンテナ内に入って
ディレクトリ調べることをお勧めします

##実行
###compose upする
```
$docker-compose up -d
```
###code-serverにアクセス
http://localhost:8443　
でアクセスできるはず！
pwはdocker-composeのログにあるため

```
$docker-compose logs
```
で確認できると思います

###discord botを作成
実際にvolumesで繋げたフォルダ内にdiscord botのコードを記述し、保存してください

```python:サンプル
import discord

TOKEN = "自分のトークンID"
client = discord.Client()

@client.event
async def on_ready():
    print('ログイン')

@client.event
async def on_message(message):
    if message.author.bot:
        return
    if message.content == 'docker/python':
        await message.channel.send('docker/pythonテスト完了!')

client.run(TOKEN)
```
###pythonコンテナで実行
本当はdocker-composeでnetworkちょいちょいすれば、code-server側から
ターミナルよんで実行できそうな気がするのですが、そこらへんはわからんです。

pythonコンテナ内に入る

```
$docker exec -it コンテナID bash
```

で、volumesで繋げたディレクトリ内で

```
python test.py
```

あとはdiscord bot入れたサーバで、docker/pythonって打って
botから返事があったら成功です！
Raspberry piで地デジサーバ構築:03_raspi-configの設定

Raspbianの設定ツールである「**raspi-config**」を使用して簡単に設定を行う。

# 設定画面に移動

```terminal:ターミナル
sudo raspi-config
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263905/8d8bfec2-087a-9cd2-fdcd-9de212eda924.png" alt="raspi-configの設定画面" width="500">


## ホストネームの変更 

「**2 Network Options**」から「**Hostname**」を選択し、設定したいホスト名を入力。
引き続きターミナルからコマンドを入力するとエラー(名前解決できない的な問題)が表示されるので再起動を行う。

```terminal:ターミナル
sudo reboot
```

## SSHの有効化

再びターミナルを開き、設定画面を開く。

```terminal:ターミナル
sudo raspi-config
```

「**5 Interfacing Options**」から「**SSH**」 を選択し、「**YES**」を選択。
リモートで作業を行わない場合は省略しても良い。

## raspi-configの更新

「**8 Update**」を選択し、更新を行う、しばらくすると設定画面に戻る。

## ファイルシステムの拡張

「**7 Advanced Options**」から「**Expand Filesystem**」を選択。

## VNCの設定

「**5 Interfacing Options**」から「**VNC**」 を選択し、「**YES**」を選択。
こちらもリモートで作業を行わない場合は省略しても良い。
【Ruby】名前の付け方と読みやすいコードについて

####はじめに
コードを記述するにあたり、初学者でも少し気をつけるだけで効果のありそうなポイントに絞ってまとめてみました。私が自身初学者なので自分の備忘録も兼ねています。情報を文献などで検証していますが、見落としや誤りがありましたらお教えいただけますと幸いです。

命名規則や記述例はRubyのものを用意しています。

#####ー　目次　ー
1.読みやすいコード
2.名前の付け方
3.見た目の整え方
4.コメントを入れる
5.条件分岐
6.参考文献

####1.読みやすいコード
プログラミングの多くの時間はコードを読み、理解することに割かれます。同じ意図が迅速に伝われば伝わるほど、読みやすいコードということができそうです。そのためにどのような工夫が必要なのでしょうか。容易に想像がつきそうなポイントは以下の２点です。

・量が少ない
・わかりやすい

上記の２点は、プログラミングに限らず両立しない場合があります。丁寧に記述された厚い本の方が、スマートに要点を整理した本よりも、苦労せず読めるということは珍しいことではありません。記述量とわかりやすさが競合するような状況では、「どちらが解読するための時間を節約できるか」を考えると、迷うことも少なくなるはずです。

この記事を書くのに参考にしました書籍、『リーダブルコード』p.3より引用したいと思います。
> コードは他の人が最短時間で理解できるように書かなければいけない。（中略）「他の人」というのは、自分のコードに見覚えのない６か月後の「君自身」かもしれない。



####2.名前の付け方
変数、メソッド、クラスなど、自分で名前を付ける機会がプログラミングにはとても多くあります。簡単に気をつけることのできそうなポイントを探してみました。

１.単語の省略を勝手に作らないこと

```ruby:example2_1.rb
#良い例
def add_point
  #名前の通り、得点を加算する処理
end

#悪い例　勝手に略語を作ってしまった場合
def ap
  #以下省略
```


２.誤解のある名前を避けること

```ruby:example2_2.rb

def calculate
  #すごく時間のかかる計算処理
end
```

なにかとてつもなく時間のかかる計算をするメソッドを作ったとします。計算なのでcalculateと名前をつけました。
自分は「とてつもなく時間がかかる」ことを知っているので問題はないのですが、他の人はcalclateが普通意味するところの「簡単な計算処理」だと思って実行してしまうかもしれないので、computeなどの言葉を使うことを検討すると良いかもしれません。


「名前自体が変数やメソッドなどの説明を果たすこと」に気をつければ、上記の問題点は解決できそうです。命名に関する決まりごとについては以下に簡単に記述しています。

##### 名前を付ける際の決まりごと　（Rubyの場合）

||文字|区切り|具体例|
|:-:|:-:|:-:|:-:|
|クラス     |単語の頭は大文字      |続けて記述        |SampleClass     |
|メソッド    |全て小文字          |_　を使用        |erace_name|
|定数|全て大文字|_　を使用|MAX_HEIGHT|
|変数|全て小文字|_　を使用|user_id|
|ファイル|全て小文字|_　を使用|sample.rb|



####3.見た目を整える
適切なインデントを入れることで、見た目を整えることができます。
また、クラスやメソッドの定義と定義の間には空行を入れます。

いつ使うのかわかりませんが、渡された具を元におにぎりを作るメソッドを例にしています。
（「おかかおにぎり」と出力されます）

```ruby:example3.rb
#改善前
def make_onigiri(nakami)
puts "#{nakami}おにぎり"
end
make_onigiri("おかか")

#改善後
def make_onigiri(nakami)
  puts "#{nakami}おにぎり"
end

make_onigiri("おかか")
```

####4.コメントを入れる
適切なコメントを記入することによって、コードを読み解く手助けをすることができます。ここでは、不要なコメントと、コメントを減らすにはどうすれば良いかを記していこうと思います。

#####コメントを入れるには？
 #を付けることによって、その行をコメントにすることができます。

```ruby:example4_1.rb
#これはコメントです
```

#####不適切なコメント

【コメントをしなくても、名前などからわかる】

```ruby:example4_2.rb
#得点を加算するメソッド
def add_point(point)
  point += 1
  return point
end
```

【定義がそもそも不適切でコメントが必要】

```ruby:example4_3.rb
def complex_method
  #コメント例　A条件が偽でないとき、かつ 条件Bか条件C少なくとも一方が真...（複雑なコメント）
  #複雑な処理...
end
```

メソッドの定義の中へのコメントは避けるべきです。もしコメントがないと理解が難しいほど複雑なメソッドを作ってしまっていたとしたら、内容を整理します。

####5.条件分岐
コードが複雑になりやすい条件分岐で簡単に注意できそうなポイントをまとめてみました。
・条件に否定形を使っている場合、肯定形に直せないか 
・三項演算子を使うよりも、複数行のコードの方がわかりやすいのではないか
・if文がネストしている場合、case文で読みやすくできないか

####6.参考文献
[Rubyコーディング規約](https://shugo.net/ruby-codeconv/codeconv.html)
[リーダブルコード より良いコードを書くためのシンプルで実践的なテクニック](https://www.oreilly.co.jp/books/9784873115658/)

####おわりに
初投稿ということもあり読みにくかったかもしれません。
最後まで読んで下さりありがとうございます。
Raspberry piで地デジサーバ構築:02_日本語入力環境の構築

特に設定していない場合、日本語は表示されるが(GUI環境でのみ確認)、入力が行えない状態なので必要なパッケージをインストールして環境を構築する。

# パッケージのインストール

```terminal:ターミナル
sudo apt-get install fcitx-mozc
sudo reboot
```

# fcitx-mozcの設定

1. 画面左上の「**メニュー**」から「**設定**」を開き「**Fcitx設定**」を選択。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263905/596b6253-5c1a-58d4-3dd3-7bff00af9fdb.png" alt="Fcitxの設定(1)" width="500">

2. 「**全体の設定**」タブを選択し、「**入力メソッドのオンオフ**」から変換に使用したいキーを設定する。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/263905/68a58d6c-d9ec-ae6a-1146-ee794cafb0e2.png" alt="Fcitxの設定(2)" width="500">
Raspberry piで地デジサーバ構築:01_インストール済パッケージの更新

Raspbianの初期設定を終えた後、既存のパッケージの更新を行う。
定期的にアップデートされるので、パッケージの更新はこまめに行うと良い。

ストレージに余裕がある場合、SDカードのフルバックアップをこまめに行なっておくとトラブルが発生した際に戻すことができるのでお勧めする。

```terminal:ターミナル
# 更新
sudo apt-get update
sudo apt-get upgrade # 少々時間がかかる

＃再起動
sudo reboot
```
【初心者】Laravelでmigrateするのに手こずった話

#環境
・windows10
・PHP(7.3.6)
・Laravel(5.8.27)
・MariaDB

マイグレーションの設定に手こずったので記録用に
#問題
artisanでPostモデルを作成
```php artisan make:model Post --migration
```
そして以下のコマンドを入力すると

```php
php artisan migrate
```
エラーが発生

```php
  Illuminate\Database\QueryException  : SQLSTATE[HY000] [1045] Access denied for user 
'root'@'localhost' (using password: YES) (SQL: select * from information_schema.tables
 where table_schema = test and table_name = migrations and table_type = 'BASE TABLE')
```



#解決
こちらのサイトが参考になりました
・[「Access denied for user ‘abcd’@’localhost’」と表示される]
(https://php1st.com/433)

どうやらユーザー「root」に対するパスワードが間違ってるぽい

.envファイル内のDBパスが
```DB_PASSWORD=secret
```


そして再びmigrateすると
```Migration table created successfully.
```
成功
PythonのPEPは何と読む　ペップ？ピーイーピー？ピーエーピー？

# 何と読む？

私は、独学でPythonを学んでいるので、PEPをどう読むのか分かりませんでした。おそらく「ペップ」だろうとは思ったのですが、調べてみました。

[pep8 - Python入門](https://kaworu.jpn.org/python/pep8)によれば、PEP8を「ぴーえーぴーえいと」と読んでいます。つまり、PEP = ピーエーピーとなります。

次に、ネイティブはどう発音するのかを調べるため、YouTube動画を確認しました。すると、私が見たものは全て「ペップ」と発音していました。以下は、閲覧した動画です。

- [Raymond Hettinger - Beyond PEP 8 -- Best practices for beautiful intelligible code - PyCon 2015](https://www.youtube.com/watch?v=wf-BqAjZb8M)
- [Python tricks: Writing user-friendly code with PEP-8 naming conventions](https://youtu.be/Sm0wwmEwqpI)
- [pep8.org — The Prettiest Way to View the PEP 8 Python Style Guide](https://youtu.be/Hwckt4J96dI)

# 結論

PEPは、「ペップ」と読むのが良さそうです。
複数の画像をnumpy配列に読み込む

CNNなどで画像を処理する際、事前に画像データを読み込んでおく必要があります。
このとき、以下のような形式でデータを読み込む方法です。

```
(n_samples,height,width,channels)
```

# コード
gistを[ここ](https://gist.github.com/takecx/da8bbad9ef1ed2f2b3a044419199cbb2)に置いています。

```python
import os
import numpy as np
import glob
import cv2

image_dir = './images/'
search_pattern = '*.png'
datas = []
for image_path in glob.glob(os.path.join(image_dir,search_pattern)):
    # (height,width,channels)
    data = cv2.imread(image_path)
    # (1,height,width,channels)
    data_expanded = np.expand_dims(data,axis=0)
    datas.append(data_expanded)
# (n_samples,height,width,channels)
image_datas = np.concatenate(datas,axis=0)
```

(幅,高さ,チャンネル数)=(50,100,3)の画像を10枚読み込んだ場合以下のようになります。
- `data.shape`：`(100,50,3)`
- `data_expanded.shape`:`(1,100,50,3)`
- `image_datas.shape`:`(10,100,50,3)`


いざというときにいつもどうやるか忘れているのでメモとして残しておきます。
jQueryがそのページに読み込まれているか確認する方法

## jQueryがそのページに読み込まれているか確認する方法
実は、jQueryを読み込むと、グローバルに`jQury`という関数が宣言されています。これは`$`と同じ関数です。
すなわち、以下の式がtrueであればjQueryが読み込まれていると分かります。
`typeof jQuery === "function"`

この記事が参考になれば幸いです。
## 終わりに
私は現在、Web3のサービスの開発をしています。詳しくはこちらの記事をご覧下さい。
        [無料でイーサリアムが当たる、Web3時代の寄付サイトを作った話](https://note.com/kenroda/n/n814347fbdcd7)
RxjavaとRetrofitで非同期通信を行う

Androidで非同期通信を使おうとして、AsyncTaskLoaderを使うと、コードが長くなり、考慮する点も多いので、Rxhjavaを使ってみました。
今ではこちらの方が主流な理由がわかりました。

####①ライブラリの追加

下記ライブラリを追加しました。

```build.gradle
    implementation 'com.squareup.retrofit2:retrofit:2.6.0'
    implementation 'io.reactivex.rxjava2:rxjava:2.2.9'
    implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
    implementation 'com.squareup.retrofit2:converter-simplexml:2.3.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
```

rxJavaのAndroid版のrxAndroidも導入しています。

####②Retrofitの実装
今回はxmlをparseする処理を行うので、Retrofitを使います。
タグの名前を指定すれば、値が取得できるので、便利です。
まずは、interfaceの作成です。

```RssClient 
interface RssClient {

    @GET("{rssPath}")
    fun get(@Path("rssPath") path: String) : Observable<BlogEntity>
}
```

Rxjavaで使うため、戻り値をObservable型にしています。
また、`@Path("rssPath")`で実際に使用するときに引数を使うことで、カスタマイズできます。
その時に`"{rssPath}"`が同じ値になります。
なので、動的にURLを作成したいときに使えます。

次にEntityを作成します。

Entityについては下記の記事を参照してください!
[Retrofitでxmlの値を取得する](https://qiita.com/kumas/items/df43680e1c5f655c0424)

ここまででRetrofitを使えるようになりました。

####③Rxjavaの実装

最後に、Rxjavaを使って、非同期処理を行います。
手順としてはRetrofitを作成し、対象のURLで通信を行い、取得したデータを使って、その後処理を行うという流れです。

```kotlin:MainActivity.kt
val retrofit = Retrofit.Builder()
                       .baseUrl(https://×××××××××××/)
                       .client(client)
                       .addConverterFactory(SimpleXmlConverterFactory.create())
                       .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                       .build()

                    val response = retrofit.create(RssClient::class.java).get("rss")

                    // 非同期で記事を取得し、メインスレッドでその後の処理を行う
                    response.observeOn(AndroidSchedulers.mainThread())
                            .subscribeOn(Schedulers.newThread())
                            .subscribe( {
                                //UIの更新など取得したデータを使って処理を行う
                            }, {
                                Log.e("ERROR", it.cause.toString())
                            })
```

`.addConverterFactory(SimpleXmlConverterFactory.create())`でxmlをパースすることを定義し、`.addCallAdapterFactory(RxJava2CallAdapterFactory.create())`でRetrofitの処理結果を、RxJava2が利用できる形にしています。
そして、interfaceで定義したget()を実行します。
1点注意しないといけないのが、baseURLは末尾に/がないとエラーが発生します。

`.subscribeOn(Schedulers.newThread())`で新しい別スレッドで実行することを定義し、`observeOn(AndroidSchedulers.mainThread())`で取得結果をメインスレッドで受け取るようにしています。subscribeの前半の{}は成功したときの処理、こうはんはエラーが発生したときの処理になっています。AsyncTaskLoaderを継承させてみたいなややこしい処理はいらず、エラー処理も単純なので、記述も減ります。

最後に使ってみて見やすくて、便利でした。また、処理のチェーンなども手軽にできそうで何より、コード量が減るのがいいですね。

####参照
[Connect to an API With Retrofit, RxJava 2, and Kotlin](https://code.tutsplus.com/tutorials/connect-to-an-api-with-retrofit-rxjava-2-and-kotlin--cms-32133)
[Android working with Retrofit and RxJava in Kotlin](https://www.learn2crack.com/2017/11/retrofit-and-rxjava-in-kotlin.html)
Kibana plugin から Elasticsearch API 以外の呼び出し

#ElasticsearchのAPI呼び出し

Kibana pluginの中から呼び出す場合

```javascript
const {callWithRequest} = server.plugins.elasticsearch.getCluster('data');
adminCluster.callWithRequest(req, 'ping');
```
こんな感じで例が書いてあります。
'ping'以外は、
https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/api-reference.html
に書いてある名前で呼び出せます。


#他のAPIの呼び出し
Open Distroを入れているので、コンソールから

```text
GET _opendistro/_security/authinfo
```

を実行するとログイン中のアカウント情報が取れます。

```json
{
  "user" : "User [name=pdev, roles=[], requestedTenant=__user__]",
  "user_name" : "pdev",
  "user_requested_tenant" : "__user__",
  "remote_address" : "127.0.0.1:41076",
  "backend_roles" : [ ],
  "custom_attribute_names" : [
    "attr.internal.testAttr"
  ],
  "roles" : [
    "DEVROLE",
    "own_index"
  ],
  "tenants" : {
    "global_tenant" : false,
    "pdev" : true,
    "devtenant" : true
  },
  "principal" : null,
  "peer_certificates" : "0",
  "sso_logout_url" : null
}

```

これをPluginのなかで取得するには、どのFunction ?を使えばいいのか1週間くらい悩みました。
https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/transport_request_examples.html
ここに client.transport.request()というものを使う例がありました。

というこで、以下のように呼び出せばいいようです。
API Referenceのところに書いておいてほしいな。。

```javascript
const {callWithRequest} = server.plugins.elasticsearch.getCluster('data');
adminCluster.callWithRequest(req, 'transport.request', {
 method: "GET",
 path: "_opendistro/_security/authinfo"
});
```

Javascriptはまだまだ慣れない。
pytzを使ってpythonでタイムゾーンを扱う"正しい"方法

# 各国のタイムゾーンを扱うpytz
Python のタイムゾーンと言えば **pytz**
Python 3 になって標準ライブラリに timezone が出来ましたが、便利な pytz もよく使います。

```python:pytz
import pytz
tz = pytz.timezone('Asia/Tokyo')
```

```python:timezone
from datetime import timedelta, timezone
tz = timezone(timedelta(hours=+9), 'Asia/Tokyo')
```

（速度を求める場合は timezone を使うのがいいみたいです）
参考：[Python3のdatetimeはタイムゾーンを指定するだけで高速になる](https://qiita.com/mattsu6/items/5511c5631e7a54550f7f)

この便利な **pytz** も正しく使わないとJSTが19分ずれたりして困る（後述）ので、正しい使い方をメモ代わりに書き残します。

といっても公式ドキュメントに書いてあることほぼそのままの記事です。
[pytzドキュメント (PyPI)](https://pypi.org/project/pytz/)


# インストール

```
pip install pytz
```


# 使い方

**naive** な datetime インスタンス（タイムゾーン情報を**持たない** datetime インスタンス）にタイムゾーン情報を付与する際には **localize()** メソッドを使います。

```python
from datetime import datetime, timedelta
import pytz

tokyo = pytz.timezone('Asia/Tokyo')

tokyo_datetime = tokyo.localize(datetime(2019, 7, 7, 14, 12, 34))

print(tokyo_datetime)
# 2019-07-07 14:12:34+09:00
```

**aware** な datetime インスタンス（タイムゾーン情報を**持つ** datetime インスタンス）のタイムゾーンを変換する際には datetime の **astimezone()** メソッドを使います。

```python
london = pytz.timezone('Europe/London')

london_datetime = tokyo_datetime.astimezone(london)  # Asia/Tokyo から Europe/London へ変換

print(london_datetime)
# 2019-07-07 06:12:34+01:00
```

ロンドンはUTC+0ですが、サマータイム中なのでUTC+1になっていますね。

サマータイムなどの特殊なタイムゾーン情報を持った datetime インスタンスを操作（進めたり戻したり）した後は **normalize()** メソッドを使ってタイムゾーン情報を正しいものに更新します。

```python
london_datetime += timedelta(weeks=24)  # 約6ヶ月後のロンドンの日時

print(london_datetime)
# 2019-12-22 06:12:34+01:00  12月なのにサマータイムのUTC+1になっている

london_datetime = london.normalize(london_datetime)

print(london_datetime)
# 2019-12-22 05:12:34+00:00
```


# NGな書き方

UTC を除いて、datetime のコンストラクタに pytz 由来の timezone (tzinfo) は渡してはいけません。サマータイムなどの特殊な日時計算が正しく動作しないためです。
Asia/Tokyo のタイムゾーンでも +09:19 になってしまいます。

```python
from datetime import datetime
import pytz

tokyo = pytz.timezone('Asia/Tokyo')

japan_dt = datetime(2019, 7, 7, 12, 24, 45, tzinfo=tokyo)  # NG（datetimeのコンストラクタに渡してはいけない）
print(japan_dt)
# 2019-07-07 12:24:45+09:19

japan_dt = tokyo.localize(datetime(2019, 7, 7, 12, 24, 45))  # OKな例
print(japan_dt)
# 2019-07-07 12:24:45+09:00

utc_dt = datetime(2019, 7, 7, 12, 24, 45, tzinfo=pytz.utc)  # UTCはOK
print(utc_dt)
# 2019-07-07 12:24:45+00:00
```

なんでも日本標準時が施行された1888年より前は各都市毎に時間が決められていて、Asia/Tokyo というのはまさしく（現在のように明石基準でなく）東京基準の時間だったので、19分ずれてるとかなんとか。
HTML で リンク を作成する ～HTML <a href="URL"></a>～

# 目的  
- HTMLファイルにてリンクを作成する方法をまとめる

# 押さえるポイント
- aタグを用いる。  
- aタグで囲ったテキストがリンクとして表示される。  
- aタグのhref属性によってリンク先を指定する。  

#書き方の例
```html
<!-- <a>タグでリンク先に飛ぶ、クリックできる文字を囲む -->
<a>Google</a>

<!-- リンク先をURLで指定する -->
<a href="https://twitter.com/mirimiripc">筆者のTwitterへ</a>
```
～実際の表示～

<a>Google</a>

<a href="https://twitter.com/mirimiripc">筆者のTwitterへ</a>


# 注意するポイント
- リンク先のURLを指定するhref属性はaタグの開始タグ内に記載する。  
- URLをダブルクオートで囲む。  

# ちょっとしたテクニック
- リンク先を開くときに新しい新規タブで開くようにする。  
- target属性で`_blank`を指定する。  
～サンプルコード～

```html
<!-- リンク先を既存タブの更新で開く -->
<a href="https://twitter.com/mirimiripc">筆者のTwitterへ</a>

<!-- リンク先を新規タブで開く -->
<a href="https://twitter.com/mirimiripc" target="_blank">筆者のTwitterへ</a>
```

～プレビュー画面～
※Qiitaは親切であるため下記のリンクはどちらともリンク先を新規タブで開くようになっている。
リンク先を既存タブの更新で開く
<a href="https://twitter.com/mirimiripc">筆者のTwitterへ</a>

リンク先を新規タブで開く
<a href="https://twitter.com/mirimiripc" target="_blank">筆者のTwitterへ</a>