読者です 読者をやめる 読者になる 読者になる

Angular2で作る天気予報チュートリアルver.2

Angular2で天気予報サービス作成する

前回の「Angular2で作る天気予報チュートリアルver.1」の続きからです。前回は環境構築と、静的に都市を入力しその都市の名前から天気予報APIによって天気予報情報を得るところまでしました。

今回の目的

今回は都市の入力フォームから動的にデータを取得し、表示させることをやっていきます。

完成作品

f:id:suga-tech3:20161225171430p:plain

https://drive.google.com/file/d/0BzC949acVHdubU1jbFlpLWdtQmM/view?usp=sharing

完成ソース

github.com

ポイント

-- formを使って都市の天気予報を取得する

  • weather-search.component.ts
<form (ngSubmit)="onSubmit(f)" #f="ngForm">
    <label for="city">City</label>
    <input ngControl="location" type="text" id="city" (input)="onSearchLocation(input.value)" required #input>
    <button type="submit">Add City</button>
</form>
  • ①buttonのsubmitが押された時の処理
  • ②onSearchLocation()で都市の名前を取得する
  • ③ngOnInit()で予測予測変換機能作成する
export class WeatherSearchComponent
{
    private searchStream = new Subject<string>();
    data: any = {};

    // WeatherServiceを使う
    constructor (private _weatherService: WeatherService) {}

    // ①form内のボタンが押された時に天気情報を追加する
    onSubmit(form: ControlGroup) {
        this._weatherService.searchWeatherDate(form.value.location)
            .subscribe(
                data => {
                    const weatherItem = new WeatherItem(data.name, data.weather[0].description, data.main.temp);
                    this._weatherService.addWeatherItem(weatherItem);
                }
            );
    }
    // ②都市の名前を取得する
    onSearchLocation(cityName: string) {
        this.searchStream
            .next(cityName);
    }
    // ③予測変換してくれる機能
    ngOnInit() {
        this.searchStream
            .debounceTime(300)
            .distinctUntilChanged()
            .switchMap((input:string) => this._weatherService.searchWeatherDate(input))
            .subscribe(
                data => this.data = data
            );
    }
}
  • weather.service.ts

-- ①のsubmitが押された時にserviceにてAPIで都市のデータを取得する

searchWeatherDate(cityName: string): Observable<any> {
    return this._http.get('http://api.openweathermap.org/data/2.5/weather?q=' + cityName + '&APPID=任意のID&units=metric')
        .map(response => response.json())
        .catch(error => {
            console.error(error);
            return Observable.throw(error.json())
        });
}

-- ①のsubmitが押された時にserviceにてAPIで都市のデータをpushして追加する

addWeatherItem(weatherItem: WeatherItem) {
    WEATHER_ITEMS.push(weatherItem);
}

追加した都市のデータを取得し表示させる

  • weather-list.component
@Component({
    selector: 'weather-list',
    template: `
        <section class="weather-list">
            <weather-item *ngFor="#weatherItem of weatherItems" [item]="weatherItem"></weather-item>
        </section>
    `,
    directives: [WeatherItemComponent]
})
export class WeatherListComponent implements OnInit {
    weatherItems: WeatherItem[];

    constructor(private _weatherService: WeatherService) {
    }
    // 全天気予報情報を取得する
    ngOnInit():any {
        this.weatherItems = this._weatherService.getWeatherItems();
    }
}

上記でデータを追加する動きをすることができました。 他の機能やサイドバーは完成ソースを見てみてください。

まとめ

チュートリアルすべて終わりです。 componentの概念とAngular2のDIの使い方が複雑でなかなか理解するのに時間がかかった。すべて自分で作るにはなかなかの学習コストが必要になると感じました。

Angular2で作る天気予報チュートリアルver.1

背景

巷で流行っているReactとAngular2、今回はどちらも学習難易度が高いですが Reactより考え方が難しくないという理由でAngular2を自分で学んでみました。 今回は前回のlaravelでのショッピングサイトと同様に同じYouTuberのチュートリアルに沿って学習を始めていきました。

