2015年1月29日木曜日

Rails 4.2からはmodule ClassMethodsではなくConcern#class_methodsを使おう

もう1月も終盤ですが、あけましておめでとうございます。

先日、と言ってももう1カ月くらいは前だった気がしますが、Ruby on Rails 4.2.0がリリースされました。
⇒ リリースノート
 リリースノート(日本語)

主要な修正は以下の5つのようです。
  • ActiveJob
  • メールの非同期処理
  • Adequate Record
  • Web Console
  • 外部キーのサポート
どれも気になるところではありますが、外部キーのサポートとか特に気になりますが、個人的に一番気になったのは「Concern#class_methodsとKernel#concernの導入」です。
以下のコミットで導入されました。
⇒ Introduce Concern#class_methods and Kernel#concern

いままでconcernはこんな感じのモジュールを用意していました。
# 4.1まで
module Hoge
  extend ActiveSupport::Concern

  included do
  end

  module ClassMethods
  end
end

class Fuga < ActiveRecord::Base
  include Hoge
end

それが4.2以降はConcern#class_methodsを使用して以下のように書けるようになります。
# 4.2以降(Concern#class_methods)
module Hoge
  extend ActiveSupport::Concern

  included do
  end

  class_methods do
  end
end

class Fuga < ActiveRecord::Base
  include Hoge
end

さらにKernel#concernを使えば以下のようにmoduleである必要すらなくり、ActiveSupport#Concernをextendする必要がなくなります。
# 4.2以降(Kernel#class_methods)
concern :Hoge
  included do
  end

  class_methods do
  end
end

class Fuga < ActiveRecord::Base
  include Hoge
end


module ClassMethodsとConcern#class_methodsではクラス変数の扱いが微妙に異なるようです。
# app/models/concerns/hoge_old.rb
module HogeOld
  extend ActiveSupport::Concern

  included do
  end

  module ClassMethods
    def func_old
      @@val_old ||= 'old style'
    end
  end
end

# app/models/concerns/hoge_new.rb
module HogeNew
  extend ActiveSupport::Concern

  included do
  end

  class_methods do
    def func_new
      @@val_new ||= 'new style'
    end
  end
end

# app/models/concerns/user.rb
class User < ActiveRecord::Base
  include HogeOld
  include HogeNew
end
上記のようなコードがあるとして、実行すると以下のような結果になります。
[1] pry(main)> User.class_variable_defined?(:@@val_old)
=> false
[2] pry(main)> HogeOld::ClassMethods.class_variable_defined?(:@@val_old)
=> false
[3] pry(main)> User.func_old
=> "old style"
[4] pry(main)> User.class_variable_defined?(:@@val_old)
=> false
[5] pry(main)> HogeOld::ClassMethods.class_variable_defined?(:@@val_old)
=> true
[6] pry(main)> User.class_variable_defined?(:@@val_new)
=> false
[7] pry(main)> HogeNew::ClassMethods.class_variable_defined?(:@@val_new)
=> false
[8] pry(main)> User.func_new
=> "new style"
[9] pry(main)> User.class_variable_defined?(:@@val_new)
=> true
[10] pry(main)> HogeNew::ClassMethods.class_variable_defined?(:@@val_new)
=> false
[11] pry(main)>
module ClassMethodsでクラス変数を定義するとincludeしたクラスのクラス変数とはならず、ClassMethodsのクラス変数(モジュール変数)になってしまします。しかし、Concern#class_methodsを利用するとincludeしたクラスのクラス変数になります。
通常、concernでクラス変数を利用する際は後者の挙動を期待するように思います。

ちなみにKernel#concernでclass_methodsを使った時はクラス変数を使用するべきではありません。
「warning: class variable access from toplevel」みたいな警告が出ます。
これが意図した挙動かまでは追ってませんが、どうなんでしょうね?

いずれにしてもmodule ClassMethodsはやめてclass_methodsを使用したほうが見通しは良くなりそうです。

