OmniAICreator/Qiita-1.07M huggingface dataset:
【Kotlin】List<Before> を List<After> に変換する関数を一度だけ使って List<Before?> を List<After?> に変換する

## 問題

`List<Before?>` 型の値を `List<After?>` 型に変換したい。
要素の値が `null` であれば `null` のまま、`Before` であれば `After` に変換する。

変換には、`List<Before>` 型の値を `List<After>` 型に変換する関数を
一度だけ[^一度だけ]使用したい。
この関数は `null` を受け付けないことに注意。

[^一度だけ]: この関数は呼び出しコストが大きい。

`Before` 型、`After` 型、および `List<Before>` 型の値を `List<After>` 型に変換する関数を、仮に次のようなものとする。

```Kotlin
data class Before(val value: String)
data class After(val value: String)

fun List<Before>.toAfterList(): List<After> =
    map { After(it.value) }
```

## 解答

次のような関数を用いることで変換できる。

```Kotlin
fun List<Before?>.toAfterOrNullList(): List<After?> {
    val beforeOrNullList = this
    val afterItr: Iterator<After> = beforeOrNullList
        .filterNotNull()
        .toAfterList()
        .iterator()
    return beforeOrNullList.map { beforeOrNull ->
        beforeOrNull?.run {
            afterItr.next()
        }
    }
}
```

### 動作確認

```Kotlin
fun main() {
    listOf(Before("A"), Before("B")).also {
        println("$it -> ${it.toAfterOrNullList()}")
        // ^ > [Before(value=A), Before(value=B)] -> [After(value=A), After(value=B)]
    }
    listOf(Before("A"), null).also {
        println("$it -> ${it.toAfterOrNullList()}")
        // ^ > [Before(value=A), null] -> [After(value=A), null]
    }
    listOf(null, Before("B")).also {
        println("$it -> ${it.toAfterOrNullList()}")
        // ^ > [null, Before(value=B)] -> [null, After(value=B)]
    }
    listOf(null, null).also {
        println("$it -> ${it.toAfterOrNullList()}")
        // ^ > [null, null] -> [null, null]
    }
}
```

## 発展

一般化して、
「`List<T>` を `List<R>` に変換する関数を用いて
　`List<T?>` を `List<R?>` に変換する関数」にする。

```Kotlin
inline fun <T, R> List<T?>.mapWithNull(
    crossinline transform: (List<T>) -> List<R>
): List<R?> {
    val tOrNullList = this
    val rItr: Iterator<R> = tOrNullList
        .filterNotNull()
        .let(transform)
        .iterator()
    return tOrNullList.map { tOrNull ->
        tOrNull?.run {
            rItr.next()
        }
    }
}
```

関数名は改善の余地がありそう。

### 動作確認

```Kotlin
fun main() {
    listOf(Before("A"), Before("B")).also {
        println("$it -> ${it.mapWithNull { it.toAfterList() }}")
        // ^ > [Before(value=A), Before(value=B)] -> [After(value=A), After(value=B)]
    }
    listOf(Before("A"), null).also {
        println("$it -> ${it.mapWithNull { it.toAfterList() }}")
        // ^ > [Before(value=A), null] -> [After(value=A), null]
    }
    listOf(null, Before("B")).also {
        println("$it -> ${it.mapWithNull { it.toAfterList() }}")
        // ^ > [null, Before(value=B)] -> [null, After(value=B)]
    }
    listOf(null, null).also {
        println("$it -> ${it.mapWithNull { it.toAfterList() }}")
        // ^ > [null, null] -> [null, null]
    }
}
```

／以上
Rustのファイルパスの扱いが複雑すぎる件

Rustでファイル一覧を得ようとしたら、かなり複雑でこんがらがったので整理してみる。

# ファイル一覧を得る

