shimada-kの日記

ソフトウェア・エンジニアのブログです

Node.jsでサーバ間通信

Node.jsのAPI内で別サーバへ問い合わせてその内容をejsへ渡すということをやってみました。リアルタイム性を持たせたいからNode.js/websocketなんだけども、処理全部をjsで書くのはなーという考えからきてます。

    var options = { 
        host : 'localhost',
        // ポート番号
        port : 8001,
        // APIのパス
        path : '/hoge?foo=' + bar, 
    };  

    http.get(options, function(resp) {
        var content = ""; 

        resp.on('data', function(chunk){
            content += chunk;
        }).on('end', function(){
            data = JSON.parse(content);
            console.log(data);

            res.render('index.ejs', {
                layout: false,
                locals: {server_data : data}
            }); 
        }); 
    }).on('error', function(e) {
        console.log("Got error: " + e.message);
    });

Nodeサーバは80番で動かして8001番でApacheを動かしてsinatraでバックエンドを構築しているという環境です。物理的には同じ筐体内で完結しています。

バックエンドはsinatraRubyで書くなら以下のような形式になります。

get '/hoge' do
    #
    # 何かの処理
    #
    JSON.generate(data)
end

JSON.generateしているのはNode側へ渡すときにjson形式にしているからです。json形式に縛られる必要は特にないです。

http-proxyモジュールなどを経由してNodeサーバを動かす場合はルーターの設定を工夫すれば外部からバックエンドのAPIへNode経由ではなく直接アクセスさせることも可能です。

var httpProxy = require('/usr/local/lib/node_modules/http-proxy');

var options = {
  //hostnameOnly: true,

  router: {
    'サーバ名/hoge'    :'localhost:8001/hoge',
  }
};

var prodServer = httpProxy.createServer(options);
prodServer.listen(80);
console.log('prodServer works');

リアルタイム部分はNode.jsで書いてバックエンドは使い慣れた処理系で、といったシチュレーションは結構あるような気がします。

Interface Builderで置いたBarButtonItemにアイコン画像を設定する

UIBarButtonItemのアイコンを独自のものに差し替えたい場合の手段です。若干はまったのでメモ書き。下の画像のように2つのBarButtonItemが配置されているとします。

f:id:shimada-k:20140308232251p:plain

facebookのいいね!の画像をアイコンとして設定することとします。

like40x36.png
f:id:shimada-k:20140308232728p:plain

コードから指定する場合
    UIImage *image = [UIImage imageNamed:@"like40x36.png"];
    [_item1 setImage:image];

僕はsetImageではなくsetBackgroundImageを使用してたためBarButtonItem.titleが表示されてしまい途方にくれていました。setBackgroundImageではなくsetImageを使用します。

Interface Builderから指定する場合

対象のUIBarButtonItemオブジェクトを選択した状態でimageを指定します。画像ファイルがプロジェクトに正しく追加されていれば右側の矢印ボタンから選択できます。ここではitem2の方の画像をいいね画像に差し替えます。

f:id:shimada-k:20140308232302p:plain

ここまでの状態でInterface Builder上では下のような見え方になります。

f:id:shimada-k:20140308232500p:plain

実行すると両方いいね!の画像にアイコン画像が変更されているはずです。

f:id:shimada-k:20140308234004p:plain

コードからアイコン画像を指定する場合はBarButtonItemをアウトレット接続しないといけないのでInterface Builderから指定する方法の方がスマートだと思います。

ソース全文

//
//  BBIViewController.m
//  barButtonIcon
//
//  Created by 島田克弥 on 2014/03/08.
//  Copyright (c) 2014年 shimada-k. All rights reserved.
//

#import "BBIViewController.h"

@interface BBIViewController ()
@property (weak, nonatomic) IBOutlet UIBarButtonItem *item1;
- (IBAction)tabItem1:(UIBarButtonItem *)sender;
- (IBAction)tabItem2:(UIBarButtonItem *)sender;

@end

@implementation BBIViewController

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    UIImage *image = [UIImage imageNamed:@"like40x36.png"];
    [_item1 setImage:image];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

