ホーム ブログ ページ 42

to_xml メソッドのエチュード

0

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

rails に xmlを出力するときに便利な to_xml メソッドがありますが

とある要件に対応する為に

ちょっと加工が必要だったことがあったのでメモです。

おもに困ったこと

1. エンティティ名が to_json のときと違う。エンティティー名に _ があるときに – にされてしまう。

2. タグの属性に type=“値のクラス” が入ってしまう。

3. 文字コードが変えられない。 <?xml version=”1.0″ encoding=”UTF-8”?> というタグが入ってしまう。

環境

CentOS Linux release 7.1.1503 (Core)

ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]

Rails 4.2.2

やっつけソース

def xml_source(object, xml_char) # xml内容作成の定義。
  <<-end_of_xml_source
<?xml version=\"1.0\" encoding=\"#{xml_char}\"?>
#{object.to_xml(dasherize: false, skip_types: true, skip_instruct: true).gsub('"', '"').gsub("'", ''')}
  end_of_xml_source
end
xml_source(object, xml_char).encode(xml_char, invalid: :replace, undef: :replace)

解説

1. to_xml メソッドの引数に dasharize: false を指定することで解決。

2. 同じく skip_types: true を指定することで解決。

3. 同じくskip_instruct: true を指定することでヘッダのタグを表示しなくできる。所望のヘッダタグの取り付けと実際の文字コードの変換はxml 出力後に行う。文字コードの変換には encode メソッドを使用した。変換不可能な文字は置換。

他に対応した点

nil の値があると <エンティティ名 nil=“true”> のようなタグが発行されてしまう為、データに nil があったら ” に変換。

xml出力のときだけ出したくなかった文字は to_xml後にgsub で変換。

CentOS5のsubversionを1.8にしたのでgit-svnのバージョンも上げる(yum)

0

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

先日CentOS5にSubversion1.8をrpmでインストールしたのですが、
git-svnもそれに合わせたバージョンを入れる必要がありました。

wandiscoが提供するリポジトリをyumリポジトリに追加すれば、
Subversion1.8に合わせたgit-svnをyumでインストールできます。

git-svnインストール時にSubversion関連も一式インストールされるので、
一度Subversionをアンインストールしてからyumでインストールしてみました。・環境・手順

  1. リポジトリを追加
  2. GPGキーを取り込む
  3. yumでインストール

・おまけ

■ 環境

CentOS5.11 x86_64

■ 手順

1. リポジトリを追加

/etc/yum.repos.d/wandisco.repo を作成。
gitとsvnの両方とも設定しておきます。
(subversion-perlがsvnの方に入ってるので必要。
最初gitの方だけ設定していて依存関係でおこられました。)

[wandisco-git]
name=WANDisco Repository - git-2.0 centos5
baseurl=http://opensource.wandisco.com/centos/5/git/$basearch/
enabled=0
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-WANdisco


[wandisco-svn]

name=WANDisco Repository – snv-1.8 centos5 baseurl=http://opensource.wandisco.com/centos/5/svn-1.8/RPMS/$basearch/ enabled=0 gpgcheck=1 gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-WANdisco

2. GPGキーを取り込む

# rpm --import http://opensource.wandisco.com/RPM-GPG-KEY-WANdisco

3. yumでインストール

一度Subversion関連をアンインストール。(しなくてもいいです)

# yum remove subversion apr

リポジトリを指定してインストール

# yum install --enablerepo=wandisco-svn,wandisco-git --disablerepo=base,extras,updates,epel git-svn
Loaded plugins: fastestmirror, security
Loading mirror speeds from cached hostfile
Setting up Install Process
Resolving Dependencies
--> Running transaction check
---> Package git-svn.x86_64 0:2.0.0-1.WANdisco.191 set to be updated
--> Processing Dependency: git = 2.0.0-1.WANdisco.191 for package: git-svn
--> Processing Dependency: perl(Git::SVN) for package: git-svn
--> Processing Dependency: perl(Git::SVN::Log) for package: git-svn
--> Processing Dependency: perl(Git::SVN::Fetcher) for package: git-svn
--> Processing Dependency: subversion for package: git-svn
--> Processing Dependency: perl(Git::SVN::Ra) for package: git-svn
--> Processing Dependency: perl(Git::SVN::Utils) for package: git-svn
--> Processing Dependency: perl(Git::SVN::Editor) for package: git-svn
--> Processing Dependency: perl(Git) for package: git-svn
--> Processing Dependency: perl(Git::SVN::Prompt) for package: git-svn
--> Processing Dependency: perl(Git::SVN::Migration) for package: git-svn
--> Running transaction check
---> Package git.x86_64 0:2.0.0-1.WANdisco.191 set to be updated
---> Package perl-Git.x86_64 0:2.0.0-1.WANdisco.191 set to be updated
--> Processing Dependency: perl(SVN::Client) for package: perl-Git
--> Processing Dependency: perl(SVN::Delta) for package: perl-Git
--> Processing Dependency: perl(SVN::Core) for package: perl-Git
--> Processing Dependency: perl(SVN::Ra) for package: perl-Git
---> Package subversion.x86_64 0:1.8.13-1 set to be updated
--> Processing Dependency: apr-util >= 1.5.3 for package: subversion
--> Processing Dependency: apr >= 1.5.0 for package: subversion
--> Processing Dependency: libapr-1.so.0()(64bit) for package: subversion
--> Processing Dependency: libserf-1.so.1()(64bit) for package: subversion
--> Processing Dependency: libaprutil-1.so.0()(64bit) for package: subversion
--> Running transaction check
---> Package apr.x86_64 0:1.5.0-1.WANdisco.2 set to be updated
---> Package apr-util.x86_64 0:1.5.3-2.WANdisco.8 set to be updated
--> Processing Dependency: apr-util-ldap = 1.5.3-2.WANdisco.8 for package: apr-util
--> Processing Dependency: apr-util-pgsql = 1.5.3-2.WANdisco.8 for package: apr-util
--> Processing Dependency: apr-util-sqlite = 1.5.3-2.WANdisco.8 for package: apr-util
---> Package serf.x86_64 0:1.3.7-1 set to be updated
---> Package subversion-perl.x86_64 0:1.8.13-1 set to be updated
--> Running transaction check
---> Package apr-util-ldap.x86_64 0:1.5.3-2.WANdisco.8 set to be updated
---> Package apr-util-pgsql.x86_64 0:1.5.3-2.WANdisco.8 set to be updated
---> Package apr-util-sqlite.x86_64 0:1.5.3-2.WANdisco.8 set to be updated
--> Finished Dependency Resolution

Dependencies Resolved

================================================================================
 Package             Arch       Version                  Repository        Size
================================================================================
Installing:
 git-svn             x86_64     2.0.0-1.WANdisco.191     wandisco-git     459 k
Installing for dependencies:
 apr                 x86_64     1.5.0-1.WANdisco.2       wandisco-svn      98 k
 apr-util            x86_64     1.5.3-2.WANdisco.8       wandisco-svn      81 k
 apr-util-ldap       x86_64     1.5.3-2.WANdisco.8       wandisco-svn     6.4 k
 apr-util-pgsql      x86_64     1.5.3-2.WANdisco.8       wandisco-svn      11 k
 apr-util-sqlite     x86_64     1.5.3-2.WANdisco.8       wandisco-svn     8.4 k
 git                 x86_64     2.0.0-1.WANdisco.191     wandisco-git      11 M
 perl-Git            x86_64     2.0.0-1.WANdisco.191     wandisco-git      66 k
 serf                x86_64     1.3.7-1                  wandisco-svn      47 k
 subversion          x86_64     1.8.13-1                 wandisco-svn     2.7 M
 subversion-perl     x86_64     1.8.13-1                 wandisco-svn     1.4 M

Transaction Summary
================================================================================
Install      11 Package(s)
Upgrade       0 Package(s)

Total download size: 15 M
Is this ok [y/N]:


  

Subversion1.8に合わせたgit-svnやgit等、一式インストールできました。

参考サイト
CentOS 6でsubversionのバージョンを上げる
リポジトリを指定してyum installする

■ おまけ

Subversion1.8だけ入れたい場合

/etc/yum.repos.d/wandisco.repoにsvnの設定だけして
GPGキー取り込み後、以下を実行すれば一式インストールされます。

# yum install --enablerepo=wandisco-svn --disablerepo=base,extras,updates,epel subversion

シェルスクリプトをダウンロード(登録制)する方法もあるようです。
Subversion 1.8系をCentOSにInstall (YUM経由)
WANdisco Subversion Download | WANdisco CentOS 5

rpmでgit-svnを入れたい場合

以下のものをインストールすればOKでした。(Subversion1.8前提)