参考URL

Angular 2 Full App Tutorial - Weather App - #1 Introduction - YouTube

*事前にtypescriptをかじっておくことがおすすめです。

 環境構築

$ git clone https://github.com/mschwarzmueller/angular2-weather-app-tut.git
$ npm install
$ npm start

以下のような画面が表示されることを確認します。

f:id:suga-tech3:20161128145904p:plain

まずは構造を見ていきましょう。

├── LICENSE.md
├── README.md
├── app
│   ├── app.component.js
│   ├── boot.js
│   └── weather
├── assets
│   └── scss
├── dev
│   ├── app.component.js
│   ├── app.component.js.map
│   ├── app.component.ts
│   ├── boot.ts
│   └── weather
├── gulpfile.js
├── index.html
├── package.json
├── src
│   └── css
├── tsconfig.json
├── typings
└── typings.json

このようなディレクトリ構造がわかります

次のように複数の地域名と天気予報情報を表示してみましょう。

f:id:suga-tech3:20161128150003p:plain

作ってみよう

app/weatherにweather-item.tsを作成する。

  • WeatherItemのクラスを定義しコンストラクタにてそれぞれ入る引数と静的型付けを行う。
export class WeatherItem {
    constructor(public cityName: string, public description: string, public temperature: number) {
    }
}

app/weatherにweather.data.tsを作成。

  • 地域名、天気予報、気温のデータが入っている定数(WEATHER_ITEMS)をインスタンスを作成し用意してあげる。
import {WeatherItem} from "./weather-item";
export const WEATHER_ITEMS: WeatherItem[] = [
    new WeatherItem('London', 'Rainy', 6),
    new WeatherItem('New York', 'Sunny', 10)
];

app/weatherにweather-list.component.tsを作成 - weather.data.tsで作ったデータを受け取って整形し、weather-itemのコンポーネントにデータを渡す。

import {Component} from "angular2/core";
import {WeatherItemComponent} from "./weather-item.component";
import {WeatherItem} from "./weather-item";
import {OnInit} from "angular2/src/core/linker/interfaces";
import {WEATHER_ITEMS} from "./weather.data";
@Component({
    selector: 'weather-list',
    template: `
        <section class="weather-list">
            <weather-item *ngFor="#weatherItem of weatherItems" [item]="weatherItem"></weather-item>
        </section>
    `,
    directives: [WeatherItemComponent]
})
export class WeatherListComponent implements OnInit {
    weatherItems: WeatherItem[];

    ngOnInit():any {
        this.weatherItems = WEATHER_ITEMS;
    }
}
  1. OnInitというインタフェースを実装し、WEATHER_ITEMSをthis.weatherItemsにて受け取る
  2. 受け取ったデータをコンポーネント定義のtemplateにてangular2のfor文を用いweatherItemsを回す。 さらにdataの属性にitemをつけてweather-itemのコンポーネントにデータを渡す。
<weather-item *ngFor="#weatherItem of weatherItems" [item]="weatherItem"></weather-item>

app/weather-item.component.tsを作成

import {Component, Input} from 'angular2/core';
import {WeatherItem} from "./weather-item";

@Component({
    selector: 'weather-item',
    template: `
        <article class="weather-element">
            <div class="col-1">
                <h3>{{ weatherItem.cityName }}</h3>
                <p class="info">{{ weatherItem.description }}</p>
            </div>
            <div class="col-2">
                <span class="temperature">{{ weatherItem.temperature }}°C</span>
            </div>
        </article>
    `,
    styleUrls: ['src/css/weather-item.css'],
})
export class WeatherItemComponent {
    @Input('item') weatherItem: WeatherItem;
}

@Input('item')を用いてweather-list.component.tsのデータ属性itemを指定して データを取得し、templateのhtml文に{{ weatherItem.cityName }}のように入れたい箇所に書いていけば複数表示されていることがわかります。

まとめ

