OmniAICreator/Qiita-1.07M huggingface dataset:
Beyond accuracy: evaluating recommender systems by coverage and serendipity　を読んでみた。

# Beyond accuracy: evaluating recommender systems by coverage and serendipity

この論文はレコメンデーションの評価指標の一つであるcoverageとserendipityについて、その計算方法を提案したものです。

通常の精度だけを評価すると一部の人気itemばかりにレコメンドが偏ります。
それらは、すでにuserが知っているitemである可能性が高く、レコメンドシステムとして実用的な推薦を行なっているとは言えません。
そこで網羅性（coverage）や意外性（serendipity）についても評価する必要があります。
これらの概念は以下の先行研究に基づいたものです。

```
Herlocker J., Konstan J., Terveen L. and Riedl J. 2004.
Evaluating collaborative filtering recommender systems.
ACM Transactions on Information Systems, 22(1), pp. 5–53.
```





## 論文情報

https://dl.acm.org/citation.cfm?doid=1864708.1864761

現時点（2019/9/30）で被引用数は434です。

---

## ABSTRACT

精度以外のレコメンデーションシステムの実用性を測る指標として、coverageやserendipityがあります。

この研究でははじめに、coverageとserendipityの指標値について議論し、そのトレードオフの関係にも言及します。


## 1. INTRODUCTION

省略します。

## 2. COVERAGE

coverage には２つのコンセプトがあります。

 1. システムが推薦するitemの割合
 2. userに対して推薦するitemの割合

この論文では1をprediction coverage, 2をcatalogue coverage と呼びます。

$$
PredictionCoverage = \frac{|I_p|}{|I|}
$$

- $I_p$ :　システムがレコメンドしうるitemの集合
- $I$ : itemの全集合

単なる価格フィルタ表示システムであれば、5000円以下のitemは全て $I_p$ に含まれます。

$$
WeightedPredictionCoverage = \frac{\sum_{i \in I_p}r(i)}{\sum_{j \in I}r(j)}
$$

- $r(i)$ : item $i$ が提案された時の有用性。この有用性については別に定義が必要です。

$$
CatalogCoverage = \frac{|U_{j=1...N}I_{L}^{j}|}{|I|}
$$

- $I_{L}^{j}$ : 計測期間中に $j$ 回目に推薦した top-n item リスト $L$ に含まれるitem集合。  
- $N$ : 観測されたレコメンデーションの回数

つまり、catalog coverage は一度でも表示されたitemの和集合とitemの全集合の比率です。

$$
WeightedCatalogCoverage = \frac{|U_{j=1...N}I_{L}^{j} \bigcap B^j|}{|U_{j=1...N}B^j|}
$$

- $B^j$ : 有効であると定義されたitem集合。定義は別に必要になります。

## 3. SERENDIPITY

推薦の意外性を計測する上で最もシンプルなのが、原理的な推薦モデル結果との違いを比較するです。

$PM$ (Primitive Model) を原始的な推薦の結果セットとし、 $RS$ (Recommender System)を計測したいレコメンデーションシステムによる推薦結果セットとする。

その意外性は以下のように定義できる。

$$
UNEXP = \frac{RS}{PM}
$$

上記だけでは意外性しか評価しておらず、レコメンデーションの有用性の指標となってはいない。
そこで関数 $u(RS_i)$ を導入する。
$RS_i$ はレコメンデーションシステムによる $i$ 番目の結果。
ユーザーが推薦結果に評価を行いそれが有用と判断されれば $u(RS_i)=1$ と定義する。
有用でないと判断すると $u(RS_i)=0$ となります。
この有用性を考慮すると以下のような指標が定義できます。

$$
SRDP=\frac{\sum_{i=1}^{N}u(RS_i)}{N}
$$

ここで、serendipity(意外性)とnovelty(新規性)の違いについて確認しておきます。
普段アクション映画ばかりを見ているuserに、彼女が知らないアクション映画を推薦することがnoveltyです。
普段アクション映画ばかりを見ているuserに、気に入られるドキュメンタリー映画を推薦することがserendipityです。

つまり、ユーザーが普段利用しているitemと類似性の低いitemを推薦しつつ、userの満足度を高めることがserendipity(意外性)です。

## 4. TRADE OFFS

accuracy(精度), serendipity(意外性), coverage(網羅性)には密接な相関性があります。

一般的には以下のような傾向があります。

- accuracyを高めると、catalog coverage が減少する。
- catalog coverage が上昇しても serendipityが上昇するとは限らない。
- serendipityが上昇すると、catalog coverageは必ず上昇する。
- accuracy と catalog coverage の両方を改善すると、serendipityが減少する。
- serendipityを改善するとaccuracyが悪化する。

serendipityを改善は、accuracyの悪化を引き起こすためuserのシステム利用離脱のリスクを引き起こします。
このリスクを抑制するためには、以下のことが重要となるでしょう。

1. userになぜこのitemが推薦されているのかを説明する
2. 推薦リストの並び替えや複数のリストを平行運用する


## 5. CONCLUSION

省略します
ApigeeでInvalid redirection uriエラー

## 事象
下記のエラーが出てしまう。

| エラーコード       | エラーメッセージ |
|:-----------------|------------------:|
| invalid_request  | Invalid redirection uri localhost:3000/******/****** |
参考：https://docs.apigee.com/api-platform/reference/policies/oauth-http-status-code-reference

## 原因
下記はApigee＞Publish＞Appsで確認できる
`localhost:3000/******/******`

