正規表現の先読みと後読み
正規表現の先読みと後読みについて、
イマイチ認識がぼんやりしてたので少し詳しく整理してみた
コードは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でssh接続設定
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
以上です
macにVagrant導入
最近の開発に欠かせなくなってきたvagrant
導入の手順と簡単な設定手順を備忘録として
環境はmac osX 10.9
インストールと起動
-
Virtualboxのインストール
-
Vagrantのインストール
公式サイトのダウンロードベージから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のバッチ処理のcrontab設定をwhenever&capistranoで自動化
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セレクタの種類と適用優先順位、指定方法等
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
他にも色々あるけど、最低限この辺押さえておけばとりあえずやっていけるかな。