Objective-Cのカテゴリ使って既存クラスの拡張書いてみた

日付に関する処理ならNSDateにメソッドを追加して処理したいと思っていたのですが、

カテゴリという機能を使えばできるという事がわかったので作ってみました。


これを使って、NSStringにbase64エンコード処理するメソッドとかNSDateに5分単位に時間を丸める処理とかを追加すると
いちいち別のメソッドを作らなくても、自分自身で処理することができます。

下記のjson-frameworkとかはカテゴリを使って既存クラスを拡張していたりします。
stig/json-framework · GitHub


例えばこれを

NSDate *date = [NSDate date];
NSDate *roundDate = [DateUtils roundDateTo5Minutes:date];

こう書き換えらます。

NSDate *date = [NSDate date] roundDateTo5Minutes];

ソース

こんな感じで書けばOK

iPhone-GAE/Jで送信されたTimestamp(ミリ秒)のデータをiPhone側でNSDateに変換して表示する(書きかけ)

iPhone側で好きなタイムゾーンで時間表示したらいいじゃん

GAE/J側(Slim3)

  • Date型を時間を保存
  • JSONでiPhoneへデータを返す

iPhone

Slim3のUnitテスト

Slim3のプロジェクトについてるbuild.xmlを使ってmodel, service, controllerを生成すれば自動生成できるけど、Utilityクラスなど自分で作ったクラスに対するテストケースを作る場合のためのメモ

Controller

  • ControllerTestCaseを継承する
    • Datastore, MemcacheAPIが使えるようになる
    • tester変数を使ってブラウザテスト的なことができる
import org.slim3.tester.ControllerTestCase;
import org.junit.*;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

public class MyControllerTest extends ControllerTestCase {
//[...]
}

それ以外

  • AppEngineTestCaseを継承する
    • Datastore, MemcacheAPIが使えるようになる
import org.slim3.tester.AppEngineTestCase;
import org.junit.*;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

public class MyServiceTest extends AppEngineTestCase {
//[...]
}

EC2上でのMySQL Multi-masterフェイルオーバー、DynamicDNS(BIND)に対応させた

d:id:stanaka:20100223 EC2上でMySQL Multi-masterフェイルオーバー - 田中慎司のログ をみて、EC2上でMulti-masterのフェイルオーバーが可能なことを知りましたが、
EC2インスタンスを管理するDNSがDynamicDNSな環境なため、修正しないと使えないなと思ってns_agentを拡張して使えるようにしてみました。

事前準備

基本的な設定は参照元のブログと同じなのでそっちにあわせます。
オリジナルのns_agentはphpで動作するのですが、今回作成したものもphpで動作します。

1. pearをインストールして、Net_DNS2をインストールします。

$ pear list-upgrades
$ sudo pear install Net_DNS2


2. mmm_ddns_agentをチェックアウトして、設定ファイルを作成します。

$ cd ~/utils
$ git clone git://github.com/roothybrid7/mmm_ddns_agent.git


DNSサーバの設定とレコードの設定を行います。

$ cd mmm_ddns_agent
$ cp mmm_ddns_agent.conf.sample mmm_ddns_agent.conf
$ vim mmm_ddns_agent.conf


確認

参照元のブログと同様に各デーモンプロセスを起動します。
mmm_ddns_agentの起動は、下記の通りです。

$ php ddns_agent.php


本番運用の際は、DaemonToolsやinit.dスクリプトを使用した方がいいでしょう。
実際にMySQL multi-masterを使った確認は別の人がやったのですが、とりあえず問題なく動いたようです。

RightScaleで生成したEC2インスタンスを動的にCapistranoのRoleにセットする拡張書いた

CapistranoはDeployツールですが、これを使用すると複数のサーバを効率的に管理できます。
通常は設定ファイルに管理するサーバのホスト名またはIPアドレスをroleと呼ばれるものとセットで記述するのですが、
EC2インスタンスは意外とよく落ちるのでその度にホストを書き換えなければなりません。

EC2インスタンスをRightScaleというWebベースの管理ツールを使用して管理しているのですが、
これを使用すると、起動時の初期セットアップを自動化できたりインスタンスをグループ化したり、インスタンスに名前をつけたりすることが可能で管理がずいぶん楽になります。

