2014年2月14日金曜日

SmartyのデリミタをHTMLコメントにした時にタグ中の属性制御で文法エラーにしない方法

SmartyのデリミタをHTMLコメント(で囲む)形式にすると、テンプレートファイルをHTMLとしてエディタで扱うことができ、シンタックスのハイライトを効かせて色分けできるメリットがある。さらにはエディタのHTML文法チェック機能も使えるから、記述効率もアップしてバグも防げる。

だからデリミタを例えば <!--{ と }--> に設定しているプロジェクトも結構あると思う。これでエディタでシンタックスハイライトと文法チェックもできてパッピー!・・・と思いきや、ちょっと問題があるんだよな。

それは、タグ中の属性を制御したい場合。例えば、ラジオボタンやチェックボックスをチェックしているかどうかは、タグ中にchecked属性が存在しているかどうかで決まるから、条件によってチェックを入れたり外したりするには、HTMLタグの中にSmartyのデリミタを書くことになる。これが文法エラーになるわけ。checked属性だけでなくdisabled属性も同じだよね。具体的には、例えばこんな感じ。
<input type="checkbox" name="chk1" value="val1"
  <!--{if $cond1}--> checked="checked"<!--{/if}-->
  <!--{if $cond2}--> disabled="disabled"<!--{/if}-->
/>

上の例は、チェックボックスで$cond1がtrueの時にチェックを入れ、$cond2がtrueの時は操作できなくするもの。だがこれは当然HTMLの文法違反になる。まあ、Smarty通すから文法エラーなんか無視すれば問題ないと言えばそうなんだけど、だったらなんでわざわざデリミタをHTMLコメントで囲う形式にしたのかって話になってしまう。

そこで、少々トリッキーだが、次のようにすることで文法エラーを回避できる。
<input type="checkbox" name="chk1" value="val1"
  data-dummy1="<!--{if $cond1}-->" checked="checked<!--{/if}-->"
  data-dummy2="<!--{if $cond2}-->" disabled="disabled<!--{/if}-->"
/>

ダミーの属性を追加して左右のデリミタを共にダブルクオーテーションの内側に収めたのがポイントだ。これで例えば$cond1と$cond2がtrueの時は、
<input type="checkbox" name="chk1" value="val1"
  data-dummy1="" checked="checked"
  data-dummy2="" disabled="disabled"
/>

$cond1と$cond2がfalseの時は、
<input type="checkbox" name="chk1" value="val1"
  data-dummy1=""
  data-dummy2=""
/>

となるわけだ。data-*という属性はご存知HTML5の独自定義属性の書き方に従っている。ダミーだと分かりやすいように名前をつけるといいと思う。

美しいやり方ではないけれど、エディタや統合開発環境の文法チェックを活かせるという意味で実用的なメリットは有ると思う。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

2014年2月13日木曜日

PHPなど動的型付け言語では変数名や関数名は長くていい

PHPなどの動的型付け言語って、Javaなどの型をはっきりさせる言語と違ってEcliseなどの統合開発環境による補完や定義の参照などが効きにくいところがある。

例えば、Javaの場合はインスタンスがコード中に登場したら、型が決まっているからメソッドの候補は自動的に決まり、例えばEclipseを使っていると候補がきっちり出てきてくれる。ところが、PHPの場合は、それがどのクラスのインスタンスなのか、いやそもそもインスタンスなのかどうかすら決まっていないから補完の候補が出せなかったりする。もちろん、EclipseのPDTなんかだと、ある程度文脈を読んでくれるから補完も出せるが、それだっていつも出せるわけじゃない。ということで、PHPを使う人間はしばしば、クラスの定義を自分で探して、どんなメソッドがあるか直接コードから探したりすることがある。

あるいは逆に、コードを読んでいて登場したメソッドがどんな働きをするのか、Javaであれば定義元にショートカット一発で飛んでいけるような場合も、PHPだとそれができないなんてこともある。そういうときも、メソッドを自分で探すことになる。

今述べたのはメソッドの例だけれども、これはインスタンスとか変数名とか関数でも同じようなことが言えて、要するに動的型付け言語では、候補探しや定義元の参照をしたいときには、既存のコードの中から探すという行為が発生しがちなのだ。

その際には既存のコードを一つ一つ探すのは大変だから、検索することになる。Eclipseの検索機能はPHPで開発してる時には上記の目的でよく使う。コードを横断的に検索し、結果を分かりやすく表示してくれるから、統合開発環境の検索機能はコマンドラインでgrepやfindを打つよりもずっと使いやすい。