http://opensource.wandisco.com/rhel/5/git/x86_64/git-2.0.0-1.WANdisco.191.x86_64.rpm
http://opensource.wandisco.com/rhel/5/git/x86_64/perl-Git-2.0.0-1.WANdisco.191.x86_64.rpm
http://opensource.wandisco.com/rhel/5/git/x86_64/git-svn-2.0.0-1.WANdisco.191.x86_64.rpm
http://opensource.wandisco.com/rhel/5/svn-1.8/RPMS/x86_64/subversion-perl-1.8.10-6.x86_64.rpm
  
# rpm -ivh http://opensource.wandisco.com/rhel/5/git/x86_64/git-2.0.0-1.WANdisco.191.x86_64.rpm http://opensource.wandisco.com/rhel/5/git/x86_64/perl-Git-2.0.0-1.WANdisco.191.x86_64.rpm http://opensource.wandisco.com/rhel/5/git/x86_64/git-svn-2.0.0-1.WANdisco.191.x86_64.rpm http://opensource.wandisco.com/rhel/5/svn-1.8/RPMS/x86_64/subversion-perl-1.8.10-6.x86_64.rpm
  

Oracleで誤ってデータを消してしまった際のリカバリ

0

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

Oracleで誤ってデータを消してコミットしてしまっても、まだ諦めてはいけません。

OracleにはフラッシュバッククエリというUNDO表領域を参照して過去データを取り出す機能があります。

 うっかりWhere句を忘れて・・・

先日、とあるレコードを変更しようとして、うっかりWhere句による絞り込みを忘れ全レコードが更新されてしまうという失態を犯してしまいました・・・。

トランザクション貼っとけ!という意見はごもっともでありますが、ついうっかりは誰しもあるもの。

 フラッシュバッククエリで復旧

しかし調べて見ると、Oracleにはフラッシュバッククエリなる過去データを参照する機能があるとのこと!

早速以下のようにUpdateして事なきを得ました。

update users u set u.name = 
(select name from users as of timestamp (systimestamp - interval '1' hour) 
where u.id = users.id)

上記は1時間前のデータを参照しています。

ちなみに、3分前は

as of timestamp (systimestamp - interval '3' minute )

1日前は

as of timestamp (systimestamp - interval '1' day )

指定した日付、時間

as of timestamp to_timestamp('2015-07-24 00:00:00', 'yyyy-mm-dd hh:mi:ss')

と指定できるようです。

 最後に

UNDO表領域はロールバックデータとしても使われ、インサートなどすると古いUNDO表領域が無くなる恐れがありますので、速やかに復旧することをおすすめします。

また、表をTruncateしたり、再作成してしまった場合は、過去データを参照できないのでご注意ください。

Tomcat起動時のスタックオーバーフローへの対処

0

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

Tomcat起動時にスタックオーバーフローが発生した際の対象方法をご紹介します。

 Tomcat起動が起動しない・・・

ある日、これまで正常に起動していたTomcatが起動しなくなりました。

ログを見ると以下の出力。

Cannot load JDBC driver class 'oracle.jdbc.OracleDriver'
java.lang.StackOverflowError
        at java.util.Stack.empty(Stack.java:96)
        at sun.misc.URLClassPath.getLoader(URLClassPath.java:283)
        at sun.misc.URLClassPath.getResource(URLClassPath.java:168)
        at java.net.URLClassLoader$1.run(URLClassLoader.java:194)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
        at sun.misc.Launcher$ExtClassLoader.findClass(Launcher.java:229)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:295)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1595)
...

StackOverflowErrorが発生しているようです。

 スタックとは?

JVMのメモリ構造はスタックとヒープに大別されます。

ヒープはオブジェクトのインスタンスなどが保存される広大な領域で、GCの対象です。

一方スタックはメソッド起動時にローカル変数などを保存する領域です。

あまりスタックが足りなくなる場合は無いのですが、意図しないケースだと再帰メソッドの終了条件の書き忘れなどで発生します。

ただ、今回の場合はアプリ起動時にローカル変数をキャッシュするようにしたことによるスタックサイズの増加でした。

そのため、対処としては最大スタックサイズを増やすしかなさそうです。

 最大スタックサイズの設定

最大スタックサイズの設定はJVMの起動パラメータの

-Xss512k

の値で変更します。

では、どれくらい増やせばよいのか・・・?

大きければいいという訳ではありません。最大スタックサイズを非常に大きな値に設定すると、

パフォーマンスが低下する可能性があります。

まずは現在の設定値 or デフォルト値の2倍程度にして様子をみるのがいいでしょう。

 まとめ

通常スタックサイズが足りなくなることは少ないかと思いますが、もし足りなくなったら上記設定で対処することができます。

もちろんその前に、無限ループなどが発生していないか確認するのをお忘れなく!

CentOS5のsvnをWin側からTortoiseSVN1.8で管理する

0

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

ホストOS(Win)からゲストOS(CentOS5)にSambaで接続して、

Win側からはTortoiseSVN1.8で管理したかったのですが、

Subversion1.6ではうまくいかなかったので、rpmで1.8をインストールしました。

yumでインストールしようとすると1.6が入ってしまいますが

それぞれのバージョンを合わせないと共存できないようです。

1.7以降であれば大丈夫な気がしつつも、

今回はEclipseでSVNクライアント1.8を選択したいので

1.8にしました。

▼作業コピーが1.6のフォーマットだと、「作業コピーをアップグレード」が完了するまで更新やコミットができない。

▼「作業コピーを新しい1.8形式にアップグレード」を選択してフォーマットが最適化されると、

TortoiseSVNでは更新等が可能になるが、ゲスト側では更新ができなくなってしまう。

$ svn up
svn: The path '.' appears to be part of a Subversion 1.7 or greater
working copy.  Please upgrade your Subversion client to use this
working copy.

 やりたいこと

Sambaで接続したゲストOS(CentOS5)にアクセスして、

ホストOS(Win8.1)側からはTortoiseSVN1.8で管理できるようにしたい。

(さらにWin側ではEclipseでSVNクライアント1.8を入れて開発できるように)

 環境

ホストOS Windows8.1 TortoiseSVN1.8 
ゲストOS CentOS5.11(VirtualBox) Subversion1.8 

 手順

1. インストール済みのSubversionがあればアンインストールしておきます

# yum remove subversion
# yum remove apr

2. 適当な場所に保存

# wget http://opensource.wandisco.com/rhel/5/svn-1.8/RPMS/x86_64/subversion-1.8.10-6.x86_64.rpm
# wget http://opensource.wandisco.com/rhel/5/svn-1.8/RPMS/x86_64/apr-1.5.0-1.WANdisco.2.x86_64.rpm
# wget http://opensource.wandisco.com/rhel/5/svn-1.8/RPMS/x86_64/apr-util-1.5.3-2.WANdisco.8.x86_64.rpm
# wget http://opensource.wandisco.com/rhel/5/svn-1.8/RPMS/x86_64/serf-1.3.4-1.x86_64.rpm
# wget http://opensource.wandisco.com/rhel/5/svn-1.8/RPMS/x86_64/apr-util-ldap-1.5.3-2.WANdisco.8.x86_64.rpm http://opensource.wandisco.com/rhel/5/svn-1.8/RPMS/x86_64/apr-util-pgsql-1.5.3-2.WANdisco.8.x86_64.rpm http://opensource.wandisco.com/rhel/5/svn-1.8/RPMS/x86_64/apr-util-sqlite-1.5.3-2.WANdisco.8.x86_64.rpm

3. 保存したパッケージをインストール

# rpm -ivh apr-1.5.0-1.WANdisco.2.x86_64.rpm
# rpm -ivh apr-util-1.5.3-2.WANdisco.8.x86_64.rpm apr-util-ldap-1.5.3-2.WANdisco.8.x86_64.rpm apr-util-pgsql-1.5.3-2.WANdisco.8.x86_64.rpm apr-util-sqlite-1.5.3-2.WANdisco.8.x86_64.rpm
※まとめないと依存関係でおこられます

# rpm -ivh serf-1.3.4-1.x86_64.rpm
# rpm -ivh subversion-1.8.10-6.x86_64.rpm

4. バージョン確認

# svn --version --quiet
1.8.10

これでWin側からもCentOS側からもsvnで管理できるようになりました。

IDCFオブジェクトストレージ使用方法

0

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

rickNo37です。
IDCフロンティアでオブジェクトストレージを使用する機会があり、
使用しているというブログが少ないので接続確認するまでの方法記載します。

用意するもの

・IDCF契約(仮想サーバ1台以上)
 CentOS7で実施
・オブジェクトストレージ契約(50GB未満の容量は無料)

管理画面上の設定・確認

