ホーム ブログ ページ 40

Docker を試してみる(2)

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Docker を試してみる(1) の続きです。

超基本ですが重要な run & exec コマンドの説明です。

 runコマンド

dockerの中で一番大事なコマンドです。コンテナを起動しコマンドを実行します。

オプションが非常にたくさんありますがリソースの設定が多いので覚えるのは少しだけ。

–nameコンテナの名前を指定する
-it標準入力を開き仮想端末を割り当てる。シェルを使うときに使う
-dバックグラウンドでコンテナを実行する。サーバとして使うときなど
-e環境変数を指定する
-pポートフォワーディングする。ホスト->コンテナ
–rm実行が終わったら コンテナを削除する
-vボリュームを割り当てる。ホストのディスクをコンテナで使いたいときに使う

実行例)

コンテナ上でシェルを実行する

対話型シェルを起動します。# のプロンプトはシェルに接続した状態です。

exitでコンテナと切断し、コンテナは終了します。終了後コンテナは削除されます。

$ docker run -it --rm ubuntu bash
root@92e455bcb613:/# 

なお、ubuntuコンテナは、コマンド省略時はbashを起動するようになっているので、

bashをつけなくても結果は同じです。

$ docker run -it --rm ubuntu
root@92e455bcb613:/# 

コマンドのみ実行する

プロセスを表示させてみます。psコマンドが実行されコンテナは終了します。終了後コンテナは削除されます。

$ docker run --rm ubuntu ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  25968  1236 ?        Rs   09:27   0:00 ps aux

サーバとして実行する

memcachedサーバとして起動します。DockerHubのmemcachedイメージを使います。

次のコマンドでは memcached というコンテナ名でサーバを起動します。

16進数のIDを出力するのみでコマンドは終了します。

$ docker run -d --name memcached memcached
b40a0f915d7ade1b6ad91c7cc9b2257da1013c21ff61821cc1a59d597b9f734b

docker ps コマンドで実行状況を確認すると実行されていることが確認できます。

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED              STATUS              PORTS               NAMES
a40a0f915d7a        memcached           "/entrypoint.sh memca"   About a minute ago   Up 59 seconds       11211/tcp           memcached

コンテナを停止するには stop コマンドを使います。さらにコンテナを削除する場合は rmコマンドを使います。

$ docker stop memcached
$ docker rm memcached 

ポートフォワーディングする

ホストのアプリからmemcachedを使いたい、別のホストから接続したいときはポートフォワーディングを行います。

次のコマンドは ホストの11212ポートからコンテナの11211(memcachedのポート)に転送します。

$ docker run -d --name memcached -p 11212:11211 memcached
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                      NAMES
ec0da0dc6aba        memcached           "/entrypoint.sh memca"   5 seconds ago       Up 4 seconds        0.0.0.0:11212->11211/tcp   memcached

上記の出力の、

0.0.0.0:11212->11211/tcp 

任意の接続元からホストの11212ポートへのアクセスを許可し、コンテナの11211ポートへの転送を許可します。

ホストから memcached に接続してみます。

$ telnet localhost 11212
set foo 0 60 3
bar
STORED
get foo
VALUE foo 0 3
bar
END

 execコマンド

runコマンドと同様にコンテナ上でコマンドを実行しますが、

こちらは起動中のコンテナに対してのみ実行可能です。

$ docker exec memcached ps axu
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
memcache     1  0.0  0.2 313740  2228 ?        Ssl  09:43   0:00 memcached
memcache    14  0.0  0.1  17492  1136 ?        Rs+  09:54   0:00 ps axu

migrationファイルの記法メモ

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Ruby on Rails でDBのテーブル作成・カラム編集などに使う

migrationファイル。ファイルを作っておけば「rake db:migrate」 のコマンドで色々な対応が出来ますが、

それぞれ書き方があり、今までにすこし引っかかったものを中心にまとめてみました。

 change と up/down

changeとup/downはどちらもmigration実行時・ロールバック時の処理を記述するメソッドです。

changeの場合

class AddNameToPlayers < ActiveRecord::Migration
  def change
    add_column :players, :name, :string
  end
end  

up/downの場合

class AddNameToPlayers < ActiveRecord::Migration
  def up
    add_column :players, :name, :string
  end

  def down
    remove_column :players, :name
  end
end  

up/downはそれぞれ実行時・ロールバック時の操作をかく必要がありますが、

changeは実行時だけの操作を書くと、ロールバック時に処理を反転させて実施してくれます。とっても便利です。

なので、全部changeで書いていた所、ある時エラーになりました。

class RemoveNameToPlayers < ActiveRecord::Migration
  def change
    remove_column :players, :name    <- ロールバックできない
  end
end  

カラムのremove処理などは、changeだけだと、カラムの型情報等が含まれないため、反転出来ないのが原因でした。

removeのmigrationの時はup/downを使うようにしましょう。

 定型外の型

マイグレーションで使えるデータ型には以下のようなものが用意されています。

integer
float
string
text
date
time
boolean
...  

ただある時、integer型のカラムをBIGINT型に変更したい、となったとき、

bigintはそのままmigrationファイルに記述してもダメでした。

  def change
  	change_column :bank, :money, "BIGINT UNSIGNED"
  end 

 カラム位置指定

普通にmigrationファイルでカラムを増やすと、どんどん最後尾にカラムが足されて行きます。

その際、「このカラムは、カラムAとカラムBの間に入れたい!」となった時。

afterを使いました。

  def change
    add_column :test_table, :insert_column, :string, after: "column_a"
  end

このようなmigrationファイルにすると、column_aの後にinsert_columnが追加されます。

おまけ コメント

DBが増えてくると、このカラムは何のカラムだったか、と分からなくなってきます。

migrationファイルから、コメントを付けておく事が出来ます。

  def change
    add_column :test_table, :encrypted_password, :string, comment:"パスワード"
    add_column :test_table, :name, :string, comment:"ログインアカウントネーム"
  end

このようなmigrationファイルを作り、DB構造を見てみると、

各カラムにコメントが入ります。

MySQL DATETIME型のミリ秒の扱いについて

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

MySQL DATETIME型のミリ秒の扱いについて

MySQLのDATETIME型のカラムに対して、ミリ秒付きデータを書き込む際の動作について、

バージョンによって挙動が異なるので注意が必要です。

MySQL 5.5 までは、強制的に切り捨てされて格納、MySQL 5.6 以降は四捨五入されて格納されるという動作になっています。

実行例

 [MySQL 5.5]

テーブル

mysql> show columns from hoges;
+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int(11)      | NO   | PRI | NULL    | auto_increment |
| data  | varchar(255) | YES  |     | NULL    |                |
| time  | datetime     | YES  |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+

書き込むデータ

mysql> SET @time1 = "2016-04-28 12:00:00.100";
mysql> SET @time2 = "2016-04-28 12:00:00.499";
mysql> SET @time3 = "2016-04-28 12:00:00.500";

