OmniAICreator/Qiita-1.07M huggingface dataset:
Vue+electron製ツールでSlackにメッセージを送る

#Vue + electron製のツールでSlackにメッセージを送る
## 事の発端
新しくアルバイトとして入社した企業の配属チームの勤怠ルール
>出社、休憩開始、休憩終わり、退社時に特定のチャンネルにそれぞれに対応する絵文字を送信する。

割とめんどくさい・・・　ちなみにこれ以外にも勤怠システムとタイムカードの３重門。
## やったこと
タイトルの通り、SlackのAPIをVue + electron のツールでたたき、ワンクリックで出社退社などの絵文字を送信できるようにっした。

##コード
`vue init`でelectron-vueのミニマムテンプレートから作っています。

```App.vue
<template>
  <div id="app">
    <div class="cont">
      <div class="button" style="border-color: rgb(129,188,189); color: rgb(129,188,189);" @click="send(1)">
        <h2>出社</h2>
      </div>
      <div class="button" style="border-color: rgb(235,115,62); color: rgb(235,115,62);" @click="send(2)">
        <h2>休憩</h2>
      </div>
      <div class="button" style="border-color: rgb(235,115,62); color: rgb(235,115,62); " @click="send(3)">
        <h2>再開</h2>
      </div>
      <div class="button" style="border-color: rgb(58,61,145); color: rgb(58,61,145);" @click="send(4)">
        <h2>退社</h2>
      </div>
    </div>
  </div>
</template>

<script>
import { SlackOAuthClient } from 'messaging-api-slack';

export default {
  name: "kintary",
  methods: {
    send(type) {
      let mes = ''
      switch(type) {
        case 1:
          mes = ':sagyo-kaishi-作業開始_green:'
          break;
        case 2:
          mes = ':sagyo-ohiru-kyukei-お昼休憩_orange:'
          break;
        case 3:
          mes = ':sagyo-saikai-作業再開_orange:'
          break;
        case 4:
          mes = ':sagyo-shuryo-作業終了_navy:'
          break;
      }
      const client = SlackOAuthClient.connect(
        'xoxp-slackから取得したアクセストークン（管理者じゃなくても自分のものは取得できた)'
      );
      client.postMessage('frontteam_kintai', mes, { as_user: true });
    }
    
  }
};
</script>

<style>
#app {
  height: 100vh;
  width: 100vw;
  display: flex;
  justify-content: center;
  align-items: center;
}

.cont {
  width: 480px;
  height: 120px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.button {
  height: 100px;
  width: 100px;
  border-radius: 50px;
  border: 3px solid;
  display: flex;
  justify-content: center;
  align-items: center;
  margin: 10px;
}
.button:hover {
  opacity: 0.6;
}
.button:active {
  transform: scale(0.95);
}
</style>
```

## 悩んだところ
SlackのAPIを叩く際、Axios.postで叩いたところCORS関連のエラーが出た。
↓
超便利なnpmモジュールがあったのでおとなしくそれを利用、importして設定して.postMessageだけで利用できた。

※as_userオプションをtrueにすることで、tokenを取得したUserとして投稿ができる。

#できたもの
![test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253700/263f3f30-f157-33ec-8aff-9f7d66367f60.gif)

##今後
ちゃんとビルドしたい。
私のデータ解析環境構築 for Windows (Docker + anaconda + Jupyter + R)

## はじめに
Jupyter notebookはデータ解析をする上で素晴らしい環境だと思います。
ただ、RをJupyter notebook上で動かす環境を構築する際、よくまとまった資料が無かったので
備忘録も兼ねて書き留めておきます。
特に、表題の通りDockerを使って、仮想化と再現性を担保しつつ、R上でjupyterを使う環境を簡単に構築することを目的とします。(なるべくDockerとかanacondaとか知らない人でも構築できるようにしているつもりです。)

なお、この記事は **Windows 10 Pro または Enterprise** の方を対象としています。
**Macの方**
以下のリンクへどうぞ。
[私のデータ解析環境構築 for Mac (Docker + anaconda + Jupyter + R)
](https://qiita.com/sgr0416st/private/59c4fb5965be96949221)

**windows 10 pro， Enterprise 64bit 以外の方**
Docker ToolBoxを使えばおそらく同じようにできると思いますが，試してないです．すみません，，，

### こんな人におすすめ
- R言語でデータ解析したいけどpythonも使いたい
- 環境は仮想化しておきたい
- 綺麗なインターフェースを使いたい
- 解析結果の再現性を担保したい
- 簡単に環境構築したい

### 環境構築
仮想化技術を使っている方へ：
バックエンドが Hyper-V らしいので、VMWare Player と共存ができないので注意してください。

１. **dockerをインストールする** 
ログインまたはアカウント作成後、
[Docker Store(windows)](https://hub.docker.com/editions/community/docker-ce-desktop-windows)からインストーラをダウンロードして，コマンドプロンプトを開き

```
docker info
```
を実行できればインストール成功です。


mac編ではスクリプトを組みましたが，windowsはよくわからなかったのでコマンドを直打ちします．

２.Jupyter Notebookを保存するディレクトリを作成し，コマンドプロンプトでそのディレクトリに移動する

```
cd \〇〇\〇〇〇〇\〇〇〇
```

３.r-jupyter notebookイメージをpullする
終わるまで割と時間がかかります

```
docker pull jupyter/r-notebook
```
 
４. イメージからコンテナを作成します．

```
docker run --rm -p 8888:8888 --name r_analysis -v "%cd%":/home/jovyan jupyter/r-notebook
```

５. コマンドラインに表示されたＵＲＬにアクセスします．
ただし，前半の
http://(efef3b9b0d5d or 127.0.0.1):8888/?
みたいな部分を，
http://127.0.0.1:8888/?
に変更してアクセスしてください．（efef3b9b0d5d に相当する文字と（）をとってください）
Jupyterが無事開けば完了です！
お疲れ様でした。  
次からは
```
docker run --rm -p 8888:8888 --name r_analysis -v "%cd%":/home/jovyan jupyter/r-notebook
```

で 指定されたＵＲＬにアクセスすることで Jupyter を立ち上げることができます．

※注意：閉じる時は必ず**セーブ**（左上の保存ボタンまたは ctr+S）をしたあと、
**quitボタン**でサーバをシャットダウンしてから閉じるボタンで消してください。
もし忘れてしまった場合は、**コマンドライン上で ctr+C を押して停止**させてください。

### hello R
ではこの環境で hello R してみましょう。  
（閉じてしまった方はもう一度Jupyterを立ち上げましょう。立ち上がらない場合は、すでにサーバが立ち上がっているのかもしれないので、

```
docker ps -a -f 'name=r_analysis' --format "{{.ID}}"
```

を実行し,文字列が出てきたらコピーして，

```
docker rm 出てきた文字列
```

を実行してからもう一度トライしてみましょう） 

次に、Jupyterの画面左上の　newボタンを押して、Rを選択しましょう.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271486/3dd64f64-8e91-b149-a4b6-a88acba7cf5a.png)

最後に、セルに

```
print("Hello, R")
```

と打ち込んで実行してみましょう。
下のイメージのようになれば成功です！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271486/5f42e83e-20ff-6eea-c039-c670f2ce114d.png)


参考までに、Jupyterの主要なボタンも紹介しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/271486/7a4b1b94-da13-45a6-8019-ab7fc9a1013a.png)



#### どうでもいい話
コレって結局何？というお話です。
1. **docker**
仮想化環境です。簡単に説明すると、コンテナという単位でアプリケーションの実行環境を分離する環境を構築するツールです。これを使うメリットとして、

- OS等に依存せずに（どこでも誰でも）同じように動かせる環境が作れる
- 環境が壊れたらその仮想化環境を削除すれば良いだけ（本体に影響を及ぼさない）

などのメリットがあります。


2. **anaconda (miniconda)**
もともとは、pythonの科学計算に特化したパッケージマネージャ兼仮想化ツールです。
今回はjupyterとR関係のモジュールを簡単かつ素早くインストールする目的で使いました。

3. **jupyter**
プログラムや説明の文章、実行結果などをまとめて管理できる、データ分析用のツールです。
ブラウザで動作するため、いろいろなOSから利用できる、解析の再現性が高い、インタラクティブな解析が可能等のメリットがあります。

4. **R**
言わずもがな。
統計解析が得意な言語です。

### まとめ
今回はJupyterでRを使う環境を構築しました。
使いこなすにはdockerの知識もある程度必要？かもしれませんので、
気になったら学習してみてください。
aws_sdk for rubyでLambda実行したら4分でNet::ReadTimeoutエラーになった話

Lambdaで10分くらいかかる処理をRubyから呼び出して実行（Lambda.invoke）しようとしたら、4分きっかりでNet::ReadTimeoutエラーが出てしまった。

```
E, [2019-04-18T19:47:42.952952 #18433] ERROR -- batch_syslog: Net::ReadTimeout

/.rbenv/versions/2.2.7/lib/ruby/2.2.0/net/protocol.rb:158:in `rescue in rbuf_fill'
/.rbenv/versions/2.2.7/lib/ruby/2.2.0/net/protocol.rb:152:in `rbuf_fill'
/.rbenv/versions/2.2.7/lib/ruby/2.2.0/net/protocol.rb:134:in `readuntil'
/.rbenv/versions/2.2.7/lib/ruby/2.2.0/net/protocol.rb:144:in `readline'
/.rbenv/versions/2.2.7/lib/ruby/2.2.0/net/http/response.rb:39:in `read_status_line'
/.rbenv/versions/2.2.7/lib/ruby/2.2.0/net/http/response.rb:28:in `read_new'
/batch/vendor/bundle/ruby/2.2.0/gems/aws-sdk-core-3.46.0/lib/seahorse/client/net_http/patches.rb:31:in `block in new_transport_request'
/batch/vendor/bundle/ruby/2.2.0/gems/aws-sdk-core-3.46.0/lib/seahorse/client/net_http/patches.rb:28:in `catch'
/batch/vendor/bundle/ruby/2.2.0/gems/aws-sdk-core-3.46.0/lib/seahorse/client/net_http/patches.rb:28:in `new_transport_request'
（中略）
/batch/vendor/bundle/ruby/2.2.0/gems/aws-sdk-lambda-1.17.0/lib/aws-sdk-lambda/client.rb:1867:in `invoke'
```

