tetsunosukeのnotebook

tetsunosukeのメモです

メモ: Sass をWindowsに入れようとしたらgemで怒られたよ

デザイナーのデスクトップ(Windows)に、Sassを入れようとしたんだけど、

C:\work>gem install sass
ERROR:  While executing gem ... (Errno::EACCES)
    Permission denied - C:/Program Files/ruby-1.9.3/bin/sass.bat

インストールに失敗!!

したので、下記の手順で対応。

Ruby をインストールするときにできる、
Windowsのスタートメニュー→すべてのプログラム→ Ruby 1.9.3-p194
→ Start Command Prompt With Ruby を右クリック
→ 「管理者として実行」 すると、黒い画面になるので

C:\Windows\system32>gem install sass
Fetching: sass-3.2.10.gem (100%)
Successfully installed sass-3.2.10
1 gem installed
Installing ri documentation for sass-3.2.10...
Installing RDoc documentation for sass-3.2.10...

成功しました!

与えられた変数が数字かどうか調べる のに ctype_digitを使って死んだ話

プログラムの受け入れテストをしていて、どうしても通らない箇所があって、こんな感じに書いてあったのです

if (ctype_digit($num)) {
    // 通ってくれない処理
}

まあもっと言うとこんな

$list = array(
    '1' => 10,
    '2' => 20,
);

foreach($list as $key => $value) {
    if (ctype_digit($key)) {
        // 通ってくれない処理
    }
}


というわけで

<?php
$list = array(
    '1' => 10,
    '2' => 20,
);

foreach($list as $key => $value) {
    var_dump($key);
    var_dump(ctype_digit($key));
}
int(1)
bool(false)
int(2)
bool(false)

ファッ?!

ああ、これはPHPの配列のキーがいつのまにかintegerになっちゃう問題なのねー。

というわけで ctype_digitは

この関数を活用するには string を渡さなければなりません。

ってやつだよ。

で、この$listはもともとこんな風にベタでは書いてなくて、実際には'98765432' みたいな値が入る。なので、動作テスト時にこれを書いた人は通ったんだろう。

で、なんで通るの。

<?php
var_dump(ctype_digit(98765432));
bool(true)

なんとっ

たとえば integer を渡すと、期待する結果にならない可能性があります。 HTML フォームに入力された整数値は、integer ではなく string 型で返されます。 マニュアルの 型 についての節を参照ください。

ああこれなのね

var_dump(ctype_digit(47));
var_dump(ctype_digit(48));
bool(false)
bool(true)

うわー。

<?php
for( $i = 1; $i <= 100; $i++) {
    if (ctype_digit($i) === TRUE) {
        echo 'T';
    } else {
        echo 'F';
    }

    if( $i % 10 === 0 ) {
        echo PHP_EOL;
    }
}
FFFFFFFFFF
FFFFFFFFFF
FFFFFFFFFF
FFFFFFFFFF
FFFFFFFTTT
TTTTTTTFFF
FFFFFFFFFF
FFFFFFFFFF
FFFFFFFFFF
FFFFFFFFFF

見事に48~57までナイジャー・モーガン

var_dump(chr(47));
var_dump(chr(48));
var_dump(chr(49));
var_dump(chr(57));
var_dump(chr(58));
string(1) "/"
string(1) "0"
string(1) "1"
string(1) "9"
string(1) ":"

あーなるほどねー。

  • 128 から 255 までの整数値を渡すと、ひとつの文字の ASCII 値とみなします (負の値には 256 を足して、拡張 ASCII の範囲に収まるようにします)。 それ以外の整数値は、10 進数を含む文字列とみなします。


あぶねーなーもう

Web検索系のAPIって結構死んでるんだ・・・(BingWeb検索APIを試してみたよ)

RESTとかで使えるAPIを探せ

特定のキーワードで検索して上位3件のデータを取りたい!

みたいなことを思ってWeb検索系のAPIを探してみました。

Yahoo!


そういえばYahoo!の検索が有料になったんだっけとか思ったら、もう終了する予定の模様。

http://developer.yahoo.co.jp/webapi/search/

□提供終了日:
・2013年8月14日
※新規利用の申請については既に終了させていただいております。
※現在ご利用いただいているみなさまは、更新いただくことなく提供終了日までご利用いただけます。

Google

Googleさんは?

https://developers.google.com/web-search/?hl=ja

Note: The Google Web Search API has been officially deprecated as of November 1, 2010. It will continue to work as per our deprecation policy, but the number of requests you may make per day will be limited. Therefore, we encourage you to move to the new Custom Search API.

なんかもうCustomSearchAPI使って世界みたいね。両方共(まあ中身同じだから?)

それではCustomSearchAPIは?

Important: The Google Custom Search API requires the use of an API key, which you can get from the Google APIs console. The API provides 100 search queries per day for free. If you need more, you may sign up for billing in the console.

