kaminariで1ページ内で複数のpaginationを使う方法

現状、あるモデルからのデータは1ページ、1paginationで使用していた
なんと、800件をも超えるデータをページングしていたのだ(ページ数は600件を超えていた)
この仕様に不満を持った何者かのダークパワーで1ページに複数のロジック(例えば、一つ目は日付順、二つ目は更新日順)などを分けるようにと仕事が回ってきてしまった

僕はタブで分けて、そして流石のkaminari、その機能は標準でついていた
当初のコントローラーはこのようになっていた

1
2
def set_hoge
@hoge = Hoge.where(fuga: 1).page(params[:page])

次に僕が書いたのはこんな感じ

1
2
3
4
5
def set_hoge
# 日付順
@hoge1 = Hoge.where(fuga: 1).page(params[:page])
#更新日順
@hoge2 = Hoge.where(fuga: 2).page(params[:page])

ただ、このままだとクエリが共有されるので、上手くいかない
最終的に次のようになった

1
2
3
4
def set_hoge
# 日付順
@hoge1 = Hoge.where(fuga: 1).page(params[:page_1])
@hoge2 = Hoge.where(fuga: 2).page(params[:page_2])

index.html.haml

1
2
.hoge= paginate @hoge1, :param_name => 'page_1'
.hoge= paginate @hoge2, :param_name => 'page_2'

このparam_nameを使ってやると、クエリは共有されなくなる
具体的には、kaminariはつぎのようなクエリを発行する

1
localhst:3000/hoge?page_1=2&page_2=3

しかし、kaminariってほとんど何も考えなくてページングできるから便利ね

haskellの関数構文

haskellの関数構文メモ
パターンマッチ 引数を定数で場合分けできる

1
2
3
4
yourName :: String -> String
yourName "poo" = "Hi, poo"
yourName "boo" = "Hi, boo"
yourName x = "invalid name"

ガード 引数を範囲で場合分けできる

1
2
3
4
5
6
yourMemory :: Int -> Int -> String
yourMemory val1 val2
| val1+val2 < 4 = "low spec"
| val1+val2 < 8 = "common spec"
| val1+val2 < 16 = "high spec"
| otherwise = "invalid value"

他の書き方

1
2
3
4
5
6
7
yourMemory' :: Int -> Int -> String
yourMemory' val1 val2
| memory < 4 = "low spec"
| memory < 8 = "common spec"
| memory < 16 = "high spec"
| otherwise = "invalid value"
where memory = val1 + val2

1
2
3
4
5
6
7
8
yourMemory'' :: Int -> Int -> String
yourMemory'' val1 val2
| memory < low = "low spec"
| memory < com = "common spec"
| memory < high = "hight spec"
| otherwise = "invalid value"
where memory = val1 + val2
(low, com, high) = (4,8,16)

whereの中に関数をかける

1
2
3
sqArea :: [(Double, Double)] -> [Double]
sqArea s = [cal w h | (w, h) <- s]
where cal weight height = weight * height

let式はin以降が値

1
2
3
4
cylinder r h =
let sideArea = 2 * pi * r * h
topArea = pi * r ^ 2
in sideArea + 2 * topArea

また、letはローカルスコープに関数を作る時に使える

1
[let listManage x = [y * x | y <- [1..10]] in (listManage 1, listManage 2, listManage 3)]

mysqlにログインできなかった問題が解決した話

エラーその1
mysql -uroot したときにログインできないよエラー

1
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2)

こんな表示がされる
これは僕の環境だと単純に/tmp/ディレクトリにmysql.sockがなかったため。
なので、touchコマンドで作ってやった

で、mysql.server restrt しようとすると今度は…
エラーその2

1
2
3
4
5
6
touch: /usr/local/var/mysql/hogehoge.local.pid.shutdown: Permission denied
cat: /usr/local/var/mysql/hogehoge.local.pid: Permission denied
ERROR! MySQL server process # is not running!
override rw-r----- _mysql/_mysql for /usr/local/var/mysql/hogehoge.local.pid?
Starting MySQL
SUCCESS!

日本語でおk
なんかmysql走ってるぽい表示されてるけど、プロセス自体はない
このエラーはmysqlがファイル作りたいけど権限がなくて作れないよ〜ウエーンというエラー(たぶん)
なので、/usr/loca/var をls -llでみてみる

1
2
3
4
5
6
7
drwxr-xr-x   3 hogehoge    admin    102  6 19  2015 hoge
drwxr-xr-x 3 hogehoge admin 102 8 29 2015 fuga
drwxr-xr-x 3 hogehoge admin 102 8 13 2015 piyo
drwxr-xr-x 9 hogehoge admin 306 9 23 2015 poko
drwxr-xr-x 30 _mysql _mysql 1020 10 26 19:16 mysql
drwx------ 25 hogehoge admin 850 9 13 22:39 puka
drwxr-xr-x 2 hogehoge admin 68 8 29 2015 poka

なるほど、確かにmysqlディレクトリには書き込み権限自体はあるが、所有者が_mysqlになってて所有者自体に権限がなさそうだ
じゃあ、mysqlディレクトリを自分のユーザ名に変えてしまおう