ファイル一覧を得るには、[std::fs::read_dir](https://doc.rust-lang.org/std/fs/fn.read_dir.html)を使う。

```read_dir.rs
let dirname = "./hoge";
let files = fs::read_dir(dirname).unwrap();
for entry in files {
    let path = dir_entry.unwrap().path();
    println!("{:?}", path);
}
```

ただし、forで反復する要素は、Result<DirEntry>となる。そこで、unwrapしてpathを得ることで、いろいろな操作が可能。

- [std::fs::DirEntry](https://doc.rust-lang.org/std/fs/struct.DirEntry.html)


# 全ファイル列挙

サブフォルダも含めて列挙するには ... PathBufを得て、`is_dir()`などで再帰的に呼び出せば良さそう。

```read_all_dir.rs
use std::path;

fn main() {
  let traget = path:PathBuf::from("./");
  enumfiles(&target);
}

fn enumfiles(target: &path::PathBuf) {
    // ファイル一覧を取得
    let files = target.read_dir().unwrap();
    for dir_entry in files {
        // PathBufを得る
        let path = dir_entry.unwrap().path();
        // ディレクトリか？
        if path.is_dir() {
            // 再帰的に検索
            enumfiles(&path, keyword);
            continue;
        }
        // ファイル名を表示
        println!("{}", path.to_str().unwrap());
    }
}
```

こんな感じかな？

さらに詳しく調べるには、metadata()などを呼び出して、各種パラメータを得よう。

# ファイル名を文字列に変換する

ただし、ファイル名などを取得する場合、OSStringを返してくる。これをRustのStringに変換する際、UTF-8チェックが行われる。もし不正なUnicodeがあると、そこでエラーが出る。

```
for dir_entry in target.read_dir()? {
  let path = dir_entry().unwrap().path();
  let fname = path.file_name().unwrap()
    .to_str().unwrap();
}
```

そこで、[to_string_lossy](https://doc.rust-lang.org/std/ffi/struct.OsStr.html#method.to_string_lossy)を使うことができる。すると正しいUnicodeを得ることができる。

```
for dir_entry in target.read_dir()? {
  let path = dir_entry().unwrap().path();
  let fname = path.file_name().unwrap()
    .to_string_lossy();
}
```

## DirEntryの主要なメソッド

 - path(&self) -> [PathBuf](https://doc.rust-lang.org/std/path/struct.PathBuf.html)
  - 以下の `PathBuf` を得る
 - file_name(&self) -> OsString
 - file_type(&self) -> Result<FileType>
   - FileTypeにはis_file/is_dirのメソッドがある 

# PathBufの主要なメソッド
 
[std::path::PathBuf](https://doc.rust-lang.org/std/path/struct.PathBuf.html)

 - ファイル名や拡張子を得る
  - file_name(&self) -> Option<&[OSStr](https://doc.rust-lang.org/std/ffi/struct.OsStr.html)>
  - extension(&self) -> Option<&OSStr>
 - 他の型に変換
  - as_os_str(&self) -> &OsStr
  - to_str(&self) -> Option<&str>
  - as_path(&self) -> &Path
  - display(&self) -> Display<'_>
 - 各種情報を得る
  - metadata(&self) -> Result<Metadata>
   - 日時やディレクトリなどのメタデータを得る
  - read_dir(&self) -> Result<ReadDir>
  - parent(&self) -> Option<&Path>
 - 存在確認など
  - exists(&self) -> bool
  - is_file(&self) -> bool
  - is_dir(&self) -> bool

## Metadataの主なメソッド

[std::fs:Metadata](https://doc.rust-lang.org/std/fs/struct.Metadata.html)

 - permissions(&self) -> Permissions
 - is_dir(&self) -> bool
 - file_type(&self) -> FileType
 - is_file(&self) -> bool
 - modified(&self) -> Result< SystemTime >
 - accessed(&self) -> Result< SystemTime >
 - created(&self) -> Result< SystemTime >
rails g　carrierwave_backgrounder：installが実行できない

# エラーの内容

carrierwave_backgrounderを導入しようとした際に　rails gcarrierwave_backgrounder：install　が起動しないエラーが発生しました。その解決方法を試したのでアップします。

以下エラーの内容

```ruby
#ターミナル
$ rails g carrierwave_backgrounder:install

Running via Spring preloader in process 51882
Could not find generator 'carrierwave_backgrounder:install'. 
Run `rails generate --help` for more options.
```

## 解決方法

解決策1：スプリングを止める

```ruby
$ bundle exec spring stop
```

解決策2：gemを書き換える

```ruby
#gemfile
gem 'carrierwave', '~> 0.5'
gem 'carrierwave_backgrounder', :git => 'https://github.com/lardawge/carrierwave_backgrounder.git'

$ bundle install
```

多くの場合は解決策１でうまくいくことが多いです。

スプリングを止めても上記エラーが解決されない場合は、carrierwave_backgrounderのリポジトリをソースとしてGemfileに記述することを試してみてください。
私はうまくいきました。

参考：

[https://github.com/lardawge/carrierwave_backgrounder/issues/247](https://github.com/lardawge/carrierwave_backgrounder/issues/247)
OpenFOAM 9 のインストール

# はじめに
2021年7月20日にOpenFOAM Foundationより、OpenFOAM 9 がリリースされました。
ここでは[OpenFOAM Foundationのホームページ](https://openfoam.org/download/9-ubuntu/)の内容に従って、OpenFOAM 9 のインストール手順を記載します。
# ubuntuのバージョン
OpenFOAM 9 は、openfoam9 パックにより提供された OpenFOAM の新しいメジャーリリースです。これには paraviewopenfoam56 パックにより提供された公式な OpenFOAM のリーダモジュールでコンパイルされた、_ParaView 5.6.3_ が同梱されています。これらのパックは次の Ubuntu のバージョンで利用可能です。
- 18.04 LTS, コード名称 bionic
- 20.04 LTS, コード名称 focal
- 21.10, コード名称 hirsute
本稿では、20.04.2 LTS を使用します。
# インストール
OpenFOAM および _ParaView_ は、apt パッケージ管理ツールを使用してインストールされます。sudo を伴う次のコマンドを実行するとき、ユーザは特権ユーザのパスワード認証を行う必要があります。<br>
1. dl.openfoam.org を apt の検索のためのソフトウェアリポジトリのリストに追加し、パッケージの署名の検証を有効にするリポジトリ向けの公開鍵 (gpg.key) を追加するために、端末プロンプトへ次のコマンドをコピー＆ペーストします。
`sudo sh -c "wget -O - https://dl.openfoam.org/gpg.key | apt-key add -“`
`sudo add-apt-repository http://dl.openfoam.org/ubuntu`
__注意1：これは与えられたシステムに対して1回だけ行います。__
注意2：セキュアな転送を行うために、公開鍵に対してセキュアな https:// を使用しますが、リポジトリに対しては http:// を使用します。これは、https:// がサポートされていない可能性があり、鍵がパッケージファイルのセキュア認証を提供するので、https:// を使用する必要がないためです。<br>
2. 新しいダウンロードリポジトリの場所を考慮して、apt パッケージリストを更新します。
`sudo apt update`
アップグレードできるパッケージがある場合はアップグレードします。
`sudo apt upgrade`<br>
3. OpenFOAM 9 をインストールします。 
`sudo apt install openfoam9`
OpenFOAM 9 および _ParaView 5.6.3_ が /opt ディレクトリにインストールされます。
# ユーザ構成
インストールされた OpenFOAM パッケージを使用するために、次の操作を行います。<br>
1. ユーザのホームディレクトリにある .bashrc ファイルをエディタで開きます。
`gedit ~/.bashrc`<br>
2. .bashrc ファイルの最後に次の行を追加してファイルを保存します。
`alias of9='source /opt/openfoam9/etc/bashrc’`
`of9`<br>
3. 新しい端末ウィンドウを開き、OpenFOAM パッケージの _simpleFoam_ アプリケーションが機能していることを入力してテストします。
`simpleFoam -help`<br>
4. “Usage” メッセージが表示されれば、インストールおよびユーザ構成は完了です。
# 入門
_userName_-9 という名前のディレクトリを $HOME/OpenFOAM 内に作成し、そのディレクトリ内に run という名前のディレクトリを作成します。
`mkdir -p $FOAM_RUN`
バックステップ流れのチュートリアルケースをコピーして、_blockMesh_ でメッシュを生成し、非圧縮性流れのソルバ _simpleFoam_ で定常流の計算を行います。最後に _paraFoam_ で _ParaView 5.6.3_ を起動して計算結果を確認します。
`run`
`cp -r $FOAM_TUTORIALS/incompressible/simpleFoam/pitzDaily .`
`cd pitzDaily`
`blockMesh`
`simpleFoam`
`paraFoam`

_ParaView 5.6.3_ で表示した計算開始時の流速 _U_
![U_start.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1784513/767a41a5-2281-548c-8d66-3144bd50bb69.png)

_ParaView 5.6.3_ で表示した計算終了時の流速 _U_
![U_end.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1784513/23c44699-0ea3-5c11-cdf6-a0ae4747ea85.png)
Google Maps API利用方法 (静的マップ、埋め込みマップ)

## コンソールにログイン

* https://console.cloud.google.com/google/maps-apis/new?hl=ja

### APIの有効化

![aaa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25728/e67d8355-8b3f-b7ec-a3b0-f4a30fd4f2f2.png)

APIを利用するには「有効にする」をクリック

![enable.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25728/0c6fd426-76e3-643c-1780-7e65f144c30f.png)


### APIキーの生成

![ffff.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25728/30a0447f-d1cf-a20e-f198-65e1d78e3f61.png)


### APIのサンプル (Maps Static APIの例)

おやきの店うしこしのマップ

key=のところに、先ほど取得したAPIキーを指定する。

* https://maps.googleapis.com/maps/api/staticmap?center=36.30403066476505,%20137.93165161571605&zoom=13&size=600x300&maptype=roadmap&markers=color:blue%7Clabel:S%7C36.30403066476505,%20137.93165161571605&markers=color:green%7Clabel:G%7C40.711614,-74.012318&markers=color:red%7Clabel:C%7C40.718217,-73.998284&key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

例

![mm.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25728/f62bcada-0e93-9eb3-31fe-84557429a9c4.png)

* [公式ドキュメント](https://developers.google.com/maps/documentation/maps-static/overview?hl=ja)

## 埋め込みマップの場合 (iframe)

```html
<iframe
  width="600"
  height="450"
  style="border:0"
  loading="lazy"
  allowfullscreen
  src="https://www.google.com/maps/embed/v1/place?key=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    &q=36.30403066476505, 137.93165161571605">
</iframe>
```

* [公式ドキュメント](https://developers.google.com/maps/documentation/embed/get-started?hl=ja)


## Googleマップ v2からv3にアップグレード方法

https://developers.google.com/maps/documentation/javascript/v2tov3

http://kikuchisan.net/gmap/gmapv3.html
JavaのSSLSocketを使う（クライアント編２）

# はじめに
次はクライアント証明書を使ってみる。ついでにオレオレのサーバ証明書に対応する。

# 概要
[サーバの時](https://qiita.com/jkomatsu/items/d65ccbc6f8e7d73a9972)と同様に keystore, truststore をそれぞれ指定して使用する。
keystore にはクライアント証明書（秘密鍵含む）、truststore にはクライアント証明書のCA局証明書、オレオレサーバ証明書のCA局証明書を入れておく。

クライアント証明書のCA局証明書は、サーバ側のtruststoreに読み込んだものと同じ。

# SSLContext
手順としては標準のtruststoreを使う場合、[クライアント編1](https://qiita.com/jkomatsu/items/40f929894e66b9739a28)と同じく SSLContext を取得、SSLSocketFactory を得て Socket を作る。

というわけで SSLContext と SSLSocketFactory を作る部分はこのようになる。大体サーバ側と同じ。

```java
package sslclient;

import java.security.KeyStore;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
import java.net.Socket;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.io.InputStream;

public class App
{
    private static SSLContext secureSocketContext;
    private static SSLSocketFactory secureSocketFactory;

    private static void initSSL()
    {
        try (
            FileInputStream p12_file = new FileInputStream("clientcert.p12");
            FileInputStream jks_file = new FileInputStream("mytruststore.jks");
        ) {
            char[] passphrase = "passphrase".toCharArray();

            KeyStore ks = KeyStore.getInstance("pkcs12");
            ks.load(p12_file, passphrase);
            KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
            kmf.init(ks, passphrase);

            KeyStore ts = KeyStore.getInstance("JKS");
            ts.load(jks_file, passphrase);
            TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
            tmf.init(ts);

            secureSocketContext = SSLContext.getInstance("TLS");
            secureSocketContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
            secureSocketFactory = secureSocketContext.getSocketFactory();
        }
        catch (Exception e) {
            System.out.println("initSSL exception: " + e.toString());
        }
    }
```

clientcert.p12 がクライアント証明書のファイル名、mytruststore.jks がクライアント証明書・サーバ証明書のCA局証明書が入っているものになる。
passphrase はそのパスワード。


# SSLSocket
あとはSocketを作って通信できる。以下の例では localhost:8443 に接続する。
通信するだけであれば、特にSSLSocketとして扱う必要はなく、ただのSocketで問題ない。
SSLSocketの機能を使う場合は SSLSocket にキャストして使う。

```java
try {
    Socket sock = secureSocketFactory.createSocket("localhost", 8443);
    OutputStream os = sock.getOutputStream();
    InputStream is = sock.getInputStream();
          .
          .
          .
}
catch (Exception e) {
    System.out.println("exception: " + e.toString());
}
```

# TLSバージョンについて
[サーバの時](https://qiita.com/jkomatsu/items/d65ccbc6f8e7d73a9972)と同様に、SSLSocketにもgetEnabledProtocols, setEnabledProtocolsがある。
Cipher Suites も同様。
AtCoderログ：0028 - ARC 123 に参加しました

この記事は 2021年07月18日(日)21:00-23:00 に開催された [ARC 123](https://atcoder.jp/contests/arc123) に参加した記録です。

#  結果
- 得点：300 (=300)
- 時間：24:42 (= 14:42 + 05:00 × 2)
- 順位：2640 / 3527
- パフォーマンス：375
- レーティング：154 → 182 (+28) Highest更新！
- 段級位：10級

![2021-07-18_arc123.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1589302/e46fd9db-d5ca-95cf-b0c5-69b7fbbe17e1.png)

# 詳細
|問題|得点|時間|提出コード|復習|
|:--:|:--:|:--:|:--:|:--:|
|[ARC 123 A](https://atcoder.jp/contests/arc123/tasks/arc123_a)|300|14:42 (2)|[○](https://atcoder.jp/contests/arc123/submissions/24356898)|[AtCoderログ：00**]()|
|[ARC 123 B](https://atcoder.jp/contests/arc123/tasks/arc123_b)|400|---|---|---|
|[ARC 123 C](https://atcoder.jp/contests/arc123/tasks/arc123_c)|600|---|---|---|
|[ARC 123 D](https://atcoder.jp/contests/arc123/tasks/arc123_d)|700|---|---|---|
|[ARC 123 E](https://atcoder.jp/contests/arc123/tasks/arc123_e)|700|---|---|---|
|[ARC 123 F](https://atcoder.jp/contests/arc123/tasks/arc123_f)|1000|---|---|---|

# 感想
- ARC に初参戦しました。それなりの時間で C を解けたものの、D 以降に歯が立たず、時間が余りました
- A は整数の大きさの見積もりを誤り、回答に手間取ってしまいました
- レーティングが伸びたのは良かったですが、私には ARC はまだ早そうです。

# リンク
- 前の記事 → [AtCoderログ：0027 - ABC 113 B](https://qiita.com/RoadLynton/items/d2f8b3e407cc4fbcd652)
- 次の記事 → [AtCoderログ：0029 - ABC 072 B](https://qiita.com/RoadLynton/items/17c771f99bb880c04acc)
AtCoderログ：0027 - ABC 113 B

# 問題：[ABC 113 B - Palace](https://atcoder.jp/contests/abc113/tasks/abc113_b) 

## 問題文
> ある国で、宮殿を作ることになりました。
> この国では、標高が $x$ メートルの地点での平均気温は $T−x \times 0.006$ 度です。
> 宮殿を建設する地点の候補は $N$ 個あり、地点 $i$ の標高は $H_i$ メートルです。
> joisinoお姫様は、これらの中から平均気温が $A$ 度に最も近い地点を選んで宮殿を建設するようにあなたに命じました。
> 宮殿を建設すべき地点の番号を出力してください。
> ただし、解は一意に定まることが保証されます。

## 制約
> ・$1 \le N \le 1000$
> ・$0 \le T \le 50$
> ・$-60 \le A \le T$
> ・$0 \le H_i \le 10^5$
> ・入力は全て整数
> ・解は一意に定まる

## 収録されている問題セット
- [AtCoder に登録したら次にやること](https://qiita.com/drken/items/fd4e5e3630d0f5859067) ([第03問類題](https://qiita.com/drken/items/fd4e5e3630d0f5859067#%E7%AC%AC-3-%E5%95%8F--abc-081-b---shift-only-200-%E7%82%B9))

# 回答1 (AC)
各地点の標高に対して平均気温を計算し、平均気温と a との差の絶対値の最小値を求めれば良いです。コードは以下のようになりました。

```C++:abc113b-1.cpp
#include <bits/stdc++.h>
using namespace std;
 
int main() {
  int n, t, a;
  cin >> n;
  cin >> t >> a;
 
  int h, answer;
  float tempature, dmin = FLT_MAX;
  for ( int i=0; i<n; i++ ) {
    cin >> h;
    tempature = t-0.006*h;
    if ( abs(a-tempature)<dmin ) {
      dmin = abs(a-tempature);
      answer = i;
    }
  }
 
  cout << answer+1 << endl;
}
```

# 回答2 (AC)
回答1では各地点の標高から平均気温を計算しているため、$N$ 回の計算が必要になります。目標とする平均気温は $a$ なので、この平均気温になる標高を求め、この標高との差が最小となるような地点を求めることで、計算回数を 1 回に削減することができます。コードは以下のようになりました。

```C++:abc113b-2.cpp
#include <bits/stdc++.h>
using namespace std;
 
int main() {
  int n, t, a;
  cin >> n;
  cin >> t >> a;
  float x = (t-a)/0.006;

  int h, answer;
  float dmin = FLT_MAX;
  for ( int i=0; i<n; i++ ) {
    cin >> h;
    if ( abs(x-h)<dmin ) {
      dmin = abs(x-h);
      answer = i;
    }
  }

  cout << answer+1 << endl;
}
```

このコードを実行させたところ、実行時間もメモリも回答1とほとんど変わりませんでした。それくらい標高から平均気温を求める計算は軽い計算だったということでしょう。


# 調べたこと
## [AtCoder の解説](https://atcoder.jp/contests/abc113/tasks/abc113_b/editorial) → [ユーザ解説](https://blog.hamayanhamayan.com/entry/2018/11/07/190428)
回答1と同じ方針でした。浮動小数を使う場合には float 形よりも double 型が良いとのことです。

## [AtCoder の解説](https://atcoder.jp/contests/abc113/tasks/abc113_b/editorial) → [コンテスト全体の解説](https://img.atcoder.jp/abc113/editorial.pdf)
回答1と同じ方針でしたが、「気温は整数ではありませんが、気温を 1000 倍して計算すると浮動小数点型を用いなくても答えを求めることができます」は参考になる工夫だと思いました。

# リンク
- 前の記事 → [AtCoderログ：0026 - ちょっとした方針変更](https://qiita.com/RoadLynton/items/eead9c38bb45997b6775)
- 次の記事 → [AtCoderログ：0028 - ARC 123 に参加しました](https://qiita.com/RoadLynton/items/094c4e1ec812aa839f6f)
スクレイピングして抽出したデータをCSVにいい感じにしたい

スクレイピングにハマっている無職です。
今回はスクレイピングをしてそこから読み取ったデータをCSVファイルでいい感じに表示させていきたいと思います

##今回やりたいこと

![スクリーンショット 2021-07-22 13.23.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764631/b1862e62-35b5-3cec-be5f-ff4fef1bae9a.png)

ここから本の題名を検索して、本のシリーズの題名、著者、出版日、Urlを抜き出してCSVファイルに移すと言うものをやっていきたいと思います。

今回の完成の動画はこちらです。

https://twitter.com/sskei11/status/1418067807736979459?s=20

完成したCSVファイル
![スクリーンショット 2021-07-22 13.39.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/764631/98d29399-ab82-7002-68a0-85bef1ee2aad.png)

参考にした記事

https://qiita.com/rosuke/items/ebe3fa26276b6c7dd0b4

https://self-development.info/

##今回のプログラムファイル

```python


import chromedriver_binary
import time
from selenium import webdriver
from selenium.webdriver.chrome.options import Options # オプションを使うために必要
import csv

option = Options()                          # オプションを用意
option.add_argument('--incognito')          # シークレットモードの設定を付与
driver = webdriver.Chrome(options=option)   # Chromeを準備(optionでシークレットモードにしている）
driver.get('https://books.google.co.jp')
time.sleep(2)
search_box = driver.find_element_by_name("q")
search_box.send_keys('葬送のフリーレン')
search_box.submit()
time.sleep(2)

i = 1               # ループ番号、ページ番号を定義
i_max = 2           # 最大何ページまで分析するかを定義
title_list = []     # タイトルを格納する空リストを用意
link_list = []      # URLを格納する空リストを用意
author_list = []
punish_list = []

while i <= i_max:
    # タイトルとリンクはclass="r"に入っている
    class_group = driver.find_elements_by_class_name('Yr5TG')
        # タイトルとリンクを抽出しリストに追加するforループ
    for elem in class_group:
        title_list.append(elem.find_element_by_class_name('LC20lb').text)           #タイトル(class="LC20lb")
        link_list.append(elem.find_element_by_tag_name('a').get_attribute('href'))  #リンク(aタグのhref属性)
        author_list.append(elem.find_element_by_class_name('fl').text) 
        punish_list.append(driver.find_element_by_xpath("//div[@class='N96wpd']/span").text) 


        # 「次へ」は1つしかないが、あえてelementsで複数検索。空のリストであれば最終ページの意味になる。
    if driver.find_elements_by_id('pnnext') == []:
        i = i_max + 1
    else:
            # 次ページのURLはid="pnnext"のhref属性
        next_page = driver.find_element_by_id('pnnext').get_attribute('href')
        driver.get(next_page)   # 次ページへ遷移する
        i = i + 1               # iを更新
        time.sleep(1.5)        


#画像のリンクをクリック
# element.click()

time.sleep(2)
driver.quit()

test = []

#listを入れ替えて二次元配列にする(見にくい版）
for i in range(len(title_list)):
    test2 = []
    test2.append(title_list[i])
    test2.append(link_list[i])
    test2.append(author_list[i])
    test2.append(punish_list[i])
    test.append(test2)


@StrawBerryMoon　さんがより、みやすく、わかりやすいコードを教えてくださいました！
#listを入れ替えて二次元配列にする
for i in range(len(title_list)):
    test.append([title_list[i], link_list[i], author_list[i], punish_list[i]])

rowtitle =["title","link","author","punish"]

#csvファイルを書き込む　utf_8_sigをつけることでエクセルで読子でも文字化けしない
with open('sample_writer_row.csv', 'w',encoding='utf_8_sig') as f:
    writer = csv.writer(f)
    writer.writerow(rowtitle)
    writer.writerows(test)
```

これでいい感じにスクレイピングしていい感じにCSVファイルを整えて出力できます。
楽しいスクレイピングライフを！
あなたの携帯電話用着メロをダウンロード

着メロは私たちの生活の一部であり続けているので、自分を表現する楽しい方法に変えてみませんか？あなたが毎日あなたの壁紙を変えるのが好きなら、着メロはあなたにとって理想的です。着メロは、更新するたびに携帯電話の外観を変えるユニークで簡単な方法です。お気に入りのコンテンツをすべての友達と共有したり、インターネットで最新のアプリを入手したり、を使って世界中のどこからでもお気に入りの音楽やテレビチャンネルにアクセスしたりできます。お気に入りのコンテンツをと共有したり、インターネットで最新のアプリを入手したり、世界中のどこからでもお気に入りの音楽やテレビチャンネルにアクセスしたりできます <a href="https://klingeltonekostenlos.de/">kostenlose klingeltöne</a>

を使用して、お気に入りの音楽ファイル、写真、ビデオ、オンラインゲーム、アプリ、さらにはテキストメッセージにアクセスできます。ローカルライブラリまたはインターネットからファイルを選択して、Androidデバイスに転送します。お気に入りの音楽ファイルをインポートするか、ポータブルメディアプレーヤーからインポートします。着メロは、更新されるたびに携帯電話の外観を一新します。

着メロはあなたの電話をパーソナライズするための最も簡単で最も楽しい方法です。動物や花からスポーツやSFまで、幅広いテーマで着メロをパーソナライズできます。着メロは、ビーチ、ジャングル、車、恐竜、おとぎ話、キッチン、愛、人生など、多くのテーマで利用できます。今すぐお使いの携帯電話用のまったく新しい着信音を入手してください！

着メロは、お気に入りのラジオ局からの着信音の無限の配列を提供することにより、素晴らしいエンターテイメントと楽しみを提供します。着メロは、MP3、WAVなどを含む多種多様なオーディオ形式を提供します。お気に入りのオーディオファイルを着メロにロードして、通勤中、自宅でリラックスしているとき、またはジムでエクササイズしているときに聞くことができます。着メロは、単純な音声発信者としての謙虚な始まりから長い道のりを歩んできました。

着メロをダウンロードしたい場合は、インターネットが最適です。あなたが何百もの無料の着メロと他のオーディオファイルを見つけることができる多くのウェブサイトがあります。ただし、注意してください。これらのサイトの中には、無料の着信音を提供すると主張するものもありますが、サイトに広告が掲載されている場合や、さらに悪いことに、コンピューターに損傷を与える可能性のあるスパイウェアやアドウェアが掲載されている場合があります。いずれかのWebサイトから着信音をダウンロードすると、そのWebサイトに、オーディオファイルを他の人に販売または配布する許可を永続的に与えることに注意してください。

着メロは、同じ古い着メロにうんざりしている人にとって完璧なソリューションです。着信音は、標準の着信音の代わりに使用したり、電話の追加のアクセントとして使用したりできます。着メロは使いやすく、それらの多くはビデオ、MP3、ラジオ再生機能などの優れた機能を備えています。あなたのライフスタイルに最適な着メロを探し、常に使用する素晴らしい着メロがたくさんあることを確認してください。あなたが才能を持っているなら、あなたはあなたの声優を強化するために着メロを使うことさえできます！
正多面体の双対性と共通値

正多面体の

- 面の角数を N
- 頂点に接する面の数を F
- 頂点数を V
- 面数を P

とすると

| | N | F | V | P |
|:-:|:-:|:-:|:-:|:-:|
| 正四面体 | 3 | 3 | 4 | 4 |
| 正六面体 | 4 | 3 | 6 | 8 |
| 正八面体 | 3 | 4 | 8 | 6 |
| 正十二面体 | 5 | 3 | 20 | 12 |
| 正二十面体 | 3 | 5 | 12 | 20 |

整理して、面が正三角形の正多面体と対になる正多面体の表にすると

| V | P | N=3 | F/N | F=3 | P | V |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| 4 | 4 | 正四面体 | 3 | 正四面体 | 4 | 4 |
| 6 | 8 | 正八面体 | 4 | 正六面体 | 6 | 8 |
| 12 | 20 | 正二十面体 | 5 | 正十二面体 | 12 | 20 |

になります。

半径1の球に接する正多面体のデータを

- 頂点座標（単位ベクトル相当）
- 法線ベクトル（単位ベクトル）

とすると、頂点座標と法線ベクトルを入れ替えると、対となる正多面体のデータになります。

対になる二つの正多面体は、頂点データと法線データを混ぜた一つの表のデータにできます。（表示したときの向きが好みのものにならない、という問題はあります）

「[正多面体のデータを作る](https://qiita.com/ikiuo/items/f5905c353858fc43e597)」を使って、中心から面までの距離（または頂点座標と法線ベクトルの内積） D を求めると

```math
D = \cot{ \frac{\pi}{F} } \cot{ \frac{\pi}{N} }
```

となって、N と F を入れ替えても同じ値になります。上の表に追加すると

| V | P | N=3 | F/N | F=3 | P | V | 　　　D　　　 |
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
| 4 | 4 | 正四面体 | 3 | 正四面体 | 4 | 4 | $\frac{1}{3}$ |
| 6 | 8 | 正八面体 | 4 | 正六面体 | 6 | 8 |  $\sqrt{\frac{1}{3}}$ |
| 12 | 20 | 正二十面体 | 5 | 正十二面体 | 12 | 20 |  $\sqrt{\frac{1}{3}\left(1+\frac{2}{\sqrt{5}}\right)}$ |

になります。正多面体の双対性には、ちゃんと共通値が出てくるんですね。

---

全ての正多面体のデータを一つのベクトル表に押し込めて縮められないか？という視点でイロイロ考えてみましたが、4,6,8面体と、12,20面体の二つのグループになってしまうので諦めました。
JavaのSSLSocketを使う（クライアント編１）

# はじめに
クライアント側の第１段階として、標準のtruststoreを使ってSSL通信する。

# 概要
サーバ側と同様にSSLContextのインスタンスを取得して、そこからSocketFactoryを得る。
SocketFactoryからSSLSocketが得られて、後は普通のSocketと同じように通信できる。

# 標準SSLContext
標準のtruststoreを使うSSLContextの取得は簡単で、getDefault() すれば良い。
コードとしては、これだけになる。

```java
import javax.net.ssl.SSLContext;

   .
   .
   .

SSLContext ctx = SSLContext.getDefault();
```

# SocketFactory
SSLContextが取得できればこちらも簡単。前述の ctx を使って getSocketFactory() すれば良い。

```java
import javax.net.ssl.SSLSocketFactory;

   .
   .
   .

SSLSocketFactory socketfactory = ctx.getSocketFactory();
```

# Socketの取得と通信
ここまでのところをまとめて、通信部分を追加してみる。
実行すると qiita.com のHTMLが出力される。


```java
package sslclient;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import java.net.Socket;
import java.io.OutputStream;
import java.io.InputStream;

public class App
{
    private static SSLContext secureSocketContext;
    private static SSLSocketFactory secureSocketFactory;

    private static void initSSL()
    {
        try {
            secureSocketContext = SSLContext.getDefault();
            secureSocketFactory = secureSocketContext.getSocketFactory();
        }
        catch (Exception e) {
            System.out.println("initSSL exception: " + e.toString());
        }
    }

    public static void main(String[] args)
    {
        initSSL();
        try {
            Socket sock = secureSocketFactory.createSocket("qiita.com", 443);
            OutputStream os = sock.getOutputStream();
            InputStream is = sock.getInputStream();
            String request_str = "GET / HTTP/1.1\r\nHost: qiita.com:443\r\n\r\n";
            os.write(request_str.getBytes());
            byte[] resp_buf = new byte[1024 * 1024 * 8];
            int resp_len = is.read(resp_buf);
            if (0 < resp_len) {
                String resp_str = new String(resp_buf, 0, resp_len);
                System.out.println(resp_str);
            }
        }
        catch (Exception e) {
            System.out.println("exception: " + e.toString());
        }
    }
}
```
OpenCVでギザギザブツブツの画像が表示される時の対処法

# 実現したいこと
空の画像に対して、マスクで特定の箇所の画素をsetToなどで変更したい

```c++
cv::Mat emptyImage;
emptyImage.create(originalImage.size(), CV_8UC1);

// some kinda operation
//  e.g., mask operation
// たとえばマスクで特定の箇所の色を変えるとか、なんでもいいですが
cv::Mat mask = hogehogehoge;
emptyImage.setTo(255, mask);

// 結果を確認するために出力
cv::imwrite("./bar/foo.png", emptyImage);


```

# 問題:ギザギザブツブツの画像が出力される
このようにすると、わけわかんないギザギザの意味不明な画像が出てきて困惑します。
なぜでしょう？

# 教訓：空の画像は明示的に画素を初期化しましょう

メモリ空間が確保されても、各画素値は初期化されてませんでした。
これは、更地を買ったからといって、農業がすぐにできないのと一緒ですね。
更地をまっさらに耕してから農業ができるのです。

## [方法1]operator::= で画素値を初期化する

**`cv::Mat::create`で空の画像を作るときは明示的に画素値を初期化しましょう。**


```c++
// 明示的に画素を初期化しましょう
image = 0;
```
これを元のやつでMatの空画像の宣言後にぶちこんでください。

```c++
cv::Mat emptyImage;
emptyImage.create(originalImage.size(), CV_8UC1);

// 明示的に画素を初期化しましょう
emptyImage = 0;

// some kinda operation
//  e.g., mask operation
// たとえばマスクで特定の箇所の色を変えるとか、なんでもいいですが
cv::Mat mask = hogehogehoge;
emptyImage.setTo(255, mask);

// 結果を確認するために出力
cv::imwrite("./bar/foo.png", emptyImage);
```

## [方法2] `cv::Mat::zeros` で初めからゼロ埋めしておく

人間は忘れがちな生き物なので、そもそも初めからゼロ埋めされた状態の画像を作成するのが安全です。

```c++
cv::Mat emptyImage = cv::Mat::zeros(originalImage.size(), CV_64FC1);
```
Redmineにプラグインを追加する。

#概要
AWS Lightsail上に構築したRedmineにPluginを追加します。
今回はチケットのテンプレートを作成できるPluginを追加します。

○プラグイン追加前
![スクリーンショット 2021-07-22 11.28.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/0a56a4a2-70f3-cf61-8cdb-b79efd0bcac4.png)

○プラグイン追加後
![スクリーンショット 2021-07-22 12.17.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/9c5e8561-0bce-8cd1-351f-2eb4f14d3fff.png)

##1.プラグイン追加
AWSの管理コンソールより、ブラウザからSSH接続する。
![スクリーンショット 2021-07-22 12.36.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/a645edeb-bca5-1c5d-f75c-62eb8a5c4e62.png)

```shell:rootにスイッチ
sudo su -
```

```shell:ディレクトリ移動。
cd /home/bitnami/apps/redmine/htdocs/plugins 
```

```shell:ダウンロード
git clone https://github.com/akiko-pusu/redmine_issue_templates.git
```

```shell:所有者変更
chown -R bitnami.daemon redmine_cut_tag
```

```shell:pluginをインストール
bundle exec rake redmine:plugins:migrate RAILS_ENV=production
```

```shell:再起動
systemctl restart httpd
※管理コンソールから再起動でも構いません。
```
Redmine再接続時に↓の画面が出力されていれば完了です。
![スクリーンショット 2021-07-22 12.17.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/d5ce1162-fb12-49e0-8093-7b4b35deb1c8.png)
元料理人でプログラミング初心者の僕がMacでPHPを始めるまで。

#Mac HomebrewでPHPをインストールする

#####1.ターミナルでHomebrewを最新の状態にする。

```
brew upgrade
```


#####2.実行してPHP 7.4をインストールする。

```
brew install php@7.4
```
#####3.PHPのバージョンを確認する。

```
 php -v
```

ここでバージョン確認したところこんな文が表示されました。

```
WARNING: PHP is not recommended
PHP is included in macOS for compatibility with legacy software.
Future versions of macOS will not include PHP.
PHP 7.3.24-(to be removed in future macOS) (cli) (built: Dec 21 2020 21:33:25) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.24, Copyright (c) 1998-2018 Zend Technologies
```
将来のアップグレードでPHPのサポートを完全に廃止する予定であるという非推奨の通知が出ました。
解決策は、Homebrewを使用してインストールし、7.4とバージョン8を含むPHPのバージョンを選択することだと思いました。

#####4.PHPの追加

```
brew tap shivammathur / php
```

#####5.PHPバージョンを選択　今回は7.4
```
brew install shivammathur / php / php@7.4
```
#####6.PHPバージョンとリンクする
```
brew link --overwrite --force php@7.4
```
#####7.一度ターミナルを再起動してバージョン確認
```
php -v
```
すると先程の警告文は消えました。

```
PHP 7.4.21 (cli) (built: Jul 12 2021 11:57:26) ( NTS )
Copyright (c) The PHP Group
Zend Engine v3.4.0, Copyright (c) Zend Technologies
    with Zend OPcache v7.4.21, Copyright (c), by Zend Technologies
```
これでPHPが使えるようになったと思います

####参考記事
https://qiita.com/miriwo/items/cd54077aad7e139cf518

https://wpbeaches.com/updating-to-php-versions-7-4-and-8-on-macos-11-big-sur-and-catalina/
【Ruby】条件演算子(?:)の読み方、書き方

Rubyの条件演算子について（?とか：が入った式）が最初に出てきたとき「???」となったので、もし同じ状況になった人がいたらと思い、以下に記録しておきます。

#条件演算子とは
以下のような式のことです

    result = score >= 70 ? "合格!" : "不合格"

この式の見方としては、

    変数 = 条件式 ? 条件が真のときの値 : 条件が偽のときの値

となっています。
つまり最初の例に挙げた式は、

    resultという変数 = scoreが70以上だったら? "合格!"と返す : (scoreが70未満だったら)"不合格"と返す

という意味になります。

試しに以下のコードを動作させてみると‥

    score = 76
    result = score >= 70 ? "合格!" : "不合格"
    puts result #=>合格!

うまく動作させることができました。

#参考
https://www.javadrive.jp/ruby/if/index10.html
Windows TerminalからPowerShellを管理者権限で開く設定

# 枕
[Chocolatey Software \| Chocolatey \- The package manager for Windows](https://chocolatey.org/)
でのinstallは管理者権限でPowerShell起動が必要である。
[microsoft/terminal: The new Windows Terminal and the original Windows console host, all in the same place\!](https://github.com/microsoft/terminal)
から、いかにして管理者権限のコンソールを開くか。

そういうことになった。

# 設定

プロファイル → 新規追加 → 
名前 : PowerShell (Administrator)
コマンドライン : 

```
Powershell Start-Process pwsh.exe -Verb runas
```

アイコン: 

```
ms-appx:///ProfileIcons/pwsh.png
```

以上。
ゼロから作るDeep Learning② word2vecコードで謎だった場所（その2）

謎だった場所

class CBOWの最後のW_in自体がword2vecで作成される単語の分散表現であるために、学習が進むごとに変化する値のはずだが、最後にいきなりself.word_vecsに代入されている。。このW_inの値はいつ変更されているのだろうか？

```python
class CBOW:
    def __init__(self, vocab_size, hidden_size, window_size, corpus):
        V, H = vocab_size, hidden_size

        # 重みの初期化
        W_in = 0.01 * np.random.randn(V, H).astype('f')
        W_out = 0.01 * np.random.randn(V, H).astype('f')

        # レイヤの生成
        self.in_layers = []
        for i in range(2 * window_size):
            layer = Embedding(W_in)  # Embeddingレイヤを使用
            self.in_layers.append(layer)
        self.ns_loss = NegativeSamplingLoss(W_out, corpus, power=0.75, sample_size=5)

        # すべての重みと勾配をリストにまとめる
        layers = self.in_layers + [self.ns_loss]
        self.params, self.grads = [], []
        for layer in layers:
            self.params += layer.params
            self.grads += layer.grads

        # メンバ変数に単語の分散表現を設定
        self.word_vecs = W_in
```
		
これはW_inがミュータブルなものとして設定されているために、実際にはそれぞれ学習される時のオプティマイザーによってW_inの値が変更されていると理解しました。

つまり、train.pyの中の「trainer = Trainer(model, optimizer)」で作成されたインスタンスを、trainer.fitで学習する際に呼ばれるoptimizerの中にある「params[i] -= （オプティマイザーにより異なる)」の箇所。

ゼロつくではword2vecの学習時にオプティマイザーとしてAdamを指定しているので、具体的にはcommon.optimizer.pyの中の以下部分で値が変更されているようです。


```python
            params[i] -= lr_t * self.m[i] / (np.sqrt(self.v[i]) + 1e-7)
```
			
【参考】
[Pythonの引数における参照渡しと値渡しについて](https://www.javadrive.jp/python/userfunc/index3.html)が分かりやすかったです！（ありがとうございます）

<br>
処理イメージを簡単に書くと、以下のように（メモリ上に展開されている？）直接W_inの値を変更しているという理解です。

```python
import numpy as np

W_in = 0.01 * np.random.randn(7, 3).astype('f')
print(W_in)

params = W_in[2]   #W_inの2行目のみparamsに代入
print(params)
print(type(params))

params += np.array([0.3,0.3,0.3])   #paramsのすべての要素に0.3を足す。つまりW_inのミュータブルな値W_in[2]を書き換えている

print(W_in)
```

出力結果は以下の通り。

```python
# W_in = 0.01 * np.random.randn(7, 3).astype('f')
# print(W_in)
[[-0.00701334 -0.0073347   0.01756549]
 [-0.00425539 -0.00826015 -0.00587883]
 [ 0.01375221  0.01287736  0.00347899]
 [-0.01656658  0.01144262  0.00432454]
 [ 0.01538134 -0.00216335 -0.00644081]
 [ 0.00757222 -0.00047482 -0.02197131]
 [ 0.01234352  0.01055192  0.00145422]]

# params = W_in[2]   #W_inの2行目のみparamsに代入
# print(params)
[0.01375221 0.01287736 0.00347899]
# print(type(params))
<class 'numpy.ndarray'>

# params += np.array([0.3,0.3,0.3])
# print(W_in)
[[-0.00701334 -0.0073347   0.01756549]
 [-0.00425539 -0.00826015 -0.00587883]
 [ 0.3137522   0.31287736  0.303479  ]   #W_inの2行目の値にそれぞれ0.3足されていることが分かる
 [-0.01656658  0.01144262  0.00432454]
 [ 0.01538134 -0.00216335 -0.00644081]
 [ 0.00757222 -0.00047482 -0.02197131]
 [ 0.01234352  0.01055192  0.00145422]]
```

<br>
【訂正履歴】
最初に記事を書いたときは、W_inの値の変更はCBOWクラスの中の以下部分で行われていると勘違いしていました。

```python
        for layer in layers:
            self.params += layer.params
```
知り合いから「間違えてるのではー？」と指摘を受けて確認してみると、この部分ではリストに値を追加しているだけでした（ｽｲﾏｾﾝ）。

具体的には、paramsというリストに、Embeddingされる回数分のW_inの値のnumpy配列を突っ込み、最後に（layersの最後に格納されている[ns_loss]部分の）NegativeSamplingLossのsample_size数分のnumpy配列を突っ込んでいるという動きをしており、W_inの値を変更するというような動きはしていませんでした。
（まだまだ上手くプログラムが読めない・・・日々精進せねば。）
[Rails]RSpecでよく使うマッチャー10選

RSpecでよく使うマッチャーを１０個厳選してまとめておきます。

ここで紹介するマッチャーはごく一部ですので、あしからず。

#紹介するマッチャー

今回紹介するマッチャーは下記１０個です。

①be_valid
②include
③eq
④have_content
⑤have_link
⑥have_button
⑦have_css
⑧click
⑨have_current_path
⑩change

#be_valid

`「有効か」`を確認できます。

具体的には、バリデーションチェックでの返り値がtrueであることを確かめます。

```rb:
it "有効なuserの場合は保存されるか" do
  # build(:user) はFactoryBotで作ったデータ
  expect(build(:user)).to be_valid
end
```

FactoryBotで作ったデータが有効であるか確認して、trueが返ってきたら、テスト成功です。

#include

`「配列に存在するか」`を確認できます。

たとえば、バリデーションチェックでのエラーメッセージは配列で返ってくるので、エラーメッセージに指定の値が存在するかを確認できます。

```rb:
it "nameが空白の場合にエラーメッセージが返ってくるか" do
  # userにnameカラムを空で保存したものを代入
  user = build(:user, name: nil)
  # バリデーションチェックを行う
  user.valid?
  # nameカラムでエラーが出て、エラーメッセージに"を入力してください"が含まれているか？
  expect(user.errors[:name]).to include("を入力してください")
end
```

このように、エラーが出たときにエラーメッセージ(expectの引数)に、指定の値が存在するか確認して、存在していればテストは成功です。

#eq

`「一致するか」`を確認できます。

比較するのは`expectの引数`と`eqの引数`です。

```rb:
it '1 + 1 は 2 になる' do
  expect(1 + 1).to eq 2
end
```

expectの引数とeqの引数が一致すれば、テストは成功です。

#have_content

`「文字列が存在するか」`を確認できます。

ページの中に、指定した文字列があるか確認したいときによく使います。

```rb:
it "見出しがあるか" do
  expect(page).to have_content "投稿一覧"
end
```

expect(page)で、今いるページを指定できます。
pageの中に"投稿一覧"が存在していれば、テスト成功です。

#have_link

`「リンクが存在するか」`を確認できます。

have_linkでは、href属性を指定することもできるので、リンク部分の値またはリンク先のパスで、テストを行えます。

```rb:
# リンクの値を指定
it "ユーザー一覧へのリンクが表示されているか" do
  expect(page).to have_link "ユーザー一覧"
end

# リンク先のパスを指定
it "投稿一覧へのリンクが表示されているか" do
  expect(page).to have_link "", href: posts_path
end
```

pageの中に指定の値があればテスト成功です。

#have_button

`「ボタンが存在するか」`を確認できます。

have_のボタンバージョンです。

```rb:
it "投稿ボタンがあるか" do
  expect(page).to have_button "投稿"
end
```

#have_css

`「cssが存在するか」`を確認できます。

cssのclass名などを指定することができるので、cssの確認をしたいときなどに使われます。

```rb:
it "切り替えタブがあるか" do
  expect(page).to have_css '.tab'
end
```

pageに'.tab'が存在すればテストは成功です。

#click

`「クリック」`を行えます。

`click_link`や`click_button`で、クリックする対象を選択してクリックを行い、その後の挙動をテストします。

```rb:
# 投稿一覧リンクをクリック
click_link "投稿一覧"

# 投稿ボタンをクリック
click_button "投稿"
```

あくまで補助的に使われます。

#have_current_path

`「パスを取得」`できます。

クリックした後の、リンク先が正しいかの確認でよく使われます。

```rb:
it "投稿一覧のリンク先は正しいか" do
  # 投稿一覧リンクをクリック
  click_link "投稿一覧"
  # pageのurlを確認
  expect(page).to have_current_path posts_path
end
```

pageのリンクが正しければテスト成功です。

#change

`「変動するか確認」`を確認できます。

削除のテストなどでよく使われます。
削除のテストでは、削除を実行して、データベースの数が減っているか確認します。

```rb:
it "削除されるか" do
  # データベースが変動したかを確認
  expect { post.destroy }.to change(Post, :count).by(-1)
end
```

`expect { post.destroy }`でpostを削除して、`change(Post, :count).by(-1)`で、postのデータベースからデータが1つ減っているかを確認していて、減っていればテスト成功です。

#まとめ

####今回紹介したマッチャーは下記１０個

>①be_valid
②include
③eq
④have_content
⑤have_link
⑥have_button
⑦have_css
⑧click
⑨have_current_path
>⑩change

冒頭でも触れましたが、今回紹介したマッチャーはごく一部で、マッチャーはまだまだありますので、テスト項目にあわせて調べながら覚えていきましょう。

RSpec系の記事他にもありますので、よかったらどうぞ！
[[Rails]RSpecでテストを行う準備(FactoryBot使用)](https://qiita.com/nakachan1994/items/29369470d50d0fcd0f6c)
[[Rails]RSpecでモデルのテスト(FactoryBot使用)](https://qiita.com/nakachan1994/items/d869db15f8482ba70d02)
jQueryを導入したい(Rails 6.0.4)

# はじめに
## やりたいこと
jQueryを導入したい
##動作環境
ruby 2.6.5 / Rails 6.0.4

##手順
- jQueryのインストール
- Webpackの設定
- application.jsの設定
- 動作確認

# jQueryのインストール

jQueryのインストールを行います。

```linux:ターミナル
% yarn add jquery

```
JqueryをWebpackerで管理する際はyarnコマンドを使用してインストールします。

# Webpackの設定

```javascript:config/webpack/environment.js
const { environment } = require('@rails/webpacker')
// ここから
const webpack = require('webpack')
environment.plugins.prepend('Provide',
    new webpack.ProvidePlugin({
        $: 'jquery/src/jquery',
        jQuery: 'jquery/src/jquery'
    })
)
// ここまで
module.exports = environment
```

# application.jsの設定
application.jsでjQueryを呼び出せるようにします。

```javascript/packs/application.js
//中略

require("@rails/ujs").start()
require("turbolinks").start()
require("@rails/activestorage").start()
require("channels")
// 追記
require('jquery')

//中略
```
以上で導入は完了です。

# 動作確認
jQueryが問題なく動作しているか確認します。
好きなページに次のコードを転記します。

``` html:好きなページ.html.erb
<p>JQuery導入失敗</p>
<script type="text/javascript">
  $(document).ready(function() {
    $("p").text("JQuery導入成功！！");
  });
</script>
```
ページに"JQuery導入失敗"ではなく、"JQuery導入成功！！"が表示されれば成功です！

↓こちらのページを参考にしました！ありがとうございました！

https://qiita.com/tatsuhiko-nakayama/items/b2f0c77e794ca8c9bd74
TypeScript用にESLintとPrettierを導入する際にインストールするパッケージの役割

typescript開発環境にESLintとPrettierを導入する際にインストールしたパッケージ達が何をやっているのかを理解するために書き殴ります。

```
    "@typescript-eslint/eslint-plugin": "^4.28.4",
    "@typescript-eslint/parser": "^4.28.4",
    "eslint": "^7.31.0",
    "eslint-config-prettier": "^8.3.0",
    "husky": "^7.0.1",
    "lint-staged": "^11.0.1",
    "prettier": "^2.3.2",
```

##ESLintとPrettier

####ESLint
javascriptのための静的検証ツール
ファイル内にバグがないかとか、コーディングスタイル一貫性があるのかどうかとかを検証してくれる。

####Prettier
コードフォーマッター
ソースコードを整形してくれるツール
ルールをプロジェクトファイルごとに設定することができる。

##それ以外のパッケージについて

```
eslint-config-prettier
```
ESLintとPrettierを併用する際に使用するパッケージ。
設定に一貫性を持たせるためにインストール。

```
@typescript-eslint/eslint-plugin
```
もともとjavascript用のESLintをtypescriptでも使えるようにするためのパッケージ

```
@typescript-eslint/parser
```
ESLintでtypescriptのファイルを解析できるようにするためのパッケージ
>parser　パーサーとは、構文解析を行うためのプログラムの総称である

```
husky
```
gitのコマンドをフックにして別のコマンドを呼び出せるようになるパッケージ

```
lint-staged
```
gitでcommitしたファイル（stagingにあるファイル）にESLintを実行することができるパッケージ

以上！だいたいわかった！
多段SSH Port Forwardingの構成例

下図のような構成で、サーバーAからサーバーDのサービスにセキュアにアクセスしたい、という要件を満たすため、多段のSSH Port Forwardingを構成しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/ce9577f2-80e6-168e-bd2a-8c95182994a3.png)

前提条件
・サーバーA〜Dの4台がある
・AとDはプライベートNWのみ持つ
・2拠点間にVPNは張らない
・2拠点間に閉域ネットワークも無い

双方ともIBM Cloud環境であればTransit Gatewayでプライベート接続できますが、今回は片方がオンプレミスの想定なので、インターネット経由の接続となります。

サーバーBで、下記のオプションでSSHポートフォワーディングを構成しつつ、サーバーC（169.62.139.xxx）にSSH接続します。（実際は、sshが切れても再接続できるよう、autosshを使いました。また、ログアウトしても維持されるよう、screenで作ったセッション内で実行しています。）

サーバーBのport 20080を、サーバーD（10.208.19.181）のport 80に転送します。
gオプションで、自分以外のサーバー（今回はサーバーA）からも接続できるようになります。

```:サーバーB
# ssh -M 0 -C -N -g -L 20080:10.208.19.181:80 root@169.62.139.xxx -i ./.ssh/id_key
```

サーバーCのSecurity Groupは、サーバーBからのSSHが許可されていればOKです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59627/7feea8ff-25b7-7c07-3ac7-71d54f00c36f.png)


サーバーAから、サーバーB（10.193.153.111）の20080にcurlすると、サーバーDで動くウェブサーバーが応答します。

```:サーバーA
# curl http://10.193.153.111:20080
This is Server D
# 
```

以上です。
PostmanでURL上にIDなどの文字列を変数として簡単にセットする方法

#概要
PostmanをつかってAPIのテストをする際によく使うのはAPI URL、トークンなどを環境変数と、パラメーターは項目はあって簡単に変更できるのですが、IDなどはURLのアドレス部分に組み込む必要があるAPIもあるので、URLのアドレス部分に変数を使う方法。
#環境変数について
APIの共通URLなどは環境変数（スコープはグローバル（多分））として登録するのが簡単。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2671/f1d5e0d4-023f-76f3-e867-e3763e1c3040.png)
一応自社製品のAPI使ってみてます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2671/3344d424-696d-34f2-9a83-c7b03a46aedc.png)

{{WrikeAPI}}/tasks/ みたいな感じで記述する。
WrikeAPIは https://www.wrike.com/api/v4/
として環境変数に定義されているので、結果的に
https://www.wrike.com/api/v4/tasks/
という感じで展開されるわけですが、例えばURLのパラメーター以前のアドレス部分にtaskIdなど、変数を入れる際にいちいち環境変数を設定するのが面倒。
#URLへPath Variableを設定する方法
調べてみるとStack overflowなどでPre-request Scriptなどでpm.environment.setをつかってセットしているのを見つけられましたがイマイチうまく動作しないなぁと思っていたらちゃんと公式サイトに簡単な方法が掲載されておりました。
https://learning.postman.com/docs/sending-requests/requests/

こんなかんじで下の方に書いてあります。
Sending parameters
You can send path and query parameters with your requests using the URL field and the Params tab.

Query parameters are appended to the end of the request URL, following ? and listed in key value pairs, separated by & using the following syntax: ?id=1&type=new
Path parameters form part of the request URL, and are referenced using placeholders preceded by : as in the following example: /customer/:id

どういう意味かというと、URLのパラメーター前のパスを変数にしたい場合は:を使いなさいということです。

例
https://www.wrike.com/api/v4/tasks/:taskId

するとなんと言うことでしょう。
Path Variablesという項目が出現し、URLのパスを変数としてセットできるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2671/3cb7f5da-7d7b-58a2-97bb-6a7b02787d57.png)
この例ではオプションのfieldsのパラメーターとして渡してます。
これでテスト時にIDなどの変数をURLに展開できるようになります。
[Python]FastAPIを用いたWebスクレイピングAPI 作成方法 メモ

* FastAPIを用いたWebスクレイピング用APIの作成方法についてメモする。
* 指定したキーワードでGoogle検索を行うAPIを作成する。
    * スクレイピング処理用のライブラリはrequestsとBeautifulSoupを利用する。

## 構成

```
fastapi_scraping --- scraper.py
				  |_ main.py
```



## コード

* `scraper.py`（スクレイピング部分）

  ```python
import requests
import bs4

class Scraper():
    def google_search(self, search_keyword):
        # 指定したキーワードで検索
        search_url = 'https://www.google.co.jp/search?q='+search_keyword
        search_response = requests.get(search_url)

        # 検索結果をパース
        soup = bs4.BeautifulSoup(search_response.text, 'lxml')
        parsed_page = soup.select('div.kCrYT>a')

        # タイトル、URLの抽出
        items = []
        rank = 1
        for site in parsed_page:
            item = {
                'rank': rank,
                'title': site.select('h3.zBAuLc')[0].text,
                'url': site.get('href').split(
                    '&sa=U&')[0].replace('/url?q=', '')
            }
            rank += 1
            items.append(item)
        return items
  ```

* `main.py`

  ```python
  from fastapi import FastAPI
  from scraper import Scraper
  app = FastAPI()
  scraper = Scraper()
  
  @app.get("/{word}")
  async def search_google(word):
      return scraper.google_search(word)
  ```

  

## 動作確認

* 起動

  ```shell
  uvicorn main:app --reload
  INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
  INFO:     Started reloader process [2020] using statreload
  INFO:     Started server process [3816]
  INFO:     Waiting for application startup.
  INFO:     Application startup complete.
  ```

* Swagger UIにアクセス

  ```
  http://localhost:8000/docs
  ```

  ![fastapi_swagger_ui.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586535/c1d42c39-774b-ed25-2fbc-1c29fea44514.png)


* 「Get /{word}」を開く→「Try it out」を押下→「word」に検索ワードを入力→「execute」押下

  ![fastapi_swagger_ui_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/586535/11f54dd0-65b7-fa58-9b51-aeed053123d2.png)


  
## 参考情報

  * [Create Your Own Scraper API with FastAPI and Python](https://www.youtube.com/watch?v=0D_BqtQtBZc)
  * [PythonでGoogle検索上位100サイトをスクレイピングする方法を紹介](https://inasala.com/scraping-google-search/)
Ubuntu 20.04 LTS


作業
-
Windows10 20H2
Ubuntu 20.04 LTSインストール
Python 3セットアップ

教材
-
https://qiita.com/SSMU3/items/ce6e291a653f76ddcf79

https://zenn.dev/yukiko_bass/articles/6b2c47357e83e1

所感
-
AWSの勉強をしていますが、Macの環境の記事も多く、まったくMacが分からないので途方に暮れることもあります。

そこでUbuntuも使うことにし、後々Dockerも使いこなせれば、大丈夫なのかなと希望を持っています。:slight_smile:
デプロイしたアプリケーションがiPhoneやiPadで表示した時のみ画面がクラッシュする原因

#経緯
railsにてアプリケーションの開発を一通り進めたので、herokuにデプロイ。
レスポンシブの確認も兼ねてiPhoneやiPadでも表示の確認をしていたところ、devise(ユーザー管理系gem)で作成したユーザー情報編集画面のみクラッシュされたと表示され画面を確認できない。
(しかし、履歴の部分からリロードすると何故か表示できたが、、、)

# 原因
複数のinputタグに``autofocus: true;``を適用していたこと。
→一つのみに適用したら、画面のクラッシュはなくなり、しっかりと表示される様になった。

# 不可解なこと
この画面のクラッシュはiPhoneとiPadにてそれぞれChromeとSafariで確認された。
しかし、android端末のChromeではクラッシュが確認されなかった。

# 参考
https://teratail.com/questions/315222

(2021年7月22日閲覧)

https://qiita.com/yuki50984555/items/db3badd0810f39d97cba

(2021年7月22日閲覧)
MVCモデルについて書く

###はじめに
Laravelを学習中ですが、先日企業の面接で「MVCモデルの説明」を求められ、
うまく答えられなかったので記事にしました。


###MVCモデルとは
+ 「Model-View-Controller」の略
+ MVCモデルのような処理を役割ごとに別の場所に書くことで、誰でも読みやすいコードになる
+ 例えば、ユーザー登録処理だけでも以下のような処理が必要になる

1. 入力画面の表示
1. 入力された値の受け取り、確認
1. DBへの登録
1. 登録完了画面


###Model(モデル)
**ビジネスロジックを担当する部分。**ビジネスロジックとは、例えば申し込みフォームの内容をDBに登録させる処理。
「処理した結果をDBへ反映させること」くらいの理解でOK。
データ処理全般を担当し、データベースに関する処理全般を扱う。また、DBからデータを取得して、Controllerへ送る部分でもある。

###View(ビュー)
Webブラウザなどに表示する**HTMLを動的に生成する部分。**
HTMLを動的に生成するとは、データベースから取得したデータをHTMLに埋め込んでアクセスするたびに、異なる HTML を生成することなど。
動的なサイトとして、わかりやすいのにTwitterがある。今、アクセスした時と、10分後にアクセスした時とではタイムラインの新着順が変わっており常にリアルタイムで変動する。それは、アクセスするたびにデータベースから最新の記事を習得して最新のタイムラインをトップページに表示するという処理をしているから。
Viewはこういった機能をもつ。

###Controller(コントローラー)
**View(ビュー)とModel(モデル)を操作する部分。**
Viewからのリクエストを受け取り、Modelへ処理の命令を出す。また、Modelから処理の結果を受け取り、レスポンスとしてViewに返したりする。ViewとModelの仲介という理解でOK。

###MVCの流れ
[![Image from Gyazo](https://i.gyazo.com/997e70a73c87554ea4500ea8ff278ad0.png)](https://gyazo.com/997e70a73c87554ea4500ea8ff278ad0)

簡単ではありますが、以上です！
CloudMapper Docker


教材
-
https://qiita.com/at-946/items/bcd74d2d355a0b9e375e

前提
-

Windows10 にて　Ubuntu
Docker for Windowsをインストール済み

作業
-
Windows10のubuntuを起動
作業用のディレクトリを作成し、移動する。

後は、参考サイト通りに実施し、できた。
自分自身のAWSアカウントのコンソールにログインし情報を確認しながら作業しました。

初回の情報収集に時間がかかります。


所感
-

本当に、本当に、Docker上で作業したら2時間かけて出来なかったものが
20分くらいで出来ました。
ググって pythonのエラーとかパッケージが無いよ等と戦わなくてもいい。
参考サイトの方本当にありがとうございました。
SurgeにWebページをデプロイするメモ

## ざっくりSurge

https://surge.sh/

静的 Web ページを公開できるサービスです。
無料プランと　月 30 ドルの有料プランがありますが、無料プランでも趣味の範囲なら十分そうです。

- `surge`コマンドを使って、マシン上のどのディレクトリのコードでもデプロイできる
- カスタムドメインを利用できる。(`xxxxxx.surge.sh`。購入済みドメインも設定できる。)
- 無料版でもパブリッシュの上限なし

## さっそくデプロイしてみる

surge の CLI ツールをインストールします。

```bash
npm i -g surge
```

デプロイしたいディレクトリに移動し、surge コマンドを実行します。

```bash
cd /path/to/publish/directory/
surge
```

初回実行時のみユーザー登録が必要なので、登録しましょう。

いくつかオプションを聞かれるので、答えます。
- `project`: デプロイしたいディレクトリ。カレントディレクトリがデプロイしたいディレクトリであればそのままで OK。
- `domain`: 今回は`xxxxxxxx.surge.sh`など`surge.sh`のサブドメインを指定しました

```
$ surge

Running as xxxxxxx@gmail.com (Student)

     project: /path/to/publish/directory/
      domain: xxxxxxxxxx.surge.sh
      upload: [] 100% eta: 0.0s (4 files, 17763 bytes)
         CDN: [====================] 100%
  encryption: *.surge.sh, surge.sh (278 days)
          IP: xxx.xxx.xxx.xxx

Success! - Published to xxxxxxxxxx.surge.sh
```

これでデプロイ完了です。
https://xxxxxxxxxx.surge.sh にアクセスすると、作成した Web ページが表示されます。

上記の手順は以下のコマンドと同等です。

```bash
surge /path/to/publish/directory/ xxxxxxxxxx.surge.sh
```