- (IBAction)tabItem1:(UIBarButtonItem *)sender {
    NSLog(@"item1が押されたよ");
}

- (IBAction)tabItem2:(UIBarButtonItem *)sender {
    NSLog(@"item2が押されたよ");
}
@end

CollectionViewとWebViewで画像ビューワを作ってみる

せっかくiOSの開発環境が手元にあるので試しにアプリを作ってみました。
webの画像をCollectionViewで表示する画像ビューワです。

Googleのロゴ画像を出しています。

f:id:shimada-k:20140215232209p:plain

WebViewでロゴ画像のURLを指定してCollectionViewで並べてるだけです。

まずCollectionViewを追加します。ControllerではなくViewのほう。

f:id:shimada-k:20140215232521p:plain

次にCellの中にWebViewを追加します。

f:id:shimada-k:20140215232552p:plain

親子関係はこんな感じ。

f:id:shimada-k:20140215232612p:plain

CollectionViewのセルにIDを振っておきます。ソースからアクセスするためです。

WebViewにタグを付けておきます。これもソースからアクセスするためです。

f:id:shimada-k:20140215233419p:plain

最後にCollectionViewをソースとアウトレット接続します。mファイルに接続しておきます。

これでIBの操作は終わり。あとはソースを書いていきます。

データソースの指定をやります。viewDidLoadの中。

    [_ViewerContents setDataSource:(id)self];

numberOfSectionsInCollectionViewとnumberOfItemsInSectionメソッドを追加します。セクションは1つだけで今回はセルの数を36とか適当に指定しておきます。

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    //セクションは1つ
    return 1;
}

// セルの個数を返すメソッド
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return 36;
}

cellForItemAtIndexPathを追加します。こいつはセルごとに呼び出されるメソッドです。今回はGoogleのロゴ画像を36個分、同じものを表示するので、indexPath.rowでごにょごにょする必要なし。

ここまでで先ほどのアプリが完成。ただし「Google」のGの上の方しか表示されてないのでIBでWebViewを選択して「Scales Page To Fit」にチェックを入れておきます。これで画像ファイルの大きさがWebViewのサイズにあわせて自動的にスケールしてくれます。

f:id:shimada-k:20140215233622p:plain

これで縦のサイズはWebViewにスケールしてくれました。まあ、Googleは横長なんでしょうがないってことで。

f:id:shimada-k:20140215233704p:plain

ソース全文
IVViewController.m

//
//  IVViewController.m
//  ImageViewer
//
//  Created by 島田克弥 on 2014/02/15.
//  Copyright (c) 2014年 shimada-k. All rights reserved.
//

#import "IVViewController.h"

@interface IVViewController ()
@property (weak, nonatomic) IBOutlet UICollectionView *ViewerContents;

@end

@implementation IVViewController

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
    //セクションは1つ
    return 1;
}

// セルの個数を返すメソッド
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return 36;
}

//Method to create cell at index path
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    
    NSLog(@"セル番号だよ index_path.row:%ld", indexPath.row);
    UICollectionViewCell *cell;
    
    cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"cell1"forIndexPath:indexPath];
    cell.backgroundColor = [UIColor blackColor];
    
    UIWebView *image = (UIWebView *)[cell viewWithTag:1];
    
    NSString *str_url = @"https://www.google.co.jp/images/srpr/logo11w.png";
    NSURL * url = [NSURL URLWithString:str_url];
    NSURLRequest *urlReq = [NSURLRequest requestWithURL:url];
    [image loadRequest:urlReq];
    
    return cell;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    [_ViewerContents setDataSource:(id)self];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

@end

参考サイト
Collection Viewの使い方

HYPER ORIGINALITY IIにエンジニアとして参加しました

HYPER ORIGINALITY IIへ技術サイドとして参加しました。

作品としてのコンセプトは下記HPをご参照ください
HYPER ORIGINALITY II

製作物としては、サーバサイドはRubyとNode.js、クライアントサイドはJQueryで構築しました。