1
chown -R your_username mysql

このコマンドで、mysqlディレクトリの所有者を再帰的にyour_usernameにしたところ、うまくログインできるようになった

CSSで一行の中で左寄せと右寄せを使いたい時

webページのデザインで、一行中にある文字列は左寄せ、ある文字列は右寄せにしたい時ってありますよね。少しはまったのでメモとして残しておきます。

inline.html

1
2
3
4
<div class="box">
<div class="left-content">Hello</div>
<div class="right-content">World</div>
</div>

inline.css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.box {
width: 100%;
background-color: yellow;
display: table;
}

.left-content {
width: 30%;
display: table-cell;
}

.right-content {
width: 70%;
display: table-cell;
text-align : right;
}

はい。これで一行中に左寄せでHello,右寄せでworldと表示されます。
親boxはwidth100%を持ち、子は横並びの部分をtable-cellで実現します。
また、子にはwidthを100%になるよう設定してあるので、一行中に収まります。
ただ、例えばHelloの部分が30%を超えるような文字数だった場合、表示が崩れるかもしれません。

Rubyでのハッシュからjson文字列に変換する方法

個人的にこんがらがったので、メモ。
Rubyでは、調べた限りだとハッシュからjsonへの変換する方法が3コ見つかりました。
どちらも標準のjsonモジュールをインポートします。

1
require 'json'

この記事では、それら三つの変換方法の違いを述べていきます。
また、記述に間違いなどありましたら申し訳ありません。

(1) generateメソッドを使う場合
generateメソッドの第一引数にオブジェクトまたは配列を渡すことで、jsonに変換します。
:例

1
2
3
hoge = {'age'=>18, 'tall'=>150}
JSON.generate(hoge)
=> "{\"age\":18,\"tall\":150}"

ここでのポイントは
第一引数にオブジェクトまたは配列を渡すこと
がgenerateメソッドの条件です。つまり、オブジェクト又は配列以外を渡すと変換に失敗します。

1
2
3
4
5
6
str = "abcde"
JSON.generate(str)
=>JSON::GeneratorError: only generation of JSON objects or arrays allowed
num = 1000
JSON.generate(num)
=>JSON::GeneratorError: only generation of JSON objects or arrays allowed

(2) to_jsonメソッドを使う場合
generateメソッドと比較して、to_jsonメソッドはあらゆるクラスに対して寛容です。

1
2
3
4
5
6
7
8
9
hoge = {'age'=>18, 'tall'=>150}
hoge.to_json
=> "{\"age\":18,\"tall\":150}"
str = "abcde"
str.to_json
=> "\"abcde\""
num = 1000
num.to_json
=> "1000"

これがgenerateメソッドとの大きな違いになります。

(3) dumpメソッドを使う場合
(このメソッドの解説は正確さに欠けるかもしれません)
dumpメソッドはこれまでの例で示してきたすべての文字列や配列をjsonに変換してくれます。
ただ、dumpメソッドはマーシャルやヤムル(いずれもデータフォーマット)のload/dump実行の一部です。
ゆえに、上のメソッドと違い、ファイルに変換したjson文字列を書き込むことができます。

(4) まとめ
とりあえずjson文字列に変換したい場合、どのメソッドを使ってもよさそう。ファイルに書き込みとかするなら、dumpメソッドがよさそう。

今回の開発で学んだこと

オープンキャンパスでweb上でくじをひけるサービスを作成しました。
herokuへデプロイしたとき、いくつかエラーが出たので、学んだことをまとめます。  

herokuの有用コマンド

アプリを再起動する

1
heroku restart —app appname

ログを後ろから見る

1
heroku logs -t

アプリ名を調べる

1
heroku list

heroku上でrailsのコンソールを実行

1
heroku run rails c

これのなにが便利かというと、デプロイした際の”App crashed”エラーの詳細を見れること。

デプロイ後のトラブル

RailsにはアセットパイプラインというJavaScriptやCSSを結合したり圧縮したりして、閲覧時の高速化を目指す仕組みがあります。この仕組みを利用するには以下の2種類の方法があります。

  1. production環境でパイプラインを利用できるよう、プリコンパイルを実行する

    1
    rake assets:precompile RAILS_ENV=production
  2. デプロイ時に自動でプリコンパイルするよう設定を変更する
    config/environments/production.rbを以下のように変更する

    1
    config.assets.compile = true

今回、僕に徹夜を強いたエラーは”rake abort! ~ Nil class ~”あんまり覚えてませんが確かこんなエラーがプリコンパイル時におきました。
原因としてはapplication.scssとfugafuga.scssで同じcssファイルを読み込んでることでした。application.scssの記述を削除すれば解決しました。

Chromeの過去のコンソールを表示

検証のpreserve logにチェックをつければ、ページ遷移後でも前のエラーを確認できる。 

railsでのデータベースカラム追加/削除 

Railsでデータベースカラムの追加/削除を行います。

カラムの追加

1
2
3
rails generate migration Addカラム名Toテーブル名 カラム名:型名
<例>
rails generate migration AddTagToPosts tag:string

カラムの削除

