OmniAICreator/Qiita-1.07M huggingface dataset:
【Ruby on Rails】springを再起動する

# springとは
今回はspring再起動の方法の紹介がメインなため詳細については割愛します。[Railsアプリケーションのプリローダー](http://ruby-rails.hatenadiary.com/entry/20141026/1414289421)。
開発中に何度も実行するような`rails`などのコマンドを素早く実行することができるようになる。

# 再起動する
初めに、springが起動しているのを確認する。

```
lime:sample_app residenti$ bin/spring status
Spring is running:

25683 spring server | sample_app | started 26 mins ago
25684 spring app    | sample_app | started 26 mins ago | development mode
```

次に、springを停止する。
念のため、ちゃんと止まっているのかを確認。

```
lime:sample_app residenti$ bin/spring stop
Spring stopped.

lime:sample_app residenti$ bin/spring status
Spring is not running.
```

最後に、springを起動し、動いていることが確認できればOK。

```
lime:sample_app residenti$ bundle exec rails c
Running via Spring preloader in process 26080
Loading development environment (Rails 5.0.3)
Cannot read termcap database;
using dumb terminal settings.
irb(main):001:0> exit

lime:sample_app residenti$ bin/spring status
Spring is running:

26067 spring server | sample_app | started 14 secs ago
26068 spring app    | sample_app | started 14 secs ago | development mode
```
Slack Botで匿名チャット

# Slack Botで匿名チャット

## 背景
チームや組織で仕事をしていると「あ、これ改善できそう」、「この運用はどうだろうか」、「これはおかしいだろう」なんてことが多々あるかと思います。

そんなときに、実名のチャットでは「あいつ、こんなこと言ってるけど、云々」、「でも、これは仕方がない」、「これを認めると立場的になぁ」といった具合にコミュニケーションがしづらい場面が出てきます。

そこで、匿名チャットを作って各自の意見を垂れ流しにしてはどうかと思いました。

垂れ流しにされた意見が時として自分の作業の改善につながったり、スキルアップにつながることもあるはずです。

興味のある方はぜひ触ってみてください。

他にもっと良さげな方法等ありましたら、コメントお願いいたします。

## Slackでの準備

### Botを作成する
* https://my.slack.com/services/new/bot

### API Tokenを確認する
* 後ほど環境変数として使用します

### 投稿用のChannelを作成する
* 作成したBotをInviteします

### 共有用のChannelを作成する
* 作成したBotをInviteします
* チャンネルのidを確認します
	* ブラウザのデベロッパーツールでチャンネルの要素を検証し、data-channel-id="チャンネルid"を確認します
  * 後ほど環境変数として使用します

### Botアプリをデプロイします
* 今回はHerokuを使用します
	* CLIを使って設定します
	* [Getting Started on Heroku with Node.js](https://devcenter.heroku.com/articles/getting-started-with-nodejs#introduction)で初期設定の方法を確認してみてください
* また、無料プランでは30分アクセスがないと停止してしまうのでご注意ください
	* https://www.heroku.com/pricing

```
$ git clone https://github.com/hirokisan/slack-anony-bot.git
$ cd slack-anony-bot
$ heroku create YourAppName
$ heroku config:add SLACK_API_TOKEN="先ほど取得したAPI Token"
$ heroku config:add ANONYMOUS_CHANNEL_ID="先ほど確認したチャンネルのid"
$ echo 'web: node index.js' >> Procfile
$ git add . && git commit -m "Add Procfile" && git push heroku master
```

## 参考
* [slack](https://slack.com/)
* [slackapi/node-slack-sdk](https://github.com/slackapi/node-slack-sdk)
* [What is the simplest way to find a slack team ID and a channel ID?](https://stackoverflow.com/questions/40940327/what-is-the-simplest-way-to-find-a-slack-team-id-and-a-channel-id)
* [Getting Started on Heroku with Node.js](https://devcenter.heroku.com/articles/getting-started-with-nodejs#introduction)
悩める子羊よ、Emacsを使いなさい。

Emacsには教会がある。
Emacsの作者たるリチャード・ストールマンは自らをSt IGNUcius(自身が起こしたGNUと名前を掛けてある。ダジャレめいた！すごい！)
そんなEmacs教会にいつの間にか入神し情報を発信する渡しから悩める子羊に２つのグッドニュースです。
Qiitaを使っているということはきっとみんな怠惰で傲慢で憤怒のプログラマなのでしょう。
だからこそおすすめのパッケージですよ

## Migemo(ローマ字で日本語を検索する)
日本語と英語を織り交ぜて文章書いたりコードを書いたりしているとどうしても日本語を検索したいのに現在の入力モードが英語！という時がある。
これは仕方ない。どうしてもそうなる。でも英語一本に絞って文章を書くのはきついし...。面倒事を受け入れるしかないかと思ってたんだけどさすがEmacs。なんでもありやね。

    func main(){
      //なんかのコメント <<これを検索したいけど今の入力は英語...
      fmt.Println('hello world')
    }

Migemoはこんなときに役に立ちます。
設定さえしておけば`C-s`でローマ字で検索すればそのまま日本語を検索してくれるという親切っぷり。
この場合はkorewoと打てばこれをが引っかかります！
本当になんでもあり。


## regexp-tool
正規表現が好きな人ってどのくらいいるだろうか？
僕はあのメタ表現はどうにも得意になれない。
いつもプリントでバッグをして結果を確かめてしまう弱卒なんだけどEmacs教会には悩める子羊を救ってくれる素晴らしいパッケージが存在する。
`regex-toop`という名前のパッケージは起動することで正規表現の結果をリアルタイムに出力してくれる。
リアルタイムに結果が出るということはもうプリントでバッグとは永久にサヨナラできるということで時間の短縮につながるから本当にありがたいし正規表現をいろいろ実験することができるREPL的環境でもあるので学習にも役に立つ。
圧倒的聖者の行進めいたパッケージに惚れ惚れした。


あとがき
---
一体どれだけ先進的なアイデアで満たされたエディタなんだと思って仕方ない。
Emacsを構成しているLispは50歳で2倍くらいの歴史がある。Emacsも40年近い歴史がある。
僕が小学校の時は一太郎スマイルとかのワードとかのソフトが全盛で今のようにGoogleドキュメントなんかがあった時代ではなかった。必ず上書き保存をしましょうねと先生に習った記憶がある。2010年にMacOSXでオートセーブ機能がついてすごい！と思ったけれどEmacsはそもそも保存しなくても勝手に保存してくれる...。40年以上も昔のソフトにそんなものがあるなんて...。
幾つかのエディタを利用してきたが日本語の検索にローマ字が利用できる機能を見たことは一度もないし、正規表現をリアルタイムで試すことができるなんて知らない。ブラウザやターミナルが取り込まれたエディタはよく見るけれど一瞬でファイルを起動できるソフトは知らない。
どれもEmacsには当たり前にあって他にはない機能が多い。
わたしはEmacsの伝道師としてこれから密やかに普及活動もとい布教活動をしていきたいと思う。
エディタ一つでこんなことを思うなんてすごいや
リキッド＆レスポンシブ対応のHTMLメールをつくる（gulp）

# リキッド＆レスポンシブ対応のHTMLメールをつくる

リキッドとレスポンシブ対応のHTMLメール用gulp開発環境をつくった。
Githubにサンプルメールとともにアップ。ほぼ大抵のメーラーでうまく表示できる。


## Githubと対応メーラー 

https://github.com/takadaeri/responsive_mail

- Outlook2010(win7)： 固定幅表示/リキッドとレスポンシブ非対応のため
- Gmail(mac)： リキッドのみ/レスポンシブ非対応のため
- thunderbird(mac)： リキッドとレスポンシブどちらも正しく挙動する
- iOS 標準メーラー(iOS10)： リキッドとレスポンシブどちらも正しく挙動する
- Android 標準メーラー(Android4.3)： リキッドとレスポンシブどちらも正しく挙動する
- Android Gmail(Android6)： リキッドとレスポンシブどちらも正しく挙動する


## あるあるバグ＆仕様一覧

### outlook2010 

- レンダリングがwordモード
- 背景画像がひけない
- リキッドもレスポンシブもできない
- line-heightの指定に注意（※mso用のcssを記載する必要がある）
- paddingがおかしい（marginはOK / https://www.campaignmonitor.com/css/　←こちらのサイトで使えるCSSは確認できる）
- floatが効かない
- cssの角丸などが効かない
- インラインブロックが効かない
- インライン要素へのスタイル描画が他メーラーと異なる
- HTMLメールが長すぎる場合、wordでいう改ページ部分で画像やテキスト関係なく白い区切りが入る
- 現状は、ボタンなどaタグのクリッカブルエリアをボタン全体とするには、aタグのborderで背景分を塗る or VMLタグを使うなどの工夫が必要

### Gmail

- メディアクエリが効かない（なのでレスポンシブが効かない / リキッドはOK）

### Thunderbird、Android 標準メーラー、iOS標準メーラー

- 特に問題なし


## 以上を踏まえての組み方  

- tableコーディングが基本。
- リキッドもレスポンシブもできないOutlookのために、mso用のコメントタグでソースを出しわける（例えば、Outlookではtableタグを採用して、その他のメーラーはdivへのスタイルが効くのでdivで対応するなど）。リキッドエリアでは、上記をネストしていくことでOutlookでも他メーラーでも、正しく表示することができる。
- line-heightの指定は、<td>タグに入れるのが良い。その際、Outlookでもline-heightを正しく表示させるためには mso-line-height-rule:exactly; を忘れずに記載する。
- メール全体をtableで囲むと、「ぶち抜きレイアウトに対応できない」「Outlookの改ページバグ(※1)に遭遇する」などが起きるため、モジュールごとに囲いのtableを区切るやり方を推奨する。通常のWEB制作におけるCSS設計などと一緒で、モジュールごとに構造を分解しておくことで、どんなデザイン、どんなレイアウトでも、素早く対応＆流用ができる。
- ボタンは、クリックエリアをテキストエリアのみでいい場合はtableタグの中のテキストをaタグで囲う構造が全てのメーラーで同じ表示となって良い。クリッカブルエリアをボタン全体としたい場合は、素のスタイルtableタグ内のaタグにpadding　or borderで余白を持たせるなどが必要になる。paddingを使用する場合、Outlook2010と他メーラーでは若干のpx数の違いが出る。クリッカブルエリアは全体としたいが、余白も正確にしたい場合、テキストにアンチがかからなくて良ければOutlookのみVMLタグによるボタンとするやり方もある。
- gulp環境では、通常のWEB制作と同じく、よく使うインラインスタイルなどは変数に持たせて共通化している。これによって一気に修正ができる。


## HTMLメール参考サイト

https://www.campaignmonitor.com/css/ 

今後も追記する、かも。おしまい。
UIパターンをより見やすく表示するam-coffee-timeの機能拡充をしました

# この記事は古いです
最新は以下レポジトリでご確認できます
https://github.com/ampcpmgp/am-coffee-time


# 過去ログ

am-coffee-timeを0.9.1に上げ、便利機能を増やしたので共有します！

お試しサンプル
https://ampcpmgp.github.io/amdev/modules/am-coffee-time/web/



## 今回追加した機能
* ツリー構造を見やすくした
* ツリー構造を開閉するボタン設置
* 階層ごとの一括開閉キーイベント設置
* parent nodeに紐づく`default` propertyの仕様追加

## 前回の見た目
![image.png](https://qiita-image-store.s3.amazonaws.com/0/85111/d6722f75-7aca-fe37-4765-46400276160c.png)


## 今回の見た目と動き
### ![coffee-time2.gif](https://qiita-image-store.s3.amazonaws.com/0/85111/24b10fb8-27b3-7580-7f6d-648bbb34339e.gif)


## 今回の意気込み
以前から、デザイナの方が見た瞬間に「うわっ、このUIひどい」と言ってくるので、直してみました。

これを導入後にドヤ顔するのが楽しみです。

---

レポジトリ
https://github.com/ampcpmgp/amdev/tree/master/modules/am-coffee-time
react-navigationでボタンを連続タップ時に画面遷移が複数回起こらないようにする

#目的
react-navigationでボタンを連続でタップすると、タップした分だけ画面遷移をしてしまう現象を回避する。
※普通につくってたらあまり起きない現象かもしれないが、Viewのレンダーに時間がかかったりすると、ボタンおしてから遷移するまでに時間がかかって連続タップされちゃう。disableにしたりする方法もあるかもしれないが、今回はrouterの設定で頑張る。

#前提
```
$ date
2017年 7月  3日 月曜日 14:46:57 JST

$ react-native -v
react-native-cli: 2.0.1
react-native: 0.45.1

$ cat yarn.lock | grep "react-navigation"
react-navigation@^1.0.0-beta.11:
  resolved "https://registry.yarnpkg.com/react-navigation/-/react-navigation-1.0.0-beta.11.tgz#4271edb23cdbcc6eb88602f7fde0a77f0ef7a160"
```

#問題
以下のようなnavigatorの設定でボタンを連続タップすると、複数回画面遷移を起こしてしまう。

```AppNavigator.js
const AppNavigator = StackNavigator({
    Home: {
        screen: Home,
    },
    MyPage: {
        screen: MyPage,
    }
})
export default AppNavigator;
```

```button.js
<TouchableOpacity
    onPress={() => {
        // 遷移先のレンダーに時間がかからないものだと一瞬で画面遷移がおこってしまうのでわざと遅らせる
        setTimeout(() => {
                this.props.navigation.navigate('MyPage');
            }, 1000)
        }
    }
    <Text>
        Tap quickly twice!
    </Text>
</TouchableOpacity>
```

![758587733f8a1116b4a8aa3fb5e264b5.gif](https://qiita-image-store.s3.amazonaws.com/0/7157/897c0555-f08d-d4a7-02cd-60f9ad126156.gif)



#回避策
ナビゲーション状態ロジックをオーバーライドし、移動先のパスと移動元のパスが一緒だったら画面遷移を無効にする。


```AppNavigator.js
const AppNavigator = StackNavigator({
    Home: {
        screen: Home,
    },
    MyPage: {
        screen: MyPage,
    }
})

// ここから追加
const navigateOnce = (getStateForAction) => (action, state) => {
  const {type, routeName} = action;
  return (
    state &&
    type === NavigationActions.NAVIGATE &&
    routeName === state.routes[state.routes.length - 1].routeName
  ) ? 
　// 1個前のパスと同じだったら何もしない
　null : 
  // デフォルトの動作
　getStateForAction(action, state);
};

AppNavigator.router.getStateForAction = navigateOnce(AppNavigator.router.getStateForAction);
// ここまで追加

export default AppNavigator;
```

```button.js
<TouchableOpacity
    onPress={() => {
        setTimeout(() => {
                this.props.navigation.navigate('MyPage');
            }, 1000)
        }
    }
    <Text>
        Tap quickly twice!
    </Text>
</TouchableOpacity>
```

![aa.gif](https://qiita-image-store.s3.amazonaws.com/0/7157/9b1c9bc6-5ea7-7849-3e08-c649188a6417.gif)

（動作検証に使ったコード https://github.com/kuniiskywalker/react-native-prevent-navigating-twice-clicking-button-quickly）


# あとがき
足りてないところもあるなと感じつつも、めちゃくちゃ便利なreact-navigationなので、色々知見をためていきたい。

#参考リンク
https://github.com/react-community/react-navigation/issues/271

（ナビゲーション状態ロジックのオーバーライドのやり方 https://reactnavigation.org/docs/routers/ より引用）
指定日時で要素の表示・非表示を切り替える

セールとか長期休暇とか指定の期間だけバナーを表示したいことがある。そんな時用の表示切替jsです。

- 要素にclass="switch"を指定し、displayを切り替えます。
- startとendに「`yyyy/MM/dd HH:mm:ss`」の形式で日時をセット。
- 切り替える要素はリストでも画像でもよく、いくつでも増減できるのがミソだけど、表示・非表示によるデザイン崩れの可能性は念頭に置いておく。

```switch.html
<!--
class="switch"の要素に開始日時・終了日時を指定しておく。
ex)start="2017/10/10 10:00:00" end="2017/11/11 11:11:11"
-->
<ul>
	<li class="switch" start="2017/01/01 00:00:00" end="2017/01/03 23:59:59">
		<img src="image.jpg">
	</li>
	<li class="switch" start="2017/01/01 00:00:00" end="2017/12/31 23:59:59">
		<img src="image.jpg">
	</li>
	<li class="switch" start="2017/07/01 12:34:56" end="2017/07/14 20:30:40">
		<img src="image.jpg">
	</li>
</ul>
```

```switch.js

// 指定期間内だけ要素の表示をする

window.addEventListener(
	"DOMContentLoaded",
		function(){
		var now = new Date();
		var switch = document.getElementsByClassName("switch");
		var switch_length = switch.length;

		// 開始日時より後、かつ終了日時より前の期間は表示
		for( var i = 0; i < switch_length; i++ ) {
			var start = new Date(switch[i].attributes["start"].value);
			var end = new Date(switch[i].attributes["end"].value);

			if(now > start && now < end){
				switch[i].style.display = "block" ;
			}else{
				switch[i].style.display = "none" ;
			}
		}

	}, false);

```
Python3 シェル呼び出しの最大文字数 (OS毎)

シェルスクリプトで書かれたプログラムをPythonで置き換える際に一部シェルコマンドをPythonから呼び出す必要があったのですが、シェル呼び出しにはOS毎に異なる文字数制限があるようなので調査を行いました。

# 調査対象のコマンド
* os.system()
* subprocess.run(shell=True)

# 調査対象のOS
* OSX Sierra
* Windows 7
* Ubuntu 16
* CentOS 7

# 調査に使用したスクリプト
## os.system()
```Python
import os
i = 100000000
n = 0
while True:
    n += i
    arg = 'b' * n
    command = 'echo "{arg}"'.format(arg=arg)
    result = os.system(command)
    if result != 0:
        if i == 1:
            break
        n -= i
        i //= 10
print("LENGTH: ", n -1 + 7) # echo、カンマ、スペースの分を足す
```

## subprocess.run(shell=True)
```Python
import subprocess
i = 100000000
n = 0
while True:
    try:
        n += i
        arg = 'b' * n
        command = 'echo "{arg}"'.format(arg=arg)
        subprocess.run(command,shell=True)
    except OSError:
        if i == 1:
            break
        n -= i
        i //= 10
print("LENGTH: ", n -1 + 7) # echo、カンマ、スペースの分を足す
```

# 調査結果

## OSX sierra (Python3.6.1)
Python3はhttps://www.python.org/ からダウンロードした物

|コマンド|os.system|subprocess.run|
|---|---|---|
|最大文字数|260554字|260549字|

## Windows7 (Python3.6.1)
Python3はhttps://www.python.org/ からダウンロードした物

|コマンド|os.system|subprocess.run|
|---|---|---|
|最大文字数|8160字|32733字|

## Ubuntu16.04 (Python3.5.2)
Python3は標準のリポジトリに入っているもの

|コマンド|os.system|subprocess.run|
|---|---|---|
|最大文字数|131071字|131071字|
## CentOS7 (Python3.6.1)
Python3はIUS communityのリポジトリからインストールした物

|コマンド|os.system|subprocess.run|
|---|---|---|
|最大文字数|131071字|131071字|


# subprocess.run(shell=False)の場合
shell=Falseのパターンも検証を行ったのですが、上手く調査できているか心配だったので上記に記載しませんでした。
第一引数に文字を詰めまくる調査を行った所
* Linux系はshell=Falseの場合とほぼ変わらない
* OSXで521342字程度
* Windowsではメモリーエラー
という結果になりました。
BigQueryで節約して書く-SELECT句内のCASE式-

別にBigQueryに限った話ではないけれど、特にBigQueryはQuery課金なので
なるべく走査するデータ量を減らしてクエリを作りたい。

ということで、普段工夫していることをちょくちょく書いていこうと思う。
まずはSELECT句内でCASE式をつかった工夫について。
これってどの程度当たり前のことなんだろうか。

## SELECT句でCASE式をつかう

同じテーブルを対象としていて、同じキーをもとに集計処理をしようとしているけど
それぞれ集計したいものが違うので2つのクエリを作って、それぞれで確認している場合に使う。

たとえば、以下の2つのクエリ。

それぞれの正規表現に一致するカテゴリごとに集計を行うクエリどす。

```sql:ページカテゴリ1について集計
#standardSQL

SELECT
    REGEXP_EXTRACT(table1.screen_name, r"^\/Category1\/［(.*)］\/.*$") AS category1_item
  , COUNT(table1.screen_name) AS item_number2
FROM
    (
        SELECT
            h.appInfo.screenName AS screen_name
          , visitId AS visitId
        FROM
            `project.dataset.ga_sessions_2017*` AS m
          , UNNEST(hits) AS h
        WHERE
            _TABLE_SUFFIX BETWEEN "0611" AND "0611"
            AND h.appInfo.screenName LIKE "/Category1/%"
        GROUP BY
            screen_name, visitId
    ) AS table1
GROUP BY
    category1_item
```


```sql:ページカテゴリ2について集計
#standardSQL

SELECT
    REGEXP_EXTRACT(table1.screen_name, r"^\/Category2\/［(.*)］\/.*$") AS category2_item
  , COUNT(table1.screen_name) AS item_number2
FROM
    (
        SELECT
            h.appInfo.screenName AS screen_name
          , visitId AS visitId
        FROM
            `project.dataset.ga_sessions_2017*` AS m
          , UNNEST(hits) AS h
        WHERE
            _TABLE_SUFFIX BETWEEN "0611" AND "0611"
            AND h.appInfo.screenName LIKE "/Category2/%"
        GROUP BY
            screen_name, visitId
    ) AS table1
GROUP BY
    category2_item
```

やっていることは結局同じで、`(.*)`の部分を取り出して、そいつでグループ化してカウントしようとしている。
見ているテーブルは同じで、｢そいつ｣を抽出する方法が違うだけだ。
この場合上記の2つのクエリを発行するよりも、以下の様にSELECT句内でCASE式を使って集計してやるほうが
走査が1回で済むので安上がりになる。


```sql:CASE式を使ってまとめたもの
#standardSQL

SELECT
  CASE
      WHEN table1.screen_name LIKE "/Category1/%" THEN REGEXP_EXTRACT(table1.screen_name, r"^\/Category1\/［(.*)］\/.*$")
      ELSE REGEXP_EXTRACT(table1.screen_name, r"^\/Category2\/［(.*)］\/.*$")
  END AS category_item
  , SUM(
      CASE
        WHEN table1.screen_name LIKE "/Category1/%" THEN 1
        ELSE 0
      END
    ) AS item_number1
  , SUM(
      CASE
        WHEN table1.screen_name LIKE "/Category2/%" THEN 1
        ELSE 0
      END
    ) AS item_number2
FROM
    (
        SELECT
            h.appInfo.screenName AS screen_name
          , visitId AS visitId
        FROM
            `project.dataset.ga_sessions_2017*` AS m
          , UNNEST(hits) AS h
        WHERE
            _TABLE_SUFFIX BETWEEN "0611" AND "0611"
            AND (
                h.appInfo.screenName LIKE "/Category1/%"
                OR h.appInfo.screenName LIKE "/Category2/%"
            )
        GROUP BY
            screen_name, visitId
    ) AS table1
GROUP BY
    category_item
;
```


特にBigQueryの場合、columnベースでhit単位で見始めると一気にコストが高くなるので上記のような方法が
かなりコストダウンに繋がることが多い。

業務でつかったことのあるクエリをマスキングしてるだけなので、例がわかりづらい気がする。。
「.eclipse」など、自動で作られるドット始まりのフォルダの保存先を変更する

毎回忘れるのでメモ。
（デフォルトが「C:\Users\[user name] なので困ることがある...）

```ini:eclipse.ini
-vmargs
-Duser.home=C:\dev\hoge
```
eclipse.ini の "vmargs" プロパティの下に "Duser.home" プロパティを指定するだけ。

AndroidStudio の場合は少し厄介だったので別の記事にまとめる予定。
[こちら](http://qiita.com/krmtyshr/items/bebc3e12444782529ff0)にまとめました！（2017/07/06 追記）
Mockitoでorg.mockito.exceptions.misusing.InvalidUseOfMatchersException

Mockitoを使ったテストで

```
when(hoge.fuga("aaa",0)).thenReturn(1)
```
みたいなことを書きたいとき。
第二引数の`0`は何が入っても同じ返り値使いたいから`org.mockito.Matchers`の`anyInt()`を使いたいのでこう直すとする

```
when(hoge.fuga("aaa",anyInt())).thenReturn(1)
```

すると

```
org.mockito.exceptions.misusing.InvalidUseOfMatchersException: 
Invalid use of argument matchers!
5 matchers expected, 4 recorded:
//略

This exception may occur if matchers are combined with raw values:
    //incorrect:
    someMethod(anyObject(), "raw String");
When using matchers, all arguments have to be provided by matchers.
For example:
    //correct:
    someMethod(anyObject(), eq("String by matcher"));

```

エラーで怒られる。


```
when(hoge.fuga(eq("aaa"),anyInt())).thenReturn(1)
```
これにすると解決。

`anyInt()`などのmatcherを使いたいときはすべての引数をmatcherのメソッドで書く必要があるみたい。

中身がどうなってるのかさっぱりなんだけどなんでこうなるか知りたい。。。
【Swift】TextFieldのキーボードを閉じる方法3選

TextFieldのキーボードを閉じる方法をまとめてみました。


![032d4bb4a0add2be1948bec3e048227f.gif](https://qiita-image-store.s3.amazonaws.com/0/160547/a226f72c-098f-c562-c722-63a750a64696.gif)

# 1. 「return」キーを押す
## コードベース
resignFirstResponder()メソッドを利用します。

```swift:ViewController.swift
import UIKit

class ViewController: UIViewController, UITextFieldDelegate{
    
    @IBOutlet weak var inputText: UITextField!
    @IBOutlet weak var outputText: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        inputText.delegate = self
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    func textFieldShouldReturn(_ textField: UITextField) -> Bool {
        // キーボードを閉じる
        textField.resignFirstResponder()
        outputText.text = textField.text
        return true
    }
}
```

## storyboardベース
Xcodeのイベントアクションを利用します。

![スクリーンショット 2017-06-26 22.56.36.png](https://qiita-image-store.s3.amazonaws.com/0/160547/555ee23d-387f-9b9f-ac2d-93393f5cb13b.png)

①TextFieldの上で右クリック（二本指でタップ）
②「Sent Events」の「Did End On Exit」を「control」キーを押しながらドラッグ
③「Name」をつけて「Connect」

![スクリーンショット 2017-06-26 22.59.41.png](https://qiita-image-store.s3.amazonaws.com/0/160547/2d8473c5-5a0c-07bd-5ee2-38a3a4a86d73.png)

コードが追加されたことを確認できます。

```swift:ViewController.swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var outputText: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    @IBAction func inputText(_ sender: UITextField) {
        outputText.text = sender.text
    }
}
```

他にも様々なイベントアクションがあるので覚えておくと簡単に実装できます。
参考記事：[ボタンによるイベント処理 – swiftによるiOSアプリ開発](https://d-suga.com/843)

# 2. ボタンを押す
endEditing()メソッドを利用します。
（resignFirstResponder()メソッドでも実装可能です。）

![005c8194729f917d5f8db590273fd42b.gif](https://qiita-image-store.s3.amazonaws.com/0/160547/66776385-0fa7-ef4c-7a2f-43e7c293601d.gif)

```swift
import UIKit

class ViewController: UIViewController {
    
    @IBOutlet weak var inputText: UITextField!
    @IBOutlet weak var outputText: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    @IBAction func button(_ sender: Any) {
        outputText.text = inputText.text
        // キーボードを閉じる
        inputText.endEditing(true)
    }
}
```

# 3. TextField以外の部分をタッチ
touchesBeganメソッドをオーバーライドします。

```swift
import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var outputText: UILabel!
    
    @IBOutlet weak var inputText: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        outputText.text = inputText.text
        self.view.endEditing(true)
    }
}
```

以上をうまく組み合わせることでユーザーにストレスを与えない実装になりそうです。

# 参考記事
[UITextFieldの入力後に入力用キーボードを閉じる方法](http://qiita.com/SRAUFactory/items/7314793bd51e4075a208)
【swift3】NotificationCenterを使用してフォアグラウンド復帰時の処理を実装する

**ググってもシンプルなものがなかなか見つからなかったので:no_mouth:**


#applicationWillEnterForegroundで通知を作成

```swift:AppDelegate.swift
func applicationWillEnterForeground(_ application: UIApplication) {
        _ = NotificationCenter.default
        .post(name: Notification.Name(rawValue:"notification"), object: nil)
    }
```

#viewWillAppearにて通知登録

```swift:ViewController.swift
override func viewWillAppear(_ animated: Bool) {
        _ = NotificationCenter.default
        .addObserver(self, selector: #selector(self.notification), name: Notification.Name(rawValue:"notification"), object: nil)
    }
```

#呼び出される関数を作成

```swift:ViewController.swift
func notification(notification: Notification?) {
        print("フォアグラウンドに復帰したよ")
}
```
言葉の単数形と複数形の変換

[inflection](https://github.com/jpvanhal/inflection)が便利

### install

```
pip install inflection
```

### 使い方

```py3
import inflection as i


print("pluralize", i.pluralize("dog"))
print("singularize", i.singularize("dogs"))

# pluralize dogs
# singularize dog
```

### 利用例

複数形になっているテーブル名からクラス名のようなものを作りたい場合に

```py
print(i.camelize(i.singularize("cats")))
# Cat
```
Laravel の FormRequest で sometimes を使いたい時は withValidator メソッド経由が良さ気

Laravel 5.4 での話。

FormRequest を用いている時、特定のフィールドの Validate Rule を、そのフィールド自身の値以外を用いて適用するかどうか等を決定したいときがある。

そんな時に、どうしたら都合が良いだろうか考えてみたメモ。

# まず結論

`laravel formrequest sometimes` とかで検索すると、 `getValidatorInstance` メソッドを使う例が出てくるけど、 多分 `withValidator` メソッドを使った方がいいよ。(主に test が書けるという理由で。)

## Validator::sometimes

https://laravel.com/docs/5.4/validation#conditionally-adding-rules にあるように、何らかの条件を付与して、 Rule を適用するべきかどうかを判定することが出来る。

```php
$v->sometimes('reason', 'required|max:500', function ($input) {
    return $input->games >= 100;
});
```

上記 URL に記載のある、この例だと、"games の値が 100 以上ならば、 reason を必須として、最大500文字以内とする"という条件となる。

games の値が 100 より小さい場合は、 reason は必須とならない。

Controller の中とかで、 Validator を用意して操作するぶんには、まぁ、これでも大丈夫。

## FormRequest

https://laravel.com/docs/5.4/validation#form-request-validation のやつ。

Controller の Action で DI して使う便利なやつ。

`rules` メソッドで必要な KEY と対応する rule を定義するだけで使える。

```php
    public function rules()
    {
        return [
            'email' => [
                'required',
                'email',
            ],
        ];
    }
```

と書いておくと、 email フィールドは必須かつ、 email 書式である、という条件で Validator が動いてくれる。

Controller の中とかで Validator を用意しなくて良いのでかなり見通しが良くなる。

それだけでも使う価値があると思う。(し、 unit test も少し楽になる。)

## FormRequest + Validator::sometimes を使いたい

FormRequest を使って、先ほどの例の games の値によって reason に条件を付与したい場合どうしたら良いか。

### ダメなやつ

```php
    public function rules()
    {
        return [
            'reason' => [
                'sometimes',
                'required',
                'max:500',
            ],
        ];
    }
```

当然、これだと、 reason に値が存在する場合は必須かつ 500 文字以内となってしまう。

### 実現できるけどイマイチなやつ

https://stackoverflow.com/questions/30300119/how-to-use-sometimes-rule-in-laravel-5-request-class とか、 `laravel formrequest sometimes` で検索すると出てくる。

FormRequest を extends した自前の Request Class で getValidatorInstance を Override し、その中で sometimes メソッドを使う。

例えば、 `app/Http/Requests/Some/Path/StoreRequest.php` の中で、以下のように書く。

```php
    protected function getValidatorInstance()
    {
        $validator = parent::getValidatorInstance();

        $validator->sometimes('reason', 'required|max:500', function ($input) {
            return $input->games >= 500;
        });

        return $validator;
    }
```

これは期待する通りに動く。

ただし、 `parent::getValidatorInstance()` してるので、 test を書く時に悲しい気持ちになる(はず)。

### 今のところ良さそうなやつ

FormRequest を extends した自前の Request Class で `withValidator` メソッドを使う。

https://laravel.com/docs/5.4/validation の 「Adding After Hooks To Form Requests」に良さそうな事が書いてある。

> This method receives the fully constructed validator, allowing you to call any of its methods before the validation rules are actually evaluated:

つまり、 rules を評価する前の状態の Validator を受け取れるので、お好きにどうぞってことだと思う。

after フックはわかり易い例として、他にも Validator への"追加的措置"が必要ならココに書くのが良いんやないかな。

例えば、 `app/Http/Requests/Some/Path/StoreRequest.php` の中で、以下のように書く。

```php
    public function withValidator(\Illuminate\Contracts\Validation\Validator $validator)
    {
        $validator->sometimes('reason', 'required|max:500', function ($input) {
            return $input->games >= 500;
        });
    }
```

こうすると `parent::` も使わないので、 test しやすい！
HiveQLにおける初心者狩り

## はじめに
HiveQLとはHDFS上のデータ（ファイル）を操作するSQLらいくなデータ操作言語です。
HiveDDLとはHiveQLで構築されたデータ構造定義です。
HDFSとはHadoopが利用している分散ファイルシステムです。
複数のマシンを1つのストレージとして扱い、一部のマシンが故障してもデータが
失われないように設計されており、データ量に応じてスケールアウトすることが可能です。

通常はHiveDDLからテーブルを作成し、HiveQLでテーブルに対して操作を行うと思います。
※dbはデータベース名、tableはテーブル名を表す。

Hive初心者向けのトラブルシューティングがあまり見当たらなかったので記事にしました。

## 初心者狩りとは
SQLらいくな言語なのに
・UNIONが使えない！
→UNION ALLで頑張りましょう。
・ORDER BYが使えない！
→limit句を使用して頑張りましょう。

……とかではないです。
もっと根本的なところで狩られます。

## そして初心者は狩られる
1. テーブルを作ったけど「select * from table」しても何もない！
2. テーブルがあるのに「Table not found」でエラーになる！
3. データベース(スキーマ)が消せない！cascadeで無理やり消したからOK！
4. HDFSの容量を凄い勢いで大量消費している！

## 狩られた原因
1:
テーブルを作った(CREATE)、のではなく、テーブルを作り直した(DROP→CREATE)したのではないでしょうか？
DROP TABLEすると、<font color="OrangeRed">TABLEが参照していたHDFSのデータはディレクトリごと消えます。</font>
その状態でデータを再配置せずにテーブルをCREATEした場合、入力ファイル（ディレクトリ）は無いので、
空のテーブルが出来上がり、「select * from table」しても何も取得できません。
この事象に出会ったときは<font color="OrangeRed">DROPしてから、データを再配置してCREATEしてください。</font>
なお、DROP TABLEしたテーブルが新規CREATEではなく元々存在するテーブルを使用して
CREATEしたテーブルである場合、大元のHDFS上のデータは消えないことを留意。

2:
データベースの指定を忘れています。
GUI(Hue)とコマンドラインの両方を使用していると陥りやすいです。
```use db; select * from table;```
のように<font color="OrangeRed">「use db;」をHiveQLの先頭につけるようにしましょう。</font>
他のDBも同じだと思うのですが、たまにこれが原因の問い合わせを受けます。

3:
データベース(スキーマ)はテーブルが存在するとき、削除できません。
```hive -e "drop database DB;"```
を実行すると「Database DB is not empty」とエラー出力されます。
```hive -e "drop database DB cascade;"```
を実行するとテーブルが存在してもDBを削除することができます。
……が、<font color="OrangeRed">そのDBに存在したテーブルの参照するデータはHDFS上に残り続けます。</font>
消した（DROPした）のはテーブルではなくDBだから。
無暗にcascade指定でDBを消していくとHDFS上にゴミデータが散乱します。
もちろんHDFSの容量は使用したままです。
無暗に消してしまった場合は<font color="OrangeRed">「hadoop fs -rmr」コマンドでゴミデータを削除しましょう。</font>

4:
間違ったHiveQLを実行して、それを実行したままにしている可能性があります。
コマンドラインでHiveQLを実行したときは、Ctrl+Cで中止できます。
が、<font color="OrangeRed">GUI(Hue)で実行したときは強制終了しないと中止されません。</font>
例えば、一億レコードと一億レコードのテーブルをユニークで無いキーでJOINした場合、
最大一億×一億のレコードを出力することになり、その分の容量を使用することになります。
そんなHiveQLを放置してしまえばHDFSが数百テラバイトあったとしても長くは持ちません。
<font color="OrangeRed">実績のないHiveQLの実行放置は極力避けましょう。</font>

## 他にも……
初心者狩りはある気がするのですが、パッと思い出せませんでした。
（ここまでの紹介は初心者の時に出会ったことのある事例を紹介しています）
何かありましたら、ご指摘・ご教授いただければと思います。
qiita始めてみた

# はじめに
３０代超前半のインフラエンジニアが初めてプログラムを学ぼうとしています。

これからqiitaへプログラムの勉強ログを気まぐれに書いていこうと思っています。

# 経験言語
特になし！　windows batch?

# やってる業務
サーバやら、ネットワークやら、セキュリティやら仮想化やら、一人でやってる社内SE的な

# 今後やりたいこと
やってる業務の自動化、WEBアプリや、モバイルアプリの作成

# 今のところの開発環境
以前、無理やり購入した surface 3 64GB windows 10 pro 容量が足りません。
visual studio commnity 2017
one drive 無料版


3日坊主にならないように。。。
Pythonの正規表現による置換でマッチした文字列に応じて置換する文字列を変化させる

# 環境
Python 3.5.2で検証

# 使用場面
正規表現による置換を行うとき，マッチした文字列に応じて置換する文字列を変化させたい場合

例として，以下のようなことができます．

* 文字列中にある数字に対して，何かしらの演算をした結果に置換したい  
"abcd1234efg" -> "abcd2468efg" (数字を2倍)
* 文字列中にある数字を漢数字に置換したい  
"abcd1234efg" -> "abcd千二百三十四efg"

(上の例以外にも，より複雑な処理をさせることが可能だと思います)

# 方法
通常の正規表現による置換で使用する`re.sub()`と，マッチした文字列に施したい処理を実装した関数を用います．

具体的には，`re.sub(パターン, 関数, 検索する文字列)`とします．
上記で呼び出す関数の引数を仮にmatchobjとすると，関数内でmatchobj.group(0)とすることでパターンにマッチした文字列を利用できます．

```python
def replace(matchobj):
    s = matchobj.group(0)
    # 行いたい処理

re.sub(`hoge`, replace, "sampletext")
```

# 実装例
下の例では，数字を漢数字に変換する関数`toKansuji()`と，5%の消費税を8%に変更する関数`fixTax()`を用意し，re.sub()内でマッチした数字に対して上記の関数を適用した結果に置換しています．

## コード
```sample.py
import re

def toKansuji(matchobj):
    a = ["一", "万", "億", "兆", "京"]
    b = ["一", "十", "百", "千"]
    c = ("", "一", "二", "三", "四", "五", "六", "七", "八", "九")
    s = matchobj.group(0)
    l = [int(i) for i in s]
    result = ""
    length = len(l)
    for i in range(length):
        n = l[length-1-i]
        if i%4 == 0 and i//4 > 0: 
            az = 1
            for j in range(4):
                if l[length-1-i-j] != 0: az = 0
            if az == 0: result = a[i//4] + result
        if n != 0:
            if i%4 != 0:
                result = b[i%4] + result
                if n != 1: result = c[n] + result
            else:
                result = c[n] + result
    return result

def fixTax(matchobj):
    d = int(matchobj.group(0))
    d = int(d / 1.05 * 1.08)
    return str(d)

if __name__ == '__main__':
    s1 = "税込315円"
    print("消費税5%:", s1)
    print("消費税8%:", re.sub('\d+', fixTax, s1))

    s2 = "IPv4は4294967296個"
    print("数字  :", s2)
    print("漢数字:", re.sub('\d{2,}', toKansuji, s2))
```

## 実行結果
```
消費税5%: 税込315円
消費税8%: 税込324円
数字  : IPv4は4294967296個
漢数字: IPv4は四十二億九千四百九十六万七千二百九十六個
```
上2行は消費税を5%から8%に，下2行は数字を漢数字に置換しています．

# その他
パターンにおいて()を用いてグループ化すると，関数内でmatchobj.group(n)とすることでそのグループ内でマッチした文字列を利用することもできます．

(また，今回用意した関数はどちらも動作確認程度に用意したものなので，あらゆる入力に対して正確な値を返さないかもしれません)

# 参考
[https://docs.python.jp/3/library/re.html](https://docs.python.jp/3/library/re.html)
サーバーソケットの作り方と、クライアントソケットとの確立の概要

サーバーソケットを作った後、
connectしてきたクライアントソケットに対応したソケットの作り方をざっくりと。

#サーバーソケットの作り方
TCPソケットを作って、host と ip で固定する

```py:example
s_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s_socket.bind((socket.gethostname(), 80))
serversocket.listen(5)
```

#接続してきたソケットにて、クライアントソケットを作る
データの送信もしないし、受信もしない。
接続してきたクライアントソケットに対応した、
サーバー側内で扱う、クライアントソケットを作る。

```py:example
while True:
    (clientsocket, address) = serversocket.accept()
    ct = client_thread(clientsocket)
    ct.run()
```
接続してきたソケットに対して、クライアントソケットを作成する。
これを、ひたすら行わせ続ける。
上で作られたソケットは勝手に喋ってくれるし、ポートの割り振りも自動的に行ってくれます。

また、ユーザー側のクライアントソケットと、サーバー側のクライアントソケットは同種のものになります。

上記、サーバー内でのクライアントソケットの扱い方、send or recv の扱い方は別途。
プログラミング初学者にこそオススメしたいPython

プログラミング初心者、あるいはこれからプログラミングをしてみたい！と思っている人にPythonというプログラミング言語を紹介したいと思います。

Web上で私がよく見かけるのは初心者にはC言語だ！とか、初心者こそJavaだ！というサイトが多いことです。私自身も初めて触れたプログラミング言語がC言語で、2番目がJavaでした。確かにC言語はほとんどのプログラミング言語の大元になったと言われていますし、JavaはIT業界の主流言語と言われているので、この2つのどれかを入門用として覚えるのもいいと思います。
ここでCとJavaの経験者として言いいたいことがあります…。
C言語→ただただ難しい
Java→書いててストレスを感じる
※あくまで個人の主観です(｡-人-｡) 

C言語とJavaは何が難しいのか？
(C言語の場合)
ポインタ
これに尽きると思います。私の友人やプログラミング経験者もほとんどがこのポインタで苦戦or挫折していました…。なので個人的にC言語は初学者が学ぶべき言語ではないかなと思います。
だだ、先ほども述べたようにC言語はほとんどのプログラミング言語の大元になっている言語なので、C言語の基本的な文法を理解すれば、他のプログラミング言語の文法もすんなりと理解できます。
※ポインタにつきましてはWebサイトなどをご参照頂ければ、わかりやすい説明がされています。

(Javaの場合)
JavaはCと比較すると覚えやすい言語だとは思いますが……。
私的にはコード量(プログラムを記述する量のこと)が多いので書いていてストレスを感じます…(私だけかもしれませんが)
多くのIT企業では新入社員研修でJavaを教えてるということはそれだけJavaでのお仕事が多いということだと思うので、使いこなせれば仕事に困ることはないのではないかと思います。

初学者にPythonをオススメする理由
①C言語やJavaに比べて習得がかなり容易
②書いていてストレスを感じない
③機械学習ブームの到来で将来性抜群←野球で言ったら清宮くんレベルで将来有望
④「Pythonプログラマー」になればお金持ちになれる！？


Javaと比較してみた
<Hello Worldと出力したい場合>

```java:Java
public class Hello{	//ここではHelloとする。
    public static void main(String[] args){
        System.out.println("Hello World");
    }
}
```

```py:Python
print("Hello World")
```

いかがでしょうか。JavaはHello Worldと出力させたいだけなのに、やたらと長い呪文を唱えます。
それに対し、Pythonはたった1行で終わりです。

<キーボードからの文字入力をしたい場合>

```java:Java
import java.util.Scanner;
public class Hello{	//ここではHelloとする。
    public static void main(String[] args){
        Scanner <任意の変数名>=new Scanner(System.in);
    }
}
```

```py:Python
<任意の変数名>=input()	
```

お分かりだと思いますが、Javaはキーボードからの文字入力の際にもやたら長い呪文を唱えましたが、Pythonでは任意の変数名にinput()を代入することでキーボードからの文字入力を受け付けます。


実例を用いながらダラダラとPythonの良さを力説しましたが、本当に素晴らしい言語です。これは実際に書いてみて初めてわかることだと思います！
私はバカの一つ覚えにPythonを連呼していましたが、プログラミング初学者の方に言いたいことは、様々なプログラミング言語に触れてみることがスキルアップには欠かせないということです。
neovimでカーソル位置を記憶させる

neovimではデフォルトでファイル編集終了時のカーソル位置が記憶されないので、`init.vim` などの設定ファイルに以下のautocmdを記載します。

``` vim
" カーソル位置記憶
autocmd BufReadPost *
      \ if line("'\"") > 0 && line ("'\"") <= line("$") |
      \   exe "normal! g'\"" |
      \ endif
```
Xcode 8でのProvisioning Profilesの削除について備忘録

iOSアプリ開発をしていると、同じ名前のProvisioning Profileをインストールして古いものが参照されてビルドが失敗する、ということが多々発生する。

```
Provisioning profile “プロファイル名” doesn’t include the currently selected device “デバイス名”.
```
Provisioning Profileにデバイスを登録している筈なのに↑が出る場合などは高確率でそのケースでしょう。

そんな時は古いProvisioning Profileを消して指定しなおせばビルドが成功する。

従来のXcodeでは Xcode > Preferences > Accounts > View Detailsから
Provisioning Profileを削除することが出来たが、Xcode 8にはView Detailsが存在しない。
従って、下記ディレクトリの中身を直接削除する。

```
/Users/ユーザ名/Library/MobileDevice/Provisioning Profiles
```

このディレクトリの中身を削除し、Provisioning Profileをインストールし直せばビルド成功するはず。
Tensorflow Real-Time Object Detection メモ

[Building a Real-Time Object Recognition App with Tensorflow and OpenCV](https://medium.com/towards-data-science/building-a-real-time-object-recognition-app-with-tensorflow-and-opencv-b7a2b4ebdc32) の方の [github](https://github.com/datitran/Object-Detector-App) に上がってるソースを動かしてみたメモです。

この方の環境と僕の環境が違ったので。

- Anaconda / Python 3.5
- TensorFlow 1.2
- OpenCV 3.0

以下の Dockerfile を作って。

```
FROM ubuntu:16.04

RUN apt-get update --fix-missing \
&& apt-get upgrade -y \
&& apt-get install -y \
      build-essential \
      cmake \
      git \
      wget \
      bzip2 \     
      sudo \
      wget \
      bzip2 \
      ca-certificates \
      curl \
      unzip \
      libgtk2.0-0  \
    && rm -rf /var/lib/apt/lists/*


# Anaconda3-4.2.0 install
RUN wget https://repo.continuum.io/archive/Anaconda3-4.2.0-Linux-x86_64.sh && \
    bash Anaconda3-4.2.0-Linux-x86_64.sh -b && \
    rm -f Anaconda3-4.2.0-Linux-x86_64.sh

ENV PATH $PATH:/root/anaconda3/bin

# OpenCV install
RUN conda install -c https://conda.anaconda.org/menpo opencv3
```

こんな shell script 作って。(run.sh とする。)

```
IMAGE=kidapu/tf-realtime
CONTAINER=tf-realtime

build () 
{
	nvidia-docker build -t $IMAGE . 
}

run () 
{
	# xhost +local:root

	nvidia-docker run --privileged -it \
	  --name ${CONTAINER} \
	  --env DISPLAY=$DISPLAY \
	  --env="QT_X11_NO_MITSHM=1" \
	  -v /dev/video0:/dev/video0 \
	  -v /tmp/.X11-unix:/tmp/.X11-unix:ro \
	  -v /home/kida/docker/08-tf-realtime/share:/tmp/share:rw \
	  ${IMAGE} bash

	# xhost -local:root
}

# main
case $1 in
	"build") build ;;
	"run") run ;;
	*) echo "引数を指定して下さい";;
esac
```

以下のコマンド実行しました。


```
# docker の中から window を使うため
$ +xhost 

# shell script の実行
$ sh run.sh build
$ sh run.sh run
```

で、docker コンテナの中に入って、いろんなものの環境を確認するとこんな感じで。（作者の方と違いましたが、、

```
# python --version
Python 3.5.2 :: Anaconda 4.2.0 (64-bit)
# python -c "import cv2; print(cv2.__version__)"
3.2.0
# conda --version
conda 4.3.22
```

とりあえず、ソースおとしたり、関連モジュール落としてきて。

```
$ git clone https://github.com/datitran/Object-Detector-App.git
$ conda env create -f environment.yml
```

てやると、なんか envirotment.yml の menpo::tbb=4.3_20141023=0 のところで、「そんなのないよ」的なエラーがでてたので、よくわからないながら、以下のように変えてしまって。 

```
menpo::tbb=4.3_20141023=0
↓
menpo::tbb
```

以下のように実行すると、ウェブカムの映像をリアルタイムに物体認識するアプリケーションが動きました。（ウェブカム必須）

```
python object_detection_app.py
```

僕の所感だと、 yolov2 と比べると、デフォルトのモデルは精度低いし、FPSも低いなって印象でした。
ただ、yolov2 より SSD のほうが精度いいってどこかの記事で見た気がするので、もうちょいモデルとか学習データとか調整して、もう１回試してみたい。
MacBook 12インチにしたら、iOSシュミレーターが激重になった

MacbookAirからMacbook 12インチに機種替えしたら、iOSのシュミレーターがめちゃくちゃ重くなりました。
動かしてたのはcocos2dのプロジェクト。
シュミレータの画面サイズを50%とか25%とかにしても何も変わらず。スゲーカクカクしてる。スローモーションみたい。（Slow AnimationはもちろんOFFにしてます。）
何かやらかしたか？というぐらいの重さで、思わず調べてみたところ、そういうもんだ、という回答を発見。

https://stackoverflow.com/questions/12351894/ios-and-cocos2d-my-app-really-slow-on-simulator-but-is-fine-on-device

とくにRetinaはやばいらしい。
もう諦めて実機で動かすことにします。
header() でリダイレクトさせるあとはだいたい exit() しよう

[header()](http://php.net/manual/ja/function.header.php) 関数を用いて、リダイレクト処理を行うことはよくあります。

```php
<?php

header('Location: /');
exit();
```

で、例えば以下のように、ユーザ認証を行って、妥当であれば、メールを送信するという場合。

ここで `header` は早期 return ぎみに使っています。だけど、`header` でリダイレクトをしたからといって、それ以降の処理を行わないわけではありません。以下のコードでは、`validate_user` の戻り値にかかわらず、`send_mail_to_user` が実行されます。

```php
<?php

if (validate_user($user)) {
    header('Location: /');
}

send_mail_to_user($user)
```

## なので

[exit](http://php.net/manual/ja/function.exit.php) を加えます。exit はスクリプトの終了。これでバリデーションが通らない場合は、`send_mail_to_user` が実行されません。

```php
<?php

if (validate_user($user)) {
    header('Location: /');
    exit();
}

send_mail_to_user($user)
```

## たしかに

`header` って ステータスコード変えたりするし、渡してるの文字列だし、`Location:` からはじまるときだけ、以降を実行しないっていうのは気持ち悪かったりするので、今の仕様で納得だな、という感じ。

`exit()` つけよう

## 参考

- [headerでexitしてなかった…… \| 株式会社LIG](https://liginc.co.jp/programmer/archives/1140)
UIFontの標準サイズを調べた + Dynamic Typeについて

# 公式ドキュメント

基本ここに書いてある
https://developer.apple.com/ios/human-interface-guidelines/visual-design/typography/


# systemFont

フォントサイズを決める参考に、UIFont.systemFontSizeが実際どんなサイズか気になったので、Simulatorで調査しました。

## 調査方法

2017/5/25時点
Simulatorの各端末
iOS 10.3
設定は標準設定です

調査コードは以下の通りです。

```swift
import UIKit

class ViewController: UIViewController {
    let deviceName = "iPhone 7"
    override func viewDidLoad() {
        super.viewDidLoad()
        print("|\(deviceName)|\(UIFont.systemFontSize)|\(UIFont.smallSystemFontSize)|\(UIFont.labelFontSize)|\(UIFont.buttonFontSize)|")
    }
}
```

## 調査結果

|機種名|systemFontSize|smallSystemFontSize|labelFontSize|buttonFontSize|
|:--|:-:|:-:|:-:|:-:|
|iPhone 5|14.0|12.0|17.0|18.0|
|iPhone 6s|14.0|12.0|17.0|18.0|
|iPhone 7|14.0|12.0|17.0|18.0|
|iPhone 7 Plus|14.0|12.0|17.0|18.0|
|iPhone SE|14.0|12.0|17.0|18.0|


端末によっては変わらないですね。
設定のアクセシビリティから文字サイズを変更しても変わりません。

これに追従させるには、DynamicTypeに対応させる必要があります。

# DynamicType

設定画面で設定できる文字サイズに対応させることができます。

```swift
label.font = UIFont.preferredFont(forTextStyle: .headline)
label.adjustsFontForContentSizeCategory = true
```

のようにして対応させます。
`.adjustsFontForContentSizeCategory` は設定の変更を監視して即時反映させる設定です。

|not larger text|larger text|
|:-:|:-:|
|![Simulator Screen Shot 2017.05.25 15.06.46.png](https://qiita-image-store.s3.amazonaws.com/0/77778/80752a3c-f3e3-17ea-c2cd-04efa3c9716f.png "Simulator Screen Shot 2017.05.25 15.06.46.png")|![Simulator Screen Shot 2017.05.25 15.07.40.png](https://qiita-image-store.s3.amazonaws.com/0/77778/adba7f1d-b1a1-d0af-c2d3-bf3d3d81e090.png "Simulator Screen Shot 2017.05.25 15.07.40.png")|


最小を1として

通常1〜7
LargerTextにすると、1〜12
初期設定だと4

機種によっても変わらないみたいです。

|設定値|title1|title2|title3|body|caption1|caption2|callout|footnote|headline|subheadline|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|1|25.0|19.0|17.0|14.0|11.0|11.0|13.0|12.0|14.0|12.0|
|2|26.0|20.0|18.0|15.0|11.0|11.0|14.0|12.0|15.0|13.0|
|3|27.0|21.0|19.0|16.0|11.0|11.0|15.0|12.0|16.0|14.0|
|4|28.0|22.0|20.0|17.0|12.0|11.0|16.0|13.0|17.0|15.0|
|5|30.0|24.0|22.0|19.0|14.0|13.0|18.0|15.0|19.0|17.0|
|6|32.0|26.0|24.0|21.0|16.0|15.0|20.0|17.0|21.0|19.0|
|7|34.0|28.0|26.0|23.0|18.0|17.0|22.0|19.0|23.0|21.0|
|8|34.0|28.0|26.0|28.0|18.0|17.0|22.0|19.0|23.0|21.0|
|9|34.0|28.0|26.0|33.0|18.0|17.0|22.0|19.0|23.0|21.0|
|10|34.0|28.0|26.0|40.0|18.0|17.0|22.0|19.0|23.0|21.0|
|11|34.0|28.0|26.0|47.0|18.0|17.0|22.0|19.0|23.0|21.0|
|12|34.0|28.0|26.0|53.0|18.0|17.0|22.0|19.0|23.0|21.0|

## 調査方法

検証コードは以下の通りです。

```swift
import UIKit

class ViewController: UIViewController {
    let name = "4"
    override func viewDidLoad() {
        super.viewDidLoad()
        let styles: [UIFontTextStyle] = [.title1, .title2, .title3, .body, .caption1, .caption2, .callout, .footnote, .headline, .subheadline]
        let sizes = styles.map { UIFont.preferredFont(forTextStyle: $0).pointSize }
        print("|\(name)|\(sizes.map{"\($0)"}.joined(separator: "|"))|")
    }
}
```
ネットワークプログラムの基本？

WebSocket通信を前提に、ネットワークプログラミングの基礎をつらつらと。
以下を基礎に発展させていく予定。

##通信種類の指定（とらんすぽーと）
**ストリーム型**
TCPにて接続を維持するコネクション型の通信をしたいですよ

```py:example
socket.socket(socket.AF_INET, socket.SOCK_STREAM)
```

**データグラム型**
UDPにて接続を維持するコネクションレス型の通信をしたいですよ

```py:example
socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
```

##ポートについて
IPだけだと、１セッションの通信しか行えない。
IP＋ポートを使用して複数通信処理が行える？（要調査）
ポート番号は 1~65535 までの指定が可能

| Port num | Des |
|:-:|:-:|
| 1~1023  | 既知ポート番号  |
| 1024~49151  | 左記も登録済み  |
| 49152~65535  | 自由に使ってよい  |

[wikiポート番号一覧](https://ja.wikipedia.org/wiki/TCP%E3%82%84UDP%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E3%83%9D%E3%83%BC%E3%83%88%E7%95%AA%E5%8F%B7%E3%81%AE%E4%B8%80%E8%A6%A7)

##サーバーソケットの作り方
**1.socket**
例）TCP通信したいです

```py:example
s.socket(socket.AF_INET, socket.SOCK_STREAM)
```
**2.bind**
作ったソケットに、使用する ip と port で固定する

```py:example
s.bind((HOST,PORT))
```
**3.listen**
作成したソケットを有効にして、サーバー接続を受け付けるようにする処理。
接続可能数を指定します。指定数以上は接続拒否されるはず…

```py:example
s.listen(1)
```
**4.accept**
接続可能状態にする。

```py:example
conn, addr = s.accept()
```
**5.send or recv**
実際の受信処理
1度の受信サイズをByteにて指定できる。

```py:example
recv_mess = s.recv()
```
**6.close**
くろーず

```py:example
s.close()
```
##クライアントソケットの作り方
**1.socket**
例）TCP通信したいです

```py:example
s.socket(socket.AF_INET, socket.SOCK_STREAM)
```
**2.connect**
対象の ip と port に接続します

```py:example
s.connect((host,port))
```
**3.send or recv**

```py:example
s.sendall(msg)
```
**4.closesocket**
くろーず

```py:example
s.close(msg)
```
UiPathでRPAを実践してみる(7) ～OCR機能について～

UiPathでは、リモートデスクトップ接続等、画面の情報しか取れない場合でも値を取得する為の機能を備えています。
今回はOCRを使った画面からの情報取得について書いていきます。

例えば、リモートデスクトップ接続でメモ帳に記述された情報が欲しかったとします。
この場合、UiPathはリモートデスクトップ接続しているウィンドウの認識は出来ますが、そのウィンドウ内で表示されているアプリケーションの情報を取得する事は出来ません。
そこで、OCR機能を使っていきます。
まずは、RecordingからCitrixを選択して…
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/154110/ae698bb1-2f40-8d93-2e7c-b30ae7994206.png)

次に、Screen Scrapingを選択しましょう
![キャプチャ(1).PNG](https://qiita-image-store.s3.amazonaws.com/0/154110/aa072070-143e-2fc7-f201-018c15cb4abf.png)

対象の画面を選択するように促されるので、取得したいウィンドウを指定しましょう。
また、この時にF3を押すと画面の一部分のScrapingを行う事も可能です。
今回は下記のメモ帳の文言を取得していきましょう。
![キャプチャ(2).PNG](https://qiita-image-store.s3.amazonaws.com/0/154110/f4ca9f79-28f0-31fd-9090-fb6915d70526.png)

下記が取得した後の画面イメージです。画面上部がTarget scraped Using **OCR**となっています。これは、画面からの情報取得がOCRによってなされた事を表しています。
それなりに情報が取得できています。(流石に完全とまではいきませんが…日本語って難しいんですね。)
画面右側では使われたエンジンと言語を選択しています。他にはGoogle OCR等も利用可能です。
![キャプチャ(3).PNG](https://qiita-image-store.s3.amazonaws.com/0/154110/26af0b7f-9f6c-2a81-3298-d6a6d6971f44.png)

というわけで、日本語は中々難しいので数字のみの情報を取得していきたいと思います。上述の通り、F3を押して画面の一部分のみの情報を取得しています。
![キャプチャ(4).PNG](https://qiita-image-store.s3.amazonaws.com/0/154110/0b699878-b350-7dd9-132f-ac3376080b29.png)

ここでは上手く、「123456」という情報を取得できていますね。

この様な形で、リモートデスクトップ上に表示されている情報を文字情報として取得する事が出来ます。
日本語の文章の取得に使うには、まだまだ難しい部分がありますが、英数字についてはそれなりの精度で情報を取得できると思います。

というわけで、リモートで利用している端末から情報を取得するには、英数字については**OCR**で情報を取得するのが手軽だと思います。
一方、もし、日本語の情報を文字として取得しようと思った場合には、`Send hotkey`機能にて、Ctrl + Cキーを使ったりしてクリップボードの情報を使うのが良さそうです。
Karabiner-Elementsの本家が「英数/かな」の切り替えに対応したみたい

Karabiner-Elementsの[本家](https://github.com/tekezo/Karabiner-Elements)がバージョン0.91.5で`complex_modifications`という機能を実装して日本語環境用に左右の⌘ボタンで「英数/かな」を切り替えられるようになったようです。

正確にはバージョン0.91.1から`complex_modifications`は使えていたみたいなのですが、0.91.5からGUIサポートしたということで早速自分の環境をこちらに切り替えてみました。

## 手順
### Karabiner-Elementsをダウンロード＆インストール
本家のGithubから最新バージョンをダウンロード。
ダウンロードしたインストーラからインストール。

### 設定
Karabiner-Elementsを開いて「complex_modifications」タブを選択し、「Add Rule」ボタンをポチ。
![Karabiner-Elements Preferences 01.png](https://qiita-image-store.s3.amazonaws.com/0/63069/6f59cd48-ff50-4036-1fe2-1983e275bd12.png)

「Import more rules from the internet (Open a web browser)」というボタンをポチ。
![Karabiner-Elements Preferences 02.png](https://qiita-image-store.s3.amazonaws.com/0/63069/39631829-264d-2ad3-5ade-4347f5e58262.png)

開いたWebページに「For Japanese（日本語環境向けの設定）」というのがあるので「Import」ボタンをポチ。
![Karabiner-Elements Preferences 03.png](https://qiita-image-store.s3.amazonaws.com/0/63069/5c02c765-a17b-a4f2-e10a-b2983fbe3a55.png)

Importしたルールが追加されているので「Enable」ボタンをポチ。
![Karabiner-Elements Preferences 04.png](https://qiita-image-store.s3.amazonaws.com/0/63069/44c91b4b-624a-c314-858f-0db269a7d0fe.png)

「英数/かな」の切り替え設定がルールに追加さていればOK。
![Karabiner-Elements Preferences 05.png](https://qiita-image-store.s3.amazonaws.com/0/63069/62a12e03-186a-87d8-f00d-16db38a084ba.png)

これで完了。
左⌘キー単押しで「英字」、右⌘キー単推しで「かな」に切り替わるようになっていれば成功です。
もしうまく動作しない場合は`~/.config/karabiner/karabiner.json`を一旦退避するなり消すなりしてからやるといいかもです（自分の環境ではこれで一時挙動がおかしかったです）。

MacをSierraにアップデートしてKarabiner動かねーってことで困った方々は代替として「[⌘英かな](https://ei-kana.appspot.com/)」とか、本家をフォークした[wwwjfy/Karabiner-Elements](https://github.com/wwwjfy/Karabiner-Elements)を使っているかと思うのですが、これらはもう捨ててよさそうです。
Ubuntu環境でRobotics System Toolbox のsshの接続に失敗する問題



## 結論
`sudo apt install libssh2-1` で解決した

## 環境
* Ubuntu16.04 + ROS Kinetic
* MATLAB 2016b
    * Simulink, Control System Toolbox, Robotics System Toolbox, Simulink Coder

## 背景
simulinkのツールボックスの中には，Simulink Coderがある．これはブロックモデルから，自動で対象のハードウェア向けコードを自動生成し，実機で実行可能にしてくれるという制御屋さんには心強いツールボックス．

Simulink CoderはRobotics System Toolboxを持ってれば，ROSでも使えるようにしてくれるので，[このサイト](https://jp.mathworks.com/help/robotics/examples/generate-a-standalone-ros-node-from-simulink.html)を参考にsimulinkモデルからROS用のコードを自動生成してみようとしたが，最初のあたりのステップでつまづいてしまった．(Task 2 - Configure the Connection to the ROS Deviceのあたり)

![matlabrosssh_failure.png](https://qiita-image-store.s3.amazonaws.com/0/154671/6878a543-4aad-371f-122f-9bedd9b3b3e8.png)


## 問題の原因
エラーが起こっていたのは，ホストPCに接続するためのssh通信テストの部分．
対象に接続できません的なメッセージだったので，アドレスやログインユーザ名が間違っているのかな？と思ったが，ターミナルでssh通信したらちゃんと通信できる．


MATLABのコマンドウィンドウから`rosdevice(deviceAddress,username,password)`でつなげるか確認してみると，
`$MATLABROOT/toolbox/robotics/robotsimulink/robotslros/+robotics/+codertarget/+internal/private/ssh2client_mex.mexa64`に問題があるようで，libssh2.so1が見つからない的なメッセージが表示されたので，
(`$MATLABROOT`はMATLABがインストールされたディレクトリ)

ターミナルから，
`sudo apt install libssh2-1` 
でインストールしたら，MATLABからもsshで通信可能となり，接続テストもクリアした．

![matlab_ros_ssh_sccess.png](https://qiita-image-store.s3.amazonaws.com/0/154671/ac80c019-72f8-76ec-0b52-3f3a67e613e5.png)
ブラウザ自動打刻ツールを作ってみました。

# 何を作ったか
私の会社では出勤の管理をwebアプリケーションで行なっています。(salesforce)
「出退勤したら、webページを開きログインして出勤・退勤ボタンを押す」という流れです。
この一連の流れを全自動でしてくれるスクリプトを組みました。
以下を参考にしました。
http://qiita.com/socket1016/items/fdcc454e4ffe70a07507
ブラウザ起動ツールのダウンロードなどは上記をそのまま実施してください。
ソースは[ここ](https://github.com/sakamotoyouta/autoAttendance)に置いてます。

# 手間取った箇所
iframe内のボタンの押し方がすごい大変でした。
iframe内のDOMを操作するには、フォーカスをiframeに写さなければいけないようです。
詳しくは[ソース](https://github.com/sakamotoyouta/autoAttendance)を参照ください。

# スクリプトの起動を簡単に
1. ターミナルを開く（windowsはコマンドライン？）
1. スクリプトファイルの場所に移動
1. python3 ファイル名　　　を入力

スクリプトの起動まで、上記3ステップが必要です。 
面倒臭いので、`python3 ファイル名（絶対パス）`のエイリアスを作成しましょう。
エイリアスの作成方法はOSによって違うので、調べてみてください。
すぐ見つかると思います。

# 作ってみて
すごく勉強になりました。
エイリアスの作成も初めてで、やっとプログラマーらしいものが作れたな〜と感慨深かったです。
pythonの勉強を始めたばかりかつ、就業の合間に作成したので、ソースが汚いのはご勘弁を・・
リファクタリングのご指摘、お待ちしております！！
[Unity]文字列データの暗号化

<h1>なぜ暗号化するのか</h1>

Unityから出力したアプリケーションファイルは基本的に暗号化されておらず、あまり安全であると言えない。
Android用apkファイルなどは、少しいじるだけで中身をばらすことができる。

Unityエディタで使ったapiキーも見られたりしてしまう。

<h1>どのようにするか</h1>
不正を防止として、簡易な暗号化をかけるだけでも、カジュアルなクラックは止めることができる。

Unityの場合、System.Security.Cryptography名前空間内のRijndaelManagedクラスを使用することで暗号化を実装できます。
Pepper 2.5.5でrequestsを使ったボックスを試す(Cloud Vision API)

Pepper 2.5.5へのアップグレードに伴い、NAOqi OS内のパッケージも更新されました。
Choregrapheの自作ボックスなどでNAOqi OSに含まれているPythonライブラリを利用している場合は、新バージョンに合わせて一部書き換える必要があるかもしれません。

今回は、[PepperでCloud Vision APIを試すためのボックスを作ってみた](http://qiita.com/yacchin1205/items/1483b382018852c5e60d)を例に、NAOqi OS内のパッケージの変更点やChoregrapheの自作ボックスの更新について見ていきます。


# サンプルプログラムのソースコード

ここで紹介する、pepper-web-boxesという、Cloud Vision API用ボックスを含むボックスライブラリは、[vr-lab/pepper-web-boxes](https://github.com/vr-lab/pepper-web-boxes)にアップロードしてあります。

今回見ていく Cloud Vision API用のボックスは、 `Google/Cloud Vision` の中に入っています。自分のプロジェクトで利用するには、ボックスライブラリのインポートが必要です。

ボックスライブラリのインポートや使い方は、[元記事](http://qiita.com/yacchin1205/items/1483b382018852c5e60d)を参照してください。

# Cloud Vision API用ボックスのPepper 2.5.5 への対応

Cloud Vision連携ボックスでは、スクリプト内でいくつかの内部ライブラリをimportしています。
その中のひとつであるHTTP通信用の `requests` ライブラリは、NAOQi 2.5においてダウングレードされたために、ボックス実行時にエラーが発生するようになっていました。

具体的には、v2.5.1からv2.3.0へダウングレードされ、 `requests.post()` の`json`パラメータが使えなくなることで、`requests.post`実行時に不明なパラメータが指定されたとエラーが発生する形です。

`json`パラメータは、Pythonのdictionaryを与えるだけで、自動的にサーバに対してそのデータをJSON化した上で送信してくれる便利なパラメータですが、v2.3.0ではこのパラメータがサポートされていません。そのため、代わりに`data`パラメータに自力でJSON化した文字列を渡すよう修正する必要が有ります。

```diff
- response = requests.post(endpoint, json=req_body)
+ response = requests.post(endpoint, data=json.dumps(req_body))
```

この修正は、pepper-web-boxesリポジトリの最新バージョンでは既に適用されており（[Pull Request \#1](https://github.com/yacchin1205/pepper-web-boxes/pull/1)）、Pepper 2.5.5上で動作することを確認しています。

# その他のNAOqi OSパッケージの更新

今回は、実行してみてエラーとなったことが確認できた例ですが、確実にアプリをメンテナンスするためには、こまめなドキュメント調査をしていきたいところです。

NAOqi OS 2.5 のすべてのパッケージ更新情報は、下の公式ドキュメントにより確認できます。

[NAOqi OS 2\.5 — Aldebaran 2\.5\.5\.5 documentation](http://doc.aldebaran.com/2-5/news/2.5/naoqi_os_rn2.5.html)

幸いなことに、ダウングレードされているパッケージはrequestsのみですが、メジャーバージョンが変わっているもの、削除されているものはいくつか見られるので、注意が必要そうです。


# まとめ

NAOQi OSのアップデートにより内部パッケージが更新され、スクリプトの修正が必要な例を見てきました。

今回のようにボックスやアプリ側の少しの修正で済めば良いですが、プラットフォームから提供されるソフトウェアパッケージはバージョンアップにより大きく変更がなされます。いくつものパッケージのバージョンが破壊的に変更されるので、できることなら依存ライブラリをそのままプロジェクト内に含めるなどして、NAOqi OS内のパッケージに依存しないようプログラムを組んだほうが良いかもしれません。
[spec] XDG Base Directory Specification

PATH忘れるので、メモです。
Ubuntu 16.04 gnome-desktop上で確認。

Linuxは、`$HOME/.foo`
macOSは、`$HOME/Library`
が使われている。

とりあえず各Appは、 `$HOME/.appname` ではなく `$HOME/.config/appname` とかを使おうよ！
`ls -a ~` で不要なものありすぎ！

# 表
| environment variable | path |
| --- | --- |
| $XDG_DATA_HOME | $HOME/.local/share |
| $XDG_CONFIG_HOME | $HOME/.config |
| $XDG_CACHE_HOME | $HOME/.cache |
| $XDG_DATA_DIRS | /usr/share/gnome:/usr/local/share/:/usr/share/:/var/lib/snapd/desktop |
| $XDG_CONFIG_DIRS | /etc/xdg/xdg-gnome:/etc/xdg |
| $XDG_RUNTIME_DIR | /run/user/(user id) |

# 参照
https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html