mysql> SELECT @time1, @time2, @time3;
+-------------------------+-------------------------+-------------------------+
| @time1                  | @time2                  | @time3                  |
+-------------------------+-------------------------+-------------------------+
| 2016-04-28 12:00:00.100 | 2016-04-28 12:00:00.499 | 2016-04-28 12:00:00.500 |
+-------------------------+-------------------------+-------------------------+
1 row in set (0.00 sec)

データ格納結果

mysql> INSERT INTO hoges (data, time) VALUES (@time1, @time1);
mysql> INSERT INTO hoges (data, time) VALUES (@time2, @time2);
mysql> INSERT INTO hoges (data, time) VALUES (@time3, @time3);

mysql> select * from hoges;
+----+-------------------------+---------------------+
| id | data                    | time                |
+----+-------------------------+---------------------+
|  3 | 2016-04-28 12:00:00.100 | 2016-04-28 12:00:00 |
|  4 | 2016-04-28 12:00:00.499 | 2016-04-28 12:00:00 |
|  5 | 2016-04-28 12:00:00.500 | 2016-04-28 12:00:00 |
+----+-------------------------+---------------------+

 [MySQL 5.7]

テーブル

mysql> show columns from hoge;
+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int(11)      | NO   | MUL | NULL    | auto_increment |
| data  | varchar(255) | YES  |     | NULL    |                |
| time  | datetime     | YES  |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

書き込むデータ

mysql> SET @time1 = "2016-04-28 12:00:00.100";
mysql> SET @time2 = "2016-04-28 12:00:00.499";
mysql> SET @time3 = "2016-04-28 12:00:00.500";

mysql> SELECT @time1, @time2, @time3;
+-------------------------+-------------------------+-------------------------+
| @time1                  | @time2                  | @time3                  |
+-------------------------+-------------------------+-------------------------+
| 2016-04-28 12:00:00.100 | 2016-04-28 12:00:00.499 | 2016-04-28 12:00:00.500 |
+-------------------------+-------------------------+-------------------------+
1 row in set (0.00 sec)

データ格納結果

mysql> INSERT INTO hoge (data, time) VALUES (@time1, @time1);
mysql> INSERT INTO hoge (data, time) VALUES (@time2, @time2);
mysql> INSERT INTO hoge (data, time) VALUES (@time3, @time3);

mysql> select * from hoge;
+----+-------------------------+---------------------+
| id | data                    | time                |
+----+-------------------------+---------------------+
|  1 | 2016-04-28 12:00:00.100 | 2016-04-28 12:00:00 |
|  2 | 2016-04-28 12:00:00.499 | 2016-04-28 12:00:00 |
|  3 | 2016-04-28 12:00:00.500 | 2016-04-28 12:00:01 |
+----+-------------------------+---------------------+
3 rows in set (0.00 sec)

上記のように、同じデータを書き込んだ場合でも、結果が異なったものになっている。

 おまけ

Railsでの動作について

RailsのActiveRecord経由で記録した場合、

Rails ~4.1だと、ミリ秒は切り捨てされて、

Rails 4.2~ だと、ミリ秒も含めてSQL文が作られる。

VirtualBox のHDDサイズ変更

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

VirtualBox のHDDサイズを変更する方法を備忘の為に記載

 環境

ホストOS: Windows 7

ゲストOS: CentOS 6

 手順

仮想マシンが起動している場合はシャットダウンする。
管理者としてコマンドプロンプトを開く。
対象のHDDのUUIDを確認しておく。
cd "c:\Program Files\Oracle\VirtualBox"
VBoxManage -nologo list hdds
サイズを変更する
VBoxManage modifyhd (UUID) --resize (サイズ(MB))

以下は50GBに変更する場合の例

VBoxManage modifyhd f7689c77-bccd-44fa-9304-3f1637aa00c2 --resize 51200

tortoiseSVNで、あるリビジョンとあるリビジョンの差分ファイルをエクスポートする

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

たまごです。

SVN上のあるリビジョンとあるリビジョンの差分ファイルを抽出したい時って、たまにありますよね。その際、自分がよく使う方法を解説したいと思います。

 tortoiseSVNでリビジョン間の差分ファイルを抽出する方法

使うのは、tortoiseSVNです。

  1. 対象ディレクトリで、show logする
  2. 任意のリビジョンを二つ選択し、Compare revisionsする
  3. ファイルを全選択し、右クリックでExport selections to…する

これでOKです。

削除ファイルがいらない場合は、手順の3で全選択せず、Actionでソートしたうえで、Deleted以外を選択するようにしましょう。

 上記方法で失敗する場合

SVNが貧弱だと、3のExport selections to…した際にエラーが出たりします。

そういう時は、仕方ないので、ファイルを選択した状態でCtr+cします。

これで表示されているパスがコピーされるので、後はコマンドプロンプト等使って、地道にコピーします。

(パスは表示されているとおりにコピーされるので、長いパスが省略されないように、なるべくFileカラムの横幅を広くして、コピーしてあげるとよいかんじです)

【JMeter】HTTPリクエストでPATCHメソッドを使用する

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Apache JMeterでPATCHメソッドを使用する方法

 概要

Apache JMeterでPATCHメソッドを使用したときに、色々うまくいかなかったので、試したことを記録しておきます。

JMeterの基本的な使い方については割愛します。

【Version】

Apache JMeter Version 2.13

 はじめに

JMeterでは、HTTPプロキシサーバを使用すると、ブラウザの操作を記録することができます。

その際、PATCHメソッドを使用している画面の操作で、エラーが発生しました。

記録されたJMeterのHTTPリクエストサンプラーを見てみると、メソッドがPATCHになっており、Parametersにリクエストで送るパラメータが表示されています。

 解析

一見問題なさそうですが、このHTTPリクエストを実行して、リスナー -> 結果をツリーで表示の「リクエスト」タブを見てみると、

PATCH data:

Content-Length: 0

となっており、リクエストボディが送信されていないようです。

ちなみに、メソッドをPOSTに変更して実行すると、リクエストで送るパラメータに設定した値が送信されていることが確認できます。

 解決方法

Parametersタブの隣にあるBody Dataタブを押すと、Warningメッセージが出ます。ここで、リクエストで送るパラメータをすべて削除するとBody Dataタブに切り替えることができます。

ここに、リクエストボディの値をそのまま貼り付けます。

(筆者はChromeの検証ツールからForm Dataをコピーしました。エンコードされた値を貼り付ける必要があるので、view sourceからコピー。)

 実行

再度実行してみます。

リスナー -> 結果をツリーで表示の「リクエスト」タブを見てみると、

PATCH data:

XXXXXXX%5AAAAAAAAAAAAAA%5D=true

Content-Length: 31

リクエストボディが正常に送信できました。

結果も正常終了になっています。

 余談

本題とは直接関係ありませんが、このとき動かしていたWebアプリはRailsアプリで、Ajaxでフォームの内容を送信していたので、「X-CSRF-Token」をリクエストヘッダーに設定する必要もありました。

以上です。

PUTメソッドも同様の動きをするようなので、同じ方法で解決できます。

