2016/08/02

Jekyll始めました

やっぱり手軽に Markdown で書きたいよね。と思っていて、どこにするかと悩んでいたんだけど、技術blogなんだからローカルに原稿残っていたほうがgrepできていいよね。ネット繋がってなくても自分の過去ログ漁れるのはかなりの利点じゃないかな?と思ったのでJekyll+Github Pagesで頑張ることにしました。→ tail -f pinzo.log

2016/07/28

serialのシーケンスがなが~い名前になる場合

<>PostgreSQLの serial はシーケンスを生成するわけだけども、シーケンスの名前は table_name_column_name_seq という形で生成される。users テーブルの id ならば users_id_seqという感じで

しかし、シーケンス名は63文字までなので、なが~い名前のテーブルのなが~い名前の列を対象にした場合は63文字を超えてしまう場合も当然あって、そんな場合は切り詰められたものが名前になる。 例えば long_long_name_table_abcd_efgh_ijkl_mnop_qrst_uvwx_yz テーブルの long_long_name_column_abcd_efgh_ijkl_mnop_qrst_uvwx_yz を対象にした場合は sample.long_long_name_table_abcd_efg_long_long_name_column_abcd_ef_seq となる。

このシーケンス名を取得するには pg_catalog.pg_get_serial_sequence 関数を使えばいいのだが、まあごくたまにはDBにアクセスせずにこのシーケンス名を指定したい場合があって、その場合は内部ルールをトレースする必要がある。

おそらく内部仕様はこんな感じ

  1. テーブル名と列名の長さが合わせて58文字以下ならそのまま作成
  2. テーブル名と列名ともに30文字以上ならばどちらも29文字に切り詰めて作成
  3. どちらかが29文字未満のばあい、短い方はそのまま使用し残りの分(58-短い方の文字数)を長い方から切り出して作成
jsで書くならこんな感じ。 この辺の仕様を説明した公式ドキュメントが無いかなと探してみたけど見つからない。ソース読むしか無いのだろうか

ちなみにこの結果、既存のシーケンスと名前が被るとき(例えば長い名前で末尾がほんの少しだけ違うテーブルのid列をserialで作成しようとした場合)は素直にエラーになってくれるので、名前を付け直すか integer で作って、シーケンスを別途作成しましょう。

2016/06/03

golangのテンプレート内でメソッドを呼び出す

text/template のサンプルをいくつか見てみたところ、構造体を渡してフィールドを埋め込んで表示するサンプルばかりだった。じゃあメソッド呼べるの?と思ったので試してみた。

panic: template: test:1: unexpected "(" in operand

goroutine 1 [running]:
panic(0x13f660, 0xc82000a440)
 /usr/local/Cellar/go/1.6.2/libexec/src/runtime/panic.go:481 +0x3e6
main.main()
 /Users/pinzolo/.cache/junkfile/2016/06/2016-06-03-101728.go:20 +0x24b
exit status 2
できなかった。

括弧が unexpected なら、括弧なしならどうなの??

pinzolo(36) lives in Kyoto.
出来た。正直言って予想外だった。

じゃあ、引数はどうやって渡すの?lenとかのビルトイン関使うようにスペース開ければいいかな?

pinzolo(36) lives in Kyoto. 2 years later he is 38.
出来た。

じゃあ2つ以上の引数はスペース区切りかな?

pinzolo(36) lives in Kyoto. 5 years later he is 41.
やっぱり。

趣向を変えて、構造体のポインタを渡してもうまく動くかな?

pinzolo(36) lives in Kyoto. 2 years later he is 38.
問題ない。構造体に定義したメソッドもポインタに定義したメソッドも呼び出せてる。

結構色々出来るんだな text/template

2016/06/01

comment on exported function GetXxx should be of thee form "GetXxx ..."

Go は大文字で始まる public な型・関数に対してその直前に書かれたコメントをドキュメントとして扱ってくれる。
lintでドキュメント書くように勧めてくるのは意識高い感じでよい。
ただ、面倒なことにその型なり関数なりの名前で始まるのを推奨しているらしく、そうじゃない場合タイトルのような警告を出してくる。

こんな感じ