2014年12月31日水曜日

へっぽこプログラマーの2014年購入ガジェットまとめ

大晦日ということで実家でまったりしています。
去年一昨年もやりましたが、購入したガジェットなどをまとめてみようかなと思います。

2014年は全然買っていないような気がしています。


スマートフォン・タブレット関連

なし!
本当にないのか思い返してみたけど特に思いつかなかったのでたぶんない。
強いて言えばNexus5のディスプレイ割って修理に出したくらい。


カメラ関連

Samyang 7.5mm F3.5 UMC Fish-eye MFT

魚眼レンズ。
お値段はお安いけど良く写る良いレンズ。
11月末に行った甘利山での撮影で活躍した。

Olympus PEN Lite E-PL6 ブラック レンズキット

サブ機。
レンズ交換面倒だなっていう時とか荷物小さ目にしたい時に使う用で購入。
E-PL5の方が安かったけど、水準器がついてるのが決め手でE-PL6にした。
レンズキットじゃなくてボディ単体で良かったんだけど100円くらいしか違わなかったのでレンズキットにしておいた。キットレンズは未使用。

Olympus VF-4

外付けEVF。
E-PL6でEVF使いたい時がそこそこあるので買ってしまった。E-M5のEVFより断然見やすい。

Olympus M.ZUIKO DIGITAL ED 12-40mm F2.8 PRO

F2.8通しの標準ズーム。
防塵防滴なの便利。下手な単焦点より良く写るんじゃないですかね。
パナの12-35mm F2.8と迷ったけど望遠端の焦点距離と最短撮影距離が決め手でこっちにした。

Olympus STYLUS XZ-10 ブラック

ポケットに入るくらいのカメラが欲しくて購入。
そこそこ明るくてズームできるやつでお値段も結構安かった気がする。
使用頻度は低めだけどカバンとかに適当に突っ込んでおいても邪魔にならないサイズ感なので良い。

Olympus M.ZUIKO DIGITAL 45mm F1.8 ブラック

中望遠の単焦点レンズ。
m4/3のコスパ良いとりあえず買っておけよてきなレンズだけど、なんかうっかり買ってしまっていた。

SLIK PRO II 3WAY

三脚は一つは持っとくべきだよねってことで。
主に軽さ重視。

Manfrottoアクティブバックパック1

撮影旅行的なものに行く用に、三脚が取り付けれてレンズがそこそこの本数入って一泊分くらいの着替えが入るカメラバッグが欲しくて購入。

Olympus RM-UC1

ケーブルレリーズ。
夜景とか撮る予定があったので買った。E-M5のLIVE BULBとLIVE TIME便利。


時計

EARNSHAW MASKELYNE

バンドが革の時計を探していて見た目に惚れて購入。

Orient Star Retro-Future SUV

機械式の時計良いなぁと思ってて定価の半額くらいの値段になってたので思わず買った。
これはSUVをイメージしたモデルだけどカメラっぽいモデルもあるのを後で知ってそっちも欲しくなった。




なんだかんだ2013年より買っているような気がしますね?買ってない気がしたのは気のせいでした。
広角レンズとマクロレンズとE-M5後継機が気になるので2015年もそこそこ出費がかさみそう。
あとスマートウォッチ欲しいな。ZenWatchとか。端末はAQUOS CRYSTAL Xがちょっと気になっている。


それでは皆様良いお年を。
来年もよろしくお願いします。

2014年10月23日木曜日

CakePHP 2.xでSQLクエリーをログに出力する

おおよそ8か月ぶりの更新らしいです。
そこそこお仕事で忙しかったみたいです。Railsな案件→VBAな案件→CakePHPな案件と渡り歩いてます。

本題に移ると、まぁタイトルの通りなのですが、CakePHPでクエリのログをファイルに簡単に吐けないの?設定をチョロっといじるだけで出来たりしないの?と思い調べました。

結果としてちょっとしたコード書けば出来ることがわかったので書きました。
http://kwski.net/cakephp-2-x/1064/を参考にやれば終わりです。