コンポーネントの概念が初めてで書き方が慣れないのと特殊なfor文や属性の受け取りに少し戸惑いましたがなんとか理解しながら進めていけました。 Angular2を学ぶためには事前にtypescriptを学ぶことで理解が早くなるのでそちらから学ぶことが必要であることと 次回にはデータを直書きでセットするのではなく動的に取得し表示させることができればと思います。

vagrantのホスト・ゲスト間の共有フォルダの作り方

環境

背景

Angular2を学ぶためにtypescriptの環境構築をvagrant上でしているときにvagrant側でファイルを作成時にホスト側にもそのファイルを共有させるディレクトリ(フォルダ)が欲しいと思い、色々と調べてみた。

ホスト側 (mac)

  1. Vagrantfileのファイルがあるか確認する
$ ls /Users/kosachan/Typescript
Vagrantfile
  1. Vagrantfileを編集する
$ sudo vi /Users/kosachan/Typescript/Vagrantfile

以下のように修正する。

config.vm.synced_folder "ホスト側Vagrantfileあるディレクトリ", "ゲスト側ディレクトリ"

config.vm.synced_folder "/Users/kosachan/Documents/vagrant", "/home/vagrant/typescript"

ゲスト側 (vagrant)

ホスト側の設定ができたらvagrant を再起動する。

vagrant reload

以上で設定が反映されました。

では実際に反映されるかやってみましょう。

[vagrant@localhost typescript]$ sudo vim test.txt     // txtの中身はhello world!
  • ホスト側 (mac)にて
$ ls /Users/kosachan/Documents/vagrant
test.txt

$ view test.txt
hello world!が表示されると設定がうまくいっています。

まとめ

簡単に共有フォルダを作ることができました。 typescriptのディレクトリを作っている通り次回はtypescriptでAngular2についての記事を書いていきます。

Laravel5.3とStripeAPIでECサイトを作ってみた。

Stripeとは

今あついベンチャー企業の台等となっている決済サービスのstripe。

シリコンバレーでもPayPal以上のペイメント企業になると言われている。 つい先日に日本でもサービスがローンチされたばかりで日本国内最大のクレジットカード会社、三井住友カード株式会社がStripeに資本参加するなどの注目のサービスである。

jp.techcrunch.com

背景

ECサイトを作ってみたかったこととlaravelで何かものを作りたいと思ったことからLaravelでECサイトを作ってみよう。 ただ、そんな簡単に作れるものじゃない...と思い何か材料がないかネットで探してみたところ あるYoutuberの人がlaravel5.2でshopping-cartを作ってみるというチュートリアルを発見!

https://www.youtube.com/watch?v=56TizEw2LgI&list=PL55RiY5tL51qUXDyBqx0mKVOhLNFwwxvH

これで実装してみよう!!

実装のポイント

  • resoucesのlayoutsフォルダにlayoutファイル,viewファイルを作っていく。
  • DBのproductsテーブルから商品一覧を表示させる。
  • 商品クリックでsessionに商品情報を保有する。
  • checkout(購入ボタン)で商品料金の合計と入力フォームを用意する。
  • stripeAPIを使って確定ボタンを押すとstripeのダッシュボードに購入金額が表示される。

f:id:suga-tech3:20161120201239p:plain

f:id:suga-tech3:20161120201301p:plain

*他にも会員か非会員かのAuthの認証もいれる必要があるのでそれの実装と分岐をさせる。

stripeAPIを使う流れ

1.stripeの会員になる

stripe.com

2.ダッシュボードからaccount settingボタンでAPIの情報が載っているので以下のように実装

レイアウトファイルで以下jsを呼び出す。 - checkout.js

Stripe.setPublishableKey('publish-key');

var $form = $('#checkout-form');

$form.submit(function (event) {
    $('#charge-error').addClass('hidden');
    $form.find('button').prop('disabled', true);
    Stripe.card.createToken({
        number: $('#card-number').val(),
        cvc: $('#card-cvc').val(),
        exp_month: $('#card-expiry-month').val(),
        exp_year: $('#card-expiry-year').val(),
        name: $('#card-name').val()
    }, stripeResponseHandler);
    return false;
});

