TitaniumMobile勉強記

Titanium Mobileもだけど、関連してJavaScript、サーバ側の開発(Ruby中心)、BDD的な話題とかについても振れていきたい本業は転職コンサルタントな非プログラマー

2012-05-09

TitaniumMobileでmustache.js活用できないか試してみた

フロントエンドでHTMLをレンダリングする技術全般を黙々とハックするclient-side templating勉強会というイベントに昨夜参加してきました。

完全に場違いであるのは事前にわかっていたけど、行ってみるとすごい人ばかり参加してたのと、知らないことが多く勉強不足な点を思い知った一日でした

mustache.jsって?

さて、ここから本題のmustache.jsの話。GtiHubのREADMEによると

mustache.js - Logic-less {{mustache}} templates with JavaScript

ということで、具体的には

var view = {
  title: "Joe",
  calc: function () {
    return 2 + 4;
  }
};

var output = Mustache.render("{{title}} spends {{calc}}", view);

みたいなことが出来るテンプレートエンジンなのですが、CommonJSのモジュールとしても使えるので

var Mustache = require("mustache");
var info = {
  name:'John'
};
var tmpl = "My name is {{ name }}"; // 結果はMy name is Johnとなる

みたいなことが出来ます。

CommonJSモジュールとして利用できるならTitanium Mobileからも利用できるよね?

client-side templating勉強会に申し込む時に、CommonJSなmustache.jsをTitanium Mobileで使えないか勉強してみたいと思いますと書いていたので、それをひたすらモクモクとやってました。

使おうと思った背景をちょっとだけ書いておくと、今作ってるアプリで

