OmniAICreator/Qiita-1.07M huggingface dataset:
もっと気軽にpip install

# 気軽にPythonのライブラリを使いたい
面白そうなライブラリ（パッケージ）を見つけたから試したいけど、グローバルのpip汚すのはちょっと……という時にオススメな方法。

好きなディレクトリにpipパッケージをインストールする方法です。

python2.7とpython3.6（両方ともpip 19.0.1）で動作確認済み

# pip install の -t オプション
`pip install <パッケージ名> -t <ディレクトリ>`
-t オプションでパッケージをインストールするディレクトリを指定できる。
例：Beautiful Soupをカレントディレクトリにインストールする場合

```console
$ pip install beautifulsoup4 -t .
```

使う時は↓（カレントディレクトリでpythonを実行）

```python:python
from bs4 import BeautifulSoup
...
```

パッケージはカレントディレクトリ内にインストールされているので、ディレクトリを消せばアンインストールしたことになる。

# -t オプションとは

```text
$ pip install --help
...
Install Options:
...
  -t, --target <dir>          Install packages into <dir>. By default this
                              will not replace existing files/folders in
                              <dir>. Use --upgrade to replace existing
                              packages in <dir> with new versions.
...
```

-t \<dir\>もしくは--target \<dir\>を付ければdirにパッケージをインストールするよ。
（元からあったパッケージは書き換えないから、書き換えたかったら--upgrade付けてね。）
ということらしい。

# 仮想環境使えば？
venvなどの仮想環境を使えば同じような（ディレクトリ毎に独立した）パッケージ管理をもっときっちり行えますが、ちょっと実験するためだけに毎回仮想環境作るのに疲れました。

「今だけちょっと使えればいい」などの場合は仮想環境使うより気軽に試せます。

# おまけ：パッケージと自前のコードが混在する問題を解決する
単純に`pip install beautifulsoup4 -t .`と実行するとディレクトリ内がカオスになる。

```
.
├── backports
│   └── ...
├── backports.functools_lru_cache-1.5.dist-info
│   └── ...
├── beautifulsoup4-4.7.1.dist-info
│   └── ...
├── bs4
│   └── ...
├── soupsieve
│   └── ...
├── soupsieve-1.8.dist-info
│   └── ...
├── tests
│   └── ...
└── 自前のコード.py
```

これを避けるためにはディレクトリを用意し、そこにパッケージをインストールすると良い。
例えば`site-packages`という名前のディレクトリを作ってパッケージをその中にインストールするには以下のようにする。

```console
$ mkdir site-packages
$ pip install beautifulsoup4 -t site-packages
```

```
.
├── site-packages
│   ├── backports
│   ├── backports.functools_lru_cache-1.5.dist-info
│   ├── beautifulsoup4-4.7.1.dist-info
│   ├── bs4
│   ├── soupsieve
│   ├── soupsieve-1.8.dist-info
│   └── tests
└── 自前のコード.py
```

このままだと、自前のコード.pyからパッケージが読めないので次の「sys.path.append()を使う方法」か「PYTHONPATHを使う方法」のどちらかを行う。

（コメントでご指摘いただいたのでPYTHONPATHについて追記）

## sys.path.append()を使う方法

```python:自前のコード.py
import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), 'site-packages'))

from bs4 import BeautifulSoup
...
```

`sys.path.append()`で作ったディレクトリをsys.pathに追加すると、ディレクトリ内のパッケージにいつも通りアクセスできるようになる。

## PYTHONPATHを使う方法

```python:自前のコード.py
from bs4 import BeautifulSoup
...
```

自前のコードには手をつけずに **一時的に** 環境変数のPYTHONPATHを書き換える方法。

永続的に環境変数を書き換える方法は「気軽」ではなくなるので割愛。

### mac・linuxの場合

ターミナル・シェルで以下のように書くと一時的な環境変数を渡すことができる。

```bash
$ PYTHONPATH="${PYTHONPATH}:./site-packages" python 自前のコード.py
```

以下のように`export`でPYTHONPATHを書き換えてもOK。
（この場合ターミナル・シェルを閉じるまで有効）

```bash
$ export PYTHONPATH="${PYTHONPATH}:/path/to/site-packages"
$ python 自前のコード.py
```

### windowsの場合

コマンドプロンプトで以下のように書くと一時的に環境変数を設定することができる。
（コマンドプロンプトを閉じるまで有効）

PYTHONPATHが定義されていない場合↓

```
> set PYTHONPATH=.\site-packages
> python 自前のコード.py
```

PYTHONPATHが定義済みの場合↓

```
> set PYTHONPATH=%PYTHONPATH%;.\site-packages
> python 自前のコード.py
```

PYTHONPATHが定義されているかどうかは以下の方法で確かめられる。

```
> set PYTHONPATH
環境変数 PYTHONPATH が定義されていません
```
ユーザー登録機能つき投稿サイト⓶(ユーザー作成とポストのアソシエーション)

１、gem deviseを導入.bundleインストール。

```ruby:qiita.rb
gem 'devise'
```
２、rails g devise:install　し、出てくる指示を実行
３、ユーザーモデル作成

```ruby:qiita.rb
rails g devise User 
rails db:migrate
```



４,ポストテーブルのカラムにinteger型のuser_idを追加。（postとuserのアソシエーション作成準備）
rails g migration add_user_id_to_post user_id:integer 
rails db:migrate

schema.rbを確認し、できているか確認

```schema.rb
create_table "posts", force: :cascade do |t|
    t.string "title"
    t.string "link"
    t.text "description"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "user_id"
  end
```
5,postとuserのアソシエーション作成。1:nの関係構築

```user.rb
 has_many:posts
```

```posts.rb
 belongs_to :user
```
アソシエーション完了。
これで、current_userなどが使用可能に

６、コントローラを以下のように変更。
@post = Post.newから@post = current_user.posts.buildにする

```ruby:qiita.rb
 
    def new
        @post = current_user.posts.build
    end
    
    def create
        @post = current_user.posts.build(post_params)
        
        if @post.save
            redirect_to @post
        else
            render "new"
        end
    end

```
６、before_action :authenticate_user!, except: [:index,:show]で、ログインしているユーザーしかアクセスできないようにする。もし、ログインしていなかったら、サインインorサインアップに返す
7,これでサーバーを再起動し、新規投稿しようとすると,これまでにpostをいくつか投稿していた場合はこれにユーザーIDを付与していなかったためエラーがでる。なので、まずは/user/sign_upでユーザー登録をする。
すでに投稿してしまっているpostはuser_idがnillなので、rails c経由でuser_idを追加するか、もしくはrails db:resetでデータベースをリセットする
ランキング機能実装

#ランキング機能
私の勝手な見解ですが、ランキング機能は、人々の目をサービスに惹かせるにはとてもいい機能だと思います。
自分の投稿したものが今何位なのかというのは、商品を作成する側からすると非常に気になるものです。
今ではありふれている機能ですが、しっかり戦略があるんだろうなあ。。という薄っぺらい意見から、機能実装入りますw
今回は、特定の食べ物(＝food)が、いいねの数によってランキングされるという機能です。

#ランキング機能実装
MVCから入ります。

##model

```food.rb

has_many: likes

#like.rb
belongs_to: food
```

##view
```index.html.erb
#ランキング結果を全て出したい時
<%=@food_ranking.all%>

#1位だけを出したい時
<%=@food_ranking.first%>
```
ここちょっと悩みどころなんですけど、開発途中だとランキング1位がnilの場合が多いから、強制的にエラーになります。
すでにランキングが埋められている状態で開発するか、nilの場合とそうでない場合をif文で切り分けるか好きなようにすれば良いかと。。
ちなみに自分は、if文で切り分けています。正式にリリースとかするのであれば切り分けた方がいいかもです。。。
この例では入れてないですが。。


##controller 
いよいよネックのコントローラーです

```foods_controller.rb
 food_like_count = Food.joins(:likes).group(:food_id).count
 food_liked_ids = Hash[food_like_count.sort_by{ |_, v| -v }].keys
 @food_ranking= Food.where(id: food_liked_ids)
```

正直ここは初心者からしたら、かなりの難題でした。。。。

一個一個見ていきます
###１行目　food_like_count = Food.joins(:likes).group(:food_id).count
####・joins
内部結合を行うメソッドです。内部結合って何やねんって話なんですが、図を見るとわかりやすいです。

