OmniAICreator/Qiita-1.07M huggingface dataset:
Gmailとスプレッドシートを使って一斉メール送信できるスクリプトを作った

Gmail、スプレッドシートおよびGASを自分で調べて使える方向けの記事です。

# 設定方法

以下のようなスプレッドシートを作成します。

<img width="1274" alt="スクリーンショット 2019-12-24 17.19.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206259/1ae11cef-4c5a-c53a-af70-a6780add95bc.png">

以下のスクリプトをスプレッドシートに設定。

```javascript
function myFunction() {
  /* スプレッドシートのシートを取得と準備 */
  var mySheet = SpreadsheetApp.getActiveSheet(); //シートを取得
  var rowSheet = mySheet.getDataRange().getLastRow(); //シートの使用範囲のうち最終行を取得

  /* 送信の確認 */
  var today = new Date();
  var strDate=mySheet.getRange(4,2).getValue(); //送信日
  if (Utilities.formatDate(today, 'Asia/Tokyo', 'yyyy/MM/dd') !== Utilities.formatDate(strDate, 'Asia/Tokyo', 'yyyy/MM/dd')) {
    Browser.msgBox("送信日が違うため送信できません。");
    return -1;
  }
  var isOk = Browser.msgBox((rowSheet - 6) + "件送信されます。続けますか？", Browser.Buttons.OK_CANCEL);
  if (isOk !== "ok") {
    Browser.msgBox("送信を中止します。");
    return -1;
  }

  /* fromアドレス、差出人名を準備 */
  var strFrom = mySheet.getRange(2,2).getValue(); //fromアドレス
  var strSender = mySheet.getRange(3,2).getValue(); //差出人

  /* シートの各行についてメールを送信 */
  for (var i = 7; i <= rowSheet; i++) {

    /* 送信済みの行はスキップ */
    var sent = mySheet.getRange(i,4).getValue(); //toアドレス
    if (sent !== "") {
      mySheet.getRange(i,4).setValue("送信スキップ");
      continue;
    }

    var strEmail = mySheet.getRange(i,1).getValue(); //toアドレス
    var strSubject = mySheet.getRange(i,2).getValue(); //メールタイトル
    var strBody = mySheet.getRange(i,3).getValue(); //本文

    /* メールを送信 */
    GmailApp.sendEmail(
      strEmail, //toアドレス
      strSubject, //メールタイトル
      strBody, //本文
      {
        from: strFrom, //fromアドレス
        name: strSender //差出人
      }
    );
    
    /* メールの送信を記録 */
    mySheet.getRange(i,4).setValue("送信済み");
  }
}
```

ボタンにスクリプトを割り当てて出来上がり。

# 手順

1. スプレッドシート「2020xxxx_サンプル一斉送信」をコピーして適当に名前変更
1. 設定を入力
  1. 差出人メールアドレス
  1. 差出人
  1. 送信日
1. 送信リストの入力
  1. メールアドレス
  1. タイトル
  1. 本文
1. 「一斉送信する」を押下
1. （一度のみ）認証の確認で「続行」を押下
1. （一度のみ）自分のアカウントを選択
1. （一度のみ）「許可」を押下
1. 送信件数が表示されるので問題なければ「OK」を押下
1. 「送信状況（自動入力）」に送信結果が表示される
1. Gmailの送信済みから送信したメールを確認できる

# 注意事項

- 誤送信防止のため「送信日」が当日でないと送信できません。
- 空行はエラーとなるので、上に詰めたデータを作ること。
- Gmailに設定しているメールアドレスからしか送れない。
- 「送信状況（自動入力）」にすでに入力がある行は送信がスキップされます。
- A~D列はプログラムで指定していますので列の追加は禁止です。
- 1~6行もプログラムで指定していますので行の追加は禁止です。
- F列以降は自由に使って問題ありません。

用法用量を守って正しくお使いください。
スクラムで見積もりを正確に行うためのポイント

## まえがき
東南アジア発のスタートアップスタジオ、[GAOGAO](https://gaogao.asia/)にてエンジニアをしているMass-minと申します。
スクラム開発で気をつけることを以前つらつらと書いてきましたが、今年ラストのアドベントカレンダー担当もスクラム開発についての記事でシメたいと思います！サクッと行きます、よろしくお願いします！

今年の記事たち↓
[【爆速Terraform入門】AWS環境をコードで管理してみよう](https://qiita.com/mass-min/items/27a071ffa346ce05ed29)
[【ザンギョーウはヤメロ】スクラム開発で、チームが絶対に残業してはいけない理由](https://qiita.com/mass-min/items/9c28d77b626581e35fe6)
[新人エンジニアが意識すると良いなと思うことまとめ](https://qiita.com/mass-min/items/de9c8bed8fb794ba9450)

## スクラム開発に置ける見積もりとは
今回のトピックはズバリ「見積もり」。何気なく使っているこの言葉ですが、スクラム開発において見積もりとはどういった意味をもつのでしょうか。

答えを先に言うと、**「一定期間の開発において、事業インパクトを大きくするための重み付け」**となります。もうちょっと深堀りしてみましょう。

スクラム開発では、issueをバックログに積み、issueの優先度を決めることで、どこまで作るかおおよその見通しを立てます。
このとき基本的にはPOがプロダクトを作りたい順にissueを積んでいきますが、開発メンバーがそれぞれのissueの見積もりをすることによって、issueに「重み」が生まれます。多くの場合、スクラムポーカーという手法で見積もりは行われます。スクラムポーカーでは、開発メンバー全員がissueに対して見積もりポイントを出し、最終的に全員の合意を得たポイントがissueの見積もりとなります。

この重み付けを加味すると、issueの優先度が変わります。「このissueに3ポイント掛けるくらいなら、こっちのissueの方が小さい工数で事業的にインパクトがある」という判断になるケースがあるってことですね。
言い換えるならば、**見積もりが正確にできると短いサイクルで大きい成果を出せる可能性が高まる**ってことです。ステキなことですね🥳

## 見積もりがずれる理由
理由は簡単です。**スプリント計画時点で、開発メンバー間でissueへの理解度がずれているから**です。
スクラムポーカーでポイントを付けた時、自分にとって不確定なものが多いissueだとずれるってことですね。合意が取れたポイントを最終的に付けるとはいえ、全員のポイントの平均値を付けていたりすると簡単にずれてしまいます。
これは当然といえば当然で、**やったことないものがどれくらいで終わるかなんて分かるはずもありません。**
これはしょうがない。どうしようもない。...本当にどうしようもないのでしょうか？

## 正確に見積もるために気をつけること
見積もりがずれる理由は分かりました。ということは、それを潰せば正確に見積もりができるはずです。
でも、やったことないものには見積もりなんて付けられない...じゃあどうすればいいのでしょうか？

答えは単純で、**「分からないところを潰すための時間に見積もりを付ける」**です。禅問答の答えみたいですが、分からないところの調査が終われば今度は正確な見積もりができますよね？スクラムでは**「見積もりの精度を上げていくこと = 開発チームの成長」**なので、分からないところをどんどん減らすのはとても重要です。
調査は1日ガッツリ取ってやるのもいいですし、半日集中で終わらせるのもいいでしょう。調査issueは正直「自分がどこまでやるか、どれだけ時間をかけるか」で見積もりを振ればそれでOKです。本来やりたかった作業issueは、調査issueが終わりやることが明確になった段階で改めて見積もりをしましょう。

調査にポイントを振ってしまうと、最初は見た目の総ポイントが増えます。しかし長期目線で見ると、次似たようなissueを見積もるときは、わからない点は既に潰せている状態で見積もることが出来ます。
したがって、開発チームの見積もりと出力が段々と合うようになってきます。あとはこの繰り返しで、正確な見積もりを出せるように調査issueを先に消化し懸念点をなくしていくと、自然と強いチームになっていきます。

## まとめ
### スクラムにおいて見積もりがずれる理由
- スプリント計画時点で、開発メンバー間でissueへの理解度がずれているから
    - 単純に、やったことがないから

### 正確に見積もるためのポイント
- 分からないところを潰すための時間に見積もりを付け、どう作業すればいいのか分かった状態にする
- 全部見通しが立った時点で、改めて作業issueに見積もりをする

見積もりが正確になれば、無理なissueの積み方もしなくなるので残業もなくなるはず。チームとして成長して、ぜひノー残業で余裕を持ってクリスマスデートに出かけましょう！
皆様、よいクリスマスをお過ごしください！🥳🥳
たのしくラクしよう、そのためのJMeter

#Webアプリ/テストは面倒？
テストにつきもののエビデンス。いちいち「どんな操作したのか」を記録しなければいけない。そのために画面ショットを取るのを繰り返す。面倒くさくない？

しかもエビデンス画像を、エクセルとかに貼り付けて保存する。ファイルサイズが馬鹿にならない。だいたいそんな毎度の操作していたら、作業時間が通常操作の数倍もかかってしまう。あー、めんどくさい。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556340/78b24f40-56e9-a949-bd5d-e8d2f27ab75e.png" width=30%>
しかも途中でプログラム修正したら、もういちど同じ手順でやり直し？
だれか責任者呼べー！


#JMeterの強み（VS人手）
ブラウザ操作を記録し、再現するツール、JMeter。通常は「負荷テストツール」だと思われているが、繰り返し操作の回避だけでなく、JMeterの実行定義体が操作内容の証左になる。こんな便利なもの、使わない手はない！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556340/771b1ed5-106a-7541-9357-1dbe3fbc2fbc.jpeg">
Ver5になってデザインがカッコよくなった(以前はWindows3.1風・・・)


#手順の記録(保存)はカンタン！　
JMeterの操作記録について、便利機能のキモは「Proxy」。つまり、

[パソコンのブラウザ]　→　[Proxy]　→　[アプリの動くサーバ]

と同じ経路で、JMeterがニセProxyになって操作の結果を保存してしまう。
・・・つまり、HTML的には、SubmitしてHTTP通信でサーバ問い合わせる内容を保存し再現する、ということ。
ニセプロキシの追加方法は・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556340/a92c0fff-619f-4388-8775-2971228cc222.png)
とやると、追加される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556340/c1c802c8-45bc-90e9-52c0-6de347693de7.png)
ブラウザ側の設定も、コレでOK。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556340/ce5c1c6f-8e4d-6e33-7c8e-65c6a211c7ae.png" width=300>
あとはJMeterの[＞開始]をクリックして、ブラウザ操作するだけ。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556340/45af1adb-7996-fa92-5569-508c6dcbd411.png" width=300>


注意として、クライアントサイドのスクリプトについては「知らんがな」であること。
逆に言えば、動作記録する際に、まごまごして入力エリアの値をなんども直したり、ラジオボタンの選択をなんども行き来しても、最終的に、Submit()したときの状態で覚えてくれるから、キニシナイ


#カスタマイズのポイント
再現時間もこまかく指定可能。素で再現すれば、待ち時間ゼロで、あっという間に全操作を実行しちゃう。
操作の間にSleep(定数タイマ)を入れれば、人間ぽく、Submitして、画面が戻って、それから数秒後に次の画面でSubmitして、ということが設定可能。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556340/d44c7d65-b964-3070-e4a2-dc74898d90cc.png" width=500>
もちろん繰り返しループの機能もあります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556340/34bab27b-8203-096d-ad1a-9e4dca03b987.png" width=500>

もっと柔軟に使いたい？
たとえば記録した時の操作が、name="skatou" だったとして、Proxy機能で記録して、再現したら、いつでもname="skatou"のまま。動的に変えたくならない？

そんなときは、こんな便利機能があって・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556340/f477c13b-07fa-f20f-65f9-4bb7be84cb99.png)
このnameの値を、任意に入力できちゃう。

さらにさらに！、これは変数化することもできて、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556340/b6fd0611-9855-6ede-ab96-5fe50e7855e5.png)