一言で言うとリアルタイム画像共有サービスです。PCからもスマホからもアクセス可能です。

f:id:shimada-k:20131105021619j:plain

PC用IF R.G.B. Intaractive Image Grid

モバイル用IF HOII WEB UPLOADER

PC用のページを展覧会場(新宿眼科画廊)のスクリーンに表示しています。スマホからアップロードされた画像がスクリーン上にリアルタイムに表示され、アップロードされた画像は作品として展示物の一部となります。
新宿眼科画廊

12月11日(水)まで展覧会は開催しておりますので、興味がある方はぜひいらしてください。

Titanium Mobileでscanditモジュールを使う

Titanium MobileでQRコードの読み取りをやってみました。

環境
Titanium Studio 2.1
Android 4.0

scanditにサインインして確認メールのURLからダウンロードページに行きます。
そこからzipファイルをダウンロードできます。

Titanium Mobileでモジュールを有効にするときはプロジェクト直下のディレクトリにzipファイルを配置します。

f:id:shimada-k:20130108225525p:plain

この状態でビルドすると自動的にzipファイルが展開されてプロジェクトにモジュールがインストールされます。

あとはscanditのホームページにサンプルのコードが載っているのでプロジェクトのapp.jsにコピーします。

さらにtiapp.xmlにscanditのモジュールを追加して、scanditにサインインした時に付与されるIDをサンプルの「--- ENTER YOUR SCANDIT SDK APP KEY HERE --- SIGN UP AT WWW.SCANDIT.COM」って書いてあるところを置き換えればよいです。

しかしこのままだとAndroidの場合QRコードのスキャンができません。AndroidではQRコードの読み取りはデフォルトでOFFになっているので、有効にしないといけないのです。

picker.setQrEnabled(true);

これでQRコードのスキャンができるようになりました。scanditはQRコードらしき物体を画面内から検出してくれてファインダーが移動してくれるので、多少角度がついていても読み取ってくれます。

左上の懐中電灯をタップするとライトをつけることもできます。

しかしながら、stopScanning()を使おうとするとエラーがでてしまいます。

謎です。stopScanning()の実行に成功された方教えてください。APIの例のまま使ってるだけなんですけどね。。

一部動かせなかった部分もあるんですが、スキャン自体の動作は担保できました。バーコードスキャンのライブラリはそもそも絶対数が少ないので、ある程度使えてTitanium用にビルドされたものがあるだけでもラッキーでした。

FUSEで遊んでみました

これは「カーネル/VM Advent Calendar2012」17日目の記事です。

今回はFUSEを使って画像の中にデータを記録していくファイルシステムを作ってみました。

基本的にファイルシステムを作るときはシステムコールの実装になります(これはOS直書きのファイルシステムでもFUSEでも一緒です)。

ただ、FUSEの場合はシステムコールを直接フックするわけではなく、カーネルモジュールとglibcを間に挟んでいます。FUSEのすばらしいところはユーザランドでFSが書けるのところで、豊富なライブラリを遠慮なくリンクできるのです。

しかもRubyPHPPythonなど主要なLLでのバインディングも充実しているのでLLでファイルシステムを書くことができるわけです。RubyPHPなどのLLを使う場合はさらに言語のエクステンションを間に挟むことになります。

実装はRuby + FUSE + Imlib2です。ソースはgithubにあります。画像の1ピクセル(R・G・B)3バイトにデータを格納する仕組みです。

スクリプトを実行するとデフォルトの設定なら自動でデスクトップにマウントされます。

環境:Linux 2.6.32-5-amd64 [debian squeeze]

./base.rb "マウントポイントのパス" hoge.yml

マウントされたファイルシステムnautilusで表示させたスクリーンショットです。ディレクトリも作ることができます。

f:id:shimada-k:20121217220814p:plain

FUSEはオンメモリのファイルシステムなのでアンマウントするとデータが消えてしまいます。なのでYAMLにデータを保存して永続性を担保しています。この辺はfusefsをインストールした段階でサンプルも入っているのでそちらを参考にしました。