CVE-2016-0800のOpenSSL脆弱性「DROWN」について、SSLv2が無効かどうかすぐに確認する方法

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

こんにちは。アピリッツの本多です。

2016年03月01日にOpenSSLの新たな脆弱性が発表されました。

SSLv2が有効な環境ではTLSセッションの暗号化が破られる攻撃を受ける可能性があるというものです。

詳しくは以下の資料を参照ください。

DROWN: Breaking TLS using SSLv2

https://drownattack.com/drown-attack-paper.pdf

その他重要度の低い修正も適用されているため、opensslの更新は行ったほうが良いと思いますが、取り急ぎ、SSLv2が現在有効なのかを確かめたいと思います。

今回の脆弱性の情報と合わせて、各ドメインの脆弱性をチェックするためのサイトも公開されました。

DROWN Attack

しかしながら、サブドメインごとにシステム(ないしSSL証明書)が異なる場合にチェックが上手く出来なかったため、centos上でコマンドを叩いて確認を取りたいと思います。

コンソール上で以下のコマンドを入力します

$ openssl s_client -connect (ドメイン名):443 -quiet # プロトコルを指定せずに接続確認

$ openssl s_client -ssl2 -connect (ドメイン名):443 -quiet # SSLv2で接続確認

試しに、グーグルにアクセスしてみたところ、以下の様な応答が返って来ました。

$ openssl s_client -connect www.google.com:443 -quiet

depth=3 C = US, O = Equifax, OU = Equifax Secure Certificate Authority

verify return:1

depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Global CA

verify return:1

depth=1 C = US, O = Google Inc, CN = Google Internet Authority G2

verify return:1

depth=0 C = US, ST = California, L = Mountain View, O = Google Inc, CN = www.google.com

verify return:1

$ openssl s_client -ssl2 -connect www.google.com:443 -quiet

139632633231176:error:1407F0E5:SSL routines:SSL2_WRITE:ssl handshake failure:s2_pkt.c:429:

SSLv2を指定した場合、接続エラーが返って来ました。

このように、グーグルでは「httpsで通信できるが、SSLv2は無効にしている」という事がわかります。

このコマンドはサブドメイン別にサーバーが分かれている場合でも確認が取れますので、もし同じ状況で上記サイトでの確認が取れない場合、今回紹介したコマンドを使って確認してみてはいかがでしょうか。

そしてSSLv2が有効になっている場合は各自対応しましょう。

もちろん、SSLv2が無効になっている場合でもopensslの更新は行ったほうが良いと思います。

Amazon RDS for MySQL でフルアクセス権限を持つユーザーを作成する

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

データベースにアクセスするユーザーを作成する場合、本来であればアクセス可能なデータベースや権限を細かく設定すべきですが、稀にフルアクセス権限を持つユーザーを作成したい場合もあると思います。

MySQLであれば

GRANT ALL ON *.* TO user@'localhost' IDENTIFIED BY 'パスワード';

とすることでフルアクセス可能なユーザーを作成することができますが、同コマンドをAmazon RDS for MySQLで実行すると

ERROR 1045 (28000): Access denied for user 'root_account'@'%' (using password: YES)

というような権限エラーが発生します。

このような場合は

GRANT ALL ON `%`.* TO user@'localhost' IDENTIFIED BY 'パスワード';

というように、 `%` で対象となるデータベースを指定することでフルアクセス可能なユーザーを作成することができます。

CVE-2015-7547のglibcライブラリの脆弱性について、CentOS系での修正済みバージョンの確認と適用方法について

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

こんにちは。アピリッツの本多です。

本日2016年02月17日、glibcライブラリの脆弱性の記事が掲載されました。

脆弱性の詳細につきましては以下の記事をご確認頂くとして、本ページでは修正後のglibcのバージョンと、適用方法に情報を絞ってご紹介したいと思います。

Google Online Security Blog: CVE-2015-7547: glibc getaddrinfo stack-based buffer overflow

https://googleonlinesecurity.blogspot.jp/2016/02/cve-2015-7547-glibc-getaddrinfo-stack.html

各CentOSでの修正後のglibcのバージョン情報

OS対応の有無修正後のバージョン
CentOS5系不要
CentOS6系必要2.12-1.166.el6_7.7
CentOS7系必要2.17-106.el7_2.4

情報元:

CVE-2015-7547 – Red Hat Customer Portal

Red Hat Customer Portal

Red Hat Customer Portal

glibcのアップデート手順

rootユーザーにて以下のコマンドを実行します。

# yum clean all

# yum update glibc

アップデート完了後はシステムの再起動を行ってください。

CentOS7系では、以下のコマンドを叩く事で、再起動せずとも更新されたglibcを適用する事が出来るそうです。

# systemctl daemon-reexec

情報元:

Critical glibc buffer overflow vulnerability in getaddrinfo() on Linux (CVE-2015-7547 & CVE-2015-5229)

おまけですが、Amazon Linuxの場合のglibcの情報も記載しておきます。

修正後のバージョン
2.17-106.166.amzn1

更新方法は上のやり方と同じですので、早急の対応を行いましょう。

情報元:

https://alas.aws.amazon.com/ALAS-2016-653.html (SSL_connect returned=1 errno=0 state=SSLv2/v3 read server hello A: sslv3 alert handshake failure)

rubyistのためのscala (3) クラスとオブジェクト指向

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。


Rubyで書くあれはScalaだとどう書くの?シリーズ。 今回はクラスとオブジェクト指向について。

クラス

クラスの宣言と継承

Ruby

class Foo < Bar
end

Scala

class Foo extends Bar {
}

コンストラクタ

Rubyではinitializeという名前のメソッドを定義する。

class Foo
  def def initialize(bar)
    # new した時点でここが実行される
  end
end

Scalaにはコンストラクタを複数定義できるが、ここではデフォルトのコンストラクタについて説明する。

デフォルトのコンストラクタはクラス定義のブラケット内にそのまま記述できる。
引数はクラス名の後ろのカッコ内に書く。

class Foo(bar: Int) {
  // new した時点でここが実行される
}

モジュール

Ruby

module Foo
  def bar
  end
end

Scala

trait Foo {
  def bar: Unit = {
  }
}

barというメソッドを持つtrait Fooを定義することができる。

Mixin

Scalaではtraitを継承することでmixinを行う。

Ruby

module Bar
end

class Foo
  include Bar
end

Scala

trait Bar {
}

class Foo extends Bar {
}

ひとつのクラスが複数のtraitをmixinする場合、二つ目以降は with というキーワードを使う。

Scala

class Foo extends Bar with Baz with Hoge {
}

TODO: prependusingについても調べて書く

名前空間

Rubyではモジュールを名前空間として使うことができる。

Ruby

module Foo
  module Bar
    class Baz
    end
  end
end

baz = Foo::Bar::Baz.new

Scalaではパッケージのほか、シングルトンオブジェクトも名前空間として使うことができる。

Scala

package foo
object Bar {
  class Baz {
  }
}

val baz = new foo.Bar.Baz()

TODO: import について書く

メソッド