Lambda自体は実行が続いていて、正常に完了まで行く。
だが、Lambdaの実行完了をこのままでは捕捉できない。
それにしても4分って何？

なので、サポートに聞いてみた。

----

## AWSサポートの回答

まず、本件で発生しております Net::ReadTimeout はクライアント側のタイムアウトでございます。
そのため、Lambda 関数は正常に終了しているものの、クライアント側でエラーが発生しております。

回避策といたしましては、Aws::Lambda::Client.new メソッドの http_read_timeout パラメータを指定いただくのがよろしいかと存じます。
お客様のソースコードを拝見したところ、http_read_timeout と retry_limit が共にデフォルトのままでご利用されているとお見受けしました。
そのため、デフォルトの http_read_timeout である 60 秒とデフォルトの retry_limit である 3 回の再試行、合わせて 4 分が経過した時点でNet::ReadTimeout エラーが発生したものと考えられます。

http_read_timeout につきましては下記のドキュメントをご参照くださいませ。

Class: Aws::Lambda::Client — AWS SDK for Ruby V3
https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/Lambda/Client.html#initialize-instance_method

----

やっぱりそういう設定があったのねー。
リファレンスはちゃんとみないとあかんね。


----

## AWSサポートの回答　その他参考情報

Lambda 関数の実行に時間がかかると予想される場合は、invoke メソッドの invocation_type を Event とすることもご検討いただければと存じます。
invocation_type を Event にした場合、Lambda 関数は非同期で実行されます。
Lambda のサービスがリクエストを受け付けた段階でクライアントにレスポンスが返されるため、タイムアウトを抑制できます。
なお、この場合はレスポンスには Lambda 関数の実行結果は含まれないため、必要に応じて別途実行結果をご確認くださいますようよろしくお願いいたします。

----

非同期処理も検討したけど（LambdaでS3に終了判定ファイルを生成して、そのファイルの存在をRubyから定期的にチェックしに行く　とかで終了判定）、なんかスマートじゃないよね。
Rails/RspecのUpdateでハマった

# 概要
RailsのテストにRspecを導入してガリガリとテストを書いていたら、アップデート周りの処理でまあまあハマったので記録しておこうと思います。色々なサイトを調べたのですが少し古かったり直接使えなかったりでうまくいかなかったので。
なお、ここではomniauthを用いた認証のみをユーザ認証に使っている場合のみに当てはまると思っていただいても差し支えはないと思います。普通に認証する場合はfixtureを使った方が間違いなく見通しよくかけます。

## (ある意味での)原因
最初はfixtureを用意してそれを元にやっているのですが、いつまでたってもうまくいきませんでした。原因はfixtureを使っていたところにあって、逐一ログイン処理を書いて〜とやるとうまくいきました。ちなみに、うまくいかなった理由はログインをTwitter経由でのみ実装しているからです。テスト環境では一々認証していたら日が暮れてしまうので、わざわざネット経由で認証したりせず無条件でパスできるようになっています。これは裏を返せばここのユーザーを識別する情報を全く使わずに認証しているわけなので、例えばfixtureを使ってあらかじめポストを作っておいても、そのポストに対応したユーザーでログインできないのでそういった部分のテストがうまくいかないのでした。
> と思っているのですが、実際には違うよ！ということがあれば教えていただけると嬉しいです。

## ハマったところ
### （おそらく）スコープがよくわからないことになっている  
まず、以下のようなコードはうまくいきません.

``` ruby
      attrs = { content: 'aaa',                                                  
              url: '',                                    
              tags: 'aaa,hogehoge' }   
...
  it ... do
      attrs = { content: 'a' }
      # contentがaaa -> a になってほしい！
      page.driver.submit :patch, "/hoges/#{@hoge.id}", hoge: attrs 
      # => うまくいかない！ 
  end
```
この原因はいまだによくわかっていないのですが、it ... end　ブロックの中のattrsという変数名を他のものにするとうまく動きました。つまり、次のコードは正常に動作します。

``` ruby
      attrs = { content: 'aaa',                                                  
              url: '',                                    
              tags: 'aaa,hogehoge' }   
...
  it ... do
      dict = { content: 'a' }
      # contentがaaa -> a になってほしい！
      page.driver.submit :patch, "/hoges/#{@hoge.id}", hoge: dict
      # => うまくいかない！ 
  end
```

### リクエストの書き方がよくわからない  
色々調べてみると、リクエストの書き方として色々な書き方が出てきます。が、残念ながらそのうちの多くは自分の場合はうまく動作しませんでした。結局うまく動いたのは上に書いているように`page.drive.submit ~`としたものでした。

### 所感
テストを書くの大変ですね。
社内SEをやめて後悔した話

# はじめに

私の失敗談です。
個人の見解ですので、意見が合う合わないあると思います。
共感できるところが少しでもあり、これからの人生に少しでも生かして頂けますと幸いです。

# 私の略歴

専門学校卒で普通の下請けのSIerに入社し、転職してSES業界へ。
インフラエンジニアとしてせっせと働き続けましたが、やりがいを見出せず社内SEへ転向。
5年以上勤めましたが、幹部との信頼関係を築くことができず転職。
またSIerに入る。

やりがいを見出せない理由は、作り上げたインフラを看取ることができないからです。
自分の作り出した成果を良い・悪いを含めて受け止めたい。
私はそんな人間です。

# 私に気づかせてくれた子供からの言葉

ある日のやり取りです。
子は5歳です。

子「おとうさん、おじいちゃんになっても働く？」
私「うーん、どうだろう、おとうさんお金いっぱい持ってないから、働いてると思うよ。
　　どうかしたの？」
子「じゃあさ、子が大きくなるまで働いてる？」
私「うん、そのころはまだおじいちゃんになってないから、働いてるよ」
子「じゃあさ、一緒に働こうよ！
　　子はおとうさんと一緒に働きたい」

涙腺崩壊しました。

# なぜ社内SEをやめたのか

社内SEで働かせていただいている会社様でほんとうにたくさんの仲間に恵まれました。
その仲間たちの話を家で楽しそうに話していました。
仲間たちと子も数回会わせており、たくさん可愛がっていただきました。
そんな私の姿を見て、仕事ってきっとすごい楽しいんだろう、と思っているんだと思います。
実際、社内SEのころはとても楽しかったです。

ですが、入社してきた幹部の方ともめました。
そして退職しました。

# ひよってしまった話

退職をする一年ほど前にも転職活動をし、数社内定を頂きました。
その際に、「一緒に働きましょう！」と言ってくださる方もいらっしゃいました。
ですが、私はその言葉を無下にし、辞退しました。

それは、社内SEだった会社で待遇の見直しを約束されたためです。
実際には待遇よりも、当時の仲間と働くことを選んだのです。
ですが、この選択は失敗でした。
私の待遇は見直されることはなく、1年間で1銭も給料が上がりませんでした。
仲間との仕事は変わらず楽しかったのですが、私のモチベーションはダダ下がりし、新しくアイデアを出すということが少なくなりました。
それは少なからず仲間へも影響を与えていたと思います。
私は癌になりました。

# 振り返って

きっと罰があたったのだと思いました。
私の考えに共感し、一緒に働きましょう！とまで言ってくれた方の言葉を無下にしたことで。

そして、仲間との楽しい仕事を言い訳に、私は簡単な道に流れてしまったのです。
仲間との仕事が楽しいから残った、と言えば聞こえはいいですが、甘えたのです。現状に。
そう思った時には手遅れでした。

そこからどんどん状況は悪化し、慌てて再度転職活動をしたころには、私は魅力的な人材ではなく、テンプレート通りの受け答えをする、つまらない人間になっていたのです。

めちゃくちゃですが、当時は志望動機はなんですか？と聞かれたときに「ありません！」と断言していました。お恥ずかしい。
君変わってるね、と笑いながら聞いてくださる方もいらっしゃいました。
そんな失礼な受け答えでも、内定を頂ける会社様はあります。
人をしっかりと見定め、その人の人間性に共感してくださるような会社様です。
このような方は人事の方には少なく、現場たたき上げの管理職の方に多い印象を持っています。（個人の見解です）
私は、現場の方に評価されやすく、出世しにくいタイプの人間です。
その時の私はブランド力があったのだと思います。
自信に満ち、個性がプラスに働いていました。

その時の話が忘れられず、恥ずかしながら、以前内定を頂いた会社様にダメもとで連絡をしてみました。
当然、そんな魅力的じゃない私の言葉など、響くはずもなく、結果はひどいものでした。
その会社様は面接時は苦境に立たされていましたが、面接時のお人柄などから、必ずこの状況は脱出するだろうと思っていました。
先日、ニュースサイトでその会社様が好調であることを見ました。
きっと、魅力的な毎日を送っておられるのだと草葉の陰から応援しております。

そして、藁にも縋る思いで得た内定を疑いもせず、再度SIerへ飛び込んだのです。