だとエントリとしてちょっとあれなので、少しアレンジします。

参考サイトだとMySQLですが、今回対応したのはPostgreSQLです。でもやってることは同じです。
あと、クエリのみ出力と詳細出力を切り替えれるようにしてます。
MySQLの場合はPostgresになってるとこをMySQLに書き換えたら動くと思います。

1. カスタムしたdatasourceを作成

/app/Model/Datasource/Database/PostgresLog.phpのような名前で作成します。
名前は何でも良いです。
対象のdatasourceを継承したクラスを作ります。
<?php
App::uses('Postgres', 'Model/Datasource/Database');
class PostgresLog extends Postgres {
    function logQuery($sql, $params = array()) {
        parent::logQuery($sql);
        $logLevel = Configure::read('Cake.logQuery');
        switch ($logLevel):
        case 1:
            $this->log($sql, LOG_DEBUG);                // SQLクエリーのみ
            break;
        case 2:
            $this->log($this->_queriesLog, LOG_DEBUG);  // SQLの実行詳細
            break;
        endswitch;
    }
}
?>

2. 使用するdatasourceを変更

/app/Config/database.phpのdatasourceの指定を以下のように1で作成したクラスの名前に変更します。
        'datasource' => 'Database/Postgres',
        'datasource' => 'Database/PostgresLog',

3. 出力するフラグを設定

/app/Config/core.phpで以下のように設定します。1を指定するとSQLのみ、2を指定すると詳細、それ以外を指定するか、未指定だと出力しません。
    // クエリログを出力する(1: SQLのみ, 2: 詳細, other: なし)
    Configure::write('Cake.logQuery', 1);


と、こんな感じでコードを書いておくとデバッグが捗るんじゃないでしょうか。

2014年2月26日水曜日

RSpec 3.0.0.beta2 + CI::Reporter 1.9.1で落ちるのをどうにかする

先日、RSpec 3.0.0.beta2がリリースされなにやら色々変わったようです。
細かいことは置いておいて、CI::Reporter 1.9.1を使おうとすると落ちるようになりました。
Jenkinsでテスト結果集計に使っていたりなんかすると非常に困ります。

で、適当に対策してみました。
以下のコードをRailsならconfig/initializers/ci_reporter.rbなどの適当なinitializerに追加します。Rails以外は知らないので適当に読み替えてください。
実行後に警告は出ますが一応動くようになります。気になる人は自分でなんとかしてください。

  module CI
    module Reporter
      class RSpec
        def example_group_started(example_group)
          @formatter.example_group_started(::RSpec::Core::Notifications::GroupNotification.new(example_group))
          new_suite(description_for(example_group))
        end

        def example_started(name_or_example)
          @formatter.example_started(::RSpec::Core::Notifications::ExampleNotification.new(name_or_example))
          spec = TestCase.new
          @suite.testcases << spec
          spec.start
        end

        def example_failed(name_or_example, *rest)
          @formatter.example_failed(::RSpec::Core::Notifications::ExampleNotification.new(name_or_example), *rest)

          # In case we fail in before(:all)
          example_started(name_or_example) if @suite.testcases.empty?

          if name_or_example.respond_to?(:execution_result) # RSpec 2
            failure = RSpec2Failure.new(name_or_example, @formatter)
          else
            failure = RSpecFailure.new(rest[1]) # example_failed(name, counter, failure) in RSpec 1
          end

          spec = @suite.testcases.last
          spec.finish
          spec.name = description_for(name_or_example)
          spec.failures << failure
        end

        def example_passed(name_or_example)
          @formatter.example_passed(::RSpec::Core::Notifications::ExampleNotification.new(name_or_example))
          spec = @suite.testcases.last
          spec.finish
          spec.name = description_for(name_or_example)
        end

        def example_pending(*args)
          @formatter.example_pending(::RSpec::Core::Notifications::ExampleNotification.new(*args))
          name = description_for(args[0])
          spec = @suite.testcases.last
          spec.finish
          spec.name = "#{name} (PENDING)"
          spec.skipped = true
        end

        def dump_summary(*args)
            if args.size == 4
              notification = ::RSpec::Core::Notifications::SummaryNotification.new(*args)
              args = []
              args[0] = notification
            end
          @formatter.dump_summary(*args)
          write_report
          @formatter.dump_failures(::RSpec::Core::Notifications::SummaryNotification.new)
        end

        def method_missing(meth,*args,&block)
          case meth.to_sym
          when :message then
            unless args[0].methods.include? :message
              args[0] = ::RSpec::Core::Notifications::MessageNotification.new(args[0])
            end
          when :start
            unless args[0].methods.include? :count
              args[0] = ::RSpec::Core::Notifications::CountNotification.new(args[0])
            end
          when :seed
            unless args[0].methods.include? :seed
              args[0] = ::RSpec::Core::Notifications::SeedNotification.new(args[0], true)
            end
          when :example_group_finished
            unless args[0].methods.include? :seed
              args[0] = ::RSpec::Core::Notifications::GroupNotification(args[0])
            end
          when :close, :start_dump, :dump_pending, :dump_failures, :dump_profile, :stop
            args[0] = ::RSpec::Core::Notifications::SummaryNotification.new if args.size == 0
          end
          @formatter.send(meth,*args,&block)
        end
      end
    end
  end

