久しぶりにRailsを勉強しようとしたら開始直後にハマりまくった

bundler で各種gemをインストールしようとすると失敗する

$ bundle install --path vendor/bundle
Fetching gem metadata from https://rubygems.org/..........
Fetching additional metadata from https://rubygems.org/..
Resolving dependencies...
Using rake 10.2.2
Using i18n 0.6.9
Using minitest 4.7.5
Using multi_json 1.9.2
Using thread_safe 0.3.3
Using tzinfo 0.3.39
Using activesupport 4.0.2
Using builder 3.1.4
Using erubis 2.7.0
Using rack 1.5.2
Using rack-test 0.6.2
Using actionpack 4.0.2
Using mime-types 1.25.1
Using polyglot 0.3.4
Using treetop 1.4.15
Using mail 2.5.4
Using actionmailer 4.0.2
Using activemodel 4.0.2
Using activerecord-deprecated_finders 1.0.3
Using arel 4.0.2
Using activerecord 4.0.2
Using bundler 1.6.0.pre.2
Using coffee-script-source 1.7.0
Using execjs 2.0.2
Using coffee-script 2.2.0
Using thor 0.19.1
Using railties 4.0.2
Using coffee-rails 4.0.1
Using hike 1.2.3
Using jbuilder 1.5.3
Using jquery-rails 3.1.0

Gem::Ext::BuildError: ERROR: Failed to build gem native extension.

/System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/bin/ruby extconf.rb
creating Makefile

make "DESTDIR=" clean

make "DESTDIR="
compiling generator.c
linking shared-object json/ext/generator.bundle
clang: error: unknown argument: '-multiply_definedsuppress' [-Wunused-command-line-argument-hard-error-in-future]
clang: note: this will be a hard error (cannot be downgraded to a warning) in the future
make: *** [generator.bundle] Error 1

make failed, exit code 2


Gem files will remain installed in /Users/ihiro81/app_name/vendor/bundle/ruby/2.0.0/gems/json-1.8.1 for inspection.
Results logged to /Users/ihiro81/app_name/vendor/bundle/ruby/2.0.0/extensions/universal-darwin-13/2.0.0/json-1.8.1/gem_make.out
An error occurred while installing json (1.8.1), and Bundler cannot continue.
Make sure that `gem install json -v '1.8.1'` succeeds before bundling.

すでにインストールされてるのに、gem install jsonを実行するようにいってきます。

bundleとかgemに関する問題かと思いそっち方面で調べるけどことごとく失敗。