function stripeResponseHandler(status, response) {
    if (response.error) {
        $('#charge-error').removeClass('hidden');
        $('#charge-error').text(response.error.message);
        $form.find('button').prop('disabled', false);
    } else {
        console.log(response);
        var token = response.id;
        $form.append($('<input type="hidden" name="stripeToken" />').val(token));
        // Submit the form:
        $form.get(0).submit();
    }
}
  • ProductConroller.php
        $oldCart = Session::get('cart');
        $cart = new Cart($oldCart);    // app/Cart.php

        Stripe::setApiKey('token');
        try {
            $charge = Charge::create(array(
                "amount" => $cart->totalPrice,
                "currency" => "jpy",
                "source" => $request->input('stripeToken'), // obtained with Stripe.js
                "description" => "Test Charge"
            ));

            $order = new Order();   // app/Order.php
            $order->cart = serialize($cart);
            $order->address = $request->input('address');
            $order->name = $request->input('name');
            $order->payment_id = $charge->id;

            Auth::user()->orders()->save($order);  // 保存
        } catch (\Exception $e) {
            return redirect()->route('checkout')->with('error', $e->getMessage());
        }

まとめ

ほぼ同じようにやっていくと完成できるので難易度は高くなかったが 若干laravelのバージョンが違うからか実装を変えていく箇所などがあった。 stripeAPIは数行のコードを加えるだけでオンライン決済機能を導入できるしECサイトを作ってみたくなったら動画なりドキュメントなりみてやってみてください。

github.com

新卒文系エンジニアIsucon6に参加してみた

Isuconとは

お題となるWebサービスを決められたレギュレーションの中で限界まで高速化を図るチューニングバトル、それがISUCONです。

ISUCON公式Blog

環境

  • サーバ
  • Webサーバ
    • nginx
  • OS
  • 言語
    • php7
  • DB
    • mysql5.7

ルール

  • 制限時間
    • 10時〜18時

チーム名簿だけ見れば気持ち悪い団体ですが偉大な先輩方2人と参加しました。

結論

これまでパフォーマンスの部分をあまり意識せず開発してきた身なので まぁ無理だと思って終了間際にスーパーで買ってきた92円のカップやきそば ×3 を振舞うことを念頭において参加 結論から申し上げるとやはり無力でした。 大きな課題はパフォーマンス部分もそうですがmysqlの氷山の一角部分しか知っていなかったことが明らかになりました。

Isucon(午前)

環境構築から
enomotodev先輩がAzureを起動・設定してデプロイする
鍵のバトンを渡されtakedajs先輩とsshで接続しにいく


その間にenomotodev先輩はnginxにてphpが動くように構築
自分とtakeda.js先輩はソースを見たり、gitbucketとsourcetreeの連携させたバージョン管理の設定をする。 無事scpコマンドでローカルに落とせたソース。 ブランチを切り、いざソースを見てみることに 以下が本問題のソース(今大会にて実装したソースです)

github.com

え〜と、isuda?isutar?
なにがなんやら view側ではhtmlではなくphpテンプレートであるtwigが使われていること

php側でisudaとisutarというファイルが似たソースがあること
一通り眺めてみると何やらmysqlの設定を見つけ とりあえずmysqlに入ってみる
構造はシンプルであるがとりあえずdumpをとっておく


無事とれたのを確認しdumpファイルを見てみるとIndexがついていないことが明らかに。
これはかなりの効果が出るのではないかと判断し、いろんな施策を考える。

とりあえずなんやかんやで午前の部はなんとか7位までつけたところでお昼休憩へ


Isucon6 (午後)

あれ?俺ら7位、いけるんじゃね?となった午前の部

から一転
構築された環境で様々な施策を打つことに


ただよくわからない
自分は「mysql パフォーマンス」などパフォーマンスに関わることを知ることからスタート
とりあえずInnoDBのメモリやQueryのデータ量などを知ることができたがここからどうパフォーマンスをどう上げるか

takeda.js先輩は細かい施策をどんどん実行していく。enomotodev先輩はAPIの改修という大きな施策を打ち出す。
自分はtakedajs先輩の出した施策からインフラ側をチューニングする