管理画面よりオブジェクトストレージ画面へ移動
※左上ロゴ横にあるサービス選択より選択
(FireFoxだと契約画面に遷移して遷移したい画面に遷移できない、chromeだといける)

APIユーザーの追加

 リージョン:EAST
 APIユーザー名:メールアドレス

バケットの作成

オブジェクト>BucketAction>バケット作成
 バケット名を入力

オブジェクトを保存

 上記で作成したバケットに画像をドロップ

記録

APIユーザーのエンドポイント、API Key、Secret Keyを後で使用します。

仮想サーバ(IDCFのサーバでなくても可能)

s3cmdインストール

参照
wget http://sourceforge.net/projects/s3tools/files/s3cmd/1.5.2/s3cmd-1.5.2.tar.gz
tar zxvf s3cmd-1.5.2.tar.gz
cd s3cmd-1.5.2
python setup.py install
yum -y install python-dateutil
s3cmd –version
=>s3cmd version 1.5.2

s3cmd設定

s3cmd –configure
Access Key:管理画面でみたAPI Key
Secret Key:管理画面でみたSecret Key
Default Region [US]:ap-northeast-1 ←東京らしい
Encryption password:
Path to GPG program [/bin/gpg]:
Use HTTPS protocol [No]:Yes
HTTP Proxy server name:
Test access with supplied credentials? [Y/n] n
Save settings? [y/N] y
Configuration saved to ‘/root/.s3cfg’

s3cmdアカウント設定

cd ~
vim .s3cfg
—————
host_base = 管理画面エンドポイント
host_bucket = %(bucket)s.管理画面エンドポイント
signature_v2 = True
—————

コマンド実行

s3cmd ls バケット参照
s3cmd setacl –acl-public s3://バケット名/オブジェクト名
コマンド参照

【Rails】RailsにAdminLTEを導入【AdminLTE】

0

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

RailsにAdminLTEを導入する手順を紹介します。

1. AdminLTEのダウンロード

ここからダウンロード (2015/03/31現在)

2. ダウンロードしたファイルを解凍し、設置

「dist」と「plgins」のフォルダを使用。下記に設置します。 ※/lib/にパスが通っている必要がある

RAILS_ROOT/lib/assets/adminlte/

3. 読み込む必要があるjsとcssをapplication.jsとapplication.cssに記入(導入するファイルは解凍したフォルダのindex.htmlを参考すると良い)

$ vim app/assets/javascripts/application.js

・
・
・
//= require jquery
//= require jquery_ujs
//= require bootstrap
//= require adminlte/dist/js/app.js                         ここら辺から読み込む必要があるファイルを記述
//= require adminlte/dist/js/raphael-min
//= require adminlte/plugins/morris/morris.min
//= require adminlte/plugins/sparkline/jquery.sparkline
//= require adminlte/plugins/jvectormap/jquery-jvectormap-1.2.2.min
//= require adminlte/plugins/jvectormap/jquery-jvectormap-world-mill-en
//= require adminlte/plugins/knob/jquery.knob
//= require adminlte/plugins/daterangepicker/daterangepicker
//= require adminlte/plugins/datepicker/bootstrap-datepicker
//= require adminlte/plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.all.min
//= require adminlte/plugins/slimScroll/jquery.slimscroll.min
・
・
・

$ vim app/assets/stylesheets/application.css

 ・
 ・
 ・
 *= require adminlte     ←このファイルにAdminLTEの読み込むcssをまとめる
 ・
 ・
 ・

$ vim app/assets/stylesheets/adminlte.css.scss

@import url('https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css');
@import url('http://code.ionicframework.com/ionicons/2.0.0/css/ionicons.min.css');
@import 'dist/css/AdminLTE.min';
@import 'dist/css/skins/_all-skins.min';
@import 'plugins/iCheck/flat/blue';
@import 'plugins/morris/morris';
@import 'plugins/jvectormap/jquery-jvectormap-1.2.2.css';
@import 'plugins/datepicker/datepicker3';
@import 'plugins/daterangepicker/daterangepicker-bs3';
@import 'plugins/bootstrap-wysihtml5/bootstrap3-wysihtml5.min';

以上で完了

flashのメッセージ以外の使い道

0

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

社会人2年目になった、まだまだ技術的に新人域を抜けきっていない気がする中西です。

今回は、flashが使えるのは、メッセージだけでないことについて紹介します。

環境

Rails 4.1.4

Ruby 2.1.2

flashに渡せる値

flashトとは、セッションに一時的にメッセージを入れておける機能です。

エラー等のメッセージを値として渡し、リダイレクト先でそのメッセージを表示する使い方が多いと思います。

test_controller.rb
flash[:notice] = "テストメッセージです。"
flash[:error] = "エラーが発生しました。"
redirect_to test_path
test/show.html.slim
= flash[:notice]
= flash[:error]

しかし、flashの実態はHashです。

そのため、そのため、任意のキーを使用することができます。

したがって、メッセージの他に、独自に設定したキーで、名前やフラグを値として渡すこともできます。

 名前の例

test_controller.rb
flash[:test_name] = "welcome"
redirect_to test_path
test/show.html.slim
- if flash[:test_name]
  = link_to "#{flash[:test_name]}", "http://www.test.#{flash[:test_name]}"

 flagの例

test_controller.rb
flash[:test_view_flag] = true
redirect_to test_path
test/show.html.slim
- if flash[:test_view_flag]
  | テスト経由

まとめ

flashを一時的なsessionとしてうまく使うと、様々なことができそうです。

参照

http://guides.rubyonrails.org/action_controller_overview.html#the-flash

http://thepugautomatic.com/2012/08/the-rails-flash/

CentOS MuninとプラグインのInstall方法

0

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

rickNo36です。
最近CentOS7でmunin入れたのでインストール方法を記載します。

環境

CentOS7
Apache(yumにてinstall済)

Muninインストール

yum install epel-release
yum install munin munin-node
systemctl start munin-node
apachectl configtest
systemctl restart httpd.service

アクセス

http://xxx/munin
これだけ、とても簡単

ラグイン追加

設定されているplugin
/etc/munin/plugins
用意されているplugin
/usr/share/munin/plugins

追加例:Apache