キーワードは同じ def
引数と返り値の型を指定する。

Ruby

class Foo
  def bar(a, b)
    a + b
  end
end

Scala

class Foo {
  def bar(a: Int, b: Int): Int = {
    a + b
  }
}

プロパティとアクセサ

デフォルトのゲッター、セッター

Ruby

class Foo
  attr_accessor :bar, :baz
  def initialize(bar, baz)
    @bar = bar
    @baz = baz
  end
end

foo = Foo.new(1, "abc")
foo.bar  # => 1
foo.baz  # => "abc"

Scala

class Foo(var bar: Int, var baz: String) {
}

val foo = new Foo(1, "abc")
foo.bar  // => 1
foo.baz  // => "abc"

セッターの実装

Ruby

class Foo
  attr_reader :bar
  def bar=(i)
    @bar = i
  end
end

foo = Foo.new
foo.bar = 1
foo.bar  # => 1

Scala

class Foo {
  private var _bar: Int = _
  def bar = _bar
  def bar_=(i: Int): Unit = this._bar = i
}

val foo = new Foo()
foo.bar = 1
foo.bar  // => 1

Scalaのセッターメソッドは def 変数名_=(引数): Unit = ??? という形で定義する。

演算子メソッド

Rubyでメソッドとして再定義可能な演算子

|  ^  &  <=>  ==  ===  =~  >   >=  <   <=   <<  >>
 +  -  *  /    %   **   ~   +@  -@  []  []=  ` ! != !~

Scalaで再定義できない予約語、キーワード
FAQ How do I find what some symbol means or does?より引用)
(これ以外の記号、英数字は使えるらしい)

// Keywords
<-  // Used on for-comprehensions, to separate pattern from generator
=>  // Used for function types, function literals and import renaming
// Reserved
( )        // Delimit expressions and parameters
[ ]        // Delimit type parameters
{ }        // Delimit blocks
.          // Method call and path separator
// /* */   // Comments
#          // Used in type notations
:          // Type ascription or context bounds
<: >: <%   // Upper, lower and view bounds
" """      // Strings
'          // Indicate symbols and characters
@          // Annotations and variable binding on pattern matching
`          // Denote constant or enable arbitrary identifiers
,          // Parameter separator
;          // Statement separator
_*         // vararg expansion
_          // Many different meanings

どちらも記号を名前としたメソッドを定義することで演算子を定義できる。

class Foo
  def +(other)
    "add method"
  end
end

foo1 = Foo.new
foo2 = Foo.new
foo1 + foo2  # "add method"
class Foo {
  def +(other: Foo): String = "add method"
}

val foo1 = new Foo
val foo2 = new Foo
foo1 + foo2  // "add method"

引数のデフォルト値

Ruby

class Foo
  def bar(baz = 1)
    baz
  end
end

foo = Foo.new
foo.bar()  # => 1
foo.bar(2)  # => 2

Scala

class Foo {
  def bar(baz: Int = 1): Int = {
    baz
  }
}

val foo = new Foo()
foo.bar()  // => 1
foo.bar(2)  // => 2

可変長引数

Rubyでは定義するときも呼び出すときも * という記号をまえにつける。

class Foo
  def bar(baz, *hoge)
    # 2つめ以降の引数はhogeに配列として入る
  end
end

foo = Foo.new
baz = 1
hoge = ["foo", "bar"]
foo.bar(baz, *hoge)

Scalaでは定義するときは *をうしろにつけ、呼び出すときは :_* という記号をうしろにつける。

class Foo {
  def bar(baz: Int, bar: String*): Unit = {
    // 2つめ以降の引数は bar にSeqとして入る
  }
}

val foo = new Foo
val baz = 1
val hoge = Seq("foo", "bar")
foo.bar(baz, hoge:_*)

※読みやすいレイアウトでまとめましたサイトを作りましたので、こちらもご参照ください。
http://kkismd.github.io/rb2scala/

rubyistのためのscala (1) 値と式

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。


Rubyで書くあれはScalaだとどう書くの?シリーズ。 今回は値や式についてまとめてみました。

数値

種類RubyScala備考
整数123123 2147483647L整数は32bit(Int),64bit(Long)の2種類がある
実数123.45123.45 123. 123.45d 123.45f実数はDoubleとFloat
浮動小数1.2e-31.2e-3
16進整数0xffff0xffff
8進数0377ゼロから始める8進数表記はScala 2.11から非推奨
2進整数0b10112進数のリテラルなし
桁区切り文字1_000_000_000桁区切り文字はなし

文字列

種類RubyScala備考
ダブルクォート"this is a string expression\n""this is a string expression\n"
シングルクォート'this is a string expression''a'シングルクォートはChar型を表すため文字列には使えない
シンボル:aaa'aaa
ヒアドキュメント<"""foo bar baz """改行を含む文字列はダブルクォートを3つ重ねる(Raw String)。バックスラッシュによるエスケープが効かない。
式埋め込み"foo #{1 + 2} baz"s"foo ${1 + 2} baz"ダブルクォートの前に小文字のs (interpolator) を置く
フォーマットsprintf("foo %d baz", 123) "foo %d baz" % [123]"foo %d baz".format(123)

変数

種類RubyScala備考
変数a = 123var a = 123
定数A = 123val a = 123変数をvalで宣言すると再代入がコンパイルエラーになる(Rubyの定数より厳格)

演算子

ScalaもRubyと同じように演算子の一部がメソッドのシンタックスシュガーとして定義されている。

1 + 2 は 1.+(2) のシンタックスシュガー

種類RubyScala備考
代入foo = barfoo = bar再代入が必要な時はvarで宣言する
配列参照array[1]array(1)カッコによる配列参照はメソッド呼び出し .apply(n) のシンタックスシュガー
属性参照foo.barfoo.bar引数なしのメソッドをカッコ省略できる
自己代入foo += 1foo += 1再代入があるのでvar変数として宣言が必要
多重代入foo, bar, baz = 1, 2, 3 foo, bar, baz = [1, 2, 3](foo, bar, baz) = (1, 2, 3) Array(foo, bar, baz) = Array(1, 2, 3)多重代入はパターンマッチのシンタックスシュガー
範囲式1 .. 201 to 20 1.to(20)整数クラスの .to() メソッドを使う。引数が1つしかないメソッドはドットやカッコを省略できる。
アンド条件foo && bar foo and barfoo && bar演算子 and は使えない
オア条件foo || bar foo or barfoo || bar演算子 or は使えない
否定!foo not foo!foo演算子notは使えない
3項演算子obj == 1 ? foo : barif (obj == 1) foo else bar3項演算子がないのでif式を使う

※読みやすいレイアウトでまとめましたサイトを作りましたので、こちらもご参照ください。
http://kkismd.github.io/rb2scala/

rubyistのためのscala (2) 制御構造

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。


Rubyで書くあれはScalaだとどう書くの?シリーズ。 今回は制御構造です。

if-then-else

Ruby

if a = b
  "equal!"
else
  "not equal!"
end

Scala

if (a == b) {
  "equal!"
}
else {
  "not equal!"
}