特にmysqlInnoDBのログファイルサイズを変更するために そのままの変更ではエラーが出るので


  • 一度mysqlを止める
  • 元にあるログファイルをリネームし、ログファイルサイズを設定
    • mysqlを起動させる

この施策だけで心肺蘇生法のような緊張感に包まれる。(多分自分だけ)

とにかくソースを見てもよくわからないので簡単な施策、計測、やきそばのタイミングを中心に行ってみた。
施策を打ち出すことができない無力さに心が折れかけるもなんとかパフォーマンスを元の状態までもっていくことに成功

ここでtakeda.js先輩の施策が当たる。
SQLのクエリのチューニングを行い、パフォーマンスが10000を超えかなり上がる。
それ以降は大きな施策を打ち出すことができなかったがベストスコアを更新していく

自分は17時以降はやきそばのチューニングにとりかかる
むしろ16時以降はやきそばのことしか考えてなかった。


結局最終結果は14620とtop10にはまだまだ遠い数字となりました。
ただ課題は明確にわかったのと外の世界のエンジニアのすごさに圧巻されたこと。
正直isuconは自分にとっては早かったが良い経験になりました。

これまでパフォーマンスにあまり興味を持てなかったが今回でもっと知りたくなったし、ソースを組む段階からパフォーマンスを意識した開発ができることが大切であると実感させられました。

来年はphpより高速な言語でベストを更新できるようになること 施策を生み出し自分がボトルネックに気づくことができることが来年に向けての課題となりました。

そのためにmysqlの根本的なところ(リレーショナルデータベースの本買いました)を理解しレベルアップしていくしかないと感じました。

Amazon CAPTCHA

それにしても学生で一般も交えた中でも上位のチームがあってとてもすごいとしか言いようがなかった。

みなさまおつかれさまでした。

node.js入門

node.jsとは

Node.jsはサーバー側で動作するJavaScriptであり、Web業界ではかなり注目されています。大量の処理に対応するために、ノンブロッキングI/Oというモデルを採用されている

いまアツいJavaScript!ゼロから始めるNode.js入門〜5分で環境構築編〜

node.js(イベントループ)

node.jsは大量のリクエストを処理していくための仕組みであるが、これまでもApacheのようにスレッドモデルの仕組みが使われていた。

スマホなどで膨大なリクエストが来た場合にどうしても対応できない問題が発生してきたのでnode.jsはイベントループという仕組みを採用した。

  • スレッドモデル

リクエストが来た時にスレッドという処理が立ち上がる。 次のリクエストが来た場合、スレッドは同時に一つのリクエストしか処理できないため新しいスレッドを作って対応することになる。 スレッドを立ち上げるにはメモリを消費するのでたくさんのリクエストが来ると、スレッドを立ち上げることができなくなり、リクエストが待ち状態になってしまう。   

  • イベントループ

そんなスレッドモデルに代わって出てきたのがイベントループ。メインスレッドとバックグランドで動いているスレッドがある。 メインスレッドには処理待ちのキューがあって、そこに処理がたまっている間、ループを回して別のリクエストが来た場合キューに登録していく。 そしてループが回ってキューに何かが登録されているのを見たら順にバックグラウンドの処理に回す。 ただ処理が終わる順番がわからない上、ループをブロックしないように書かなくてはならない。

Webサーバーを作成する

  • server.js
//httpとfsを読み込む
var http = require('http'),
    fs = require('fs');
//settingsファイルを読み込む
var settings = require('./settings');
var server = http.createServer();

//serverにリクエストが来た時の処理
server.on('request', function(req, res) {
    //ファイル読み込む
    fs.readFile(__dirname + '/public_html/hello.html', 'utf-8', function(err, data) {
        if (err) {
            res.writeHead(404, {'Content-Type': 'text/plain'});
            res.write("not found!");
            return res.end();            
        }
        //書き出す
        res.writeHead(200, {'Content-Type': 'text/html'});
        res.write(data);
        res.end();
    });
});
//serverの設定を読み込む
server.listen(settings.port, settings.host);
console.log("server listening ...");
  • settings.js