無料だと1日100回までなのかー。詰んだ。

そもそも自サイト内検索とかが主な目的みたいだしなんか違う気がする。

Bing

最初思いつかなかったけどこんなエンジンもありましたね

http://datamarket.azure.com/dataset/bing/search

ふむ。Windows Azure Marketplaceとやらに登録して利用することができるらしい。こっちは月に5000リクエストまでOKのようだ。Googleよりちょっとだけ多いレベル。

すんごいわかりにくいんだけどページ右側に Bing API Quick Start & Code というのがある。

これを見てみると、.NETとかPHPのサンプルコードが載っていた。

あと、これもまたわかりにくいのだけど、(つかこのテキストリンクなのかよ)

このデータセットの参照
DataMarket サービス エクスプローラーを起動して、データセットを対話形式で参照できます。

を参照すると、実際にどういうURLを叩くとどういう結果が返ってくるか試せる。(トランザクション数がカウントされて減らされるので注意)

例えば

Query: 検索
Markter:ja-JP

を入れると、

こんなかんじのURLになるそうだ。
https://api.datamarket.azure.com/Bing/Search/v1/Web?Query=%27%E6%A4%9C%E7%B4%A2%27&Market=%27ja-JP%27

だが、このURLを直接叩くとベーシック認証を要求される。

このURLはベーシック認証で叩くものらしい。そのキーはといえば、

https://datamarket.azure.com/account/keys アカウントキーというところで取得できる。アカウントキーのくせに、ベーシック認証のID/パスワード両方ともこれらしい。なんだそら。

踏まえてプログラム

サンプルはそのまま動かなかったのでcurlバージョン。

<?php
// 検索したい文字列
$query = "検索";
// アカウントキー
$accountKey = '<key>';
// とりあえずWeb検索に限定。結果はJSONにする。「$format」なので注意。
$ServiceRootURL = 'https://api.datamarket.azure.com/Bing/Search/v1/Web?$format=json';
// ここはとりあえず検索キーワードだけ
// それぞれのパラメータをシングルクォートでくくる仕様
$queryParameters = array(
    'Query' => "'" . $query . "'",
);
$url = $ServiceRootURL. "&" . http_build_query($queryParameters);
$result = curl_get($url, $accountKey);

// 先頭の1件を出力
var_dump($result[0]);

function curl_get($url, $key) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    // ID/PWともにアカウントキーなので
    curl_setopt($ch, CURLOPT_USERPWD, $key. ":" . $key);
    curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    $buf = curl_exec($ch);
    curl_close($ch);
    $json = json_decode($buf);
    return $json->d->results;
}
結果
object(stdClass)#3 (6) {
  ["__metadata"]=>
  object(stdClass)#4 (2) {
    ["uri"]=>
    string(91) "https://api.datamarket.azure.com/Data.ashx/Bing/Search/v1/Web?Query='検索'&$skip=0&$top=1"
    ["type"]=>
    string(9) "WebResult"
  }
  ["ID"]=>
  string(36) "b8d565a7-0757-4603-8981-bc139c88e4b8"
  ["Title"]=>
  string(33) "郵便番号検索 - 日本郵便"
  ["Description"]=>
  string(270) "郵便番号検索はこちらから。地図、住所、郵便番号から郵便番号を検索できます。 English サイトマップ よくあるご質問・お問い合わせ ここからサイト内検索です 検索したい文字列を入力してください"
  ["DisplayUrl"]=>
  string(29) "www.post.japanpost.jp/zipcode"
  ["Url"]=>
  string(37) "http://www.post.japanpost.jp/zipcode/"
}

PILで文字画像を抜き出す

初めてのPIL。
もっとカンタンに書けるかもしれないけど無理やり。

こんなデータから
f:id:kidd-number5:20130604192047j:plain

白い部分を取り除いてこんなかんじにする
f:id:kidd-number5:20130604192112j:plain

# -*- coding: utf-8 -*-

from PIL import Image
from numpy import *

im = Image.open("test.jpg")

# とりあえず配列にする
pixel_array = array(im)

x = []
y = []
for i in range(0, im.size[0]):
    for j in range(0, im.size[1]):
        # 白以外のピクセルを探索
        # ここはもう少しゆるいしきい値でも良いと思う
        if pixel_array[i][j].tolist() != [255, 255, 255]:
            y.append(i)
            x.append(j)

# x, yそれぞれの最大最小が白以外に初めて出る色。
# そのまま切るとジャストで切れちゃうから2ピクセルくらいのバッファを確保            
buffer = 2
left = min(x) - buffer
top = min(y) - buffer
right = max(x) + buffer
bottom = max(y) + buffer

# 切り取って保存
cropped_im = im.crop((left, top, right, bottom))
cropped_im.save("cropped.jpg")

これで手書き文字認識とかもできるのかもしれないとちょっと夢が広がった。