また、APIも公開されているためこのAPIを使って管理を自動化することも可能です。

題名のCapistranoの拡張書いたのはずいぶん前なのですが、RightScaleを使ってEC2インスタンスを管理している場合
APIを使ってEC2インスタンスの一覧が取得できるのでこれを使用してCapistranoのRoleを動的にセットするような機能をつけました。

ここではその使い方を備忘録として記録しておきたいと思います。

前提

  • RightScaleを使用してEC2インスタンスを管理していること
  • それらのインスタンスは、RightScaleのDeploymentと呼ばれる単位でグループ化していること
  • RightScaleのServerArray(Auto Scaling機能)の使用有無は問わない
  • Capistranoをインストールしたホストから管理対象のインスタンスSSH接続可能になっていること

環境

  • CentOS5.4
    • AMIはRightImage_CentOS_5.4_x64_v5.6.28あたりを使用(RightScaleのTag機能が使えればOK)
  • Capistrano v2.5.1(capistrano-extとかは必要ならインストール)
  • Ruby v1.8.5

インストール

RightScaleのAMIを使用すれば必要なものはだいたいインストールされているので、足りないものをインストールします。

1. 必要なPackageのインストール

ruby-develが必要なのでインストール

$ sudo yum install ruby-devel


rubygemsはyumでインストールするよりも新しいバージョンがあらかじめインストールされているのでそっちを使います。

2. capistranoのインストール

capistrano本体と今回開発した拡張: cap-rightscaleをインストールします。
下記のコマンドで一緒に入ります。

$ sudo gem install cap-rightscale
$ sudo gem install capistrano-ext #必要であればインストール

動作確認

簡単な設定は下記の通りです。

  • RightScale認証情報の設定

capistrano実行ユーザのホームディレクトリに''.rsconf''を作成し、下記の形式でファイルを作成します。
ツールで使用するRightScaleのユーザを別途作成した方がいいでしょう。

$ mkdir $HOME/.rsconf
$ vim $HOME/.rsconf/rsapiconfig.yml
# rsapiconfig.yml
username: user@example.com  #RightScaleのログインID
password: yourpassword      #RightScaleのログインパスワード
account: 1                  #RightScaleのAccountページのURL末尾の番号
  • capifyを実行して必要なファイルの生成
$ cd work/cap_test
$ capify .
  • deploy.rbの編集
$ vim config/deploy.rb


下記のように書き換えます。

# config/deploy.rb
require 'rubygems'
require 'cap-rightscale'
require 'cap-rightscale/recipes'

DEPLOYMENT_ID = xxxxx       # RightScale Deployment IDを指定します。(RightScaleの該当のDeploymentページのURLの末尾の番号)
SERVER_ARRAY_ID = xxxx      # RightScale ServerArray IDを指定します。(RightScaleの該当のServerArrayページのURLの末尾の番号)

# set roles
nickname :web, :name_prefix => "lb", :deployment => DEPLOYMENT_ID
server_array :app, :array_id => SERVER_ARRAY_ID
tag :dbm, :tags => "xx_db:role=master", :deployment => DEPLOYMENT_ID
tag :dbs, :tags => "xx_db:role=slave", :deployment => DEPLOYMENT_ID, :no_release => true


Roleをセットするメソッドを3種類あるのですが、

  • nicknameで指定すると、指定したDevploymentの範囲内で、RightScaleで設定した名前(nickname)が:name_prefixに部分一致するインスタンス
  • server_arrayで指定すると、RightScaleで作成した該当のIDを持つServerArrayの現在起動しているインスタンス
  • tagで指定すると、指定したDevploymentの範囲内で、RightScaleでインスタンスにつけた指定のtagを持つインスタンス

が設定されます。

設定通りインスタンスがセットされるかどうかは、下記のコマンドで確認できます。

$ cap rs:none

使い方

通常のCapistranoの使い方と同じです。
RightScaleAPIから取得したインスタンスのリストをキャッシュしているのですが、そのキャッシュをクリアするTaskを追加しています。
キャッシュの有効期限はデフォルトでは1日ですが、設定で変更可能です。