で、この変数、CSV入力ができる！これぞJMeterの真骨頂！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556340/ac0b5db1-1f17-2d81-48c2-8526bd308ff9.png)


#使いたくなった？
とりあえずインストールし、Proxy設定し、ブラウザ操作してシナリオを記録。
その定義内容をみるだけでも、お昼の退屈な時間がパラダイスに！？

あ、そういや、JMeterの動作記録として、jmeter.logを出力するので、それがエビデンスとして使えますが、JMeterを２つ以上起動すると、出力の担保がアヤシイことだけは、記しておきます。
items()を使った辞書型の処理

items()をつかって、forループでひとつずつ取り出す。

```sample.py
dict = {
    "A": 1,
    "B": 2,
    "C": 3
}

for dict_str, dict_num in dict.items():
    print(dict_str,dict_num,sep="→")

# 出力結果
# A→1
# B→2
# C→3
```
中身を見てみるとリストの中にタプルが入っている。

```sample.py
print(dict.items())

# 出力結果
# dict_items([('A', 1), ('B', 2), ('C', 3)])
```
これらのタプルがアンパックされて

A → dict_str
1 → dict_num

へと入っていることが読み取れる。
定数の必要性、意味、使い所がわからないので調べてみた【初心者】

# 定数を代入する意味がわからない

## 定数について
本に記載がある内容では
**定数とは、スクリプトの中で何らかの意味を持つ値にあらかじめ名前をつけておく仕組みであると言えます。**
と書かれています。

```const構文.php
const 定数名 = 値;
```


## 疑問内容（変数にする必要性はあるのか）

本を読んでいて思ったこと
「**だからなに**」「**定数にする意味とは**」

```.php
<?php
$price = 1000;
$sum   = $price * 1.08;
?>
```

上記の文章では消費税が変わるたびに、スクリプトのあちこちにある「1.08」を変更しなければ行けないので
定数として定義をして下記のようにするみたいです。

```.php
<?php
const TAX = 1.08;
$price = 1000;
$sum   = $price * TAX;
?>
```

ただ、普通に下記で良いんじゃね？とか思ってしまう。

```.php
<?php
$tax = 1.08;
$price = 1000;
$sum   = $price * $tax;
?>
```

## メリット（調べた結果）
**一回代入したら、再代入がされない。**

下記のコードでテストしてみました。

```.php
<?php
const TAX =  1.08;
$price = 1000;
$sum   = $price * TAX;
print $sum."\n";
?>

<?php
const TAX =  1.05;
$price2 = 1000;
$sum2   = $price2 * TAX;
?>

=>出力は
1080
1080
```
ファイル内に一度でも定義されたら、何が何でも他の物を受け付けないみたいです。
⇨変数で書いた場合、同じ名前の変数で別の内容を書いてしまうとそれが影響してしまうため定数にすることでミスの予防になるみたいです。



## 参考サイト
<a href = https://rightcode.co.jp/blog/become-engineer/php-const>RIGHTCODE様</a>
とても参考になりました。
Youtuber向けにサービス開発したが需要が無かった話

##はじめに
プログラミング始めて半年くらいで自分の好きなタイ在住日本人Youtuberに使ってもらいたくて開発した時の話です。結論から言うと上手くいかなかったです。その原因としてリサーチ不足と自己満の開発であったからです。今後サービス開発をする人にとって今回の自分の経験が役立てばいいなと思い、今回のことの経緯をまとめておきます。

##結論
・競合サービスのリサーチが大切
・ユーザーにしっかりとリサーチすることも大切
・自己満のサービスになっていないか

##何を作ったのか
私は1つのMAP上でYoutuberは撮影した場所にピンをさすことができ、そこをクリックすると詳細情報が見れて最終的にはYoutubeチャンネルに移動する。そして視聴者はYoutuberに撮影して欲しい場所に対してピンを挿してリクエストできる双方向コミュニケーションサービスを開発しました。

##難易度
サービス自体はRuby on Railsで開発を行いProgate卒業すれば作れるような簡単なものでした。
MapにはGoogle map APIを使用しました。
そして機能自体もシンプルだったので開発自体はそれほど時間がかかりませんでした。

##実際に提案してみて
この時の最大にミスとしては自分が作りたいものを作ってから、これYoutuber向けに改良すれば良いだろうと仮説を立て開発してから売り込んだことです。そのため世界的な大企業が競合となるとも知らずに自信満々にサービスの提案をYoutuberの方に行いました。

####結果
・Google MapにあるMy Mapと言う似たような機能使っているが、それとは何が違うか聞かれ答えられなかった。
→正直自分でもGoogle Mapで十分だと感じました。
・Youtuberがこのサービスを「使うメリットが全くなかった。

##そこからの改善
せっかく作ったので諦めたくないなと思い、Youtuberの方にヒアリングなどを行い何か改善点はないか探りました。その結果1つだけありました。

それは**視聴者**の目線に立つことでした。

そのYoutubeチャンネルの視聴者は日本人だけではなく、タイ人の方もおられました。
そのことに関してリサーチを行った結果、Yotuberの方はタイ人視聴者からの撮影して欲しいリクエストが多くあるみたいですが、詳細情報が聞けずに諦めていたようです。さらに視聴者としてもここを動画にして欲しいなどの思いがあったみたいです。ここから私はタイ人視聴者と日本人Yotuberがコミュニケーションできるサービスに方向転換をしました。

##最終結果
最終的には良いサービスじゃん！とYoutuberの方に言って頂きました。
そしてリリースに向けて準備していたときに、Youtuberの方が解散をされてしまいました、、、
しかもやりとりをしていた方が脱退という形だったので大打撃でした。
結局その後いろいろありましたが、幻のサービスとして世に出ることはありませんでした。

##学び
結果的には失敗だったのですがたくさんの学びがありました。
それはサービスを開発する上でユーザーの立場になって考えるのは大切ということです。
こんな技術を使いたい！これ面白そう！って言うのが理由で開発も素晴らしいです。しかし誰かに使ってもらう前提で作るなら、徹底的にリサーチを行い他に似たサービスがないか調査するのは必要だなと思います。

これは今後開発以外の場面であっても使えそうな考え方かなと思います。

##最後に
今回かなり短い文面にはなりましたが最後までみていただきありがとうございます。
インターネットに関して全くの知識がなかった自分がサービスを作れるようになったと考えると感慨深いです。テクノロジーを使えば世の中にとって役に立つことを生み出すことができます。
しかし役に立つものしか生み出してはいけないかと言われると違うと考えてます。
意味のわからない、誰が使うかもわからないサービスであっても誰かの役に立つかもしれません。

