AlloyでのiPhoneアプリ開発にCoffeeScriptを使う

犬が早朝から吠えたので4時に叩き起こされたが、タイムラインを追いかけていたらAlloy with CoffeeScript のお誘いというこれは!というエントリがあがっていたので、早速誘われてみた。尚、jsとcoffeeは一緒のディレクトリに置いておくのが好みなので@k0sukeyのオリジナルなjmkに変更をくわえてあります。

ストップウォッチをつくる

Titanium Mobile iPhone/Androidアプリ開発入門のストップウォッチを参考にしました。

ProductName Titanium Mobile iPhone/Androidアプリ開発入門―JavaScriptだけで作る
小澤 栄一
秀和システム / 2520円 ( 2012-02 )


本書ではui.jsという1つのファイルで完結していますがAlloyのようなMVCフレームワークを使ったほうがコードの見通しは良くなると思います。

雛形をつくる

$ titanium create stopwatch # 対話でターゲットデバイスやapp-id設定
$ cd stopwatch
$ alloy new # alloyのひな形作成
$ alloy generate jmk

coffeeがコンパイルされるようにする

stopwatch/app/alloy.jmkを以下のように書きなおす。これでapp/controller のcoffeeファイルはalloy compileでコンパイルされるようになります。

task("pre:compile", function(event,logger) {
    var wrench = require("wrench"),
    fs = require("fs"),
    path = require("path"),
    controller_root = event.dir.controllers,
    coffee = require("coffee-script");

    wrench.readdirSyncRecursive(controller_root).forEach(function(controller){
    if (controller.match("coffee$")) {
        fs.writeFileSync(
        path.join(controller_root,controller.replace("coffee", "js")),
        coffee.compile(
            fs.readFileSync(path.join(controller_root, controller)).toString(),
            { bare: true }));
    }
    });
});

task("post:compile",function(event,logger){
});

alloyの開発

あとは普通に。

app/views/index.xml

<Alloy>
    <TabGroup>
        <Tab id="tab1" icon="dark_clock.png" title="ストップウォッチ">
            <Window id="window1" title="ストップウォッチ">
                <Label id="label">
          00:00:00.000
                </Label>
        <Button id="button">
          Start
        </Button>
            </Window>
        </Tab>
        <Tab id="tab2" icon="dark_info.png" title="使い方">
            <Window id="window2" title="使い方">
          <WebView url="howto.html">
          </WebView>
            </Window>
        </Tab>
    </TabGroup>
</Alloy>

/app/styles/index.tss

{"Window": {
  "barColor": "#000",
  "backgroundColor": "#000"
},
"Tab": {
  "backgroundColor": "#000"
},
"#label": {
  "color": "#fff",
  "width": "auto",
  "height": "auto",
  "font": {
    "fontSize": 40,
    "fontWeight": "bold"
  }
},
"#button": {
  "color": "#000",
  "width": 150,
  "height": 40,
  "bottom": 30
}}

webview用のhtmlは /app/assets/iphone/howto.htmlに

<html>
    <head>
        <title>使い方</title>
    </head>
<body>
    <h1>使い方</h1>このサンプル
は<a href="http://www.amazon.co.jp/dp/479803231X">Titanium Mobile
iPhone/Androidアプリ開発入門</a>のストップウォッチのサンプルをAlloyで
書きなおしてみたものです。
<img src="http://ec2.images-amazon.com/images/I/41jDZ5IY01L._BO2,204,203,200_PIsitb-sticker-arrow-click,TopRight,35,-76_AA300_SH20_OU09_.jpg"/>
</body>

興味を持ったら購入して読んでみるとよいと思いますよ。

</html>

最後にcoffeeをapp/controllers/index.coffeeに

started =false
intervalID = null