その時に、スコープが分かれているからといって同じ変数名・関数名・メソッド名などをコードの色々な箇所で使っていると、目的のものを探し出しにくい。いや、もちろん言語仕様的には全く問題ないし、むしろ同じ目的の変数・関数・メソッドの名前を統一したほうがコード体系としては筋が通っているのかもしれない。だがしかし、開発をスムーズに進めていく上では、別の場所に登場した変数名・関数名・メソッド名は、たとえ同じ目的でも違う名前を付けていった方がいいと思う。

よって、区別のために色々と接頭辞や接尾辞をつけていくという意味で、変数名・関数名・メソッド名等は長くていいっていうことになるのだ。だから僕はPHPのコードを書く時にはわざと長めの関数名をつけたりするんだよね。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

2014年2月12日水曜日

ソースコードはチャートとして見えるようにするべき

他人の書いたプログラムのソースコードは読みにくいものだというのは、プログラミングに関わる人達の共通認識であるように思う。ソースコードはある意味自分の脳内思考回路の具現化とも言えるわけで、他人の思考回路と自分の思考回路が違う以上、読みにくいものであるというのは自然なことだろう。

とはいえ、読みやすいコードと読みにくいコードというものはある。もちろん、インデントをちゃんと揃えることや変数名・関数名・クラス名・メソッド名の命名規則など、基本的な部分を押さえていることは前提だ。そのうえで、コードを読んでいて何を意図してどういう動作をしているのか、分かりやすいコードとわかりにくいコードがある。

分かりやすいコードというものは一大テーマであって、それだけで本ができてしまうくらいのものであり、実際数えきれないほどの本が存在する。だけれど、僕が常に気をつけていることはたった一つだったりする。

それは、

「ソースコードはチャートとして見えるようにする」

ってことだ。

チャートというのはフローチャートでもいいし、PADでもNSチャートでもHCPチャートでもいい。とにかく、コードをパッと読んだ時に、そのコードが処理を表現する何らかのチャートとして見えるようにする、ってことだ。どういう条件で処理が分岐し、あるいは繰り返し、その中でどんな処理をするのか、といったことが「パッと見で」把握できることだ。

もし処理が複雑でチャートとして見えなくなりそうならば、関数やメソッドを作って処理をまとめる。たったそれだけの話だ。何だ当たり前の話だと思われるかもしれないが、それができていないソースコードなんて腐るほど見てきた。しかもプロの現場でだ。

冒頭に述べたように、他人のプログラムはそもそも読みにくいものだ。そして、あるコードを一人が延々とメンテし続けることは極めて稀で、一定期間経つと別の人へとコードのメンテは引き継がれる。その際、他人に少しでも分かりやすいように、「ソースコードはチャートとして見えるようにする」ようにしておけば、無駄な労力も時間も節約できるはずだ。

なんでこんなに熱くなってるかって?まあその辺は察してください(笑)

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

2014年2月11日火曜日

Eclipseのエラー抑制:DLTK問題・ビルドパス・バリデータ

EclipseでGitなどのソースコードリポジトリから新たにプロジェクトをチェックアウトした時など、ビルド時や検証時に出現するエラーの対応に苦労するのは僕だけではないはず。必要なところだけにエラーをだして欲しいのだが、外部のライブラリや自分の担当ではない部分のエラーまで出てしまったりする。

中にはエラーを放置していて、数百ものエラーをそのままにしている人も見たことがあるけれど、必要なエラーだけを出すようにすれば、バグを未然に防いだり、非推奨のコードをうっかり使うのを防止してくれたりと、Eclipseのメリットを享受できるのだ。

まあでも、この設定意外とわかりにくい。僕も全部は分かっていないが、とりあえず現時点で自分が分かっている範囲で概要をまとめてみたい。まず、Eclipseでの出るエラーは

  1. DLTK問題
  2. <言語名>問題

の2つに大きく分けられると思う。後者は例えばXML問題とかHTML問題とかPHP問題とかいうやつだ。

で、対処法としては、

  1. DLTK問題 → ビルドパスの設定
  2. <言語名>問題 → バリデータの範囲設定とレベル設定(可能なもの)

という感じ。

まず、「DLTK問題」の場合は、ビルドパスから検証不要なコードを除外すればいい。プロジェクトのプロパティから「ビルドパス」(or相当するもの)を選択する。なお下の画像はPDTを適用しているため「PHP ビルド・パス」となっているが、適当に読み替えて欲しい。


「除外:〜」をクリックして「編集」ボタンをクリックし、除外パターンを追加すればいい。


