unhurried

コンピュータ関連ネタがほとんど、ときどき趣味も…

シリコンバレー周辺のお土産

最近にそれなりの量のお土産を買う機会があったので、お土産に良さそうなものを調べてみました。

探す条件

  • 多人数に配れるように個包装されている食べ物や飲み物
  • シリコンバレー周辺で購入できるもの
  • カリフォルニア、アメリカ、もしくは北米地域発祥で日本では入手しにくいもの

お土産リスト

  • Ghirardelli Chocolate

    • サンフランシスコ発祥の1852年から続く老舗チョコレートメーカー
    • 正方形のチョコレートが定番で色々なフレーバーがある
    • Great Mallにアウトレット、West Field Valley Fairに小さな販売ブースがある他、スーパーマーケット等でも購入できる
    • (アメリカの定番お土産になっているので新鮮味は少ないかも)
  • See's Candies

    • ロサンゼルスにて1921年に開業したチョコレート、キャンディメーカー
    • 棒つきのキャンディやチョコレートが人気がある
    • シリコンバレー周辺にお店が5,6店舗ある
  • Sugarfina

    • ロサンゼルスにて2012年に設立されたおしゃれなキャンディショップ
    • 箱に色々な種類のキャンディを組み合わせて詰めて
    • Santana Rowにお店がある
  • スーパーマーケットで購入できるお菓子(キャンディ、チョコレートなど)

    • Whole Foods、Trader Joe's、Safewayなどで購入できる
    • (Trader Joe'sのリテールブランドのお菓子は個包装のものが少ない)
  • DAVIDs TEA

    • カナダのトロントに2008年に開店した紅茶ショップ
    • アメリカにも多くの店舗を出店していて人気がある
    • Palo Altoにお店がある

How to cope with Callback Hell #2 co generator

The second countermeasure of Callback Hell is using co. co is a library which utilizes Generator mechanism and make it easy to write serial and parallel flows.

const co = require('co');
const thunkify = require('thunkify')

// a function which has a callback in its argument
var callbackFunction = function(message, timeout, callback) {
    setTimeout(function() {
        console.log(message);
        callback(null, message);
    }, timeout);
};

// thunkify converts a function with a callback to one returning a thunk.
// yield can be applied to thunk in generator. (yeildable)
var thunkifiedFunction = thunkify(callbackFunction);

// a function which returns a Promise
var promiseFunction = function(message, timeout) {
    return new Promise(function(resolve, reject){
        setTimeout(function() {
            console.log(message);
            resolve(message);
        }, timeout);
    });
};

// When a Generator (function*(){...}) is passed to co,
// co will proceed the process of the Generator until a yeild statement appear,
// and resume the process after the Promise object is finished.
co(function*() {
    console.log('--- co start ---');
    
    // Process two functions in parallel.
    // Specify an array or an object to yeild.
    var response = yield [
        thunkifiedFunction('co two', 800),
        promiseFunction('co one', 600),
    ];
    console.log("response: " + JSON.stringify(response));

    // Process two functions serially.
    yield thunkifiedFunction('co three', 200);
    yield promiseFunction('co four', 400);

    // Functions defined using co.wrap can be used in co generator.
    yield wrappedFunction();

    console.log('--- co end ---');

    // When return is called, co.then() is executed.
    return "Generator finished.";

    // When a reject function is called in one of Promise objects, 
    // co.error() is executed.
    // yield Promise.reject("An error happened.");

}).then(function(value){
    console.log('--- then start ---');
    console.log(value);

}).catch((error) => {
    console.log('--- error start ---');
    console.log(error);
});

// Functions using co generator can be defined by using co.wrap.
// They are also yieldable because they also return a Promise object.
var wrappedFunction = co.wrap(function* () {
    console.log('--- co.wrap start ---');

    var response = yield [
        thunkifiedFunction('co.wrap two', 800),
        thunkifiedFunction('co.wrap one', 600),
    ];
    console.log("co.wrap response: " + JSON.stringify(response));
    yield thunkifiedFunction('co.wrap three', 200);
    yield thunkifiedFunction('co.wrap four', 400);

    console.log('--- co.wrap end ---');

    return yield Promise.resolve(null);
});

Result

$ node co_generator.js
--- co start ---
co one
co two
response: ["co two","co one"]
co three
co four
--- co.wrap start ---
co.wrap one
co.wrap two
co.wrap response: ["co.wrap two","co.wrap one"]
co.wrap three
co.wrap four
--- co.wrap end ---
--- co end ---
--- then start ---
Generator finished.

Callback地獄対策 (2) co generator

Callback地獄対策の2つ目はcoについてまとめました。
coはGeneratorの仕組みを利用して簡単に並列・直列処理フローを記述できるようにするライブラリです。

const co = require('co');
const thunkify = require('thunkify')

// callbackを引数にとる一般的な関数
var callbackFunction = function(message, timeout, callback) {
    setTimeout(function() {
        console.log(message);
        callback(null, message);
    }, timeout);
};

// thunkifyはcallbackを引数にとる関数をthunkを返却する関数に変換する
// thunkにはgenerator内でyeild式を適用できる(yeildable)
var thunkifiedFunction = thunkify(callbackFunction);

// Promiseを返却する一般的な関数
var promiseFunction = function(message, timeout) {
    return new Promise(function(resolve, reject){
        setTimeout(function() {
            console.log(message);
            resolve(message);
        }, timeout);
    });
};

// co関数にGenerator(function*(){...})を渡すと、
// co関数はGeneratorの処理をyeild式まで進め、
// yeild式に指定されたPromiseの完了を待ってから処理を再開させる
co(function*() {
    console.log('--- co start ---');
    
    // 2つの関数を並列処理する
    // 並列処理をするにはyeildにArrayもしくはObjectを指定する
    var response = yield [
        thunkifiedFunction('co two', 800),
        promiseFunction('co one', 600),
    ];
    console.log("response: " + JSON.stringify(response));

    // 2つの関数を直列処理する
    yield thunkifiedFunction('co three', 200);
    yield promiseFunction('co four', 400);

    // co.wrapで定義した関数(後述)はco generator内で利用できる
    yield wrappedFunction();

    console.log('--- co end ---');

    // Generatorでreturnするとco.then()に指定した関数が実行される
    return "Generator finished.";

    // いずれかのPromiseでreject関数が呼ばれると
    // co.error()に指定した関数が実行される
    // yield Promise.reject("An error happened.");

}).then(function(value){
    console.log('--- then start ---');
    console.log(value);

}).catch((error) => {
    console.log('--- error start ---');
    console.log(error);
});

// co.wrapを使うことでco generatorを利用した関数を定義できる
// この関数もPromiseを返却するのでyeildableとなる
var wrappedFunction = co.wrap(function* () {
    console.log('--- co.wrap start ---');

    var response = yield [
        thunkifiedFunction('co.wrap two', 800),
        thunkifiedFunction('co.wrap one', 600),
    ];
    console.log("co.wrap response: " + JSON.stringify(response));
    yield thunkifiedFunction('co.wrap three', 200);
    yield thunkifiedFunction('co.wrap four', 400);

    console.log('--- co.wrap end ---');

    return yield Promise.resolve(null);
});

実行結果

$ node co_generator.js
--- co start ---
co one
co two
response: ["co two","co one"]
co three
co four
--- co.wrap start ---
co.wrap one
co.wrap two
co.wrap response: ["co.wrap two","co.wrap one"]
co.wrap three
co.wrap four
--- co.wrap end ---
--- co end ---
--- then start ---
Generator finished.

How to cope with Callback Hell #1 Promise

We often fall into what is called "Callback Hell" when we write some asynchronous processes with Node.js, which is a problem the hierarchy of the code becomes too deep.

There are many countermeasures and it looks a little messy, so I will sort out them in several posts. This time, I will introduce the way of returning Promise objects which hold the running state in an asynchronous process instead of making a callback an argument.

// An example of a function returning Promise.
var promiseFunction = function(message, timeout) {
    // Pass a function which has resolve and reject as arguments
    // when creating a Promise object.
    // * resolve: a function returning an object when the process succeeds
    // * reject: a function returning an error object when the process fails
    return new Promise(function(resolve, reject){
        setTimeout(function() {
            console.log(message);
            resolve(message);
            // Call reject function when an error occurs.
            // reject(error);
        }, timeout);
    });
};

// The usage of a Promise instance
var promise = promiseFunction('promise', 100);
// then: write post-processing
promise.then(function(result){
    console.log("result: " + JSON.stringify(result));
// catch: write error processing
}).catch(function(error){
    console.log("error: " + JSON.stringify(error));
});

Result

$ node promise.js
promise
result: "promise"

Reference

Callback地獄対策 (1) Promise

NodeJSで非同期処理を書いていくと必ずはまるのが、Callbackで階層が深くなりすぎるいわゆるCallback地獄です。対策を調べてみたところ色々ありすぎて混乱してきてしまったので、何回かに分けて整理してみようと思います。

今回は、非同期処理を行う関数でCallbackを引数にとる代わりに、実行状態を保持するPromiseオブジェクトを返却する方法をまとめてみました。

// Promiseを返却する関数の例
var promiseFunction = function(message, timeout) {
    // Promiseオブジェクト生成時の引数に
    // resolve、rejectを引数に持つFunctionを渡す
    // * resolve: 処理成功時の返却オブジェクトを渡す関数
    // * reject: 処理失敗時のエラーオブジェクトを渡す関数
    return new Promise(function(resolve, reject){
        setTimeout(function() {
            console.log(message);
            resolve(message);
            // エラー発生時はreject関数を呼び出す
            // reject(error);
        }, timeout);
    });
};

// Promiseインスタンスの使い方
var promise = promiseFunction('promise', 100);
// then関数:後処理を記述する
promise.then(function(result){
    console.log("result: " + JSON.stringify(result));
// catch関数:エラー処理を記述する
}).catch(function(error){
    console.log("error: " + JSON.stringify(error));
});

実行結果

$ node promise.js
promise
result: "promise"

参考: http://qiita.com/koki_cheese/items/c559da338a3d307c9d88

Forever Stampでアメリカから国際郵便を送る

アメリカから国際郵便(USPS Frist-Class Mail Int'l)を送るときはGlobal Forever Stampを1枚貼れば良いのですが、普段国内郵便しか使わない人にとっては、Global Forever Stampが手元にないことが多いと思います。調べてみると、国内郵便用のForever Stampでも少し損はするものの国際郵便も送れるそうです。

→ Foever Stampを3枚貼れば国際郵便を送ることができます。

Google Cloud Print APIを使った印刷手順

最近はどのプリンターもインターネットに繫がっているのが当たり前になって来ています。Webサービスを作るときにバックグラウンドでプリンターに印刷ジョブを投げれたらいいなというケースがいくつかあったので、その手段の一つとしてGoogle Cloud Print APIを調べてみました。

Google Cloud Print APIを使ってWebページを印刷する手順

(0) Google API ConsoleでクライアントIDとクライアントシークレットを取得する

(1) 認可画面を出力し、ユーザーから認可をもらう

https://accounts.google.com/o/oauth2/v2/auth?
redirect_uri={登録したリダイレクトURI}&
response_type=code&
client_id={発行されたクライアントID}&
scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloudprint&
access_type=offline

(2) リダイレクトURIにて認可コードを受信し、アクセストークンを取得する

  • リダイレクトURIのcodeパラメータに認可コードが付与されてリダイレクトされて来ます。
  • URIから取得した認可コードをToken Endpointに送信し、アクセストークンを取得します。
リクエスト

POST https://www.googleapis.com/oauth2/v4/token
Content-Type: application/x-www-form-urlencoded

code={認可コード}&
client_id={発行されたクライアントID}&
client_secret={発行されたクライアントシークレット}&
redirect_uri={登録したリダイレクトURI}&
grant_type=authorization_code

レスポンス

{
  "access_token": {アクセストークン},
  "token_type": "Bearer",
  "expires_in": 3600,
  "refresh_token": {リフレッシュトークン}
}

(3) プリンター検索APIを呼び出し、印刷したいプリンタのプリンタIDを取得する

リクエスト

GET https://www.google.com/cloudprint/search
Authorization: OAuth {アクセストークン}

レスポンス

{
    "printers": [
    {
        "id": {プリンタID},
        "displayName": {プリンタの表示名},
        ...
    }]
        ...
}

(4) プリンタジョブ送信APIを呼び出し、印刷を実行する

  • 例)URLを指定してそのWebページを印刷するリクエスト
POST https://www.google.com/cloudprint/submit
Content-Type: application/x-www-form-urlencoded

printerId={プリンタID}&
title={印刷タイトル}&
contentType=url&
content={印刷するURL}&
ticket={チケットオブジェクト}
{"version":"1.0","print":{"vendor_ticket_item":[],"color":{"type":"STANDARD_MONOCHROME"},"copies":{"copies":1}}}

参考