# アクセス数
ln -s /usr/share/munin/plugins/apache_accesses /etc/munin/plugins/apache_accesses
# プロセス数
ln -s /usr/share/munin/plugins/apache_processes /etc/munin/plugins/apache_processes
# データ転送量
ln -s /usr/share/munin/plugins/apache_volume /etc/munin/plugins/apache_volume
cd /etc/httpd/conf
vim httpd.conf
———————
nclude conf.modules.d/*.conf
下記追加
ExtendedStatus On

SetHandler server-status
Require local

———————
vim extra/httpd-vhosts.conf
———————

ServerName hoge

RewriteEngine On
RewriteRule ^/balancer-manager$ /balancer-manager/ [L]
RewriteRule ^/server-status$ /server-status/ [L]
RewriteRule ^(.*)$ [R=404,L]

SetHandler server-status
Order deny,allow
Deny from all
Allow from 127.0.0.1

SetHandler balancer-manager
Order Deny,Allow
Deny from all
Allow from 127.0.0.1

———————
systemctl restart httpd.service
systemctl restart munin-node

Rails4 + Mongoidでデータ収集するあれこれ

0

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

MongoDBのObjectId型を物検索するのににこれたたいいかか、

  1. ObjectId型の_idを着する
  2. $ whereディレクター
  3. 分類の指定方法
  4. モンゴイドのセレクション展開の指定方法
  5. 集合体group_by?

 ObjectId型の_idを着検索する

▼サンプル

> db.members.find();
{"_id":ObjectId( "54daf7307da4107a3694f5af")、 "name": "hoge"、 "count":1、 "status":1123、 "created_at":ISODate( "2015-02-11T12:20:18.766Z" )、 "updated_at":ISODate( "2015-02-11T12:20:18.766Z")、 "deleted_at":0}

MongoDBの_idはObjectId型要素ので、

ざに「/ ^ 54d /」九なぷあり勝しませんでした。

文字列型に変換して「ObjectId( “」からなっ一致するようにすししししますがううーん。

Member.where( '$ where' => 'function(){return this._id.match(/ ^ ObjectId \(\ "%s /)}'%Regexp.escape(@ search.id))

とたたら定的に変換する

http://docs.mongodb.org/manual/reference/method/ObjectId.valueOf/

ObjectId.valueOf()を外とカッコ内の文字列をしてせます。

ObjectId( "54daf7307da4107a3694f5af")。valueOf()=> "54daf7307da4107a3694f5af"

をしたばば↓このように書けます。(※ももするばができます)

Member.where( '$ where' => 'function(){return this._id.valueOf()。match(/ ^%s /);}'%Regexp.escape(@ search.id))
Member.where( '$ where' => 'this._id.valueOf()。match(/ ^%s /)'%Regexp.escape(@ search.id))

省略、検索条件が$ where省略の属性は$ whereも省略可能。

Member.where( 'this._id.valueOf()。match(/ ^%s /)'%Regexp.escape(@ search.id))

使いまわすそうえる、モデルの方でスコープにPlanskお実行かと。

コントローラ
Member.id(@ search.id)

モデル
scope:id、->(id){'this._id.valueOf()。match(/ ^%s /)'%Regexp.escape(id)if id.present?}

mongoシェルメソッド

http://docs.mongodb.org/manual/reference/method/

IntegerもtoString()で文字列に変換すし、正規表現での検索が可能です。

 $ where

ここ$ whereです。

http://docs.mongodb.org/manual/reference/operator/query/where/

た内でjavascriptを実行したいいは$ whereをするようにとのこと。

公式のコメント通りですが、ざ解説

使いろろはえくらありようです。

  • 陳ができない
  • 絶された内でありない(表レベルであること)
  • $ whereBEGINの条件と國可能。先に$ whereBEGINの変形を評価する。(その結婚が0の属性$ where内は評価されない)
  • ファル3微妙
  • ず$ whereえずにありならそっちをててね
  • でできてなら、$ whereEVERの条件もいして、$ whereでで立てをすると、でなるのできる

検索条件の指定方法

ももな指定方法がますます。

モンゴイドの選択

http://mongoid.org/en/origin/docs/selection.html#standard

http://mongoid.org/en/origin/docs/selection.html#symbol

▼名前に「ほ」をするもの

(文字列型のフィールドフィールド正規表現表現が有効です)

Member.where(name:/ ho /)

▼1と3コンポーネント

Member.where(:status.all => [1,3])

▼1と3をこれ

Member.where(:status.in => [1,3])

▼1と3をこれないもの

Member.where(:status.nin => [1,3])

▼ステータスがnilfalseRefer

Member.where(:status.exists => true)

▼ステータスが1

Member.where(:status.gt => 1)

▼ステータスが1>

Member.where(:status.gte => 1)

▼ステータスが3

Member.where(:status.lt => 3)

▼ステータスが3

Member.where(:category.lte => 3)

▼ステータスが1

Member.where(:category.ne => 1)

▼updated_atg2015-02-1110:15:00.000円

Member.where(:updated_at.gte => '2015-02-11 10:15') 

▼updated_atが2015-02-1110:15:01.000前前

 ミリ秒表ので、「2015-02-11 10:15.725」等男勝表1秒後のdatetimeで

Member.where(:updated_at.lt => Time.at(Time.parse( '2015-02-11 10:15')。to_i + 1))

モンゴイドのセレクション展開の選択方法

Model.where()の術モンゴイドのセレクションダブルモデル、

なりのように指定する、MongoDBのシェルとありのくらがいようです。

db = Mongoid :: Sessions.default
コレクション= db [:models]
collection.find()

矯正

Model.collection.find()

※「Mongoid :: Sessions.default」はconfig / mongoid.ymlの設定内容?

もはもももをあります。

mongoDBdb.models.find({count:{‘$ gte’ => 1}})
RailsModel.where(:count.gte => 1)モンゴイド::基準
RailsModel.collection.find(count:{‘$ gte’ => 1})原付::クエリ

オブジェクトオブジェクトオブジェクトオブジェクトオブジェクトオブジェクト。

基準= Model.where(:count.gte => 1)

ppcriteria.class
 => Mongoid :: Criteria

ppcriteria.class.ancestors
 => [Mongoid :: Criteria、
 Mongoid :: Sessions :: Options、
 Mongoid :: Criteria :: Scopable、
 Mongoid :: Criteria :: Modified、
 Mongoid :: Criteria :: Marshalable、
 Mongoid :: Criteria :: Inspectable、
 Mongoid :: Criteria :: Findable、
 Origin :: Queryable、
 Origin ::オプション、
 Origin :: Selectable、
 Origin :: Aggregable、
 起源::マージ可能、
 Mongoid :: Contextual、
 可算、
 オブジェクト、
 Delayed :: MessageSending、
 Mongoid :: Extensions :: Object、
 BSON :: Object、
 Origin :: Extensions :: Object、
 ActiveSupport :: Dependencies :: Loadable、
 PP :: ObjectMixin、
 V8 :: Conversion :: Object、
 JSON :: Ext :: Generator :: GeneratorMethods :: Object、
 カーネル、
 BasicObject]
moped = Model.collection.find(count:{'$ gte' => 1})

pp moped.class
 =>原付::クエリ

pp moped.class.ancestors
 => [Moped :: Query、
 Mongoid :: QueryCache :: Query、
 Mongoid :: QueryCache :: Cacheable、
 可算、
 オブジェクト、
 Delayed :: MessageSending、
 Mongoid :: Extensions :: Object、
 BSON :: Object、
 Origin :: Extensions :: Object、
 ActiveSupport :: Dependencies :: Loadable、
 PP :: ObjectMixin、
 V8 :: Conversion :: Object、
 JSON :: Ext :: Generator :: GeneratorMethods :: Object、
 カーネル、
 BasicObject]

 aggregatedegroup_byする

MongoDBのシェルがされて↓ここにあるメソッドもメソッドを…

http://docs.mongodb.org/manual/reference/method/

data = Member.collection.aggregate({'$ group' => {'_id' => '$ count'、 'count' => {'$ sum' => 1}}})

=> [{"_ id" => 3、 "count" => 1}、{"_ id" => 2.0、 "count" => 2}、{"_ id" => 1.0、 "count" => 1} ]

できました!

pp data.class
 =>配列

pp data.class.ancestors
 => [配列、
 Mongoid :: Extensions :: Array、
 BSON :: Array、
 BSON :: Encodable、
 Origin :: Extensions :: Array、
 カラム化、
 V8 :: Conversion :: Array、
 JSON :: Ext :: Generator :: GeneratorMethods :: Array、
 可算、
 オブジェクト、
 Delayed :: MessageSending、
 Mongoid :: Extensions :: Object、
 BSON :: Object、
 Origin :: Extensions :: Object、
 ActiveSupport :: Dependencies :: Loadable、
 PP :: ObjectMixin、
 V8 :: Conversion :: Object、
 JSON :: Ext :: Generator :: GeneratorMethods :: Object、
 カーネル、
 BasicObject]

SQL特徴group_byとは別ですが、比較にらない

Enumerable#group_byがますます。

(Mongoid :: CriteriaもMoped :: QueryもEnumerableファンを実装してます)

Member.where(:count.gte => 1).group_by {| member | member.status}
Member.collection.find(count:{'$ gte' => 1})。group_by {| member | member [:status]}

jsファイルを渡して、言いからコマンドは

mongo sample_dev〜 / mongo / batch.js

SQLとの比較表も定にありました。

決定に。

http://docs.mongodb.org/manual/reference/sql-comparison/

浮動小数点誤差対策

0

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

小数の計算が必要な場面というのは多々ある。Rubyでは、例えば「1.5」と書けばFloatクラスのオブジェクトになるが、Floatクラスで注意しなければならないのが、浮動小数点誤差である。

この浮動小数点誤差に関して、原因、注意点、対策方法を纏める。

 浮動小数点誤差が発生する必要条件

Floatクラスのオブジェクトは内部的に2進数にて表現される。

2進数で表現した場合に無限小数となる数の演算は全て、浮動小数点誤差が発生する可能性がある。

「2進数で表現した場合に有限小数となる」とは、既約数で表現した場合の分母に2以外の素因数がない事である。

以下に例を示す。

1.5 = 3/2 … 分母を素因数分解すると2^1なので浮動小数点誤差は発生しない。

0.75 = 3/4 … 分母を素因数分解すると2^2なので浮動小数点誤差は発生しない。

0.1 = 1/10 … 分母を素因数分解すると2^1 * 5^1 なので浮動小数点誤差が発生する可能性がある。

 浮動小数点誤差が発生しがちな計算の例

浮動小数点誤差が発生しがちな計算は、例えば消費税の計算がある。

浮動小数点誤差の知識はあったとしても、意識していないと、225円の商品の税込み価格計算で「225 * 1.08」の様に書いてしまいがちである。

この計算は「243.0」を期待しているが、実際は「243.00000000000003」となる。

この結果の問題点は、税込価格が端数切り上げで計算している場合に、本来の価格より1円高くなってしまう点である。

(225 * 1.08).ceil

=> 244

 浮動小数点誤差に対する対策

対策1 : 演算の順序を工夫する

小数は分数で表現できる。分数は除算で表現できる。

整数の集合は、加算・減算・乗算において閉じているので、

除算を最後の1回のみ行う様に式を置き換える。

(225 * 108 / 100.0).ceil

=> 243

デメリット

・有効になる場面が限られている。

・複雑な演算には向かない。

対策2 : BigDecimalクラスを使用する

人間が数値として表現できる数は殆どが10進法の有限小数または整数である。

10進法で表記した各桁の精度を落とさずに演算が可能なBigDecimalクラスを使用すれば、有効桁数がそれ程多くない数同士の加算・減算・乗算では誤差は発生しない。

(恐らく電卓と同じ様なロジックになっていると思われる)

(225 * BigDecimal(“1.08”)).ceil

=> 243

デメリット

・分子の素因数に2, 5以外の数を持つ既約有理数による除算は誤差が発生する可能性がある。

・String型に変換する必要がある。

・遅い。

対策3 : Rationalクラスを使用する

有理数の集合は四則演算において閉じている(0による除算を除く)ので、

既約有理数の分母分子の精度を落とさずに演算が可能なRationalクラスを使用すれば、有理数同士の四則演算では0による除算を除いて誤差は発生しない。

(225 * 1.08r).ceil

=> 243

デメリット

・数値的に求めるには型変換をしなければならない。

 Rationalクラスを使用する場合の注意点

Rationalクラスは丸め誤差の影響を考えなくて良い便利なクラスであるが、RationalオブジェクトとFloatオブジェクトの四則演算を行う場合、RationalオブジェクトはFloatオブジェクトに暗黙型変換されて演算される為、注意が必要となる。

(例えば、「225.0 * 1.08r」は「225.0 * 1.08」と同じになってしまう)

つまり型の優先度としては、Rationalは、Integerより強く、Floatより弱い様である。

Rationalクラスの利点を活用するのであれば、Floatオブジェクトをto_rメソッドによってRationalオブジェクトにしてから演算を行う必要がある。

テストデータ作成時の注意点

0

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

テストデータで決定する数値は無作為に決定していると、予期せぬ矛盾が発生する事がある。

その注意点を記述する。

 問題点

例えば商品のテストデータを作成する場合を考える。

商品の値段は当然自然数である。

これは税抜価格であっても税込価格であっても当てはまるが、1点異なる部分がある。

税抜価格は単純に決定された値であり、この値を導いている値はないのに対して、税込価格は税抜価格から導かれて決定する。

つまり税込価格は税抜価格の関数になっている。

この関数をfとした場合、f(x)は

f(x) = floor(1.08x)

と表現できる。このfはN→Nの写像になっており、下記の様にF(y)を定義すると、Fはfの逆関数になる

F(y) = ceil(y/1.08)

テストデータとして、税込価格を決定する場合、ランダムに自然数が発生する様なプログラムでは、例えば1700という値になる可能性がある。これを関数Fによって

F(1700) = 1575

と税抜価格を算出したとすると、実際のf(1575)は1701の為、誤差が発生する。

 原因

この誤差はfの逆関数が存在する事と矛盾する様に見えるが、そうではなく、この写像fがN→Nにおいて全射でない点が問題である。

このfの定義域はN全体であるが、値域はN全体ではなく、1700というのが値域外の数値である事が原因である。

(つまり f(x) = 1700 を満足するxは存在しない)

 結論

テストデータを作成する際は、それを導いている値があるかないかを意識する必要がある。

導いている値がある場合、その関数の写像において全射であるかを考え、

全射でない場合は、値域外の数値にならない様、データを工夫する必要がある。

devise ログイン後のリダイレクトについて(POST編)

0

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

Rails の Devise でログイン後にログインする直前のPOSTリクエストにリダイレクトさせます。

 目標

RailsでPOSTリクエストではリダイレクトできないため、deviserログイン後はGETリクエストのみリダイレクトできます。

下記の場合、お気に入り登録が出来ないし、お気に入り一覧にリダイレクトできないです。

未ログイン⇒商品詳細⇒お気に入り登録(POST)⇒ログイン画面⇒ログインする⇒お気に入り一覧

 対策

POSTなので、直接遷移が出来なくて、after_login_to_postを作成して、その後自動サブミットすれば、POSTリクエストにリダイレクトを実現します。

動き:

未ログイン⇒商品詳細⇒お気に入り登録(POST)⇒ログイン画面⇒ログインする⇒after_login_to_post⇒真っ白ページ(自動サブミット)⇒お気に入り一覧

 セッションに POST リクエストのパラメータを保存

POSTリクエスト中で、POSTリクエストのパラメータ、コントローラ、アクションをセッションに保存します。

class CustomersFavoritesController < ApplicationController
  before_action :authenticate_customer!, except: :create
  def create
    if current_customer.signed_in?
      current_customer.favorites.create(product_id: params[:id]) unless current_customer.favorites.find_by(product_id: params[:id])
      redirect_to action: :index
    else
      #セッションに POST リクエストのパラメータを保存
      session[:after_login_to_post] = {
        controller_name: controller_name,
        action_name: action_name,
        params: { id: params[:id] }
      }
      redirect_to new_customer_session_path
    end
  end
end

 ログイン後のリダイレクトをカスタマイズ

ログイン後、セッションにPOST情報があれば、after_login_to_post_pathにリダイレクトさせます。

class CustomersSessionsController < Devise::SessionsController
  # ログイン後、リダイレクト先メッソドafter_sign_in_path_forをオーバーライドする。
  def after_sign_in_path_for(resource)
   #お気に入り登録(POST)の場合、after_login_to_post_pathに遷移します。
    if session[:after_login_to_post]
      after_login_to_post_path
    else
      super
    end
  end
end

 ログイン後にセッション情報からログイン直前のページ情報を復元

セッション情報から、POSTリクエストの情報を取得し、設定します。

class TopController < ApplicationController
  def after_login_to_post
   #セッションにafter_login_to_postがあれば、
    if session[:after_login_to_post]
      @back_controller = session[:after_login_to_post]['controller_name']
      @back_action = session[:after_login_to_post]['action_name']
      @params = session[:after_login_to_post]['params']
   #セッションをクリア
      session[:after_login_to_post] = nil
      render :after_login_to_post, layout: false
    else
      #直接アクセスが来たら、TOPに遷移する。
      redirect_to top_path
    end
  end
end

 真っ白VIEW

after_login_to_postのVIEWは自動サブミットします。

一瞬だけ真っ白なページが表示されますが、

自動サブミットによって、POST送信が出来ます。

after_login_to_post.slim

doctype transitional
html[xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja" dir="ltr"]
  head
    meta[http-equiv="Content-Type" content="text/html; charset=UTF-8"]
    title = '戻る'
  body onLoad="document.forms[0].submit()"
    - if @back_controller && @back_action && @params
      = form_tag( { controller: @back_controller, action: @back_action }, { method: 'post' } ) do
        = hidden_field_tag 'id', @params['id']
        <noscript>
        = button_tag '戻る', class: 'btn_sub'
        </noscript>

 routes

  ### ログイン後、POSTアクションに戻る ###
  get 'after_login_to_post', to: 'top#after_login_to_post'

 参照

http://easyramble.com/cannot-post-redirect-on-rails.html

devise ログイン後のリダイレクトについて(GET編)

0

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

Rails の Devise でログイン後はログインする直前のGETリクエストにリダイレクトさせます。

 デフォルトの動き

before_action :authenticate_user!を通して、ログイン画面に遷移する場合、ログイン後、ログイン直前の画面に戻します。

下記のようなredirect_toを利用して、ログイン画面に遷移する場合、ログイン後、元の画面に戻らなくて、root_pathに遷移します。

unless user_signed_in?

redirect_to new_user_session_path

 対策

ログイン直前のリクエストをセッションに保存しておいて、

ログイン成功後のafter_sign_in_path_forメソッドをオーバーライドして、戻る先をセッションから取得して設定します。

注意:ログイン関連のリクエストをセッションに入れないこと。

 ログイン直前のリクエストをセッションに保存

applictaion_controller.rb

class ApplicationController < ActionController::Base
  after_action  :store_location
  def store_location
    if (request.fullpath != "/users/sign_in" &&
        request.fullpath != "/users/sign_up" &&
        request.fullpath !~ Regexp.new("\\A/users/password.*\\z") &&
        !request.xhr?) # don't store ajax calls
      session[:previous_url] = request.fullpath 
    end
end

 deviseのリダイレクをカスタマイズ

devise標準のafter_sign_in_path_forをオーバーライドして、ログイン後のリダイレクト先はカスタマイズします。

SessionsControllerをカスタマイズしなければ、applictaion_controller.rbに追加します。

SessionsControllerをカスタマイズすれば、カスタマイズしたコントローラ中に追加します。

class UsersSessionsController < Devise::SessionsController
  ・・・
  def after_sign_in_path_for(resource)
    if (session[:previous_url] == root_path)
      super
    else
      session[:previous_url] || root_path
    end
  end
end

 参照

https://github.com/plataformatec/devise/wiki/How-To:-Redirect-back-to-current-page-after-sign-in,-sign-out,-sign-up,-update

http://easyramble.com/friendly-forwarding-redirect-on-devise.html

Rails 4.2 にアップデートすると他のマシンからサーバーにアクセスできなくなる?

0

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

一瞬ハマったのでメモとして書き残します。

 Rails 4.2 にアップデートすると他のマシンからサーバーにアクセスできなくなる?

「rails s」で起動するサーバーの IP アドレスはもともとは「0.0.0.0」でしたが、

Rails 4.2 から「localhost」になっているようです。

この影響で他のマシンや、ホスト OS から立ち上げたサーバーにアクセスできない!

ということがあります。

 解決方法

いままで通りにアクセスできるようにするには簡単で、「rails s -b 0.0.0.0」のような

オプションを指定するだけです。オプションの詳細は「rails –help」で確認できます。

rails s
#=> 'http://localhost:3000' で起動するため外部からアクセス不可
rails s -b 0.0.0.0
#=> 'http://0.0.0.0:3000' となり、いままで通りアクセス可能

 どこで変わったの?

Rails のサーバー部分の実装に使われている Rack に、デフォルト IP の変更があったみたいです。単純に「その方が安全だよね」という話ですね。

参考: https://github.com/rack/rack/commit/28b014484a8ac0bbb388e7eaeeef159598ec64fc

Railsの可逆暗号化方法 ActiveSupport::MessageEncryptorの使用方法

0

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

Railsで簡単に可逆暗号化方法を提供しています。ActiveSupport::MessageEncryptorです。

今日は、ActiveSupport::MessageEncryptorの使い方についてご紹介します。


①プレーンテキスト(message)、鍵(secret)を以下のようにする。
2.1.1 :001 > message = ‘test’
 => “test”
2.1.1 :002 > secret = SecureRandom::hex(50)
 => “06e40eedc81374bbef74bb2ac1c4a5d9928287c5146949d8d56a566495a5c8ecb8499177bce629d55a8e485d7dc66a183d47”
2.1.1 :003 >

②ecryptorの宣言(AES-256, Cipher Block Chainingで暗号化)
2.1.1 :003 > encryptor = ::ActiveSupport::MessageEncryptor.new(secret, cipher: ‘aes-256-cbc’)
 => #, @serializer=Marshal>

③暗号化(encrypt_and_sign)
2.1.1 :006 > encrypt_message = encryptor.encrypt_and_sign(message)
 => “YjBvd0dOemF5YWN3eHI1OXRUUmk5Zz09LS11OXR6akNlK0IxWUFTdyt5YTlQUWNBPT0=–737d367a2db323c2890084a3139e1e39e7e16c5d”

④復号化(decrypt_and_verify)
2.1.1 :007 > encryptor.decrypt_and_verify(encrypt_message)
 => “test”

以上は、通常の使い方です。

でも、ここで、注意しないとできないのは、
③暗号化(encrypt_and_sign)
2.1.1 :006 > encrypt_message = encryptor.encrypt_and_sign(message)
は、実行するたびに、結果が変わります。
2.1.1 :012 >   encrypt_message = encryptor.encrypt_and_sign(message)
 => “bjZOekxmZnlqRUtzT3V4NFBNb2lqdz09LS1FVEFVZ0xsTUFRK2U2dWZtUmRQZ0RBPT0=–8f8882f9d87d60d7ac85069cd7d2d58d105bbf25”
2.1.1 :013 > encrypt_message = encryptor.encrypt_and_sign(message)
 => “am1sQzI5SUhweG43TFVrK05hb3lPdz09LS1Cb0d3SHZKa2dobjQxU3ZLWVRpd3B3PT0=–b951c5800a881b79d309cace046349b41866936b”

ソースを見れば、原因がわかると思いますが、
.rvm/gems/ruby-2.1.1/gems/activesupport-4.1.4/lib/active_support/message_encryptor.rb

 67
 68     def _encrypt(value)
 69       cipher = new_cipher
 70       cipher.encrypt
 71       cipher.key = @secret
 72
 73       # Rely on OpenSSL for the initialization vector
 74       iv = cipher.random_iv #### => ここです。ivが実行するたびにランダムで生成する
 75
 76       encrypted_data = cipher.update(@serializer.dump(value))
 77       encrypted_data << cipher.final
 78
 79       “#{::Base64.strict_encode64 encrypted_data}–#{::Base64.strict_encode64 iv}”
 80     end
 81

こうすると、暗号化の結果を別のところで、使いたい場合、ivを固定させるカスタマイズする必要があります。

例えば:

 44     def initialize(secret, *signature_key_or_options)
 45       options = signature_key_or_options.extract_options!
 46       sign_secret = signature_key_or_options.first
 47       @secret = secret
 48       @sign_secret = sign_secret
 49       @cipher = options[:cipher] || ‘aes-256-cbc’
 50       @verifier = MessageVerifier.new(@sign_secret || @secret, :serializer => NullSerializer)
 51       @serializer = options[:serializer] || Marshal
 52       @iv = options[:iv]

53     end

 68
 69     def _encrypt(value)
 70       cipher = new_cipher
 71       cipher.encrypt
 72       cipher.key = @secret
 73
 74       # Rely on OpenSSL for the initialization vector
 75       iv = @iv || cipher.random_iv
 76
 77       encrypted_data = cipher.update(@serializer.dump(value))
 78       encrypted_data << cipher.final
 79
 80       “#{::Base64.strict_encode64 encrypted_data}–#{::Base64.strict_encode64 iv}”
 81     end

こうすると、実行するたびに結果が同じです。


irb: warn: can’t alias context from irb_context.
2.1.1 :009 > message = ‘test’
 => “test”
2.1.1 :010 >
2.1.1 :011 > message.encoding
 => #
2.1.1 :012 >
2.1.1 :013 >   secret = SecureRandom::hex(50)
 => “b9aa9c2b5af0349c3b334ead4f8b1e2a3ff42921425bf2df1aefd3d3aefd4b4b5ce816e38fdcad231b985dee8dbf40200d06”
2.1.1 :014 > iv = SecureRandom::hex(20)
 => “fa4ad72743b789ff188c7c50a56b3b563e1e68e9”
2.1.1 :015 > encryptor = ::ActiveSupport::MessageEncryptor.new(secret, cipher: ‘aes-256-cbc’, iv: iv)
 => #, @serializer=Marshal, @iv=”fa4ad72743b789ff188c7c50a56b3b563e1e68e9″>
2.1.1 :016 > encrypt_message = encryptor.encrypt_and_sign(message)
 => “b0NGSXRYQVY2OCs4OS8vcDcrYU12Zz09LS1abUUwWVdRM01qYzBNMkkzT0RsbVpqRTRPR00zWXpVd1lUVTJZak5pTlRZelpURmxOamhsT1E9PQ==–c249ada02f27b5482b682537180c751e266bd608”
2.1.1 :017 >
2.1.1 :018 >   encrypt_message = encryptor.encrypt_and_sign(message)
 => “b0NGSXRYQVY2OCs4OS8vcDcrYU12Zz09LS1abUUwWVdRM01qYzBNMkkzT0RsbVpqRTRPR00zWXpVd1lUVTJZak5pTlRZelpURmxOamhsT1E9PQ==–c249ada02f27b5482b682537180c751e266bd608”
2.1.1 :019 >
2.1.1 :020 >   encrypt_message = encryptor.encrypt_and_sign(message)
 => “b0NGSXRYQVY2OCs4OS8vcDcrYU12Zz09LS1abUUwWVdRM01qYzBNMkkzT0RsbVpqRTRPR00zWXpVd1lUVTJZak5pTlRZelpURmxOamhsT1E9PQ==–c249ada02f27b5482b682537180c751e266bd608”
2.1.1 :021 >


もう一つ、目で見るとき、同じ”test”で、encodingが違うと、暗号化の結果が変わります。


2.1.1 :023 > message.encoding
 => #
2.1.1 :024 > encrypt_message = encryptor.encrypt_and_sign(message)
 => “b0NGSXRYQVY2OCs4OS8vcDcrYU12Zz09LS1abUUwWVdRM01qYzBNMkkzT0RsbVpqRTRPR00zWXpVd1lUVTJZak5pTlRZelpURmxOamhsT1E9PQ==–c249ada02f27b5482b682537180c751e266bd608”
2.1.1 :025 >
2.1.1 :026 > message_sjis = message.force_encode_utf8_to_sjis
2.1.1 :028 > message_sjis.encoding
 => #
2.1.1 :031 > message_sjis == message
 => true
2.1.1 :033 > encrypt_message_sjis = encryptor.encrypt_and_sign(message_sjis)
 => “NkRsUTVWU0M5N3Z4Sm1VUGI3czNsY3poYnRyVGJhdHB3NE5IeE1qMGdQaz0tLVptRTBZV1EzTWpjME0ySTNPRGxtWmpFNE9HTTNZelV3WVRVMllqTmlOVFl6WlRGbE5qaGxPUT09–daf7901c7949383921e3b1b47dc389defd40892d”
2.1.1 :036 > encrypt_message_sjis == encrypt_message
 => false

プロジェクトにRubocopを導入することのメリットとデメリット

0

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

Ruby でコーディングルールを設けるのに役立つ gem「Rubocop」をチームで利用するメリットとデメリットについて紹介します。

 はじめに

チームでプログラミングをするときに立ちはだかる壁のひとつに「コードの統一」があると思います。

どんなに有能な人が複雑な機能を瞬く間に作り上げたとしても、チームで開発し保守する以上は、それが他人に理解できるものでなければあまり意味がありません。

なぜならばたったひとりしか理解できないコードは、その人にしか保守できないからです。

この「コードの統一」を図るために出てくるのがコーディングルールかと思います。

コーディングルールがあれば、皆が理解できるコードを共有し合うことができます。

が、このルールを作るのも守るのも面倒くさい!

そこで導入を検討したのが Rubocop です。

この gem は Ruby のコードを静的に評価して「ここはこう書いた方が良いよ」というアドバイスをくれるというものです。

「これがあればコーディングルールなしにキレイなコードを担保できるかも?!」という希望をもとに、新しいプロジェクトで実際に使ってみることにしました。

この記事では、Rubocop をチームで運用してみて感じたメリットとデメリットを紹介したいと思います。

 Rubocopとは

Rubocop 自体の説明は割愛します。

どんなものかをご存知でない方は下記記事などをご参照ください。

Rubocopを使ってコーディングルールへの準拠チェックを自動化

http://qiita.com/yaotti/items/4f69a145a22f9c8f8333

 Rubocopの運用方法

今回のプロジェクトでは Rubocop を自動テストの一端として組み込む形で利用しました。

この自動テストと GitLab のマージリクエスト (GitHub でいうプルリクエスト) を組み合わせることで

「テストが成功するまでマージできませーん」という流れができるので、 [45/1090]

Rubocop によるコーディングルールを守らざるを得ない環境を作ることができます。

ただし、Rubocop のデフォルト設定はチームで運用するには敷居が高い内容となっているので最初にある程度縛りを緩くしておき、

また途中からでもメンバーからの意見が多ければ設定を変更する体制を取りました。

 Rubocopのメリット

ようやく本題になります。

メリットとしては今回挙げるのは下記の3つになります。

1. コーディングスタイルをある程度統一できる

長いメソッド/モジュールの分割をほぼ強制されるので、

数百行のメソッドや数千行のモジュールがプロジェクト内に生まれることがありませんでした。

2. Ruby 歴が短い人、改善意識の高い人の良い矯正になる

Rubocop が提案してくる内容を素直に受け止められる人は、Rubocop 色に染まることで、

どんどん怒られにくいコードを自発的に書く方向に矯正できているように感じました。

メソッドひとつひとつがシンプルになったり、早い時期からメソッド郡のモジュール化を考えたりするようになったり、と良い感じです。

3. Ruby の新しい構文、便利な構文を知る良い機会になる

Rubocop を使っていると「こういう風に書きたいならこう書いた方が分かりやすいよ」という提案を受けることがあります。

これによっていままで知らなかったメソッドや慣習などを学ぶ機会を得ることができました。

 Rubocopのデメリット

Rubocop 単体のデメリット、というよりも、チームで使う際の注意点というような観点になります。

メリットと同じく、こちらも3つ挙げています。

1. コーディングスタイルをある程度しか統一できない

「メソッドが長いなら分ければ良いんでしょ」と安易に考え、

「method1」「method2」「method3」…のように無意味な名前でメソッドを分割するケースがありました。

残念ながらいま現在の Rubocop はそれを良しとして通してしまうため、

ただ単にメソッド1つの長さが短いだけの、本質的に意味のないメソッド分割を許容してしまうことがあります。

この辺は、「なぜ Rubocop を使うのか」についてチームで共通認識を持っておくべきだと感じました。

2. Ruby 歴が長い人、改善意識の低い人の阻害となる

Rubocop の提案を良しとしない人(たとえば手続き的な記述で書いた方が、

それでメソッドが長くなったとしても分かりやすいと考える人、など)とは、当然衝突することになります。

これは本人にとってもストレスですし、嫌々で書いたコードが良いものになるとも思えません。

ただしこれはどんなコーディングルールにも共通することなのかなと思います。

基本的にはルールを尊重しつつ、どうしてもという場合にはルールを見直していくしかないと思います。

3. 急いでいるときなどに本質的でない指摘で開発が止まる

Aさん「急ぎでこの処理を追加して欲しいんだけど」

Bさん「1行追加するだけでできました!」

Rubocop「メソッドが長いのでエラーです」

Aさん「・・・」

Bさん「・・・」

ということが何度かありました(あと1行だけでも追加すれば Rubocop に怒られる、という罠が巧妙に仕込んである)。

こうなってしまえばリファクタリングを余儀なくされることになるので、後のことを考えれば良いことなのですが、その場では大きなストレスになります。

 さいごに

Rubocop は自分ひとりで利用するのであれば本当に良い味方になると思います。

とくに最近のバージョンはABCメトリクス値の計測も取り入れられて、よりいっそう養成ギブスとしての能力を増しています。

あと、僕個人としては Rubocop が大好きなので心の中でもっと広まれ!と思っています(もっと広まれ!)。

ただしチームやプロジェクトで利用するには使い方に工夫が必要だと感じました。

チームメンバーにコーディングルールの意識をしっかりと共有しておくことである程度は回避可能だと思いますが、もっと良い方法があればぜひ教えてください!

Mac で 暗号化zip を作成する

0

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

ローカル環境で圧縮を行うときは gzip や tar zcf を使うことが多いが

社外の人にファイルを渡すときに暗号化zipを使う。

Windowsでは定番の圧縮解凍ツールに暗号化zipの作成機能があるが

Macは暗号化zip作成ソフトの話は聞かない。

というのもMacで暗号化zipを作成するのに特別なツールをインストールする必要はない。

ターミナルの zip コマンドにオプションをつけるだけで暗号化zipは作成できる。

簡単な操作だが、オプションやファイル名指定の順番をよく忘れてしまい再度検索して試すということが何度かあったので備忘の為に記載する。

単体のファイルを暗号化zipする場合

zip -e target_file.zip target_file.ext

ディレクトリを暗号化zipにする場合

zip -e -r target_directory.zip ./target_directory
-eオプションについて

※ man zip の出力の一部

--encrypt
       Encrypt the contents of the zip archive using a password which is entered on the terminal in response to
       a prompt (this will not be echoed; if standard error is not a tty, zip will exit with  an  error).   The
       password prompt is repeated to save the user from typing errors.

意訳的なもの

--暗号化
     (コマンドの)応答でターミナル上から入力するパスワードを使ってのzipアーカイブ内容の暗号化。
     (このパスワードは画面出力されない;もし標準エラーがttyでなければ、zip(コマンド)はエラー終了する) 
      このパスワード入力は打ち間違いを防ぐ為に再度行う。

仕事で使うScala入門

0

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

プログラミング言語Scalaは、昨今注目が集まっている「関数型プログラミング」を実際のプロジェクトで実践するのに適した有力なプラットフォームです。本エントリではScalaの概要について紹介します。

Scalaとは

スイス連邦工科大学のMartin Odersky教授によって開発されたプログラミング言語です。
Scala(スカラ)という名前は、言語設計の理念であるスケーラブル(Scalable)という言葉からとられています。

特徴について箇条書きでいくつか挙げると

  • JVM(Java仮想マシン)で動作し、Javaで書かれたライブラリやクラスを利用することができる
  • 静的型付け+型推論を行いJVM用のclassファイルにコンパイルされる
  • オブジェクト指向と関数型プログラミングの双方をサポートする(マルチパラダイム)
  • Java8で導入されたラムダ式などの高度な機能をを簡潔な文法で使うことができる

などが挙げられます。

文法は、基本的にJavaやC++などのスタンダードな文法をベースにしていますが、OCamlやHaskellなどの関数型言語に近い簡潔な記述ができるように工夫がこらされています。
弊社で主に使われているRubyにも若干似ているため、親しみが持てるひともいるかもしれません。

本家Webサイト
http://www.scala-lang.org/

Scalaの開発体制

Odersky教授によって開発が始まったScalaですが、現在では教授らが設立したTypesafe社を中心に、 オープンソースソフトウェアとして精力的に開発が続けられています。
https://github.com/scala/scala

開発環境の構築

JDK 1.6以上がインストールされたPCであれば、すぐにインストールして動かすことができます。
http://www.scala-lang.org/download/

また、Macを使っていれば、Homebrewで簡単にインストールすることができます。

しかしなんといってもおすすめは、ScalaをサポートしたIDEをインストールして、その中で開発することです。

IntelliJ IDEAを使いましょう。フルスペックのUltimate Editionは有償ですが、軽量版のCommunity Editionが無料で使えます。
https://www.jetbrains.com/idea/

現在Emacs, Vimを使っているひとは、設定やプラグインによってキーバインドを合わせることができるので問題ありません。私はIdeaVimというVim互換のキーバインドを実現するプラグインを使っています。

長年Eclipseを使っていてもどうしても手放せない、というひとは Scala IDE for Eclipseというプラグインを入れるとScalaが使えるようになるみたいですよ。
http://scala-ide.org/

最初の一歩

お約束として、Hello Worldを書いてみましょう。Scalaのコードはいろいろな方法で実行することができます。

  • コンパイルなしでスクリプトとして実行する
  • REPL(Read Eval Print Loop)の対話環境で実行する
  • .classファイルにコンパイルしてJVMで直接実行する

お仕事で使う場合も、対話環境でいろいろ試しながらコンパイル用のソースコードを書き進めるやり方が便利です。

対話環境でHello World

コマンドラインから scalaコマンドで対話環境を起動します。 scala> という文字列がプロンプトです。


% scala
Welcome to Scala version 2.11.5 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_71).
Type in expressions to have them evaluated.
Type :help for more information.

scala> println("Hello World!")
Hello World!

scala> _

println()というのが画面に文字列を出力するメソッドです。正式には Console.println() ですが、デフォルトでimportされているため直接使うことができます。

スクリプトでHello World

拡張子 .scala のファイルにコードを書き、 scalaコマンドで実行します。


% cat hello.scala
println("Hello World!")
% scala hello.scala
Hello World!

ソースコードに書いたメソッドがそのまま実行されるので、Rubyなどのスクリプト言語とほぼ同じように書くことができます。

コンパイルしてHello World


% cat HelloWorld.scala
object HelloWorld {
  def main(args: Array[String]): Unit = {
    println("Hello World!")
  }
}
% scalac HelloWorld.scala
% ls
HelloWorld$.class	HelloWorld.class	HelloWorld.scala
% scala HelloWorld
Hello World!

これで、ソースコードを書き、コンパイルしてからそれを実行することができました。

Javaでは public static void main(String args[]) というシグニチャのメソッドを含むクラスを定義しますが、 Scalaのクラスにはstaticメソッド(Rubyでいうクラスメソッド)というものが存在せず、同じようなことをするためにはクラス(class)ではなくオブジェクト(object)というものを定義します。
Scalaで定義されたobjectも、コンパイルするとJVM用のクラスファイル(拡張子が.class)にコンパイルされます。
用語が紛らわしいですね。でもすぐに慣れます。

おわりに

Scalaという言語がどういうものなのか、その概要を駆け足で説明しました。
インストールも実行も簡単ですので、是非ためしてみて下さい。

Mac homebrew の imagemagick のバージョンを下げる

0

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

大人の事情(保守の関係など)でどうしても古いバージョンのミドルウェアを使用しなければならないことがある。自分が遭遇したのは imagemagick のバージョンを下げなければならないということだった。これはそのメモ。

とあるアプリケーションを動かす為に枯れた ruby をインストール し

gem もさらにバージョンを下げておいた。

 [kadosaway@imac ~]$ ruby -v
ruby 1.9.3p551 (2014-11-13 revision 48406) [x86_64-darwin13.4.0]
[kadosaway@imac ~]$ gem -v
1.7.2
bundle install を実行しようとするとエラーが発生し必要な gem が整えられなかった。
This installation of RMagick was configured with ImageMagick 6.8 but ImageMagick 6.9.0-3 is in use. (RuntimeError)

どうも versions というツールでバージョンを管理できるらしいが

インストールされていなかったのでリポジトリを追加。

[kadosaway@imac ~]$ brew tap homebrew/boneyard
Cloning into '/usr/local/Library/Taps/homebrew/homebrew-boneyard'...
remote: Counting objects: 717, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 717 (delta 0), reused 1 (delta 0)
Receiving objects: 100% (717/717), 172.84 KiB | 196.00 KiB/s, done.
Resolving deltas: 100% (336/336), done.
Checking connectivity... done.
Tapped 66 formulae

そして imagemagick のバージョンリストを取得

[kadosaway@imac ~]$ brew versions imagemagick
Warning: brew-versions is unsupported and will be removed soon.
You should use the homebrew-versions tap instead:
  https://github.com/Homebrew/homebrew-versions

6.9.0-3  git checkout 8b2b988 /usr/local/Library/Formula/imagemagick.rb
6.8.9-8  git checkout 9efbcda /usr/local/Library/Formula/imagemagick.rb
...
そしてフォーミュラ置き場へ移動し、目的のバージョンを再インストールする。
[kadosaway@imac ~]$ cd /usr/local/
[kadosaway@imac local]$ git checkout 9efbcda /usr/local/Library/Formula/imagemagick.rb
[kadosaway@imac local]$ brew reinstall imagemagick
==> Reinstalling imagemagick
==> Downloading https://downloads.sf.net/project/machomebrew/Bottles/imagemagick
Already downloaded: /Library/Caches/Homebrew/imagemagick-6.8.9-8.mavericks.bottle.tar.gz
==> Pouring imagemagick-6.8.9-8.mavericks.bottle.tar.gz

with_optionsでifを用いる時の注意

0

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

社会人1年目真っ最中の中西です。

今回は、model内でwith_optionsを用いた時の、ifの扱いについて紹介します。

環境

Rails 4.1.4

Ruby 2.1.2

with_optionsのif

model内で、バリデーションを書く時に、特定の場合のみバリデーションを動かしたい場合、ifを用います。

validates :test_id, length: { minimum: 50 }, if: test?

def test?
  return false if hogehuga == 'hogehuga'
  true
end

特定の場合のバリデーションが、1つの場合上記で問題ありません。

しかし、特定の場合のバリデーションが複数ある場合、それそれにifをつけるのは大変です。

そこで使用するのが、with_optionsになります。

with_options if: :test? do |v|
  v.validates :test_id, length: { minimum: 50 }
  v.validates :hoge, presence: true
end

with_optionsに条件を付けることにより、バリデーションをグルーピングすることができます。

この時に、with_optionsでまとめたバリデーションの1つに、さらに条件を加えたくなった場合、下記のように書きました。

with_options if: :test? do |v|
  v.validates :test_id, length: { minimum: 50 }
  v.validates :hoge, presence: true, if: :hoge
end

これで、大丈夫だと思っていたら、with_optionsでまとめたバリデーションを行う条件が、

「if: :test?」ではなく「if: :hoge」になっていました。

どうも、with_optionsの条件がifの場合、その中でさらにifを用いるとグルーピングの条件であるifを上書きしてしまうようです。

 解決方法

解決方法は簡単で、with_optionsの条件がifの場合は、その中ではifは使わない様にするだけです。

中でif文を使いたい場合は、逆のunlessを使います。

unlessを使えば、with_optionsのifを上書きすることはありません。

with_options if: :test? do |v|
  v.validates :test_id, length: { minimum: 50 }
  v.validates :hoge, presence: true, unless: 'hoge.blank?'
end

また、with_optionsの条件がunlessの場合には、その中でunlessは使わずに、ifを用いると条件を上書きせずにすみます。

rails4.2のwith_options

上記問題とは全く関係ありませんが、rails4.2では、with_optionsの書き方が少し簡略化されたようです。

 rails4.1

with_options if: :test? do |v|
  v.validates :test_id, length: { minimum: 50 }
  v.validates :hoge, presence: true
end

 raile4.2

with_options if: :test? do
  validates :test_id, length: { minimum: 50 }
  validates :hoge, presence: true
end

ブロック引数をつけなければいけなかったのが、つけなくて良くなったようです。

https://github.com/rails/rails/pull/16339

地味に楽になる気がします。

最近人気な記事