次に「<言語名>問題 」の場合には、バリデータの範囲設定で不要なソースを除外する。プロジェクトのプロパティから「検証」を選択する。


各バリデータの右側にある設定ボタンをクリックすると、バリデータの対象範囲を設定できる。まず、特定の範囲だけにバリデータを適用したい場合はまず内包グループを追加し、そのグループにさらにルールを追加して適用範囲を指定する。特定の範囲を除外したい場合は同様にまず除外グループを追加し、そのグループにさらにルールを追加して除外範囲を指定する。


ここまでの設定で不要なソースに対してのエラーは抑制できると思う。ただ、必要なソースに対しても、無視して欲しいエラーが出ているケースもあるだろう。例えばWeb系言語のフレームワークではHTMLタグを出力する命令があったりして、その場合はテンプレートをHTMLとしてバリデートすると開始タグがないというエラーが出てしまったりする。

こういう場合には、バリデータのレベル設定を変更してやればいい。例えば、HTMLバリデータの場合は、プロジェクトのプロパティから「検証>HTML構文」を選び、「プロジェクト固有の設定を可能にする」にチェックを入れれば色々いじることができる。


だいたいこんなところ。他にも言語やプラグインによって設定できる箇所は増えると思うけれど、だいたい上記の設定は共通して行うことになるんじゃないかな。

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

2014年2月10日月曜日

BloggerでSyntaxHighlighterをAutoloadしつつObjective-Cもハイライト

このブログでも使っているSyntaxHighlighterは、ブログなどでソースコードのハイライトを手軽に実現してくれる便利なスクリプトで、サーバへのスクリプト配置に制限がある環境でも公式サイトのホスティングを使えば比較的少ない手順でソースコードのハイライトを実現できる。

Brushと呼ばれる言語ごとのハイライトのルールを記述したスクリプトと対応CSSを読み込むことで複数言語に対応しているのだが、ブログの記事ごとにSyntaxHighlighterの設定コードを書き分けると面倒だし、かといって共通部分にSyntaxHighlighterの設定コードを埋め込むなら、ブログで使いそうな全ての言語についてBrushを読み込まなくてはならず、表示速度が遅くなってしまう。

そこでAutoloader機能を使えば、必要なBrushと対応CSSだけを自動的に読み込むことで表示速度を遅くせずに済む。これで記事ごとにSyntaxHighlighterの設定コードを書き分けなくても、共通部分に1つコードを埋め込んでおけばいいってことになる。

ただ、公式ページに掲載されているAutoloader対応スクリプトは、カスタムBrushに対応したコードではない。そこで今回公式にはBrushのないObjective-Cのコードを掲載するにあたり、設定コードを書いてみた。

下記のコードをHTML/Javascriptガジェットとしてタイトル無しでサイドバーの一番下に配置し読み込んで使っている。コード中のObjective-CのカスタムBrushはここからダウンロードして少しだけ色をいじったもの。

<!-- Common Scripts -->
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css" rel="stylesheet" type="text/css" />
<!--<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css" />-->
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeMidnight.css" rel="stylesheet" type="text/css" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js" type="text/javascript"></script>

<script type="text/javascript">
////////////////////////////////////
// Autoloader
////////////////////////////////////
function path()
{
  var args = arguments,
      result = []
      ;

  for(var i = 0; i < args.length; i++)
      result.push(args[i].replace('@', 'http://alexgorbatchev.com/pub/sh/current/scripts/'));

  return result
};

SyntaxHighlighter.autoloader.apply(null, path(
  'applescript            @shBrushAppleScript.js',
  'actionscript3 as3      @shBrushAS3.js',
  'bash shell             @shBrushBash.js',
  'coldfusion cf          @shBrushColdFusion.js',
  'cpp c                  @shBrushCpp.js',
  'c# c-sharp csharp      @shBrushCSharp.js',
  'css                    @shBrushCss.js',
  'delphi pascal          @shBrushDelphi.js',
  'diff patch pas         @shBrushDiff.js',
  'erl erlang             @shBrushErlang.js',
  'groovy                 @shBrushGroovy.js',
  'java                   @shBrushJava.js',
  'jfx javafx             @shBrushJavaFX.js',
  'js jscript javascript  @shBrushJScript.js',
  'objc obj-c             @shBrushPlain.js', // dummy
  'perl pl                @shBrushPerl.js',
  'php                    @shBrushPhp.js',
  'text plain             @shBrushPlain.js',
  'py python              @shBrushPython.js',
  'ruby rails ror rb      @shBrushRuby.js',
  'sass scss              @shBrushSass.js',
  'scala                  @shBrushScala.js',
  'sql                    @shBrushSql.js',
  'vb vbnet               @shBrushVb.js',
  'xml xhtml xslt html    @shBrushXml.js'
));