キャッシュのクリア
$ cap rs:cache:clear
OR
$ cap rs:cc
キャッシュを使用しない
$ cap invoke COMMAND='hostname' RS_CACHE=false

設定など

Role定義
  • RightScaleのnicknameでRoleをセット
# config/deploy.rb

# Deployment ID:12345でグループ化しているインスタンスのうち、RightScaleのnickname: ''lb''に一致するものをRole '':web''にセットする
nickname :web, :name_prefix => "lb", :deployment => 12345
#=> 通常のCapistranoの設定だと: role :web, *%w[lb-001 lb-002 lb-003]
  • RightScaleのServerArray(Auto scaling)でRoleをセット
#RightScaleのServerArray(Auto scaling) ID:6789で管理しているインスタンスをRole'':app''にセットする
server_array :app, :array_id => 6789, :backup => true
#=> 通常のCapistranoの設定だと: role(:app, :backup => true) { %w[web-001 web-002 web-003] }
  • RightScaleのTagでRoleをセット
#RightScaleのTag: "xx_db:env=prod", "xx_db:role=master"が設定されているインスタンスをRole'':dbm''にセットする
tag :dbm, :name_prefix => "db", :tags => ["xx_db:env=prod", "xx_db:role=master"], :deployment => 12345, :primary => true
#=> 通常のCapistranoの設定だと: role(:dbm, :primary => true) { %w[db-001 db-002] }^4
キャッシュ設定

下記の設定により、キャッシュの有効期限を変更することが可能です。

  • キャッシュ保持時間変更
# config/deploy.rb
set :rs_lifetime, 86400         # 1日(default)
set :rs_lifetime, 86400 * 7     # 1週間
set :rs_lifetime, 7200          # 2時間
set :rs_lifetime, 0             # 無効
rs_disable :use_rs_cache        # 同じく無効
set :rs_lifetime, -1            # 無期限
Roleにセットするホスト情報

デフォルトではAmazonEC2 Local IP address(10.から始まる)を使用します。

rs_enable :use_public_ip        # EC2 Public IP addressを使用する場合
rs_enable :use_nickname         # RightScaleのnicknameをそのまま使う(DNSやhostsファイルによる名前解決の設定が事前に必要)


ServerArray(Auto scaling機能)で起動する各インスタンスのnicknameの末尾には''[スペース]#番号''が付与されるので、nicknameをDNSホスト名に置き換えるには工夫する必要があります。
nickname: ''web #1'' => DNS hostname: ''web-001''に変換している場合は、Capistranoでは下記のように設定します。

rs_array_number_format "-%03d"  # sprintf formatを使って変換後の書式を指定
Validate

無効なホストを除外するために、pingによる死活確認とDNSにホストが登録されているかどうかをチェックすることができます。
ただし、既にインスタンスがキャッシュされている場合は実行しませんので、キャッシュクリアする必要があります。

rs_enable :validate_echo, :validate_resolv
#=> :validate_echo[ハングアップしたホストを除外]
#=> :validate_resolv[名前解決できないホストを除外]

おわりに

RightscaleAPIを使うとインスタンスの管理を自動化するツールを作成することが可能です。
このほかにも、Nagiosのホスト設定の自動化やPuppetのmanifest適用制御を行ったりと様々のことができます。

このcapistrano拡張で、自分で作成したRubyで書いたRightScaleAPIのWrapperライブラリを使用しているのですが、その頃はRubyとか全くわかっていなかった時期に作ったので
Mixinって何? メタプログラミングすぎて解読できなくてつらい...とかでいろいろ妥協した部分があるため、今度どっかで使用することになったら書き直したいなと思ってます。

Rails3+jQueryでデータ削除ができない

Rails3でjQueryを使った場合に、下記のリンクをクリックして、データを削除しようとしたら
なぜかPOSTではなく、GETでデータを取得してしまう問題がありました。

<a rel="nofollow" data-method="delete" data-confirm="Are you sure?" href="/foos/2">Destroy</a>