今回はスキーム (http://やapp://)が設定されていなかったため、Apigee側がリダイレクト先がアプリなのかサーバーなのか判断できず上手くいかなかった。

## 解決
スキームをつけることでエラーが解消される
`http://localhost:3000/******/******`

※リクエスト内容にhttp://をつけるのはもちろんだが、ApigeeのApp設定も同一に修正するのを忘れないこと。
　ここから変更：Apigee＞Publish＞Apps
「ついていきたい」と思われるリーダーになるためには

「俺についてこい」という強烈なリーダーシップでもないし、カリスマ的な魅力で部下を引っ張るといったマネジメントでもない。
**「誰よりも会社を愛し、みんなのために頑張ろう」と思っているリーダー
=>みんなに「ついてきたい」と思われるリーダーになった**


###ビジョナリーカンパニー2より
リーダーにはカリスマ性の有無は全く関係はない。
むしろ謙虚さを持っている。何かがうまくいったとしたら「それは運が良かったからだ」「部下が頑張ってくれたからだ」と受け止める。
逆にうまう行かなかった時は「全て自分の責任だ」と捉える。

そうした謙虚な姿勢をもち、人格的にも優れたやつ。

##人を収める前にまず自分を修めよ
自分を収めることもできないのに、人を治めることはできない


###人が権力や地位、金に溺れ始めたら終わりですよと
上の立場になったり、部下が増えたら責任も肥大化していく
こうしたことについての畏れがないと**危ない**

責任を忘れて権力の誘惑に流されていくと、待ち構えているのは不祥事や不正。
部下からの信頼を失う=>悲劇的な結末だけ待っている
=>これは一時期の山田さんだね。

**自分を磨いておくことこそ、実はリーダーの最低条件**

#リーダーが発するべきはミッションである。
##自分たちの存在理由を考える
間違っても、売上を大きくする、利益目標を達成するといった短格的なものにしちゃいけない

・情熱を持って取り組めるもの
・自社が世界一になれる部分
・経済的原動力になるもの

の3つの縁が重なる部分を会社としては目指しなさい。と
本質的なミッションができたら部下は「ついていきたい」とリーダに対して感じ自ら動き出してくれるようになる

##個人的なミッションも定める
情熱=好きなこと
世界一=得意なこと
経済的原動力=人のためになること
*その対価としてお金がもらえる*

その3つの円の重なる部分で突き詰める

![AdditiveColorMixiing.svg.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/267752/cd51d787-3b02-af59-eb6d-45a4a1b71b14.png)

#リーダーとしてのあり方
・リーダーの様子は部下は常に見ている
・リーダーの市営は組織にどんどん伝染していく
・リーダーがクヨクヨしたらみんなクヨクヨする

・常に長期的にはなんとかなる！と楽観的であることが必要である
・部下の前では1%でも可能性があるのであれば絶対に行けるぞ。大丈夫！という気持ちを持ち続けること

・ネガティブなワードを吐かないこと(苦手、たくさん飲んだから、あまり寝てないから、忙しい)

**・ただ虚勢は貼るな**
=>信頼のできる部下には弱みも見せていい
=>周りから「ついていきたい」という気持ちを生み出すことになる

##リーダーの方から部下の意見を積極的に聞く
●一緒にやっていこうという姿勢を打ち出す
部下が何を考えているんかを理解せず、勝手にリーダーが何もかも決めていいものか。
部下は一緒に目標に向かっていく仲間なのだから一緒に考えるべきやと。

#心得
1.一緒に働ける縁を大切にしましょう
2.共に人間成長しましょう
3.社長が交代しても具体的な行動を起こさなければ何も変わらない。
「一人一人が変革に参画する」という気持ちで会社をよくしていこう
4.社長ではなく現場を見て仕事をしよう。現場重視、小売の原点に戻って仕事を振り返ろう


**シンプルでキャッチーなフレーズを作る**
=>それはチームや組織にやろうとしているということを浸透させ、勢いを付けられることである

##思いは文字にすると伝わる
週に一回でも月に一回でも文書にして今後の方針や今の現状。を書き起こした方が下には伝わる。
*大事なことは何回も書く、そして何十回も言葉で発する*

##叱り方
###自尊心を傷つけない叱り方をする
筆者がよくしていたのは、「あなたらしくない」というニュアンスで話すこと
=>大きな期待をしているあなただからきっとできたはずなのに、どうしてなんかな？という

ただ絶対にしてはいけない事には、リーダーは声を荒げても怒りを表すべき。
そうしなければ周りにも悪影響を与えてしまう。
=>顔は出来るだけ早く取り除く

###何かを指摘するときにはまず褒めてから
褒めすぎて困ることはない

#"リーダー"の原動力
「みんなを幸せにしたい」という思い
これがリーダーの最大の原動力でなければいけない


##部下の部下にまで気を配る
組織のリーダーとして一番大事なことは、部下に興味を持つこと
**関心を持たれていないことは部下にとって一番辛いこと**

飲みに行かなくても本音が聞ける関係を作ること

##現場には優しくはするが、近いやつには厳しく
ポジションが上がると現場を軽視した理、現場感覚が弱くなったりする人がいるのも事実
社長自身が守ってあげること

#仕事を頼む時には「why」から始めなさい
●使命感が持てるような頼み方にする
1つ資料を作るにしても、どういう意味があるかなんのためか、それを伝えてあげなければいけない

必ず褒めてから指摘する

#「事実」と「判断」を混同しない
●人を信じてもいいが人のすることを信じてはいけない

##やらなかった後悔とやった後悔について
前向きなチャレンジであるという大前提の時には、迷ったらやってみる。という決断をすること
###なぜ
もし失敗したとしても前向きなチャレンジならその経験から何らかのプラスは得られる
もしまくいかなかった場合、そこから何かを学べばいいだけのことなのです
しかしやらなかった後悔というものはチャレンジをしていないわけですから何を学ぶことがなくまるで違う結果になってしまう。


頭で考えがちな人たちはどうしてもリスクを避ける傾向にある。
やらないほうが費用も降りかかってこないし、手間もかからない
責任も取らずに済む


#無私の心を持つ
いい格好を見せようとして表面的に演じていたとすればこれは逆効果。
組織のためになるかどうかという観点で判断する。自分にとって特になるか損になるかは考えない。

言葉では「会社のためだ」「君たちのためだ」とは行っても言動の端々に本音は出てくる
そういうところから部下は上司の演技に勘付く。
=>組織崩壊

完璧に無私になるのは無理だから、そのほかに組織を維持するために必要なのは志(ミッション)！
=>なんのために仕事をしているのかを定める。社員同士で目的やモチベが違うとしても向かう方向は同じにする。

##金のためにはなく、世のため、人のためになるような志を
###志(ミッション)をベースに無私の心で頑張る
alpine の readlink -f に存在しないファイル名を渡すと絶対パスにならないことがある

# TL;DR

* alpine の `readlink -f` は挙動の差があり、絶対パスが取れないケースがある
* alpine がベースイメージの docker コンテナでシェルスクリプトを動かすときは、思わぬ罠にはまる場合があるので注意しよう

# 通常の readlink -f の挙動

`readlink -f <PATH>` で相対パス PATH を絶対パスに変換できます。

```shell-session
$ readlink -f .bashrc
/home/nakatt/.bashrc
```

Ubuntu や CentOS などの通常の readlink では、パスの最後の要素だけは存在しなくてもかまいません。([man readlink](https://linux.die.net/man/1/readlink) の -f オプションの説明にそう書かれています。)

```shell-session
$ readlink -f no_exist_file # 存在しないファイル名
/home/nakatt/no_exist_file
```

# alpine の readlink -f の挙動

alpine では、readlink -f に存在しないパスを渡すと空文字が返る場合があります。

```shell-session
/ # readlink -f no_exist_file # 存在しないファイル名
/ # 何も出力されない！
```

私の場合、シェルスクリプトの中で `real_path=$(readlink -f $path)` としていたので変数が空になって罠にはまりました。

alpine の readlink は GNU coreutils ではなく busybox を使っているので、必ずしも細かい仕様は一致しないようです。

```shell-session
$ readlink
BusyBox v1.30.1 (2019-06-12 17:51:55 UTC) multi-call binary.

Usage: readlink [-fnv] FILE

Display the value of a symlink

        -f      Canonicalize by following all symlinks
        -n      Don't add newline
        -v      Verbose
```

docker で何かを動かす場合は軽量な alpine を使うことが多いですが、それなり以上の規模のシェルスクリプトを docker で運用したい場合は、ベースイメージに alpine を使うのは避けたほうが良いかもしれません。
diffで先頭行や、先頭から数行分とばして比較する。

# 概要

プログラムによっては、
先頭行や、先頭と次の行にタイムスタンプや、ファイルのフルパスを出力するものがある。

このような場合、diff で比較するには、その部分を避ける必要がある。

# 結論

`diff` でできる。

# 例

`featurecounts_output` と `featurecounts_output_new` を比較したいとする

先頭から数行は、解析したときのタイムスタンプや、ファイルのフルパスがはいっているとする。

## 先頭行だけ飛ばすなら

```
diff <(tail -n +2 featurecounts_output) <(tail -n +2 featurecounts_output_new)
```

## 先頭から２行飛ばすなら

```
diff <(tail -n +3 featurecounts_output) <(tail -n +3 featurecounts_output_new)
```

# 参考情報

- [comparison \- Compare two files in Linux: ignoring first and last lines \- Stack Overflow](https://stackoverflow.com/questions/14558418/compare-two-files-in-linux-ignoring-first-and-last-lines)
Arduino + Max8 (3) select, counter, scale, splitオブジェクトによるセンサデータの応用

## ここまでのおさらい

前回は、ArduioとMax8を連携させ、センサの値によって音を再生するパッチを組みました
今回は、センサデータと組み合わせて使うと便利な以下のオブジェクトを紹介していきます

- select

- counter

- scale

- split

- flonum(scaleの下に接続されているオブジェクト)

  

## パッチのダウンロードと挙動の確認

[こちら](https://www.dropbox.com/s/ixa35sulwtkua4a/someFunk.maxpat?dl=1
)からsomeFunkパッチをダウンロードして開きます


![スクリーンショット 2019-10-01 16.29.27](https://tva1.sinaimg.cn/large/006y8mN6gy1g7iqfkkw7yj30qc0hzac4.jpg)

パッチをロックした状態で、ナンバーボックスをドラッグして値を変え、挙動を確認します。以下で各オブジェクトの機能を紹介します

- select : 特定の値が入力された際に反応を返す
  例えば、上の記載方法ではセンサの値が「200ピッタリ」になった際に、左のアウトレットからbutton信号が出力されます。「200以外」の場合は、右のアウトレットからbutton信号が出力されます
- counter: buttonが押された回数をカウントする
  インレットに接続されたbuttonが押された回数をカウントします。selectオブジェクトと組み合わせることで、「センサが10回反応した際に特定の行動をする」などのプログラムを作ることができます
- scale: arduinoのmap()関数と類似、値のスケーリングを行う
  左から（インレットに入力される値の最小値, インレットに入力される値の最大値, アウトレットから出力される値の最小値, アウトレットから出力される値の最大値）と指定することで、センサのデータを音や映像の再生スピードに対応付けることができます
- split: 指定された範囲外の数値は通過しないようにする
  センサの値は必ずしも安定したものではなく、センサによってはある値から+2, -2くらいの値の間で細かく移動する「ドリフト」という現象が起こることがあります。splitを使うことで、センサが確実に反応している際に値を取得する、などのプログラムが組めます
- flonum: 小数点を扱うことができるナンバーボックスです。左にあるnumber(i)ボックスは小数点を扱うことができず、例えば0.9を0、1.5を1として扱ってしまいます。小数点を含めた値の指定にはflonumを使う必要がありますので注意してください

## 復習も兼ねた応用



> 1. **sensorPlayer**パッチで`send`オブジェクト、`receive`オブジェクトを使って**someFunk**パッチでセンサの値を見れるようにして下さい
> 2. **someFunk**パッチで受けたセンサの値をナンバーボックスに接続し、センサの値による各オブジェクトの振る舞いを確認してみましょう
> 3. **someFuncMoviePlayer**を開き、`send`オブジェクト、`receive`オブジェクトを使ってセンサの値で再生速度をコントロールしてみましょう



sensorPlayerパッチは[こちら](https://www.dropbox.com/s/1o8e3gjbsg2z46n/sensorPlayer.maxpat?dl=1
)からダウンロードできます




someFuncMoviePlayerパッチは[こちら](https://www.dropbox.com/s/xmhsuilvjchctja/someFuncMoviePlayer.zip?dl=1
)からダウンロードできます
（メモ）海外でUberを利用したい場合は日本でUberのアプリを導入・初期設定しておくべき

### タクシーでの交渉は難しい

例えば公共交通機関を使って行ける場所であればいいのですが、少し郊外にある場所にどうしてもいきたい！という時に、Uberが使えるとよかった！という経験がありました。
背景や制約について、自分で詳細を調べられてはいないのですが、経緯をTipsとして載せます。
（実際に海外でUberを使いたい方は、ご自身でも現地でUberが使えるか、自分のスマホでは制約があるのか？等、ちゃんと調べてくださいませ。）

####個人経営の小さなお店にいきたかったので詳しそうな人に相談

- 最初は路線バスを乗り継ぐルートをGoogle Mapで見つけたので、それでいく気になる。（1回乗り継ぐぐらいなら行けそうな気がして。。）
- 現地の日本人向けツアーデスクの人に（日本語で）聞くと「タクシーで行ったほうがいいんじゃない？）」と言われる。（そんなに難しいの？？）
- ホテルからお店に直接電話して「路線バスよりタクシーがいいってホント？」「タクシーに住所言えばわかる？」と（日本語で）聞いたら、「タクシーがいいよ」「住所でわかるよ」と言われる。（ならばタクシーにしよう。。）

####ボーイさんにエージェントになってもらう作戦
それでも、交渉する英語力ないし、なんだかボラれたくないし心配だった

- ホテルの車寄せでボーイのお兄さんに住所を書いた紙をみせて「大体いくらくらいで行けるか？（そこに停車している）タクシーの運転手に聞いてもらえる？」と英語で頑張って言ってみた
- ボーイさんが運転席のおじさんに確認した後、困った顔で戻ってくる
- 「だいたい$15前後だけど、混み具合によって変わる。直接交渉したいそうだから、直接会話してくれる？」と言われる T-T

####運転手との交渉
運転手さんと交渉するも、不本意ながら相手のペースのることに。。

- 運転手のおじさんは超早口で、「17ドルは絶対かかるし、混み具合によって変わる」「メーターで課金にする？、「渋滞にあっても一律料金でチップ込みで$21はどう？」と言われる
- 迷ったけど$21で合意する
- 到着すると「帰りはどうする？すぐ終わる用事なら待ってる！」と言われたので「数時間かかる(事実)けど待っててくれるの？」というと「帰るねー！」と去ってく

###Uberとの出会い
#### お店で店主と雑談しながらUberを調べる

お店で店主（日本人）と雑談しながら、調査。Google検索。

- 現地のタクシー標準料金を調べるサイトをみつけて計算すると17ドルが相場だった。（チップ込み？か不明。チップが別でも$21はちょっと高い。）
- Google先生での検索情報をもとにすると、、タクシーよりUberのほうが明瞭会計で安いという情報が多数出てくる
- 「Uber使えばよかった！」と思わず叫ぶ（大きな声は出してませんが・・）

#### 店主からの有難き提案
店主が「帰りはわたしのスマホでUberで車呼んであげる！」と言われ、お言葉に甘える。

- 9ドルだった！
- 行先の細かいことなどの会話も不要で、最低限、アカウント名を伝えるだけで大丈夫だった！
- 支払いはUberのアカウントで登録したクレジットカードから引き落とされるので現金を渡そうとしたところ「私も最初にタクシーでいいんじゃない？」と言っちゃったから！、とまさかのUber代をおごっていただくことに。。（心からの感謝の意を表明しました。）

###Uber利用時の注意点
今回は寛容な現地の店主にお支払いまでサポートしていただいてしまったのですが、その経緯として、Uberの初期設定の制約があります。（店主さんから聞いただけで、自分ではちゃんと調べられてません。）

- Uberは**アプリインストール後の初期設定で、SMS認証が必要**なのだそう。
- 日本だと自分のスマホにSMSが届きますが、海外でSMS認証が対応できない場合は、初期設定が完了できず使えないらしい。（スマホの契約プランによっても違いそうだけど。。）
- 私がその場でwifi経由でアプリを導入し始めたのを見て「初期設定できないからだめだよ。こっちで呼んであげる。」と言われた。（私のスマホは電話として機能してなかった。）

###まとめ
ということで、海外に行く人は、日本でスマホにUberを導入＆初期設定しておくことをお勧めします。
Tasklet in Spring Batch 

Tasklet in Spring Batch

Strategy for processing in a step.

It includes a method,

RepeatStatus execute(StepContribution contribution,
                     ChunkContext chunkContext)
              throws java.lang.Exception
Given the current context in the form of a step contribution, do whatever is necessary to process this unit inside a transaction. Implementations return RepeatStatus.FINISHED if finished. If not they return RepeatStatus.CONTINUABLE. On failure throws an exception.

Parameters:
contribution - mutable state to be passed back to update the current step execution
chunkContext - attributes shared between invocations but not between restarts
Returns:
an RepeatStatus indicating whether processing is continuable.
Throws:
java.lang.Exception
Pythonで動画再生する方法（自分用）

import cv2
import sys

file_path = 'file' //ファイル置いてる場所
delay = 1 //再生スピード調整
window_name = 'frame'

cap = cv2.VideoCapture(file_path)

if not cap.isOpened():
    sys.exit()

while True:
    ret, frame = cap.read()
    if ret:
        cv2.imshow(window_name, frame)
        if cv2.waitKey(delay) & 0xFF == ord('q'):
            break
    else:
        cap.set(cv2.CAP_PROP_POS_FRAMES, 0)

cv2.destroyWindow(window_name)
localstorageに直接true/falseを格納しようとした時にハマったこと

# ハマったこと
localstorage上にtrue/falseを格納して、取り出してifしようとしたところ、falseを取り出してるのにifをtrueで抜けてきた

```js
localstorage.setItem('setVariable',true)
```

# 参考記事
・[JavaScriptで"false"が文字列扱いされる場合の覚書](https://qiita.com/miihon_ani/items/4760259953bd78626734)

# 解決方法
原因としては、true/falseを格納してもStringで格納されてしまうので、1/0で格納する。

```js
localstorage.setItem('setVariable',1)
```
取り出して来た時にparseIntで数値化して、Booleanで真偽値型に変更する

```js
Boolean(parseInt(localstorage.getItem('setVariable'),10))
```
MACS2 install to MacOS with Anaconda3 (AnacondaにPython3とPython2を共存させたよ。の回）

# 1.はじめに
ちょっとこれをやってみたかっただけ。

[ChIP-Seq データの解析方法　Bowtie2 + MACS2]
(https://bi.biopapyrus.jp/hts/chipseq/bowtie2-macs2.html)

# 2.環境
・MacOS Mojave 10.14.3
・Anaconda３インストール
・Python3がデフォルト
基本condaで環境構築中

# 3.つまずいた
とりあえずcondaでインストール
https://anaconda.org/bioconda/macs2

```terminal:command
$ conda install -c bioconda macs2
```

エラー・・・・・
↓こんなんでてきた・・・・

```terminal:command
UnsatisfiableError: The following specifications were found                                                                                                                                          
to be incompatible with the existing python installation in your environment:

Specifications:

  - macs2 -> python[version='2.7.*|>=2.7,<2.8.0a0|>=2.7,<3']

Your python: python=3.7

If python is on the left-most side of the chain, that's the version you've asked for.
When python appears to the right, that indicates that the thing on the left is somehow
not available for the python version you are constrained to. Note that conda will not
change your python version to a different minor version unless you explicitly specify
that.

The following specifications were found to be incompatible with each other:
（以下略）

```

なにやらpython2やないとあかんとかなんとか。。。
（`pip install macs2` でも同じようなエラーが出てきましたよ）

# 4.めんどくさいけどAnaconda３にPython2も使えるようにしてみた。
Python２は使うことないはず！！と勝手におもっていたが、避けられないようなのだ。

先人の知恵（参考URL)：
[Anacondaでpythonのバージョンを使い分ける](
https://qiita.com/hurts14/items/116ef0e332e159a51c4f)


# 5.改めまして、install

参考URLと同じですが。。。

```terminal:command
#仮想環境にpython2をインストール
$ conda create -n py27 python=2.7 anaconda

#python2に切り替える
$ source activate py27
```

さて、インストールできるかな？
成功！！

```terminal:command
#お目当のパッケージをインストール
$ conda install -c bioconda macs2
```

Python2の仮想環境を抜ける。（こちらは参考URLの`source deactivate`ではエラーになりましたが、下記コマンドで成功しました。）

```
$ conda deactivate
```

---
# おわり
以上です！

解析が成功したかどうかはまた別のお話。。。
→`conda install -c anaconda numpy`が必要。とのことで、インストールしたが、エラー・・・。
uninstallして、再度`conda install -c anaconda numpy==1.16.3`で動いた！
（なぜかMACS2もuninstallされたので、再度install）

参考URL：https://www.reddit.com/r/bioinformatics/comments/bhlbx0/macs2_callpeak_error/
今日覚えたvim

#挿入モード時は背景色変える

Vimmerのみなさん、
「なるべくINSERTモード入らないように、長居しないようにしましょう」
といいます。

だったら、INSERTモードかノーマルモードか
分かりやすくして欲しかったですね、、、
とか思いながら検索したら、

.vimrcに数行いれるだけで解決しました:
https://qiita.com/L_Y/items/81395569675d7e55861d

色は235番だとぼくのモニタだとイマイチわかりにくいので、236番使いました。

色番号:
https://qiita.com/tmd45/items/226e7c380453809bc62a


#画面を縦に分割

`:sp`

Vimは縦移動よりも横移動が多いから
横に広く見れる形で分割できるといいと聞いて、主にこっちを使ってます。

分割したあとのキーバインドは
以下のエントリのを参考にして、sを中心に使ってます:
https://qiita.com/tekkoc/items/98adcadfa4bdc8b5a6ca


このキーバインドが便利すぎて、

- `sk`下の画面に移動
- `*` その単語を検索
- sjn`　上の画面に戻って検索結果に移動

とかが流れるようにできます。


#インクリメント・デクリメント

いつも忘れるやつ。
カーソルにある数字を１大きくor小さくするだけ、という地味なキーバインド。

`C-a`
`C-x`

全角数字は駄目でした。微妙にケチですね。



#f移動

例えば、
`f"`とやると、次の"までカーソルが移動してくれる。

そのあと`;`でさらに前に検索、`,`で後ろに検索
`fa;;;;;`とかでも移動できます。

草生やす（wで移動すること）よりも早い。


##未解決
ESC時にIME切る
ぐぐってでてきたやつ、２つくらい試したのですが、
なんか、通常モード時にESC押すと変な挙動します。
gitの鍵とは

#gitの鍵とは

自分のgithubをいじる時はパスワードやユーザーネームは入力しない。

でも他人に自分のリモートリポジトリをめちゃめちゃにされたら困る。

###そこで出てくるのが公開鍵と秘密鍵！

鍵には
######公開鍵
と
######秘密鍵
の２種類がある。

公開鍵は、githubに送って、秘密鍵は自分のパソコンの中にある。
この鍵が一致することで、本人確認ができてgit hubをいじることができる。

```
$cd ~/.ssh
```
これで鍵を作成するディレクトリに移動する

```
ssh-keygen
```
で鍵をつくる
すると以下のようなのが出てくる

```
Generating public/private rsa key pair.
Enter file in which to save the key (/Users/xxx.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
```


######id_rsa.pub
これが公開鍵
######id_rsa
これが秘密鍵

次に

```
$ pbcopy < ~/.ssh/id_rsa.pub
```

これを実行すると超長い鍵がコピーされるので、githubの鍵を貼るところに貼り付ける

```
ssh git@github.com
```

最後にこれを実行してHi!って返ってきたらおっけー！
ウォータフォール開発

　ウォーターフォール開発について記述していく。
　流れは
「要件定義」→「機能設計」→「基本設計」→「詳細設計」→「開発」→「単体テスト」→「結合テスト」→「機能テスト」
となっている。





##要件定義
　目的はエンドユーザのRFP、要求仕様をもとに顧客から作ってほしいシステムの要望を明確にすること。
　成果物は要件定義書。

　以下の内容が明記されている。
　・システムの概要
　・導入目的
　・背景
　・現状
　・問題点
　・ユースケース図
　・概念図
　・入出力要件
　・非機能要件





##機能設計
　目的は要件定義書をもとに最終的に作るシステムを設計すること。
　成果物は機能仕様書。

　機能仕様書には以下の要は内容が明記されている。
　・ユースケース図
　・画面遷移図
　・機能一覧





##基本設計
外部設計とも呼ばれている。
　目的は機能仕様書をもとにユーザから見えるインターフェース部分の仕様を決定すること
　成果物は基本設計書。

　基本設計書には以下のような内容が明記されている。
　・業務フロー
　・機能一覧表
　・ネットワーク構成図
　・テーブル定義書
　・ER図
　・画面遷移図
　・画面レイアウト


##詳細設計
　内部設計とも呼ばれている。
　目的は基本仕様書をもとに、プログラマが理解できる（顧客が理解できない）システム内部の動作、機能、DBなどを設計すること。
　成果物は詳細設計書。

　詳細設計書には以下のような内容が明記されている。
　・機能設計書
　・DB定義書
　・クラス図
　・DFD


##開発
　目的は詳細設計書をもとにプログラムを実装すること。
　成果物はプログラム。


##単体テスト
　目的はプログラムの最小単位（モジュール：メソッドや関数）ごとに対象プログラムが正しく機能するか確認すること。
　成果物は結果を記載した単体テスト仕様書。


##結合テスト
　目的は複数のモジュールを組み合わせて正しく機能するか確認すること。
　成果物は結果を記載した結合テスト仕様書。


##機能テスト
　機能設計に従ったシステムができているか確認すること。
　成果物はテスト結果を明記した機能テスト仕様書。
Phaser3の豆知識

#■ポイント：画像アセットが既にロードされたかどうかの確認方法
画像アセットをロードする時で、この画像アセットが既にゲームオブジェクトに割り当てられていたかどうかを確認する必要があります。
画像アセットがTextureManagerに保存されていますので、Textureインスタンスとしてアクセスできます。
###・方法１

```
var tex = game.textures.get('key');
```
keyに一致するTextureManagerからTextureを返します。
キーが未定義の場合、__DEFAULTテクスチャを返します。キーが与えられたが見つからない場合、__MISSINGテクスチャを返します。
###・方法２

```
var　isExist = game.textures.checkKey('key');
```
指定されたtextureのkeyをチェックし、keyが既に使用されている場合はconsole.warnをスローし、falseを返します。
console.warnを避けたい場合は、TextureManager.exists代わりに使用してください。

#■ポイント：Spriteクラスを作成する時の必須な処理
以下は、新しいSpriteクラスを作成しておきます。

```#javascript
class Player extends Phaser.Physics.Arcade.Sprite {
  constructor(scene, x, y, texture, frame) {
    super(scene, x, y, texture, frame);
    this.scene = scene;
    //シーンのDisplayListとUpdateListにゲームを追加する
    this.scene.add.existing(this);
    //Arcade物理ボディをゲームオブジェクトに追加する
    this.scene.physics.world.enable(this);
    this.setTexture(texture, frame);
    this.setPosition(x, y);
    this.setSize(1, 1);
    this.setOffset(0, 0);
  }
}
```
###・this.scene.add.existing(this);
上記ステップで、シーンのDisplayListとUpdateListに、このゲームを追加しておきます。
このステップが漏れたら画像は画面に表示することができないでしょうか。
###・this.scene.physics.world.enable(this);
上記のステップで、物理エンジンのボディをゲームに追加しておきます。
このステップが漏れたら画像におけるいろいろな設定や制御ができないはずです。

#■ポイント：衝突処理の実現方法
###・衝突ファンクション(collider)のコールバック関数で衝突処理を実行

```
create() {
player = this.add.sprite(100, 100, 'image1');
//ライルマップから川レイヤーを作ります
river = this.map.createStaticLayer("River", this.tileset, 0, 0);
//属性設定(collision=true)から、衝突しようとするタイルを決定しておく
river.setCollisionByProperty({ collision: true });
//衝突発生
scene.physics.add.collider(player, river, this.hitRiver, null, this);
}
hitRiver() {
//ここで、衝突処理を実施する
}

```

###・衝突対象がオーバーしたかどうかを判断してから衝突の処理を実行

```
create() {
    sprite1 = game.add.sprite(100, 200, 'atari1');
    sprite1.inputEnabled = true;
    sprite1.input.enableDrag();
    sprite2 = game.add.sprite(400, 400, 'atari2');
    sprite2.inputEnabled = true;
    sprite2.input.enableDrag();
    text = game.add.text(16, 16, 'Drag the sprites. Overlapping: false', { fill: '#ffffff' });
}
update() {
    if (checkOverlap(sprite1, sprite2)){
        text.text = 'Drag the sprites. Overlapping: true';
    } else {
        text.text = 'Drag the sprites. Overlapping: false';
    }
}
checkOverlap(spriteA, spriteB) {
    var boundsA = spriteA.getBounds();
    var boundsB = spriteB.getBounds();
    return Phaser.Rectangle.intersects(boundsA, boundsB);
}
```
###・スプライトの座標によって、タイルマップに対応しているタイルオブジェクトを取得できるかどうかから、衝突処理を実行

```
create() {
    var tile = ropeLayer.getTileAtWorldXY(monkey.x, monkey.y);
    if (tile.index == 1) {
      this.hitRope();
    }
}
hitRope() {
    //ここで、衝突処理を実施する
}
```
ロープレイヤーから、モンキーの座標のところにあるタイルを取得してみましょう。
タイルオブジェクトが作られていたら、衝突が発生した。タイルが取得できない(Null)場合は、衝突が発生しませんでした。

#■ポイント：背景の画像がずっと横移動する方法
これは、タイルスプライトを使用して、「tilePosition」を移動することで実行できます。

```
function preload () {
   game.load.image('bgtile', 'bgtile.jpg');
}
function create () {
   this.bgtile = game.add.tileSprite(0, 0, game.stage.bounds.width, game.cache.getImage('bgtile').height, 'bgtile');
}
function update () {
   this.bgtile.tilePositionX -= 1;
}
```
縦移動する吐合は、tilePositionYを使います。
EC2でrailsアプリをpwa化しようとしたらsafariでServiceWorker responce error is nullが発生

## どのような問題が発生したのか？
awsのec2サーバー上にrailsプロジェクトとapacheの環境を作りrailsで顧客管理ツールをpwa化しようとした際に、
iOS版 safari,OSX版 safariで

```
"サイトを開けません。The operation couldn't be completed. Protocol error" (NSPOSIXErrorDomain:100)
```
とのエラーが頻出する問題が発生した。

何度もリロードしてみると時々画面が表示されるが、画像やアイコンなどが全く表示されない。
コンソール画面を開いてみると大量の

```
"FetchEvent.respondWith received an error: Returned response is null"
```
というエラーが発生していた。


## 動作環境
- AWS EC2
- apache for ruby
- Ruby on Rails 5.2.3
- rails gem service worker

## 結局の原因
### 　　　「ec2内ロードバランサーのhttp/2通信が有効になっている」

##結論に到るまで
コンソール上に表示された

```
"FetchEvent.respondWith received an error: Returned response is null"
```
というエラーをググると大概出てくるサイトは[こういった](https://stackoverflow.com/questions/53431195/service-worker-fechtevent-respondwith-response-is-null-on-ios-12-1-safari)ものだが、根本的な解決ができなかった。

ここでハマってしまった私個人の原因は、
「safariのpwa対策は不完全」という先入観である。
確かにsafariのpwa対策はchromeよりも遅れているが、
エラーの原因をservice workerにあるのではないかと考え長い時間serviceworkerについての記事を巡っていた。
なにせ時々リクエストが成功する意味がわからない。

エラー文からすると
railsのgem,serviceworker-railsで生成されるjsファイル

```js:/app/aseets/javascripts/serviceworker.js.erb
function onFetch(event) {
  event.respondWith(
    fetch(event.request).catch(function() {
      return caches.match(event.request).then(function(response) {
        if (response) {
          return response;
        }
        if (event.request.mode === 'navigate' ||
          (event.request.method === 'GET' && event.request.headers.get('accept').includes('text/html'))) {
          console.log('[Serviceworker]', "Fetching offline content", event);
          return caches.match('/offline.html');
        }
      })
    })
  );
}
```
ここの

```js:
if (response) {
 return responce;
}
```
が空になっているようだが、今まではこんなことがなかったし、
第一、開発用の別のec2サーバーでは同問題が全く起きていなかった。


serviceworkerの古いキャッシュかと思いブラウザのキャッシュを消してみてもダメ、
turbolinkとの兼ね合いの悪さかと思いrailsのturbolinksをオフにしてもダメ、

```html:/app/views/application.html.erb
<head>
  ...
  <meta name="turbolinks-cache-control" content="no-cache">
</head>
```

これらでは効果がなかったので視野を広げることとし、サーバーの設定を確認してみることにした。

はじめに出てくる
　「"サイトを開けません。The operation couldn't be completed. Protocol error" (NSPOSIXErrorDomain:100)」
というエラーを調べてみるとこんなサイトが

[iOS 11, macOS Hight Sierra で The operation couldn't be completed. Protocol error が出る場合の対処](https://qiita.com/ameyamashiro/items/8d4be0f11ffe12472052)
[Safariで発生するプロトコルエラー](https://idealive.jp/blog/2019/08/21/safariで発生するプロトコルエラー/)

safariだとapacheから独自レスポンスが乗っかり、クライアントではコネクトの失敗を返す「時がある」。
試しにロードバランサー => http/2　無効　にしてみると
全て治った。エラーも全く表示されない。

どうやらec2サーバーにアクセスするうちに何度か成功するが、
レスポンスが成功した時の不完全なキャッシュをserviceworkerが登録してしまい、次のリロードで不完全なキャッシュを元に描画され、
ファイルが見つからないというエラーを吐いていたということらしい。

これで真っさら解決した。


## 次に出てくる問題
ec2 LBのhttp/2通信を無効にしてしまったので、通信速度が懸念される。
＋1%の高速化だけでもありがたい。
~~ただでさえrailsの速度はマイペースで、このツールでは膨大なデータのやりとりをするのに~~

なのでec2 LBのhttp/2は確保して、apache内の http/2通信の設定が必要になる。
Pics.Info 字畫擴增實境導覽

Android 字畫辨識應用

https://gitlab.com/kangyueluo/Android_Pic.Info

1. 準備辨識字畫放入 Drawable
2. 在 string.xml 設定，字畫的資訊
3. 設定圖片過濾器
4. 設定handler顯示文字資訊
aws-vault loginでChromeのウィンドウをAWSアカウント毎に分離する

[aws-vault](https://github.com/99designs/aws-vault) を使うと `aws-vault login <profile>` でAWSマネージメントコンソールのログイン用の一時URLを発行して、コマンドからシュッとマネージメントコンソールをブラウザで開いてログインでき、大変べんりです。

しかしながら、複数のAWSアカウントを切り替えて使う場合、1つのChromeのウィンドウを共有していると、アカウントを切り替える度に以下の画面が出てログアウトが必要になって、ちょっと不便。

![logout.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61152/0341211c-f8b7-044e-4df1-3e5d6005c66d.png)

Chromeのシークレットブラウザで開けばよいかと思ったんですが、シークレットブラウザで開いても同じChromeのウィンドウが再利用されてしまい。同時に2つのAWSアカウントにログインすることができません。

なんかよい方法はないものかと調べたところ、macOSの場合は、Chromeのプロファイルを分ければシークレットウィンドウを分離できることが分かりました。

[How to open new windows as separate application instances for Chrome, Terminal?](https://apple.stackexchange.com/questions/230390/how-to-open-new-windows-as-separate-application-instances-for-chrome-terminal)

ここでいうプロファイルというのは、いわゆるGoogle認証のユーザのことではなく、アプリケーションのユーザデータのことです。
例えば プロファイル名 `dev` でマネージメントコンソールにログインするなら、以下のコマンドでChromeを起動します。

```
$ open -na "Google Chrome" --args --incognito --user-data-dir=$HOME/Library/Application\ Support/Google/Chrome/aws-vault/dev $(aws-vault login dev --stdout)
```

新しいChromeのプロファイルを作ると、初回のみ以下のダイアログが出てきますが、「Google Chromeを起動」を選択すると、2回め以降は聞かれません。(AWSアカウントごとに初回のみ聞かれます)

![install.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/61152/557d6537-78d9-8aa4-2024-c1e1b9a3752f.png)

これでAWSアカウントごとにシークレットウィンドウを分離して、マネージメントコンソールにログインできるようになりました。
複数のAWSアカウントを管理している場合は、以下のようなAWSアカウントのプロファイル名を引数に取るzshのaliasを定義しておくと、

```zsh
alias avl='(){ open -na "Google Chrome" --args --incognito --user-data-dir=$HOME/Library/Application\ Support/Google/Chrome/aws-vault/$@ $(aws-vault login $@ --stdout) }'
```

以下のようなコマンドで、マネージメントコンソールをシュッっと開いてログインできるようになります。

```zsh
$ avl dev
```

どうぞご利用下さい。
射影変換って結局なにすればええんや

## モチベーション
- Photoshopの自由変形(ctrl+T)みたいなのを実装したかった
- なぜかJSでネイティブで書きたかった

## 概略

### 要件定義
- input : 射影前の画像および射影後の4点
- output : 変形した画像

### 実装
- 射影前後の座標の変化から射影関数を求める
- output画像のピクセルの位置に相当する、input画像上の点を上記射影関数の逆写像から求める
- その点は確率1で格子点上にないので、周囲のピクセルから補間を行う

## 射影関数
射影変換の一般系は、$f:(x,y)\rightarrow(X,Y)$とすると

```math
(X,Y) = f(x, y) = \bigg(\frac{Ax+By+C}{Gx+Hy+1}, \frac{Dx+Ey+F}{Gx+Hx+1}\bigg)
```
である。
定数は$A\dots H$の8個であるので連立8元一次方程式となるが、4点およびその移動先、合計8点を指定してやれば解けるはずである。

計算量削減のため、[遠近法の射影変換パラメータ計算の高速化](https://ja.osdn.net/projects/nyartoolkit/docs/tech_document0001/ja/tech_document0001.pdf)を参考にし、$4*4$の行列計算に落とし込む。($E$の式にtypoがあるので注意)
式$(11)$の$G,H$の部分について、

```math
G = a - Cb\\
H = c - Cd\\
G = e - Ff\\
H = g - Fh
```
とした時に、

```math
  \left\{
    \begin{array}{l}
      C =\frac{h(a-e)-f(c-g)}{bh-fd}\\
      F =\frac{d(a-e)-b(c-g)}{bh-fd}
    \end{array}
  \right.
```

と解ける。$A,B,D,E,G,H$はこれらから簡単に求まる。

### 場合分け
色々と考えていくと、移動先の点に$(0,0)$が含まれる場合などで、情報量が減って逆行列が求められなくなる。適宜 **"うまい場所に変形→平行移動"** のように対策をしてやる必要があると思う。

私の場合は、左上の点と右下の点の中心に原点が来るように変形してから移動した。(これでももちろんバグり得る)


## 画素補間
射影変換に限らず、ほとんどの変形では(格子点→格子点)となるとは限らない。
そのため、像の格子点は始閾からのサンプリングによって得る。これを**補間(interpolation)**という。

https://imagingsolution.net/imaging/interpolation/


```math
Dst(X, Y) = Interpolation \big(Src,  f^{-1}(X, Y) \big) 
```

出力の際はBicubic法を用いればいいが、メタメタに重いのでプレビューにおいてはNearest Neighborを用いると良いと思う(めっちゃジャギるけど)。

### 境界処理
Bicubicは周辺16画素を参照するので、縁の場合にundefinedになる。

もっとも縁のピクセルで埋めたり、鏡像(折り返し)扱いにしたり、周りに透明なピクセルを配置したりするなど様々にやりようはあるので、目的によって実装仕分ければ良い。



## コード(概略だけ)

```JavaScript:projection.js
function projection(img, dst_pts, output_size) { // counterclockwise from upperleft

  let src_pts = [[0, 0], [0, img.height], [img.width, img.height], [img.width, 0]];
  const [A, B, C, D, E, F, G, H] = calc_proj_params(src_pts, dst_pts);
  const [w, h] = output_size;
  let output = new ImageData(w, h);
  let dw = parseInt((src_pts[2][0] - src_pts[0][0]) / 2);
  let dh = parseInt((src_pts[2][1] - src_pts[0][1]) / 2);

  for (let i = 0; i < w * h; i += 1) {
    let [X, Y] = [i % w + dw, parseInt(i / w) + dh];
    let x = (A * X + B * Y + C) / (G * X + H * Y + 1);
    let y = (D * X + E * Y + F) / (G * X + H * Y + 1);
    if (1 < x && x < img.width - 1 && 0 < y && y < img.height - 1) {
      let b = nearest_neignbor(img, x, y);
      //let b = bicubic(img, x, y);
      output.data[4 * i] = b[0];
      output.data[4 * i + 1] = b[1];
      output.data[4 * i + 2] = b[2];
      output.data[4 * i + 3] = b[3];
    }
  }
  return output;
}
```

## まとめ
- 処理は煩雑だがアルゴリズム自体は割と単純
- 補間アルゴリズムは普通に他の場所でも使えそう
Raspbian メール転送

# Postfix
```
# apt install postfix
```

YES

```
# vi /etc/postfix/main.cf
```

```
relayhost = [smtp.gmail.com]:587
smtp_use_tls = yes
smtp_tls_CApath = /etc/pki/tls/certs/ca-bundle.crt
smtp_sasl_auth_enable = yes
smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd
smtp_sasl_tls_security_options = noanonymous
smtp_sasl_mechanism_filter = plain
```


# 認証設定
Google アプリパスワード生成

```
# vi /etc/postfix/sasl_passwd
```

```
[smtp.gmail.com]:587    <gmail_address>:<password>
```

```
# postmap hash:/etc/postfix/sasl_passwd
# rm /etc/postfix/sasl_passwd
```

# 転送設定
```
# vi /etc/aliases
```

```
pi: username@gmail.com
```
Android Debug Bridgeとは？Android開発のデバッグツールのインストールと初期設定


## Android Debug Bridgeとは？

- windowsやmacといった開発端末から、android端末の操作を行えるようにするためのコマンドラインツール
- Android SDK Platform-Tools package に同梱されている
- コマンドは、全てadbで始まる

## 環境
- macOS Mojave
- Android Studio 3.5
- zsh

## 前提条件
- Android Studioセットアップ済み
- SDK Managerで何かしらのversionのsdkがインストールされている

## インストールの前に

terminalで、コマンドが見つかればすでにインストールされているのでこの記事を読む必要はありません。

```bash
# adbがない or adbへのパスがとおっていない場合
$ adb
zsh: command not found: adb

# adbが利用可能な場合
$ adb --version

Android Debug Bridge version 1.0.41
Version 29.0.4-5871666
Installed as /Users/TomoakiTANAKA/Library/Android/sdk/platform-tools/adb

```

## インストール

### 1. SDK Managerを開き、パスを確認する

SDK Managerを開いて、Android SDK のインストールパスを確認します。
SDKがインストールされていれば、adbは同梱されているので、パスを通すだけでOKです。

![Pasted_Image_2019_10_01_15_39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37873/0aaf916c-4f05-034d-666d-6ae4021834c0.png "Pasted_Image_2019_10_01_15_39.png")


![Pasted_Image_2019_10_01_15_42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37873/7b4ba857-5050-6a2f-aa2c-d83c18deb592.png "Pasted_Image_2019_10_01_15_42.png")

### 2. adbのパスを通す

`ユーザ名` の部分は、1. で確認したパスを参考にしてください。

```
$ export PATH=$PATH:/Users/<ユーザー名>/Library/Android/sdk/platform-tools
```

### 3. 動作確認

`adb` コマンドを打ち込んで、結果がでればOKです。

```
$ adb --version

Android Debug Bridge version 1.0.41
Version 29.0.4-5871666
Installed as /Users/TomoakiTANAKA/Library/Android/sdk/platform-tools/adb
```


### 4. （オプション）rcファイルへのパスを書き込む

exportコマンドを毎回実行するのはめんどくさいので、設定ファイルに記述しておきます。

```bash
vi ~/.zshrc
```

`ユーザ名` の部分は、1. で確認したパスを参考にしてください。
.zshrcに下記の記述を追加

```txt:.zshrc
export PATH=$PATH:/Users/<ユーザー名>/Library/Android/sdk/platform-tools
```

```bash
source ~/.zshrc
```

## おわりに

adbをコマンドラインから利用できるようになりました。
adbを使うと、firebaseにログを送信しつつ、firebaseのdebugViewでログの確認をできたりします。


## 参考

- [MacにAndroid Studioを入れて、adbを使える状態にする](https://qiita.com/hujuu/items/509fb234d29de10d6f1b)
- [Android Debug Bridge (adb)](https://developer.android.com/studio/command-line/adb?authuser=1)
【CSS3】Safari,Chromeでfontがデザインより太く見える時の対処法

#はじめに
みなさん、構築をしていて、
「あれ？ 指定されたfont-weightにしているのに反映されたテキストがデザインより太く見える・・・」
ということに遭遇したことあるでしょうか？

safariとchromeでこの現象に遭遇して長らく解決できなかったのですが、
先日ふとしたきっかけで対処法が分かったのでメモに残しておきます。

#問題点

デザインで指定した**`font-weight`**とブラウザで表示されたfontの文字の太さが何故か異なる。
指定されたfontで**`bold`**にしているのであっているはずなのに何故・・・？

![custom – 1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/486362/20dd474b-7a4e-bacd-8685-34708a4afe5a.png)

条件が同じにもかかわらず、なんだか一回り太く見えてしまい
デザイン全体の雰囲気が崩れてしまいますよね。

何故こうなってしまうのか、原因をいろいろ調べてみたところそれっぽいのを発見！

#Webブラウザ上の文字のアンチエイリアス設定をしていないのが原因

font-smoothingというCSSプロパティを使うことによって
フォントのアンチエイリアスを調節することでfontが太くなる問題を解決できました。

アンチエイリアスを調節することで滑らかな表示が可能になり
スマートな文字の印象に変わります。

注意点として、このプロパティを使えるのはMacのみで、Windowsでは使えません。
また、font-smoothingプロパティはブラウザによって微妙にかけ方が違います。

#アンチエイリアスとは？

コンピュータでの文字表示はピクセル単位より細かく描画することができません。
このため、物体の輪郭にジャギーと呼ばれるギザギザが発生してしまいます。
このジャギーを軽減し少しでも目立たなくするために、文字の輪郭を背景となじませるように、色を滑らかに変化させることをアンチエイリアスといい、その処理をアンチエイリアシングと言います。

**font-smoothing** はこのアンチエイリアスをコントロールするCSSプロパティになります。
参照サイト： https://dainamo.jp/font-smoothing/

#font-smoothingでフォントのアンチエイリアスを調整することで解決

```ruby:css
-webkit-font-smoothing: antialiased;
```
ブラウザにより設定は多少異なりますが、これでデザイン通りのフォントの太さになりました。

対応ブラウザは下記サイトで確認可能↓
https://caniuse.com/#feat=font-smooth

#まとめ
デザインをする上でfont-weightはかなり重要な部分になるため、この設定があるかどうかでクオリテイを左右しかねないので、解決方法が分かって本当に良かったです。

ブラウザによってこんな機能のちがいがあるのだという勉強になりました。

参照サイト↓
https://dainamo.jp/font-smoothing/
https://qiita.com/htomine/items/3086f30ca09adea2f8e0
Dockerコンテナから、同一ホストマシンのPostgresqlに接続できるようにする(CentOS7)

Dockerコンテナから、(コンテナを起動している)ホストで起動しているPostgresqlに接続できなかった時に試行錯誤した備忘録的なやつ。

## 前提
- Dockerネットワークはデフォルト(bridge)で起動する
- [Postgresqlの接続設定](https://gist.github.com/toenobu/5026188d73b69046248c8792962a7b22)をやったけど動かなかった

## 環境
- CentOS 7.6
- Postgres Server 9.2.24
- Docker 1.13.1

## 結論
firewalldにPostgresqlを追加する。

```
# firewall-cmd --zone=public --add-service=postgresql
# firewall-cmd --reload
```
1個目の`firewall-cmd`コマンドに`--permanent`オプションをつけるとOSを再起動しても設定が継続する。

## 参考
http://www.madeinclinic.jp/%E3%83%87%E3%83%BC%E3%82%BF%E3%83%99%E3%83%BC%E3%82%B9/20150118/
MySQLでSELECT文の結果に固定値を入れる

# SELECT文の結果に固定値を入れる

以下のようにすることで、select文の結果に固定値を入れることが出来ます。

```sql:sql
SELECT id, name, email, '固定値' AS label FROM users;
```

実際に以下のようなSQLを実行すると

```sql:sql
SELECT id, name, 'ゲスト' AS label FROM users;
```

以下のように固定値が入ります。

|id |name|label|
|--:|---|---|
|  1|一郎|ゲスト|
|  2|次郎|ゲスト|
|  3|三郎|ゲスト|

では、どんなときに使うかですが次に示します。

# INSERT文と組み合わせると便利
SELECT結果の固定値はINSERT文と組み合わせると便利です。

```sql:sql
INSERT INTO wainting_users (id, name, label) SELECT id, name, 'ゲスト' AS label FROM users;
```

INSERT文と合わせることでSELECT文の結果につけたいカラムの情報を固定値で追加してINSERTすることが出来ます。
【Python】スプレッドシート操作小技集


以下の形でクライアントを作成している前提で書いていきます。
※herokuでデプロイする想定で作っているので環境変数から読み込むようになっていますがよしなに読み替えてください。

```python
#2つのAPIを記述しないとリフレッシュトークンを3600秒毎に発行し続けなければならない
scope = ['https://spreadsheets.google.com/feeds','https://www.googleapis.com/auth/drive']

TYPE           = os.getenv("GS_TYPE",           "")
CLIENT_EMAIL   = os.getenv("GS_CLIENT_EMAIL",   "")
PRIVATE_KEY    = os.getenv("GS_PRIVATE_KEY",    "")
PRIVATE_KEY_ID = os.getenv("GS_PRIVATE_KEY_ID", "")
CLIENT_ID      = os.getenv("GS_CLIENT_ID",      "")

key_dict = {
          'type'          : TYPE,
          'client_email'  : CLIENT_EMAIL,
          'private_key'   : PRIVATE_KEY,
          'private_key_id': PRIVATE_KEY_ID,
          'client_id'     : CLIENT_ID,
}

SPREADSHEET_KEY = os.getenv("SPREADSHEET_KEY", "")
credentials = ServiceAccountCredentials.from_json_keyfile_dict(key_dict, scope)

#OAuth2の資格情報を使用してGoogle APIにログインします。
gc = gspread.authorize(credentials)
```

## 新しいシート作成


```python
gc.add_worksheet(title='test', rows=5000, cols=26)
```

rowとcolは必須のようです。

## シートが存在するかチェック

```python
worksheets = gc.worksheets() # シートの一覧が取得できる

for sheet in worksheets:
    if 'シート1' == sheet.title:
        return True
    else:
        return False
```

調べた限りシートが存在するかを答えてくれる関数はなかったのでこんな感じでやってます。

参考：
https://qiita.com/ymk1102_1t/items/5c29d4f3229301e7c50e
テキストサイトで使いたいテンプレート

テンプレートはあくまでもテンプレートとして
自分でカスタマイズやアレンジをして使ってみようという気持ちです

テンプレートも素材もたくさん見て学んで
自分で作れるようにしたい

JSで作りこんだ動きのあるサイトもつくってみたいので
よいサイトがあれば随時更新します


##テンプレート


◇線の細いイラストと組み合わせて
　https://alicex.jp/oysm/74/

◇きれいなバナーと組み合わせて
　http://vmeer.55950.xrie.biz/?guid=on

◇自分で撮った写真と組み合わせて
　http://vmeer.99162.blog.xrie.biz/?guid=on

◇ブログっぽい見え方をする
　http://drop.37449.xrie.biz/?guid=on

◇配色の勉強に
　http://drop.18668.xrie.biz/?guid=on

◇自分でいじっていろいろと応用が利きそう
　http://drop.11190.xrie.biz/?guid=on



##お手本サイト

お手本サイト集め
テキストサイトとは趣ちがうけど、とっても参考になる
https://www.manicyouth.jp/webdesign-matome-dressing-up/
よくあるPythonプロジェクトのREADME（開発手順の部分のみ）

Pythonプロジェクトを作るたびに、`README.md` や `DEVELOPMENT.md` の**開発手順** には毎回似た内容を書いている気がするので、コピペ用テンプレを残しておきます。


    # プロジェクト名

    ## 開発手順

    ### ブランチ戦略

    Githubフローです。MR先はmasterブランチです。

    ### 開発環境構築

    [pipenv](https://github.com/pypa/pipenv) で環境を作成します。pipenv の使い方は[公式ドキュメント](https://pipenv-ja.readthedocs.io/ja/translate-ja/)を参照してください。

    ```shell
    pip install pipenv
    pipenv install --dev
    ```

    ### テスト

    [pytest](https://docs.pytest.org/en/latest/)を使います。テストは以下のように実行できます。

    ```shell
    pipenv run pytest --doctest-modules
    ```

    ### スタイルチェック

    [mypy](http://www.mypy-lang.org/), [black](https://github.com/psf/black) などでテストします。`bin/lint`でまとめて実行できます。

    ```shell
    bin/lint
    ```

    ### リリース

    マージ後、担当者は以下の手順でリリースします。

    **1. setup.cfg のバージョンを更新**

    `setup.cfg` 関数の引数のバージョンを更新します。

    ```diff
      [metadata]
      name = hoge
    - version = 1.4.0
    + version = 1.5.0
    ```

    変更をコミットし push してください。

    ```shell
    $ git commit -m "バージョン 1.5.0" setup.cfg
    $ git push
    ```

    **2. バージョンタグの追加**

    `setup` と同じバージョン番号のタグを作ってください。

    ```shell
    $ git tag 1.5.0
    $ git push --tag
    ```

    **3. 社内PyPIへのデプロイ**

    ローカルマシンで `setup.py` を実行して公開物をビルドします。

    ```shell
    $ pipenv run python setup.py sdist
    ```

    [twine](https://twine.readthedocs.io/en/latest/)を使って社内PyPIにデプロイします。環境変数を定義し忘れると公式PyPIに公開されるかもしれないので注意。

    ```shell
    $ export TWINE_REPOSITORY_URL=https://packages.example.com/repository/pypi-internal/
    $ export TWINE_USERNAME=ore-boku # LDAPユーザー名
    $ pipenv run twine upload dist/hoge-1.5.0.tar.gz # 公開物を指定して実行
    ```

    **4. リリース連絡**

    社内Slackで連絡してください。

    ```
    各位

    hoge のバージョン 1.0.0 をリリースしました。以下の機能を追加しています：

    - 機能の説明
    - 機能の説明
    - 機能の説明

    なお、後方互換性を崩す変更は加えていません。

    アップデートは、各チームごとに影響を判断した上で、以下のコマンドで行なってください。

    pip install --upgrade hoge==1.0.0
    ```
PuttyからWinSCPにインポートしたサイトの初回接続で「ホスト鍵が一致しない」エラーが出る場合の対応方法

WinSCPには、PuttyまたはFileZillaからサイトをインポートする機能があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/6b28ca75-a238-8fc3-eee8-8664966a42ac.png)

Puttyのサイトを選択して取り込み可能です。
![aaaaa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/48c06cd9-7b00-5d1d-ef51-37890bb6d59b.png)

ところが、インポート後の初回接続で `ホスト鍵が設定された鍵 "xxxxxxxxxx" と一致しません！ ホストの認証鍵は xxxxxxxxxx です。認証に失敗` というエラーになる場合があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/8860ffae-58d2-f00a-b68e-60f3f7278748.png)

この場合、インポートした設定をあれこれ変えてしまいがちですが、<font color="red">一度WinSCPを終了し、再度起動するだけで接続できるようになります。</font>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/516a68cc-92d6-a181-0b2a-c158f459d499.png)
正常に接続できる場合。マスクしてありますが表示されている鍵はエラー画面のものと同じです．．．

このエラーは WinSCP 5.9.4で確認しました。インポート直後の鍵の処理に問題あるのかと思いますが、対応としては<font color="red">WinSCPを最新版(2019/10/01 時点で 5.15.4)にしても発生しない</font>ので、可能な限り最新版に更新するようにしましょう。
GCEのカスタムイメージを別のプロジェクトにコピーする

## 手順
- 【移行元】手動で作成したGCEインスタンスをカスタムイメージとして保存する
- 【移行元】カスタムイメージをGCSにエクスポートする

 ```bash:command
$ gcloud compute images export --destination-uri <出力先URI> --image <作成したカスタムイメージ名> --project <移行元プロジェクトID>
```

```bash:example
$ gcloud compute images export --destination-uri gs://dev-my-bucket/my-gce.tar.gz --image my-gce --project dev-my-project
```

- 【移行元】エクスポートしたイメージファイル（tar.gz）をローカルにダウンロードする
- 【移行先】3.でダウンロードしたイメージファイルを移行先のGCSにアップロードする
- 【移行先】カスタムイメージをGCSからインポートする

```bash:command
$ gcloud compute images create <作成するカスタムイメージ名> --source-uri <GCSのイメージファイルURI> --project <移行先プロジェクトID>
```

```bash:example
$ gcloud compute images create my-gce --source-uri gs://prd-my-bucket/my-gce.tar.gz --project prd-my-project
```

## 注意点
カスタムイメージのサイズが非常に大きい場合、ローカルへのダウンロードや移行先プロジェクトへのアップロードでうまく行かない or 時間がかかりすぎることがあるかも。


## 参考
https://cloud.google.com/compute/docs/images/export-image?hl=ja
https://cloud.google.com/compute/docs/images/import-existing-image?hl=ja
自分のポートフォリオを説明するだけ

# はじめに

転職活動用に自己紹介ページを作りました。

でも私はもうエンジニア4年生なのに、ただの静的な自己紹介ページだと一応CRUDとかをやってる入門用のTODOリストにも及ばないし……。。
と思って一応背景を動くようにしたりログインの有無によって表示内容を変えたりするようにしました。

背景がキラキラしています。
ただ、**背景がうるさい**と肝心の自己紹介を読むときに**とても気が散る**と思うので、止められるようにもしました。
右上のカメラのアイコンのところでドラッグしたりスクロールしようとしたりすると、視点を変えて見ることができます。
![portfolio.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265900/29192e75-e372-28ef-ac21-8ef218a3da66.gif)

# 成果物

[portfolio(自己紹介ページ)](https://jesusisao-portfolio.herokuapp.com/)

# コード

[github jesusisao/portfolio](https://github.com/jesusisao/portfolio)

# 構成図

![スクリーンショット 2019-09-29 13.26.47(2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/265900/245d37c7-5687-066c-4bd4-745974e44601.png)

# 説明

左上にログインボタンがありますが、ログインすると私の本名やら経歴やらにアクセスできてしまうので採用担当者の人以外は秘密です！
ネット上で真名を決して出してはならないという教えを守り続けるインターネット老人なので、ごめんなさい。

直近の仕事でNuxt.jsを使っていたので、これのTypeScript対応をしたらどんな感じになるのだろう、と思ってNuxt + TypeScriptを実験的にやってみました。

それから、ログインはFirebase Authを、これまた初めて使ってみました。ログインだけFirebaseを利用しつつもバックエンドは普通のAPIサーバーやMySQLを使いたかったので、クライアント側でFirebaseにトークンを取得しに行って、クライアントが取得したトークンを、自前のExpressのAPIで受け取った後にFirebase Admin SDKを使って検証し、ログインを確認するというようなことをやっています。

Herokuは無料なのでありがたく使わせて頂いています。MySQLの接続情報やFirebase Admin SDKのアクセス情報などの大事な情報は当然コード中に書くわけにはいきません。今回はそういった情報はHerokuの都合もあってすべて環境変数に持たせるようにしています。

背景の動くキラキラはThree.jsで作りました。実は最初はCanvas APIでパーティクルを作ろうとしたのですが、処理がとてつもなく重たくなってしまって普通のPCやスマホでは動かせない……つらい……となりました。そこからWebGLやThree.jsなるものを知って勉強して作り直しました。私のiPhone6上でもヌルヌル動きます。WebGLのパワーはすごい。

# 感想

「次転職する時は最低限ポートフォリオないとあかんやろなぁ…」と思ってまとまった休みが取れたお盆休みとかでわーっと作っていたのですが、今まで触ったことのない技術（FirebaseとかWebGLとか）に触れるのはなんだかんだ楽しかったです。
特にWebGLは面白く、大学生の時に映像を少しだけかじっていたのもあってインタラクティブに動くパーティクルの楽しさに惹き込まれました。仕事に生きるかは全く不明ですが、時間のある時にちょいちょい触っていきたいですね。
SpringBootのScaffoldツールを作ってみた

# はじめに

RailsやLaravelにはScaffoldコマンドがありますが、SpringBootにはなさそうだったので自分で作ってみました。
このツールで作成されるのは、RestController、Service、Repository、Modelです。

# 準備

1. [こちら](https://github.com/mr-hisa-child/sbcui)からリポジトリをダウンロードします。
2. ダウンロード後、適当なところに解凍します。
3. 解凍したフォルダ内にあるbinフォルダをパスに通します。


# 使い方

1. SpringBootプロジェクトのルートに移動します。
2. 以下のコマンドを実行します。

```
sbcui -p com.example.demo -c "User[id:Long,name:String]"
```
```-c``` のクラス情報はダブルクォートで囲むのを忘れずに！
  
実行後、以下のようなファイルが作成されます。
![コメント 2019-10-01 144949.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/53830/749e620c-e49f-f638-d723-a3b1631844a3.png)


```java:domain/model/User.java
@Entity
@Table
@Getter
@Setter
@EqualsAndHashCode(of = { "id" })
public class User {
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    @Column
    private Long id;
    @Column
    private String name;
}
```

```java:domain/repository/UserRepository.java
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
```

```java:domain/service/UserService.java
@Service
public class UserService {

    @Autowired
    private UserRepository userRepository;

    public List<User> findAll(){
        return userRepository.findAll();
    }

    public User find(Long id){
        return userRepository.findById(id).orElse(null);
    }

    public User save(User entity){
        return userRepository.save(entity);
    }

    public void delete(User entity){
        userRepository.delete(entity);
    }
}
```

```java:app/controller/UserController.java
@RestController
@RequestMapping("user")
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(method = RequestMethod.GET)
    public List<User> findAll() {
        return userService.findAll();
    }

    @RequestMapping(method = RequestMethod.GET, value = "{id}")
    public User find(@PathVariable Long id) {
        return userService.find(id);
    }

    @RequestMapping(method = RequestMethod.POST)
    public ResponseEntity<String> create(@Validated @RequestBody User input) {
        userService.save(input);
        return ResponseEntity.ok().build();
    }

    @RequestMapping(method = RequestMethod.PUT, value = "{id}")
    public ResponseEntity<String> update(@PathVariable Long id,
            @Validated @RequestBody User input) {

        User entity = userService.find(id);

        if (Objects.isNull(entity)) {
            return ResponseEntity.badRequest().build();
        }

        userService.save(input);

        return ResponseEntity.ok().build();
    }

    @RequestMapping(method = RequestMethod.DELETE, value = "{id}")
    public ResponseEntity<String> delete(@PathVariable Long id) {

        User entity = userService.find(id);

        if (Objects.isNull(entity)) {
            return ResponseEntity.badRequest().build();
        }

        userService.delete(entity);

        return ResponseEntity.ok().build();
    }
}
```

# おわりに

毎回、手動で1セット作るのが手間だったので、このツールを使って効率化できるかなと。
よかったら、ぜひ、使ってみてください。
jboss/wildflyのJDKとstandalone-full.xmlを変えたい

## 背景
JDK11とJDK8でFrameworkのふるまいが若干異なり、wilfly(16.0.0.Final)のコンテナをJDK8で動くか確認しようとしていた。
それからwildflyの構成を変更する意図で、standalone-full.xmlを書き換えたいと考えていた。

以降、下記の実装として対応した内容を記録する。
- jboss/wildfly:16.0.0.Finalを使いたい
- デフォルトJDK11になっているけど、JDK8を使いたい
- standalone-full.xmlを書き換えたい

**※あくまで検証で試行錯誤した手順であり、本番環境等での適用を考慮した対応ではありません。**

## 実施したこと
### JDKの変更

以下の対応を実施。

- DockerfileにjDK8をyum installしてJAVA HOMEを設定する処理を追加
- yumインストールの処理は毎度実行されると重いので、分離してイメージをPrivate Registryに登録
- 通常利用するjboss/wildflyのDockerfileは上記のJDK入れ替え済みイメージをベースとして利用するように変更

### standalone-full.xmlの置換

- コンテナにログインしてls -alを実行すると、「/opt/jboss/wildfly/standalone/configuration/standalone-full.xml」のパーミッションは、「jboss:root」だった
- 普通にコピーするとjboss:rootにchownできないので、Dockerfile内でCOPYの処理時に「--chown=jboss:root」させた


## サンプル
下記をdocker buildして、private repo(my.private.repo.com:5000)に「/jboss/wildfly:16.0.0.Final-jdk1.8.0_222」として登録する。

```Dockerfile:Dockerfile.wildfly-jdk8
FROM jboss/wildfly:16.0.0.Final
USER root
RUN yum -y install java-1.8.0-openjdk-devel && yum clean all
ENV JAVA_HOME /usr/lib/jvm/java
RUN /opt/jboss/wildfly/bin/add-user.sh admin1234 Password1! --silent
```
CIや開発端末上で使うDockerfileは以下の形式にする。（必要に応じて、warファイルとstandalone-full.xmlを更新できる。）

```Dockerfile:Dockerfile
FROM my.private.repo.com:5000/jboss/wildfly:16.0.0.Final-jdk1.8.0_222

RUN ls -la /opt/jboss/wildfly/standalone/configuration/ && rm -f /opt/jboss/wildfly/standalone/configuration/standalone-full.xml
COPY --chown=jboss:root ./standalone-full.xml /opt/jboss/wildfly/standalone/configuration
ADD target/*.war /opt/jboss/wildfly/standalone/deployments/
RUN ls -la /opt/jboss/wildfly/standalone/configuration/ && ls -la /opt/jboss/wildfly/standalone/deployments/ && /opt/jboss/wildfly/bin/add-user.sh admin1234 Password1! --silent

```

## その他
- これがベストな対応かわからないので、もっとスマートな対応をご存知の方がいれば、教えてください。
ActiveModel as_json のオプション一覧

### .as_json(options = nil)

|option名|詳細|
|---|---|---|
|`root`|`true`を指定すると、root要素としてモデル名のkeyが挿入される（ネストがひとつ深くなる）|
|`only`|要素名の配列を渡すと、その要素のみが出力される|
|`except`|要素名の配列を渡すと、その要素以外が出力される|
|`methods`|モデルのメソッド名（配列可）を渡すと、そのメソッドの実行結果が出力される|
|`include`|アソシエーション名（配列可、ネスト可）を渡すと、その関連情報も一緒に出力される|

```ruby:root
user.as_json(root: true)
# => { "user" => { 
#        "id" => 1, "name" => "Konata Izumi", "age" => 16, "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true 
#    } }
```
```ruby:only
user.as_json(only: [:id, :name])
# => { "id" => 1, "name" => "Konata Izumi" }
```
```ruby:except
user.as_json(except: [:id, :created_at, :age])
# => { "name" => "Konata Izumi", "awesome" => true }
```
```ruby:methods
user.as_json(methods: :permalink)
# => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
#      "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
#      "permalink" => "1-konata-izumi" }
```
```ruby:include
user.as_json(include: :posts)
# => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
#      "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
#      "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" },
#                   { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] }
```
```ruby:include(example)
user.as_json(include: { posts: {
                           include: { comments: {
                                          only: :body } },
                           only: :title } })
# => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
#      "created_at" => "2006-08-01T17:27:13.000Z", "awesome" => true,
#      "posts" => [ { "comments" => [ { "body" => "1st post!" }, { "body" => "Second!" } ],
#                     "title" => "Welcome to the weblog" },
#                   { "comments" => [ { "body" => "Don't think too hard" } ],
#                     "title" => "So I was thinking" } ] }
```

### 参考

https://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html#method-i-as_json