正規表現の先読みと後読みについて、
イマイチ認識がぼんやりしてたので少し詳しく整理してみた
コードはrubyで書いてます

  • 先後読みの書式

    • 先読み : (?=)
    • 後読み : (?<=)
    • 否定の先読み : (?!)
    • 否定の後読み : (?<!)

    特徴として、先後読みにマッチしたテキストはマッチから取り除かれる

  • 先読みの動き

    先読みはそれまでにマッチした位置を記憶して
    先読み部分にマッチするかをチェックする

    先読み部分にマッチしたら
    先読み部分を削除して記憶していた位置に戻る

    つまり、下記の例だと、
    最初の文字列aaabbbはマッチするが、aaacccはマッチしない

    str = "aaabbb aaaccc"
    str.scan(/aaa(?=bbb)/)
    => ["aaa"]
    

    さらに、マッチ位置が先読み部分の先頭位置になるため、
    先読みの後に以下のようにcccを足してみると何もマッチしない
    これは先読みのマッチ後、aaaの最後のaの直後に
    マッチしているので次の文字がbとなるため。

    str = "aaabbb aaaccc aaabbbccc"
    str.scan(/aaa(?=bbb)ccc/)
    => []
    # 何もマッチしない
    
    str.scan(/aaa(?=bbb)\w+/)
    => ["aaabbb", "aaabbbccc"]
    # マッチ対象を単語構成文字とするとaaabbbとaaabbbcccがマッチ
    
  • 後読みの動き

    後読みは正規表現エンジンが到達した位置のすぐ左に
    後読みに含まれているテキストが存在するかをチェックする
    後読み部分にマッチしたら、後読みマッチ部分を削除する
    下記の例だと、最初の文字列bbbaaaはマッチするが、cccaaaはマッチしない

    str = "bbbaaa cccaaa"
    str.scan(/(?<=bbb)aaa/)
    => ["aaa"]
    

    後読みの直前にcccを追加すると何もマッチしなくなる
    これは、後読みが現在位置の左側をチェックしに行くため、
    cccの部分を参照する事が原因(cccとbbbはどうやってもマッチしない)

    str = "bbbaaa cccaaa cccbbbaaa"
    str.scan(/ccc(?<=bbb)aaa/)
    => []
    # 何もマッチしない
    
    str.scan(/\w+(?<=bbb)aaa/)
    => ["bbbaaa", "cccbbbaaa"]
    # マッチ対象を単語構成文字とするとbbbaaaaとcccbbbaaaがマッチ
    
  • HTMLタグの中身だけを取り出す正規表現

    "normal bold normal"の様なHTMLテキストから
    bタグの中身だけにマッチする正規表現を考える

    str = "normal <b>bold</b> normal"
    regex = /(?<=<b>)\w+(?=<\/b>)/
    str.match(regex)
    => #<MatchData "bold">
    

    開始タグを後読みで、終了タグを先読みでマッチさせるのがポイント

    先読みは現在位置より右側をチェックして、
    後読みは現在位置の左側をチェックする
    そして、先後読みはグループのような記述だけど実際は
    アンカーのような、位置にマッチするだけのものである
    という理解が大事だと思う

    先後読みをマッチ結果から対象を除外する特殊なグループ、
    のように考えてしまうと

    regex = /(?=<b>)\w+(?=<\/b>)/
    

    こんな正規表現(前後両方を先読み)でも
    タグの中身が取れてしまうような勘違いをしてしまうが
    実際は開始タグの部分の先読みが完了した時点で<タグの直前に
    マッチしている事になるので\wではなにもマッチしない事になるので注意。


vagrantで仮想マシンを構築している場合、
vagrant sshコマンドで便利にssh接続できるが
sshコマンドで接続したいケースを想定してsshの接続設定をする

  • ホストオンリーネットワークの設定

    Vagrantfileに以下の設定を加えてIPを設定する

    # V1形式
    config.vm.network :hostonly , "192.168.50.12"
    # v2形式
    config.vm.network :private_network , ip: "192.168.50.12"
    

    この状態でホストOSからとりあえずつないでみる

    ssh vagrant@192.168.50.12 -i ~/.vagrant.d/insecure_private_key
    

    無事接続できればOK

  • ホスト側のssh設定

    毎回上記のsshコマンドを打つのはだるいので設定を追加
    具体的には~/.ssh/configに設定を追加するだけだけど
    vagrant ssh-configコマンドを使うと接続設定を吐き出してくれるので

    vagrant ssh-config >> ~/.ssh/config
    # ホスト名を指定する場合は
    vagrant ssh-config --host hogehoge >> ~/.ssh/config
    

    としてやればOK

    デフォルトではhostname:127.0.0.1でport:2222になっているので
    hostnameとportを変更しておく

    $ vim ./.ssh/config
    # hsotnameとportを変更
    Host hogehoge
      HostName 192.168.50.12
      User vagrant
      Port 22
      UserKnownHostsFile /dev/null
      StrictHostKeyChecking no
      PasswordAuthentication no
      IdentityFile /Users/xxxx/.vagrant.d/insecure_private_key
      IdentitiesOnly yes
      LogLevel FATAL
    

    以下のようなsshコマンドで接続できればOK

    ssh hogehoge
    

    以上です