startStopwatch = ->
  $.label.text = "00:00:00.000"
  startTime = new Date()

  _updateTimer = ->
    u_sec = 1000
    u_min = 60 * u_sec
    u_hour = 60 * u_min
    now = new Date()
    diff = now - startTime

    hour = Math.floor(diff/u_hour)
    min = Math.floor((diff - hour*u_hour)/u_min)
    sec = Math.floor((diff - hour*u_hour - min*u_min)/u_sec)
    msec = diff / u_sec
    $.label.text = ("0" + hour).slice(-2) + ":" +
                   ("0" + min).slice(-2) + ":" +
                   ("0" + sec).slice(-2) + "." +
                   ("00" + msec).slice(-3)

  intervalID = setInterval(_updateTimer, 3)
  return

$.button.addEventListener 'click', -> 
  if started
    clearInterval(intervalID)
    $.button.title = "Start"
    started = false
  else
    startStopwatch()
    $.button.title = "Stop"
    started = true
  return

$.index.open()

実行

$ alloy run

alloy_stopwatch

Let's Enjoy!

レスポンシブデザインのサイトにGoogleAdsense放り込むとめんどくさいよね

今、728x90の横長のやつを張り付けているんだけど、スマホで表示させると邪魔だ。動的に大きさを変更するのは規約違反らしいので、横長よくないじゃんと。まぁサーバー側でチェックしてサイズ変えてもいいんだけどいちいち面倒くさい(というかbootstrap使う必要ないじゃん的な)。

2カラムにしてサイドバーの方に320x320くらいのレクタングルを貼り付けておくのが良いかなと思ったのでTwitter Bootstrap: Build A Stunning Two Column Blogを見てた。

というわけで2カラムのデザインでヨサゲなやつを参考にしてなんか作ろうかなと思ってwordpressのテーマを漁っている。

bootstrap関係

flymakeを使ってAlloyのTSSをリアルタイム文法チェック

Alloyのtssの文法ってよく分からんですね。css-modeあててもjs2-modeをあててもエラーばっか吐くし、快適にコーディングできん。

ということでちょっと調べてみた。

Alloyのソースコード探していたらgrammerのとこにtss.pegjsってのがあって、ヘッダを読んでみるとTSS parser based on JSON parserって書いてあったので基本的にはJSONらしい。