このコードはRSpecとCI::Reporterの実装に強く依存しているのでバージョンが変わったら動かなくなる可能性が高いです。あくまで暫定対処なのでその点はご了承ください。

2014年2月7日金曜日

ESET Smart SecurityでHTTPSを使用するアプリケーションが動作しない場合の対処法

ESET Smart Securityのプロトコルフィルタリングが有効になっているPCだとHTTPSを使うアプリケーションが一部動作しなかったりする。
理由は証明書のあれこれなんですが、使えないと不便なので対策を。
動作確認したバージョンは7.0.302。

対策は2パターンあるのでどっちかをやる。

対策1

[詳細設定] -> [Webとメール] -> [プロトコルフィルタリング]
[アプリケーションプロトコルフィルタリングを有効にする] のチェックを外す。


対策2

対策1だと全アプリケーションでプロトコルフィルタリングが無効になってしまいます。せっかくの昨日なので動作するアプリケーションはそのままプロトコルフィルタリングを適用し、動作しないアプリケーションだけ適用外にするように設定したいと思います。

[詳細設定] -> [Webとメール] -> [プロトコルフィルタリング] -> [対象外のアプリケーション]
[追加]ボタンを押し除外したいアプリケーションを選択する。


おまけ

Firefox編

%FIREFOX_ROOT%\firefox.exeを対象外アプリケーションに追加

Git(msysgit)編

%GIT_ROOT%\libexec\git-core\git-remote-https.exeを対象外アプリケーションに追加
ついでに%GIT_ROOT%\bin\curl.exeなども追加しておくと良いかもしれない。
cygwinは使っていないので同じ手順で動作するかは不明です。
go getは内部でgit cloneを使てるのでgo get出来ない時もこれで解消するはず。

Mercurial(Windowsネイティブ版hg)編

%HG_ROOT%\hg.exeを対象外アプリケーションに追加

2014年1月24日金曜日

Windowsネイティブ版のMercurialをちょっといい感じに使う設定を考えてみた。

Windowsネイティブ版のMercurialをそこそこ快適に使うための設定に悩んでました。
それっぽい設定が出来た気がするので晒しておきます。


要件っぽいもの


個人的になんとなく快適に使うのに必要な要件はこれくらい。
  • カラー表示に対応
  • ページャに対応
  • hg logが文字化けしない
  • UTF-8のファイルのhg diffが文字化けしない
  • Gitっぽくrebaseとかstash(Mercurialだとshelve)もしたい
  • SSHも使える
  • TortoiseHgは使わない
  • コマンドプロンプト使う
  • 日本語ファイル名とか使わないので気にしない