////////////////////////////////////
// Custom Brush Objective-C
////////////////////////////////////
/**
 * SyntaxHighlighter - Objective-C Brush
 * http://codepirate.seaandco.com/
 *
 * @version
 * 1.0.0 (February 22 2009)
 *
 * @author
 * Geoffrey Byers
 *
 * @copyright
 * Copyright (C) 2009 Geoffrey Byers.
 *
 * Licensed under a GNU Lesser General Public License.
 * http://creativecommons.org/licenses/LGPL/2.1/
 *
 *  Updated From:
 *  Code Syntax Highlighter for Objective-C.
 *  Version 0.0.2
 *  Copyright (C) 2006 Shin, YoungJin.
 *  http://scottdensmore.typepad.com/blog/2008/12/objective-c-cocoa-syntax-highlighter.html
 */

SyntaxHighlighter.brushes.ObjC = function()
{
  var datatypes =  'char bool BOOL double float int long short id void';

  var keywords = 'IBAction IBOutlet SEL YES NO readwrite readonly nonatomic nil NULL ';
  keywords += 'super self copy ';
  keywords += 'break case catch class const copy __finally __exception __try ';
  keywords += 'const_cast continue private public protected __declspec ';
  keywords += 'default delete deprecated dllexport dllimport do dynamic_cast ';
  keywords += 'else enum explicit extern if for friend goto inline ';
  keywords += 'mutable naked namespace new noinline noreturn nothrow ';
  keywords += 'register reinterpret_cast return selectany ';
  keywords += 'sizeof static static_cast struct switch template this ';
  keywords += 'thread throw true false try typedef typeid typename union ';
  keywords += 'using uuid virtual volatile whcar_t while';
  // keywords += '@property @selector @interface @end @implementation @synthesize ';


  this.regexList = [
    { regex: SyntaxHighlighter.regexLib.singleLineCComments,  css: 'comments' },    // one line comments
    { regex: SyntaxHighlighter.regexLib.multiLineCComments,   css: 'comments' },    // multiline comments
    { regex: SyntaxHighlighter.regexLib.doubleQuotedString,   css: 'string' },      // double quoted strings
    { regex: SyntaxHighlighter.regexLib.singleQuotedString,   css: 'string' },      // single quoted strings
    { regex: new RegExp('^ *#.*', 'gm'),                      css: 'variable' },    // preprocessor
    { regex: new RegExp(this.getKeywords(datatypes), 'gm'),   css: 'functions' },   // datatypes
    { regex: new RegExp(this.getKeywords(keywords), 'gm'),    css: 'functions' },   // keyword
    { regex: new RegExp('\\bNS\\w+\\b', 'g'),                 css: 'constants' },   // keyword
    { regex: new RegExp('\\bUI\\w+\\b', 'g'),                 css: 'constants' },   // keyword
    { regex: new RegExp('\\bCG\\w+\\b', 'g'),                 css: 'constants' },   // keyword
    { regex: new RegExp('@\\w+\\b', 'g'),                     css: 'functions' },   // keyword
    ];

}

SyntaxHighlighter.brushes.ObjC.prototype = new SyntaxHighlighter.Highlighter();
SyntaxHighlighter.brushes.ObjC.aliases = ['objc', 'obj-c'];

////////////////////////////////////
// Settings and initialize
////////////////////////////////////
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.defaults['toolbar'] = false;
//SyntaxHighlighter.defaults['gutter'] = false;
SyntaxHighlighter.all();
</script>

ポイントはAutoloaderの設定部分で、ファイルタイプに対応するBrushスクリプトのURLにダミーのURLを設定すること(SyntaxHighlighter.autoloader.applyの部分)。その設定後に、カスタムBrushのコードを追加して上書きしている。
実際の動作サンプルは、昨日の記事の下の方をご覧頂ければ。

参考サイト:
SyntaxHighlighter 3.0 にアップデート - 電脳スピーチ blog
アプリ開発の記録 SyntaxHighlighterでObjective-Cをきれいに表示

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

2014年2月9日日曜日

iPhoneアプリでHello World(Xcode5)

これまでモバイルアプリ開発には接点が全然なかったら、実はObjective-Cでコードを書いたことも一度もなかったんだよね。てことで、iPhoneアプリ開発をやってみようということでまずはお決まりのHello Worldしてみることにした。環境はOS X 10.9 MavericksにXcode 5.0。