Scalaでもifは式であり、値を返すことができる。 後置式はない。

unless

Ruby

unless foo?
  "not foo"
else
  "foo"
end

Scala

if (!isFoo)
  "not foo"
else
  "foo"

Scalaにunlessはない。

case

Ruby

case hoge
when 1
  "one"
when 2, 3
  "two or three"
end

Scala

hoge match {
  case 1 =>
    "foo"
  case 2|3 =>
    "two or three"
}

Rubyのcase式もScalaのmatch式もどちらも強力だがここでは最も基本的な形を挙げている。

繰り返し

whileループ

Ruby

while true
  puts("Hello")
end

Scala

while (true) {
  println("Hello")
}

untilループ

Ruby

until f.eof?
  print f.gets
end

Scala

while (!f.isEof) {
  println(f.getLine)
}

untilはない。

forループ

Ruby

for i in [1,2,3]
  print i * 2, "\n"
end

Scala

for (i <- Seq(1,2,3)) {
  println(i * 2)
}

Scalaのfor式はもっと多機能だがここでは基本的な形を挙げている。

break

Ruby

i = 0
while true
  puts i
  i += 1
  if i > 10
    break
  end
end

Scala

import scala.util.control.Breaks
val breaks = new Breaks
var i = 0
breaks.breakable {
 while (true) {
   println(i)
   i += 1
   if (i > 10) breaks.break()
 }
}

ループのブレイクが構文として用意されていないので、標準ライブラリの Breaks を使う必要がある。

next, redo, retry

Ruby

lines.each do |line|
  next if line.blank?
  puts line
end

Scala

lines.foreach { line =>
  if (!line.isEmpty) {
    println(line)
  }
}

next, redo, retryなどの構文はないのでifなどを使って実装する必要がある。

例外処理

例外の送出

Ruby

raise StandardError.new("例外が起きました")

Scala

throw new RuntimeException("例外が起きました")

例外の捕捉

Ruby

begin
  do_something
rescue ZeroDivisionError => e
  recover(e)
ensure
  must_to_do
end

Scala

try {
  doSomething
}
catch {
  case e: ArithmeticException =>
    recover(e)
}
finally {
  mustToDo
}

return

Ruby

return 1

Scala

return 1

returnはほぼ同じ。 関数やメソッド、ブロックの最後のretrunを省略すると、Rubyと同じように最後に評価された値を返す。

※読みやすいレイアウトでまとめましたサイトを作りましたので、こちらもご参照ください。
http://kkismd.github.io/rb2scala/

重いeclipseを早くするには?試した8つの方法

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

たまごです。

業務でSTSを使ったのですが、激重で心がくじけそうになりました。eclipseを軽くする小技は色々ありますが、今日は備忘も兼ねて、自分の試した小技をまとめていきたいと思います。

 コンテンツアシストを早くする

Window>Preferences>Java>Editor>Content Assist

↑上記のAuto activation delayを80くらいにする。(僕は60にしています)

これでかなり挙動のもっさり感が改善。

 テーマをクラシックにする

Window>Preferences>General>Appearance

↑上記のThemeをClassicに変更。

 アニメーションを禁止する

Window>Preferences>General>Appearance

↑上記のEnable animationsを非選択に。

 パースペクティブをカスタマイズする

Window>Customize Perspective>Tool Bar Visibility

↑上記で、不要なツールバーを非表示にする。(僕はhelp以外は全部チェック外してます)

 Tomcatのホットデプロイを無効化する

Serversタブ>Tomcat>Overview>Publishing

↑上記で、Never publishing automaticallyを選択。

 プロパティエディタからSimplePropertyEditorに変えてみる

こっちの方が軽いらしいです。

 iniファイルのメモリ設定を変えてみる

  • Xms2048m
  • Xmx2048m
  • XX:MaxPermSize=512m

こんなかんじにしてみる。数字はテキトーなので、最適かどうかは不明です。

 こまめにrun garbage collectorしてみる

Window>Preferences>General

↑上記で、Show heap statusにチェック。

すると、heap statusと共にバケツアイコンが表示されるようになるので、こいつをクリックしてこまめにrun garbage collectorする。(ただし、よく忘れる)

ruby-opencvのインストール

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

RubyでOpenCVを使ってみたくて
gemのruby-opencvを入れてみました。

OpenCVは画像処理や画像解析ができるオープンソースのライブラリです。

http://opencv.org/http://opencv.jp/

画像の顔認識や、
動画中の特定条件に合致したコマのみキャプチャするとか
そういうことができます。
機械学習機能があるので、
学習させれば、好みの顔が写ってる画像だけ拾って来る
みたいなこともできるようです。(難しいようですが)

RubyはOpenCV公式の対象言語ではないとのことですが、
問題なく使えるとのこと。
gemもあって、わりとメジャーなようだったので
インストールしてみました。

https://github.com/ruby-opencv/ruby-opencvhttp://www.rubydoc.info/gems/ruby-opencv/frames

なかなか依存関係のバージョンが合わず一苦労だったので
以下手順メモ。・環境・OpenCVインストール

  1. ライブラリをいろいろインストール
  2. yasm のインストール
  3. x264 のインストール
  4. fdk-aac のインストール
  5. ライブラリ設定
  6. ffmpeg1.2 のインストール
  7. opencv-2.4.6.1 のインストール

・ruby-opencvインストール

■ 環境

  • CentOS6.7
  • Ruby 2.1.7(rbenv)
  • Rails 4.1.6(rbenv)

■ OpenCVインストール

1. ライブラリをいろいろインストール

# yum --setopt=group_package_types=optional groupinstall Base
# yum groupinstall "Development Tools"
# yum install http://pkgs.repoforge.org/rpmforge-release/rpmforge-release-0.5.3-1.el6.rf.x86_64.rpm

# vi /etc/yum.repos.d/rpmforge.repo
※enabled=0にする

# yum -y install gtk2-devel
# yum install expat-devel
# yum install libarchive libarchive-devel
# yum install libcurl3 ※ない
# yum install qt qt-devel

# yum install gtk*
# yum install cmake28

# yum install autoconf automake make gcc gcc-c++ pkgconfig wget libtool zlib-devel
# yum install http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
※enabled=0にする

2. yasm のインストール(x264 のビルドに必要)

# cd /usr/local/src
# wget http://www.tortall.net/projects/yasm/releases/yasm-1.3.0.tar.gz
# tar zvxf yasm-1.3.0.tar.gz 
# cd yasm-1.3.0
# ./configure
# make
# make install

3. x264 のインストール(ffmpeg のビルドに必要)

# cd /usr/local/src
# git clone git://git.videolan.org/x264
# cd x264
# ./configure --enable-shared
# make
# make install

4. fdk-aac のインストール

# cd /usr/local/src
# git clone --depth 1 git://github.com/mstorsjo/fdk-aac.git
# cd fdk-aac
# autoreconf -fiv
# ./configure
# make
# make install

5. ライブラリ設定