ダイアログも表示されないことからJavascriptが動いてないんでないかと思ってたら
同じような問題がありました。

rails3 + jqueryで DELETEやらPOSTのリンクががが。 | おれせか

上のサイトをみると、どうやら

$ rails g jquery:install

だけでは足りなくて、追加の設定が必要らしいというところまでわかりました。

以下は修正の手順です。

手順

1. githubにあるrails.jsをダウンロードして、`public/javascript`に配置
railsコマンドでjqueryをインストールすると、デフォルトのrails.js(prototype.js由来)は消されてしまうのですが、
jquery用のrails.jsを追加する必要があります。

$ cd public/javascript
$ wget https://github.com/rails/jquery-ujs/raw/master/src/rails.js

2. `config/application.rb`に記述を追加
デフォルトでは空なので、jqueryrailsを追加します。

config.action_view.javascript_expansions[:defaults] = %w(jquery rails)

これで動くはずです。

確認

$ rails g scaffold foo name:string
      invoke  active_record
      create    db/migrate/20110520140721_create_foos.rb
      create    app/models/foo.rb
      invoke    rspec
      create      spec/models/foo_spec.rb
       route  resources :foos
      invoke  scaffold_controller
      create    app/controllers/foos_controller.rb
      invoke    haml
      create      app/views/foos
      create      app/views/foos/index.html.haml
      create      app/views/foos/edit.html.haml
      create      app/views/foos/show.html.haml
      create      app/views/foos/new.html.haml
      create      app/views/foos/_form.html.haml
      invoke    rspec
      create      spec/controllers/foos_controller_spec.rb
      create      spec/views/foos/edit.html.haml_spec.rb
      create      spec/views/foos/index.html.haml_spec.rb
      create      spec/views/foos/new.html.haml_spec.rb
      create      spec/views/foos/show.html.haml_spec.rb
      invoke      helper
      create        spec/helpers/foos_helper_spec.rb
      create      spec/routing/foos_routing_spec.rb
      invoke      rspec
      create        spec/requests/foos_spec.rb
      invoke    helper
      create      app/helpers/foos_helper.rb
      invoke      rspec
      invoke  stylesheets
   identical    public/stylesheets/scaffold.css

$ rake db:migrate
==  CreateFoos: migrating =====================================================
-- create_table(:foos)
   -> 0.0017s
==  CreateFoos: migrated (0.0027s) ============================================
$ rails s

f:id:zepbag:20110520233047p:image
f:id:zepbag:20110520233046p:image

MacPortsをやめてHomebrewにしてみた

MacPortsアップデートに時間が(ry
`port list | wc -l`ってやって、8100くらいパッケージが入ってたみたいですが、これほとんどSystemのやつでいいじゃないかと
思ったので、MacPortsでインストールした奴は必要になったときに入れることにします。

事前準備

必要なもの
Xcode with X11のダウンロード(右下のダウンロードリンク:Xcode3)

Xcode4がほしかったら、$4.99出して買ったらいいよ!!

Xcode 4 - Apple Developer

手順

Macportsのアンインストール

いらないので削除します。

$sudo port -f uninstall installed
$sudo mv /opt/local Tmp/
Homebrewのインストール

インストールスクリプトがrubyスクリプトのようなので、事前確認で新規にシェルを立ち上げてsystemのrubyが使えることを確認します。
(rubyMacportsでインストールしていたので)

$which ruby
/usr/bin/ruby

で、インストール

$ ruby -e "$(curl -fsSLk https://gist.github.com/raw/323731/install_homebrew.rb)"
セットアップ

パッケージの管理にgitを使うらしいのでインストールします。

$ brew install git

これで使えるようにはなりましたが、このままだと、`brew update`がこけるので追加の設定を行います。

※追記
どうやら、一度`brew update`を実施してgit repositoryを作らせないといけないようです。

$ brew update
$ cd `brew --prefix`
# Homebrewのgit repositoryを追加
$ git remote add origin git://github.com/mxcl/homebrew.git
# 最新をダウンロード
$ git fetch origin
# 作業ツリーをリセットして最新のを反映
$ git reset --hard origin/master

updateできるか確認します。

$ brew update