nautilusで開くと上のスクリーンショットのような状態になります。nautilusには「画像のデータである」と返しているのでnautilusは画像データと判断してサムネイルを表示しています。

ファイルブラウザで見る限り一見画像に見えますが、実体はテキストなので通常のテキストエディタで開くことができますし、コンパイルすることができます。

f:id:shimada-k:20121217220834p:plain

上の画像は以下のソースをコンパイルしたものです。透過的な実装というやつです。

#include<stdio.h>

int main(int argc, char *argv[])
{
	puts("笑い男、それがお前の正体か");
	return 0;
}

今回は縦200×横182ピクセルのサイズの画像を扱っています。

テキストにすると上記のソースは120バイト程の内容なので、そのまま画像に格納すると120 / 3 = 40で、横が182ピクセルなので画像のピクセル上1行の4分の1もいかない程度のデータ量ですがコンパイルすると標準ライブラリをリンクしている都合上、バイナリのサイズが大きくなっている様子が可視化できます。

画像の中にピクセルの中にデータを保存しているのでノイズ(というかデータ)が多く入っています。

ただ現状、拡張子がついているとnautilus拡張子の辞書を見に行って、辞書に登録された拡張子ならファイルの内容まで見に行かずにアイコンを決定してしまうという課題があります。

なのでtest.cというファイルを作ると画像形式の内容をnautilusに返してもC言語のソースと判断されてアイコンが目的の画像にはならないということです。

ファイルの拡張子というものはあったらあったで不便ですが、無いとそれはそれで不便なものですね。

FUSEは色んな言語のバインディングがリリースされててアイディア勝負でサクッとファイルシステムが作れるので面白かったです。

apache-passengerで動かすsinatra環境のよさげな設定

自宅サーバsinatraアプリを動かすことにしたので、既存のslimアプリとバッティングしない設定を試した時のメモです。

/var/www/slimにslimアプリがあるとします。slimアプリもsinatraアプリも

http://www.hoge.jp/slim/
http://www.hoge.jp/sinatra/

のようなパスでアプリを起動させるところを目指します。

環境:Linux debian 3.2.0-3-amd64 [debian wheezy]

apacheはすでにインストール済みとします。

sinatraアプリの開発環境のインストール

#apt-get install ruby rubygems
#gem install sinatra

sinatraapacheで動かすためにpassengerをインストールします。

#gem install passenger

passengerをインストールします。

#passenger-install-apache2-module

足りないものがあればコンソールに表示されるので、追加でインストールします。

apacheの設定

PassengerRootとPassengerRubyの箇所をmods-enabled/passenger.conに設定します。

<IfModule mod_passenger.c>
   RackBaseURI /sinatra
   PassengerRoot /var/lib/gems/1.9.1/gems/passenger-3.0.18
   PassengerRuby /usr/bin/ruby1.9.1
</IfModule>

sinatraアプリに必要なファイルを作成します。アプリのファイルはapp.rbにします。今回アプリのファイルは

#cd /var/www/sinatra
#mkdir public
#mkdir tmp
#touch app.rb

sinatraアプリはプロジェク直下のpublicディレクトリをDocumentRootに設定しないといけないのですが、その設定をしてしまうと他のアプリに影響がでることが多々あるかと思います。

sinatra
├── app.rb
├── config.ru
├── log.txt
├── public
└── tmp

そこでpublicディレクトリのシンボリックリンクを/var/wwwに以下に張って対応します。

#cd /var/www
#ln -s "リンク先" sinatra

これだけじゃ動かないのでRackBaseURLを設定します。私は上述のmods-enabled/passenger.confに設定しました。

さらにsinatraアプリの直下にconfig.ruを配置します。このファイルはpassengerが自動で読んでくれます。

require './app.rb'

run Sinatra::Application

以上でApacheを再起動すれば

http://www.hoge.jp/sinatra
sinatraアプリにアクセスできます。

ちなみに、リンクさえApacheが読めるところに貼ってやれば、実際のソースファイルの場所はどこでもOKです。