iOSの設定画面にアプリのライセンスを置く

WWDCのアプリが設定画面にライセンスを置いていて「じゃあそうしよう」みたいな話になったので調べた。

Settings.bundleとRoot.plistを用意

まずはSettings.bundleってファイルを作れば良くって、その作り方はググるとすぐ出てくる。
具体的には「New File > Resource > Settings Bundle」でOK。
で、そのBundleのディレクトリの下に以下のような内容のRoot.plistを用意する

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>StringsTable</key>
        <string>Root</string>
        <key>PreferenceSpecifiers</key>
        <array>
                <dict>
                        <key>Type</key>
                        <string>PSChildPaneSpecifier</string>
                        <key>Title</key>
                        <string>License</string>
                        <key>File</key>
                        <string>Acknowledgements</string>
                </dict>
        </array>
</dict>
</plist>

Acknowledgements.plistを作る

上で作ったRoot.plistはAcknowledgements.plistを読むような設定になっている(Acknowledgements)ので、Acknowledgements.plistにライセンスをつらつら書けば良い。

で、例えば既に「Acknowledgement.txt」がResourcesディレクトリにあるとして、
以下のようなscriptをResourcesディレクトリに置いて叩けばAcknowledgements.plistが作れる(「Resourcesディレクトリにscript置くのはどうなの?」という疑問は置いておく)

#!perl
use strict;
use warnings;
use FindBin;

my $input = shift || "$FindBin::Bin/Acknowledgement.txt";
my $output = "$FindBin::Bin/Settings.bundle/Acknowledgements.plist";

open my $rfh, '<', $input or die "cannot open $input: $!";
my $license = do { local $/; <$rfh> };
close $rfh;

my $template = do { local $/; <DATA> };

my $plist = sprintf($template, $license);

open my $wfh, '>', $output or die "cannot open $output: $!";
print {$wfh} $plist;
close $wfh;

__DATA__
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>StringsTable</key>
        <string>Acknowledgements</string>
        <key>PreferenceSpecifiers</key>
        <array>
                <dict>
                        <key>Type</key>
                        <string>PSGroupSpecifier</string>
                        <key>Title</key>
                        <string>%s</string>
                </dict>
        </array>
</dict>
</plist>

実際に「Resources/Settings.bundle/Acknowledgements.plist」が作成されていればOK。

MacBook AirのディスクをCase-sensitiveにして大文字小文字のファイルを作れるようにする

MacBook Airを買ってから2年経ったのでこの前新たにMacBook Airを買いました。
で、会社でも家でもCase-sensitive問題で頭を抱えた経験があるので今回は最初からCase-sensitiveでインストールしました。

流れ

1. commandキーとRキーを押しながら再起動

2. 無線LANが繋がってることを確認する

3. Disk Utility を選択して、Eraseを選択。元のフォーマットはこんな感じでただのジャーナルが選ばれてる

4. Case-sensitiveを選んで消去

5. OS Xの再インストールを選ぶと「ダウンロードするよ!自動的に再起動するよ!」となるのでひたすら待つ(ウチのWiMAXがホント遅いせいで長時間がかかったけど普通は2時間くらい)

6. これで大文字ファイルと小文字ファイルが区別される

% touch test
% touch Test
% ls
Test test

おまけ. brew doctorするとこんなWarningが出ますが問題なく使えています(このWarningわかりやすくていいですね)

% brew doctor 
Warning: Your file-system on / appears to be CaSe SeNsItIvE.
Homebrew is less tested with that - don't worry but please report issues.


APPLE MacBook Air 1.3GHz Dual Core i5/13.3

APPLE MacBook Air 1.3GHz Dual Core i5/13.3"/4GB/128GB MD760J/A




Perlのリファレンス比較は==演算子

Perl徹底攻略 (WEB+DB PRESS plus)を買いました。
p.18にリファレンス同士の比較が書いてあり、

リファレンス同士の比較

2つのリファレンスが同じものを指していれば値は等しくなります。ですから、リファレンスが入っている変数$xと$yがあったとき、==演算子で値を比べれば同じ物を指しているかどうかがわかります。

# $xと$yにはリファレンスが入っている
if ($x == $y) {
    # $xと$yは同じものを指している
} else {
    # $xと$yは別のものを指している
}

となっていて
「あれ、リファレンスって数字ではないのに==演算子なのか。アドレスが入っているからかな?」
と気になったのでperldoc perlrefしてみました。

Using a reference as a number produces an integer representing its storage location in memory. The only useful thing to be done with this is to compare two references numerically to see whether they refer to the same location.

if ($ref1 == $ref2) {  # cheap numeric compare of references
    print "refs 1 and 2 refer to the same thing\n";
}

リファレンスを数値として使うと、メモリ内のストレージの位置の 整数表現を生成します。 これを利用して便利な唯一の状況は、二つのリファレンスを数値として 比較することで、同じ場所を参照しているかどうかを調べる場合です。

if ($ref1 == $ref2) {  # cheap numeric compare of references
    print "refs 1 and 2 refer to the same thing\n";
}

http://perldoc.jp/docs/perl/5.16.1/perlref.pod

おー、なるほど。やっぱり==で数値として扱われて比較されるのかー。
こういうコンテキストやっぱり楽しい。Perl好きです。

commコマンドを使って文字列でsort済みの2つのファイルの比較をし「片方だけに存在する行」「共通の行」を出力する

追記

「文字列でsort済みの」って入れ忘れてた。
manに

The comm utility reads file1 and file2, which should be sorted lexically,

って書いてある通り、「sorted lexically」でないとダメ(「sort -n」ではなく「sort」でsortする)