今回リサーチは大事や自己満で作るな。どと言っておきながら、
最後に矛盾したことを言いますが**楽しかったら全てokです！**

メリークリスマス！！
.stopで伝播を防ぐ ❏Vue.js❏

マウスがのった場所のX軸とY軸を表示します。
ただし、`stop`にのった時は伝播を防いで`mousemove`イベントを発火させません。

[![Screenshot from Gyazo](https://gyazo.com/a576c49b61a3b2f98329d284540587e0/raw)](https://gyazo.com/a576c49b61a3b2f98329d284540587e0)

開発環境はJSFiddle
https://qiita.com/ITmanbow/items/9ae48d37aa5b847f1b3b

```html:html
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<div id="app">
  <p v-on:mousemove="changeMousePosition">のせて！
  <span v-on:mousemove.stop>stop</span></p>
  <p>x:{{ x }}, y:{{ y }}</p>
</div>
```

```javascript:javascript
new Vue({
  el: "#app",
  data: {
    x: 0,
    y: 0
  },
  methods: {
    changeMousePosition: function(event) {
      this.x = event.clientX;
      this.y = event.clientY;
    }
  }
})
```

`stopPropagation`を仕込むと伝播を防いでくれます。
本来はJS側で`event.stopPropagation()`と記述します。

しかし、Vue.jsでは、html側で`v-on:mousemove.stop`と繋げることで簡単に実装できます。

<br>
ではまた！
OpenID Connect, OAuth2.0のエンドポイントをポリシーによって保護する話 

本稿では、OpenID Connect/OAuth2.0のエンドポイントへのアクセスをポリシーに基づいて制御する機能について説明します。これはコンソーシアム版OpenAM 14に含まれることになった新規機能です。既にSAML2.0のケースについては、[SAML2.0のエンドポイントをポリシーによって保護する話](https://qiita.com/iwakata/items/c62b142c654b441205cf)で説明しました。目的や機能はかなり重複しているため、ここではOpenID Connect/OAuth2.0に特徴的な部分について説明します。

# 認証の方式、強度の指定方法

SAML2.0と同様にOpenID Connect/OAuth2.0でもACR(認証コンテキスト・クラス・レファレンス)を使用して認証の方式や強度をリクエスト時に指定することが可能です。詳細については、[この記事](https://qiita.com/TakahikoKawasaki/items/185d34814eb9f7ac7ef3#161-acr_values-%E3%83%AA%E3%82%AF%E3%82%A8%E3%82%B9%E3%83%88%E3%83%91%E3%83%A9%E3%83%A1%E3%83%BC%E3%82%BF%E3%83%BC)がお勧めです。この仕様は、OpenAMを含む多くの処理系で実装されてはいるのですが、あまり効果的に利用されては言えません。そこで、IdP/認可サーバ側に予め一定の条件（ポリシー）を設定しておき、その条件が満たされたときにのみトークンを発行するという新たな機能をコンソーシアム版 OpenAM 14 に追加しました。

# ポリシーの設定単位

SAML2.0の場合は、IdPとSPのEntity IDのペアに対してポリシーを定義しましたが、OpenID Connect/OAuth2.0の場合は、クライアントに対して定義を行います。クライアント毎に細かく条件を指定し、その条件が満たされたときにのみトークンが発行されるようにすることが可能です。

# 設定方法
設定方法について説明します。
## 機能の有効化
本機能はディフォルトでは無効になっています。有効にするために以下の手順を行ってください。

1. 管理コンソールを開き エージェント > OAuth2.0 クライアント　に移動し、対象となるクライアント名（ここでは"testClient")をクリックします
2. ページの末尾までスクロールし、「ポリシーに基づくエンドポイントの保護」を有効にします
![client.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43512/ba232e96-6aa7-031f-00fb-b36d771bc9b8.png)
表示されるページを下の方にスクロールします。
![enable1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43512/5b301279-60a4-cfef-47ed-d09f2774d4c1.png)


## ポリシーの定義
次にポリシーの定義方法について説明します。リソースタイプ、ポリシーセット、ポリシーの順に登録していきます。

### リソースタイプの登録
以下の項目を登録します

- 名前 : 任意の名前（ここでは"OAuth2Client"と指定） 
- パターン : client_id=\*
- アクション : IssueAssertion: 許可/拒否
（デフォルト値なのであとから変更可能です）
![newResourceType.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43512/deaa87d5-ed7c-16f1-24c2-affb590c0138.png)


### ポリシーセットの登録

「新規プロバイダポリシーセット」ボタンを押下します
![policySet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43512/1deac8a6-58b7-85c1-d327-f3cb795c81ad.png)

以下の項目を設定します：

- Id：OAuthClientService 
- 名前：任意の名前
- リソースタイプ： 上で登録したリソースタイプの名前(ここでは"OAuth2Client")

![newPolicySet.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43512/7119af83-a431-e1a7-167a-1192cef7c62c.png)


### ポリシーの登録

以下の項目を設定します：

- 名前：任意の名前
- リソースタイプ：上で登録したリソースタイプの名前(ここでは"OAuth2Client")
- リソース：対象となるクライアントの`client_id`を設定

その他は通常のURLポリシーと同じです。
![newPolicy.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43512/854518c0-07dd-ff27-4a5e-081019810d90.png)

# acr_valuesとの関係

arc_valuesの指定がある場合にはそれを無視することはありません。認証コンテキスト・クラスの条件が満たされた上でポリシーが満たされているかのチェックが行われます。
Jupyter NotebookでModuleNotFoundError: No module named XXXを解消する方法

#この記事について
`pip3`で`Selenium`を落としたのにJupyter Notebookを開くと以下のようなエラーがあってハマったので、対処法を記事にしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464595/c52fe772-6a71-dce7-be4a-7eb202fb1452.png)

##参考
[pythonのパッケージの保存場所](https://qiita.com/ophhdn/items/4d3ecc6354d92b7ac0ba)で、なんとなくパッケージの場所のPATHが通っていないんだと悟りました。
[MacでPATHを通す](https://qiita.com/nbkn/items/01a11392921119fa0153)でパスを通してみましたが、動きませんでした。

よって今回は自力で解決したので、以下にそのやり方を説明します。

##原因調査
まず、コンソールで、`pip3 show [Package Name]`のコマンドを叩き、Seleniumがどこに落ちているのかを確認しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464595/7afc050d-7f0a-0939-2f1f-29d33580e39b.png)

`Location:`の箇所がSeleniumが落ちている場所です。自分の場合は`/usr/local/lib/python3.7/site-packages`にあるようです。

次に、Jupyter Notebookを開き、通っているPATHを以下のコマンドで確認しました。（自分のユーザー名には一応モザイクかけてます。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464595/93725b35-1b62-dfa8-c7f4-2d38ad764b6a.png)

これをみて、`/usr/local/lib/python3.7/site-packages`がない事がわかります。

##解決策

[MacでPATHを通す](https://qiita.com/nbkn/items/01a11392921119fa0153)を参考に、PATHを指定してみましたが、うまくいかなかったので、以下のコマンドをJupyter Notebook内で実行しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/464595/25f1cc32-9490-24aa-487b-00013762edaa.png)

これで、`import selenium`がエラーなく実行されました。

##最後に
以上、Jupyter NotebookでModuleNotFoundError: No module named XXXを解消する方法でした。
GCViewerの使い方メモ

久しぶりにJVMのGCログを確認するためにGCViewerを使ったのでメモを残しておく。

## 環境
- OS: Windows 8.1
- Java: java version "1.8.0_231"
- Gcviewer: gcviewer-1.36-SNAPSHOT.jar

## ダウンロード

以下のページでなるべく最新バージョンの「download | download mac version」のリンクをクリックし、ダウンロードする。
https://github.com/chewiebug/GCViewer/wiki/Changelog

Windowsの場合はjarファイルがダウンロードされるので、任意のフォルダに配置する。

## 起動

1. gcviewerのjarをダブルクリックする<br>
![021.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556297/54f2fd29-b20c-5a51-95fa-449594d5e9c2.png)<br>
→以下の画面が表示される
![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556297/de80f6ff-8621-f95d-9130-17a90d256482.png)
2. ウィンドウ内にGCログのテキストファイルをdrag and dropする<br>
→以下のような画面になる
![020.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556297/5db420e9-bdb4-2d0c-9be6-60a4aca296c4.png)

## 表示の変更

1. メニュー > View > Data Panel をチェックオフ<br>
→右側のデータパネルが消えて広くなる
![004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556297/8b8bcc00-c49e-0b44-7a4a-880eff3d18d7.png)
2. 時間列の周辺を右クリック > Log start time をチェックオン<br>
→時間列が時刻表示になる(GCログにタイムスタンプが入っている場合)
![005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556297/2d3eac73-7197-322e-bc13-dfb6606d7ca2.png)
3. Total Heapをチェックオン<br>
→トータルヒープサイズが赤で表示される<br>
ここでは-Xms(開始サイズ)と-Xmx(最大サイズ)を同値に設定しているため、トータルヒープサイズは横一直線になっている。
![006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556297/6b5c5c11-81d5-5b46-3a00-f1e80add055e.png)
4. Total Heapをオフにし、Tenured Generation, Young Generationをチェックオン<br>
→Young世代のサイズが黄色で、Tenured世代(Old世代)のサイズが紫で表示される。
通常、Young Generation + Tenured Generation = Total Heap
![007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556297/dad02497-5435-ccdb-4b29-19cac481f5b1.png)
5. Used Tenured Heap, Used young Heapをチェックオン<br>
→各世代の中のヒープ使用量が線で見えるようになる。縦線はGCによってメモリが解放されたことを表す。
![008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556297/469c43f1-c20c-6ac5-fc48-f6daac1cef95.png)
6. GCログの開始/終了がウィンドウ全体に納まるように上部のセレクトボックスで比率を調整する(★重要)。<br>
これをしないと、一部の範囲しか確認せずに問題ないと思っていしまう可能性がある。今回はこのタイミングで比率を調整したが、なるべく早めにやって全体を見れる状態にしてから調査を始めたほうがよい。<br>
グラフからわかること
- Young GCが結構頻繁に発生している。Yong GCによってYoung世代はほぼ解放される(灰色の線が黄色領域の下まで落ちている)
- Tenured世代のFull GCが2回発生している。どちらもほぼ下まで落ちているので、解放はできている。その後Tenured領域のメモリが増えていっているのが気になる。おそらくこの後再度Full GCが発生するが、その時に下まで解放されれば問題はないのではないか（自信なし）
![009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556297/089eae86-213e-3c2c-99a5-aaebdae959c7.png)
7. Used Heapをチェックオン<br>
→これまではYoung世代とTenured世代を別々に表示していたが、これでYoung世代とTenured世代の合計(=ヒープ使用量)が青で見れるようになる
![010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556297/271755ec-effd-30d5-2c32-bbef423dcf96.png)
8. Full GC Linesをチェックオン<br>
→Full GCが黒線で見えるようになる
![011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556297/95b517a4-806a-8e17-558e-25ba69471f87.png)

## チェック観点

1. Young世代のヒープ使用量が、Young GC後に下まで下がっているか。
2. Tenured世代のヒープ使用量が、Full GC後に下まで下がっているか。

私自身、実はGCログの評価方法はよくわかっていない。
このグラフではTenured世代が増えてきているが、次のFull GCで下まで解放されれば良い、のかな？
Word で履歴を export するメモ

## 背景

* Word で契約書の文章の差分管理をしなければならない(相手方法務が git など使えず, Word 履歴機能しか使えない) :cry:
* 何回も変更された Word で, どこが変わったのか日付順でソートして確認したいが, Word の標準機能ではなぜか日付でソートというのができない

## 対処方法

Word マクロ(VBA)で履歴情報を csv にエクスポートして, csv で日付順にソートしてチェックする.

## 方法

https://stabucky.com/wp/archives/2339
https://docs.microsoft.com/en-us/office/vba/api/word.revision

VBA は初めてでしたが, VBA を 10 分くらい習って以下のようなものができました.
VBA で比較的簡単に履歴オブジェクトにアクセスできますね.

```vb
Sub rev()
'
' rev Macro
'
'

myFile = Options.DefaultFilePath(wdDocumentsPath) & "\revs.csv"

Open myFile For Output As #1

For Each myRev In ActiveDocument.Revisions
  Write #1, myRev.Date, myRev.Author, myRev.Type, myRev.Range.Start, myRev.Range.Text
Next myRev

Close #1

MsgBox "Wrote revisons to " & myFile

End Sub
```

Revison.Range.Text に変更文章があります.

変更点があまり多くなければ, とりあえずあとは目視作業で Word 中の文章とマッチングできるでしょうか.

## TODO

* [ ] Revison.Range.Start がどのページ(+行)に対応するかを計算する
* [ ] 変更履歴の Start(Range)と, 文章のマッチングを求める

## さらなる高みへ

docx 自体が xml なので, docx にして xml を直接パースするほうがはやいかも

Word docxファイルの中身を探検する - Office Open XML ざっくり入門
https://qiita.com/sky_y/items/2ab80adfb7a998e47239
JavaはExcel文書を作成します

Spire.XLS for Java機能が豊富なエクセルのセットです，JavaアプリケーションでExcelファイルの作成、編集、変換、印刷をサポートします。本論文では、Spire.XLS for Javaを使ってExcelファイルを作成し、データを追加する方法を紹介します。

使用ツール：Free Spire.XLS for Java （無料版）

https://www.e-iceblue.com/Introduce/free-xls-for-java.html

Jarファイルの取得と導入：

Method 1：ホームページを通じてjarファイルのカバンをダウンロードします。ダウンロード後、ファイルを解凍して、libフォルダの下のSpire.xls.jarファイルをJavaプログラムに導入します。

https://www.e-iceblue.com/Download/xls-for-java-free.html

Method 2：maven倉庫設置による導入。

https://www.e-iceblue.com/Tutorials/Licensing/How-to-install-Spire.PDF-for-Java-from-Maven-Repository.html

```java

import com.spire.xls.*;
import java.awt.*;

public class CreateExcel {
    public static void main(String[] args){
        
        //新しいWorkbookの実例
        Workbook workbook = new Workbook();

        //最初のシートを取得します。(新しいWorkbookはデフォルトではシートが3つあります)
        Worksheet sheet = workbook.getWorksheets().get(0);

        //最初のシートの名前を設定します
        sheet.setName("Data Sheet");

        // 列の最初のセルスタイルを作成します
        CellStyle style1 = workbook.getStyles().addStyle("Header Style");
        style1.getFont().setSize(12f);
        style1.getFont().setColor(Color.BLACK);
        style1.getFont().isBold(true);
        style1.setHorizontalAlignment(HorizontalAlignType.Center);
        style1.setVerticalAlignment(VerticalAlignType.Center);

        //データセルのスタイルを設定
        CellStyle style2 = workbook.getStyles().addStyle("Data Style");
        style2.getFont().setSize(10f);
        style2.getFont().setColor(Color.BLACK);

        //列の先頭セルにデータを追加し、スタイルを適用します
        for (int column=1; column<5; column++)
        {
            CellRange header =sheet.getCellRange(1,column);
            header.setValue("Column " + column );
            header.setStyle(style1);
            header.setColumnWidth(15f);
        }

        //データセルにデータを追加し、スタイルを適用します
        for (int row=2; row<11; row++)
        {
            for (int column=1; column<5; column++)
            {
                CellRange cell = sheet.getCellRange(row, column);
                cell.setValue("Data " + row + ", " + column);
                cell.setStyle(style2);
            }
        }

        //結果ファイルを保存
        workbook.saveToFile("CreateExcel.xlsx", FileFormat.Version2013);
    }
}
```
効果図：
![Sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278784/0c3fe0ac-ad90-3002-8a13-71eaafd0b1eb.png)
NVIDIA Visual Profiler "nvvp" を使おうとしたら起きたエラーと対処法

#はじめに

タイトル通り，NVIDIAのprofile用のGUIアプリであるNVIDIA Visual Profiler (nvvp) を使おうとして起きたエラーとその対処法お話です．

#環境

MacOS High Sierra

#何が起こったか

CUDA Toolkit 10.2 (当記事執筆時点での最新版)をダウンロードしてnvvpを起動，nvprofでprofileした結果をimport後，Finishでloadする際

```

CUPTI initialization Failed

Unable to locate CUPTI library
```

等とエラーが発生
（エラー画像は撮るの忘れました．すみません．）

（ダウンロード等の詳しいことはHiroki11xさんの[NVIDIA Visual Profiler 入門](https://qiita.com/Hiroki11x/items/3737e4e267c1035a4b55)が詳しいです）

#対処法

[CUPTIの説明ページ]に

```

CUPTI calls will fail with CUPTI_ERROR_NOT_INITIALIZED if the CUDA driver version is not compatible with the CUPTI version.
```

とあったので，CUDAのdriverとCUPTIのversionが合ってないと推測
CUDA Toolkit 10.2 を消去し，"CUPTI install"で検索をかけて出てきたCUDA Toolkit 10.1 Update 1 をダウンロードしたら動作しました．
b'driver failed programming external connectivity on endpoint

## Dockerコンテナ起動時、ポートが！？:scream:

今回は変なプロセスも残ってないのにな
コンテナ破棄して作り直したりもしたけど:confounded:

■ 環境

- Win10 Enterprise (1809)
- Docker for Windows 2.1.0.5(40693) stable
- Docker version 19.03.5, build 633a0ea

## クジラさん再起動
1.Docker for Windows停止
2.Docker for Windows起動
3.コンテナ起動 → 変わらず :boom:

## OSごと
1.Docker for Windows停止
2.OS再起動
3.Docker for Windows起動
4.コンテナ起動 → 変わらず :boom:

何故だろう(´・ω・`)

## Hyper-Vを切る

1. Docker for windows停止
2. Hyper-VをOff
3. OS再起動
4. Hyper-VをON
5. OS再起動
6. Docker for winfows起動
7. コンテナ起動成功:sunny:

たまたま治ったのかな？

### 参考
[nable to bind ports: Docker-for-Windows & Hyper-V excluding but not using important port ranges #3171](https://github.com/docker/for-win/issues/3171)
Papertrailの用語整理


半分想像だけど多分こんな感じ

## System
ログの発生元を示す。
ログの送信者がsystemのフィールドを設定している場合はそれが使われ、ない場合はIPアドレスが使用される。
systemフィールドを設定する場合もインスタンスIDとなる何かを設定するのがスタンダードの模様。

なので、実質的にはほぼインスタンスと同義。

## LogDestination
Papertrail側でのログの受け口。
ホスト名とポートからなる。

アカウントごとに複数作成可。

## Group
ユーザが任意に名前をつけられるグループ

## System, LogDestination, Groupの関係
- A Group can have multiple Systems
- A System can be belonging to multiple Groups
  - 多分。あんまり使いみちなさそうだがUIを見る限りできそう。

GroupとLogDestionationは直接の関係はない。

Cloud環境ではインスタンスは頻繁に入れ替わるのでGroupとSystemを自動的に関連付ける仕組みがある。
その方法は以下の2つ

### LogDestination毎に関連グループを設定
- このLogDestinationで受けたSystemを自動的に任意のGroupに追加
- 設定できるのは一つのGroupだけ

### Groupにsystem名のパターンマッチを登録
- 例えば、`myapp-develop-*`を登録しておけば、system名がそれにマッチするものが自動的にGroupに追加される
- Group毎に複数のパターンを登録可
VSCodeでJava開発環境を整える

# はじめに

VSCodeでJavaの開発を行う際、何も設定を行わないと使い物になりません。ネットとかで調べてもJavaが使えるようになるところまでしか紹介していない記事が多いので、ガチでJavaの開発をしたい人のために、設定すべき内容を紹介したいと思います。

# 前提
- [Java Extension Pack](https://marketplace.visualstudio.com/items?itemName=vscjava.vscode-java-pack)がインストールされていること

# プラグインのインストール

VSCodeでJavaを開発するためのインストーラーがありますが、これだけだと足りません。
他に便利なものがあるので紹介します。

- [Java IDE](https://marketplace.visualstudio.com/items?itemName=YouMayCallMeV.vscode-java-saber)
ClassやInterfaceなど、Javaに関連したファイルがクラス定義やパッケージが定義された状態で作成されます。

- [Lombok Annotations Support for VS Code](https://marketplace.visualstudio.com/items?itemName=GabrielBB.vscode-lombok)
自動生成でお馴染みのLombokをVSCodeでサポートするためのプラグインです。Lombok関連のアノテーションをVSCodeが認識できるようになります。

# フォーマッター定義ファイルの作成

VSCodeでJavaのフォーマッターを指定する箇所があるのですが、URLまたはローカルファイルパスが指定できます。今回はカスタマイズしたいので、まずは独自のフォーマッターを作成します。定義ファイルをテキストファイルなどで作成もできるのですが、GUI上のほうがプレビューなどもあるので、今回はEclipseを使います。

(1) Eclipseを起動し、[Preferences]を開きます。
(2) [Java]-[Code Style]-[Formatter]を選択します。
(3) [New]ボタンをクリックします。
(4) [Profile name]に任意の名前（ここでは「MyFormatter」）を入力し、「OK」ボタンをクリックします。
(5) 以下のフォーマット設定を行います。　　

|カテゴリ|項目|値|
|:--|:--|:--|
|Indentation|Tab policy|spaces only|
|Line Wrapping|Never join already wrapped lines|チェック|  

(6) 設定が完了したら、「OK」ボタンをクリックします。
(7) [Active Profile]から上記で作成したもの（MyFormatter）を選択し、[Export All]ボタンをクリックし、ローカルに保存します。保存するファイル名は「formatter.xml」としておきます。



## 設定値の概要

- Tab policy: spaces only  
タブ使用時に、スペースに変換します。
- Never join already wrapped lines: チェック  
改行済みのコードはフォーマットされません。これがないと、１行の最大サイズの範囲内で、改行されたコードが結合されて（改行が削除されて）しまいます。




# VSCodeの設定

[Settings]を開き、以下の設定を行います。

|項目|設定値|
|:--|:--|
|Editor: Detect Indentation|チェックなし|
|Editor: Format On Save |チェック|
|Java › Format › Settings: Url|/path/to/formatter.xml|
|Java › Format › Settings: Profile|MyFormatter|
|Java › Save Actions: Organize Imports|チェック|

### 設定値の概要

- Editor: Detect Indentation  
インデントの扱い（タブをスペースにするとか、いくつインデントするとかなど）をファイルごとに設定可能にするかの設定です。チェックがない場合はフォーマット定義に従います。
- Editor: Format On Save  
保存時にフォーマットが自動で実行されます。
- Java › Format › Settings: Url  
フォーマット定義ファイルを指定します。ここで定義されたフォーマットが実行されます。
- Java › Format › Settings: Profile  
フォーマット定義ファイルに設定したプロファイル名です。どのプロファイルを適用するか設定します。
- Java › Save Actions: Organize Imports  
保存時にインポートの最適化が実行されます。未使用のインポート削除、インポートのソートが実行されます。

# おわりに

これで最低限、Javaを開発できる環境が整いました。
他にこれやったほうがいいという意見がありましたら、教えていただけると幸いです。
（失敗）Flaskで作ったwebアプリをherokuでデプロイする

Flaskのアプリをherokuとgithubを使ってデプロイしてみます。
メモのために書きます。と思ってやってたんですが、デプロイしたページを見ようとするとエラーが出てします。前はできたんだけど、、、


まずプログラムを入れるファイルを作ります。

```
(base) PS C:\working\web_app> python -m venv 新しく作るファイル名(今回はmessage_app)
```

仮想環境を設定する。
下のコマンドを打つと仮想環境が稼働します。

```
(base) PS C:\working\web_app\message_app>Scripts\activate
```
次にライブラリを仮想環境にインストールします。

```
(message_app) PS C:\working\web_app\message_app>pip install 使うライブラリ名（今回はflaskとrequestとgunicorn)

pip install -r requirements.txt(requirements.txtを使ってインストールもできる）
```
Procfileという名前のファイルを作るってエディタでファイルに以下の文を書いておく

```
web: gunicorn app:app --log-file=-
```

requirements.txtを作って中にしたのコマンドで出てきたインストールされてるライブラリの情報をコピペする

```
pip freeze
```

以下のコマンドを実行する

```
git init
```

```
git add .
```

addと.の間に半角スペースがあるのに注意

```
git commit -m “コメント”
```

```
heroku login
```

```
heroku create
```

```
git push heroku master
```

ってやってみたけど、できなかった　＞＜
ドロップダウン機能を有効にするため、Railsのapplication.jsファイルを通して、Bootstrapに同梱されているJavaScriptライブラリとjQueryを読み込むようアセットパイプラインに指示

railsチュートリアルで、Accountをクリックしてプルダウンメニュー出すために、少し手こずったのでメモ。

実装させたい機能⬇️
![fullsizeoutput_b8c.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508248/3dbcf45c-8c8d-802a-0263-771a884bb949.jpeg)
application.jsに２つのライブラリを追加。

```application.js
//= require rails-ujs
//= require jquery  　　　⇦追加
//= require bootstrap　　 ⇦追加
//= require turbolinks
//= require_tree .
```
![fullsizeoutput_b8b.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508248/0551ebd6-ce14-558f-d626-69e1de097a55.jpeg)
と、Sprockets::FileNotFoundエラーが出る。

Gemfileにgemを追加。

```Gemfile
gem 'rails', '~> 5.2.4', '>= 5.2.4.1'
gem 'bcrypt',         '3.1.12'
gem 'bootstrap-sass', '3.3.7'
gem 'jquery-rails', '~> 4.3', '>= 4.3.1'　　　⇦追加
```
追加後bundle installとrails sでサーバー再起動。
![fullsizeoutput_b8d.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/508248/5ecf0c25-c002-f3c3-034c-ee4e1e98715d.jpeg)
無事に、機能実装完了。
MIT Open CoursewareのPerformance Engineering of Software Systemsをやっていく(4)

MIT Open CoursewareのPerformance Engineering of Software Systemsをやっていきます。

https://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-172-performance-engineering-of-software-systems-fall-2018/index.htm

#引数の通過

Cにおいては、関数への引数は値で渡されます。これは、もし`foo(int f)`において、整数を伝達しようとしたら、新しい変数fがfoo関数内で初期化されるということです。
例えば、二つの整数を交換する以下のコードを例に取ってみましょう。期待した動きをするでしょうか？

```c
#include <stdio.h>

void swap(int i,int j){
  int temp = i;
  i = j;
  j = temp;
}

int main() {
  int k = 1;
  int m = 2;

  swap(k,m);
  printf("k = %d,m = %d \n",k,m);  //k = 1,m = 2 

  return 0;
}
```

交換されていませんね。直すのに二つの方法があります。一つは、swap()をマクロに変えて、マクロが呼び出されるスコープにて操作が評価されるようにする。もう一つの方法は、swapをポインタを使って表現すること。

以下がマクロを使った構文。

```c
#include <stdio.h>

/*
void swap(int i,int j){
  int temp = i;
  i = j;
  j = temp;
}*/

#define SWAP(type,a,b) { type temp = a; a = b; b = temp; }

int main() {
  int k = 1;
  int m = 2;

  SWAP(int,k,m);
  printf("k = %d,m = %d \n",k,m);  //k = 2,m = 1

  return 0;
}
```

ポインタを使った公文は以下の通り

```c
#include <stdio.h>
void swap(int *i,int *j){    //ポインタを引数として取る
  int temp = *i;  //ポインタから値を取り出す
  *i = *j;        //ポインタから値を取り出してポインタの指す値へ代入
  *j = temp;      //値からポインタが挿す値へ代入する。
}
int main() {
  int k = 1;
  int m = 2;

  swap(&k,&m);  //&k、&mはk、mのアドレスを返す。
  printf("k = %d,m = %d \n",k,m);  //k = 2,m = 1

  return 0;
}
```

ということでまた次回
【Dart/Flutter】SVG画像がアプリ上に表示されない時どうしたか

##起こったこと ・ Flutterアプリ上でSVG画像が表示されない


- Flutter 1.9.1+hotfix.4
- Dart 2.5.0

上記環境でアプリ開発中、読み込んだSVGファイルがiOS実機で表示されない
(ios simulator上では問題なく表示されていたのだが…)


##前提 ・ そもそもSVGファイルって？


ベクター画像形式と呼ばれる画像フォーマットのひとつ。
JPEG形式、PNG形式など画像を点で描画するビットマップ画像とは異なり、
画像を計算式で表現するため、拡大縮小しても画像が劣化しない特徴がある。

参考 : https://ferret-plus.com/7089


##解決方法 ・ サイズ指定するだけ


widthとheightの指定。

**Before**

```dart
BottomNavigationBarItem(
  icon: SvgPicture.asset(
    'assets/bottom_bar/home.svg'
  )
)
```
**After**

```dart
BottomNavigationBarItem(
  icon: SvgPicture.asset(
    'assets/bottom_bar/home.svg',
    width: 30.0,
    height: 30.0
  )
)
```

これだけ。簡単なことでした。


##補足・SVGファイルの仕様


JPEGやPNGといったビットマップ画像とは異なり、
ベクター画像形式であるSVGファイルは「画像の大きさ」を情報として持たない場合がある。
ゆえに要素にwidthを指定してやらないと、widthに0が与えられ、見かけ上表示されない、ということが発生するらしい。

参考:https://jdash.info/archives/SVG_SVGZ_Graphic_File_dont_show_in_browser
NervesのSSH環境を整えた　〜upload.shを使ってファームウェア更新も〜

NervesでSSHを利用する場合の日本語の情報がほとんどありませんよね。なければ自分で作るということでSSHの環境をまとめてみたいと思います。

これは「[Nervesのネットワーク環境を整えた](https://qiita.com/nishiuchikazuma/items/1341b32c4ce997fc362d)」の続編になります。

#追記（2020.01.21）
ネットワークやSSHの設定は`config`ディレクトリにある`config.exs`と`target.exs`どちらに記述しても問題なさそうです（記述方法も同じ）。今回は`config/config.exs`に記載することとします。

```
config/
  config.exs  ←今回はこちらに設定を記述
  target.exs
```


#環境
- RaspberryPi 3
- Nerves


#わかること
このようなことがわかります。
ですが、SSHで利用する公開鍵と秘密鍵の理解は必要です。
[公開鍵暗号 - Wikipedia](https://ja.wikipedia.org/wiki/%E5%85%AC%E9%96%8B%E9%8D%B5%E6%9A%97%E5%8F%B7)

- NervesのデフォルトSSHログインユーザがわかる
- Nervesに複数ログインアカウントを作成できる
- Nervesに登録しているSSH公開鍵の調べ方がわかる


#やらないこと
`nerves.local`とUSBでの接続はパスします。
ラズパイ環境ではないですが、 @takasehideki さんの[ElixirでIoT#4.2：BeagleBone GreenでNerves/Elixirを動かす - VirtualEthernet越しにssh接続でIEx起動＆ファームも更新する！](https://qiita.com/takasehideki/items/e7cc1a2d0a4a7140c1cf#virtualethernet%E8%B6%8A%E3%81%97%E3%81%ABssh%E6%8E%A5%E7%B6%9A%E3%81%A7iex%E8%B5%B7%E5%8B%95%E3%83%95%E3%82%A1%E3%83%BC%E3%83%A0%E3%82%82%E6%9B%B4%E6%96%B0%E3%81%99%E3%82%8B) にBeagleBoneを使ったUSBでの接続方法があるので`nerves.local`を使う場合はこちらを参考にしてくださいm(_ _)m


#SSH公開鍵を登録する
NervesのファームウェアをSDカードに焼く場合、以下の手順で実行します。
この時`mix firmware`する時にSSH鍵を登録しています。


```shell:bash
$ mix deps.get
$ mix firmware

$ mix firmware.burn　←手元のPCでSDカードを焼いてファームウェアを更新する場合
or
$ mix firmware.gen.script　←upload.shを作成するコマンド（初回のみ）
$ ./upload.sh NervesのIPアドレス　←ネットワーク経由でファームウェアを更新する場合
```


##デフォルトの動作
実行ユーザの`$HOME/.ssh/id_rsa.pub`または`$HOME/.ssh/id_ecdsa.pub`を自動登録しています。`$HOME/.ssh`ディレクトリが無い場合はエラーとなります。

##デフォルトの動作じゃなくしたい場合
デフォルトの動作じゃなくしたい場合ははじめに`mix.exs`に以下の設定を追記します。

[Nervesのネットワーク環境を整えた](https://qiita.com/nishiuchikazuma/items/1341b32c4ce997fc362d)で書いた`:nerves_network`の場合と同じく、`targets: @all_targets`がないとMacの環境でも構築しようとしてしまうようで`mix deps.get`した時にエラーが出ます。

```elixir:mix.exs
 defp deps do
    [
      （略）
      # 追加
      {:nerves_network, "~> 0.5", targets: @all_targets},
      {:nerves_firmware_ssh, "~> 0.3", targets: @all_targets}　←今回追記した行
    ]
  end
```

そして、Aさんの秘密鍵A（例えば`$HOME/.ssh/id_rsa.pub`の内容）を使ってログインする場合は`config/config.exs`に以下の設定を追記します。

```elixir:config/config.exs
#
# ssh公開鍵登録
#
config :nerves_firmware_ssh,
  authorized_keys: [
    "公開鍵A"
  ]
```

この状態でSDカードのファームウェアを更新し、NervesにSSHできるかどうか確認します。

```shell:bash
$ mix deps.get
$ mix firmaware
$ mix firmware.burn

$ ssh NervesのIPアドレス　←秘密鍵Aを指定しない場合
$ ssh -i 秘密鍵A IPアドレス　←秘密鍵Aを指定する場合
```


###複数の鍵を登録する場合
例えば、Aさんの秘密鍵AとBさんの秘密鍵Bでログインできるようにする場合は`config/config.exs`に公開鍵Aと公開鍵Bをリスト形式で記述します。1つのNervesに複数PCからの同時ログインもOKということが分かりました。

```elixir:config/config.exs
#
# ssh公開鍵登録
#
config :nerves_firmware_ssh,
  authorized_keys: [
    "公開鍵A",
    "公開鍵B"
  ]
```


#登録しているSSH公開鍵の確認方法
Nervesにログイン後、以下のコマンドで登録されている公開鍵を確認できます。

```elixir:iex
iex> Application.get_env(:nerves_firmware_ssh, :authorized_keys)
or
iex> Application.get_all_env(:nerves_firmware_ssh)
```


#まとめ
NervesのSSH環境について整理することができました。
やってみると分かるんですが、`upload.sh`を使ったネットワーク越しのファームウェア更新がかなり便利です！
たま〜にかえってこなくなる場合があるのはなんでかなぁー。


# 参考URL
[nerves-project/nerves_firmware_ssh - Installation](https://github.com/nerves-project/nerves_firmware_ssh#installation)
Laravel Blade で extendsとincludeでyieldの挙動が変わる

## 起きたこと
Laravel 6.6 （執筆時 最新版）のBladeファイルで、includeしたテーマのyield部分に、sectionが挿入されないことがあったのでメモ。

## ファイル

こんなベースファイルに

```php:base.blade.php
<!DOCTYPE html>
<head>
    @yield("head")
</head>

<body>
    @yield("body")
</body>
</html>
```

こんなheadタグのパーツを作って

```php:head.blade.php
@section("head")
<title>@yield("title")</title>
@endsection

```

こんな実装bladeを作ったとする。

```php:index.blade.php
@extends("base") // baseを継承

@include("head") // headをinclude

@section("title")  
index   //includeしたheadに、タイトル[index]を挿入
@endsection

@section("body")
body   //extendsしたbaseに、[body]を挿入
@endsection
```

これで生成されるhtmlは

```html
<!DOCTYPE html>

<head>
<title></title>    <!-- タイトルが挿入されていない -->
</head>

<body>
body               <!-- bodyは挿入されている -->
</body>
</html>

```

このように、 `@section("body")` はきちんとyieldに挿入されるが `@section("title")` の部分は無視される。

思い通りの動き方のためには

```php:index.blade.php
@extends("base")

// titleが挿入されるテンプレート head をincludeする前に
// section("title")を設定する
@section("title", "index")  

@include("head")

@section("body")
body
@endsection

```

このようにするとhead内のyieldにきちんと挿入される。

どうやら

* extendsしたテンプレートへ挿入したいsectionはどこに書いても良い
    * extends宣言の前でも後でも良い
* includeしたテンプレートへ挿入したいsectionは、includeするよりも先にsectionを書かないといけない
    * sectionの記述後にincludeしないといけない


といった制約がある様子。
どう日本語で表現したらいいのか難しいですが、多重yieldとか、includeしたテンプレートへのyieldとか、そういった感じでしょうか。

そのうちLaravelのコード直接読んで仕組みを理解したいところですが、今は覚書として。
expressメモ

# サニタイズするとは
HTMLフォームなどから悪意のあるスクリプトコードが入力されたとしても単なる文字列として扱うようにして無力化してしまうこと。

# mongooseでdb接続
 connect成功と失敗でlogを出力させた。

```
// Set up Default mongoose connection
var mongoDB = require('./.db_url');
var DB_URL = mongoDB.LOCAL_DB;
// connect mongoDB
mongoose
  .connect(DB_URL, {
    useUnifiedTopology: true,
    useNewUrlParser: true,
  })
  .then(() => console.log('DB Connected!'))
  .catch(err => {
    console.log(`DB Connection Error:${err.message}`);
  });
```
Lazy advent calendar 2019

# OCRの自己満足実装

## Plans

1. 個々の文字をまず認識する。(Using haar or YOLO?)

2. Rectangleで個々の文字を覆い、以下の処理を施す。

3. Rectangleで囲まれた領域において、輝度値を3D-plotしたものに対して、これを3D-modelingして、回転させた際の写像をもとに個々の文字を正確に認識させる。

```
# Modules
from pathlib import Path
from skimage import io
import matplotlib.pyplot as plt
import cv2
import numpy as np

# Putting some image files of any documents under dataset

p = Path("../dataset")
paths = list(p.glob("**/*.jpg"))
data1 = io.imread(paths[0])

# Using tile strategy to evaluate the recognition accuracy.

mini = data1[1200:1400, 800:1000, 0]
plt.imshow(mini)
print("Showing data to be processed...")
plt.show()

```

## Switch1

* 紙全体の領域をAffine変換などで切り取った上で、一文字のpixel-widthを推定する。


```
# Highlighting the target

## Lazy normalization

if mini.max() > 256:
    subject = np.true_divide(mini, 256).astype("uint8")
else:
    subject = mini.astype("uint8")

## Creating a mask to remove noise.

mask = (subject < 200)
                
masked = mask * subject

## Distance transform

distmap = cv2.distanceTransform(masked,1,3)
                
## Creating all zero matrix, which size equal to data

featuremap = distmap*0
                
## Deciding kernel size to convolve.

ksize = 20
                
## Detecting edge with convolution...

for x in range(ksize,distmap.shape[0]-ksize*2):
    for y in range(ksize,distmap.shape[1]-ksize*2):
    
    ### Kernel内で最大の値を持つ座標をfeaturemap内に1として出力している...
    ### max-poolingと処理は近い。Max-poolingと標準化を同時に実施している。
    
        if distmap[x,y]>0 and distmap[x,y]==np.max(distmap[x-ksize:x+ksize,y-ksize:y+ksize]):
            featuremap[x,y]=1

        ### defining feature_dilated for imshow

        feature_dilated = cv2.dilate(featuremap, (50, 50))

print("Masked image is ... : ")
plt.imshow(masked)
plt.show()

plt.imshow(feature_dilated)
print("Feature shape is ... : " + str(feature_dilated.shape))
plt.show()

Feature_mask = (feature_dilated > 0)

Cropped = masked * Feature_mask
plt.imshow(Cropped)
plt.show()
```

## Switch2

* Tile-strategyにおいて切り取った画像ファイルをPublic-OCR-serverに処理させる。

* 切り取った画像ファイルを自作した1文字認識のためのMNISTのようなサーバーに処理させる。(One-hotベクトル化を行ったのちに、その座標から1文字だけをcropする。)

# 今後の課題

* 今はやりの（？）Spiking neural networkでYOLOを実装し、解釈性ゴリゴリの1文字cropping processを完成させる。
* 3次元実行列の固有値探索に有効な射影行列に関する研究（勉強）をする。
* フルスクラッチ次元削減

# 参考

* [射影行列](https://qiita.com/takseki/items/e0de89ab82d4d287a0d7)
* [射影行列の性質](https://risalc.info/src/projection-matrix-properties.html)
* [射影作用素](https://ja.wikipedia.org/wiki/射影作用素)
* [HSV変換](https://ja.m.wikipedia.org/wiki/HSV%E8%89%B2%E7%A9%BA%E9%96%93)
ExpoアプリにAmplitudeを導入する+GAも試してみる

Expoで開発しているアプリにアナリティクス系のサービスを導入しようとすると、選択肢は概ねAmplitudeに限られるようなので、簡単に導入までやってみます。

# Amplitude

## アカウント・プロジェクト作成

Amplitude
https://amplitude.com/

<img width="1448" alt="Amplitude___Product_Analytics_for_Web_and_Mobile.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514229/9275600e-a869-6d73-5e55-f73b80866014.jpeg">

アカウントを未登録の場合は「Explore demo now」からデモを開始します。
メールアドレスや名前などを入力したら下のようなデモ画面が表示されるはずです。

<img width="1448" alt="Amplitude_Demo__Music_Streaming_App_-_Amplitude.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514229/4dfe8603-5384-4948-55e0-ca443bdd4b74.jpeg">

メールが届いているはずなので、認証してから、上部に表示されている「Set up the Free Plan」からひとまずフリープランにアップグレードします。

アカウントを作成すると、チームスペースが表示されます。

左メニューの「Manage Data」→「Create Project」で新規プロジェクトを作成し、「API Key」を取得しておきます。

## Expoプロジェクトで初期化

Expoから使用するためのパッケージをインストールします。

```console
$ npm install --save expo-analytics-amplitude
```

JS側での設定はこれだけです。

```js:App.js
import * as Amplitude from "expo-analytics-amplitude";

Amplitude.initialize("API Key"); // API Keyを指定
```

## ログを送信

ユーザーの情報やイベントを送信することができます。

```js
Amplitude.setUserId("xxxx"); // アプリケーションで使っているユーザーIDを入れる

Amplitude.setUserProperties({ // ユーザーの情報を適宜入れる
  plan: "free",
  gender: "f",
  organization: "XX Inc."
});

Amplitude.logEvent("Init"); // イベントを送信

// 任意のプロパティを渡してイベントを送信
Amplitude.logEventWithProperties("Send", { title, message });

// こんな感じでユーザーをgroupingできる（フリープランだと活用できなさそうだけど）
Amplitude.setGroup("interests", ["sports", "science", "art"]);
```

[Expoの公式ドキュメント](https://docs.expo.io/versions/latest/sdk/amplitude/)に詳しく載っていますが、アプリケーション側でできることはシンプルにこれだけです。

プロジェクト詳細からイベントがうまく拾えているのが確認できます。

<img width="1448" alt="Events_-_Amplitude.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514229/3528362a-c143-7b38-d7ed-c625c8b0a038.jpeg">

左メニューの「User Look-Up」から個々のユーザーの情報が閲覧できます。
<img width="1448" alt="User__xxxx_-_Amplitude.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/514229/63433058-655c-dc28-ccca-96a2a26ca0ae.jpeg">

# [補足]Google Analyticsも使ってみる

Amplitudeを使ったリポジトリを探しているうちに[expo-analytics](https://www.npmjs.com/package/expo-analytics)というExpoでGoogle Analyticsを使うためのライブラリを見つけたので、これも試してみます。
そもそもモバイルアプリ用のGAはFirebaseAnalyticsに統合される（されている）ようなのでその場凌ぎのような感じになってしまいますが。。。

内部的には単純に、Google AnalyticsのAPI（`https://www.google-analytics.com/collect`）を直接叩いているようです。

```console
$ npm install --save expo-analytics
```

```js:App.js
import { Analytics, Event, PageHit } from 'expo-analytics';

const analytics = new Analytics('UA-XXXXXXXX-X');
analytics.hit(new PageHit('Init'))
  .then(() => console.log("hit success"))
  .catch(e => console.log(e.message));
analytics.event(new Event('Video', 'play', 'Fall Campaign', 42))
  .then(() => console.log("event success"))
  .catch(e => console.log(e.message));
```

これで見たところ問題なくヒットやイベントが送信できました。ただし、[ここにもある通り](https://github.com/ryanvanderpol/expo-analytics#support-for-web--app-projects)現在ベータ版の`G-`から始まるトラッキングIDをもつWeb+App版のプロパティでは使用できず、現在は通常のプロパティのみ使用できるようです。
オープンソースで3Dデータを解析できるVaa3Dの基本

最近は二光子顕微鏡やlattice顕微鏡などで3次元データがきれいに撮れるようになってきました。解析する機会も多いです。
3Dデータ解析の有名なツールとしてImarisがありますが、高価な有料のソフトウェアです。
ImageJ Pluginには3D Viewerもありますが、あまり機能が充実していません。

そこでVaa3Dというソフトを見つけました。名前は「3D Visualization-Assisted Analysis」の意味です。
オープンソースで、特に神経科学研究向けのソフトウェアです。

開発者ホームページ：https://alleninstitute.org/what-we-do/brain-science/research/products-tools/vaa3d/
githubのwiki:https://github.com/Vaa3D/Vaa3D_Wiki/wiki/Vaa3D-Wiki
論文：https://www.nature.com/articles/nprot.2014.011
日本語の参考記事：http://www.brain.rcast.u-tokyo.ac.jp/wp/nebula/graphics/vaa3d

#インストール・使い方
インストールはホームページからBinary releaseをダウンロードして解凍するだけです。中のvaa3d_msvc.exeによってGUIが起動します。
後は画像をドラックアンドドロップすれば開くことができ、[See in 3D]ボタンで３次元表示ができます。

留意することとしては、Vaa3Dは**日本語パスには対応していない**という点です。
C:などからフルパスですべて英語のディレクトリの中に入れましょう。
そうしないとエラーになります。（例えば、「ドキュメント」や「デスクトップ」などの日本語のフォルダに入れているデータは開こうとするとエラーになります。）
また、3D表示では、PCのグラフィック処理に負荷をかけます。GPUの使用が望ましいです。

以下に主な機能を紹介します。
##Viewerとしての機能
MIPやmIP、Alphaで投影処理を変えると見え方が違います。
Volume Cutで断面をみることができます。
他にも様々な設定があります。

例：立方体と円柱の3次元表示
![2020-03-16 (3).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521288/c8242407-71b3-fb08-7a9c-c1d5f98e4517.jpeg)


##Virtual Fingerによるマーカー機能
3D表示上に右クリックし、「1(2) right-click to define a marker」で3次元内にマーカーをつけることができます。(2クリックの方が2方向から定義できるため、正確につけられる。)
他にも「1-right stroke...」で一筆書きができたり、「Series of right click.....」で点をつないだ線を描くこともできました。

例：マーカー(赤)と線(青)（適当な場所をマーキングしました）
![2020-03-16 (4).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/521288/2327a815-ade4-8b4e-553c-878ec9d7a8d6.jpeg)


##メッシュ作成機能
「Surf/Object」タブの「Load/Save Surf」ボタンでメッシュを作成することができ、画像とは別のデータとして保存できます。

##4次元解析
時系列を含めた４次元のデータを見るには、一つのフォルダに連番で３次元のスタック画像ファイルを入れることですることができます。
この連番のスタック画像の作成方法を記します。
まずImageJ(Fiji)で４次元画像を開き、xyztのハイパースタック状態にしましょう。(複数のchannelがある場合は、RGB画像に変換する)
ImageJのマクロで以下のようなコマンドを実行します。

```java
filename = "<開いている画像の名前>";
outputdir = getDirectory("output_folder"); 
setBatchMode(true); 
selectWindow(filename);
Stack.getDimensions(width, height, channels, depth, frame);
for (i=1; i<=frame; i++) {
	selectWindow(filename);
	run("Duplicate...", "title=" + filename + "-" + i  + ".tif duplicate frames=" + i);
	saveAs("Tiff", outputdir + "/" + filename + "-" + i);
	close();
}
setBatchMode(false)
```

これを実行するとまずフォルダ選択のGUIが開き、選択したフォルダにフレーム順に連番がついたファイルが保存されます。
連番スタック画像が作成出来たら、Vaa3Dの[Import general image series to an image stack..]で連番スタック画像のどれかを選択して開き、次のGUIで出てくる最後の項目を「Pack images in `Channel' dimension」にしましょう。
これで4次元画像として開くことができました。
（RGB画像を読み込ませた場合は5次元ということになります。）

#終わりに
Vaa3Dにはまだたくさんの機能があり、全て把握しきれていません。
また自分でプラグインを作成して、機能を追加することも可能のようです。(言語はC++)
ここまでの機能があってオープンソースというのが驚きです。
医療分野ではMDCTやMRIで3次元データが取得可能であり、解析ソフトは3D Slicer( https://www.slicer.org/ )が便利そうです。また別記事にする予定です。
TransformTOPで簡単に作れるクレイジー🤪なGlitch

##はじめに
TransformTOPを使った簡単なGlitchの作り方を紹介します！
洒落たGlitchではなく、クレイジーなやつです。
![glitach3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266203/1943323f-c233-b6fc-0c11-a7580825a7e7.gif)

##作り方
**(1) まずは、GlitchをかけたいTOPにTransformTOPを繋げます。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266203/b60b97fb-988e-6957-4b46-0d71f04533e2.png)


**(2) 次に、TransformTOPのパラメータを触っていきます。**

Tileタブの「Extend」を「Repeat」にして、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266203/1ca5dfe6-2dd2-9ad7-477f-8dbc10a11f45.png)


Transformタブの「tx」「ty」「rotate」に、変化し続ける値（「absTime.frame」など）を入れると、、、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266203/77553f03-59f7-d8a9-a07e-91a2d7f2a7a7.png)

**(3) できあがり！**
![glitach3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266203/1943323f-c233-b6fc-0c11-a7580825a7e7.gif)

##MIDIコンのスライダーと連携
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266203/9396631c-c874-af27-2026-a9a25040624a.png)
VJするときにMIDIコンのスライダー（ツマミ）とGlitchのパラメータを連携させて、
DJのスクラッチに合わせてグリグリとGlitchをかけても楽しいです。


##音の波形をGlitchのパラメータに使う
音の波形をGlitchのパラメータに影響させても面白いです。
数値のビジュアライズはtouchdesignerの得意分野ですね。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">音の波形をGlitchのパラメータにアサインするテスト。 <a href="https://t.co/h7Y8y99Eby">pic.twitter.com/h7Y8y99Eby</a></p>&mdash; egroncc (@egroncc) <a href="https://twitter.com/egroncc/status/1209200290525675521?ref_src=twsrc%5Etfw">December 23, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

##最後に
今回のサンプルを下記にアップしています。
[https://github.com/egroncc/crazy_glitch](https://github.com/egroncc/crazy_glitch)

TransformTOP一つで色んなルックが作れます。
今回紹介したパラメータ以外を触っても面白い表現ができますので、
興味を持ってくれた方は是非試してみてください。
omniauth-lineが`redirect_uri does not match`で失敗する。


使用したライブラリはこちらです。
https://github.com/kazasiki/omniauth-line

組み込んだアプリで[omniauth-google-oauth2](https://github.com/zquestz/omniauth-google-oauth2)を使用していて、omniauth-google-oauth2がomniauth-oauth2のバージョン1.6以上を要求していたので問題が起こりました。

詳しいエラーの内容と、根本的な問題はスタックオーバーフローに投稿し、的確な回答をいただいたのでそちらをご覧ください。

https://ja.stackoverflow.com/questions/61565/web%e3%81%aeline%e3%83%ad%e3%82%b0%e3%82%a4%e3%83%b3%e3%81%a7redirect-uri-does-not-match

トークンをリクエストする時の必須パラメータ`redirect_uri`に必要の無いパラメータが付与されてたので`redirect_uri does not match`になっていたわけですが、そもそもリクエスト組み立ててるの俺じゃねえし、どうすれば良いの？というところを解説したいと思います。


[この辺](https://github.com/omniauth/omniauth-oauth2/commit/26152673224aca5c3e918bcc83075dbb0659717f)でomniauth-oauth2は`callback_url`をオーバーライドしなくなり、omniauthの[この実装](https://github.com/omniauth/omniauth/blob/c2380ae848ce4e0e39b4bb94c5b8e3fd0a544825/lib/omniauth/strategy.rb#L443-L445)が使われるようになりました。

googleやfacebookはなんでうまくいくのかな？と思ってみてみたら各自オーバーライドしていました。

https://github.com/zquestz/omniauth-google-oauth2/blob/e2ace464ac2025bb287e78f7ac0a1249c73658b1/lib/omniauth/strategies/google_oauth2.rb#L102-L104
https://github.com/mkdynamic/omniauth-facebook/blob/5fdcad76fbef1fb6acc2cbc395c6d6d36563c7b1/lib/omniauth/strategies/facebook.rb#L81-L84

よく見るとPR出てましたね・・・
https://github.com/kazasiki/omniauth-line/pull/10
ただGoogleは1.6を要求するので1.6にアップしたＰＲを出しておきました。

https://github.com/kazasiki/omniauth-line/pull/16
一応出しましたが丸二年メンテナンスされてないっぽいのでマージはなさそう。

私のフォークしたmasterには反映してあるので良かったら使ってください。
https://github.com/gomo/omniauth-line

スタックオーバーフローでは的確な回答を直ぐいただいて助かりました。この場を借りてお礼をさせていただきます。スタックオーバーフローはコメント書こうとすると「ありがとうなどのお礼は書かないでください」と言われるんですよね。なんか日本人の文化に会わないというか、お礼したいと思いました。まあ▲ボタンを押して評価するのがお礼なんだと思いますが。