英語で書く場合なら、GetFoo returns Foo みたいに書きやすいのだが、日本語でコメントを書く場合冗長なコメントになりがちな気がする。
関数名も型名も真下にあるじゃないか!
なんて言っても仕方ないし、エディタにマークつけ続けられるのも気持ち悪いのでせっせとおっしゃる通りにするのだ

2016/05/31

golang 入門した

ちょっととあるコマンドラインツール作ろうと思って、また gem で作ろうかと思ったんだけど Windows でも動かせるようにしたいし、使う人に Ruby 入れさせるのもアレなので、golang で作ってみようと思った。

土日ぐらいで A Tour of Go. を始めとしていろんな入門エントリ読んで、とりあえず手始めに github.com/pinzolo/casee とかいうライブラリ作ってみた。

とりあえずテストは通るし、動いている。

golang にて文字列のn番目の文字を取得する

文字列 s に対して、s[0] は最初のバイトを返すので、一旦 rune スライスを作成しないと n 番目の文字は取得できない。

2016/04/07

Ubuntu 12.04 on さくらのVPS に GitBucket を入れたメモ

Tomcatのインストール

sudo aptitude install -y tomcat7 tomcat7-admin

Tomcatの設定

sudo vi /etc/tomcat7/server.xml
HTTPのConnector設定をコメントアウトし、AJPのConnector設定を有効にする。

GitBucketの配置

wget https://github.com/gitbucket/gitbucket/releases/download/3.13/gitbucket.war
sudo mv gitbucket.war /var/lib/tomcat7/webapps
ls -al /var/lib/tomcat7/webapps
drwxrwxr-x 4 tomcat7 tomcat7 4096 Apr 7 14:37 . drwxr-xr-x 6 root root 4096 Apr 7 14:24 .. drwxr-xr-x 7 tomcat7 tomcat7 4096 Apr 7 14:37 gitbucket -rw-r--r-- 1 root root 39783199 Apr 7 14:37 gitbucket.war drwxr-xr-x 3 root root 4096 Apr 7 14:25 ROOT

ApacheのGitBucket用設定ファイル

sudo vi /etc/apache2/sites-available/gitbucket.conf
<VirtualHost *:443> ServerName gitbucket.my-domain:443 <Location /> ProxyPass ajp://localhost:8009/ </Location> SSLCertificateFile /etc/apache2/cert/ssl-cert.pem SSLCertificateKeyFile /etc/apache2/cert/ssl-cert.key </VirtualHost>
sudo a2ensite gitbucket

再起動

sudo service tomcat7 restart
sudo service apache2 restart
しかし、アクセスしても 404 となる。
/var/log/tomcat7/localhost.2016-04-07.log を見ると、こんなエラーが出ている。
SEVERE: Error configuring application listener of class gitbucket.core.ssh.SshServerListener
java.lang.UnsupportedClassVersionError: gitbucket/core/ssh/SshServerListener : Unsupported major.minor version 52.0 (unable to load class gitbucket.core.ssh.SshServerListener)
        at org.apache.catalina.loader.WebappClassLoader.findClassInternal(WebappClassLoader.java:2948)
        at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:1208)
        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1688)
        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1569)
        at org.apache.catalina.core.DefaultInstanceManager.loadClass(DefaultInstanceManager.java:529)
        at org.apache.catalina.core.DefaultInstanceManager.loadClassMaybePrivileged(DefaultInstanceManager.java:511)
        at org.apache.catalina.core.DefaultInstanceManager.newInstance(DefaultInstanceManager.java:139)
        at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4888)
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5467)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
        at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
        at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
        at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:632)
        at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1073)
        at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1857)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
        at java.util.concurrent.FutureTask.run(FutureTask.java:262)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
        at java.lang.Thread.run(Thread.java:745)
https://github.com/gitbucket/gitbucket/issues/1082 によると、Java8を必要としているがJava8ではない場合に出るエラーのようだ。TomcatがJava8で動いていないっぽい。

JAVA_HOMEの設定

