コピペコードで快適生活

明日使えるソースを自分のために

Vagrant+シェルでRails開発サーバを作る

新規案件でRails開発サーバを準備する必要があり、Vagrantとシェルのプロビジョニングで作成したときのメモです。
プロビジョニングにシェルを使用したのは、開発用PCがWindowsのためです。
無理してchefとか使おうとすると、環境構築を楽にするための環境構築が大変になるというよくわからないことになりそうでしたので。

下記ページを大いにパクらせて参考にさせていただきました。多謝です!
Rails開発のための仮想環境をvagrantでつくる - Qiita

主にインストールされるもの

※CentOS7/MySQL5.7の罠は回避。

よし作ろう

前提:VirtualBoxVagrantはインストール済

1.boxファイルを取得
vagrant box add CentOS_6_7 https://github.com/CommanderK5/packer-centos-template/releases/download/0.6.7/vagrant-centos-6.7.box
2.初期設定
cd ~
mkdir centos6-rails
cd centos6-rails
vagrant init CentOS_6_7
3.Vagrantfileを作成

下記のように書き換える

# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
  GUEST_RUBY_VERSION = '2.3.1'

  # The most common configuration options are documented and commented below.
  # For a complete reference, please see the online documentation at
  # https://docs.vagrantup.com.

  config.ssh.username = "vagrant"
  config.ssh.password = "vagrant"

  # Every Vagrant development environment requires a box. You can search for
  # boxes at https://atlas.hashicorp.com/search.
  config.vm.box = "CentOS_6_7"
  config.vm.hostname = "centos6-rails"

  # Disable automatic box update checking. If you disable this, then
  # boxes will only be checked for updates when the user runs
  # `vagrant box outdated`. This is not recommended.
  # config.vm.box_check_update = false

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine. In the example below,
  # accessing "localhost:8080" will access port 80 on the guest machine.
  # config.vm.network "forwarded_port", guest: 3000, host: 3000

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  config.vm.network "private_network", ip: "192.168.33.10"

  # Create a public network, which generally matched to bridged network.
  # Bridged networks make the machine appear as another physical device on
  # your network.
  # config.vm.network "public_network"
  config.vm.network "public_network", ip: "192.168.135.227"

  # Share an additional folder to the guest VM. The first argument is
  # the path on the host to the actual folder. The second argument is
  # the path on the guest to mount the folder. And the optional third
  # argument is a set of non-required options.
  # config.vm.synced_folder "../data", "/vagrant_data"

  # Provider-specific configuration so you can fine-tune various
  # backing providers for Vagrant. These expose provider-specific options.
  # Example for VirtualBox:
  #
  config.vm.provider "virtualbox" do |vb|
    # Display the VirtualBox GUI when booting the machine
    vb.gui = false
  
    # Customize the amount of memory on the VM:
    vb.memory = "4096"
  end
  #
  # View the documentation for the provider you are using for more
  # information on available options.

  # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
  # such as FTP and Heroku are also available. See the documentation at
  # https://docs.vagrantup.com/v2/push/atlas.html for more information.
  # config.push.define "atlas" do |push|
  #   push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
  # end

  # Enable provisioning with a shell script. Additional provisioners such as
  # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
  # documentation for more information about their specific syntax and use.
  # config.vm.provision "shell", inline: <<-SHELL
  #   apt-get update
  #   apt-get install -y apache2
  # SHELL

FUNCTION_INSTALL =<<EOS
function install {
  echo installing $1
  shift
  yum -y install "$@" >/dev/null 2>&1
}
EOS

  # development tools etc...
  config.vm.provision "shell", privileged: true, inline: <<-SHELL
    #{FUNCTION_INSTALL}
    yum -y update >/dev/null 2>&1

    install "development tools"  gcc-c++ glibc-headers openssl-devel readline libyaml-devel readline-devel zlib zlib-devel
    install "Git" git
    install "sqlite" sqlite sqlite-devel
    install "Nokogiri dependencies" libxml2 libxslt libxml2-devel libxslt-devel
    install "ImageMagick" ImageMagick ImageMagick-devel
    install "vim" vim-common vim-enhanced

    cp /etc/localtime /etc/localtime.org
    ln -sf  /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
    echo "ZONE=\"Asia/Tokyo\"" > /etc/sysconfig/clock
    service crond restart
  SHELL

  # MySQL5.6
  config.vm.provision "shell", privileged: true, inline: <<-SHELL
    #{FUNCTION_INSTALL}
    yum install -y http://dev.mysql.com/get/mysql-community-release-el6-5.noarch.rpm >/dev/null 2>&1
    install "MySQL" mysql mysql-server mysql-devel
    chkconfig --add mysqld
    chkconfig --level 345 mysqld  on

    echo "Start and Initialize MySQL"
    service mysqld start >/dev/null 2>&1
    mysql -uroot <<SQL