ふと、実行結果を見なおしてるとエラーメッセージでてた。(気づくの遅い

clang: error: unknown argument: '-multiply_definedsuppress' [-Wunused-command-line-argument-hard-error-in-future]

これでググってみると以下のサイトがヒット
https://langui.sh/2014/03/10/wunused-command-line-argument-hard-error-in-future-is-a-harsh-mistress/

どうやらxcodeコマンドラインツールがosアップデートの際に、clang っていうのが更新されたのが原因とのこと。

以下のコマンドを実行する。

export ARCHFLAGS="-Wno-error=unused-command-line-argument-hard-error-in-future"

ただし、こちらの方法でも将来的に動作しなくなるとのことなので注意が必要みたい。

もう一度コマンドを実行

$ bundle install --path vendor/bundle
Fetching gem metadata from https://rubygems.org/..........
Fetching additional metadata from https://rubygems.org/..
Resolving dependencies...
Using rake 10.2.2
Using i18n 0.6.9
Using minitest 4.7.5
Using multi_json 1.9.2
Using thread_safe 0.3.3
Using tzinfo 0.3.39
Using activesupport 4.0.2
Using builder 3.1.4
Using erubis 2.7.0
Using rack 1.5.2
Using rack-test 0.6.2
Using actionpack 4.0.2
Using mime-types 1.25.1
Using polyglot 0.3.4
Using treetop 1.4.15
Using mail 2.5.4
Using actionmailer 4.0.2
Using activemodel 4.0.2
Using activerecord-deprecated_finders 1.0.3
Using arel 4.0.2
Using activerecord 4.0.2
Using bundler 1.6.0.pre.2
Using coffee-script-source 1.7.0
Using execjs 2.0.2
Using coffee-script 2.2.0
Using thor 0.19.1
Using railties 4.0.2
Using coffee-rails 4.0.1
Using hike 1.2.3
Using jbuilder 1.5.3
Using jquery-rails 3.1.0
Installing json 1.8.1
Installing tilt 1.4.1
Installing sprockets 2.11.0
Installing sprockets-rails 2.0.1
Installing rails 4.0.2
Installing rdoc 4.1.1
Installing sass 3.2.19
Installing sass-rails 4.0.3
Installing sdoc 0.4.0
Installing sqlite3 1.3.9
Installing turbolinks 2.2.2
Installing uglifier 2.5.0
Your bundle is complete!
It was installed into ./vendor/bundle
Post-install message from rdoc:
Depending on your version of ruby, you may need to install ruby rdoc/ri data:<= 1.8.6 : unsupported
= 1.8.7 : gem install rdoc-data; rdoc-data --install
= 1.9.1 : gem install rdoc-data; rdoc-data --install
>= 1.9.2 : nothing to do! Yay!

成功しました!

rake を実行すると失敗する

しかし、喜んだのもつかの間すぐにまたハマるorz

rake db:migrate
Could not find rake-10.2.2 in any of the sources
Run `bundle install` to install missing gems.

rakeがインストールされているにもかかわらずまたもエラーに。
bundle exec を付けると、プロジェクトのライブラリ保存場所から読みだせという意味になるとのことなので実行してみた。

$ bundle exec rake db:migrate
Could not find rake-10.2.2 in any of the sources
Run `bundle install` to install missing gems.

こちらでもダメでした。

最終的に成功したのはこちらの記事
http://altarf.net/computer/rails/1814

HOMEにある.gemを削除でいけるとのこと。
HOMEに.gemなんて普通作らないよね。ッて書いてありましたが、なぜか自分のところにもありましたw

rm -rf ~/.gem

うーん、また失敗。同じエラーに。

同じページにやったけどダメだったっていうのもあったので一応やってみたらこっちでビンゴでした。

ここ↓

Could not find rake with bundle exec

これを見ると、 .bundle/config の
BUNDLE_DISABLE_SHARED_GEMS: ’1′
の行を消せ! と書いてある。
※これはさっきbundle install の先を変えた際にconfigへ追加される行のひとつ

それで動く、 と。。。
え、けどこれって共通のgemを見なくする設定だよね。
さっきbundle install でvender/bundler にわざわざインストールしてるんですが。。
まぁいいや、消してみよ。

消した ⇒ ダメ

というわけで、BUNDLE_DISABLE_SHARED_GEMS: ’1′ を消して実行

$ bundle exec rake db:migrate
/Users/ihiro81/app_name/vendor/bundle/ruby/2.0.0/gems/sqlite3-1.3.9/lib/sqlite3/sqlite3_native.bundle: [BUG] Segmentation fault at 0x00000000000418
ruby 2.1.0p0 (2013-12-25 revision 44422) [x86_64-darwin13.0]


長いので省略

[NOTE]
You may have encountered a bug in the Ruby interpreter or extension libraries.
Bug reports are welcome.
For details: http://www.ruby-lang.org/bugreport.html

Abort trap: 6

.gem は削除する必要があったのかなかったのかわかりませんが、成功しました!

ここまでやったのはいいけど

vagrant使ったほうがあとあとも楽なんじゃないかと気づき、さっそく試してみたら何も問題なくできました。

最近だとmacに直接インストールしてやる必要ないですねー。

逆に特有の問題でハマります。

vagrant最高!!

こんな時期に今さらだけど人型寝袋を買ってみたらすごいよかった!

僕は2週間前くらいに知ったのだけど、調べてみるとメディアやブログに取り上げられててそこそこ有名っぽい。

届いた箱です。
f:id:ihiro81:20140408161757j:plain

こんなバックに入ってます。持ち運びもできますね。
f:id:ihiro81:20140408161831j:plain

広げたところ
f:id:ihiro81:20140408162159j:plain

暖かくなってきたけど、部屋で厚着あまりしたくない派。
なのでまだヒーターちょこちょこ使ってて電気代がバカにならない。

1週間ほど使ってみたところ、冬はこれだけだと寒そうだけど、今はちょうどいい!
あと2ヶ月くらいは使えるかなー。

買った奴は最低利用温度5度で、薄着だと10〜15度くらいがちょうどよさそう。
最低利用温度が-6度のやつもあるので冬はそっちを買おうかなぁ。高いけど。

ver6.0が一番新しくてスッキリした見ためでいいんだけどver5.0よりも3000円くらい高いです。
室内利用だしお試しなので今回はver5.0を購入しました。

amazonのレビューでちょっと小さめを買うといいと書いてあったけど、
ファスナーを閉じてみると立ってるときはいいんだけど、寝てる時に手を伸ばすと窮屈でした。
幅とかはもともとぶかぶかなんでちょっと大きくてもそんなに使い勝手は変わらないので、次買うときは少し大きいやつにしよう。

あと自分がちょっとアトピー体質なんだけど、スペースがあかないため寝てる間にかくことができなくてよかったです。


ロゴス(LOGOS) ダウンワンピースシュラフ・-6[最低使用温度-6度] 72600500

ロゴス(LOGOS) ダウンワンピースシュラフ・-6[最低使用温度-6度] 72600500

CakeEmail のログに件名、宛先などを追加する

CakePHP2 でのメール送信は CakeEmail を使っているのですが、メールのログを見てみると、件名や宛先(to,cc,bcc)が入っていませんでした。
残しておきたいので以下のようにしました。

まずは、app/Lib/Network/Email フォルダを作成して AppCakeEmail.php というファイル名で下記のコードを保存します。
(コアファイルの Network/Email/CakeEmail.phpの send() 関数をコピーしたものにちょっと手を加えてます)

<?php
App::uses('CakeEmail', 'Network/Email');

class AppCakeEmail extends CakeEmail {

	public function send($content = null) {
		if (empty($this->_from)) {
			throw new SocketException(__d('cake_dev', 'From is not specified.'));
		}
		if (empty($this->_to) && empty($this->_cc) && empty($this->_bcc)) {
			throw new SocketException(__d('cake_dev', 'You need to specify at least one destination for to, cc or bcc.'));
		}

		if (is_array($content)) {
			$content = implode("\n", $content) . "\n";
		}

		$this->_textMessage = $this->_htmlMessage = '';
		$this->_createBoundary();
		$this->_message = $this->_render($this->_wrap($content));

		$contents = $this->transportClass()->send($this);
		if (!empty($this->_config['log'])) {
			$level = LOG_DEBUG;
			if ($this->_config['log'] !== true) {
				$level = $this->_config['log'];
			}
			$to = $cc = $bcc = '';
			if (!empty($this->_to)) {
				$to = PHP_EOL . 'To:' . implode(',', $this->_to);
			}
			if (!empty($this->_cc)) {
				$cc = PHP_EOL . 'Cc:' . implode(',', $this->_cc);
			}
			if (!empty($this->_bcc)) {
				$bcc = PHP_EOL . 'Bcc:' . implode(',', $this->_bcc);
			}
			if (!empty($this->_subject)) {
				$subject = PHP_EOL . 'Subject:' . $this->_subject;
			}
			CakeLog::write($level, $to . $cc . $bcc . $subject . PHP_EOL . $contents['headers'] . PHP_EOL . $contents['message']);
		}
		return $contents;
	}
}


あとは、CakeEmail を使用する時に app/Lib/Network/Email/AppCakeEmail.php を読み込んで使えばOKです!

<?php
	public function sendMail() {
		App::uses('AppCakeEmail', 'Lib/Network/Email');

		$email = new AppCakeEmail('default');
		$email->config(array('log' => 'emails'));
		      ->to('noreply@example.com')
		   ->send();
	}


本当は別名で継承してというやり方ではなく、 CakeEmail のままで出来れば一番いいのですがわかりませんでした。
何か方法があれば教えてください!!

以上。

日本語のカラム名を使った時に文字化けする

既存のデータベース(SQLServer)からデータを取得するAPIを作成しようとした時に躓いたので残しとく。
SQLServerは初めてだったのですが、テーブル名やカラム名を日本語にすることが多いんですかね。
MySQLとかも日本語のカラム名使う場合があるみたいなのでそういう時にも変換が必要だと思います。

取得した内容を出力すると、データは表示されるけど、カラム名が文字化ける。
調べるとカラム名sjis で、データは utf8 でした。

まずは Config/database.php の encoding を修正してみる。がエラーになる。
charsetを付け足すと文字コードは変わるが、カラム名とデータの文字コードが入れ替わるだけでやっぱり片方文字化ける。

DB設定では直りそうにないので、コア部分でカラム名文字コードだけ utf8 に変換するようにしました。


app/Model/Datasource/Database/AppSqlserver.php を作成します。

<?php
class AppSqlserver extends Sqlserver {

	public function resultSet($results) {
		$this->map = array();
		$numFields = $results->columnCount();
		$index = 0;

		while ($numFields-- > 0) {
			$column = $results->getColumnMeta($index);
+			$name = mb_convert_encoding($column['name'], 'utf8', 'sjis');

			if (strpos($name, '__')) {
				if (isset($this->_fieldMappings[$name]) && strpos($this->_fieldMappings[$name], '.')) {
					$map = explode('.', $this->_fieldMappings[$name]);
				} elseif (isset($this->_fieldMappings[$name])) {
					$map = array(0, $this->_fieldMappings[$name]);
				} else {
					$map = array(0, $name);
				}
			} else {
				$map = array(0, $name);
			}
			$map[] = ($column['sqlsrv:decl_type'] == 'bit') ? 'boolean' : $column['native_type'];
			$this->map[$index++] = $map;
		}
	}
}

app/Config/database.php で作成した datasource を読み込むようにする。

<?php
class DATABASE_CONFIG {

	public $default = array(
-		'datasource' => 'Database/Sqlserver',
+		'datasource' => 'Database/AppSqlserver',
		'persistent' => false,
		'host' => 'localhost',
		'login' => 'root',
		'password' => '*********',
		'database' => 'database',
	);
}

カラム名・データともに utf8 で出力されるようになりました。
encoding とか charset で指定した文字コードを利用するようにしたほうがいいのですが、そこまでやってません。

以上。

CakePHP2系のShellでComponentを使う。

2系から shell は app/Console/Command に配置するようになりました。
ファイル名は TestShell.php とします。

1.3系ではファイルの読み込みに App::import を使っていましたが、2系では App::uses を使うようになりました。

ComponentCollection と、使用したい Component を読み込んで、

startup() でコード例のようにインスタンス化します。

<?php
App::uses('ComponentCollection', 'Controller');
App::uses('TestComponent', 'Controller/Component');

class TestShell extends Shell {

    public function startup() {
	$collection = new ComponentCollection();
	$this->Test = new TestComponent($collection);
	parent::startup();
    }
}

これで Shell で TestComponent を使えるようになりました!!

MongoDBプラグインを使う時には、behavior の SqlCompatible をご一緒にっ!

先日のエントリーの「MongoDBプラグインとSearchプラグインであいまい検索できるようにした。」ですが、
もっと簡単な方法が提供されてました。
http://d.hatena.ne.jp/ihiro81/20111031/1320080671

SqlCompatible という behavior が用意されているので下記のように設定するだけでOKでした。

<?php
class Article extends AppModel {
    public $actsAs = array('Search.Searchable', 'Mongodb.SqlCompatible');
    public $filterArgs = array(
        array('name' => 'filter', 'type' => 'query', 'method' => 'orConditions'),
    );

    public function orConditions($data = array()) {
        $filter = $data['filter'];
        $cond = array(
            'OR' => array(
                'title LIKE' => '%' . $filter . '%',
                'description LIKE' => '%' . $filter . '%',
            ));
        return $cond;
    }
}

これを使えば、Mongoの条件オペレータを一般的なSQLのものに変換してくれるので、超絶に楽になりました!!
(今までMongoDBの条件オペレータを手入力してましたよ…。)

資料読んでたのが readme と sample とかだけだったので、
てっきりこのあたりは対応していないのかと思ってたorz
ソースを確認する癖をつけないとダメですね!!

MongoDBプラグインとSearchプラグインであいまい検索できるようにした。

この記事はバッドノウハウです。以下の記事で正しい方法を書き直しました。

MongoDBプラグインを使う時には、behavior の SqlCompatible をご一緒にっ!
http://d.hatena.ne.jp/ihiro81/20111103/1320328426


普段検索フォームを作る時にCakeDCのSearchプラグインを使っているのですが、
MongoDBはSQLではない為そのままだとあいまい検索が使えません。

だいぶ無理やりですが、
app_model.phpを編集して対応させました。

<?php
class AppModel extends Model {
	function beforeFind($queryData) {
		foreach ($queryData as $key => $value) {
			if (is_array($value)) {
				foreach ($value as $k => $v) {
					if ($key == 'conditions' && preg_match('/(.*) LIKE$/i', $k)) {
						unset($queryData[$key][$k]);
						$k = preg_replace('/(.*) LIKE$/', '$1', $k);
						$v = preg_replace('/^%(.*)%$/', '$1', $v);
						$queryData[$key][$k] = new MongoRegex("/{$v}/i");
					}
				}
			}
		}
		return $queryData;
	}
}

単純にbeforeFindでクエリーを操作してるだけですが、
これで、MySQLとMongoDBを一緒に使っててもあいまい検索できますね!