最近の開発に欠かせなくなってきたvagrant
導入の手順と簡単な設定手順を備忘録として
環境はmac osX 10.9

インストールと起動

公式サイトのダウンロードベージからdmgをダウンロード
DLしたインストーラを起動してインストール

完了したら確認を含めversionを表示してみる

vagrant --version

boxファイルを選択する

vagrantのbox共有サイト、ここで大体欲しいboxは見つかる

Vagrantfileを作成する
今回は上記サイトの[Ubuntu precise 64 VirtualBox]を使用する

mkdir vagrant_work
cd vagrant_work
vagrant init precise64 http://files.vagrantup.com/precise64.box

仮想マシンを構築する
初回はremoteでboxファイルのDLが発生する。
次回以降はローカルに保存済みのため高速
ベースになるboxファイルは[~/.vagrant.d/boxes]配下に出来る
VirtualBoxを使ってvagrant upすると[~/VirtualBox\ VMs/]配下に仮想マシンが出来る

vagrant up

仮想マシンにsshで接続する

vagrant ssh

仮想マシンをサスペンドする

vagrant suspend

仮想マシンを停止する

vagrant halt

仮想マシンを廃棄する

vagrant destory
# `vagrant destroy`すると`vagrant up`で作成した仮想マシンは完全に削除される

boxファイルの管理

ダウンロード済みのboxファイルのリストを表示

vagrant box list

不要になったboxファイルの削除

vagrant box remove box名

Vagrantfileについて

仮想マシン構築時の設定等を記述する。
デフォルトは100行ぐらい色々書いてあるけどほとんどコメント。

grep -v -e '\#' -e "^$" Vagrantfile

初期の設定内容は実際はこんな感じ。
rubyのスクリプトになっている。

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "precise64"
  config.vm.box_url = "http://files.vagrantup.com/precise64.box"
end

※バージョン管理でVagrantfileのあるディレクトリを管理する場合
は同ディレクトリ内にある./vagrant以下をバージョン管理下から外す。
これらは稼働している仮想マシン固有の情報なので共有してしまうと問題が起きる。

ホストOSとの共有ディレクトリを作成する

共有ディレクトリ内のファイルはvagrant destroyしても消えない。
デフォルトではVagrantfileがあるディレクトリが共有ディレクトリとして設定されている
ゲストOSからはデフォルトでは[/vagrant/]でアクセスできる。
共有ディレクトリの場所を変更するにはVagrantfileを以下のように変更する

# 第一引数は固定、第二引数でゲストOS上の共有ディレクトリパス、第三引数でホストOS上のパスを指定する
config.vm.share_folder "v-root", "/shared_dir", "/Users/xxx/Dropbox/work/vagrant_share"

設定を変更したら以下のコマンドで仮想マシンを再起動、新しい設定で読み込ませる

vagrant reload

ここでエラー

There are errors in the configuration of this machine. Please fix
the following errors and try again:

vm:
* The following settings shouldn't exist: share_folder

確認したところvagrantはV1とV2という二つのバージョンの設定方法を
サポートしていて上記の書き方はV1のモノだったのでV2では以下のように書く

config.vm.synced_folder "/Users/xxx/Dropbox/work/vagrant_share", "/shared_dir"

ちなみに、vagrantはV1とV2の両方の設定を共存する事を許容しているので

# V2形式での記述
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  ...
end

# V1形式での記述
Vagrant::Config.run do |config|
  config.vm.share_folder "v-root", "/shared_dir", "/Users/xxx/Dropbox/work/vagrant_share"
end

みたいな風にしても問題はない。

ただ、V1とV2でゲストOS側のパスとホストOS側のパスの引数の順番が
逆になっているので注意

