読者です 読者をやめる 読者になる 読者になる

技術情報は、Qiita でも書いています。

elasticsearch:import タスクで eager loading を行う

elasticsearch rails

elasticsearch-rails には、DB のデータを Elasticsearch にインポートするelasticsearch:import という便利な rake タスクがあります。

ただ、has_many などで関連しているデータもインデックスしたい場合に

rake environment elasticsearch:import:all FORCE=y

だけだと、eager loading が行われず、インデックス速度が遅くなってしまいます。

そこで、モデルに

scope :search_import, -> { includes(:categories, :sub_categories) }

のような scope を作ってあげて、

rake environment elasticsearch:import:all SCOPE="search_import" FORCE=y

とすると、eager loading が行われ、インデックス速度を上げることができます。


また、Gemfile に

gem 'ansi'

を追加すると、タスクの実行中にプログレスバーが表示されて便利です。

iTerm2 上の Emacs で、テーマの色を適切に表示する

mac

iTerm2 で Emacs を起動し (emacs -nw)、 テーマを選択すると、適切な色が表示されないことがあります。

調べたところ、iTerm2 の開発版と、パッチを当てた Emacs を使用することで直すことができました。

以下、Homebrew を使用していて、環境変数 TERM が xterm-256color になっている前提です。

使用中の iTerm2、Emacs を削除

brew cask uninstall iterm2
brew uninstall emacs
brew uninstall tmux  # tmux を使っている場合

iTerm2 開発版のインストール

brew tap caskroom/versions
brew cask install iterm2-nightly --appdir=/Applications

パッチを当てた Emacs のインストール

brew install choppsv1/term24/emacs
brew install choppsv1/term24/tmux  # tmux を使っている場合

iTerm2 の再起動

あとは、iTerm2 を起動しなおして、

ITERM_24BIT=1 emacs

とすれば、適切な色が表示されるはずです。

いちいち ITERM_24BIT=1 と打つのは面倒なので、.zshrc などに書いておくと良いと思います。

.zshrc

export ITERM_24BIT=1

参考

できるだけ簡単に ActiveRecord で utf8mb4 を動かす

rails ruby

MySQL で絵文字を扱うのに必要な utf8mb4 の設定方法です。

新規にデータベースを構築する場合向けです。

Rails 4.2.6 で確認しています。

データベースの設定

config/database.yml

default: &default
  adapter: mysql2
  encoding: utf8mb4
  collation: utf8mb4_bin

encoding を utf8mb4 に、collation を utf8mb4_bin にします。

collation を utf8mb4_bin にすることで、「ハハ」と「パパ」などが同一のものとして扱われるのを防ぎます。

ただし、「A」と「a」などのアルファベット大文字・小文字も別のものとして扱われるようになります。

string の文字数を変更する

インデックス長の関係で、string が VARCHAR(255) ではなく VARCHAR(191) になるようにします。

config/initializers/mysql_utf8mb4_fix.rb

require 'active_record/connection_adapters/abstract_mysql_adapter'

module ActiveRecord
  module ConnectionAdapters
    class AbstractMysqlAdapter
      NATIVE_DATABASE_TYPES[:string] = { name: 'varchar', limit: 191 }
    end
  end
end

参考

active_decorator-rspec gem が便利

active_decorator rspec rails ruby

Active Decorator の decorator 内で、ヘルパーのメソッドを使っていると、テストをうまく書けないという問題があります。

今までは、

active_decorator のdecoratorをrspecでテストする方法 - アジャイルSEを目指すブログ

を参考にさせていただいていたのですが、これだと、ApplicationController に書いた helper_method がテストで使えないようです。

悩んでいたところ、active_decorator-rspec という便利な gem を見つけました。

Gemfile は、こんな感じ。

gem 'active_decorator'

group :test do
  gem 'active_decorator-rspec', require: false
  gem 'rspec-rails', group: 'development'
end

あとは、spec/rails_helper.rb で

require 'active_decorator/rspec'

と読み込んで、

spec/decorators 内のテストで、

describe UserDecorator, '.hoge' do
  let(:user) { User.new }
  subject { decorate(user).hoge }
end

のように使えます。

最新版の Redis を wercker で使う

wercker redis

wercker/install_redis.sh

if [ ! -d "$WERCKER_CACHE_DIR/redis" ]
then
    mkdir $WERCKER_CACHE_DIR/redis
fi

if [ ! -f "$WERCKER_CACHE_DIR/redis/redis-3.0.2.tar.gz" ]
then
    cd $WERCKER_CACHE_DIR/redis
    wget http://download.redis.io/releases/redis-3.0.2.tar.gz
    tar zxvf redis-3.0.2.tar.gz
    cd redis-3.0.2
    make
    sed -r "s#^daemonize no\$#daemonize yes#;" redis.conf > redis.conf.new
fi

cd $WERCKER_CACHE_DIR/redis/redis-3.0.2
sudo src/redis-server redis.conf.new

wercker.yml

build:
    steps:
        - script:
            name: install redis
            code: sh wercker/install_redis.sh

RSpec で Active Job のテストを書く

rspec rails

追記(2016/11/11 10:33):

この記事の内容は古くなっており、あまりおすすめしません。RSpec 公式の方法をお使い下さい。

追記ここまで


Rails 4.2.1 で確認しています。

Post#ping で、10 分後に PingJob をエンキューするというテストを書いてみます。


spec/rails_helper.rb

RSpec.configure do |config|
  config.include ActiveJob::TestHelper
  config.include ActiveSupport::Testing::TimeHelpers  # 時間関係のテスト (travel_to メソッド) で使用
  ...
end

app/jobs/ping_job.rb

class PingJob < ActiveJob::Base
  queue_as :default

  def perform(msg)
    # do something...
  end
end

app/models/post.rb

class Post < ActiveRecord::Base
  def ping
    PingJob.set(wait: 10.minutes).perform_later 'hello'
  end
end

spec/models/post_spec.rb

require 'rails_helper'

describe Post, '#ping' do
  before { @post = Post.new }

  it '10 分後に PingJob をエンキューする' do
    time = Time.current

    travel_to(time) do
      assertion = {
        job: PingJob,
        args: ['hello'],
        at: (time + 10.minutes).to_i,
      }
      assert_enqueued_with(assertion) { @post.ping }
    end
  end
end

参考