var exports = {
  createComposeRow :function(){
    var composeRow = Ti.UI.createTableViewRow($$.composeRow);
    composeRow.showd = true;
    var mail = Ti.UI.createImageView($$.lightMailBtn);
    composeRow.add(mail);

    var readLater = Ti.UI.createImageView($$.lightTextBtn);
    composeRow.add(readLater);

    var star = Ti.UI.createImageView($$.lightStarBtn);
    composeRow.add(star);

    var tweet = Ti.UI.createImageView($$.lightCommentBtn);
    composeRow.add(tweet);

    return composeRow;
};

みたいな処理がちょいちょい出てきます。ひたすらTi.UI.createImageViewする処理が多いのでこの部分をもうちょっとスマートに書けないかなと思ってmustache.jsでうまいこと出来ないかと思ってモクモクしようと思ってました

結果的には、上記問題解決まではいたらず、とりあえずのサンプル程度しか出来ませんでしたがひとまずコードを晒しておきます


buildするとこうなります
f:id:h5y1m141:20120509090037p:plain

2012-05-05

Titaniumから少し浮気してRubyMotionいじってみた感想など

タイトルそのままですが、キャンペーン価格で1万円ちょっとで、自己投資と考えてポチって少しだけいじった状態の感想を

自分のスキル

  • Objective-Cでの開発経験無し
  • Titanium Mobileで1年半ほどいじってる。あすなろBLOGをオフラインでも閲覧できるようなアプリを作っててもうすぐリリースできそうな所まで来た
  • Rubyは初めてのRubyを読んで、次にどの本を読むべきか悩んでるレベル

初めてのRuby
初めてのRuby
posted with amazlet at 12.05.05
Yugui
オライリージャパン
売り上げランキング: 19735

いじる前に期待していたこと

Titanium Mobileみたいな感じで、iOS SDKAPIをラップしたようなものが提供されてて、それがRubyMotionから手軽に利用できるものを何となく期待していました。

自分の環境

実際やったこと

アプリケーションのインストールは特に問題もなく、RubyMotionの公式ドキュメント 見ながらHelloWorld的なアプリも一応動きました。

その後GitHubのRubyMotionサンプルのTwitterクライアント的なやつを単に写経してbuildしてこんな感じで動きました
f:id:h5y1m141:20120505162307p:plain

使った感想や気づきなど

Titanium Mobileの感覚で開発出来ることを期待してた自分にとってはちょっと敷居高く感じました。

そう感じた一番の要因はiOS SDKAPI Reference見たことが無いからで、ひげろぐさんが書かれているエントリで以下のような一文があったのでちょっと引用します。

RubyMotionは基本的にiOS SDKAPIを忠実になぞった出来になっていて、iOS SDKのクラス名やメソッド名がRubyのコードの中でそのまま使える。そのためiOS SDKの持つすべての機能を使うことができ、Objective-Cでの開発経験がある開発者なら今までの知識を生かしつつ、Objective-Cの煩わしさを捨ててRubyでサクサク書いていくことができる。

こういう経験ある人だと、きっとサクサク開発進むんでしょうね。

以前こんなことを書いたことがありますがTitanium Mobileの内部構造をもう少し理解したいという漠然とした考えを持っていました。

今回RubyMotion使って気づいたのは、iOS SDKのクラス群がどんなものがあって、TitaniumMobileからそれらがどう使われてるかを理解したいっていうことだったのかもしれません。

基本的にはTitanium Mobileでアプリ開発をしつつ、もう少しiOS SDKついて理解を深める手段として RubyMotionを使うというのが今のところ自分にとっての活用方法して最適なのかなと感じたので、ちょっとだけ浮気したけどTitanium Mobileメインにいじっていこうと思います

2012-05-03

環境構築で最近はまった事のまとめ

サーバ側の環境構築してる時に最近はまっていたことをいくつか

Apacheのプロセスがどのユーザで起動されているか確認する

Linux/BSDな環境のサーバ管理している人には常識なんだろうけど、実はこれがすぐわからなかった。こちらのページ見たらpsコマンドの一覧まとめていて「u」オプション付けることでプロセスの実行ユーザ確認できるとのこと。なので

ps aux | grep apa

みたいな感じにすればOK

Sinatraのクエリーパラメータ取得した後に、数値→時刻への変換処理でErrorになる

実はこれに随分悩まされた。やりたかったことは

http://h5y1m141.info/entry/oyamada/1330000000

みたいなURLにアクセスした時に、

  • ブロガー名:oyamada
  • ブログの投稿日:Unix time(=1330000000)を変換して得られる結果。具体的には2012-02-23 21:26:40 +0900 以降。

というクエリーを発行して該当するエントリをDBから得ることをやろうと思ってました。

require 'sinatra'
get '/entry/:blogger/:num' do
  t = Time.at(params[:num])

みたいな感じでクエリーパラメータ取得して変換すればOKかと思っていたのですが「TypeError - can't convert String into an exact number:」と怒られてしまいました。params[:num]を数値変換すれば良かったみたいで、具体的には

require 'sinatra'
get '/entry/:blogger/:num' do
  t = Time.at(params[:num].to_i)

で解決しました。

ただ、http://h5y1m141.info/entry/oyamada/1330000000 みたいなURLにアクセスしてデータ取得するよりも、DBのlimit句などを駆使してpagination実装したほうがシンプルなことについさっき気づいたので、このURLにアクセスするのはやめることにしようと思います

MongoDBのpagination

どうやるんだろうとググっていたら、Stack Overflowにそのものズバリなことがあったけど1年ほど前にすでにはてブしてたのを忘れていた・・

MongoDBインストールしたLinux上でmongoシェル起動して

db.entries.find({"blogger":"oyamada"},{"title":1})
{ "_id" : ObjectId("4f860b45b8dac929af000001"), "title" : "まずは、自己紹介" }
{ "_id" : ObjectId("4f86114db8dac92c71000001"), "title" : "まずは、自己紹介" }
{ "_id" : ObjectId("4f86114fb8dac92c71000003"), "title" : "新しい職場で働く時" }
{ "_id" : ObjectId("4f861152b8dac92c71000005"), "title" : "Relax" }
{ "_id" : ObjectId("4f861154b8dac92c71000007"), "title" : "イチローにみるリーダーシップ" }
{ "_id" : ObjectId("4f861156b8dac92c71000009"), "title" : "Sun Grid から見るネットワークインフラ展望" }
{ "_id" : ObjectId("4f861158b8dac92c7100000b"), "title" : "何を勉強するか考える前に" }
{ "_id" : ObjectId("4f86115bb8dac92c7100000d"), "title" : "MicrosoftのOfficeの牙城を切り崩そうとするサービスの登場" }
{ "_id" : ObjectId("4f86115db8dac92c7100000f"), "title" : "メールとの付き合い方" }
{ "_id" : ObjectId("4f86115fb8dac92c71000011"), "title" : "お金のことについて" }
{ "_id" : ObjectId("4f861161b8dac92c71000013"), "title" : "セミナーやイベント参加時のちょっとした準備" }
{ "_id" : ObjectId("4f861163b8dac92c71000015"), "title" : "オープンソースといえば?" }
{ "_id" : ObjectId("4f861166b8dac92c71000017"), "title" : "お客さんの期待感" }
{ "_id" : ObjectId("4f861168b8dac92c71000019"), "title" : "身になる読書のための5つのTips" }
{ "_id" : ObjectId("4f86116ab8dac92c7100001b"), "title" : "パッケージングの妙" }
{ "_id" : ObjectId("4f86116cb8dac92c7100001d"), "title" : "実務経験xx年以上という”モノサシ”" }
{ "_id" : ObjectId("4f86116fb8dac92c7100001f"), "title" : "Winny対策PC導入。でもその前に・・" }
{ "_id" : ObjectId("4f861171b8dac92c71000021"), "title" : "時間の流れの見方を変えてみませんか?" }
{ "_id" : ObjectId("4f861173b8dac92c71000023"), "title" : "情報漏えい対策セミナー" }
{ "_id" : ObjectId("4f861175b8dac92c71000025"), "title" : "情報漏えい対策セミナーまとめ" }

となっている状況で先頭の5件取得するには

db.entries.find({"blogger":"oyamada"},{"title":1}).skip(0).limit(5)
{ "_id" : ObjectId("4f860b45b8dac929af000001"), "title" : "まずは、自己紹介" }
{ "_id" : ObjectId("4f86114db8dac92c71000001"), "title" : "まずは、自己紹介" }
{ "_id" : ObjectId("4f86114fb8dac92c71000003"), "title" : "新しい職場で働く時" }
{ "_id" : ObjectId("4f861152b8dac92c71000005"), "title" : "Relax" }
{ "_id" : ObjectId("4f861154b8dac92c71000007"), "title" : "イチローにみるリーダーシップ" }

でOK。6件目から10件目を取得したい場合には

db.entries.find({"blogger":"oyamada"},{"title":1}).skip(5).limit(5)
{ "_id" : ObjectId("4f861156b8dac92c71000009"), "title" : "Sun Grid から見るネットワークインフラ展望" }
{ "_id" : ObjectId("4f861158b8dac92c7100000b"), "title" : "何を勉強するか考える前に" }
{ "_id" : ObjectId("4f86115bb8dac92c7100000d"), "title" : "MicrosoftのOfficeの牙城を切り崩そうとするサービスの登場" }
{ "_id" : ObjectId("4f86115db8dac92c7100000f"), "title" : "メールとの付き合い方" }
{ "_id" : ObjectId("4f86115fb8dac92c71000011"), "title" : "お金のことについて" }

でOK。

2012-04-25

WebViewの表示をする際にResources以下のCSSを読み込むようにする方法

UIの配色を試行錯誤してましたが、基本的にはこんな感じでいこうかと思ってます。
f:id:h5y1m141:20120425092721p:plain
遷移後の画面はこんな感じ
f:id:h5y1m141:20120425092706p:plain

一覧から任意のエントリをタッチした時に画面遷移し、エントリ詳細情報をWebView利用して表示してますが

「Titanium StudioのプロジェクトのResources以下に配置したcssをWebViewに適用する方法ってどうやるんだろう?」

というのが気になってちょっと調べてみました。

Local CSS not used in Local HTML Webviewを読んだらどうやるかおおよそイメージついたのでこんな感じで実装すれば大丈夫でした。

cssファイルの場所はResources/ui/html/main.cssとしてます
f:id:h5y1m141:20120425093426p:plain

// prepare css for iphone
var file = Titanium.Filesystem.getFile(Titanium.Filesystem.resourcesDirectory, '/ui/html/main.css');
var css = file.read();
var webView = Ti.UI.createWebView($$.webView);
var htmlHeaderElement = '<html><head><meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"><style type="text/css">'+ css + '</style></head>';
var html_body = '以下省略・・'
webView.html = htmlHeaderElement
        +'<body>'
        + html_body
        + '</body></html>';
webView.show();

CSSはこんな感じ

body {
    margin:10;
    padding:10;
    line-height:1.6;
    font-size:1.1em;
    background-color:#ededed;
    color:#333;
    width:90%;
}
2012-04-20

FacebookのiPhoneアプリ風のメニューをパクってみた



ということをつぶやていましたが、具体的にはこんな感じのUIを作ってみました。基本的にはastronaughtsさんが以前書いたエントリをそのまま流用しただけなんですけどねw

起動時の状態

普通にエントリー一覧が表示されています。
f:id:h5y1m141:20120420095424p:plain
左上のボタンをタッチすることで、エントリー一覧のTableViewが右にスライドして、あらかじめ下側にレイアウトしておいたブロガー一覧のTableViewが表示される
f:id:h5y1m141:20120420095502p:plain

ソースコード

GitHubにあるのですが、ちょっとやっつけ仕事な所が残ってるのと、UIの配色をこちらを見ながら試行錯誤してる段階なので、ちょっとづつ修正する予定です。

2012-04-17

TableViewのあるウィンドウからWebViewのあるウィンドウへの画面遷移のもたつき解消について

先月位に


ということをつぶやいていた件について、状況まとめ&回避方法についてまとめます

状況

TableViewを利用してこのような一覧表示をするウィンドウを最初に生成してます。
f:id:h5y1m141:20120416084729p:plain

任意の箇所をタッチすることで、以下のような詳細画面に遷移するといったよくあるViewerアプリの実装をしてます
f:id:h5y1m141:20120416084743p:plain

画面遷移はもちろん機能するのですが、起動して、最初の1回目のタッチの時だけ、表示がすぐに行われず、モッサリした感じがあります。

ただ最初の1回目だけその症状になり、2回目以降は一覧表示画面→詳細画面→一覧表示画面・・というの画面遷移のくり返しをしても、スムーズに行われる所を考えると、WebViewを生成するタイミングを考えてあげれば回避できそうに思いました。

取り組んだ事

アプリ起動時に、WebViewを生成して非常時にしておく。必要になったらHTMLコンテンツを挿入して表示をするというアプローチにして解消しました。

実装にあたって、CommonJSスタイルを貫く

CommonJSスタイルでの自分なりの書き方がなんとなく見えつつあります。

MVCのModelとなるentry.jsでブログエントリを管理。ui.jsにてUI全体の管理をする方針で考えていたけど、特にTableViewの各種処理を書いてくとちょっと見通し悪くなりそうなので、WebViewの生成などを担うwebView.jsとTableViewの生成&各種イベントリスナーの設定をするtableView.jsとをひとまず別管理というやり方に現状してます。

ただui関係の処理というか名前空間がしっくりこなかったり、ローカル or リモートのJSONDBから条件にマッチするエントリを読み込んだりする処理のハンドリングをするためにController的なものが必要な気もしてきたのでこの辺りは作りながらもう少し試行錯誤しようかと思ってますがいづれにしても、今後CommonJSスタイル推奨されているのでこれは貫いていこうと思ってます

ソースコード一部

// app.js
var myApps = {};
myApps.ui = require('ui/ui');
myApps.webView = require('ui/webView');
myApps.tableView = require('ui/tableView');
myApps.entries = require('model/entries');
(function(){
  var entries;
  var blogger= 'xxxxxx'
  myApps.entries.load(blogger,function(entries){
    var rows = [];
    for(var i=0;i<entries.length;i++){
      var entry = entries[i];
      var row = myApps.ui.createEntryRow(entry);
      rows.push(row);
      myApps.tableView.setTableData(rows);
    }
  });

  myApps.ui.addElement(myApps.tableView.init());
  myApps.ui.addElement(myApps.webView.init());
  myApps.ui.createMainWindow();

}).call(this);
// ui/webView.js
var $$ = require('ui/styles').prop;
var webView = Ti.UI.createWebView($$.webView);
var htmlHeaderElement = '<html><head><meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1"></head>';
var exports = {
  init:function(){
    webView.html = htmlHeaderElement
      +'<body>'
      + '</body></html>';
    webView.hide();
    return webView;
  },
  insertContents:function(body){
    webView.html = htmlHeaderElement
        +'<body>'
        + body
        + '</body></html>';
    webView.show();
    return webView;
  }
};


2012-04-09

さくらVPSのDebianにApache+Passengerな環境作った



ということをつぶやいた通りなんだけど、理想的なやり方(*1)を追求しすぎてしまって、色々二の足を踏んでいたのでまずは動作する環境をつくることを優先にしました。

色々間違ってしまってなかなか動作しなかったのですが、Passengerを使ってRuby on RailsとApacheを連携させてみるという記事がとても参考になったので基本的にはこれに従って作業すれば出来ると思います。

ただ上記ブログに辿りつくまでに色々はまった所を簡単にまとめておこうかと思います

rvm使ってRuby1.9.2を導入

元々入ってるRubyのバージョンが1.8.xだったので、1.9系を使おうと思って、rvm使ってインストールしました。rvm使ってRuby1.9.2インストールは問題なかったのですが、gemsetが最初よく理解できませんでしたがこれ等を参考にして少し理解が進んだきがします

gemsetというのを使うと、例えばRailsの2.xのgemやRails3.xのgemという感じでいくつかのセットを作ることが出来るようなのでこれをうまく使えば、環境依存しそうな機能の検証とかうまく出来そうな気がしました

※ついでに、手元のMacにもrvm導入して、1.9系使えるようにした

Apacheの設定でちょっとはまる

凄い昔にApacheは勉強したことあるので、/etc/apache2以下のhttpd.confに色々書くのかと思ったら、そこには何も書かれてない・・

この時点で一瞬戸惑ったけど、同ディレクトリにapache2.confというそれっぽいファイルが見つかったのできっとここに見慣れた記述があるのかと思ったら案の定そのとおりでした。

その他、いくつかディレクトリが存在してて、ディレクトリ名&ちょっと調べた感じで以下のように理解しました。

conf.d

設定ファイル群をこのディレクトリ配下に置く。configuration directoryっぽい。

mods-availableとmods-enabled

modsの接頭辞から推測されるようにmoduleの読み込み&設定などをここで行なっているっぽい。最初これが理解できておらず、

passenger-install-apache2-module 

したあとに

The Apache 2 module was successfully installed.
Please edit your Apache configuration file, and add these lines:

LoadModule passenger_module /xxxxx/passenger-3.0.11/ext/apache2/mod_passenger.so
PassengerRoot /xxxxx/passenger-3.0.11
PassengerRuby /xxxxx/ruby

みたいな画面出て、上記3行をどこに書くのかかなりはまりました。

sites-availableとsites-enabled

どのディレクトリをWeb上に公開するのかといった設定ファイルをsites-availableに置く。設定ファイルをおいただけでは実際に公開されずsites-enabled以下に任意の名前でシンボリックリンクを貼ることで公開されるっぽい。

Apache+Passengerは動作してるっぽい

Railsなプロジェクトをひとまず作って、おなじみの画面は出た。
f:id:h5y1m141:20120409091652p:plain

でも、Sinatraベースのアプリを公開しようと思ったら、こんな感じでエラーになったのでこのあたりもう少し勉強する必要有りそう
f:id:h5y1m141:20120409091743p:plain

(*1)手元のMac上のVirtualBox上のDebianをテスト環境 で、さくらVPSを本番環境という位置づけで考えていたから、極力サーバに直接sshせず、Capistrano使ってdeployしたり、Chef使ってOSの設定(iptablesとか)や各種ミドルウェアの設定をスマートにやることを目指してました。たかだか2台のサーバ環境とはいえ、しっかりと運用しようと思ったらこうやるのが本来の姿なのかなと思っていたので。