ネットワーク設定

ゲストOS上のwebサーバにホストOSからアクセスできるようにする

  • Vagrantfileを以下のように編集

    config.vm.network "forwarded_port", guest: 80, host: 8080
    # V1のconfigは以下のようにする
    # config.vm.forward_port 80, 8080
    
  • 仮想マシンを再起動

    vagrant reload
    
  • ゲストOSにapacheをインストール、起動

    sudo apt-get -y install apache2
    cd /etc/apache2/sites-available
    sudo cp default mysite
    sudo a2ensite mysite
    sudo /etc/init.d/apache2 start
    
  • ホストOS側からゲストOSのwebページにアクセスしてみる

    ホストのブラウザ上から[localhost:8080]にアクセスしてwebページが表示されれば成功

メモリサイズ変更

Vagrantfileに以下を追記してvagrant reloadする

# V2の場合
config.vm.provider :virtualbox do |vb|
  vb.customize ["modifyvm", :id, "--memory", "1024"]
end
# V1の場合
config.vm.customize ["modifyvm", :id, "--memory", "1024"]

共有ディレクトリが遅い!

railsのアプリを開発する時にソースを共有ディレクトリに置いて
エディタはホスト側のモノを使ってゲスト側でアプリを実行した時に
最初、遅過ぎて使いモノにならなかった
rails s した時、assets関係の読み込みに1分以上かかるとか。。。
この問題は共有ディレクトリをNFSとしてマウントすればかなり改善される
private_networkのipは適宜変更

config.vm.synced_folder "/xxxx/VirtualBox VMs/vm_share", "/shared_dir", :nfs => true
config.vm.network :private_network, ip: "192.168.33.10"

まとめ

ここまで設定ができれば、ホスト側のエディタで今まで通り開発して
ゲストOS上のwebページの表示をホストのブウラザで確認するという
一連の開発作業が問題なくできるようになると思う。
なにより、数秒でクリーンな開発環境が作れるのは
色々な作業の敷居を下げてくれるので手放せない。


Railsアプリのデプロイ自体はcapistranoで行う事が多いが
バッチ処理のcron設定もwheneverを使ってruby側で行う事で一元管理できる。
さらにcapistranoと連携する事でデプロイ時のcrantab設定まで自動化してみる。
設定漏れも防げるし、管理もすっきりするのでいいことずくめ。

wheneverインストール

Gemfileに以下を追加

gem 'whenever', :require => false

railsプロジェクトにインストール

bundle install --path vendor/bundle

以下を実行してschedule.rbを作成

bundle exec wheneverize .

作成したschedule.rbを編集する

  • outputを定義するとログの出力先を指定できる
  • environmentを定義すると環境変数を利用できる
  • crontabの指定は以下のような形でrails風な感じでもcrontab風でもOK
  • job_typeを定義してPATHの定義等カスタマイズする事も可能

    set :output, 'log/crontab.log'
    set :environment, @environment
    
    job_type :runner, "export PATH=\"$HOME/.rbenv/bin:$PATH\"; eval \"$(rbenv init -)\"; cd :path && RAILS_ENV=:environment bundle exec rails runner :task :output"
    
    every 1.day, :at => '1:30 am' do
      command "sh batches/main.sh"
    end
    
    every 1.day, :at => '22:00 pm' do
      runner "Tasks::Testbatch.execute"
    end
    
    every :month, :at => '1:00 am' do
      rake "batch:samplerake"
    end
    

作成して以下を実行するとcrontabに記述されるスケジュールが表示される(この時点では実際には登録されない)

bundle exec whenever --set environment=development

しかしエラーが発生した。

uninitialized constant Whenever::JobList::Tasks (NameError)

Tasksが見つけられないとの事なので以下をschedule.rbに追加

require File.expand_path(File.dirname(__FILE__) + "/environment.rb")

再度[bundle exec whenever]するとcrontab用のテキストが表示された

実際にcrontabに反映するには以下のコマンドを実行する

bundle exec whenever --update-crontab --set environment=development

jobを削除するコマンド(crontabの他jobには影響はない)

bundle exec whenever --clear-crontab --set environment=development

capistranoと連携

とりあえずwheneverを使ってcrontabの設定を出来るようになったので
capistranoに組み込んでデプロイ時に自動反映出来るようにする。

capistranoをインストール

gem install capistrano -v '~>2.15'

※rbenv環境下の場合は以下のような感じでPATHにrbenvを加える