必要なモジュールとか


Mercurial 2.8だとtextful extensionだけはモジュール取得しておく必要がある。
2.7以前だとshelveとかも取得しておかないといけないかもしれない。
  • color extension
  • pager extension
  • textful extension (https://bitbucket.org/yuja/hgext-textful)
  • graphlog extension
  • extdiff extension
  • mq extension
  • shelve extension
  • rebase exntesion
  • histedit extension
  • Kaoriya版Vim(UTF-8が扱える好きなエディタで良いと思う。)
  • WinMerge日本語版(外部ツールでdiff見る用。好きなもので良いと思う。)
  • msysGit(sshとlessを使う。色々試したけどlessはこれが一番良かった気がする。)


設定


環境変数HGENCODINGutf-8を設定する。
(環境変数HOMEにUSERPROFILEと同じ値を設定しておいた方が良いかもしれない。)
以下はMercurial.iniの設定例。
パスとかは適宜読み替えてください。
[ui]
username = Your Name <email@example.com>
editor = C:\tools\vim74-kaoriya-win64\vim.exe
ssh = "C:\Program Files (x86)\Git\bin\ssh.exe"

[extensions]
graphlog =
color =
mq =
rebase =
histedit =
shelve =
pager =
extdiff =
textful = C:\tools\hgext\hgext-textful\hgext\textful
textful.encoding = C:\tools\hgext\hgext-textful\hgext\textful\encoding.py
textful.msexcel = C:\tools\hgext\hgext-textful\hgext\textful\msexcel.py
textful.pipe = C:\tools\hgext\hgext-textful\hgext\textful\pipe.py

[color]
mode = ansi

[pager]
pager = "C:\Program Files (x86)\Git\bin\less.exe" -FRXr
quiet = True
attend = annotate, cat, diff, export, glog, log, qdiff, status, heads, incoming, outgoing, shelve

[extdiff]
cmd.winmerge = C:\Program Files\WinMerge\WinMergeU.exe
opts.winmerge = /r /e /x /ub

[textful]
encoding = utf-8, euc-jp, cp932, iso-2022-jp
attend = annotate, cat, diff, glog, log, qdiff, status, heads, incoming, outgoing
pipe.doc = antiword -w0 $<


まとめ的な


これくらい設定しておくと必要な要件はだいたい満たせると思う。
いまのところは特に問題なさそうな感じで使えています。
なんか間違っているところとか、もっとこうしたほうが良いとかあったら教えてください。

2014年1月23日木曜日

Mercurialでgit stashっぽいことをする方法

Mercurialでgit stashと同様の機能を使用する場合、shelve extensionを使用する。


shelve extensionの有効化


Mercurial 2.8以降であれば標準で組み込まれているので有効化する。
.hgrcまたはMercurial.iniに以下を記述する。
[extensions]
shelve =
2.7以前で使用する場合はローカルにモジュールを取得する必要がある。
$ hg clone https://bitbucket.org/astiob/hgshelv
その後、.hgrcまたはMercurial.iniに以下を記述して有効化する。
[extensions]
shelve = /path/to/hgshelve.py


使用方法


ローカルの変更を退避させておくには以下のコマンドを使用する。
$ hg shelve
逆に退避させたものを取り出すには以下のコマンドを使用する。
$ hg unshelve
退避させたものの一覧を表示するには以下のコマンドを使用する。
$ hg shelve --list
shelve/unshelveする際に名前を指定したい時は以下のように--nameオプションを使用する。
名前を指定せずにshelveした際はブックマーク名またはブランチ名が使用される。すでにそのブックマーク名なりブランチ名でshelveされている場合は連番を追加した名前なる。
$ hg shelve --name <name>
$ hg unshelve --name <name>
shelveしたものを取り出さずに削除する場合は以下を使用する。
$ hg shelve --delete <name>