-- SET ROOT PASSWORD --
UPDATE mysql.user SET Password=PASSWORD('vagrant') WHERE User='root';
-- REMOVE ANONYMOUS USERS --
DELETE FROM mysql.user WHERE User='';
-- REMOVE REMOTE ROOT --
DELETE FROM mysql.user
WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
-- REMOVE TEST DATABASE --
DROP DATABASE IF EXISTS test;
DELETE FROM mysql.db WHERE Db='test' OR Db='test\\_%';
-- RELOAD PRIVILEGE TABLES --
FLUSH PRIVILEGES;
CREATE USER 'vagrant'@'localhost';
SET PASSWORD FOR 'vagrant'@'localhost' = PASSWORD('vagrant');
GRANT ALL PRIVILEGES ON *.* to 'vagrant'@'localhost';
SQL
  SHELL

  # memcached
  config.vm.provision "shell", privileged: true, inline: <<-SHELL
    #{FUNCTION_INSTALL}
    install "memcached" memcached memcached-devel
    chkconfig --add memcached
    chkconfig --level 345 memcached  on
    echo "Start and Initialize memcached"
    service memcached start >/dev/null 2>&1
  SHELL

  # rbenv
  config.vm.provision "shell", privileged: false, inline: <<-SHELL
    echo installing rbenv
    git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
    git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
    echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bash_profile
    echo 'eval "$(rbenv init -)"' >> ~/.bash_profile
    source ~/.bash_profile
    echo 'gem: --no-ri --no-rdoc' >> ~/.gemrc
    echo installing ruby#{GUEST_RUBY_VERSION}
    rbenv install #{GUEST_RUBY_VERSION}
    rbenv global #{GUEST_RUBY_VERSION}
    echo installing Bundler
    gem install bundler -N >/dev/null 2>&1
    echo installing ruby-gemset
    cd ~/.rbenv/plugins/
    git clone https://github.com/jf/rbenv-gemset.git    
  SHELL

  # ntp
  config.vm.provision "shell", privileged: true, inline: <<-SHELL
    #{FUNCTION_INSTALL}
    install "ntp" ntp
    chkconfig ntpd on
    echo "Start and Initialize ntp"
    service ntpd start >/dev/null 2>&1
  SHELL
end
4.起動&プロビジョニング
vagrant up

以上になります。

Windows環境でVagrant+Chefとか使おうとすると設定が思った以上に大変で、
危うく環境構築の手間を削減するための環境構築に手間をかけるという本末転倒なことになりかけていました。
トレンドにとらわれずに問題解決のために技術選定するって大切ですね。

Vagrentの基本的な使い方メモ

準備

1.仮想化支援機構の有効化

2.VirtualBoxのインストール

3.Vagrantのインストール

4.CentOSVirtualBoxへの登録

# 例
vagrant box add CentOS_6_3 https://dl.dropbox.com/sh/9rldlpj3cmdtntc/chqwU6EYaZ/centos-63-32bit-puppet.box

※公開box: http://www.vagrantbox.es/

5.Vagrant初期設定

> mkdir -p ~/Vagrant/centos6
> cd ~/Vagrent/centos6
> vagrant init CentOS_6_3
> ls # Vagrantfileファイルが存在することを確認

テキストエディタでVagrantfileを編集する。

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|

  # Authentication failure. Retrying が出るときはパスワード指定
  config.ssh.username = "vagrant"
  config.ssh.password = "vagrant"

  config.vm.box = "CentOS_6_3"

  #コメントアウトを外す
  config.vm.network "private_network", ip: "192.168.33.10"
end

Vagrantコマンド