PATH="$HOME/.rbenv/bin:$PATH" sudo gem install capistrano -v '~>2.15'

設定ファイル生成コマンド(アプリケーションルートディレクトリで実行)

capify .

deploy.rbを編集する

細かい設定は使い方はこちらで
https://github.com/capistrano/capistrano

wheneverの設定を追加するためにdeploy.rbを編集

require 'whenever/capistrano'
set :whenever_command, "bundle exec whenever"
set :whenever_environment, defer {'staging'}
set :whenever_roles,defer{"IPADDR:PORT"}
after 'deploy:update', 'whenever:update_crontab'

設定が出来たら試してみる

cap staging deploy

エラーが発生。

Net::SSH::AuthenticationFailed: Authentication failed

以下を参考にnet-sshをバージョン下げて再インストールした
http://stackoverflow.com/questions/21811026/capistrano-cannot-deploy-code-because-netsshauthenticationfailed-authentic

gem uninstall net-ssh -v 2.8.0
gem install net-ssh -v 2.7.0

今度は動いたんだけどwhenever:update_crontabが空振りしてしまう。。。
こちらを参考に上のwhenever_xxxのパラメータ設定を変更
http://stackoverflow.com/questions/21395839/whenever-gem-failing-on-deploy

role :web, target_ip
set :whenever_roles,        ->{ :web }
set :whenever_options,      ->{ {:roles => fetch(:whenever_roles)} }
set :whenever_command,      ->{"bundle exec whenever"}
set :whenever_identifier,   ->{ fetch :application }
set :whenever_environment,  ->{ fetch :rails_env, "production" }
set :whenever_variables,    ->{ "environment=#{fetch :whenever_environment}" }
set :whenever_update_flags, ->{ "--update-crontab #{fetch :whenever_identifier} --set #{fetch :whenever_variables}" }
set :whenever_clear_flags,  ->{ "--clear-crontab #{fetch :whenever_identifier}" }

これで無事動いた。
設定自体はたいした事無いんだけど、色々ハマった。。。


CSSのセレクタの適用優先順位をまとめてみました。
あとセレクタの指定方法もあやふやになる事があるのでついでに。

セレクタの種類

  • IDセレクタ : #idの様なid単位のセレクタ
  • クラスセレクタ : .style1の様な各要素のclass単位のセレクタ
  • 属性セレクタ : [title="test"]の様な要素の属性単位のセレクタ
  • 疑似セレクタ : li:first-childの様な疑似セレクタ
  • タイプセレクタ : pやdivの様な要素単位のセレクタ

セレクタ以外

  • !important

    id指定等はもちろん事、インライン指定よりも優先される。
    とにかく最優先する場合に有効。
    どうしても対象スタイルを適用したい場合には有効だが使いすぎると
    修正する時に辛い。

    p {color:blue !important;}
    
  • インライン指定

    !importantを除いて一番優先される。

    <p style="color:red;">test</p>
    

各セレクタ+αのスタイル適用優先順位

1. !important
2. インライン指定
3. IDセレクタ
4. クラスセレクタ/属性セレクタ/疑似セレクタ
5. タイプセレクタ
6. より後ろに書かれているスタイル

CSSのポイントの話

各セレクタでの指定には以下のような配分で点数が割り振られていると考えられる

  • インライン : 1000pt
  • id : 100pt
  • クラス/疑似/属性 : 10pt
  • タイプ :1pt

なので以下のようなケースの場合上の行のスタイルが適用され、redになる

.c1.c2.c3.c4{color:red}    /* クラス×4=40pt */
.c1.c2{color:blue}    /* クラス×2=20pt */

ただしあくまでセレクタタイプが同レベルの場合の判断基準のようなので

#id1{color:red;}
.c1.c2.c3.c4.c5.c6.c7.c8.c9.c10.c11{color:blue}

みたいな(普通ありえないだろうけど)指定をしたからといって
上述した各セレクタの力関係は変わりませんのでこの場合はredになる
(IDセレクタ > クラスセレクタ)

CSSセレクタの指定方法

  • カンマ区切り

    同じスタイルを複数の要素に適用する

    .style1, style2
    
  • スペース区切り

    .style1の中のp要素に適用する

    .style1 p
    
  • >(不等号)

    p要素の直下の.styleに適用する

    p>.style1
    
  • .(ドット)でつなげる

    .style1かつ.style2に適用する

    .style1.style2
    

他にも色々あるけど、最低限この辺押さえておけばとりあえずやっていけるかな。