# export LD_LIBRARY_PATH=/usr/local/lib/
# export PKG_CONFIG_PATH="/usr/local/lib/pkgconfig"
# echo /usr/local/lib > /etc/ld.so.conf.d/custom-libs.conf
# ldconfig

6. ffmpeg1.2 のインストール

# cd /usr/local/src
# git clone git://source.ffmpeg.org/ffmpeg.git ffmpeg
# cd ffmpeg
# git checkout -b release1.2 origin/release/1.2
# ./configure --enable-gpl --enable-nonfree --enable-libfdk_aac --enable-libx264 --enable-shared
# make
# make install

# yum --enablerepo=rpmforge install ffmpeg-devel

バージョン確認

# ffmpeg -version

7. opencv-2.4.6.1 のインストール

# cd /usr/local/src
# wget ftp://ftp.jp.netbsd.org/pub/pkgsrc/distfiles/opencv-2.4.6.1.tar.gz
# tar vxzf opencv-2.4.6.1.tar.gz
# cd opencv-2.4.6.1
# cmake28 .
# make
# make install
# ldconfig

バージョン確認

# cat /usr/local/share/OpenCV/OpenCVConfig-version.cmake

■ ruby-opencvインストール

$ vi Gemfile

以下を追記
--------------------
gem 'ruby-opencv'
--------------------

$ bundle update

インストール完了です。

以下は失敗例

・opencv-2.4.11入れた時のエラー

/usr/local/src/opencv-2.4.11/modules/highgui/src/ffmpeg_codecs.hpp:104: error: ‘CODEC_ID_H264’ was not declared in this scope

コーデック関連のライブラリでエラーがたくさんでる。。

http://code.opencv.org/issues/3986OpenCV3.0RC1で解決されたらしいですが、ruby-opencvはOpenCV3.*に対応してないのでバージョンは上げられず。

https://github.com/Itseez/opencv/issues/4940OpenCV2.4.9、2.4.10でも同様のエラーがでるぽいです。OpenCV2.4.8も別のエラーがでて結局だめでした。

とりあえず試してみる分には問題ないので
2.4.6.1でよしとしました。

bashプロンプトのカスタマイズ

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

bashのプロンプトが突然「-bash-4.1$」のようになってしまって
直した時に調べたメモです。

いつもは「[hoge@localhost ~]$」のように
[ユーザ名@ホスト名 カレントディレクトリ]$
の形で表示されていたのが、
突然「-bash-4.1$」となってしまいました。

何かのタイミングでデフォルト設定に戻ってしまったようです。

・フォーマットの設定
・色の設定

■ フォーマットの設定

bashのプロンプトはPS1でフォーマットを設定できます。
デフォルトは¥s-¥v¥$です。

[hoge@localhost ~]$export PS1="\s-\v\$"
-bash-4.1$

-bash-4.1$

むむ。どうも落ち着つきません。

なので、いつもどおり
[ユーザ名@ホスト名 カレントディレクトリ]$
のフォーマットで表示されるよう設定し直します。

[\u@\h \W]\$

これを設定する。

-bash-4.1$export PS1="[\u@\h \W]\$"
[hoge@localhost ~]$

直りました。
これを「/etc/profile」や「~/.bashrc」に追記しておけばOKです。

他にも設定できるフォーマットがいろいろあって、
man bashで確認できます。

PROMPTING
       When  executing  interactively, bash displays the primary prompt PS1 when it is ready to read a command, and the secondary prompt PS2 when it needs more input to complete a command.  Bash allows these prompt strings to
       be customized by inserting a number of backslash-escaped special characters that are decoded as follows:
              \a     an ASCII bell character (07)
              \d     the date in "Weekday Month Date" format (e.g., "Tue May 26")
              \D{format}
                     the format is passed to strftime(3) and the result is inserted into the prompt string; an empty format results in a locale-specific time representation.  The braces are required
              \e     an ASCII escape character (033)
              \h     the hostname up to the first ‘.’
              \H     the hostname
              \j     the number of jobs currently managed by the shell
              \l     the basename of the shell’s terminal device name
              \n     newline
              \r     carriage return
              \s     the name of the shell, the basename of $0 (the portion following the final slash)
              \t     the current time in 24-hour HH:MM:SS format
              \T     the current time in 12-hour HH:MM:SS format
              \@     the current time in 12-hour am/pm format
              \A     the current time in 24-hour HH:MM format
              \u     the username of the current user
              \v     the version of bash (e.g., 2.00)
              \V     the release of bash, version + patch level (e.g., 2.00.0)
              \w     the current working directory, with $HOME abbreviated with a tilde (uses the value of the PROMPT_DIRTRIM variable)
              \W     the basename of the current working directory, with $HOME abbreviated with a tilde
              \!     the history number of this command
              \#     the command number of this command
              \$     if the effective UID is 0, a #, otherwise a $
              \nnn   the character corresponding to the octal number nnn
              \\     a backslash
              \[     begin a sequence of non-printing characters, which could be used to embed a terminal control sequence into the prompt
              \]     end a sequence of non-printing characters

■ 色の設定

サーバ毎にプロンプトの色を変えておけば
どのサーバにログインしているのかわかりやすくなります。

・文字色をシアンに変える例