![スクリーンショット 2018-02-16 15.13.39.gif](https://qiita-image-store.s3.amazonaws.com/0/147990/9e807f21-99c9-52e3-d1a5-46fdc61d2fbe.gif)

今、foodテーブルとlikeテーブルの状態が上記のようになっているとします。
右のlikeテーブルは、ある特定のユーザーが、特定のfoodをいいねしている状態を表しています。すなわちいいね機能です。一番上のレコードは３というuserが１であるvegetableをいいねしていることになります。
※わかりづらいという方は、ここについて詳しく書いている記事がありますので、お読みください。＝＞いいね機能の実装と仕組みhttps://qiita.com/tomoharutsutsumi/items/4b045fa12f29502f4b4b

ではこれを内部結合すると。。。
![スクリーンショット 2018-02-16 15.17.14.gif](https://qiita-image-store.s3.amazonaws.com/0/147990/2cbec0a9-136f-5092-1dbf-298fc1575b38.gif)
このようになります。ちょっと特徴的なのはlikeテーブルで存在していないfood_id（この場合は３のfishです）は削除されています。

####・group
これは読んで字のごとく、グループ化します。さらに詳しくいうと特定のカラムを基準としグループ化します。
今回は.group(:food_id)となっています。
上の内部結合の例で言えば、food_idが同じものをまとめるという動作になります。food_idが１であるものが２つあるので、これらはまとめられます。

####・count
これは有名な数えるメソッドです。上のgroupでまとめたものの中身が何個あるかを数えます。
上の表であれば、food_id1は２、２は１になります。

ちなみにこの１行目を動作させると、答えはハッシュになります。
※ハッシュとはなんぞやの記事＝＞ハッシュと配列の違いhttps://qiita.com/tomoharutsutsumi/items/2dd5dd4b6edb17244cd6
{1=>2, 2=>1}という感じで返ってきます。

###２行目　food_liked_ids = Hash[food_like_count.sort_by{ |_, v| -v }].keys

####・Hash
ざっくりいうと、これは配列になっているものをハッシュに変換できるものです。

####・sort_by{ |_, v| -v }
まずはsort_byですが{}内にある条件で、要素の順番を並べ替えなさいというメソッドです。今回でいう要素とはfood_rankの中にある値のことです。(ex.{1=>2, 2=>1})
では条件とは何かなんですが。。。これは「value（１＝＞２で言えば２の部分）が大きい順で」という意味です。
そうすれば、いいねをしている人数が増えることになるので、結果ランキング上位の並び替えとなります。

####・keys
最後にこの部分ですが、これはハッシュのキーの部分（１＝＞２で言えば１の部分）を取り出します。
これで、いいねしているユーザーが多い順にfoodのidが取れます。

###３行目 Food.where(id: food_liked_ids)
ランキングにされたidと同じidを持っているfoodを見つけます。


これで、ランキングの完成です。

#補足
ちなみに以下の記事と合わせて読んでいただければ、ユーザーがいいねできるようになり、かつそのいいね数によってランキングができるようになる、というそれっぽいことが実現できます。ぜひ！
いいね機能の実装と仕組みhttps://qiita.com/tomoharutsutsumi/items/4b045fa12f29502f4b4b
Amazon Product Advertising API を python3 で使う

次のページのプログラムを python3 で走るようにしました。
[Amazon Product Advertising APIの利用(Python)](http://www.tech-tech.xyz/archives/amazon_product_advertising_api.html)

```py3:pa_api.py
#! /usr/bin/python
# -*- coding: utf-8 -*-
#
#	pa_api.py
#
# ------------------------------------------------------------------
import sys
import requests
import hmac
import hashlib
import base64
import urllib.parse
from datetime import datetime
#
# ------------------------------------------------------------------
sys.stderr.write("*** 開始 ***\n")
# Amazon Product Advertising APIの設定
hash_func = hashlib.sha256
encode_func = base64.b64encode
#id関係の設定
access_key = "AK******************"
secret_key = "mn**************************************"
associate_id = "w**********"
#
# メッセージの生成
time_stamp = urllib.parse.quote(datetime.now().strftime('%Y-%m-%dT%H:%M:%S'))
#
# 商品のASINコード
asin_code = "487311778X"	# (ASINコードがない場合は変わりにISBN-10)
#
query="AWSAccessKeyId=" + access_key + \
	"&AssociateTag=" + associate_id + \
	"&ItemId=" + asin_code + \
	"&Operation=ItemLookup" + \
	"&ResponseGroup=Images%2CItemAttributes%2COffers%2CReviews" + \
	"&Service=AWSECommerceService" + \
	"&Timestamp=" + time_stamp + \
	"&Version=2013-08-01"
api_domain = "webservices.amazon.co.jp"
api_page = "/onca/xml"
message = "\n".join(["GET", api_domain, api_page, query])
#
# HMACのSignature生成
#
sing_gen = hmac.new(secret_key.encode('utf8'), message.encode('utf8'), hash_func)
raw_sign = sing_gen.digest()
sign = urllib.parse.quote(encode_func(raw_sign))

# APIの呼び出し
url = "http://" + api_domain + api_page + "?" + query + "&Signature=" + sign
res = requests.get(url)
with open(asin_code + ".xml", "wb") as ff:
	ff.write(res.content)
sys.stderr.write("*** 終了 ***\n")
# ------------------------------------------------------------------
```

実行すると、
487311778X.xml　が出来ます。

これを整形するには

```bash
xmllint --format 487311778X.xml
```
puppeteerでiframe内の要素を操作する

## サンプルページ
iframe内にボタンを用意して、そのボタンを押すとボタンの色が変わるだけのページを作ります。

```app/index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>サンプルページ</title>
</head>
<body>
  <iframe src="iframe.html" name="hoge">
</body>
<html>
```

```app/iframe.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>iframeページ</title>
</head>
<body>
<button type="button" id="button" style="background-color:red;" onclick="chColor()">
おしてね
</button>
<script>
  function chColor(){
    document.getElementById('button').style.background='blue';
  }
</script>

</body>
<html>
```
<img width="350" alt="スクリーンショット 2018-02-16 16.58.38.png" src="https://qiita-image-store.s3.amazonaws.com/0/53709/d9e3ca58-0ad1-19a4-95d9-6cf7ab3d4e6e.png">

## サンプルコードを作成
puppeteerのコードを書いていきます。

```app/script.js
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch({
    args: [
      '--no-sandbox',
      '--disable-setuid-sandbox'
    ]
  });

  const page = await browser.newPage();

  // urlは適宜変更してください
  await page.goto('file:///Users/somiya/app/index.html'); 
  await page.screenshot({ path: 'before.png' });

  const frame = await page.frames().find(f => f.name() === 'hoge');
  const button = await frame.$('#button');
  await button.click();
  await page.screenshot({ path: 'after.png'});

  browser.close();
})();
```

frameの指定をnameで指定しています。
その後そのフレーム内のボタン要素を取得し、クリックという流れです。
（他の指定方法があったら教えてください。iframe自体あまり使わないので調べてないですm(_ _)m）

## 実行
```
$ node script.js
```
実行したらボタンを押す前と押した後のスクリーンショットが保存されてるので確認します。

[befor]

|![before.png](https://qiita-image-store.s3.amazonaws.com/0/53709/b9678e27-c79b-7973-a43c-448276442a56.png)|
|:-:|

[after]

|![after.png](https://qiita-image-store.s3.amazonaws.com/0/53709/c46523f6-ab81-d12f-5d27-f2db2f268790.png)|
|:-:|

このframeを使えばiframe内でも外でも同様の操作ができそうですね。
PageSpeed Insightsで「ブラウザのキャッシュを活用する」「圧縮を有効にする」を最適化推奨されたときの対処法

使ってみて大丈夫そうだったのでメモ。


# ブラウザのキャッシュを活用する

- ファイルタイプごとにキャッシュの有効期限を設定し、以前表示されたデータがキャッシュに残ってる場合はそちらを活用するように。
- 更新が少ないファイルはサーバーにリクエストしなくなる→読み込むファイル数が少なくなるので、ページ表示までの時間が短くなる。
- 設定前よりキャッシュが強くなるので、画像やjavascriptの更新が反映されにくくなることがある。

1. サーバーがApacheのモジュール「mod_expires」に対応しているか確認する
2. FTPを使って.htaccessをダウンロード
3. .htaccessの上の方に下記記述を追加

```ruby:.htaccess
<ifModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 15 days"
ExpiresByType image/gif "access plus 10 days"
ExpiresByType image/jpg "access plus 10 days"
ExpiresByType image/jpeg "access plus 10 days"
ExpiresByType image/png "access plus 10 days"
ExpiresByType application/x-javascript "access plus 10 days"
ExpiresByType text/x-javascript "access plus 1 month"
ExpiresByType application/javascript "access plus 1 month"
ExpiresByType application/x-javascript "access plus 1 month"
ExpiresByType text/js "access plus 1 month"
ExpiresByType text/javascript "access plus 1 month"
ExpiresByType application/x-shockwave-flash "access plus 1 month"
</ifModule>
```

4. 修正した.htaccessを元の場所に上書きアップロード


# 圧縮を有効にする

- この設定をすると、サーバーはリクエストされたファイルをクライアント（ユーザー側）に送る前に圧縮するようになる。クライアント側では、ブラウザが自動で受け取った圧縮データを展開し表示する。
- データを圧縮して送るので通信の負担が激減→ページ表示までの時間が短くなる。

1. サーバーがApacheのモジュール「mod_deflate」に対応しているか確認する
2. FTPを使って.htaccessをダウンロード
3. .htaccessの上の方に下記記述を追加

```ruby:.htaccess
<IfModule mod_deflate.c>
SetOutputFilter DEFLATE
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|ico)$ no-gzip dont-vary
SetEnvIfNoCase Request_URI _\.utxt$ no-gzip
#DeflateCompressionLevel 4
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/atom_xml
AddOutputFilterByType DEFLATE application/x-javascript
AddOutputFilterByType DEFLATE application/x-httpd-php
</IfModule>
```

4. 修正した.htaccessを元の場所に上書きアップロード

-------------------------------------

.htaccessの書き換えを行うので、【ミスするとサイトが閲覧できなくなる】。
必ず作業前にバックアップを取り、問題が発生したら古いファイルに戻すこと。
Scotch BoxとAdminerで簡単にローカルWordpress環境を作る

Wordpressでサイトを作った後に更新とか改修があるとサーバーで変更するの面倒ですよね。
そこローカルで簡単にWordpressを動かしてみる。
既存Wordpressをローカル環境に移植のケースで説明しますが、新規Wordpressサイト構築でも使えるかとー。
簡単なコマンドライン操作は出来る前提の内容です。

## 下準備
#### 1. まずは必須なツールがインストールされてるか確認。なければ入れる。

- [Vagrant](https://www.vagrantup.com/)
- [VM VirtualBox](http://www.oracle.com/technetwork/jp/server-storage/virtualbox/overview/index.html)

#### 2. FTPでWordpressサイトを落としてくる。

#### 3. SQLファイルをDumpしてくる。
mysqladminが入っていれば、そこからエクスポート(Droptableにチェックして)する。

#### 4. 今回のキモである便利ツール達をDLしておく。
Adminierは`Adminer 4.6.1 for MySQL`とかで良いと思う。

- [Adminer](https://www.adminer.org/)
- [ScotchBox](https://github.com/scotch-io/scotch-box)

## ここからセットアップ

#### 1. ScotchBoxの準備
ScotchBoxをDLするとpublicフォルダがあるので、そこがサーバーのrootとなるのでFTPで落としてきたWordpressサイトをまるっとぶち込む。
publicフォルダの隣にある`Vagrantfile`を開くと下記の記述があり、これがローカルの*アクセスURL*になる。
これは変更してもOK。

```
config.vm.network "private_network", ip: "192.168.33.10"
```
これだと`http://192.168.33.10`でアクセスできる。

#### 2. wp-config.phpの編集
ぶちこんだファイルに`wp-config.php`があるはずなので、それを開いて下記に変更する。

```
/** WordPress のためのデータベース名 */
define('DB_NAME', 'scotchbox');
/** MySQL データベースのユーザー名 */
define('DB_USER', 'root');
/** MySQL データベースのパスワード */
define('DB_PASSWORD', 'root');
/** MySQL のホスト名 */
define('DB_HOST', 'localhost');
```

#### 3. Adminerの準備
Adminerはファイル名を`db.php`に変えてpublic直下に配置する。

#### 4. SQLファイルの準備
dumpしてきたSQLファイルを開き、`option`や`option_id`などで検索すると`siteurl`と`home`が見つかるので、そこを`Vagrantfile`のIP(アクセスURL)に変更する。

```
(1, 'siteurl', 'http://192.168.33.10', 'yes'),
(2, 'home', 'http://192.168.33.10', 'yes'),
```

変更出来たら`adminer.sql`にリネームしてpublic直下へ配置する。

#### 5. ローカル環境の立ち上げ
ここまできたら`Vagrantfile`のある場所で下記コマンドを実装する。

```
vagrant up
```

立ち上がったら`http://192.168.33.10`にアクセスしてみる。
上手く行っていればWordpressのインストール画面が出ているはず。
ここではインストールはしない。

#### 6. データベースの設定
`http://192.168.33.10/db.php`にアクセスする。
![sample01.png](https://qiita-image-store.s3.amazonaws.com/0/20201/f5a0f78d-7c1c-5c53-975a-850779b2254e.png)

下記でログインする。

```
Username : root
Password : root
Database : scotchbox
```

ログインしたら、DBに`scotchbox`を選んだ状態で`Import`を押す。
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/20201/462c13a5-2a8d-dc02-f920-78578c3b80dc.png)

上の画面になったら右の`From server`の項目の`Run File`ボタンを押す。

#### 7. 完成
あとは`http://192.168.33.10`にアクセスすれば、問題なければWordpressサイトが表示されるはず！

## 雑感
もっとハイテクなWP-CLIや、それを使ったVCCWとかあるけども、個人的に大げさに思ってたのでWordpressサイトを触るだけならこれくらいでもいいのかなーと思う。

## おまけ
ScotchBoxとAdminerを入れたフォルダは下記においてます。
これに既存サイトのWordpressファイル+SQLファイルか、新規のWordpressぶち込むと幸せになれる。
なれると5分くらいでサクッと立ち上げれるよー。

https://github.com/hanuman6/wp-rakuraku
Project Euler 32「パンデジタル積」

美しくない。

# Problem 32 「パンデジタル積」
すべての桁に 1 から n が一度だけ使われている数をn桁の数がパンデジタル (pandigital) であるということにしよう: 例えば5桁の数 15234 は1から5のパンデジタルである.
7254 は面白い性質を持っている. 39 × 186 = 7254 と書け, 掛けられる数, 掛ける数, 積が1から9のパンデジタルとなる.
掛けられる数/掛ける数/積が1から9のパンデジタルとなるような積の総和を求めよ.
HINT: いくつかの積は, 1通り以上の掛けられる数/掛ける数/積の組み合わせを持つが1回だけ数え上げよ.

```python
def search(sa, ea, sb, eb):
    d = list('123456789')
    digits = set()
    for a in range(sa, ea + 1): # a : 掛けられる数
        for b in range(sb, eb + 1): # b : 掛ける数
            c = a * b
            if sorted(list(str(a) + str(b) + str(c))) == d:
                digits.add(c)
    return digits

def hoge():
    # 掛けられる数/掛ける数/積が1から9のパンデジタルとなるのは
    #「1桁 x 4桁 = 4桁」と「2桁 x 3桁 = 4桁」のみ（たぶん）
    return sum(search(1, 9, 1234, 9876) | search(12, 98, 123, 987))

print(hoge())
```

はじめは重複チェックや桁数チェック入れてたけども見辛いし性能も大して変わらなかったのでやめた。
コメントで書いてる内容も自動的に判定するようにしたかったけど思いつかなかったので諦めた。
入力されたテキストをURLの末尾に付与して遷移するだけのChrome拡張機能

#やりたいこと
- Chrome拡張機能開発の練習をしたい。
- 社内システムの個別ページに直接遷移したい。

社内の案件管理システムは、案件番号がURLの末尾になります。

http://（固定のURL）/AAAAA
http://（固定のURL）/BBBBB


個別のページにたどり着くまでの階層が深かったので、案件番号を入力して直接遷移するためにChrome拡張機能を作ることにしました。（~~社内システムを直した方が…~~）

#完成品のイメージ
<img width="306" alt="Screen Shot 2018-02-16 at 15.29.45.png" src="https://qiita-image-store.s3.amazonaws.com/0/228205/0586fc72-133d-190e-c849-0ab341279fdb.png">

テキストボックスに値を入力してボタンを押すと、入力値を末尾に付与したURLのページを新しいタブで開きます。
なお、アイコンは[ICOOON MONO](http://icooon-mono.com/)からいただいています。

#完成したソース
```json:manifest.json
{
    "name"          : "Direct",
    "version"       : "1",
    "manifest_version"  : 2,
    "description"       : "個別ページに直接遷移します。",
    "browser_action": {
      "default_icon": {
      "19": "19px.png"
    },
    "default_title": "Direct",
    "default_popup": "popup.html"
  },
    "permissions"       :
        [
            "tabs"
        ],
  "content_security_policy": "script-src 'self' https://ajax.googleapis.com; object-src 'self'"
}
```
jQueryを使うために、`https://ajax.googleapis.com`を読込みできるように`content_security_policy`に記載しています。


```html:popup.html
<DOCTYPE HTML>
<html lang="ja">
  <head>
  <meta charset="UTF-8">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.min.js"></script>
    <script src="popup.js"></script>
  </head>
  <body style="width:280px;">
    <center>
      <h1>Direct</h1>
      <input type="text" id="q"></input>
      <input type="button" id="s" value="Go">
    </center>
  </body>
</html>
```
ポップアップ表示されるHTMLです。
テキストボックスとボタンがあるだけのシンプルなページです。

```javascript:popup.js
$(function() {
  $('#s').click(function() {
     var url  = "（固定のURL）" + $('#q').val();
     chrome.tabs.create({url: url});
  });
  //Enterキーでボタン動作
  $('#q').keypress(function(event) {
  	if (event.which === 13){
  	//空だったら何もしない。
  		if(!$(this).val().match(/\S/g)){
 		 return false;
		}
		else{
		  	$('#s').click();
		}
	}
  });
  //自動フォーカス
  $('#q').focus();
});
```
popup.htmlのボタンクリックで動きます。
こちらもシンプルな内容ですが、Enterキーで動作したり、空だったら何もしないようにしています。

#おわりに
社内で好評なので、作ってよかったです。
Chromeウェブストアで公開したアプリや拡張機能を削除する方法(2018年)

## はじめに
chromeのウェブストアに公開した拡張機能(extension)を整理しようと思い、デベロッパー ダッシュボードを見渡しても削除する方法が見当たらないので、調べて→記事あった！→できない＿|￣|○ だったの情報をアップデートします。

## 結論
結論から言えば、**2018年2月時点ではできなくなっています。**

## 昔はできたらしい？
調べてたら以下の記事がありました。
[Chromeウェブストアで公開したアプリや拡張機能を削除する方法*](https://qiita.com/shoito/items/8687d604cec10d12dc86)2013年09月の記事
これを見て実行しました。
`cxDeleteItem("アイテムID")`を実行

**…がエラーになります。**
![chrome-webstore-delete.png](https://qiita-image-store.s3.amazonaws.com/0/206716/fb25d4fb-76b7-3e3a-08b4-397bacb93396.png)

コード補完の候補にもでてきません。
![chrome-webstore-delete2.png](https://qiita-image-store.s3.amazonaws.com/0/206716/11990c48-f7f0-f6f3-d2a8-623dfdbf7386.png)

Source確認してしてもやっぱりいない...
![chrome-webstore-delete3.png](https://qiita-image-store.s3.amazonaws.com/0/206716/bd448066-d400-55df-bcf6-1cb69ad50363.png)




## さらに調べてみた
調べたらstackoverflowに以下の記事がありました。
[How do I delete an extension in my Chrome developer dashboard?
](https://stackoverflow.com/questions/31233063/how-do-i-delete-an-extension-in-my-chrome-developer-dashboard)*2015年07月

>You can't delete an extension from the Chrome developer dashboard.
>
>In the past, there was a delete button at the dashboard, but it has been removed because >developers accidentally removed apps/extensions (and then tried to re-upload the app/extension with the same ID, which failed).
>
>Unpublished extensions/apps do not count toward the extension limit, so there are no consequences for having a lot of unpublished apps/extensions besides the clutter at the dashboard.
>
>If you're really bothered by the entry at your dashboard, create a user style, user script or extension to hide it.


機械翻訳
>Chromeデベロッパーダッシュボードから拡張機能を削除することはできません。
>
>以前はダッシュボードに削除ボタンがありましたが、開発者が誤ってアプリ/拡張機能を削除してから（同じIDのアプリ/拡張機能を再アップロードしようとしたが失敗したため）、削除されました。
>
>未公開の拡張機能やアプリは拡張機能の上限には数えられないため、ダッシュボードの乱雑さに加えて、未公開のアプリや拡張機能がたくさんあることには何の影響もありません。
>
>ダッシュボードのエントリに本当に気をつけている場合は、ユーザースタイル、ユーザースクリプト、または拡張子を作成して非表示にします。

とのことで、できないようです。
cxDeleteItemについても今は動作していないとコメントされています。

## 考察
経緯を推察するに、
1. はじめは削除ボタンがあった。
2. 削除ボタンでトラブルが多かったため、この機能とオミット。ただし、内部的には削除処理のcxDeleteItemを残したままにしてあった。これにより、裏ワザ的に、コンソールで実行できていた
3. cxDeleteItemもいつもまにかなくなっていた
といったところでしょうか。


## どうすればいいの？
削除の代替案として、非公開にするのはもちろん
上記のstackoverflowでは、manifestファイルのバージョンや名前を変えて識別することを提案しています。

```json:manifest.json
{
    "manifest_version":2,
    "name": "（deleted）",
    "version": "0"
}
```

また、削除したい拡張機能を誰もインストールしていない場合、別の実際の拡張機能（Chromeストアには古いバージョンが保存されていない）に置き換えることも可能なようです。


stackoverflowの投稿(2015年7月)からこの記事を書いた2018年2月で2年半たっていますので、削除する方法は今後も提供される期待は薄いと感じます。
Interface 2018年2月号のRISCV記事を試したメモ その２

一応，わすれてはいませんでしたが進捗がなかったので．間が空いてしまいました．

### SCR1_ARCH_RST_VECTOR について
前回の記事で，「なぜだかわからないが0xFFFFFF00にするとよいらしい」と書きましたが，改めて Interface 2018年2月号を確認したら p.112 の左下にちゃんと書いてありました．
リセットベクタというのは，コアが最初に読みに行くアドレスらしいです．start.Sに\_start があって，これがリンカスクリプト make.ld で 0xFFFFFF00 に配置されていました．\_start\_csr とラベルも近くにありますが，CSRは C Startup Runtime のことらしいです．あとは，start.Sのなかのラベルが1とか2とかの数字になっていてビビったり，さらにこれがジャンプ命令のターゲットになるときに[bとかついていて混乱](http://masahir0y.blogspot.com/2012/03/blog-post.html) したりしました．
たった4行を見逃したために1か月かかってしまいましたが，これがなければ `riscv-unknown-objdump -D PROJ_MAX10.elf` で逆アセンブルして確認したりとかすることもなかったとおもうので，かえって良かったと今では思います．

まだ残っている疑問としては，以下の2点があります．
- 割り込みベクタと違って1個なのに，リセット「ベクタ」なのはなぜだろう？
- リンカスクリプトでのセクション配置の指定が，0x00000000から積み上げずにむしろアドレスの大きいほうからさかのぼっていくのはなぜだろう？
このあたり，オフ会に行けたら直接うかがえたのかもしれませんが，都合がつかず無念．

### リスト6 回路シミュレータModelSimの起動と論理シミュレータの実行
vivadoからのテストベンチ実行になりますが，無事GPIOの下位3ビットがXから1になるのが確認できました．

今は手元に書き込み機がないので，まだまだつづきます．


理解が深まったのでよしとしましょう
スマホでGoogleMapアプリの経路を開くリンク

動作サンプルは[こちら](http://jsdo.it/takumifukasawa/mzzf)です。

iOSとAndroidの場合でリンクの貼り方が違いました。

## iOSの場合

URLスキームで指定します

**`comgooglemaps://?saddr=[出発地]&daddr=[到着地]&directionsmode=[手段]`**

- saddr（出発地） ... [経度,緯度]の形式、またはキーワード

- daddr（到着地） ... [経度,緯度]の形式、またはキーワード

- directionsmode（手段） ... driving, transit, bicycling, walking のいずれか

リンクを開く際に、GoogleMapアプリで開くかどうかの確認ダイアログが出てきます。

## Androidの場合

Googleのurlで指定します。

**`https://www.google.co.jp/maps/dir/[出発地]/[目的地]`**

- 出発地 ... [経度,緯度]の形式、またはキーワード

- 目的地 ... [経度,緯度]の形式、またはキーワード

iOSの場合とは異なり、確認ダイアログは開かれずにGoogleMapアプリが開きます。

## 参考

[https://developers.google.com/maps/documentation/ios-sdk/urlscheme?hl=ja](https://developers.google.com/maps/documentation/ios-sdk/urlscheme?hl=ja)

[http://apoc.jp/googlemap-route-link/](http://apoc.jp/googlemap-route-link/)
Cognos Analyticsのファイルのアップロード機能で、元ファイルの更新がどこまで許されるか

#ファイルのアップロードで元ファイルの更新を行う
ファイルのアップロード機能とは、

こんな感じに、1行目に列タイトルが入るようにExcelでデータを用意し、
![002.PNG](https://qiita-image-store.s3.amazonaws.com/0/147246/a915bc69-e91e-fe4f-0206-925268be2798.png)

Cognos Analyticsの「ファイルのアップロード」から、Cognos上にアップロードを行う事で、ダッシュボードの機能などでデータソースとして使う機能です。
![001.PNG](https://qiita-image-store.s3.amazonaws.com/0/147246/08b2958b-c183-1d32-5000-3ccce3061f75.png)

アップロードしたデータは、Cognosのポータル上にエントリーが作成されますが、元のExcelファイルのデータを更新した場合に、以下のような「更新ファイル」のメニューで、再アップロードが可能です。
![004.PNG](https://qiita-image-store.s3.amazonaws.com/0/147246/ff404b63-904a-ec05-febc-adfb7e3c5dfb.png)

この時に、元のExcelファイルにどんな変更を加えても大丈夫なのだろうか、という問いへの回答になります。

いきなりですが、以下の様な結果になります。
![003.PNG](https://qiita-image-store.s3.amazonaws.com/0/147246/0a1de67d-ff2d-a8ac-27e6-4ec1c5918ef6.png)

下から2番目の、「データタイプをDateに変更する」とは、

最初、こんなデータをアップロードしたとします。
売上とかは数値(Number型)として認識されます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147246/dd884640-79a0-18e5-666c-1df595542672.png)

その後、元ファイルをこんな風に変更したとします。
こんな事やる人はいないと思いますが、売上列が日付データになってます。
これをアップロードすると、エラーになる、という事です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147246/dadc09f4-ccac-1d81-bef8-e2b42806700d.png)

ファイルのアップロード機能を使う時には、思い出して頂ければと思います。

#参考
**How to Update an Existing Uploaded File in IBM #Cognos Analytics**
https://www.ibm.com/communities/analytics/cognos-analytics-blog/how-to-update-an-existing-uploaded-file-in-ibm-cognos-analytics/
Elixirのforはpythonのforとは違ったというお話

弊社ではpythonを使った開発が主流ですが、ここ最近はelixirでの開発にも取り組んでおります。
そこでelixirの勉強をしているのですが、現時点で一番ハマったことを共有します。

# 問題
リスト[1, 2, 3, 4, 5]の各要素の合計を求めるプログラムを実装しなさい。
（ただし、Enum.sum()は使わない）

## 実装
pythonで実装したらどうなるかを考えて、それをelixirで書き直せばいいんじゃないかなと思った（のがそもそもの間違いだった）。

```python:sample.py
total = 0
for i in [1, 2, 3, 4, 5]:
    total += i
print(total)
```
これをelixirで書くと・・・（なお、間違いです）

```elixir:sample.ex
total = 0
for i <- [1, 2, 3, 4, 5] do
    total = total + i
end
IO.puts total
```
### 結果
IO.puts total は0でした。

# なぜ？
かなり回りくどい書き方になってしまいますが、私が理解した順番に記述します。

## スコープの問題だった
for の中と外でスコープが異なるため、IO.puts totalのtotalは、forの外側のtotalでした。

## 湧き上がる疑問
じゃぁforの中ではちゃんとリストの要素を足してるんだろうなぁ。

```elixir:sample_2.ex
total = 0
for i <- [1, 2, 3, 4, 5] do
    total = total + i
    IO.puts total
end
```
### 結果
<img width="312" alt="2018年2月-1.png" src="https://qiita-image-store.s3.amazonaws.com/0/98381/050474f8-04a7-4ce2-7078-70e29dc9cc51.png">
（本気で「！？」でした）

# そもそもの誤解

```elixir:generator.ex
for i <- [1, 2, 3, 4, 5] do
    ...
end
```
これはelixirではループではなく、[1, 2, 3, 4, 5]のリストから要素を取り出す、内包表記と呼ばれる記述方法でした。
なのでforの中で足し算するものの、毎回その結果は捨て去られている（という表現が適切かわかりませんが）。
ですので「スコープの問題だった」という見出しを作りましたが、根本的に間違っていたということになります。

# で、elixirで実装するとしたら？

Enum.reduce()を使って

```elixir:reduce.ex
total = Enum.reduce([1, 2, 3, 4, 5], fn(x, acc) -> x + acc end)
IO.puts total
```
と書く感じでしょうか。

# ちなみに封じていたEnum.sum()は？
気になったので見て見たらこのように定義されてました。

```elixir:enum_sum.ex
def sum(enumerable) do
    reduce(enumerable, 0, &+/2)
end
```

## &+/2 とは一体・・・
なんすかこれ！？と思って調べたところ・・・

### &記法
elixirでは無名関数をよく使うため、省略記法として、&記法というものがあるそうです。
&以降の式を関数に変換する演算子とのこと。
なので、「&+/2」は「+/2」という無名関数・・・「+/2」とは？

### +/2
実現させたいのは2つの数の和です。
それは下記のように書けます。

```elixir:summary.ex
fn x, y -> x + y end
```
この無名関数は引数が2つあって、それの単純に足しています。
これをelixirは &+/2 と書けてしまうのだそうです。
（ちなみに「/2」という記述は「アリティが2つ」を意味してます。）

### &+/2と書けてしまう、ではなく・・・
elixirではそもそも「+」という関数が定義されておりました。
https://hexdocs.pm/elixir/Kernel.html#+/2
引数2つの「+」という関数を渡す際に &+/2 と表記するようです。

# 話が脱線してしまいましたが・・・
せっかくelixirで便利な関数が用意されているのだから、使用を禁止せずに素直に使った方が楽ですね（当然ですよねw）。
現行の伺かベースウェアのリスト

# 備考

- 幅広くリストアップするために互換性については無視してます。お察しください

# Windows

## 伺か

- [うさださくら](http://usada.sakura.vg/)
  - 本家本元。長い間、更新されていない

## SSP

- [独立伺か研究施設 ばぐとら研究所](http://ssp.shillest.net/)
- 現在も更新が続けられているベースウェア。Wineと組み合わせてWindows以外のOSで動作させることもできる
- EasyWineと組み合わせてmacOS上でも動作確認
  - Boothで公開中のEasyWineベータ版との組み合わせで正常に動作するとのこと。情報ありがとうございます！

# Unix

## ninix-aya

- [ninix\-aya開発プロジェクト](http://ninix-aya.osdn.jp/)
  - Unix系OSで動作するベースウェア。DLLはモジュールで再現しているらしい
- [Tatakinov/ninix\-kagari: ninix\-ayaのフォーク。](https://github.com/Tatakinov/ninix-kagari)
  - ninix-ayaのフォーク版

# Android

## Androidの伺か

- ~~[Androidの伺か \- Google Play の Android アプリ](https://play.google.com/store/apps/details?id=com.dendenfactory.ukagakaforandroid&hl=ja)~~
- Androidで利用できるベースウェア。一部機能制限はあるもののゴーストのデータがそのまま利用できる
- 公開終了?（2024/02/22）

# iOS

## まめフラスコ

- [「まめフラスコ」をApp Storeで](https://itunes.apple.com/jp/app/%E3%81%BE%E3%82%81%E3%83%95%E3%83%A9%E3%82%B9%E3%82%B3/id452252764?mt=8)
- SHIORI「里々」風の書き方で作られたキャラクターデータが利用できる

# ブラウザ

## 如何か

- [Ikagaka Demo](http://ikagaka.github.io/Ikagaka.demo/)
- 様々なブラウザ上で動作するベースウェア。SAORIが利用できないなど制約はあるものの、ブラウザ上でゴーストが起動する様は一見の価値あり
- electronを使ってWindows上で動くオープンソースのベースウェアを開発するプロジェクトもあります
  - [Ikagaka/ikagaka\-desktop: まずはWindowsデスクトップ用のものをFSやDLLエミュなしで作る方が問題が分割できるという目論見](https://github.com/Ikagaka/ikagaka-desktop)
Classic ASPでJSON出力

##この記事について

Classic ASPをいまだに利用しています。

すでに新規で使うところも無いと思いますが、こんな状況でもClassic ASP用のJSON出力用のモジュールを開発してくれている人がいます。

これでWebAPIを作ってモダンなシステムと連携できるのでありがたいことです。

使い方をメモしておきます。

##ダウンロード

公式 https://github.com/rcdmk/aspJSON

##使い方とサンプルコード

JSONを一から構築する方法とレコードレコードセットから一括構築する方法があります。一から構築する方法は公式を参照。

使い方は簡単で、クラス「JSONobject」のインスタンスを作り、レコードセットを設定して出力するだけです。


出力メソッドは２つあり、`JSON`を使うとトップレベルオブジェクト"data"の値として配列が作られ、`JSONarr`を使うと生の配列が設定されます。この辺も公式に説明があります。

```vb

<!--#include file="jsonObject.class.asp" -->
<%

'jsonObjectを使うための準備
Response.ContentType = "application/json; charset=UTF-8"
Response.LCID = 1041 'ロケールコード 日本


'データベース接続 
dim cn, rs, sql
set cn = CreateObject("ADODB.Connection")
cn.Open "UID=SAMPLE;PWD=PASSWORD;DSN=DATABASENAME"

'SQL
sql="SELECT * FROM SAMPLE"

'データ取得
set rs = cn.execute(sql)

' instantiate the class
set JSON = New JSONobject
JSON.LoadRecordset rs

rs.Close
cn.Close

set rs = Nothing
set cn = Nothing

'JSON書き出し
JSON.Write()

%>
```

## 参考
Classic ASPでAjax
https://qiita.com/hyakuson/items/4ff6b9f8154a34950103
ランニング用のiOSアプリ作ってみた

##なんでつくったの？

増えてしまった体重を減らすためにApple Watchを買ってランニング再開。 
AppleWatch + iPhoneが予想以上にランニングデータを管理してくれて便利。

でも、そのうちサボり始めるよね。

１ヶ月単位でわかりやすく表示できたら計画立てやすいし、サボってたら反省してもっと走れるよね。

というわけで、１ヶ月単位でどんだけ（何日／何キロ）走ったかわかるアプリを作ることにしました。

##最低限必要な機能

・カレンダーを表示して走った日に◯をつける
・デイリーで走った距離を棒グラフで表示
・１ヶ月間の総距離を棒グラフで表示
・走ったデータはiPhoneのヘルスケアから参照

##つかったライブラリなど
- HealthKit
- Charts (CocoaPods)
- FSCalendar (CocoaPods)

最近iOS開発から離れてたのでCarthageって知りませんでした。
でも早くリリースしたかったのでCocoaPodsで。

##環境
Swift4 - iOS11
自分が使うのが目的なので古い環境は対象外。
前はObjective-Cで書いてたのでSwift間の変遷はわかりませんが、メソッド名が短くなった。

##リリース
水曜日夜に申請
木曜日朝リジェクト
木曜日に再申請
金曜日リリース

はやっ。

http://appstore.com/MonthlyWorkout

##使い方
- ワークアウトが登録できるアプリを使って走る（歩く）
- 見る
- 以上

私は「NIKE+ RUN CLUB」を使ってApple Watchあら登録していますが
他のアプリでもワークアウトに距離が登録できればOKだと思います。
Runkeeperとか使ってる人も多いのではないでしょうか。
ちなみに、ポケモンGOでも登録できてました。
画像ファイルがBase64形式に変換されててハマった話し

知らずにハマってしまったので備忘録として残しておきます。

# 画像を表示する方法
ぼくが知っているいつものやり方。

```html
<img src="/img/hoge.jpeg" />
```

こういうやり方もできます。

```html
<img src="data:image/jpeg;base64,Y2QgL3Zhci93d3cvZWNvcmFuZ2UvaHR〜" />
<!-- 途中省略してます -->
```

何をやってるかというと画像データをBase64変換して直接HTMLに埋め込んでいます。

# Base64形式に変換するメリット
1つ目のやり方は画像を表示するためHTTPリクエストしますが、
文字列をHTMLやCSSに埋め込むことでリクエスト回数を減らすことができます。

画像をたくさん表示する場合に非常に有効です。

# デメリットはないのか？
Base64変換するとサイズが30％ほど増えます。
リクエスト回数は減らせますが、大きい画像も変換すると逆効果になるかもしれません。
小さい画像（アイコンとか）だけBase64変換すると効果的でしょう。

# 何にハマったの？
順序がおかしい気がしますが、何にハマったか一応書いておきます。

ぼくが開発を引き継いだサイトである画像を表示してほしいと依頼がありました。
画像パスを取得する関数(PHP)は既にあったので、「じゃあ、この関数呼び出してテンプレートに書くだけじゃん」と思ってました。
でも、やってみると変な文字列しか返されず、「？？？」でしたがBase64変換して直接埋め込めることを知っていればそんな悩まず済んだ話しだったでしょう。

基本的にシステム部分のみ開発してますが、こういうのは知ってないとデザイナーと連携取れないんで知っておくべきですね。
ユーザー登録機能つき投稿サイト➀

1,postモデル,コントローラー作成


```ruby:qiita.rb
1:rails g model post title:string link:string description:text
2:rails db:migrate

3:rails g controller Posts

4:resources :posts
5:root "posts#index"
```

```posts.controller
class PostsController < ApplicationController
    before_action :find_post, only: [:show, :edit, :update, :destroy]
    
    def index
        @posts = Post.all.order("created_at DESC")
    end
    
    def show
    end
    
    def new
        @post = Post.new
    end
    
    def create
        @post = Post.new(post_params)
        
        if @post.save
            redirect_to @post
        else
            render "new"
        end
    end
    
    def edit
    end
    
    def update
        if @post.update(post_params)
            redirect_to @post
        else
            render "edit"
        end
    end
    
    def destroy
        @post.destroy
        redirect_to root_path
    end
    
    private
    
    def find_post
        #自分のコントローラー内のものを見つける時はparams[:id]、自分のコントローラーに無い時(ex:userコントローラー)はparams[:user_id]で参照
        @post = Post.find(params[:id])
    end
    
    def post_params
        params.require(:post).permit(:title,:link,:description)
    end
end

```

```index.html.erb
<% @posts.each do |post| %>
<!--postはpost.showへ行く-->
<p><%= post.id %>:<%= link_to post.title,post %></p>
<% end %>

<%= link_to "新規投稿",new_post_path %>
```

```new.html.erb
<h1>post/new</h1>
<%= render "posts/form" %>
```

```show.html.erb
<h1>title:<%= @post.title %></h1>
<h1>link:<%= @post.link %></h1>
<h1>description:<%= @post.description %></h1>
<h1>id:<%= @post.id %></h1>


<%= link_to "編集",edit_post_path(@post) %>
<%= link_to "削除",post_path(@post),method: :delete,data: {confirm:"削除してもいいですか"} %>
<%= link_to "ホーム",root_path %>
```

```edit.html.erb
<h1>編集<h1>
<%= render "posts/form"%>
```

```_form.html.erb
<%= form_for(@post) do |f| %>
   <p>titl:<%= f.text_field :title %></p>
   <p>link:<%= f.text_field :link %></p>
   <p>description:<%= f.text_field :description %></p>
   <p><%= f.button :submit %></p>
<% end %>
```
12-12/23min
[EC-CUBE3]ブロック追加でPHP触ったら「HTTP ERROR 500」エラー

EC-CUBEで新規ブロックを作ろうと思い、DBやPHPを修正して、いざ確認！と思ったら、画面は真っ白で、管理ページも「HTTP ERROR 500」で全く見えなくなって焦った話。

## 確認したこと
- 追記したDBの値や、PHPの行を削除した。　→　ダメだった。
- EC-CUBEログの確認。ログは /app/log/site*****.log にある。　→　何も出てない。
- [【EC-CUBE3】エラーメッセージとその原因について](https://www.shiro8.net/blog/eccube_report/642.html)を参考に、/html/index.php 29行目の display_errors を On に変更。

```
ini_set('display_errors', 'On');
```
## エラーが出るようになった！
display_errors を On にして、サイトにアクセスすると、真っ白だった画面にエラーメッセージが出た！

```
Fatal error: Namespace declaration statement has to be the very first statement in the script in /(ECCUBE DIR)/src/Eccube/ControllerProvider/FrontControllerProvider.php on line 25
```

## 原因判明
これは、[こちら](http://d.hatena.ne.jp/do_aki/20100201/1265043827)のページに書かれている通りでした。

> 原因は BOM でした。
utf-8 には BOM と呼ばれる unicode であることを示す（かつエンディアンを表す）値が付くことがあります。
これ、通常のエディタでは表示されないので付いているのかどうか見た目にわかりません。
また、phpはこれを無視してくれないため<?php の前にデータがあると見なされ、エラーとなったようです。

notepadでちょちょっと修正したのがまずかったようです。
ブロック追加のため触ったPHPと、エラー出力のために触ったhtml/index.phpをサクラエディタでBOMなし保存することで、無事解決しました。
SwiftLintを変更のあったファイルのみに実行する方法

# SwiftLintの個別ファイル実行方法

公式にも書いてあるRun Script Phaseを少し書き換えてgitのdiff差分で回すだけ。

```sh
if which swiftlint >/dev/null; then
  git diff --name-only | grep .swift | while read filename; do
    swiftlint --path "$filename"
  done
else
  echo "SwiftLint does not exist, download from https://github.com/realm/SwiftLint"
fi
```
- この方法だと、全体に実行しないので、ビルド実行時間が短くなる利点もあり
- `git diff` に ` --cached`を付けるべきかは、開発フローによるのでお好みで
 
# なぜ差分だけに実行？
既存サービスの途中からSwiftLintを導入した場合、膨大な数の警告が出て、lint以外の警告を見逃してしまうため。


本当は、個別ファイルの保存のタイミングで自動実行できると良いのだけど、そこまでは方法わからず。
Xcode Source Editor Extensionを作ればいけるのかな。


# おまけ: `swiftlint autocorrect --format`
re-indentしてくれる。autocorrectするときは、合わせてどうぞ。

```
$ swiftlint help autocorrect 

[--format]
	should reformat the Swift files
```

## 公式サイトのCHANGELOG.md説明
```
--format option to autocorrect command which re-indents Swift files much like pasting into Xcode would.
```
Laravel5で1ページに複数のPaginationを実装する

LaravelのPagination機能を同じ画面で複数使用すると、複数のテーブルで同時にページ移動してしまいます。

```php
public function index()
{
    $users = DB::table('users')->paginate(5);
    $items = DB::table('items')->paginate(5);

    return view('user/index'), ['users', 'items']);
}
```

ページ移動時に``user/index?page=2``というリクエストが送られて、どちらの一覧もpageパラメータを参照するためです。

## Paginationのパラメータ名を変更する
paginateメソッドの引数にパラメータ名を指定することができます。

```php
public function index()
{
    // ユーザー一覧のページ番号をuserpageというパラメータ名に変更
    $users = DB::table('users')->paginate(5, ["*"], 'userpage');

    // アイテム一覧のページ番号をitempageというパラメータ名に変更
    $items = DB::table('items')->paginate(5, ["*"], 'itempage');

    return view('user/index'), ['users', 'items']);
}
```
>参考
https://laravel.com/api/5.3/Illuminate/Database/Eloquent/Builder.html#method_paginate

これで``user/index?userpage=2``または、``user/index?itempage=2``というリクエストとなります。
それぞれの一覧は異なるパラメータを参照するので、同時にページ移動することはありません。

しかし、片方がページ移動をするともう片方は1ページ目に戻ってしまいます。
ページ移動時のリクエストからもう片方のページパラメータが消えてしまうためです。

## Paginationにパラメータを追加する
ページ移動時にもう片方のページパラメータを追加できれば解決します。
追加するにはappendsメソッドを使用します。

```php
public function index()
{
    // ユーザーPaginationにitempageのパラメータを追加する
    $users = DB::table('users')->paginate(5, ["*"], 'userpage')
                 ->appends(["itempage" => Input::get('itempage')]);

    // アイテムPaginationにuserpageのパラメータを追加する
    $items = DB::table('items')->paginate(5, ["*"], 'itempage')
                 ->appends(["userpage" => Input::get('userpage')]);

    return view('user/index'), ['users', 'items']);
}
```
これで``user/index?userpage=2&itempage=3``というリクエストとなります。
それぞれの一覧が独立してページ移動できるようになります。
CocoaPodsのキャッシュクリアして再インストールするコマンドライン

# はじめに
CocoaPodsはキャッシュを持っていて、アップデートしたときにバージョンが不整合になることがあります。

 `pod install --repo-update` した後のビルドで `terminating with uncaught exception of type NSException` などが発生して思い当たる節がない場合、キャッシュの不整合が原因だったりします。

キャッシュクリアするコマンドを備忘のためにメモします。(かなり初歩的な情報ですが、すぐ検索できるように。)

# コマンドライン

Podsディレクトリが入っているディレクトリに移動した上で、次のコマンドラインを実行します。

```
$ rm -rf ~/Library/Caches/CocoaPods/ ; rm -rf Pods ; pod install
```
GitHubのWikiを始める小さなメモ（sshでgit push）

# 手順
１ First Page作成
Wikiタブで「Create the first page」を押下し、保存、作成

![ScreenClip.png](https://qiita-image-store.s3.amazonaws.com/0/50690/3cbedd2a-a29d-7ad8-d8aa-d8ce7192e5e7.png)

２ git clone
以下の例ではHTTPSでclone。あとでoriginを書き換える

```
$ git clone https://github.com/[user]/[RepoName].wiki.git
```

※sshでcloneしても可 

```
$ git@github.com:${ユーザー名}/${リポジトリ名}.wiki.git
```

３ 編集

```
$ cd [repoName].wiki/
$ vim Home.md
```

４ PUSH

```
$ git add Home.md
$ git status
$ git commit -m "Update"
$ git remote -v
$ git remote set-url origin git@github.com:${ユーザー名}/${リポジトリ名}.wiki.git
$ git push origin master
```

# 参考
【GitHub】GitHubのWikiを書く | AdMax Tech Blog http://tech.admax.ninja/2014/10/08/how-to-write-wiki-of-github/

GitHubにSSHでgit pushする方法 - 毎日Learning http://yoshiyoshifujii.hatenablog.com/entry/2014/08/12/230144
IBM CloudのLiberty for Java上のファイルを直接編集する方法

##Liberty for Java上のファイルを直接編集したい
Liberty for Java上のサーバー構成ファイルを直接編集する便利な方法があったのでメモ。

IBM Cloudで稼働中のLibertyサーバーのトレース出力設定を動的に変更するのに、管理画面からGUIを用いて行うことができましたが、先日突然その機能が無くなってしまいました。

どうしたものかと思っていましたが以下の方法を見つけました。
SCPを用いて直接ファイルのアップロードができるようです。

[How to copy files from Liberty on Bluemix to Windows?
](https://stackoverflow.com/questions/41858496/how-to-copy-files-from-liberty-on-bluemix-to-windows)
###SCPの設定方法
以下のパラメータを用いて接続します。

ホスト：各リージョンごとの接続先。イギリスの場合は、ssh.eu-gb.bluemix.net
ポート：2222
ユーザー名：cf:guid/インスタンス番号
パスワード：bx cf ssh-code　で取得したワンタイム・パスワード

guidは以下のコマンドで取得することができます。
bx cf app アプリケーション名 --guid

例えばguidがabc123だった場合、0番目のインスタンスに接続するには以下のようなユーザー名で接続します。
`cf:abc123/0`

###ファイルの更新
SCPで接続できればあとは直接編集するなり、ローカルで編集したものをアップロードするなりすればOKです。
iPhoneX の下のバー(Home Indicator)を常に消す方法 ※実用性はありません！！

# 通常のバーの消す方法

iPhoneXには、下に操作用のバー(Home Indicator)があります。

ViewControllerで以下のように、

```swift
class ViewController: UIViewController {
    
    override func prefersHomeIndicatorAutoHidden() -> Bool {
        return true
    }
}
```
prefersHomeIndicatorAutoHiddenをオーバーライドして、trueを返すと、バーを非表示にすることができます。
ただし、画面の下部を操作すると、バーが一定期間再表示されます。

※ 詳しくは、[Developers.IO - iPhone XのHome Indicatorの表示/非表示を制御する](https://dev.classmethod.jp/smartphone/iphone/iphone-x-dealing-with-home-indicator/)にて、紹介されています。

# 無理やりバーを消えたように見せる方法

上記とは異なる方法で、下のバーが常に表示されていないよに、見せる方法があります。
審査に引っかかると思いますが、なんとなくやってみたらできたので紹介します。

個人専用のアプリを作成するときは、使えなくもないかも知れません。

方法としては、黒背景の画面上に、白色のバーをいい感じに配置すると、Home Indicatorが見えないようになるといった、しょぼい方法です。

## 手順

(1) 画像の様に、黒背景の画面を用意して、Home Indicatorが表示される位置(以下に記載)に白いViewを設置します。

    * size:128 x 5 
    * postion:中央寄せ、画面したから8pointに制約を設置

<StoryBoard上>
![スクリーンショット 2018-02-16 15.28.28.png](https://qiita-image-store.s3.amazonaws.com/0/68288/9e31d9c7-61e3-937c-08d7-4b646565313e.png)

(2) iPhoneXでアプリを表示する

通常のバーを消す方法とは違い、画面の下部を操作しても、バーは表示されません。

![スクリーンショット 2018-02-16 15.35.09.png](https://qiita-image-store.s3.amazonaws.com/0/68288/6f39a1fc-696d-da47-6c4e-4c7ac510f4a5.png)

特に使い道は、ありませんが個人専用のアプリを作成するときに、バーを表示させたくないときに使ってみてください。
※ 私は、自分専用の音楽プレイヤーに使ってみました。
  バーが表示されないので、少しスッキリした感じになります。
HoloLensで手の位置をトラッキングする

HoloLensでジェスチャー処理をするときによく使うのは[GestureRecognizer](https://docs.unity3d.com/ScriptReference/XR.WSA.Input.GestureRecognizer.html)です。これを使えばair-tapやドラッグのジェスチャーを簡単に処理することができます。
しかしこれだけではジェスチャーをしていない時に手がどこにあるかをトラッキングできません。ジェスチャーをしていないときに常に手の位置を把握しようと思ったら、[InteractionManager](https://docs.unity3d.com/ScriptReference/XR.WSA.Input.InteractionManager.html)を使うと良いです。このクラスはインスタンス化することなく`static`に使用します。

`InteractionManager.InteractionSourceUpdated`に登録するとHoloLensが手を検出している間、手の位置が変更されたりする度に通知が呼び出されます。このイベントハンドラ内で手の位置を取得すれば、ジェスチャーをしているかどうかに関係なく常に手の位置をトラッキングできます。
`InteractionManager.InteractionSourcePressed`,`InteractionManager.InteractionSourceReleased`で指の上げ下げのタイミングが拾えます。`InteractionManager.InteractionSourceLost`で手が視界から消えたタイミングが拾えます。

簡単な例として、手の位置に球のオブジェクトを表示するサンプルを下記に載せます(シーン内にSphereという名前の球オブジェクトが存在する前提)。

```csharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.WSA.Input;

public class HandPosition : MonoBehaviour
{
  public GameObject target;

  void Start()
  {
    InteractionManager.InteractionSourceUpdated += InteractionSourceUpdated;
    InteractionManager.InteractionSourcePressed += InteractionSourcePressed;
    InteractionManager.InteractionSourceReleased += InteractionSourceReleased;
    InteractionManager.InteractionSourceLost += InteractionSourceLost;
  }

  // HoloLensが手を検出している間、位置が変わったりするたびに呼ばれる。
  private void InteractionSourceUpdated(InteractionSourceUpdatedEventArgs ev)
  {
    // 手の位置に置くオブジェクトがnullでなければ
    if (target != null)
    {
      // 手の位置を取得して
      Vector3 position;
      if (ev.state.sourcePose.TryGetPosition(out position))
      {
        // オブジェクトの位置を手の位置に更新
        target.transform.position = position;
      }
    }
  }

  // air-tap の指を下ろした時に呼ばれる
  private void InteractionSourcePressed(InteractionSourcePressedEventArgs ev)
  {
    // シーンの中にある Sphere というオブジェクトを手の位置に持ってくるようにする
    target = GameObject.Find("Sphere");
  }

  // air-tap の指を上げた時に呼ばれる
  private void InteractionSourceReleased(InteractionSourceReleasedEventArgs ev)
  {
    target = null;
  }

  // 手が視界から外れた時に呼ばれる
  private void InteractionSourceLost(InteractionSourceLostEventArgs ev)
  {
    target = null;
  }
}
```
ssh周り



##keyのpassphraseを変更したい時

```
$ ssh-keygen -f [key] -p
Enter old passphrase:
Enter new passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved with the new passphrase.
```


```
$ ssh-add -D
All identities removed.

```



## host nameを変更したい時

```
vi /etc/sysconfig/network
```

```
HOSTNAME=*****
```

これでOSなりネットワークを再起動。
LaTeXで(文中に)ローマ数字を使う方法

毎回頭を悩ませて、なかなかすぐに答えに辿りつけないので自分用にメモ。

## otfパッケージを使う方法
使えるならば一番スマートで簡単な方法

```tex:otf_roman.tex
¥usepackage{otf}

¥ajRoman{1}
¥ajroman{2}
```

## counterを使う方法
otfパッケージが使えない場合など。比較的有名な回避法らしい。

```tex:counter.tex
¥newcounter{num}

¥setcounter{num}{1}
¥Roman{num}, 

¥setcounter{num}{2}
¥roman{num}
```
なお、カウンターは使う度に数字を代入しないといけず、めんどくさいのでマクロをかぶせるとまだ使いやすい.

```tex:counter_macro.tex
¥newcounter{num}
¥newcommand{¥Rnum}[1]{¥setcounter{num}{#1} ¥Roman{num}}
¥newcommand{¥rnum}[1]{¥setcounter{num}{#1} ¥roman{num}}

¥Rnum{1}
¥rnum{2}
```

## アルファベットで打って文字間隔を調整する方法

最もめんどくさい方法、だが、検索するとよくヒットする方法でもある。
セレクトボックスで項目を選択後にIEだけ青色に反転するのを修正する方法

メモ

```scss:scss

select {
  &::-ms-value {
    background: none;
    color: #000;
  }
}
```

##おまけ
選択中の項目の色を変更する方法（IEだけ）

```scss:scss

select {
  &::-ms-value {
    background: none;
    color: #000;
  }

  option {
    &:hover,
    &:checked {
      background: red;
    }
  }
}
```
【GSuite】 GoogleAppsScriptを用いて、ユーザごとのデータ使用量を取得する

#なぜユーザごとのデータ使用量が必要になったのか
　GSuiteの料金プランは大きく分けて３つある。
　・Basic
　・Business 
　・Enterprise 

Basicの場合には(メールとドライブの合計の)使用可能量が30GB決まっています。
その上限を超えてしまうとメールが受信できないといった問題が発生してしまいます。

上限を超える前に利用者に通知を行うために取得を行うことを考えました。
＃プランをBusinessに変更すれば解決するので、アップグレードした方が早いし・・・
　そもそもメールやファイルが30GB行く前に整理しろよ・・・・

#実際に実装開始
「Reports API」といううのを使えば行けそうやな・・・
[公式リファレンス](https://developers.google.com/admin-sdk/reports/v1/reference/)

しかし、問題が発生
**情報がない。しかも公式リファレンスが英語しかない⇒読めない**
そうだよね！アップグレードした方が早いんだもん！

グーグル翻訳を片手に実装を行いました。

```GAS:コード.gs
/**
* return:2次元配列
* [[名前,使用量],・・・]
*/
function getUserReports(){
  //取得パラメータ指定
  // accounts:used_quota_in_mb ⇒ 使用量
  // accounts:admin_set_name   ⇒ フルネーム
  var options = {parameters:"accounts:used_quota_in_mb,accounts:admin_set_name"};

  var date = new Date();
   //2日前の日付を設定(それ以降の日付だとレポートが作成されていないことが有る。) 
  date.setDate(date.getDate()-2);

  usedDate = Utilities.formatDate(date, 'Asia/Tokyo', 'yyyy-MM-dd');
  reports = AdminReports.UserUsageReport.get("all", usedDate, options).usageReports;
  
  var cells = [];
  for(var i = 0;i < reports.length;i++){
    var rows = [];
    var paramArray = userdMbReports[i]["parameters"];
    var fullName = paramArray[0]["stringValue"];//名前
    var usedMb = paramArray[1]["intValue"];//使用量

    rows.push(fullName);
    rows.push(usedMb);
    cells.push(rows);
    }
  }
  Logger.log(cells)
  return cells;
}
```
足りない情報が多々あるため、
適宜追記していきます。


#最後に

主にこちらの記事を参考にさせていただきました。
https://qiita.com/ttyokoyama/items/5df1d9ad98eaac0a1a8b
｢Google Home｣でいろいろやってみた。 -準備編-

話題のGoogle Homeでいろいろ遊んでみたくなったので、気ままにやってみた。

#Google Homeでできること
1．音楽を再生する
2．調べ物をする
3．家のデバイスを操作する

#初期設定
AIスピーカー「Google Home mini」の初期設定をしてみる。
###準備するもの
・Google Home mini 本体
<img src="https://qiita-image-store.s3.amazonaws.com/0/190792/b2183b3d-52ec-d2ad-9361-e9e6f8ce4276.jpeg" height=25%; width=25%;>


・Google Home アプリ
<img src="https://qiita-image-store.s3.amazonaws.com/0/190792/341b5e64-cb7a-3233-34b3-2ee4dfebb06f.png" height=25%; width=25%;>

・Wi-fi環境

・Googleアカウント

###手順
####1．Google Home miniを電源に接続する

####2．スマホからGoogle Home アプリを起動
<img src="https://qiita-image-store.s3.amazonaws.com/0/190792/851cd42a-317e-8eb6-cfaf-248ae21e7a83.png" height=25%; width=25%;>

項番3以降、Google Home アプリを操作

####3．Googleアカウントでログイン
<img src="https://qiita-image-store.s3.amazonaws.com/0/190792/c1c0f72d-17b9-e529-aee3-7560e1824373.png" height=25%; width=25%;>

####4．Wi-fiを登録する
使用するWi-fiを登録します。
<img src="https://qiita-image-store.s3.amazonaws.com/0/190792/23be645d-3316-2c54-f54a-a3560fd6c031.png" height=25%; width=25%;>

同Wi-fi内にGoogle Homeが存在すれば、以下の画面になります。

<img src="https://qiita-image-store.s3.amazonaws.com/0/190792/9bbfe5d6-d2a2-764a-2df3-6b7b04a6fe06.png" height=25%; width=25%;>

####5．音声による同期
音声が聞こえれば、Google Homeとの連携されました。
<img src="https://qiita-image-store.s3.amazonaws.com/0/190792/e2d3ceea-f7c8-9152-087a-ce6e78ca660b.png" height=25%; width=25%;>

####6．接続完了
ここまででGoogle Homeとスマートフォンの接続は完了です。
<img src="https://qiita-image-store.s3.amazonaws.com/0/190792/2a064168-713e-8d33-7e66-074e5404c965.png" height=25%; width=25%;>

####7．Voice Matchの登録
Voice Matchとは、ユーザーのGoogleアカウントとユーザーの声のリンクします。
画面表示に従って「Ok,Google」、「ねぇ、Google」と発声します。

<img src="https://qiita-image-store.s3.amazonaws.com/0/190792/8681c07e-a042-ab1b-cb85-8f51d16305ce.png" height=25%; width=25%;>

<img src="https://qiita-image-store.s3.amazonaws.com/0/190792/193f2c9b-290f-0ade-77f0-eed861e7ac12.png" height=25%; width=25%;>


####8．Voice Match登録完了
以下の画面表示でVoice Matchの登録は完了です。
<img src="https://qiita-image-store.s3.amazonaws.com/0/190792/d041fd29-6e23-f43c-9084-7b40ae3caa12.png" height=25%; width=25%;>

####9．位置情報の登録
位置情報は任意です。
<img src="https://qiita-image-store.s3.amazonaws.com/0/190792/bb2a3951-9061-8a4e-ba75-5b62a3d4fe44.png" height=25%; width=25%;>

#サービスとの連携
今回は音楽配信サービス「Spotify」と連携してみた。
○「Spotify」
https://www.spotify.com/jp/


「Spotify」を選ぶ。
<img src="https://qiita-image-store.s3.amazonaws.com/0/190792/25ed8d84-55f4-a5f3-8b89-9397ade0f566.png" height=25%; width=25%;>

「Spotify」のアカウントでログインする。
<img src="https://qiita-image-store.s3.amazonaws.com/0/190792/2f4799d3-d582-0df1-3307-92ad5208e0db.png" height=25%; width=25%;>


「Spotify」アカウントと同期が成功すると、"ご利用中の音楽サービス"に「Spotify」が追加される。
<img src="https://qiita-image-store.s3.amazonaws.com/0/190792/a58e99e1-6239-d46d-aea9-f1ad8105ee72.png" height=25%; width=25%;>

無事、音楽を再生することができた。

#所見
基本的に画面の説明通りに進んでいけば、簡単にGoogle HomeとGoogle Homeアプリの同期は簡単だった。
Googleアカウントは必須だが、Androidの人は持ってるので準備はほぼ必要なさそう。

多くのサービスと連携できるが、サービスそれぞれにアカウントが必要となるので、各サービスに新規登録していくのは骨が折れる…。

#次回予告

「Google Home」と「IFTTT」を使っていろいろやってみる。
GoogleHomeアプリアップデートのお話

GoogleHomeアプリを一旦リリース後アップデート対応をする事になると思いますが、その際にGoogleによるレビュー突破後に反映される事柄とレビュー突破前(即時反映)に反映される事柄があるので、簡単に纏めてみました。
なお、本記事はDialogflowでアプリを作成した場合のお話です。

#即時反映されるもの
- webhook
    - これは言わずもがな自分のサーバーなので処理を変更すると即時反映されます。
- 通常のIntent
    - DialogflowコンソールでIntentを編集すると即時反映されます。
        - 例えばText responseを変更した、User saysにフェーズを追加した等。
- Entities
    - DialogflowコンソールでEntitiesを編集すると即時反映されます。
        - 路線Entitiesに京浜東北線を追加した等。

#即時反映されないもの
- ちょっと特別なIntent
    - GoogleAssistantにIntegrateする際に設定するIntentはレビューを突破しないと反映されません
        - 例えば「京浜東北線の運行情報は」で自身のアプリをおすすめしてくれるようにImplicit invocation Intentを設定してもすぐには反映されません。(テストアカウントでサインインしてる端末では反映されます)
- Actions on Googleコンソールで設定するもの
    - アイコン、アプリの説明等はレビューを突破しないと反映されません。
    - Dialogflow側で設定したと思ってたImplicit invocation Intentの設定がOverView->Action discovery and updatesに有るのが味噌ですね。

#所感
- レビュー突破後に結構な箇所を自由に変えれるので、レビューにどこまで意味があるのだろうか？
- アプリの使い方等を説明文で出していて、アップデートで大きく変わる場合等説明文と実際の動作が不一致する期間が発生してしまう。

#纏め
![即時反映２.jpg](https://qiita-image-store.s3.amazonaws.com/0/200860/3271f8ef-11ec-4f27-42cc-2c4e6b330cc3.jpeg)

- 即時反映されるのは右２つのDialogflow、Webhookで担当する部分。
- Implicit invocation Intent等はDialogflowでIntegrateした際にActions on Googleに登録するイメージ。

#参考
https://medium.com/@leonnicholls/google-assistant-trivia-game-742f38cae5de