まずXcodeを起動する。最初の画面で"Create a New Xcode project"を選んでプロジェクトを新規作成するか、メニューから"File > New > Project..."を選ぶ。


するとプロジェクトのテンプレートを選ぶよう言われるので、"Empty Application"を選ぶ。


プロジェクトの初期設定情報を聞かれる。次のように入力する。

Product Name: HelloWorld (製品名)
Organization Name: Takaaki Kasai (組織名)
Company Identifier: com.takaakikasai (ドメインの逆さ形式で指定)
Class Prefix: Hello (クラス名の頭につける文字列)
Devices: iPhone
Use Core Data: チェックは入れない

あとは適当な場所に保存する。


作成したプロジェクトが開くので、左のプロジェクトナビゲータから"HelloAppDelegate.m"を選ぶ。


該当部分のコードを下記のように修正する。

修正前
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.›
    self.window.backgroundColor = [UIColor whiteColor];
    [self.window makeKeyAndVisible];
    return YES;
}

修正後
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    // Override point for customization after application launch.›
    self.window.backgroundColor = [UIColor whiteColor];
    
    // 追加ここから
    UILabel * label = [[UILabel alloc] initWithFrame:CGRectMake(20.0f, 100.0f, 280.0f, 100.f)];
    label.text = @"Hello World!!";
    [self.window addSubview:label];
    // 追加ここまで
    
    [self.window makeKeyAndVisible];
    return YES;
}

コードを修正したら、Xcodeの左上の再生マークボタン("Build and then run the current scheme")をクリックする。すると、iOSシミュレータが自動的に起動して、アプリを実行してくれる。


おお、できた。それにしてもXcodeっていう専用の開発環境があるのは便利だな。

参考サイト:
http://php6.jp/iphone/basics/hello-world/

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。

2014年2月8日土曜日

gitでサブモジュールを削除する(バージョンごとの方法まとめ)

gitのサブモジュールってバージョンによって削除の方法が違う。
サブモジュールのリポジトリのルートからの相対パスが path/to/submodule の場合、リポジトリのルートで次のようなコマンドを実行すればいいと思う。

v1.8.5以上
$ git submodule deinit path/to/submodule
$ git rm path/to/submodule
$ rm -rf .git/modules/path/to/submodule

v1.8.3以上v1.8.5未満
$ git submodule deinit path/to/submodule
$ git config -f .gitmodules --remove-section submodule.path/to/submodule
$ git rm path/to/submodule
$ rm -rf .git/modules/path/to/submodule

v1.8.3未満
$ git config -f .git/config --remove-section submodule.path/to/submodule
$ git config -f .gitmodules --remove-section submodule.path/to/submodule
$ git rm --cached path/to/submodule
$ rm -rf .git/modules/path/to/submodule
$ rm -rf /path/to/submodule

ちなみに、これまで使ってたgitはDebian7のパッケージ管理システム(apt-get)でインストールしたやつだったけど、バージョンは1.7.10.4と古かったので、サブモジュールの削除も面倒。この際バージョンアップすることにした。他にも表示が親切になるらしいし。

まずコンパイルに必要なパッケージを入れる。
$ apt-get install libcurl4-gnutls-dev libexpat1-dev gettext libz-dev libssl-dev
 

git最新版のDebian公式パッケージはないので、ソースからコンパイルする。
https://code.google.com/p/git-core/downloads/list
から最新版をダウンロードする。この記事を書いている時点では1.8.5.4が最新だ。ダウンロードしたら解凍して、コンパイル&ルート権限でインストール。
% wget https://git-core.googlecode.com/files/git-1.8.5.4.tar.gz
% tar zxvf git-1.8.5.4.tar.gz
% cd git-1.8.5.4
% ./configure
% make
% su -
# make install

特に迷うこともなくあっさり完了。

ちなみに僕はzsh使いだけれど、git submodule deinitが補完で出てこない。gitのソースにはzsh用の補完関数スクリプトが添付されている(解凍したソースのcontrib/completion/git-completion.zsh)けど、これを使っても補完候補に出てこないので注意。

余談だけれどgitのバージョン上げたらgitコマンドの出力がモノクロからカラーになってて超便利。中でもgit diffで差分を色分けしてくれるのは素晴らしいね。

参考サイト:
git submoduleを今風な感じで削除する - Qiita
git submoduleについてのメモ - rcmdnk's blog
How do I remove a Git submodule? - Stack Overflow

※この記事について指摘・意見・提案・感想などありましたら下のコメント欄にどうそ。