# CentOS起動
vagrant up

# CentOS停止
vagrant halt

# サーバ廃棄
vagrant destroy

# boxファイルを作る
vagrant package

# ローカルのboxファイルを登録する
vagrant box add MyOS package.box

その他メモ

boxファイルの保存場所

Mac
/Users/<username>/.vagrant.d/boxes/
Windows
C:\Users\<username>\.vagrant.d\boxes\

日本語ドキュメント
http://lab.raqda.com/vagrant/index.html

Windows上でVirtualBox+Vagrant+CentOSによる仮想環境構築
http://qiita.com/hiroyasu55/items/11a4c996b0c62450940f

MySQLにblobで格納されたデータをまとめてファイル出力する

旧システムから新システムへのデータ移行の現場での一コマ。
DBにblobで格納されてた画像データをまとめてファイル出力したかったので、
PHPスクリプトを書いてみた。

<?php
// DB接続情報
$db_host = 'localhost';
$db_user = 'root';
$db_pass = 'hogehoge01';
$db_name = 'db_name';

// 対象テーブルリスト
$tables = array(
  'a_images',
  'b_images',
  'c_images',
  'd_images',
  'e_images',
  'f_images',
);

$db = mysql_connect($db_host, $db_user, $db_pass);

if ($db == false) {
  echo "database connect error\n";
  exit;
}
echo "database connect\n";

if (!mysql_select_db($db_name, $db)) {
  echo "database $db_name is not exist\n";
  exit;
}
echo "selcted database $db_name\n";

foreach ($tables as $table) {
  $sql = "SELECT * FROM $table";
  $result = mysql_query($sql);
  while ($row = mysql_fetch_array($result)) {
    // ファイル出力先ディレクトリ
    $dir_path = 'files' . '/' . $table . '/' . $row['id'];
    mkdir($dir_path, 0777, true);

    // ファイル名はfilename,
    // バイナリはdataカラムに格納されている前提
    $file_path = $dir_path . '/' . $row['filename'];
    $fp = fopen($file_path, 'w');
    fwrite($fp, $row['data']);
    fclose($fp);
    echo 'File output from ' . $table . '(id = ' . $row['id'] . ")\n";
  }
}

シェルでカレントディレクトリ配下のテキストファイルの中身を一斉置換

よく忘れるのでメモ。

# カレントディレクトリ以下 hoge を含むファイルを確認
grep -rn "hoge/" ./.

# hoge を fuga に一斉置換
grep -rl "hoge" ./.  | xargs sed -i -e "s/hoge/fuga/g" ${1}

# コピペ用
FROM=hoge
TO=fuga
grep -rl "${FROM}" ./.  | xargs gsed -i -e "s/${FROM}/${TO}/g" ${1}

# 特定のディレクトリを除外
grep -rl --exclude-dir=.git "${FROM}" ./.  | xargs gsed -i -e "s/${FROM}/${TO}/g" ${1}

VirtualBoxのゲストOS(CentOS)に複数の固定IPを設定する

表題の件の作業メモです。
ネットワークの割当は[ブリッジアダプター]を想定。

作業前:
eth8 に 192.168.135.224 を割り当て

作業後:
eth8 に 192.168.135.224 を割当
eth9 に 192.168.135.225 を割当

アダプターを増やす

VirtualBoxの設定 > ネットワーク でアダプター2を有効化。
それぞれ割当を[ブリッジアダプター]にします。

次にCentOSを起動しrootでログインし、
ifconfigでネットワークアダプタが増えていることを確認します。

eth8      Link encap:Ethernet  HWaddr 08:00:27:9F:8E:30
          inet addr:192.168.135.224  Bcast:192.168.135.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fe9f:8e30/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2466 errors:0 dropped:0 overruns:0 frame:0
          TX packets:467 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:232580 (227.1 KiB)  TX bytes:62891 (61.4 KiB)

eth9      Link encap:Ethernet  HWaddr 08:00:27:5C:87:BF
          inet addr:192.168.135.71  Bcast:192.168.135.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fe5c:87bf/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:2185 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:218982 (213.8 KiB)  TX bytes:1152 (1.1 KiB)
増やしたアダプターにIPアドレスを割当

既に存在するeth8の設定をコピーします。