sudo vi /etc/default/tomcat7
JAVA_HOME=/usr/lib/jvm/java-8-oracleを追加して再起動。
しかしまだ表示されない。今度はこんなエラー。
SEVERE: Exception sending context initialized event to listener instance of class gitbucket.core.servlet.InitializeListener
java.sql.SQLException: Connections could not be acquired from the underlying database!
        at com.mchange.v2.sql.SqlUtils.toSQLException(SqlUtils.java:118)
        at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:692)
        at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:140)
        at scala.slick.jdbc.JdbcBackend$DatabaseFactoryDef$$anon$3.createConnection(JdbcBackend.scala:47)
        at scala.slick.jdbc.JdbcBackend$BaseSession.conn$lzycompute(JdbcBackend.scala:397)
        at scala.slick.jdbc.JdbcBackend$BaseSession.conn(JdbcBackend.scala:397)
        at scala.slick.jdbc.JdbcBackend$BaseSession.withTransaction(JdbcBackend.scala:420)
        at scala.slick.backend.DatabaseComponent$DatabaseDef$$anonfun$withTransaction$1.apply(DatabaseComponent.scala:54)
        at scala.slick.backend.DatabaseComponent$DatabaseDef$$anonfun$withTransaction$1.apply(DatabaseComponent.scala:54)
        at scala.slick.backend.DatabaseComponent$DatabaseDef$class.withSession(DatabaseComponent.scala:34)
        at scala.slick.jdbc.JdbcBackend$DatabaseFactoryDef$$anon$3.withSession(JdbcBackend.scala:46)
        at scala.slick.backend.DatabaseComponent$DatabaseDef$class.withTransaction(DatabaseComponent.scala:54)
        at scala.slick.jdbc.JdbcBackend$DatabaseFactoryDef$$anon$3.withTransaction(JdbcBackend.scala:46)
        at gitbucket.core.servlet.InitializeListener.contextInitialized(InitializeListener.scala:30)
        at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4973)
        at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5467)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
        at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
        at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
        at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:632)
        at org.apache.catalina.startup.HostConfig.deployWAR(HostConfig.java:1073)
        at org.apache.catalina.startup.HostConfig$DeployWar.run(HostConfig.java:1857)
        at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
        at java.util.concurrent.FutureTask.run(FutureTask.java:266)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)
Caused by: com.mchange.v2.resourcepool.CannotAcquireResourceException: A ResourcePool could not acquire a resource from its primary factory or source.
        at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable(BasicResourcePool.java:1469)
        at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:644)
        at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:554)
        at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutAndMarkConnectionInUse(C3P0PooledConnectionPool.java:758)
        at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:685)
        ... 25 more
Caused by: org.h2.jdbc.JdbcSQLException: Error while creating file "/usr/share/tomcat7/.gitbucket" [90062-190]
        at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
        at org.h2.message.DbException.get(DbException.java:179)
        at org.h2.message.DbException.get(DbException.java:155)
        at org.h2.store.fs.FilePathDisk.createDirectory(FilePathDisk.java:274)
        at org.h2.store.fs.FileUtils.createDirectory(FileUtils.java:42)
        at org.h2.store.fs.FileUtils.createDirectories(FileUtils.java:309)
        at org.h2.mvstore.db.MVTableEngine.init(MVTableEngine.java:73)
        at org.h2.engine.Database.getPageStore(Database.java:2450)
        at org.h2.engine.Database.open(Database.java:672)
        at org.h2.engine.Database.openDatabase(Database.java:269)
        at org.h2.engine.Database.(Database.java:263)
        at org.h2.engine.Engine.openSession(Engine.java:65)
        at org.h2.engine.Engine.openSession(Engine.java:175)
        at org.h2.engine.Engine.createSessionAndValidate(Engine.java:153)
        at org.h2.engine.Engine.createSession(Engine.java:136)
        at org.h2.engine.Engine.createSession(Engine.java:28)
        at org.h2.engine.SessionRemote.connectEmbeddedOrServer(SessionRemote.java:349)
        at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:107)
        at org.h2.jdbc.JdbcConnection.(JdbcConnection.java:91)
        at org.h2.Driver.connect(Driver.java:72)
        at com.mchange.v2.c3p0.DriverManagerDataSource.getConnection(DriverManagerDataSource.java:175)
        at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:220)
        at com.mchange.v2.c3p0.WrapperConnectionPoolDataSource.getPooledConnection(WrapperConnectionPoolDataSource.java:206)
        at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool$1PooledConnectionResourcePoolManager.acquireResource(C3P0PooledConnectionPool.java:203)
        at com.mchange.v2.resourcepool.BasicResourcePool.doAcquire(BasicResourcePool.java:1138)
        at com.mchange.v2.resourcepool.BasicResourcePool.doAcquireAndDecrementPendingAcquiresWithinLockOnSuccess(BasicResourcePool.java:1125)
        at com.mchange.v2.resourcepool.BasicResourcePool.access$700(BasicResourcePool.java:44)
        at com.mchange.v2.resourcepool.BasicResourcePool$ScatteredAcquireTask.run(BasicResourcePool.java:1870)
        at com.mchange.v2.async.ThreadPoolAsynchronousRunner$PoolThread.run(ThreadPoolAsynchronousRunner.java:696)
