リア充爆発日記

You don't even know what ria-ju really is.

capistrano3 + wheneverでハマったメモ

ハマった内容としては、deployしてもcronがupdateされないという現象。

結論からいうと、サーバのroleの設定ミスだった。

wheneverのcapistranoプラグインはデフォルトでdbロールのサーバに対してcrontabへのupdateを行うのだけれど、
対象のサーバがdbロールを持っていなかったので実行されていないというだけだった。これ、ログにもでないので気づきにくい。


で、cronを実行するサーバにはbatchロールを付与することにした。
さておき、wheneverのcapistrano周りで足した記述をまとめると
Capfile

~snip~
require 'whenever/capistrano'

# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

deploy.rb

~snip~
set :whenever_identifier, ->{ "#{fetch(:application)}_#{fetch(:stage)}" }
set :whenever_roles,        ->{ :batch }
~snip~

deploy/staging.rb

~snip~
server 'ria10.example.com', user: 'ria10', roles: %w{web app batch} # batchを足した
~snip~

これだけ。
はぁ。2時間くらい使っちまった・・・。

パーフェクト Ruby on Rails

パーフェクト Ruby on Rails

Rspec2->Rspec3にバージョンアップしたときのメモ

仕様の変更部分とかそういうのは置いといて、実際に2.14.xで運用していたRailsアプリを3.0.xにアップデートしたときの作業メモ。無くなったメソッドなどは書き換えで対応することとし、後方互換のためのgemなどは一切使わぬゆえ。

なので、3へのアップデート手順としては、当然アプリによって過不足がでるからね。

というわけでGemに更新から。

group :development, :test do
  gem 'rspec-rails', '~> 3.0.1'
  gem 'rspec-mocks', '~> 3.0.2'
~snip~

で、まず実行。

$ rspec
/Users/ria10/IdeaProjects/ria10-web/spec/controllers/application_controller_spec.rb:4:in `block in ': undefined method `controller' for RSpec::ExampleGroups::ApplicationController:Class (NoMethodError)
~snip~

起動しなかった。調べると、これだった。
https://github.com/rspec/rspec-rails/pull/970
今までは、ディレクトリの場所でそれがcontrollerなのかとかの判断をする機能がデフォルトで有効になってたんだけど、3からはそれをオプトイン方式にしたそうで、これは特定のフレームワーク依存じゃないツールとしては適当じゃないよね、ということなんでしょう。

対処方法は

spec_helper.rb

RSpec.configure do |config|
  config.infer_spec_type_from_file_location! # 追加!
~snip~

これでこのエラーはでなくなった。

再実行。

$ rspec
/Users/ria10/IdeaProjects/ria10-web/spec/models/user_spec.rb:11:in `block in ': undefined method `its' for RSpec::ExampleGroups::Route:Class (NoMethodError)
~snip~

見ての通り、itsはなくなったんですな。理由はこのとおり。
https://gist.github.com/myronmarston/4503509

で、どう書き換えていくべきかを調べていたら、なんと廃止された記法などをシャレオツなやつに自動的に書き換えてくれるgemを発見した。
http://yujinakayama.me/transpec/


で、インストラクションに従って実行すると結果のサマリーが最後にでる。

$ transpec
~snip~
Summary:

163 conversions
from: it { should ... }
to: it { is_expected.to ... }
17 conversions
from: describe 'some controller' { }
to: describe 'some controller', :type => :controller { }
13 conversions
from: describe 'some routing' { }
to: describe 'some routing', :type => :routing { }
12 conversions
from: obj.should_receive(:message)
to: expect(obj).to receive(:message)
9 conversions
from: describe 'some model' { }
to: describe 'some model', :type => :model { }
4 conversions
from: it { should_not ... }
to: it { is_expected.not_to ... }
2 conversions
from: describe 'some mailer' { }
to: describe 'some mailer', :type => :mailer { }
1 conversion
from: == expected
to: eq(expected)
1 conversion
from: be_true
to: be_truthy
1 conversion
from: its(:attr) { }
to: describe '#attr' do subject { super().attr }; it { } end
1 conversion
from: obj.should_not_receive(:message)
to: expect(obj).not_to receive(:message)

224 conversions, 0 incompletes, 0 warnings, 0 errors

A commit message that describes the conversion summary was generated to
.git/COMMIT_EDITMSG. To use the message, type the following command for
the next commit:
git commit -aeF .git/COMMIT_EDITMSG


具体的な部分を抜粋するとこんな感じ。
should系

 -    it { should render_template 'index' }
 +    it { is_expected.to render_template 'index' }

 -        UserMailer.should_receive(:password_reset).and_return(stub_mailer)
 +        expect(UserMailer).to receive(:password_reset).and_return(stub_mailer)

 -      it { should respond_with(404) }
 -      it { should render_template('errors/404') }
 +      it { is_expected.to respond_with(404) }
 +      it { is_expected.to render_template('errors/404') }

 -  its(:user) { should == @route.user }
 +  describe '#user' do
 +    subject { super().user }
 +    it { is_expected.to eq(@route.user) }
 +  end