# cd /etc/sysconfig/network-scripts/
# cp ifcfg-eth8 ifcfg-eth9

ifcfg-eth9を編集します。

TYPE=Ethernet
BOOTPROTO=none
IPADDR=192.168.135.225                    # IPアドレス書き換え
PREFIX=24
GATEWAY=192.168.135.1
DNS1=192.168.135.1
DEFROUTE=yes
IPV4_FAILURE_FATAL=yes
IPV6INIT=no
NAME="eth9"                               # 名前変える
UUID=ecc2e8f6-81b0-4e04-ae26-6ece18645648 # UUIDは下一桁を1ずらすなど別の値に調整
ONBOOT=yes
HWADDR=08:00:27:5C:87:BF                  # VirtualBox > 設定 > ネットワーク > アダプター2 で確認できる MACアドレスを記載
LAST_CONNECT=1426591352
設定を反映

下記コマンドで設定を反映します。

# service network restart
インターフェース eth8 を終了中:  デバイスの状態: 3 (切断済み)
                                                           [  OK  ]
インターフェース eth9 を終了中:  デバイスの状態: 3 (切断済み)
                                                           [  OK  ]
ループバックインターフェースを終了中                       [  OK  ]
ループバックインターフェイスを呼び込み中                   [  OK  ]
インターフェース eth8 を活性化中:  アクティブ接続の状態: アクティベート済み
アクティブ接続のパス: /org/freedesktop/NetworkManager/ActiveConnection/3
                                                           [  OK  ]
インターフェース eth9 を活性化中:  アクティブ接続の状態: アクティベート済み
アクティブ接続のパス: /org/freedesktop/NetworkManager/ActiveConnection/4
                                                           [  OK  ]
確認

ifconfigで設定が反映されていることを確認しておわり。

# ifconfig
eth8      Link encap:Ethernet  HWaddr 08:00:27:9F:8E:30
          inet addr:192.168.135.224  Bcast:192.168.135.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fe9f:8e30/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:4727 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1167 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:431732 (421.6 KiB)  TX bytes:164535 (160.6 KiB)

eth9      Link encap:Ethernet  HWaddr 08:00:27:5C:87:BF
          inet addr:192.168.135.225  Bcast:192.168.135.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fe5c:87bf/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:3529 errors:0 dropped:0 overruns:0 frame:0
          TX packets:8 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:349928 (341.7 KiB)  TX bytes:1152 (1.1 KiB)

UPDATE文でLEFT JOIN

SQL力が不足していて、LEFT JOINしたテーブルの値でUPDATEしたいときのやり方がわからなかったのでメモ。

# 確認用
SELECT * FROM users 
LEFT JOIN stores ON stores.id = users.store_id 
WHERE 
stores.company_id != users.copany_id; 

# UPDATE文
UPDATE users
lEFT JOIN stores on stores.id = users.store_id 
SET users.company_id = stores.company_id 
WHERE stores.company_id != users.copany_id; 

闇が深いDB設計になっているのは気にしない。

こちら参考にさせていただきました!感謝です!
mysqlでjoinした結果をupdate - Qiita

RailsのArelの使い方

RailsでArelを使うときは、下記サイトをいつも参照させていただいていましたが、自分メモ用として転載します。
メモ || Arelのwhere系メソッド一覧 User.w...

User.where(User.arel_table[:name].eq("aa")).to_sql
# => SELECT "users".* FROM "users" WHERE "users"."name" = 'aa'

User.where(User.arel_table[:name].eq(nil)).to_sql
# => SELECT "users".* FROM "users" WHERE "users"."name" IS NULL