/usr/share/tomcat7/.gitbucket が作成できないらしい。パーミッションか
ls -al /usr/share/tomcat7
drwxr-xr-x 4 root root 4096 Apr 7 14:24 . drwxr-xr-x 155 root root 4096 Apr 7 14:25 .. drwxr-xr-x 2 root root 4096 Apr 7 14:24 bin -rw-r--r-- 1 root root 39 Feb 21 2014 defaults.md5sum -rw-r--r-- 1 root root 2030 Feb 21 2014 defaults.template drwxr-xr-x 2 root root 4096 Apr 7 14:24 lib -rw-r--r-- 1 root root 53 Feb 21 2014 logrotate.md5sum -rw-r--r-- 1 root root 118 Feb 21 2014 logrotate.template
rootになっとる。

/usr/share/tomcat7/.gitbuckeのパーミッション設定

sudo chown -R tomcat7:tomcat7 /usr/share/tomcat7
再度再起動したら表示はされたが assets が効いていない。

assets有効化

どうやら assets のパスは固定になっているらしい。今回は VirtualHost でやったため、/gitbucket が存在しないから /gitbucket/assets も当然見えないわけだ。
しかたないので /etc/apache2/sites-available/gitbucket.conf を書き換えた。
<VirtualHost *:443>
  ServerName gitbucket.my-domain:443
  ProxyReverseHost On
  RewriteEngine On
  ProxyPass /gitbucket ajp://localhost:8009/gitbucket/
  ProxyPassReverse /gitbucket ajp://localhost:8009/gitbucket/
  ProxyPass /gitbucket/assets ! # 静的ファイルはAapacheに処理させる
  Redirect / /gitbucket # ルートにアクセスしても大丈夫なように
  RewriteRule ^/gitbucket/(.*) ajp://localhost:8009/gitbucket/$1 [P,L,QSA]
  SSLCertificateFile    /etc/apache2/cert/ssl-cert.pem
  SSLCertificateKeyFile /etc/apache2/cert/ssl-cert.key
</VirtualHost>
RewriteRule がないとトップは表示されても、サインインできなかった。
もうちょっとスッキリした設定があるような気がするけど、まあとりあえず。

Ubuntu 12.04 on さくらのVPS に Jenkins を入れたメモ

Jenkinsのインストール

wget -q -O - http://pkg.jenkins-ci.org/debian/jenkins-ci.org.key | sudo apt-key add -
sudo sh -c 'echo "deb http://pkg.jenkins-ci.org/debian binary/" > /etc/apt/sources.list.d/jenkins.list'
sudo aptitude update
sudo aptitude install -y jenkins

Jenkinsの設定変更

sudo vi /etc/default/jenkins
HTTP_PORTを-1に、AJP_PORTを8008に(Tomcat導入予定のため)

Apacheの設定

sudo vi /etc/apache2/sites-available/jenkins.conf
<VirtualHost *:443> ServerName jenkins.my-domain:443 <Location /> ProxyPass ajp://localhost:8008/ </Location> SSLCertificateFile /etc/apache2/cert/ssl-cert.pem SSLCertificateKeyFile /etc/apache2/cert/ssl-cert.key </VirtualHost>
sudo a2ensite jenkins

再起動

sudo service jenkins restart
sudo service apache2 restart

とっても簡単だった

2016/04/06

git 2.0.0 → git 2.8.1