最後の、「これもカバーしてるんか」って思った。


true, false系

 -          expect(post.draft?).to be_true
 +          expect(spot.draft?).to be_truthy

type系

 -describe ApplicationController do
 +describe ApplicationController, :type => :controller do

これは、さっき設定でカバーしたからいらないかったなぁ。


これは、後で追加するものとの一貫性を保ちたいので戻したい。たくさんあるので、手でやるのはキツイ。
オプションがないかな、と思ったらあった。

$ transpec -t

ヘルプは-hで確認できるので、他に飛ばしたいやつとかあればどうぞ。
また、このツールはgitに未コミットのものがあると起動されずにエラーを吐くため、、実行前後の状態が保ちやすい。そのため、こういうケースでも切り戻しが楽にできてすごくよかった。


で、テストが問題なく通るか確認する。

$ rspec
................................................................................................................................................................................................................................................................................................................................

Deprecation Warnings:

                                                                                                                                                              • -

Capybara::RSpecMatchers::HaveText implements a legacy RSpec matcher
protocol. For the current protocol you should expose the failure messages
via the `failure_message` and `failure_message_when_negated` methods.
(Used from /Users/ria10/IdeaProjects/ria10-web/spec/features/signup_spec.rb:10:in `block (2 levels) in ')

                                                                                                                                                              • -

~snip~

むぅ。これはcapybaraが2.2だったためで、2.3で対応されているらしい。なので最新の2.4にすればOKだった。
https://github.com/jnicklas/capybara/issues/1254

Gemfile書き換えてupdateして再実行

$ rspec
................................................................................................................................................................................................................................................................................................................................

Deprecation Warnings:

                                                                                                                                                              • -

Paperclip::Shoulda::Matchers::HaveAttachedFileMatcher implements a legacy RSpec matcher
protocol. For the current protocol you should expose the failure messages
via the `failure_message` and `failure_message_when_negated` methods.
(Used from /Users/ria10/IdeaProjects/ria10-web/spec/models/user.rb:8:in `block (2 levels) in ')

                                                                                                                                                              • -

~snip~

むむぅ。

これはpaperclipか。対応してるのかしら。ごく最近対応されていた。Gemにもなってた。
https://github.com/thoughtbot/paperclip/issues/1568#issuecomment-48601191

Gemfile書き換えてupdateして再実行

$ rspec
................................................................................................................................................................................................................................................................................................................................

Deprecation Warnings:

Requiring `rspec/autorun` when running RSpec via the `rspec` command is deprecated. Called from /Users/ria10/IdeaProjects/ria10-web/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:247:in `require'.

If you need more of the backtrace for any of these deprecations to
identify where to make the necessary changes, you can configure
`config.raise_errors_for_deprecations!`, and it will turn the
deprecation warnings into errors, giving you the full backtrace.

1 deprecation warning total

むむむぅ。

でもこれはWarningどおりやるだけで、spec_helper.rbから

require 'rspec/autorun'

を削除するだけだった。


これでテストがキレイに通った!


パーフェクト Ruby on Rails

パーフェクト Ruby on Rails

OSXでECDSA鍵を作ろう

http://d.hatena.ne.jp/hnw/20140705 を読んで、「ECDSA鍵にしていると一目置かれるのか!」と思ったので即座に対応することにした。

鍵長は、最低の256bitでもRSA鍵の3072bit相当ということで、無意味に強度を強くしてもコストの無駄と思うし、どうせ誰に見せる予定もないパンツをおしゃれなものに履き替える程度のモチベーションなので256bitでよしとすることにした。

で、生成しようとしたら・・・

$ ssh-keygen -t ecdsa -b 256
unknown key type ecdsa

なんと、おしゃれで一目置かれたい人たちが使うPCで定評のあるMacでこんな結果になるとは。

これで挫けたらイカンということで、sshをupdateする方法を調べた。あった。
http://www.dctrwatson.com/2013/07/how-to-update-openssh-on-mac-os-x/

さっそく実行。

$ brew tap homebrew/dupes
$ brew install openssh --with-brewed-openssl --with-keychain-support

$ launchctl stop org.openbsd.ssh-agent
$ launchctl unload -w /System/Library/LaunchAgents/org.openbsd.ssh-agent.plist
$ sudo vi /System/Library/LaunchAgents/org.openbsd.ssh-agent.plist # ここで/usr/bin/ssh-agent -> /usr/local/bin/ssh-agentに書き換える
$ launchctl load -w -S Aqua /System/Library/LaunchAgents/org.openbsd.ssh-agent.plist

で、ここまでやったら新しいターミナルを開いて・・・

$ ssh-keygen -t ecdsa -b 256
Generating public/private ecdsa key pair.
Enter file in which to save the key (/Users/ria10/.ssh/id_ecdsa):

やったねたえちゃん!


CentOSのyumでSegmentation fault

さくらのVPSでCentOS6.3での話。
たまにしかメンテしないので、いつのタイミングからこうなったのかもよくわからない。困った。

そういうときはstraceをかましてみる。

[root@ria10_no_server www]# strace yum update
~snip~
stat("/var/cache/yum/x86_64/6/jenkins/primary.xml.gz", {st_mode=S_IFREG|0644, st_size=27127, ...}) = 0
stat("/var/cache/yum/x86_64/6/jenkins/primary.xml.gz", {st_mode=S_IFREG|0644, st_size=27127, ...}) = 0
stat("/var/cache/yum/x86_64/6/jenkins/primary.xml.gz", {st_mode=S_IFREG|0644, st_size=27127, ...}) = 0
open("/var/cache/yum/x86_64/6/jenkins/primary.xml.gz", O_RDONLY) = 9
lseek(9, 0, SEEK_CUR) = 0
read(9, "\37\213\10\10\0\0\0\0\2\377/var/www/pkg.jenkins-c"..., 8192) = 8192

      • SIGSEGV (Segmentation fault) @ 0 (0) ---
      1. killed by SIGSEGV +++

Segmentation fault

これ、標準出力にビビるくらいアウトプットされるので、リダイレクトしたほうがいいかもしれないけど、とにかくセグフォが最後に来るケースがほとんどだと思うのでそのままでも問題ないかも。

で、jenkinsでコケてるので、どうもjenkinsの何かが問題らしい。このサーバでは今jenkinsはまともに運用されてないので消しちゃうことにした。

$ rm -rf /var/cache/jenkins
$ rm -rf /var/lib/jenkins/
$ yum remove jenkins

そしたら、スカッと治った。

ヨッシー New アイランド

ヨッシー New アイランド

Railsでページビュー(PV)をとるならimpressionist

https://github.com/charlotte-ruby/impressionist

「ブログ記事ごとのPVがとりたい!」がカンタンに実現できるgem。PV記録用のテーブルを別途作ってそこにアクセスを記録してくれる。

とりまGemfileに設定追加してbundle installからのmigrateで。
Gemfile

gem 'impressionist', '~> 1.5.1'

$ bundle install
$ bundle exec rails g impressionist
$ bundle exec rake db:migrate

あとは、カウントしたいcontrollerとmodelに設定をいれるだけ。

class EntriesController < ApplicationController
  impressionist actions: [:show]

記事一覧のPVは取らずに、記事詳細のPVだけとりたいので、showだけ指定。

モデルには一行いれるだけ。

class Entry < ActiveRecord::Base
  is_impressionable

これだけ。これで、テキトウな記事にアクセスしたあとにこんな感じで結果を確認すると・・・

irb(main):001:0> Entry.find(1).impressionist_count
Entry Load (0.5ms) SELECT `entries`.* FROM `entries` WHERE `entries`.`id` = 1 LIMIT 1
(0.5ms) SELECT DISTINCT COUNT(DISTINCT `impressions`.`request_hash`) FROM `impressions` WHERE `impressions`.`impressionable_id` = 1 AND `impressions`.`impressionable_type` = 'Entry'
=> 1

「おっ」と思ったのが、アプリにログイン機構が存在し、ログイン中のユーザーをbefore_actionでcurrent_userヘルパ、もしくは@current_userでアクセスできるようにしておくと、そのidをimpressions.user_idに記録してくれる。
リファラやIPはデフォで記録されるし、必要に応じてカスタムメッセージも記録できるらしいのでそのあたりはギッハブ参照のこと。

なお、http://www.user-agents.org/allagents.xml に記載されているbotのアクセスは弾かれるとのことで、特にその設定を変更するIFはないようでしたので、弾かれたくない人は自分でコード読んでなんとかするしかないようです。

こちらからは以上です。

マリオカート8

マリオカート8

Javascript初心者に初心者++程度の自分が言語仕様を説明したときのメモ(第五回)

Javascript初心者に初心者++程度の自分が言語仕様を説明したときのメモ(第四回)の続き

今回はnewの動作について。ほぼ、以下のリンクの和訳。。
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new

  1. 新しいオブジェクトを作り、コンストラクタ関数のprototypeを引き継ぐ。
  2. コンストラクタ関数を実行する。その際、thisには1で作られた新しいオブジェクトを設定する。
  3. 戻り値として、1で作られた新しいオブジェクトを返す。ただし、コンストラクタ関数内で、プリミティブ以外の値が返されているときは、その値が返され、1で作られた新しいオブジェクトは返されない。
function foo() {  // コンストラクタ関数
  this.a = 1;      // このthisはnewした場合は2.のとおり、あたらいオブジェクトを指す
                        // 何もreturnしていないが、this(新しく作られたオブジェクト)が返される
}
foo.prototype.getA = function() { // この内容が引き継がれる
   return this.a;
}

var obj = new foo(); // () がなくても同様に動作する。


こんなとこなのだけれども、ここで、さらっと書いた1の「コンストラクタ関数のprototypeを引き継ぐ」についてもうちょっと深堀りをしていこうと思う。


この引継の動作を理解するにあたっては、Javascriptの以下の特性を抑えていく必要がある
参考:http://zeekat.nl/articles/constructors-considered-mildly-confusing.html#sec-1

  • すべてのオブジェクトには内部的にprototypeというプロパティを持っている。以下、この内部的なprototypeを[prototype]と表記。
    • [prototype]はobj.prototypeとイコールではない。
  • [prototype]を直接読み込む方法はない
    • objの任意のプロパティが参照されたとき、そのプロパティが存在しない場合に、[prototype]が参照される
    • デベロッパーコンソールとかで見られる__proto__とかが[prototype]を指しているので、アクセスできるかもしれないが、今のところこの__proto__へのアクセスは推奨されていない。このへんは「ECMAScript」とかそのあたりをググりながら別途調べてください。
  • [prototype]はオブジェクト生成時以外には書き込みできない

※実際は、コンストラクタ関数以外にもObject.create()などのオブジェクト生成方法があるけど、ややこしくなるのでここでは触れない


つまり、前回のプロトタイプチェーンの話で触れた「prototypeをたどる」という動作は、[prototype]の内部で起っているということになる。

前から、なんで以下のように、newして作ったオブジェクトのprototypeにアクセスできないのか不思議に思っていたのだけども、これでなっとくできた。

hoge = new Hoge();
hoge.prototype.a = 1 // TypeError: Cannot set property 'a' of undefined

そして、なんでアクセスできないような仕様になっているのかというと、継承関係がカオスになるからなんだろうなぁ、となんとなく思っておくくらいで、今回は済ませておくことにしました。

マリオカート8

マリオカート8

Javascript初心者に初心者++程度の自分が言語仕様を説明したときのメモ(第四回)

Javascript初心者に初心者++程度の自分が言語仕様を説明したときのメモ(第三回)の続き。

前回ではprototypeのメリットの1つである、複数オブジェクトの関数の共有を確認した。

今回は、次のメリットのいわゆるプロトタイプチェーンについて確認してみる。

プロトタイプチェーンとは、"他のオブジェクト(または null )への内部的な繋がり"を持つprototypeを通して、クラスベースの言語で言うところの「オブジェクトの継承」と同じようなことを実現する仕組みで、以下のようにして実現できる。

function Animal() {
  this.name = 'Animal';
  this.voice = '------';
}
Animal.prototype = {
	bark: function(){
		return this.name + " barks: " + this.voice;
	}
}

animal = new Animal();

animal.bark(); // "Animal barks: ------"

これがベースにする予定のオブジェクトで、特筆するところもない。

この動物オブジェクトを引き継いだ犬オブジェクトを作る。

function Dog() {
	this.name = "dog";
	this.voice = "bowwow!"
}

Dog.prototype = animal;

var dog = new Dog();
dog.bark(); // "dog barks: bowwow!"

nameとvoiceはDogオブジェクトのものを使っているが、Dogオブジェクトでは定義していないbark関数が使えている。

今度は、関数以外のプロパティも継承しないオブジェクトを作ってみる。

function Rabbit() {
	this.name = "rabbit";
}

Rabbit.prototype = animal;

var rabbit = new Rabbit();

rabbit.bark(); // "rabbit barks: ------"

Rabbitオブジェクトではvoiceを定義しなかった。それでも、undefinedとはならず、Animalオブジェクトで定義したvoiceが参照されていることがわかる。
なお、うさぎ好きの人は「うさぎも鳴くよ」と言いたくなるかもしれないが、そこはスルーでおねがいします。

この通り、プロトタイプチェーンとは、アクセスされた各プロパティについて、自オブジェクトに存在しなければ、prototypeをたどっていき、発見したらそれを使う、という仕組みだ。

ちなみにJavascriptでは、delete演算子でプロパティを削除することもできる。

bulldog = new Dog();
bulldog.bark();
// ブルドックはあまり鳴かないらしい
delete bulldog.voice;

bulldog.bark(); // "dog barks: ------"

とこの通り、dogからvoiceを削ってみると、Animalオブジェクトのvoiceが参照され、bark()の結果が変わったことが確認できる。

今回はプロトタイプチェーンの基礎編ということで、終わり。

マリオカート8

マリオカート8