User.where(User.arel_table[:name].eq_all(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" = 'aa' AND "users"."name" = 'bb'))

User.where(User.arel_table[:name].eq_all(["aa",nil])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" = 'aa' AND "users"."name" IS NULL))

User.where(User.arel_table[:name].eq_any(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" = 'aa' OR "users"."name" = 'bb'))

User.where(User.arel_table[:name].eq_any(["aa",nil])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" = 'aa' OR "users"."name" IS NULL))

User.where(User.arel_table[:name].not_eq("aa")).to_sql
# => SELECT "users".* FROM "users" WHERE ("users"."name" != 'aa')

User.where(User.arel_table[:name].not_eq(nil)).to_sql
# => SELECT "users".* FROM "users" WHERE ("users"."name" IS NOT NULL)

User.where(User.arel_table[:name].not_eq_all(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" != 'aa' AND "users"."name" != 'bb'))

User.where(User.arel_table[:name].not_eq_all(["aa",nil])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" != 'aa' AND "users"."name" IS NOT NULL))

User.where(User.arel_table[:name].not_eq_any(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" != 'aa' OR "users"."name" != 'bb'))

User.where(User.arel_table[:name].not_eq_any(["aa",nil])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" != 'aa' OR "users"."name" IS NOT NULL))


User.where(User.arel_table[:name].gt("aa")).to_sql
# => SELECT "users".* FROM "users" WHERE ("users"."name" > 'aa')

User.where(User.arel_table[:name].gt_all(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" > 'aa' AND "users"."name" > 'bb'))

User.where(User.arel_table[:name].gt_any(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" > 'aa' OR "users"."name" > 'bb'))

User.where(User.arel_table[:name].gteq("aa")).to_sql
# => SELECT "users".* FROM "users" WHERE ("users"."name" >= 'aa')

User.where(User.arel_table[:name].gteq_all(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" >= 'aa' AND "users"."name" >= 'bb'))

User.where(User.arel_table[:name].gteq_any(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" >= 'aa' OR "users"."name" >= 'bb'))


User.where(User.arel_table[:name].lt("aa")).to_sql
# => SELECT "users".* FROM "users" WHERE ("users"."name" < 'aa')

User.where(User.arel_table[:name].lt_all(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" < 'aa' AND "users"."name" < 'bb'))

User.where(User.arel_table[:name].lt_any(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" < 'aa' OR "users"."name" < 'bb'))

User.where(User.arel_table[:name].lteq("aa")).to_sql
# => SELECT "users".* FROM "users" WHERE ("users"."name" <= 'aa')

User.where(User.arel_table[:name].lteq_all(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" <= 'aa' AND "users"."name" <= 'bb'))

User.where(User.arel_table[:name].lteq_any(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" <= 'aa' OR "users"."name" <= 'bb'))


User.where(User.arel_table[:name].in(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE "users"."name" IN ('aa', 'bb')

User.where(User.arel_table[:birthday].in('1980-01-01'.to_date..'1990-12-31'.to_date)).to_sql
# => SELECT "users".* FROM "users" WHERE "users"."birthday" BETWEEN '1980-01-01' AND '1990-12-31'

User.where(User.arel_table[:name].in_all(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" IN ('aa') AND "users"."name" IN ('bb')))

User.where(User.arel_table[:name].in_any(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" IN ('aa') OR "users"."name" IN ('bb')))

User.where(User.arel_table[:name].not_in(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE ("users"."name" NOT IN ('aa', 'bb'))

User.where(User.arel_table[:name].not_in_all(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" NOT IN ('aa') AND "users"."name" NOT IN ('bb')))

User.where(User.arel_table[:name].not_in_any(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" NOT IN ('aa') OR "users"."name" NOT IN ('bb')))


User.where(User.arel_table[:name].matches("aa")).to_sql
# => SELECT "users".* FROM "users" WHERE ("users"."name" LIKE 'aa')

User.where(User.arel_table[:name].matches("%aa")).to_sql
# => SELECT "users".* FROM "users" WHERE ("users"."name" LIKE '%aa')

User.where(User.arel_table[:name].matches_all(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" LIKE 'aa' AND "users"."name" LIKE 'bb'))

User.where(User.arel_table[:name].matches_any(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" LIKE 'aa' OR "users"."name" LIKE 'bb'))

User.where(User.arel_table[:name].does_not_match("aa")).to_sql
# => SELECT "users".* FROM "users" WHERE ("users"."name" NOT LIKE 'aa')

User.where(User.arel_table[:name].does_not_match_all(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" NOT LIKE 'aa' AND "users"."name" NOT LIKE 'bb'))

User.where(User.arel_table[:name].does_not_match_any(["aa","bb"])).to_sql
# => SELECT "users".* FROM "users" WHERE (("users"."name" NOT LIKE 'aa' OR "users"."name" NOT LIKE 'bb'))