なので、

comm <(sort id_num.txt) <(sort id_even.txt)

ってやるのが確実

追記終わり



commコマンドの名前をすぐに忘れてしまい、ググっても「catしてuniq」とかそういうのしか出てこなくて「あのコマンド名なんだっけなー」ってなるのでメモ残す

やりたいこと

  • 2つのファイルがあり、どちらかのファイルにのみ存在する行と、その行がどちらのファイルに存在するかを出力したい
  • 2つのファイルがあり、両方に存在する行を出力したい

実際にやってみる

書いてもよくわからないし後で見たときによくわからないので実践。

id_num.txt と id_even.txt という2つのファイルを用意する
  • id_num.txt
% cat id_num.txt
1
2
3
4
5
  • id_even.txt
% cat id_even.txt
2
4
6

grepすればわかる通り、

  • 「1」「3」「5」はnumにだけ存在する
  • 「6」はevenにだけ存在する
  • 「2」と「4」は共通
commコマンドを(オプションなしで)実行するとタブ区切りで以下の出力がされる
% comm id_num.txt id_even.txt
1
		2
3
		4
5
	6

左から「1つめの引数のファイルだけに存在する行」「2つめの引数のファイルだけに存在する行」「両方のファイルに存在する行」となる

「表示したくないカラム番号」をオプションで渡す
  • id_num.txtだけに存在する行が欲しい(つまり、2番目と3番目のカラムを表示したくない)
% comm -23 id_num.txt id_even.txt
1
3
5
  • id_even.txtだけに存在する行が欲しい(つまり、1番目と3番目のカラムを表示したくない)
% comm -13 id_num.txt id_even.txt
6
  • 両方のファイルに存在する行が欲しい(つまり、1番目と2番目のカラムを表示したくない)
% comm -12 id_num.txt id_even.txt
2
4
(補足)大文字と小文字を区別したくない場合には「-i」を付ければ良い
% cat id_large.txt
A

% cat id_small.txt
a

% comm id_large.txt id_small.txt # 区別されているので別々に出力される
A
	a

% comm -i id_large.txt id_small.txt # 区別されないので「共通の行」として出力される
		A

ちなみに comm の由来と思われるもの

manを見ると

NAME
     comm -- select or reject lines common to two files

と書いてあるので「common」の略ですかね

foreachで連想配列に代入した後はcurrentが変わるのでwhileでeachするとハマる

へぇ、phpってこういう動きするんだーと思ったので。
普段while each使わないのでハマったことなかったのだけど、何かbugがあるなぁと思うコードを見てみたらforeachの後にwhile使ってるのが原因ってことがわかった。



bugをわかりにくくしていたのは以下のことがあったから。

  • foreachしてるでカーソルは最後まで進む
  • ただし、foreachの中で元の連想配列に代入していた場合にはカーソルが2つめになる

このせいでwhileが中途半端に成功してるのがbugに繋がった。

<?php
$hash = array(
    'a' => 1,
    'b' => 2,
    'c' => 3,
);

var_dump('current => ' . current($hash)); // string(20) "before: current => 1"

foreach ($hash as $key => $val) {
}

var_dump('foreach: current => ' . current($hash)); // string(19) "after: current => "

foreach ($hash as $key => $val) {
    $hash[$key] = $val;
}

var_dump('foreach + insert: current => ' . current($hash)); // string(19) "after: current => 2"

while (list($key, $val) = each($hash)) {
    var_dump($key, $val);
}

// string(1) "b"
// int(2)
// string(1) "c"
// int(3)

refs. PHP: each - Manual

USBに1Password.agilekeychainを入れててパスワード解除ができなくなったときの対処法

MacBook Airのディスク容量が少ないのでDropboxをUSBに入れてそこに1Passwordを入れているのですが、
USBを外して付け直したりすると1Passwordが上手く動かないことがあり困りました。

現象

  • 正しいマスターパスワードを入れているのに解除できない
    • コンソールにはこんな感じのエラーが出る

13/03/17 14:11:47.436 1Password Helper: 暗号化キーファイルは存在しますが 'file://localhost/Users/monmon/Dropbox/1Password/1Password.agilekeychain/data/default/1password.keys' からは読み込めません

  • 1passwordのhelper(メニューに出てるやつ)が起動したり終了したりを繰り返す

対処法

権限の問題だと思うんですがググったりしても同じような例が出なかったので、
1Passwordを終了させた後に

mv /Users/monmon/Library/Containers/com.agilebits.onepassword-osx-helper ~/tmp/. 

みたいにしてonepasswordの情報をよけて起動し直しました。

そうすると「初めてお使いですか?」「使ったことありますか?」のあの起動画面が出てくるので、「使ったことある」を選んで進めればよしと。

Rubyはハッシュに数値と文字列のkeyが持てるんですね

Rubyで書かれたコードにbugがあって調べることになり、初めて知りました。

Ruby

% ruby -e 'h = {1 => true, "1" => false}; p h'
{1=>true, "1"=>false}

Perl

% perl -MData::Dumper -e '$h = {1 => true, "1" => false}; warn Dumper $h'
$VAR1 = {
          '1' => 'false'
        };

PHP

% php -r '$h = array(1 => true, "1" => false); var_dump($h);'
array(1) {
  [1]=>
  bool(false)
}

追記

ということで、

  • Object#hash ハッシュの格納に用いられるハッシュ値の計算
  • Object#eql? キーの同一性判定

の2つを定義すれば何でもkeyになるよという話。
ハッシュの特性を考えれば確かにそうですよねそうですよね。