# SIerの中で

協力会社様が多数いらっしゃり、その管理を行うことになりました。
実際には、相手の人生を切って売っているだけで、私ではない誰でもできる仕事だと感じました。
つまらない…そう思いながら、人の目を盗み勉強をする、定時が来たら、帰宅するだけの毎日でした。
人の顔色を見たり、こそこそと勉強しないといけない現状にイライラし、なんて無駄なことをしてるんだと自問自答の毎日でした。

すべてのSIer様、すべての管理業務を否定するわけではありません。
ただ、私には合いませんでした。

そこで、子供から前述の言葉をかけられました。

# 気が付いたこと

子供はしっかり私のことを見ていて、一緒に仕事をしたいとまで思ってくれていました。
この夢が現実的でないことは百も承知ですが、いいじゃん、やってみようよ、と思いました。

そして、いまのままではその未来にたどり着いたとしても「なんだよ、おとうさんの仕事つまらないじゃん」と言われることに気づきました。
私は子供に、背中で仕事が楽しいと教えたい、そんな仕事がしたい、とよく同僚と話をしていました。
子供はしっかり見ていて、私の思いを汲んでくれていた。
なのに私はいつの間にか忘れていました。

子供が言ってくれてうれしかった言葉を、かけてくれた方がいらっしゃったのに。
私は、その言葉を捨ててしまったのです。

# それから

今の会社では人の人生を切り売りして、数年後にチームが作れたとして、楽しく仕事ができる未来が想像できない。
この数年はきっと、私の人生に大きな影響を与えることになる。このまま腐っていてはだめだ、と思いました。

勉強会をやろうと呼び掛けたり、今の仕事は向いていないとアピールしたりしましたが、割り切って頑張ってよ、と言われてしまいました。
この時、私の中で結論は出ました。
すぐに行動に起こさなければ、と。

勉強しながら転職活動を続けます。
もう一度、一緒に働きましょう、と言っていただける自分になるために。
子供に胸を張って、まぢ仕事超楽しいから！一緒にITやろうぜ！って言えるようになるために。

昔の仲間は今でも大切な仲間で、いまだに連絡をくれる可愛い部下たちです。
そんな仲間たちの期待を裏切らないためにも、私は立ち止まらず成長し続けます。
1日1%でも。

# 感謝

励まして応援してくれるすべての方に。
今の仕事をすぐにやめて、転職してもいいと言ってくれた妻に。
腐っていた私の性根を叩き直してくれた子供に。
これからも、頑張り続けます。
ドメイン証明書の有効期限監視

# アイテム
- タイプ：外部チェック
- キー：ssl_cert_check.sh["{HOST.DNS}"]
- 単位：d

# トリガー
- 名前：{HOST.NAME}：証明書期限が30日を切りました
- 条件式

```bash
{0_SSL:ssl_cert_check.sh["{HOST.DNS}"].nodata(86400)}=1
 or {0_SSL:ssl_cert_check.sh["{HOST.DNS}"].last(0)}<30
 or {0_SSL:ssl_cert_check.sh["{HOST.DNS}"].count(#2,0)}>1
```

# ssl_cert_check.sh
```bash
#!/bin/bash

SERVER=$1
#SERVER="www.domain.com"
PORT=$2
if [ -z ${PORT} ]; then
  PORT="443"
fi

ret=`openssl s_client -connect ${SERVER}:${PORT} -servername ${SERVER} < /dev/null 2> /dev/null | openssl x509 -text | grep After`
edate=$(date +%s --date="`echo $ret | sed -e "/Not After/{s/^.* : //;p};d"`")
cdate=$(date +%s)

echo $((($edate - $cdate) / 60 / 60 / 24)) 
```

# メモ
- -servername ${SERVER} <- SNIでは必須
- テンプレートにすると漏れがない
- これで証明書期限切れ事故が大分減ったと。

### ドキュメントルートにある複数サイトをまとめ監視の場合
```bash
#!/bin/sh

dir_path="/var/www/html/*"
dirs=`find $dir_path -maxdepth 0 -type d`

for dir in $dirs;
do
  domain=${dir:14}

  if [[ ! $domain == "z_"* ]] && [ ! $domain = "default" ]; then    #監視不用のドメインディレクトリは除外
    ret=`openssl s_client -connect ${domain}:443 < /dev/null 2> /dev/null | openssl x509 -text | grep After`
    edate=$(date +%s --date="`echo $ret | sed -e "/Not After/{s/^.* : //;p};d"`")
    cdate=$(date +%s)

    limit=$((($edate - $cdate) / 60 / 60 / 24))     

    if [ $limit -lt 20 ]; then
      echo "${domain}: ${limit} days"
    fi
  fi
done
```
 * zabbixのトリガーでは帰り値があるとNGとする

 * 上記スクリプトを忘れまたつくちゃった。微妙に違うので追加。
```bash
#!/bin/sh

dir_path="/var/www/html/*"
dirs=`find $dir_path -maxdepth 0 -type d`

for dir in $dirs;
do
    domain=${dir/.\//}

    if [[ "$domain" == "default" ]]; then
        continue
    fi

    ret=`openssl s_client -connect ${domain}:443 -servername ${domain} < /dev/null 2> /dev/null | openssl x509 -text | grep After`
    edate=$(date +%s --date="`echo $ret | sed -e "/Not After/{s/^.* : //;p};d"`")
    cdate=$(date +%s)
    remain=$((($edate - $cdate) / 60 / 60 / 24))

    if [ $remain -le 10 ]; then
        echo "${domain}, Less than ${remain} days"
    fi
done
```

# 参考
http://www.todesking.com/blog/2017-02-11-openssl-alert-handshake-failure/

###追記
スクリプトパラメタにポートを追加
まとめ監視追加
【Blockchain】Visual Studio Code で開発するための準備