1
2
3
rails generate migration Removeカラム名Fromテーブル名 カラム名:型名
<例>
rails generate migration RemoveTagFromPosts tag:string

マイグレーションの適用

1
rake db:migrate

実際に確認する on sqlite3

1
2
3
cd ~/yourdb
sqlite3 ~.db
.schema <table_name>

Scalaはじめました

Scalaいいですね。フィボナッチ数列を書いてみたら綺麗にかけて感動しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 object fib {
def fib(n : Int) : Int = {
def loop(n :Int) : Int = {
if (n == 0) 0
else if (n == 1) 1
else fib(n-2) + fib(n-1)
}

loop(n)
}

def main(args: Array[String]): Unit =
println(fib(9))
}

末尾再帰によって実装しました。すこし前にCで実装したコードはなかなかゴチャゴチャしてたのですが、Scalaだとシンプルにまとまったように思えます。さすがに速度には劣りますが。

Electronでsqlite3を使おうとしたら色々大変だった

今年も夏めく季節がやって参りました。
年をとると時が過ぎるのも早いもので、もう蝉の声すら聞こえてきそうです。

さて、Electron(0.33.6)でsqlite3を使用しようと思ったところ、盛大にはまったので備忘録を残しておきます。
調べてみると幾つか記事が見つかります。

http://verysimple.com/2015/05/30/using-node_sqlite3-with-electron/
http://qiita.com/shghdyji/items/20d2a913e3e492726c55

原因としては

electron で探しに行っているsqlite3のpathと実際にinstallしているsqliteのパスが異なっているために起こる。

らしい。解決作としては

1
2
3
4
cd node_modules/sqlite3
npm run prepublish
node-gyp configure --module_name=node_sqlite3 --module_path=../lib/binding/node-v47-darwin-x64
node-gyp rebuild --target=0.36.1 --arch=x64 --target_platform=darwin --dist-url=https://atom.io/download/atom-shell --module_name=node_sqlite3 --module_path=../lib/binding/node-v47-darwin-x64

を実行するといいらしいが…。
prepublishはpackage.jsonのnpm-scriptsを実行するみたいだ。

….が、まさかの1行目からエラーを吐かれた。
すでに精神がボロボロだったが、色々いじってみた結果以下のコードでうまくった

まずグローバル環境にnode-gypをインストールする。
npm install -g node-gyp

このモジュールはNode.jsのモジュールをコンパイルしてくれるモジュールで、今回はこいつのrebuildを使ってやる。なお、Python2.7系に依存しているかもしれない。
それで、node_modules/sqlite3に移動し、以下を実行

1
node-gyp rebuild --target=<your electron version> --arch=x64 --target_platform=darwin --dist-url=https://atom.io/download/atom-shell --module_name=node_sqlite3 --module_path=../lib/binding/electron-v<your electron version>-darwin-x64

実行後いろいろwarningがでるがなんとか使えるようになった。
個人で使うものだしまぁいいかなということでwarningは放置してます。

2016-6-15追記
warning出たまま使ってみたらsql文は実行できるんだけどデータベースの挙動が不自然。ここにきて再度エラーを解決しようと頑張ったのでメモ。
まずNodeとnpmとelectronのバージョンを最新にアップデートした。

1
2
3
4
5
cd node_modules/sqlite3
npm install nan@2.3.3
npm run prepublish
node-gyp configure --module_name=node_sqlite3 --module_path=../lib/binding/node-(your version)-darwin-x64
node-gyp rebuild --target=<update Electron version> --arch=x64 --target_platform=darwin --dist-url=https://atom.io/download/atom-shell --module_name=node_sqlite3 --module_path=../lib/binding/node-(your version)-darwin-x64

warningが出るがスルーしてsqlite3/lib/に移動し、

1
mv node-v48-darwin-x64 electron-v1.2-darwin-x64

sqlite3のモジュールにnan@2.3.3を突っ込んだ。
これで一応現在はうまく動いている。

jqueryのeachは個人的に使いにくい

jqueryのeachのreturnが使いにくかった話。

具体的にはeachの中でreturnすると制御が関数に移るのではなくただのbreakになってしまう。
例えば以下のようなコード
:HTML

1
2
3
4
5
<input type="text">
<input type="text">
<input type="text">
<input type="text">
<button>go</button>

:JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$(function(){
var fuga = function(){
$('input').each(function(){
if(!$(this).val().match(/\S/g)){
console.log("foo");
return false;
console.log('bar');
}
//return false;
console.log('poo');
})
console.log('puu')
return true;
}

$('button').on('click', function(){
console.log(fuga());
})
})

このコードでinput値に空白があった場合にfuga()の戻り値をfalseにしたかったが、現実ではeachの中でreturnするとbreak文となってしまい、このコードでは常にtrueが返る。この問題を解決するために、書き直したコードが下にある。

:JS

1
2
3
4
5
6
7
8
9
10
11
var fuga = function(){
var elem = document.getElementsByTagName('input');
for(var i=0, num=elem.length;i<num;i++){
if(!elem[i].value.match(/\S/g)){
console.log('foo')
return false;
}
console.log('bar')
}
return true;
}