% git --version
git version 2.0.0
% cd /opt/src % wget https://www.kernel.org/pub/software/scm/git/git-2.8.1.tar.gz % tar xf git-2.8.1.tar.gz % cd git-2.8.1 % ./configure --prefix=/opt/git/git-2.8.1 --with-openssl --with-curl % make % make install % /opt/git/git-2.8.1/bin/git --version
git version 2.8.1
% ln -sfn /opt/git/git-2.8.1/bin/* /usr/bin % git --version
git version 2.8.1
とくに問題なし、前回と同じ。

2016/03/12

よくあるテーブルのヘッダ固定の1つ

業務アプリを作っているとテーブルのヘッダを固定して欲しいという話はよくある。よくありすぎる。

2つのテーブルを作って列の幅を固定して、body担当テーブルはdivで囲って overflow-y 程度でできればいいんだけど、IEはスクロールバーを出しっぱなしなのでずれる。

仕方なくheader担当にもスクロールバーをだしっぱなすとかまあ色々苦労する。

今回は、ページの上部にヘッダが来たらそこでheader担当を固定させてやればいいよね。ということでやってみた。

AngularJS の directive でやったけど、参考にした jQuery も備忘録的にメモっておいた

Bootstrapのpanelなんかの中にあった場合、固定した瞬間にはみ出て横幅がずれるので、body担当の横幅に合わせられるようにしている。

本当は thead に設定して1つのテーブルでやりたかったんだけどどうしても幅指定が出来なくて仕方なくテーブルを分けた。

横幅やスクロール距離、固定位置などパラメータは要件次第だけど結構手軽にできるもんだな

2016/03/08

お使いの起動ディスクは一杯です

作業中に「お使いの起動ディスクは一杯です」というダイアログが表示された。

この3つのコマンド走らせるだけで、30Gほど空きスペースを確保できた。

ちょっと放置し過ぎか

2016/02/25

JavaでPostgreSQLの?演算子を使用するために

PostgreSQL には ? という演算子があって、JSONB型に対して文字列の検索が行える。

-- true を返す
SELECT '["a", "b", "c"]' ? 'a';

-- 管理者検索
SELECT *
FROM users
WHERE roles ? 'ADMIN';
といった使い方ができる。

ところが、例えば DOMA のようなフレームワークやバックエンドでJDBCを使っているツールなどではエラーとなる。

これを避けるためには ? をエスケープしてやればよくて、

-- true を返す
SELECT '["a", "b", "c"]' ?? 'a';

-- 管理者検索
SELECT *
FROM users
WHERE roles ?? 'ADMIN';
としてやればよい。

2016/02/03

FactoryGirlを使用したDeviseのテストでLinuxとMacでのテスト結果が異なった話

tail -f pinzo.log: Deviseの有効期限設定をテストする で書いた有効期限検証のコードで不思議な事が起こった。Macでは正常にパスするのだが、Amazon Linux上ではパスしない。

ミニマムなコードで示すとこんな感じ

で、こんなデバッグコードを仕込んで実行してみた

Macで実行するとこんな感じ

[test-code][sent_at] to_s: 2016-02-01 23:51:31 +0900, to_i: 1454338291, usec: 197593, subsec: 197593/1000000
[test-code][added]   to_s: 2016-02-11 23:51:31 +0900, to_i: 1456930291, usec: 197593, subsec: 197593/1000000
[devise][now]     to_s: 2016-02-11 23:51:31 +0900, to_i: 1456930291, usec: 197593, subsec: 197593/1000000
[devise][sent_at] to_s: 2016-02-01 23:51:31 +0900, to_i: 1454338291, usec: 197593, subsec: 197593/1000000
[devise][added]   to_s: 2016-02-11 23:51:31 +0900, to_i: 1456930291, usec: 197593, subsec: 197593/1000000
[devise] now > added: false
[devise] now.subsec > added.subsec: false

Amazon Linuxで実行するとこんな感じ

[test-code][sent_at] to_s: 2016-02-01 23:53:20 +0900, to_i: 1454338400, usec: 369658, subsec: 369658841/1000000000
[test-code][added]   to_s: 2016-02-11 23:53:20 +0900, to_i: 1456930400, usec: 369658, subsec: 369658841/1000000000
[devise][now]     to_s: 2016-02-11 23:53:20 +0900, to_i: 1456930400, usec: 369658, subsec: 369658841/1000000000
[devise][sent_at] to_s: 2016-02-01 23:53:20 +0900, to_i: 1454338400, usec: 369658, subsec: 184829/500000
[devise][added]   to_s: 2016-02-11 23:53:20 +0900, to_i: 1456930400, usec: 369658, subsec: 184829/500000
[devise] now > added: true
[devise] now.subsec > added.subsec: true

usecまでは同じなのだが、subsec をみると Amazon Linux では、桁落ちが発生している。

ためしにそれぞれで Time.now.subsec としてみる。

# mac
$ ruby -e 'puts Time.now.subsec'
276987/1000000

# Amazon Linux
$ ruby -e 'puts Time.now.subsec'
704462593/1000000000

つまりこういうことが起こっている

  1. OSにより Time#subsec が返す精度が異なる
  2. FactoryGirlが create で返すのは、コードで指定された値を格納したオブジェクトであり、DBに登録後取得したものではないので confirmation_sent_at はそのままの精度である。つまり、スタブで突っ込んでいるのは元の精度
  3. DB(sqlite)に格納するとき、DBの型に合わせて精度の桁落ちが発生する
  4. 実際に処理されるときは、DBから取得したデータなので桁落ちが発生したデータになる
  5. DB格納前の元の精度の値と、DBから取得した桁落ちした値を比較しているため期限を超えたことになってしまう
  6. Mac上ではたまたま、Time#subsecの精度がsqliteの精度と同じだったため桁落ちが発生せず正常に動作する

これを避けるためには、スタブに突っ込む値をDBから取得したものにすれば良さそうだ。たとえばこんな感じ

ちなみに Mac でも、桁落ちを引き起こすことは可能で、こんな感じで usec を超えた範囲を指定すれば桁落ちさせられる。

あー疲れた、腹減った

2016/01/30

Deviseで複数モデルを利用した場合、ログアウトすると全てのスコープでログアウトされてしまう

管理者とユーザーでのログイン機能が存在し、別モデルで管理したい場合、Devise を使うとscoped_viewsをtrueにすれば簡単に実装できる。(参考:Ruby - Railsでdeviseひとつで複数モデルを管理しよう - Qiita

しかし、デフォルトの場合 Devise のログアウトは全てのスコープでログアウトしてしまう。つまり管理者とユーザーでログインしていた場合、管理者としてログアウトすると同時にユーザーとしてもログアウトしてしまう

これを防ぎたい場合、config/initializers/devise.rb にて config.sign_out_all_scopes = false としてやればよい。

初期状態ではこの設定はコメントアウトされているので、コメントアウトを外しfalseに設定するだけで、スコープごとのサインアウトが実装できる。

テストコード込みのサンプルコードはこんな感じ。

2016/01/18

Rails の TimeWithZone を JavaScript で読み込めるフォーマットで出力する

JavaScript で new Date(text)Date.parse(text) した時に読み込める形で TimeWithZone の値を出力したい。

そのまま to_s すると 2015-01-18 12:34:56 +0900 となるが、これでは Chrome でしか読み込めない。

2015/01/18 12:34:56 +0900 という形式ならば Chrome, IE, Firefox で読み込むことができた

もしくは、せっかく to_json メソッドがあるんだからこれを利用すると安心。しかし余計なダブルクォートは取り除く必要がある。あと、人が見た時は先述のやり方のほうがぱっと見で理解しやすいと思う

to_json 経由だとミリ秒まで含まれているから完全に等価ではないけど、そこらへんは要件しだいということで

まあ、色々やり方はあるので好きなやり方を使えばいいと思う。

2016/01/10

EC2(Amazon Linux)上のRailsアプリにQiita::Markdownをインストール

インストール

Qiita::Markdownはシンタックスハイライトに Pygments を使用しているのでインストールしておく(sudo pip install pygments)

そして、おもむろに Gemfile に gem 'qiita-markdown' として bundle install してみる。

Installing charlock_holmes 0.7.3 with native extensions

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

    /usr/bin/ruby2.2 -r ./siteconf20160110-30152-1igbivj.rb extconf.rb
checking for main() in -licui18n... no
which: no brew in (/home/ec2-user/bin:/home/ec2-user/.rbenv/bin:/home/ec2-user/bin:/home/ec2-user/.rbenv/bin:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbin:/opt/aws/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin:/opt/aws/bin:/opt/aws/bin:/home/ec2-user/.local/bin:/home/ec2-user/bin:/opt/aws/bin)
checking for main() in -licui18n... no


***************************************************************************************
*********** icu required (brew install icu4c or apt-get install libicu-dev) ***********
***************************************************************************************
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib64
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/usr/bin/$(RUBY_BASE_NAME)2.2
        --with-icu-dir
        --without-icu-dir
        --with-icu-include
        --without-icu-include=${icu-dir}/include
        --with-icu-lib
        --without-icu-lib=${icu-dir}/lib64
        --with-icui18nlib
        --without-icui18nlib
        --with-icui18nlib
        --without-icui18nlib

extconf failed, exit code 1

Gem files will remain installed in /home/ec2-user/rails_app/vendor/bundle/ruby/2.2/gems/charlock_holmes-0.7.3 for inspection.
Results logged to /home/ec2-user/rails_app/vendor/bundle/ruby/2.2/extensions/x86_64-linux/2.2/charlock_holmes-0.7.3/gem_make.out
Installing rugged 0.24.0b11 with native extensions

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

    /usr/bin/ruby2.2 -r ./siteconf20160110-30152-1y0cp2j.rb extconf.rb
checking for gmake... yes
checking for cmake... no
ERROR: CMake is required to build Rugged.
*** extconf.rb failed ***
Could not create Makefile due to some reason, probably lack of necessary
libraries and/or headers.  Check the mkmf.log file for more details.  You may
need configuration options.

Provided configuration options:
        --with-opt-dir
        --without-opt-dir
        --with-opt-include
        --without-opt-include=${opt-dir}/include
        --with-opt-lib
        --without-opt-lib=${opt-dir}/lib64
        --with-make-prog
        --without-make-prog
        --srcdir=.
        --curdir
        --ruby=/usr/bin/$(RUBY_BASE_NAME)2.2
        --use-system-libraries

extconf failed, exit code 1

Gem files will remain installed in /home/ec2-user/rails_app/vendor/bundle/ruby/2.2/gems/rugged-0.24.0b11 for inspection.
Results logged to /home/ec2-user/rails_app/vendor/bundle/ruby/2.2/extensions/x86_64-linux/2.2/rugged-0.24.0b11/gem_make.out

とまあ、2つのエラーが出た。Amazon Linuxは yum だからlibicu-dev じゃなくて libicu-devel にすればOKかな?

sudo yum install -y libicu-devel cmake して bundle install したらインストールは成功したようだ

ハイライト

インストールは成功したのでこんなコードを書いて使えば Markdown に変換された。ばっちりばっちり

module ApplicationHelper
  def markdown(source)
    Qiita::Markdown::Processor.new.call(source)[:output].to_s.html_safe
  end
end

変換はばっちりだがシンタックスハイライトがされていない。いや、正確にはスタイルシートがない。
richleland/pygments-css あたりでお好みのものを利用させてもらおう

絵文字

シンタックスハイライトも完了した。次は絵文字である。
gemoji | RubyGems.org | your community gem host がインストールされているはずなので、Rakefile に下記の行を加える。

load 'tasks/emoji.rake'
あとは bundle exec rake emoji を実行すれば /public/images/emoji 以下に画像はファイルが配置される。
これだけで絵文字が表示できるようになる。bundlerake で導入できるので /public/images/emoji.gitignore に加えておいてもいいかもしれない。大量に画像ファイルをリポジトリに登録したくないもんね

2016/01/09

オブジェクトが存在するときにだけ処理をする

みんな大好き Object#tap

でもこんなソースはあまり書きたくない。

foo.bar.baz.tap do |baz|
  do_something(baz) if baz
end

tap_if とかあれば便利じゃない?

foo.bar.baz.tap_if do |baz|
  do_something(baz)
end

# もしくは
foo.bar.tap_if(:baz) do |baz|
  do_something(baz)
end

似たようなことを考える人はいるものですでにある。→ tap-if | RubyGems.org | your community gem host

そして Rails やる上では try で事足りることがわかった。ブロック取れるのか。

foo.bar.baz.try do |baz|
  do_something(baz)
end

# もしくは
foo.bar.try(:baz) do |baz|
  do_something(baz)
end

結論:Object#try サイコー