今までは、[Remix](https://remix.ethereum.org) を使ってSmart Contractを作ってきましたが、そろそろ本格的なプロジェクトを作りたいのでIDE(Visual Studio Code)の準備をしていきたいと思います。

## プロジェクト初期化

- Node.js と npm は必要ですので、Macでのインストールは[こちらの記事](https://qiita.com/toshiok/items/276b082b69055717609f)を参考にしてください。


- 任意の場所に任意のプロジェクトフォルダを作成します。

```sh
mkdir {プロジェクトフォルダ}
```

- 作成したフォルダに入り、npmを初期化します。

```sh
npm init
```

※ いろいろ聞かれるので、 `return` キーで次へと進みます。最後の質問は `yes` で完了します。

## 必要なパッケージをインストール

### Solidity コンパイラ

- インストール可能なバージョンを確認します。

```sh
npm show solc versions
```

- 任意のバージョンのコンパイラをインストールします。

```sh
npm install --save-dev solc@{バージョン}
```

### web3.js ライブラリ

- インストール可能なバージョンを確認します。

```sh
npm show web3 versions
```

- 任意のバージョンのライブラリをインストールします。

```sh
npm install --save-dev web3@{バージョン}
```

## Visual Studio Code

- 次のURLからダウンロードし、インストールをします。

https://azure.microsoft.com/ja-jp/products/visual-studio-code/

### Solidity開発のための拡張機能 

- VSCodeを立ち上げ、次の手順でSolidity開発のため拡張機能 `Solidity support for Visual Studio Code` をインストールします。

![20190423_Blockchain_VSCode_Plugin.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273166/fe997ee5-266b-0554-92ea-27fcb131839e.png)

### Solidityバージョンの設定

- `File > Open...` の手順で、上で作成したプロジェクトフォルダをVSCodeで開きます。

- `Code > Preferences > Settings` の手順で設定画面を開いて、下のように進めて現在開いているプロジェクトの `settings.json` を編集します。

![20190423_Blockchain_VSCode_solidity_ver.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/273166/e2e32a8e-45de-b0de-4949-aa5daa940455.png)

- この例では、Solidityの `v0.4.24+commit.e67f0147` を設定しています。

```json
{
    "solidity.compileUsingRemoteVersion": "v0.4.24+commit.e67f0147"
}
```

これで、一通り `Visual Studio Code` で `Blockchain` の開発をするための準備は完了です。
【Ruby2.5】yield_selfとtapを使ったサンプルコード

# yield_selfとtapをサンプルコードで比較

[github](https://github.com/keiorkt/ruby_tips/blob/master/useful_methods/yeild_self.rb)にもアップしました。

### Kernel#yield_self
> レシーバを引数として与えられたブロックを呼び出し、ブロックの評価結果を返す。
tapと似ているが、tapはレシーバ自身を返す。

### Object#tap
> ブロック変数にレシーバ自身を入れてブロックを実行します。戻り値はレシーバ自身です。

```yield_self_vs_tap.rb
### まずは単体で
10.yield_self { |x| x ** 2 }
# 100

10.tap { |x| x ** 2 }
# 10

### 繋げてみる
10.yield_self { |x| x ** 2 }.yield_self { |y| y * 2 }
# 200

10.tap { |x| x ** 2 }.tap { |y| y * 2 }
# 10

### yield_selfとtapの組み合わせ
10.yield_self { |x| x ** 2 }.tap { |y| y * 2 }
# 100

10.tap { |x| x ** 2 }.yield_self { |y| y * 2 }
# 20
```
# 参考
技術評論社 『WEB+DB PRESS Vol.103』 p.50
Rubyリファレンス url: https://ref.xaio.jp/ruby
セレクトボックス 選択中のオプション名を取得

こちらの[ページを参考](https://javascript.programmer-reference.com/js-select-value/)に
セレクトボックスの選択中のオプション名を取得する

```javascript
var getValue = function (tar_id) {
	// ラベル名を取得する
	var obj = document.getElementById(tar_id);
	var result = obj[obj.selectedIndex].label;
	//var result = obj.value;

	// Alertで表示する
	alert('選択中のラベルは' + result + 'です。');
}
```
Android ボタンの短押し対策について

## はじめに
Androidアプリでよくあるやつです。

OnClickListenerを設定したViewを、もの凄い勢いで連打した場合、
高確率で複数クリックイベントが発行されてしまいます。

Activityが多重に重なったり、通信処理が複数走ったりと、バグの要因になりえます。

今回、ボタンの短押し対策をKotlinの拡張関数で実装してみました。

## 対策前
ボタン連打で多重に画面が起動する。

![pre.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112186/9a00a050-acca-2400-9560-f09bfcc4413f.gif)

## 対策コード

拡張関数で以下を定義します。

画面に複数コントロールを配置していても、
clickTime が 1000ms 満たしていないと、クリック不可となります。

```kotlin:ViewExtension.kt
import android.view.View

private var clickTime: Long = 0

fun <T : View> T.setOnSafeClickListener(block: (T) -> Unit) {
    this.setOnClickListener { view ->
        if (System.currentTimeMillis() - clickTime < 1000) {
            return@setOnClickListener
        }
        @Suppress("UNCHECKED_CAST")
        block(view as T)
        clickTime = System.currentTimeMillis()
    }
}
```

呼び出し元はこんな感じです。

```kotlin
view.setOnSafeClickListener {
    // ...
}
```

## 対策後
![post.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/112186/2309b9ab-37e5-9630-9e38-f99ea139487f.gif)

画面をブロッキング(プログレス表示)しない場合は、
isEnabled や isClickable をよになに無効にすればいいかと思います。

```kotlin:
hogeButtion.setOnSafeClickListener { button ->
    button.isEnabled = false
    // ...
```

以上。
#jq コマンドで否定形で検索・絞り込みする例

# negative

` | not` を使う

```
$ echo '[{"count":1},{"count":3},{"count":2}]' | jq '.[] | select(.count==2 | not)'
{
  "count": 1
}
{
  "count": 3
}

```

# positive 
```
$ echo '[{"count":1},{"count":3},{"count":2}]' | jq '.[] | select(.count==2)'
{
  "count": 2
}
```


# Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/1428








<!-- Update From Qiita API -->

# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ





# Twitter


https://twitter.com/YumaInaura


<!-- Update From Qiita API -->
moviefileinTOPで画像をロード後、Exifを読んで適切なOrientationで表示する


下の例は、縦向きの写真をデジカメで撮った後、TouchDesignerにその画像ファイルをドラッグ&ドロップした様子です。縦長の画像の筈が、TouchDesigner上では横長に表示されてしまっています。向きが正しくありません。

![Annotation 2019-04-05 195841.jpg](https://qiita-image-store.s3.amazonaws.com/0/86490/7c30905c-d66a-cf40-90c5-fdd9c45dbc12.jpeg)


2019年4月の時点ではmoviefileinTOPが画像を正しくない向きに表示してしまう不具合があるようです。この問題をDerivativeの人に確認してみた所、RFEリストには入っているけどまだ対応してないスマンな... との事でした。


## 原因は？
Exif（デジカメで撮影した画像に記録されている、撮影条件に関するメタデータ）に入っている`Orientation`タグが、画像表示をする際に反映されていないんでしょうか。
`Orientation`タグについて分かりやすくて詳しい参考記事: http://dwm.me/archives/4203



## 対策は？

`Orientation`タグは1～8の計8種類定義されています。全8種類の画像が適切な向きで表示されるようなCOMPが欲しいです。

1. 検証用素材の準備
2. 検証用素材のExifから`Orientation`タグを読む
3. flipTOPを使い、読んだタグに応じて向きを変更する
4. COMPのCustom Parameterに画像パスを入れると、上記2~3が走るようにまとめる

これで勝てそうな気がします。



## ImageFileInCOMP を作る

上記1～4を順に行います。
4のCOMPの名前をImageFileInCOMPとしました。
（完成品Download URLは[ページ一番下](#imagefileintox-download)に）


### 1. 検証用素材の準備

1~8の全Orientationタグが記録されている画像集が必要です。
今回は [https://github.com/recurser/exif-orientation-examples](https://github.com/recurser/exif-orientation-examples) を使わせて頂きます。ここに上がっている8枚 * 2パターン (Portrait / Landscape) 計16枚の画像がそれぞれ正しい向きで表示されればOKとします。

↓ `Orientation=2` の画像をExif情報無視してそのまま表示した様子
![photo.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86490/eb83eb2e-2106-c7d6-f818-a79f196b6390.jpeg)

↓ 全サンプル画像をExif情報無視してそのまま表示した様子
![all.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86490/15308656-b866-f7e0-aefb-e781a2f51607.jpeg)

※Exifは全ての画像ファイルに入っている訳ではありません。例えばTDBananaには`Orientation`タグは入っていません。この記事では、Exifが入っている画像を扱う事を前提に書いているので注意してください。




### 2. Exifから`Orientation`タグを読む

今回は[PythonでPILを使用してExifから欲しいデータだけを抜き出す](https://qiita.com/Gen6/items/88c69ab3a0666895e7a8)を参考に、Exifを読んでみようと思います。(環境にあわせて、piexif, ExifRead等、他のパッケージを使ってもOKと思います)

```
$ pip install pillow
```

PILを使って `Orientation` を取得します。

```
from PIL import Image

filepath = "/path/to/myimage.jpg"

try:
	img = Image.open(filepath)
	exif = img._getexif()
	orientation = exif.get(0x112, 1)
	print('Orientation:', orientation)
except:
	raise ValueError("Couldn't find exif in " + filepath)
```



### 3. flipTOPを使い、読んだタグに応じて向きを変更する

`Orientation`タグに応じてflipTOPの`flipx`, `flipy`, `flop`パラメータをどうするか、tableDATにまとめます。（taskカラムは「どう回転させるか」のメモです。PILで画像の回転を操作する時のenumを記載しています。今回は特に使用していません。）
![Annotation 2019-04-25 171416.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86490/7c0439f2-4858-da27-3ea9-c93556639176.jpeg)




### 4. COMPのCustom Parameterに画像パスを入れると、上記2~3が走るようにまとめる

まとめました。
![Annotation 2019-04-25 173330.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86490/58418979-503c-348b-e58b-300dca9abdaa.jpeg)
![Annotation 2019-04-25 173224.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86490/a46ea756-55fe-70c5-2fe5-b32fd852027e.jpeg)


これで全サンプル画像が適切な向きで表示されました。
![all1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/86490/df1657b1-91b7-1815-e641-ac84815f6b94.jpeg)


## ImageFileIn.tox Download

Download --> [ImageFileInCOMP.tox](https://github.com/motoishmz/opentox/raw/master/ImageFileInCOMP.tox)
RailsにおけるMVC

#MVCとは
UI（ユーザーインターフェース）を持つソフトウェアのアーキテクチャの1つ。
簡単に言うとUIを作るための構造の1つである。
M（Model）、V（View）、C（Controller）の3つから構成されており３つに役割を分けた構造をすることで管理しやすくしている。Railsはこのアーキテクチャを使用している。
##M（Model）
Modelはデータとデータに関わる処理をオブジェクトとして実装したもの。
##V（View）
クライアントがサーバーに対してリクエストして、サーバーはレスポンスを返す。このレスポンスの中身を組み立てているのがViewである。ViewにControllerがModelのオブジェクトを埋め込みデータベースの情報を表示させている。
##C（Controller）
Controllerはクライアントからのリクエストを解析し、リクエストに応じたレスポンスを返すように制御する。
##実際のMVCの流れ
1. コントローラ層でブラウザなどのクライアントが送るリクエスト先のURLやVerb(GET、POST...)などをもとに、どのコントローラのどのアクションの処理が対応するのかを決める。
2. アクションは必要に応じてModelを利用しデータベースのデータの読み出しや書き込みを行う。
例えば、特定の記事の詳細（ここではPostというModel）を表示したい場合、showアクションが該当するアクションだとすると、showアクションでは、PostオブジェクトをPostクラスから取得する。
3. アクションごとにViewテンプレートが用意されており、このテンプレートを用いてHTMLを生成する。
ここでは2でオブジェクトの取得をした場合、HTMLにその情報を埋め込む。
4. 最後にコントローラがレスポンスを作成し、Webサーバーを介してクライアントへ返す。

##最後に
Railsを使いこなせるようになるためにはまずは、MVCの理解は不可欠である。
いろいろな記事を見て理解してもらえたらと思う。
もし、間違っている点がありましたらご指摘お願いします。
Rails5.1+のプロダクション環境で「Uglifier::Error: Invalid assignment」

# Uglifier::Error: Invalid assignment

```
...
Uglifier::Error: Invalid assignment
 /opt/rubies/ruby-2.5.1/bin/bundle:23:in `load'
 /opt/rubies/ruby-2.5.1/bin/bundle:23:in `<main>'
 Tasks: TOP => assets:precompile
 (See full trace by running task with --trace) (Executor::NonZeroExitStatus)
```

バージョン移植の時に遭遇したES6のエラー。Rails5.1+のプロダクション環境で発生するとのこと。

# 解決

```ruby:production.rb
config.assets.js_compressor = Uglifier.new(harmony: true)
```

以上の設定を追記すれば解消。

参照：https://github.com/lautis/uglifier/issues/132
【Laravel】Arr::wrap()は必要か？

Laravelのコードリーディングをしてる時に学んだことを書きます。

Laravelの`Arr`クラスに`wrap`というメソッドががあります。
このメソッドは引数が

- `null`なら空の配列
- 配列なら引数
- それ以外なら引数を格納した配列

を返します。

```php:Illuminate/Support/Arr.php
/**
 * If the given value is not an array and not null, wrap it in one.
 *
 * @param  mixed  $value
 * @return array
 */
public static function wrap($value)
{
    if (is_null($value)) {
        return [];
    }

    return is_array($value) ? $value : [$value];
}
```

`(array)`何が違うのか？と思ったわけです。
ですが、以下のコードを実行すると違いが明瞭になります。

```php:PHP
use Illuminate\Support\Arr;
class A 
{
    public $a = 1;
}

$a = new A;

var_dump(
    Arr::wrap($a),
    (array)$a, 
);

/*
array(1) {
  [0]=>
  object(A)#324 (1) {
    ["a"]=>
    int(1)
  }
}
array(1) {
  ["a"]=>
  int(1)
}
*/
```

`Arr::wrap($a)`はオブジェクトを格納した配列を返しているのに対し、`(array)$a`はオブジェクトを配列に型変換しています。

そもそもラップと型変換は違う概念ですね。

# 結論
`Arr::wrap()`は必要。
Rails5でjqueryを使ったスクロールボタンを実装

# 環境
ruby 2.6.3
Rails 5.2.3

#事前準備
何でも良いのですが、ここではscaffoldを使って、blog機能を先に実装してます。

# jquery / Bootsrap のインストール
bootstrapを入れた理由は、今回の場合は特にない。

```command
$ yarn add jquery

$ yarn add bootstrap
```

```ruby：app/assets/javascripts/application.js(マニフェストファイルの修正)
//= require rails-ujs
//= require jquery
//= require jquery/dist/jquery.js
//= require bootstrap/dist/js/bootstrap.min
//= require activestorage
//= require turbolinks
//= require_tree .
```

```ruby：app/assets/stylesheets/application.css(マニフェストファイルの修正)
*= require bootstrap/dist/css/bootstrap.min
*= require_tree .
*= require_self
```
# 補足：font-awesome-railsを導入

このリンク先にあるタグ\<head>タグに入れる
https://fontawesome.com/start

gemを使ったやり方を試したが、動作しないため、簡単なこちらを選択した。

# コード

```html:app/views/layouts/application.html.erb
<body>
    <%= yield %>
    <div id="back-to-top" style="position:fixed;right:25px;bottom:5px">
      <a href="#"><i class="fas fa-arrow-alt-circle-up fa-5x"></i></a></div>
  </body>
```
```css:top.css(ファイル名はなんでも良い)
.fa-arrow-circle-up:hover{
  color:blue;
  background-color:none;
}

#back-to-top a:hover{
  background :none;
  color:black;
}
```

```js:scroll.js(ファイル名は何でも良い)
$(function(){
  // #back-to-topを消す
  $("#back-to-top").hide();

  // スクロールが十分された時に#back-to-topを表示。スクロールされたら非表示
  $(window).scroll(function(){
    // this(window要素)がどれだけスクロールしたかをscrollTop()を使って値を取る
    $('#pos').text($(this).scrollTop());
    if ($(this).scrollTop() > 60){
      $("#back-to-top").fadeIn();
    }else{
      $('#back-to-top').fadeOut();
    }

  });

  //#back-to-topがクリックされたら上に戻る
  // animateメソッドを使用
  $('#back-to-top a').click(function() {
      $('html, body').animate({
          scrollTop:0
      }, 800);
      return false;
  });

});
```
composerとmemory_limit

# はじめに

composerによるmemory_limit設定の扱いについて。

# memory_limit設定

現行バージョンの1.8.5では以下のようになっています。

* もし現在の`memory_limit`の設定値が1536M未満なら、composerが自分で1536Mに増やします
* `COMPOSER_MEMORY_LIMIT`環境変数による指定があればその値で設定します

したがって、メモリ不足のエラーが `Allowed memory size of XXX bytes exhausted...`だったときはphpの`-d memory_limit=XX`オプションを使ったり、`COMPOSER_MEMORY_LIMIT`環境変数によって`memory_limit`の制限を緩めて再実行すると思いますが、1536Mより小さな値では意味がありません。1.5GBでだめならもう次に試すのは制限無しの`-1`でいい気がします。

# 余談: Out of memory

同じメモリ関係のエラーでも`Out of memory`のときはOSからメモリをもらえなかったので、PHPの設定は関係ありません。ulimitで制限してないか確認するとかメモリ食ってるプロセスをいったん殺すとかスワップ追加とかそういう対応になります。

# おわりに

それにしてもどうしてこんなにcomposerはメモリ食いなんだろう?
SIPレジストレーションで内線通話をするためのコード

:::note alert
2023年5月1日を持ちまして、株式会社KDDIウェブコミュニケーションズのTwilioリセール事業が終了したため、本記事に記載されている内容は正確ではないことを予めご了承ください。
:::

# はじめに

みなさん、こんにちは。
Twilioエバンジェリストの高橋です。

今回は、TwilioのSIPレジストレーションをつかって、レジストされた端末同士の通話をするコードをご紹介します。

# SIPレジストレーションについて

Twilio内にあるレジストレーションサーバーをつかって、SIPクライアントをレジストするための機能で、SIPインターフェースのオプションになります。
SIPレジストレーションについては、[こちらの記事](https://qiita.com/mobilebiz/items/26f91550556088545c49)もご覧ください。

今回は、すでにレジストレーションされている端末同士で内線通話するというシナリオで進めます。
レジストされていれば、それだけで内線通話ができるわけではなく、Twilio内部で転送をする必要があります。具体的にいうと、内線番号1111(端末A）という端末から、内線番号2222（端末B）に対して発信をすると、端末Aから発信依頼を受けたSIPインターフェースが、そのコールをどうするかをユーザに問い合わせます（Webhook）。ここで、端末Bに対して転送するTwiMLを返却することで初めてTwilioから端末Bに呼び出しがかかり、応答することで端末Aと端末Bの通話が成立します。

今回は、TwilioからのWebhookに応答するためのコードをご紹介します。

# ハンズオン

## シナリオ

今回は以下のシナリオで進めます。

- 内線番号が1111と2222という２台のSIPクライアントがあるとします。
- すでにSIPレジストレーションの設定が終わっていて、SIPクライアントからは発信ができる状態になっています。
- 内線同士の通話のみをサポートし、外線発信はしません。

## 作業手順

1. Twilio Functionsを使って、転送するためのコードを登録します。
2. 作成したFunctionのURLをSIPインターフェースのWebhook先に登録します。

## Twilio Functionsを使って、転送するためのコードを登録します

- Twilioの管理コンソールにログインします。
- スライドメニューを開き、Runtime -> Functionsを選択します。
- **Create a Function**ボタンを押します。
- ダイアログが表示されたら**+Blank**を選択して**Create**ボタンを押します。
- FUNCTION NAMEに、「SipToSip」と入力します。
- PATHには、「/sip-to-sip」と入力します。
- CODE欄には、以下のプログラムを貼り付けます。

```javascript
exports.handler = function(context, event, callback) {
    // 発信元番号（SIPアドレス形式）を取得
    let from = event.From || '';
    console.log(from);
  
    // 発信先番号（SIPアドレス形式）を取得
    let to = event.To || '';
    console.log(to);
  
    // SIPアドレス（sip:0AB〜J@xxxxxxx）を分解して0AB〜Jの部分と、SIPドメインの部分を取得する
    const sipTo = to.indexOf("@") > 0 ? to.substring(4, to.indexOf("@")) : '';
    const sipDomain = to.indexOf("@") > 0 ? to.substring(to.indexOf("@")) : '';

    // Fromアドレスの内線番号部分を取り出す
    const sipFrom = from.indexOf("@") > 0 ? from.substring(4, from.indexOf("@")) : '';

    console.log(sipTo, sipFrom, sipDomain);

    // TwiMLを作成
    let twiml = new Twilio.twiml.VoiceResponse();
  
    let dialParams = {};
    dialParams.callerId = sipFrom;
    dialParams.answerOnBridge = true;
    dial = twiml.dial(dialParams);
    dial.sip({
    }, "sip:" + sipTo + sipDomain);
    callback(null, twiml);
};

```

- **Save**ボタンを押して保存します。
- 保存ができたら、PATH欄の右にあるコピーボタンを押して、URLをコピーします。

## 作成したFunctionのURLをSIPインターフェースのWebhook先に登録します

- Twilio管理コンソールのスライドメニューから**Programmable Voice**を選択します。
- **SIPドメイン**を選択して、すでに作成済みのSIPドメインをクリックします。
- **Voice Configurations**の中の、**REQUEST URL**欄に、先程コピーしておいたURLを貼り付けます。
- **Save**ボタンを押して設定を保存します。

以上で設定はすべて完了です。
実際に内線番号で電話をかけて、通話ができることを確認してください。

---
### Twilio（トゥイリオ）とは

https://twilio.kddi-web.com
Twilioは音声通話、メッセージング（SMS/チャット）、ビデオなどの 様々なコミュニケーション手段をアプリケーションやビジネスへ容易に組み込むことのできるクラウドAPIサービスです。初期費用不要な従量課金制で、各種開発言語に対応しているため、多くのハッカソンイベントやスタートアップなどにも、ご利用いただいております。
moment.js→day.jsへ移行してダイエットしよう

## 概要

日付操作でお馴染みのmoment.jsですが、容量がでかいことでも知られています。

今回は[day.js](https://github.com/iamkun/dayjs)を代替品として使って容量を軽くしていきたいと思います。



## バンドルサイズの変化

私達のプロジェクトはNuxt.jsなので、バンドルサイズが可視化できるコマンドを使って見ていきたいと思います。

そもそものバンドルサイズを減らして何になるの？と思った人は下のサイトを閲覧するといいかもしれません。

[webpackのbundle後のJavaScriptのサイズを減らしている話 | リクルートテクノロジーズ　メンバーズブログ](https://recruit-tech.co.jp/blog/2018/12/15/try_optimization_webpack_bundle_size/)

### Nuxt.jsでバンドルサイズを確認する方法

```
yarn build --analyze
```

### moment.js

右上の方であぐらをかいていたmoment.jsが…
<img width="955" alt="スクリーンショット 2019-04-25 16.38.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264910/5771e23c-ea60-8b76-2195-219b4942a066.png">


### day.js

いなくなりました。
<img width="942" alt="スクリーンショット 2019-04-25 17.03.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264910/ed83d842-1893-19a5-afb8-ba6dd71cbbf7.png">


### 比較

Stat sizeだけでも、143.42KB→8.25KBへの削減ができています。めっちゃすげ〜（小並感）

<img width="261" alt="スクリーンショット 2019-04-25 17.09.33.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264910/010747a4-5932-9d69-3dc3-177e84c86194.png">

<img width="180" alt="スクリーンショット 2019-04-25 17.07.06.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/264910/49756ffe-689c-edff-10ad-f5f9c7893e16.png">

## 手順

### momentをアンインストールする

```
yarn remove moment
```

### dayjsをインストール

```
yarn add dayjs
```

### とりあえずプロジェクト内で「moment」を対象にGrepし置換していく

基本「moment」を「dayjs」に置換するだけでいいはずです。

dayjsのlocaleはimportしないとundefinedのままでエラーが出てしまうので、各自importしておきましょう。
[Cannot read property 'weekdays' of undefined · Issue #235 · iamkun/dayjs](https://github.com/iamkun/dayjs/issues/235)

```javascript:moment
import moment from 'moment';

moment.locale('ja');
```

```javascript:dayjs
import dayjs from 'dayjs';
import 'dayjs/locale/ja';

dayjs.locale('ja');
```

### fromNowなどの相対時刻取得にも別importが必要なので注意

これに限らず他にもPluginsはあるそうです

必要な場合は[こちら](https://github.com/iamkun/dayjs/blob/dev/docs/en/Plugin.md)を参考にimport、extendしましょう


```javascript:moment
import moment from 'moment';

moment.locale('ja');

const dateStr = '2019/04/22';
moment(new Date(dateStr)).fromNow();
```

```javascript:dayjs
import dayjs from 'dayjs';
import 'dayjs/locale/ja';
import relativeTime from 'dayjs/plugin/relativeTime';

dayjs.locale('ja');
dayjs.extend(relativeTime);

const dateStr = '2019/04/22';
dayjs(new Date(dateStr)).fromNow();
```

## まとめ

- dayjsすご
- importが多くなって行数増えるけどこれはトレードオフかなぁ
- 関係ないけどlottieのサイズでかすぎだろ

バンドルサイズをうまく減らせてダイエットできました〜

lodashなども使用するメソッドのみimportするといいかもですね〜

ありがとうございました〜

僕も自分のバンドルサイズを減らすためにスケボーしたいと思います〜

## 参考資料

[dayjs/API-reference.md at dev · iamkun/dayjs](https://github.com/iamkun/dayjs/blob/dev/docs/en/API-reference.md)

[日付時刻操作ライブラリをmomentからdayjsへ乗り換えた - Qiita](https://qiita.com/yagi_suke/items/2848c8981ea6d9f26587)

[JavaScript dayjsはMoment.jsの代替になるか? | nansystem](https://nansystem.com/dayjs-alternative-to-momentjs/)
SIerに入って８年ぐらい経ったのでいろいろ棚卸しをしてみる。【vol.005 Silverlight × Spring .NET】

## 出会い

初C#は、2013年末から半年ぐらい関わった開発メインのPJで、
「Silverlight × Spring .NET」という一風変わった組み合わせの案件だった。

発注元の要望でO/Rマッパーを一切使わない実装で、
Javaでいえば、JDBC＋マッパークラスは自前という古き良きスタイルだったので、
DBアクセス周りは、特筆すべきことは何もない。

発注元の自社エンジニアが技術力がないので、教育的観点から進んだ技術を使うのではなく、
開発初心者のサンプルアプリにもなるものにしたい、という話だった。

「Silverlight × Spring .NET」の組み合わせも顧客要望だった。

確かフロントは、ヌルサクのデスクトップアプリっぽく作りたいけど、
サーバーサイドは、発注元にJava出身者がいるので、その人にわかりやすいように作って欲しい、
とかだった気がする。

Visual Basicで作った.NETアプリケーションからのコンバートPJでもあった。
VBってあたりが時代を感じる。

## Silverlightとは

>HTMLやCSSなど通常のWebページの技術では困難な高い表現力や操作性を備えたアプリケーションをブラウザ上で実現することができる。
いわゆるRIA（Rich Internet Application）プラットフォームと呼ばれる技術の一つで、似た技術としてAdobe Flashがある。
>[Silverlightとは - IT用語辞典 e-Words](http://e-words.jp/w/Silverlight.html)

生みの親のMicrosoftにも見放された初期バージョンリリースから開発更新停止まで5年という短命な技術である。
次々と主要ブラウザのGoogle ChromeやFirefoxで動作しなくなり、もはやInternet Explorerと一緒に葬り去られる運命にある。

## Spring .NETとは

JavaのSpring Frameworkの.NET版であるが、[公式の活動は2012年を最後に息をしていない](https://springframework.net/)。
[DIやAOP](https://qiita.com/shuntaro_tamura/items/ba5a2e9b3ba305285edd)といったSpringが提供している仕組みが使える。

##振り返ってみて

サーバーサイド担当だった自分にとっては、Camelで書くか、Pascalで書くか、ぐらいの違いしかなくて、C#でJavaっぽく開発したPJだった。
それもSpring使って、O/Rマッパー使わずにJDBCでDBアクセス部分を実装する、ような振り返ってみても奇妙なアーキテクチャである。

JavaとC#の記法の違いはこちらのサイトが詳しい。
>[JavaとC#の命名規約まとめ （JavaプログラマーがC#でプログラムを書いて引っかかったところ　その３）](https://yujisoftware.hatenablog.com/entry/20110529/1306676847)

Modelクラスを作った時に、Getter・Setterを書かなくて良かったので、それだけが印象に残っている。とはいえ、Javaでも[Lombok](https://qiita.com/yyoshikaw/items/32a96332cc12854ca7a3)を使えば、同じことは出来る。

アーキテクチャ的には、[MVVM](https://qiita.com/homyu/items/046c9a43b15b9e376198)モデルで作ったので、JavaのMVCモデルのWebアプリでそれまで開発していた身からすると、その点は、自分にとって新しかった。

ViewModelを定義してフロントエンドとサーバーサイド間で、状態を非同期で連動させていくスタイルは、当時のJavaのWebアプリに比べると圧倒的にUIが使いやすい印象を持った。

今で言えば、フロントエンドをVueか、Reactあたりで作って、サーバーサイドととは、JSONでやり取りするスタイルに近いのかもしれない。

「Silverlight」「Spring .NET」もどちらも.NET界隈主流の開発スタイルからは外れている。
６－７年経った今、その後のサポートとか大丈夫なんだろうか、と心配になる。

そういった意味では、流行り廃りも考慮した上でフレームワークを採用するのって重要だな、
と教えてくれたPJでもある。

「Silverlight」のXamlの技術とかは、元々WPFで使われているし、
「Spring .NET」のDipendency Injectionは、他のフレームワークでも普通に取り入れられている。

技術要素は、そのフレームワーク自体が廃れても新しいフレームワークに組み入れられていたりして、エンジニアからすれば、培った経験は無駄になることはない。

とはいえ、システム単体で見るとフレームワークの開発サポートが切れたりすると、一気に技術的負債の塊になる。脆弱性の問題もあるし、進化し続ける他のフレームワークと生産性の差は開くだろうし、提供できる機能にも制限がかかるだろう。

もしかしたら重要なのは、進化し続けるだろうフレームワークを当てることより、
外れても乗り換えが容易に出来るようにシステム構成を整えることなのかもしれない。
Javascriptでmatch→replaceを成功させるために

## 概要

javascriptで文字列をmatchして抜き出し→replaceで置換するだけの処理なのにnullとか出て思いっきりハマった(3-4時間飛んだ)ので次からはハマらないために頑張りたい。

## なぜnullになるのか

今回初めて知ったけど、matchの結果は**Array型**で帰ってきている。

そして**replaceはString型**なので、型がかみ合わなくてnullになってしまう。

## 防ぐためには

`toString()`する。
書き方はjQueryだけど、素のJSでも一緒

```javascript
var hoge = $('fuga').toString();
```

## 汎用化したい

簡単にfunction書いて汎用化してみた。

```javascript
function matchReplace(selector, pattern, before, after){
  const matched = selector.match(pattern).toString();
  const replaced = matched.replace(before, after);
  return replaced;
}
```

### 例

#### HTML
```html:index.html
<p id="hoge">私はDAFUです。</p>
```

#### JS
```javascript:script.js
$('#hoge').text(matchReplace($('#hoge'),$('#hoge').text(), /DAFU/, 'ダフ'));
```

#### 表示
```出力
私はダフです。
```

### 引数について

当然引数が正規表現でもよい。

|引数|説明|例|
|:--|:--|:--|
|selector|ターゲット要素のセレクタ|`document.getElementById(id)`|
|pattern|matchでの検索パターン|`/正規表現/`|
|before|replaceの置換前の文字列|`'置換前'`|
|after|replaceの置換後の文字列|`'置換後'`|

## まとめ

正直今まで型を気にしたことがなかったのだが、今回痛い目にあったのでちゃんと注意しようと思った。

また、割とmatch→replaceの処理は個人的に使う機会が多そうなのでfunctionにまとめられてよかった。
tmux をソースコードからビルドしてローカルインストールする

そこそこプレーンなシステムに `tmux 3.2-rc3` をビルドしようと思ったらうまく行かなかったのでメモ。
次の記事を参考にしています。[tmuxをローカルインストールする時のメモ - Qiita](https://qiita.com/makisyu/items/d6b32d88cdd97b01a00f "tmuxをローカルインストールする時のメモ")
具体的には `tmux` を `make` するときに `LDFLAG` `CFLAG` を指定するところが元の記事と違う点。

`${HOME}/local` 以下にインストールすることを想定しています。

(2020/11/30: tmux のバージョンが Pre-release の 3.2-rc3 になったので変更)

# 環境
インストールした環境。

* FreeBSD 11.2-RELEASE-p14


# ビルドの流れ
### [libevent](https://libevent.org/) のビルド
バージョンは 2.1.12。

```bash
# libevent のビルド
wget https://github.com/libevent/libevent/releases/download/release-2.1.12-stable/libevent-2.1.12-stable.tar.gz
tar zxf libevent-2.1.12-stable.tar.gz
cd libevent-2.1.12-stable
./configure --prefix=${HOME}/local
make
make install
```
### [ncurses](https://invisible-island.net/ncurses/) のビルド
バージョンは　6.2。

```bash
# ncurses のビルド
wget ftp://ftp.invisible-island.net/ncurses/ncurses.tar.gz
tar zxf ncurses-6.2.tar.gz
cd ncurses-6.2
./configure --enable-pc-files --prefix=${HOME}/local --with-pkg-config-libdir=${HOME}/local/lib/pkgconfig --with-termlib
make
make install
```

### [tmux](https://github.com/tmux/tmux/releases) のビルド
tmux のバージョンは 3.2-rc3 (Pre-release)。

元の記事と違って、`LDFLAGS` と `CFLAGS` を指定する必要があった。
( 参考: [ローカルユーザーでtmuxをソースインストールする - よくわからないエンジニア](https://www.unknownengineer.net/entry/2016/09/06/170203) )

```bash

# tmux のビルド
wget https://github.com/tmux/tmux/releases/download/3.2-rc/tmux-3.2-rc3.tar.gz
tar zxf tmux-3.2-rc3.tar.gz
cd tmux-3.2-rc3
PKG_CONFIG_PATH=${HOME}/local/lib/pkgconfig ./configure --prefix=${HOME}/local --enable-static LDFLAGS="-L${HOME}/local/lib" CFLAGS="-I${HOME}/local/include"
make
make install
```

# 参考リンク
* [tmuxをローカルインストールする時のメモ - Qiita](https://qiita.com/makisyu/items/d6b32d88cdd97b01a00f)
* [ローカルユーザーでtmuxをソースインストールする - よくわからないエンジニア](https://www.unknownengineer.net/entry/2016/09/06/170203)
新しいSlackチャンネルに別チャンネルメンバーをまとめて招待する

追記：最近のUPDATEでチャンネルへの一斉追加ができるようになりましたので、予め追加するユーザリストを持っている場合は下のような対応は不要です。

# 何を作ったのか？

数百人以上が入っているチャンネルを目的別に分けたかった。元のチャンネルのメンバーを新しいチャンネルにまとめて招待したい。
→手作業無理、/inviteコマンドも1ユーザごとしか使えないのでAPIで実装します。

# できたやつ

## APP(token)の作成

以下スコープを許可したAPPを作成、token(xoxp-XXXX)を習得しましょう。詳細割愛。

```
channels:read  #パブリックチャンネルに所属しているメンバー一覧を取得するため
channels:write #パブリックチャンネルにインバイトするため
groups:read    #プライベートチャンネルに所属しているメンバー一覧を取得するため
groups:write   #プライベートチャネルにインバイトするため
```

## 実行

以下XXXXXXにパラメータを入れ込んで実行しましょう。

※ channel_idとはチャンネル名ではなくブラウザで見た時のURLのCXXXXXの部分です。
※ limitはコピー元のユーザ数より大きい値で設定ください。

```sh
#!/bin/sh
slacktoken=XXXXXXXXX # set your token
channel_copy_from=XXXXXXXXX # set copy channel_id
channel_copy_to=XXXXXXXXX # set invite channel_id 
limit=700 # set number of members
members=`curl -H 'Content-Type: application/x-www-form-urlencoded' "https://slack.com/api/conversations.members" -d "token=$slacktoken&channel=$channel_copy_from&limit=$limit" | jq -r '.members[]'`
for u in $members; do
  echo Inviting user $u;
  curl -X POST -H 'Content-Type: application/x-www-form-urlencoded' -i "https://slack.com/api/conversations.invite" -d "token=$slacktoken&channel=$channel_copy_to&users=$u";
  sleep 2;
done;
```

# 参考にさせていただいたサイト

[Invite multiple members to slack channel(slackで複数人まとめてinviteする)](https://qiita.com/yu1ro/items/a3d481f6586d92a23311)
【備忘録】Qiita投稿ガイド

# 背景
ガチ理系もすなる機械学習といふものを、エセ理系もしてみむとてするなり。
ということで、真面目に勉強することにしました。自分の学習の整理と、モチベーションの維持を目的として、Qiitaに記事を書いていきます。
記法に悩んだときの参照先として、過去の偉人たちの知恵を蓄積してきます。

# tips
+ [Markdown記法 チートシート](https://qiita.com/Qiita/items/c686397e4a0f4f11683d)
+ [Qiitaの数式チートシート](https://qiita.com/PlanetMeron/items/63ac58898541cbe81ada)

以上、そのほかは随時更新予定。
PHPのtry-catchで拡張エラーを捕まえたい時の注意点

割と当然だと思ってましたが、社内の若手さんが知らなかったので何かの役に立つかと思いメモ。

######try-catchで、エラーごとに処理を分けたい時
あると思うんですよ。そんなときに、catchする順番が重要だったりします。具体的には、

```php5
class MyException extends Exception{}

try{
  throw new MyException('hoge');
}catch(Exception $e){
  var_dump('Exception');
}catch(MyException $e){
  var_dump('MyException');
}
```
こういうコードですね。（言うまでもないですがvar_dumpの部分には本来ログ取ったり処理分けたりしたいコードが入ります）

で、この場合、トップレベルでExceptionを捕まえてしまうと、MyExceptionはExceptionを継承してるから、`catch(MyException)`の所で捕まえられません。（必ず`catch(Exception $e)`のブロックに入ってしまうため。）
拡張エラーを使って処理を分けたい場合、継承元のエラーは必ず最後に書かないと駄目、という当たり前の話でした。

######ちなみに
Rubyも同じです。

```ruby
class ExceptionTest
  def test
    begin
      1/0
    rescue StandardError => ex
      puts "StandardError"
    rescue ZeroDivisionError => ex
      puts "ZeroDivisionError"
    end
  end
end
obj = ExceptionTest.new
obj.test # => StandardError
```

エラーのクラス毎に処理分けたい場合、トップで継承元のエラーを捕まえてはいけない、という話でした。
Re:VIEW 3.1へのアップグレード

## Re:VIEW 3.0から3.1へアップグレードした際の作業メモ

### 環境

* Mac OS High Sierra(10.13.6)
* TexLive 2018
* Ruby 2.3.7p456

### 作業手順

1. sudo gem update review
2. 適当なフォルダを作ってテスト
  1. review init reviewtest
  2. cd review
  3. rake pdf でtexのエラー
  4. texの見付からないパッケージをTexLiveUtilityでインストール
     1. everypage.sty が見つからない > everypageパッケージ
     1. suffix.sty が見つからない > bigfootパッケージ
     1. framed.sty が見つからない > framedパッケージ
     1. warpfig.sty が見つからない > warpfigパッケージ
  5. 再度 rake pdf してPDFが作成されることを確認
3. 実際の原稿をコンパイル
    4. 表紙画像が使用されない
    5. 埋め込み画像のサイズが正しく設定されない
    6. 表のセル内にフリガナを振った漢字があるとパディングが小さいせいで罫線に被る
もう npm ls でイラッとしない

みなさんは `npm ls` で依存モジュールがずらずらと表示されて、イラッとしたことはありませんか。私はあります。

```
npm ls --depth=0
```

とすれば、トップレベルのモジュールのみに制限できますが、いちいち `--depth=0` をつけるのは面倒です。

そこで、次のコマンドを実行してみましょう。

```
npm config set depth 0
```

これ以降、`--depth=0` をつけなくても、`npm ls` でトップレベルのモジュールのみが表示されるようになります。設定は `~/.npmrc` に保存されます。

依存ツリーが見たい場合は、

```
npm ls --depth=9999
```

のように、`depth` を適当に大きな値で上書きすれば OK です。


# 参考

- https://gist.github.com/yyx990803/6045243#gistcomment-2580857
- https://docs.npmjs.com/cli/config
Serverless FrameworkでIAMのマネージドポリシーを利用する

LambdaからSSMでRun Commandしたり、CloudFormationのスタックを触ったりしようとすると、IAMのポリシーで結構詰まります（オレ調べ）

Serverless Frameworkのドキュメントを見ると、マネージドポリシーを定義する方法が記載されていました。

```yaml
service:
  name: ssm-command

provider:
  name: aws
  runtime: nodejs8.10
  region: us-west-2
  stage: development
  iamManagedPolicies:
    - 'arn:aws:iam::aws:policy/AmazonSSMFullAccess'
    - 'arn:aws:iam::aws:policy/CloudWatchLogsFullAccess'
```

こっちで事足りるものはこいつをつかった方が平和かもしれません。
FuelPHPを静的Webサイトと共存させたい場合の.htaccessの書き方

######フレームワークと静的Webサイトを共存させたい時
FuelPHPでpublic以下に静的サイトを共存させたい場合、apacheでVirtualHost設定して、ホスト名毎にDocumentRoot設定すれば分けることができます。
######例えば
こんな構成でWebサイト作るイメージです。
public/　←`http://hogehostのDocumentRoot`
　├ assets/
　├ static_site1/　←`http://mogehostのDocumentRoot`
　├ static_site2/　←`http://sogehostのDocumentRoot`
　├ .htaccess
　├ favicon.ico
　└ index.php

だけどこういう構成の場合、static_site1,2ディレクトリに、リクエストされたファイルやディレクトリがなかった場合、Fuelのリクエストとして処理されてしまい、結果として（コントローラーへのアクセスとして処理されて）500エラーとかが返ってきてしまって良くないです。
これはpublicの.htaccessで
`RewriteCond %{REQUEST_FILENAME} !-f`
`RewriteCond %{REQUEST_FILENAME} !-d`
というRewriteCondが設定されていて、実際にファイルやディレクトリがなかった時に、index.phpのパラメータとして処理される様になっているためです。そこで、特定のHost以外はFuel側で処理してほしくない場合、
`RewriteCond %{REQUEST_FILENAME} !-f`
`RewriteCond %{REQUEST_FILENAME} !-d`
`RewriteCond %{HTTP_HOST} ^hogehost$ [NC]`
とか書けば、hogehostでアクセス来た時のみindex.phpのパラメータとして処理され、mogehost、sogehostに無いファイルがリクエストされた時に404を返してくれる様になります。
######だけどそもそも
fuelのディレクトリと同階層にstatic_siteのディレクトリ置けばこんなに苦労する必要ないので、使い所は限られると思います。あんまり記事書く必要ないなこれ。
TomcatとGlassFishって結局どっちを使うのがいーの？

# はじめに 
はじめまして。ykusaです。
今日はTomcatとGlassFishの使い分けについて説明します。

JavaでWebアプリの開発をしたことがある方ならTomcatは使ったことがあるかと思います。一方で、美味しそうなお魚マークのGlassFishは使ったことがありますか？
どちらもWebアプリを運用できるという点では同じことができますが、それぞれに向き不向きがあります。

機能の○×を付けるだけであれば、その辺に落ちている表を見ればいいのですが、運用やサポートも含めた比較内容はなかなかまとまっていません。
実際にどちらを使うの？となった際に、機能の実現性の有無だけでなく、運用やサポート、それに加えて将来的な展望なども知っておくと、選定の際に役立ちます。

どっちを使うのがいい？となった際に、説明できるとかっこいいですよ！

# 基本情報
まずは、Tomcat、GlassFishについて、基本情報を簡単に整理します。

## Tomcatとは
JSPやSevletを実行するWebコンテナ(Webアプリを動かす環境のこと)です。Apacheのコミュニティ内で開発がされています。
最新版はTomcat9.0.19で開発が継続されています。(2019年4月13日時点)

## GlassFishとは
JavaEEの参照実装です。JavaEEの詳細については、他のサイトを見てほしいのですが、簡単にいうとJavaフレームワークの集合体でJavaEEの一部にJSP、Servletが含まれます。そのため、Tomcat同様のWebコンテナとしての機能も持っています。
紆余曲折あったのですが、今はEclipse Foundation内のコミュニティで開発がされており、最新版はEclipse GlassFish 5.1になります。(2019年1月末時点)

<h2>TomcatとGlassFishの違い</h2>

## 機能面

|機能|Tomcat|GlassFish|
|----|------|---------|
|Webコンテナ(JSP、Servlet)|○|○|
|EJBコンテナ|×|○|
|フレームワーク(JSF、JPA、CDIなど)|×|○|
|アプリケーションの管理|×|○|
|リソースの管理|×|○|
基本的に、TomcatはWebコンテナを動作させるための必要最低限の機能になります。一方でGlassFishは大規模な商用システムの運用も考慮し、アプリやリソースの管理機能もついています。 

## サポート面
ここでいうサポートは、電話やメールでの対応などではなく、開発が継続している(新機能の追加、セキュリティパッチの提供、障害修正の公開などが行われる)ことを指します。
サポート中であれば、障害を見つけた際にコミュニティに連絡をすれば、修正してもらえる可能性があります。
Tomcat、GlassFishで以下のような特長があります。

### Tomcat
以下のような特長があります。

* 1バージョン当たりのサポート期間が長い(一例として、Tomcat7系は2011年に安定板がリリースされた後、2019年4月時点でもサポート継続中)
* 数多くのITベンダーが、QAサービスを提供(「Tomcat 電話 サポート」で検索すると結構出てきます)

### GlassFish
以下のような特長があります。

* コミュニティのスポンサーにIBM、Oracleなどが名を連ねており、安心感がある
* GlassFishをベースにした商用製品があり、それを使うとベンダーからサポートを受けられる

# まとめ
個人的には、GlassFishの方が上述したように機能が充実しているのでおススメです。
そうはいっても、Tomcatで事足りていると、なかなか移行する決断は難しいとも思います。
私の周りでTomcatからGlassFishへの移行を決断する際には、以下の様な点が契機になることが多いですので、ご参考まで。

* 画面系のフレームワークに何を使おうか(Spring等)と悩んでいる
(GlassFishのJSFを利用する)
* O/Rマッピングのライブラリを持ってきて使おうとしている
(GlassFishのJPAを利用する)
* 運用管理のGUI画面を使いたい

最終的には、全体のコストも考えて、ご自身で選択ください。
間違っているところ、指摘などはコメントいただけると嬉しいです。なお、記事情報は2019年4月時点のものとなります。
CentOS7.6へVim8.1をインストール

![06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258841/a7d6fca4-7de0-814d-402b-337daf445521.png)

### 背景

プラグインが動かない！なんで！vimが古い！！
ってなって最新版を入れようと思って色々調べた際のメモ

### 環境

``` bash
cat /etc/redhat-release ; uname -r
CentOS Linux release 7.6.1810 (Core)
3.10.0-957.el7.x86_64
```

### インストール手順 (前準備)

ビルドにはいろいろと必要なので入れてきます。

#### gcc 他

``` bash
$ yum install -y gcc lua-devel ncurses-devel
```

#### ruby (1.6.0以降)

``` bash
$ yum install ruby-devel

# GitHubからとってくる
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv

# パスを通す
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile

# 初期化
$ ~/.rbenv/bin/rbenv init

# 読み込み
$ bash

# MakeFile作成時に使うのでメモしておきます
/usr/bin/ruby
```

ないと怒られたり

``` bash
checking --with-ruby-command argument... defaulting to ruby
checking for ruby... no
configure: error: could not configure Ruby

# または
checking for /usr/bin/ruby... /usr/bin/ruby
checking Ruby version... OK
checking Ruby rbconfig... RbConfig
checking Ruby header files... not found; disabling Ruby
configure: error: could not configure Ruby
```

古くても怒られます

``` bash
checking for /usr/bin/... /usr/bin/
checking Ruby version... too old; need Ruby version 1.6.0 or later
configure: error: could not configure Ruby
```

### インストール

```bash
$ git clone https://github.com/vim/vim.git

$ cd vim
# 特定のバージョンは下記のように指定できる
$ git checkout v7.4.393

$ ./configure --prefix=/usr/local --with-features=huge --enable-multibyte --enable-rubyinterp --enable-luainterp --enable-cscope --enable-fail-if-missing --with-ruby-command=/usr/bin/ruby

$ make && sudo make install

$ vim --version | head -n3
VIM - Vi IMproved 8.1 (2018 May 18, compiled Apr 25 2019 16:19:07)
適用済パッチ: 1-1201
Compiled by root@localhos
```

### 参考サイト

[vim-jp » Vimのユーザーと開発者を結ぶコミュニティサイト](https://vim-jp.org/docs/build_linux.html)
react で作ったアプリケーションのiOS12.1 svg imageが表示されないとき

iOS12.2 と iOS12.1 の間でsvg image タグの挙動が変わっているので対応するために href と xlinkHrefの双方書かないと出なかったのでメモ。

結論から言うと  (react jsx 内にインラインで書く様な時）

```
<div>
        <svg
            xmlns="http://www.w3.org/2000/svg"
            width={なんか}
            height={なんか}
            viewBox={`0 0 ${なんか} ${なんかt}`}>
              <image
                xlinkHref={画像URL}　　　　<<<<<<<<<<<
                href={画像URL}            <<<<<<<<<<<
                height={なんか} width={なんか}
                x={なんか}
                y={なんか}
            />
         </svg>
</div>
```

のように xlinkHref と href を双方記入すると画像が表示されます。 

- iOS12.2は hrefだけで表示します。
- iOS12.1は xlinkHrefも書いてやらないと httpリクエストすら出しません。
- href , xlinkHrefの双方書いていても害は無いようです。