karmaでらくらくJavaScriptをテスト

Testaclarの後継?ことKarmaを使ってみたのでメモ。

※ 現在のカバレッジ取得は
karma で テストしながらカバレッジを取る - tetsunosukeのnotebook を参考にしてください。


Karma(元Testacular)を使って簡単にテストを実行しよう をほぼ参考にしました。

node.js をインストールしているマシンで

c:\> npm install

適切なフォルダで

c:\work> karma init

するといろいろ聞かれる。
テストフレームワークはJasmineを使うことにする。

Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> jasmine

Do you want to use Require.js ?
This will add Require.js adapter into files.
Press tab to list possible options. Enter to move to the next question.
> no

Do you want to capture a browser automatically ?
Press tab to list possible options. Enter empty string to move to the next quest
ion.
Chrome

Which files do you want to test ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.


Any files you want to exclude ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.


Do you want Testacular to watch all the files and run the tests on change ?
Press tab to list possible options.
> yes

Config file generated at "c:\work\karma.conf.js".


こんな感じで初期設定が終わる。

下記の箇所に、ソースファイルとテストファイルの場所を追加しよう。

// list of files / patterns to load in the browser
files = [
  JASMINE,
  JASMINE_ADAPTER
];

こんなかんじ。

// list of files / patterns to load in the browser
files = [
  JASMINE,
  JASMINE_ADAPTER,
  'src/*.js',
  'spec/*.js'
];

スクリプトを書いてみる

// src/add.js
function add (a, b) {
    return a + b;
}

// spec/addSpec.js
describe("test add", function() {
    it("1+2=3", function() {
        expect(add(1, 2)).toEqual(3);
    });
})

この状態で

c:\> karma start

すると、下記のようになる。

INFO [karma]: Karma server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 26.0 (Windows)]: Connected on socket id zdixoohRJSrQcoer04vh
Chrome 26.0 (Windows): Executed 1 of 1 SUCCESS (0.346 secs / 0.002 secs)

f:id:kidd-number5:20130409122738p:plain

テストが起動しっぱなしになっているので、
この状態でテストを失敗させてみたりすると、

// spec/addSpec.js
describe("test add", function() {
    it("1+2=3", function() {
        expect(add(1, 2)).toEqual(30);
    });
});

こんな具合に自動認識される。

INFO [watcher]: Changed file "c:/work/spec/addSpec.js".
Chrome 26.0 (Windows) test add 1+2=3 FAILED
        Expected 3 to equal 30.
        Error: Expected 3 to equal 30.
            at null. (c:/work/spec/addSpec.js:4:27)
Chrome 26.0 (Windows): Executed 1 of 1 (1 FAILED) (0.339 secs / 0.008 secs)

オプションいくつか

karma.conf.js

browsers

['Chrome', 'IE'] のようにすることで複数ブラウザを扱うことができる

singleRun

singleRun = True; にすると、都度コマンドで立ち上げる形になる。CI環境で使う場合はこちら

カバレッジを見る

以下のようにして実行する

preprocessors = {
  'src/*.js': 'coverage'
};
reporters = ['progress', 'coverage'];

coverageReporter = {
  type: 'html',
  dir : 'coverage/'
};

karma start したあとに、coverageフォルダができて、その中のindex.html を開くと下記のようになります。

f:id:kidd-number5:20130409124024p:plain

ファイル単位

f:id:kidd-number5:20130409124037p:plain


Jasmineを入れてから~ とかしなくて良いので初期設定が結構ラクチンでした。

GoogleApps で社外(自ドメイン以外)の人を宛先に含む場合のTips

実は対策がされている

補完って便利ですよね!でも補完のせいで、うっかり宛先にすべきでない人を宛先にしてしまいお詫びをした経験はありませんか?誰しも一度くらいはやってしまったことがあるのではないかと思います。

GoogleAppsの場合、自ドメイン以外のメールアドレスに対しては、ある程度の目印をつけてくれているのですがご存知でしたか?

新しいメール作成画面の場合

f:id:kidd-number5:20130307115439p:plain
アドレスをクリックすると

f:id:kidd-number5:20130307115446p:plain

古いメール作成画面

f:id:kidd-number5:20130307115450p:plain

うすっ!
気づかないよね!普通気づかないよね!言われてから初めて気づくレベルだよね!

というわけで

これをユーザCSSで目立たせてみよう
というわけです。

例えばChromeをお使いの場合、Stylist などの拡張を用いてユーザスタイルシートを作成します。

f:id:kidd-number5:20130307120112p:plain

私の場合はこのように設定しています。

この設定で自ドメイン以外のメールアドレスを入れると・・・

f:id:kidd-number5:20130307120348p:plain
f:id:kidd-number5:20130307120356p:plain

ほらわかりやすい!

ご活用ください。

注:クラス名がもし変わったりしたら使えなくなるので注意...