$ export PS1=”¥[¥e[0;36m¥][¥u@¥h ¥W]¥$¥[¥e[0m¥]”

¥[¥e[0;36m¥]~¥[¥e[0m¥]で挟まれた部分が「0;36」で指定された装飾/色になります。

この形式はANSIエスケープシーケンスというそうです。

0;36をもっと詳しく説明すると、
0と36の2つの装飾指定がされていて、

0→文字装飾なし
36→文字色をシアンにする

という意味になります。
これにさらに背景色を青にしたい、となると0;36;44となって、0と36と44の3つの装飾指定をする
という意味になります。

0→文字装飾なし
36→文字色をシアンにする
44→背景色を青にする

\[\e[0;36;44m\][\u@\h \W]\\$\[\e[0m\]

なので、最初に「¥[¥e[0;36m¥]~¥[¥e[0m¥]」挟まれた部分が…
とは言いましたが、本来は、¥[¥e[0;36m¥]以降 を「装飾無し&文字色シアン」で表示する
¥[¥e[0m¥]以降 を「装飾なし」で表示するという意味になるようです。

もっと他の装飾指定を追加することも可能です。

↓こちらのページでとてもわかりやすく説明されていました。
http://oxynotes.com/?p=5418

とりあえず↓のように設定。

$ export PS1="\[\e[0;36m\][\u@\h \W]\\$\[\e[0m\]"

これでどのターミナル開いてるか分かりやすくなりました。

FullCalendarで営業日カレンダーを作成する(小さいカレンダー)

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

営業日カレンダーを作成したくて、FullCalendarのオプションを色々いじって実装しました。

バージョン:fullcalendar-2.4.0

js

$(document).ready(function() {

	$('#calendar').fullCalendar({
		header: {
			left: 'prev,next today',
			center: 'title',
			right: ''
		},
		buttonText: {
			today: '今日'
		},
		dayNamesShort: ['日','月','火','水','木','金','土'],
		defaultDate: '2016-02-03',
		businessHours: false,
		titleFormat: {
			month: 'MMM YYYY'
		},
		// 表示用データ
		events: [
			{
				start: '2016-02-09',
				rendering: 'background',
				color: '#ff9f89'
			},
			{
				start: '2016-02-06',
				end: '2016-02-08',
				rendering: 'background',
				color: '#ff9f89'
			},
			{
				start: '2016-02-13',
				end: '2016-02-15',
				rendering: 'background',
				color: '#ff9f89'
			},
			{
				start: '2016-02-20',
				end: '2016-02-22',
				rendering: 'background',
				color: '#ff9f89'
			},
			{
				start: '2016-02-27',
				end: '2016-02-29',
				rendering: 'background',
				color: '#ff9f89'
			},
		]
	});
});

eventsのデータの中で、

dayNamesShort: [‘日’,’月’,’火’,’水’,’木’,’金’,’土’]で曜日の表示を1文字にします。

rendering: ‘background’を指定することにより、そのイベントの日が背景色の表示になります。

color: ‘#ff9f89’を指定することにより、休日っぽい色にしています。

businessHours: falseで土日の背景色を無効にしています。

実際はevents部分のデータはjsonでサーバから取得していたのですが、今回は表示のテスト用にjsの中に記述しています。

css

body {
	margin: 40px 10px;
	padding: 0;
	font-family: "Lucida Grande",Helvetica,Arial,Verdana,sans-serif;
	font-size: 14px;
}

#calendar {
	max-width: 220px;
	margin: 0 auto;
}

max-width: 220px;でカレンダーのサイズを指定します。

また、デフォルトのままだとスクロールバーが出たりするので、微調整します。

css続き

.fc-basic-view .fc-body .fc-row {
	min-height: 1em !important;
}

.fc button {
	height: 2em !important;
	padding: 0 .1em !important;

	/* text & cursor */
	font-size: 0.8em !important;
}

jsのオプションでセルの高さを指定する方法がわからなかった(小さくできなかった)ので、fullcalendar.cssのスタイルを一部上書きしています。

これで冒頭の営業日カレンダーを作成することができました。

irb 上で using の動作を試したときのメモ

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

インスタンスメソッドを特定の場面のみ変更したい場合に using と refine を使う方法があります。irb 上でなかなか思った通りに動かなかったのでメモしておきます。

 参考 (instance method Module#refine)

http://docs.ruby-lang.org/ja/2.0.0/method/Module/i/refine.html

 環境

OS (sw_vers ; uname -a)

ProductName: Mac OS X

ProductVersion: 10.11.3

BuildVersion: 15D21

Darwin pomme.local 15.3.0 Darwin Kernel Version 15.3.0: Thu Dec 10 18:40:58 PST 2015; root:xnu-3248.30.4~1/RELEASE_X86_64 x86_64

ruby (ruby -v; irb -v ; pry -v)

ruby 2.1.9p441 (2015-12-23 revision 53262) [x86_64-darwin15.0]

irb 0.9.6(09/06/30)

Pry version 0.10.3 on Ruby 2.1.9

 使ってみました。

変更されるクラス (Enterprise) の定義

pomme:~ kadosaway$ pry
	[1] pry(main)> class Enterprise
	[1] pry(main)* def earn
	[1] pry(main)* puts "much!"
	[1] pry(main)* end
	[1] pry(main)* end
	=> :earn

変更を加えるモジュール (Strategist) の定義

[2] pry(main)> module Strategist
	[2] pry(main)* refine Enterprise do
	[2] pry(main)* def earn
	[2] pry(main)* puts 'more!!!'
	[2] pry(main)* end
	[2] pry(main)* end
	[2] pry(main)* end
	=> #<refinement:enterprise @strategist>

変更前の動作確認

[3] pry(main)> prj = Enterprise.new
	=> #<enterprise:0x007fc5788456d0>
	[4] pry(main)> prj.earn
	much!
	=> nil

変更を using によって適用しての動作確認のつもり

[5] pry(main)> using Strategist
	=> main
	[6] pry(main)> prj = Enterprise.new
	=> #<enterprise:0x007fc57909ca90>
	[7] pry(main)> prj.earn
	much!
	=> nil

irb (pry)上では期待した通りにならない!?

命令を1行にして実行してみると

[8] pry(main)> using Strategist; prj = Enterprise.new; prj.earn
	more!!!
	=> nil

期待通りになりました。

同じメソッドを再度呼んでみると

[9] pry(main)> prj.earn
	much!
	=> nil

変更が解除されている。

試しに

[10] pry(main)> using Strategist; prj.earn; prj.earn
	more!!!
	more!!!
	=> nil
	[11] pry(main)> using Strategist; prj.earn; \
	[11] pry(main)* prj.earn
	more!!!
	more!!!
	=> nil
	[12] pry(main)> prj.earn; prj.earn; using Strategist; prj.earn
	much!
	much!
	more!!!
	=> nil

using を使用後に同行にて変更が反映される様でした。

arel_tableを使った時のメモ

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

日々の気温差に体がついて行っていない気がする中西です。

少し前にarel_tableを使用したので、簡単な使い方等をメモしてみます。

複雑なSQL文

スコープなどを書こうと思った場合に、条件がすこし難しい場合があります。

そういった時に、生のSQL文を使用することもあると思います。

少し前に、下記のようなスコープを書いたとこがありました。

scope :except_codes, -> { where.not('(test_code = 0 OR test_code IS NULL) AND code_created_at IS NULL') }

生のSQL文をを用いています。

そのとき、先輩からarel_tableの話を聞き使ってみることにしました。

使ってみた結果が、下記になります。

scope :except_codes, -> { where.not(arel_table[:test_code].eq_any([0, nil]).and(arel_table[:code_created_at].eq(nil))) }

今回は、短いSQL文ですが、長いSQL文になった場合、生のSQL文では後々わかりずらくなってしまう恐れがあります。

そういった場合に、arel_tableを使うことで後々理解がしやすくなると思われます。

arel_tableの使い方

今回上記で使用した部分について簡単にメモします。

まず、下記部分で使用するカラム名を指定しています。

arel_table[:test_code]
arel_table[:code_created_at]

このあとに、条件を追加します。

それぞれ、今回使用している部分は下記のような意味となります。

eq_any(○) :○以外の場合(≠)
eq(○)   :○の場合(=)

○の部分が複数ある場合、配列として入力することで複数の値を条件にできます。

a.eq([0, 1]) :aが「0」か「1」の場合

複数の条件を組み合わせたい場合には、andやorを用いることができます。

こうすることで、複雑な条件文の場合にも比較的簡単に書くことができます。

まとめ

今回は実際に自分が使用した、簡単な部分についてメモしてみました。

このほかにも、likeやorder等も簡単に書けるようです。

気になる方が、調べてみてください。

追記(2016/2/2)

ちょっと指摘があったので追記。

今回紹介しているarel_tableは、Rails用の非公開APIなので、使用する場合は、それを念頭に置き、注意してください。

CarrierWave使用時のTips

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Railsに画像等のファイルをアップロードする機能を実装するgem「CarrierWave」を使用する際のTipsを纏める。

Railsに画像等のファイルをアップロードする機能を実装するgem「CarrierWave」を使用する際のTipsを纏める。

 話の前提

テーブル「users」にフィールド「picture_url」があるという前提。

(picture_urlにはアップロードするファイルのURLが格納される。)

 モデルに関するTip

acts_as_paranoid を併用する場合の注意点

Railsに論理削除を実装できるgem「acts_as_paranoid」を導入しているモデルにCarrierWaveを使用する場合は、skip_callback を設定する必要がある。

class User < ActiveRecord::Base
  acts_as_paranoid
  mount_uploader :picture_url, ImageUploader
  skip_callback :commit, :after, :remove_picture_url!
end

※skip_callbackがないと、データベースが更新された後、画像が削除されてしまう。

 コントローラに関するTip

確認画面が存在する場合の工夫

確認画面がなく、1クリックで更新する様なシステムの場合は非常に単純(編集画面→更新)。

# データベースの更新を行い、指定のディレクトリにファイルを配置する。
@user.save!

しかし、確認画面がある場合は、少し複雑になる(編集画面→確認画面→更新)。

確認画面がある場合は、暗黙的に以下を満足する必要がある事が恐らく一般的である。

  1. 確認画面にファイルを表示する(画像の場合)。
  2. 確認画面を表示しただけでは、データベース更新も格納ファイルの変更もしない。

CarrierWaveでは、通常のアップロード(save)以外に、一時的なアップロードを行う事ができるので、その機能を活用する。

※一時的アップロードでは、データベースは更新されず、指定のディレクトリとは別の一時ディレクトリにファイルがキャッシュとして保存される。

確認時のアクション
# 一時的アップロード(@userインスタンスには一時ディレクトリのパスが入る)
@user.picture_url.cache!
確認画面のビュー
<%# 一時ディレクトリの場所にある画像を表示 %>
<%= image_tag(@user.picture_url.url) %>

<%# キャッシュ名(一時ディレクトリの名前)をhiddenで渡す %>
<%= hidden_field_tag :"cache[picture_url]", @user.picture_url.cache_name %>

※アップロードファイルのフィールドが1つの場合はパラメータ名は”cache_picture_url”等でも良いが、この例では複数フィールドがある場合にも対応できる様に配列にしている。

更新時のアクション
# パラメータで受け取ったキャッシュから画像を復元する
@user.picture_url.retrieve_from_cache! params[:cache][:picture_url]
@user.save!

 ビューに関するTip

form_for, form_tag 使用時の注意点

form_for, form_tagを使用する場合はmutipartオプションが必要である。

<%= form_for(@user, html: { multipart: true }) do |f| %>

【Java】Bean Validationで条件付きチェック

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

Javaで条件付きチェックを実装するにあたり、Bean Validationのバリデーショングループを使ってみました。

※Spring Frameworkを使用しています。

 【グループ】

検証グループのインターフェースを作ります。

public interface Role {
	interface Admin {}
	interface General {}
}

 【Bean】

チェック対象のフィールドにグループを指定します。

public class ValidationTestBean {

	private String role;

	@NotEmpty(groups = Admin.class)
	@Size(min = 1, max = 30, groups = General.class)
	private String name;
	
	// getter,setter省略
}

 【コントローラ】

コントローラからバリデータを呼び出します。

@Controller
@RequestMapping({ "/validation/test" })
public class ValidationTestController {

	@Autowired
	private Validator validator;

	@RequestMapping(value = "confirm", method = GET)
	public String confirm(@ModelAttribute("bean") ValidationTestBean bean,
			Errors errors, Model model) {

		// 検証グループ生成
		Object[] groups = createValidationGroups(bean);

		// 入力チェック
		ValidationUtils.invokeValidator(validator, bean, errors, groups);
		if (errors.hasErrors()) {
			return "validation/test/input";
		}
		return "validation/test/confirm";
	}

	private Object[] createValidationGroups(ValidationTestBean bean) {

		List<object> groups = new ArrayList<object>();

		// 条件によりグループを追加
		if ("admin".equals(bean.getRole())) {
			groups.add(Admin.class);
		} else {
			groups.add(General.class);
		}
		return groups.toArray();
	}
}

createValidationGroupsメソッドでグループを追加しています。

このとき、条件により異なるグループを設定することにより、Beanで指定したグループのみバリデーションを実行することができます。

今回割愛しましたが、グループは省略したり複数指定したりも可能です。

グループを活用することにより条件付きチェックをできるだけアノテーションで実装することができました。

RailsでMySQLに格納できない文字が入力された場合の対処法方の検討

0

この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

RailsでMySQLに格納できない文字が入力された場合の対処法方の検討しました

 テーブル例

FieldType
idint(11)
hogevarchar(255)

 エラーの出る文字の一例(本文中に記載すると投稿できないため外部サイト参照)

 実行例

ユーザが自由に文字列を入力できる場合、MySQLに格納できない文字が入力される可能性がある。

> t = Hoge.new(hoge: "<エラーの出る文字>")
=> #<hoge id: nil, hoge: "<エラーの出る文字>">
> t.save!
(0.6ms)  BEGIN
SQL (0.3ms)  INSERT INTO `hoges` (`hoge`) VALUES ('<エラーの出る文字>')
Mysql2::Error: Incorrect string value: '\xF0\xA8\xBC\xB2' for column 'hoge' 
at row 1: INSERT INTO `hoges` (`hoge`) VALUES ('<エラーの出る文字>')
(0.1ms)  ROLLBACK
ActiveRecord::StatementInvalid: Mysql2::Error: Incorrect string value: 
'\xF0\xA8\xBC\xB2' for column 'hoge' at row 1: INSERT INTO `hoges` (`hoge`) 
VALUES ('<エラーの出る文字>')

 検出方法

直接Mysql2::Errorは直接例外検出できないため、ActiveRecord::StatementInvalidを検出して判定することでエラー処理を行うことができる

テスト用のメソッド

def hoge_test(str)
  t = Hoge.new(hoge: str)
  t.save!
rescue ActiveRecord::StatementInvalid => e  
  if(e.cause.class == Mysql2::Error 
    && e.cause.message.match(/^Incorrect string value/))
    puts "[DEBUG!] MySQLに格納できない文字が入力されました."
	# エラー処理等
  end
end

実行結果

> hoge_test("<エラーの出る文字>")
(0.6ms)  BEGIN
SQL (0.5ms)  INSERT INTO `hoges` (`hoge`) VALUES ('<エラーの出る文字>')
Mysql2::Error: Incorrect string value: '\xF0\xA8\xBC\xB2' for column 'hoge' 
at row 1: INSERT INTO `hoges` (`hoge`) VALUES ('<エラーの出る文字>')
(0.1ms)  ROLLBACK
[DEBUG!] MySQLに格納できない文字が入力されました.
=> nil

最近人気な記事