exports.port = 1337;
exports.host = '192.168.0.11'; //localhost
  • public_html/hello.html
<html>
<h1>Hello!</h1>
</html>

ターミナルにて以下コマンドを実行する

node server.js

f:id:suga-tech3:20160912234401p:plain

mysql × node.js

npm install mysqlでモジュールインストール

var mysql = require('mysql');

//mysql接続情報
var connection = mysql.createConnection({
  host  : 'localhost',
  user  : 'user_name',
  password : 'password',
  database : 'database_name' 
});

//sql作成
var sql = 'SELECT * FROM user WHERE id = ?;';

var userId = "1";

//接続する
connection.connect();

//プレースホルダー使用
var query = connection.query(sql, [userId]);

query
//error処理
.on('error', function(err) {
  console.log('err is: ', err);
})
//結果
.on('result', function(rows) {
    console.log('The res is: ', rows );
})
//終了
.on('end', function(rows) {
  console.log('end');
  connection.destroy(); //接続終了
});

上記ソースを以下コマンドで実行

$ node mysql.js
The res is:  RowDataPacket { id: 1, nickname: 'kosachan' }

上記データが取得できます。 node.jsまだ入門ですが次回はフレームワークexpressと合わせたコードを書いていけたらと思います。

MacにAzure入れてみた

Azureとは

Windows Azureは世界規模に展開されているクラウドサービスで、ユーザーが数人の小規模なサービスから、数百万人規模の大量にアクセスが発生するような大規模なサービスまで、さまざまなクラウドサービスに対応し、開発や提供することができます。

thinkit.co.jp

インストール方法

CLIインストール

Azure SDK とツールのダウンロード | Azure

  • npm パッケージのインストール
    • Node.js と npm がインストールされている必要があります

[Mac] Node.js と npm をインストールする方法 [Homebrew] | CodeNote.net

次のコマンドを使用して Azure CLI パッケージをインストールします。

npm install azure-cli -g

f:id:suga-tech3:20160910094349p:plain


azureコマンドが使うことができるか確認する

azure

f:id:suga-tech3:20160910094401p:plain

他にも azure help

azure version

でazureの情報を確認することができました。

Azure ログインを使用した対話型認証

引数なしで azure login コマンドを使用して、次のいずれかによる対話型認証を行います。

  • 多要素認証を必要とする職場または学校のアカウント ID (組織アカウントとも呼ばれる)

  • Microsoft アカウント ID (Resource Manager モード コマンドにアクセスする場合)

azure login                                                                                                                                                                                         
info:    Executing command login
info:    To sign in, use a web browser to open the page http://aka.ms/devicelogin. Enter the code XXXXXXXXX to authenticate. 

上記操作で表示されたコードをコピーし、ブラウザーを開いて http://aka.ms/devicelogin (または指定された場合は別のページ) にアクセスする。コードを入力すると、使用する ID のユーザー名とパスワードを入力するように求められます。このプロセスが完了すると、コマンド シェルによってログイン プロセスが完了します。

info:    Added subscription Visual Studio Ultimate with MSDN
info:    Added subscription Azure Free Trial
info:    Setting subscription "Visual Studio Ultimate with MSDN" as default
+
info:    login command OK

次にアカウントの発行設定ファイルをダウンロードするには、次のコマンドを使用します。

azure account download

f:id:suga-tech3:20160910095437p:plain

ここでサブスクリプションが取得できるはずがないというメッセージ ここでつまづきましたがmicrosoftのアカウントは作成しているがazureの無料試用版のアカウントは登録してないので取得できない。

azure.microsoft.com

以下URLで必要情報を入力し再度 azure account downloadをすると

ブラウザが立ち上がり f:id:suga-tech3:20160910100112p:plain

証明書のpublishsettingsファイルがダウンロードされる

次に 先ほどダウンロードしたファイルをimportする

azure account import <file>

次回は仮想マシンの作成から進めていきたいと思います。