それにしては{で始まらないよなぁとソースコードを追いかけていくとcompilerUtils

// Add enclosing curly braces, if necessary
contents = /^\s*\{[\s\S]+\}\s*$/gi.test(contents) ? contents : '{' + contents + '}';

ってあって、要するに{で始まってない場合ファイルのコンテントを{}で囲むことになっているらしい。それからtssはキー値を""で囲まなくてもいいんだけど、囲んでも別に問題なかったりする。

結論としてはJSONで書いておけば、即時構文チェックとかシンタックスハイライトとか効かせられていいんじゃないかと。

つまり

"Label": {
  font: {fontSize: 20, fontFamily: "Helvetica Neue"},
  color: "#999"
}

って書いてるところを、最初から

{"Label": {
  "font": {"fontSize": 20, "fontFamily": "Helvetica Neue"},
  "color": "#999"
}}

って書くようにすれば、事前に構文のエラーを潰せて気持ちがいい。

Emacsの設定

json-modeを使ってみますが、package.elでは入れられないのでgit cloneしてrequireします。javascript-modeを継承しているのでTabの設定なんかはそっちの設定に従う。

次にFlymakeの設定をする。構文チェックにjslintを使うのでインストールしてない場合には

npm install -g jslint

で入れます。

Emacsのほうの設定はこんな感じ。tabはスペース2個分(個人的な好み)にした。それからelectric-pair-modeも入れてるので{},""をよろしくやってくれる。

;; JSON
(require 'json-mode)
(add-to-list 'auto-mode-alist '("\\.tss$" . json-mode))
(add-hook 'json-mode-hook 'electric-pair-mode)
(add-hook 'json-mode-hook '(lambda ()
                 (setq js-indent-level 2)
                 ))

(when (load "flymake" t)
  (defun flymake-jslint-init ()
    (let* ((temp-file (flymake-init-create-temp-buffer-copy
               'flymake-create-temp-inplace))
           (local-file (file-relative-name
                        temp-file
                        (file-name-directory buffer-file-name))))
      (list "jslint" (list "--terse" local-file))))

  (setq flymake-err-line-patterns
    (cons '("^\\(.*\\)(\\([[:digit:]]+\\)):\\(.*\\)$"
        1 2 nil 3)
          flymake-err-line-patterns))

  (add-to-list 'flymake-allowed-file-name-masks
               '("\\.tss\\'" flymake-jslint-init)))

(load-library "flymake-cursor")

この設定で少し使ってみる。

Emacs+titanium cli+alloyでiPhoneアプリを開発する

静岡javaScript勉強会 #2に参加されるみなさんこんにちは、参加されないみなさんもこんにちは。僕は11月のすべての土曜日を留守にして家族の顰蹙を買うという重圧に耐え切れずに早々に離脱してしまいましたので朝からお家でjsってます。

ところで、@tomofの「Backbone.js入門」面白かったですか?よっしゃーいっちょおとうさんもbackbone.jsでウェブアプリケーションつくっちゃうぞーっていう気になりましたか?はいそうですか、そうですよね。ついでに、その勢いでBackbone.jsっぽい開発のノリでiPhoneアプリを開発できたら面白いと思いません?思いますよね。

やりましょう、Alloyで!(孫社長風味でよろしく)

というような内容のエントリです。

1351985625

Titanium CLIとAlloyのインストール

Titanium CLIはTitaniumで開発するためのコマンドラインのツールで、AlloyはTitaniumのためのMVCフレームワークですが、両方共Node.jsのv0.8以上がインストールされていればnpmでインストールできるはずです。

npm install -g titanium
npm install -g alloy

うまくいかない場合は下のサイトを参考にしてください。

エディタ

EmacsでもvimでもST2でも好きなモノを使えば良いとおもいますが、僕はEmacs(24)をつかっています。でnxmlモードにzencoding-modeをあててあります。入ってない場合はM-x list-packaegesでzencoding-modeを選んでインストールしたらinit.elに下の内容を追記しておけばいいです。

(add-hook 'sgml-mode-hook 'zencoding-mode) 
(add-hook 'nxml-mode-hook 'zencoding-mode)

他にはjavascriptの開発用にjs2-modeを入れているのとcoffeeモードにflymakeが効くようにしてありますが今回の内容では必要ありません。

追記121118: CoffeeScriptを使って開発する方法

開発の流れ

最初にtitaniumコマンドでプロジェクトの雛形をつくります

$ titanium create
Titanium Command-Line Interface, version 3.0.13
Copyright (c) 2012, Appcelerator, Inc.  All Rights Reserved.

Please report bugs to http://jira.appcelerator.org/

Target platforms: (android,ios,ipad,iphone,mobileweb) iphone
App ID: com.example.tabsample
Project name: tabsample

[INFO] Creating Titanium Mobile application project
[INFO] Project 'tabsample' created successfully in 276ms

プロジェクトのディレクトリに移動してalloyの雛形をつくります。

$ cd tabsample/
$ alloy new
       .__  .__                
_____  |  | |  |   ____ ___.__.
\__  \ |  | |  |  /  _ <   |  |
 / __ \|  |_|  |_(  <_> )___  |
(____  /____/____/\____// ____|
     \/                 \/
Alloy by Appcelerator. The MVC app framework for Titanium.

[INFO] Installed "ti.physicalSizeCategory" module to tiapp.xml
[INFO] Installed "ti.alloy" plugin to tiapp.xml
[INFO] Deployed ti.alloy compiler plugin to plugins/ti.alloy/plugin.py
[INFO] Deployed ti.alloy hooks to plugins/ti.alloy/hooks
[INFO] Generated new project at: app

ここからEmacsを使っていきます。まずはapp/views/index.xmlを修正します。中身を全部消してzencodingでさくっと用意します。zencodingはC-returnで起動させて

Alloy>TabGroup>(Tab#tab icon title>Window#window title backgroundColor>Label#lable)*2

みたいにワンライナーで書けるので便利すぎです。

emacs-zen

zencodingで大まかにスキャフォールドを用意したら、アトリビュートなんかを設定します。ちなみに画像はここから取ってきてapp/assets/iphone/に起きました。

<Alloy>
    <TabGroup>
        <Tab id="tab1" icon="KS_nav_views.png" title="Tab 1">
            <Window id="window1" title="Tab 1" backgroundColor="#fff">
                <Label id="label1">
          I am Window1
                </Label>
            </Window>
        </Tab>
        <Tab id="tab2" icon="KS_nav_ui.png" title="Tab 2">
            <Window id="window2" title="Tab 2" backgroundColor="#fff">
                <Label id="label2">
          I am Window2
                </Label>
            </Window>
        </Tab>
    </TabGroup>
</Alloy>

app/styles/index.tssはLabelの設定だけ。

    "Label": {
    font: {fontSize:20, fontFamily:"Helvetica Neue"},
    color: "#999"
}

tssにはどのモードをアサインするのがいいのでしょうか?誰か知っていたら教えて下さい。(追記121117 json-modeあててみた)

iOSシミュレータを起動

プロジェクトのトップディレクトリで

$ alloy run

するとシミュレータが立ち上がります。素敵ですね。これで、雛形作ってシミュレータを動かすところまでできました。あとはapp以下を良い感じにいじってイケてるアプリを作るだけですね。

ti-alloy

シミュレータが立ち上がるまでに結構待たされるのでストレスを感じますが、それは僕のmacが遅いからですね。はやく買い換えたい。

おまけ(Alloyの開発にJadeを使う)

@k0sukeyAlloyJadeという素敵なものがあることを教えてもらいました。Jade派の僕としては非常に惹かれるので後で試そうかなと思っています。

大観を呑んでいる

風邪も治りかけてきたので、ふんわり柔らか美山錦の日本酒をぬる燗にして飲むぞーと澤の花でも買おうかなといわせさんに寄ってみたら、茨城産美山錦を醸したお酒が置いてあって、ラベルもなかなか素敵だったので買ってしまった。

1353059528

ふんわり系で美味い系だが、若干のエグ味というか苦味がある。これは嫌な感じではなくて個性でしょう。綿屋のほうがキレイ目だけど、燗つけて飲むならこっちかな。

この美味さで2.3Kという値段はコストパフォーマンス高いですね。

オススメ

The Haskell Platformを2012.4.0.0にあげた

あげたら

cabal install virthualenv
cabal install hlint ghc-mod

と打って開発環境を整える。

三島Haskell無名関数の会 第一回目

11/24に三島Haskell無名関数の会 第一回目をやります(@富士)。

僕は「静岡のHaskellerはEmacsを使う」というタイトルで、初学者向けのHaskell開発環境の話をするつもりなので、Haskellに興味があるけど一歩の踏み出し方の分からないヒトは参加すると幸せになれるかもしれません(Gentooの布教活動も噂されていますが)。

それから、状態計算から理解するモナドという話をしようと思っています。

他にはYesodの話が出るそうです。

なんか話したいネタがあればコメント欄に書いておけば@ando_ando_andoが調整してくれるはず。

local-set-keyに出てくる(interactive)

よく分からないでコピペで増やしたlocal-set-keyだが

(local-set-key "\C-j" (lambda () (interactive) (insert " -> ")))

と、ラムダ式に(interactive)が含まれている。試しに(interactive)を排除して

(local-set-key "\C-j" (insert " -> ")))

を評価(C-x C-e)してC-jを押してみると

Wrong type argument: commandp, (lambda nil (insert " -> "))

とミニバッファにエラーが出力される。commandpとは一体なんだろうか?と f1 f commandp

(commandp FUNCTION &optional FOR-CALL-INTERACTIVELY)

commandかどうかを判定する関数らしいが、よく分からんのでf1 f local-set-keyで見てみると

(local-set-key KEY COMMAND)

関数ではなくてCOMMANDである必要があるらしい。

コマンドとは、定義の最初にinteractiveスペシャルフォームが置かれた関数ですEmacsLISPテクニックバイブル p.79

EmacsLISPテクニックバイブルにちゃんと書いてあった。

ProductName Emacs Lispテクニックバイブル
るびきち
技術評論社 / 3129円 ( 2011-11-26 )


最近elispをよく読むのでかなりお世話になっている本だ。

macbookの要らないファイルを消した(.MobileBackups.trashとか.Spotlight-V100)

次に買うmacbookで悩んでいるわけだが、ファイルサイズが128Gに収まれば選択の幅が広がるかも!なんてことを思いついたので、Traktorで使わないmp3をNASに移して、写真をDVDに焼きつつNASに移したのにまだ200G近くある。

/でdu -m -d 1してみたら、.MobileBackups.trashが50Gと.Spotlight-V100が4Gあったので、

sudo rm -rf /.MobileBackups.trash
sudo tmutil disablelocal #ローカルにバックアップしない
sudo mdutil -i off /
sudo rm -rf /.Spotlight-V100/
sudo mdutil -E -i on /

とやって、140Gぐらいまで減らした。テクノ系のmp3もNASに移せば120Gくらいまで減らせそうな感じはするんだが、そうすると何のためにmacbook買い換えるのか分からなくなってしまうしなぁ。

素直に256GのSSDを選択しようっと。

CoffeeScriptでFlymake

Emacs24にしたので再設定

coffeelintのインストール

npm install -g coffeelint

coffee-modeはM-x list-packagesからiで選んでxで実行

Tabの設定が変だったので、init.elにちょっと追加。ついでにアローとかファットアローを挿入するキーバインドも設定。

(add-hook 'coffee-mode-hook '(lambda ()
  (make-local-variable 'tab-width)
  (set 'tab-width 2)
  (local-set-key "\C-j" (lambda () (interactive)(insert " -> ")))
  (local-set-key "\M-j" (lambda ()(interactive)(insert " => ")))))

flymakeはこの通りに設定しておけばでオッケー

ApressのEbooksDailyDealをGmailに送る

Nexus7を購入してから次の2つの電子書籍のDailyDealにはよく目を通すようになった。

ApressのほうはGoogle Readerでうまく更新されなくてちょっと困ったので、毎朝Gmailに送るようにしてみた。

import requests
import feedparser
import smtplib
from email.MIMEText import MIMEText
from email.Header import Header
from email.Utils import formatdate

EMAIL = 'your_account@gmail.com'
PASSWORD = 'your_password'

def sendmail(subject, body):
    msg = MIMEText(body, 'plain', 'utf-8')
    msg['Subject'] = Header(subject, 'utf-8')
    msg['From'] = EMAIL
    msg['To'] = EMAIL
    msg['Date'] = formatdate()

    s = smtplib.SMTP('smtp.gmail.com', 587)
    s.ehlo()
    s.starttls()
    s.ehlo()
    s.login(EMAIL, PASSWORD)
    s.sendmail(EMAIL, EMAIL, msg.as_string())
    s.close()

if __name__ == '__main__':

    r = requests.get("http://www.apress.com/index.php/dailydeals/index/rss")

    if r.status_code == 200:
        d = feedparser.parse(r.content)
        e = d.entries[0]
        sendmail(
            u"[APRESS DailyDeal] {}".format(e.title),
            u"{}\n\n{}".format(e.summary, e.links[0].href)
            )

ちなみに本日のディールはProtect Your Wealth from the Ravages of Inflationという本で僕は想定読者ではなかった。

people who have reached a level of financial stability and whose income is greater than monthly expenses.