ためすう
eager_load! を使ってみる (Rails)
2020-10-25やったこと
eager_load! を使ってみます。
例えば設定で eager_load を使わない Rails.env の時、 ファイルを先にロードしておきたい時などに使います。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
$ rails --version
Rails 5.2.4.1
調査
app/models/book.rb
class Book < ApplicationRecord
belongs_to :auther, class_name: Author
end
Author クラスは存在していないクラスとします。
config/environments/development.rb
Rails.application.configure do
# 抜粋
# Do not eager load code on boot.
config.eager_load = false
end
出力結果
$ bundle exec rails c
Running via Spring preloader in process 5654
Loading development environment (Rails 5.2.4.1)
irb(main):001:0> Rails.application.eager_load!
Traceback (most recent call last):
3: from (irb):1
2: from app/models/book.rb:1:in `<main>'
1: from app/models/book.rb:3:in `<class:Book>'
NameError (uninitialized constant Book::Author)
eager_load! すると落ちることが確認できます。
こんな感じで、shell 上で rails のコマンドを実行して、 ロード失敗するクラスを検知することも出来るようになります。
$ bundle exec rails runner 'Rails.application.eager_load!'
Running via Spring preloader in process 7687
Please specify a valid ruby command or the path of a script to run.
Run 'bin/rails runner -h' for help.
uninitialized constant Book::Author
Hugo のインストール、セットアップ
2020-10-24やったこと
Hugo のインストール、セットアップをした時のメモを残します。
確認環境
$ hugo version
Hugo Static Site Generator v0.53/extended darwin/amd64 BuildDate: unknown
hugo インストール
$ brew install hugo
プロジェクトセット
$ hugo new site tech-blog
テーマ適用
ブログのテーマを適用します。
$ git submodule add https://github.com/qqhann/hugo-primer themes/hugo-primer
$ echo 'theme = "hugo-primer"' >> config.toml
記事投稿
$ hugo new post/my-first-post.md
サーバー起動
$ hugo server -D
jekyll から Hugo に移行させたのですが、記事のビルドのスピードが圧倒的に速くなりました。
rails runner を使ってみる (Rails)
2020-09-12やったこと
runnerコマンドを使うと、非対話的にRailsの文脈でRubyのコードを実行することができます。たとえば次のようになります。
シェルのコマンドで、Rails のメソッドを使ってみます。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
$ rails --version
Rails 6.0.3.2
調査
実行するコードをそのまま渡す
$ rails runner 'puts Rails.env'
出力
Running via Spring preloader in process 37027
development
外部ファイルを実行してみる
lib/hoge.rb
class Sample
def self.hello
puts 'called: hello'
puts Rails.env
end
end
Sample.hello
出力
$ rails runner lib/hoge.rb
Running via Spring preloader in process 38718
called: hello
development
参考
file を使ってみる (Unix)
2020-09-07やったこと
ファイルの種類を識別するコマンドの file
を使ってみます。
man file
より抜粋
file tests each argument in an attempt to classify it.
調査
ファイルを用意する
a.php
<?php
echo 'hoge'
b.rb
class Sample
def hoge
p 'hoge'
end
end
Sample.new.hoge
実行結果
$ file a.php
a.php: PHP script text, ASCII text
$ file b.rb
b.rb: Ruby module source text, ASCII text
php
, ruby
を識別することができました。
rspec で pending を使ってみる
2020-09-04やったこと
rspec の pending を使ってみます。
確認環境
$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]
$ rails --version
Rails 5.2.3
$ gem list | grep rspec-rails
rspec-rails (3.8.2)
調査
spec/models/task11_spec.rb
require 'rails_helper'
RSpec.describe Task, type: :model do
it 'failedケース pendingあり' do
pending('pending理由を何か書く')
raise '例外発生1'
end
it 'successケース pendingなし' do
expect(3 + 4).to eq 7
end
it 'successケース pendingあり' do
pending('pending理由を何か書く')
end
end
出力結果
$ bundle exec rspec spec/models/task11_spec.rb
Task
failedケース pendingあり (PENDING: pending理由を何か書く)
successケース pendingなし
successケース pendingあり (FAILED - 1)
Pending: (Failures listed here are expected and do not affect your suite's status)
1) Task failedケース pendingあり
# pending理由を何か書く
Failure/Error: raise '例外発生1'
RuntimeError:
例外発生1
# ./spec/models/task11_spec.rb:7:in `block (2 levels) in <top (required)>'
Failures:
1) Task successケース pendingあり FIXED
Expected pending 'pending理由を何か書く' to fail. No error was raised.
# ./spec/models/task11_spec.rb:14
Finished in 0.00317 seconds (files took 1.58 seconds to load)
3 examples, 1 failure, 1 pending
Failed examples:
rspec ./spec/models/task11_spec.rb:14 # Task successケース pendingあり
pending メソッドを使った場合、後続の処理でエラーが発生しないと
そのケースは失敗となります。(エラーが発生するのが正しいため)
rails-assets とサヨナラした話
2020-09-01やったこと
rails-assets を利用しているシステムから、切り離した時の話です。
調査
rails-assets とは
The solution to assets management in Rails
js, css の依存関係について、gem を介して解決します。
しかし、rails-assets は近年のシステムでフロントエンド、バックエンドを明確に分けるという流れもあり、メンテナンスされない事が決まっていました。
そのような背景もあり、高頻度でサービスに繋がらなくなることありました。
GitHub の issues の例 サービスが落ちる度に、issue が立ち上がっています。
rails-assets の gem を自前でホスティングするように変更
rails-assets 経由で、ダウンロードできる gem が欲しかったのですが、
このサイトでは調べる事ができないようでした。
rails-assets を Gemfile に書いてインストールされているものを gem にすれば OK と思っていたのですが、それで正しいのか不安でした。
本家の rails-assets
をローカルで gem を生成する事ができると分かりました。
なので、下記の2つの差分を見比べて、ソースコードに差異がなければ OK ということにします。
1.rails-assets から gem を生成したもの
2.rails-assets を Gemfile に書いてインストールされたもの
2.rails-assets を Gemfile に書いてインストールされたもの
は、既に持っているので、
1.rails-assets から gem を生成したもの を頑張って作ります。
rails-assets から gem を生成する
tenex/rails-assets: The solution to assets management in Rails
基本的には、上記の README.md
を見ながらセットアップすればOKです。
ここで注意があります。
メンテナンスされてないため、Ruby, npm のバージョンが新しいと動きません。
ハマったところを書いておきます。必要なバージョンは頑張ってインストールします。
- Ruby 2.2.3 (.ruby-version)
- npm 6.14.4
また foreman と postgresql を動かしておく必要があります。
$ foreman start
$ postgres -D /usr/local/var/postgres
今回は select2
の gem を生成してみましょう。
$ bin/rake 'component:convert[select2,4.0.3]'
これで gem が生成されます。
public/gems/rails-assets-select2-4.0.3.gem
生成した gem を展開します
$ gem unpack public/gems/rails-assets-select2-4.0.3.gem
差分を比較する
差分を確認してみます。
$ diff -r {1. rails-assets から gem を生成したもの} {2. rails-assets を Gemfile に書いてインストールされたもの}
動作確認
- GitHub のリポジトリを新設
- bundle install して動作確認
これで rails-assets の gem を使ってる箇所を確認できれば、OKです。
gem を生成した後、ローカルの gem を bundle install できるようにする
ここはおまけなので、別記事で書きます。(多分)
最後に
こういう作業は、もうやりたくないですね。
参考
assign を使ってみる (C++)
2020-07-18確認環境
$ g++ --version
g++ (Homebrew GCC 9.2.0) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
調査
test.cpp
#include <bits/stdc++.h>
#include <cassert>
using namespace std;
int main() {
vector<int> v;
// 5個の要素を値10で埋める
v.assign(5, 10);
for (auto val : v) {
cout << val << " ";
}
cout << endl;
cout << "---" << endl;
// 2個の要素に置き換える
vector<int> before_v = {5, 4, 3, 2, 1};
v.assign(before_v.begin(), before_v.begin() + 2);
for (auto val : v) {
cout << val << " ";
}
cout << endl;
}
出力結果
10 10 10 10 10
---
5 4
参考
gem を複数バージョンでテストするため、appraisal を使ってみる
2020-07-12最近、Rails Upgrade のプロジェクトをやったのですが、そのとき困ったことがありました。
複数のリポジトリに分けてソースコードを使っており、それぞれのリポジトリが共通して利用している社内gemがあり、複数の Rails バージョンで動作を保証する必要が出てきました。
いままでは複数のリポジトリが同じRailsバージョンだったので、大きな問題には、ならなかったのですが 社内gem が複数の Rails で動くことを保証するため、 複数の Rails バージョンでテスト (rspec) を実行するようにしました。
「なんで “複数の Rails で動くことを保証” する必要があるのですか?」と思われるかもしれませんが、 このようなリポジトリが、Railsのバージョンが新しい順にあるとします。
- リポジトリA (ver. x)
- リポジトリB (ver. y)
- リポジトリC (ver. z)
(文字だけだとイメージしづらいので、図を置いておきます)
例えばですが、x になってから新しいメソッド def only_x
が追加された時を考えましょう。
この新しいメソッドを使っていたとして、ver. y
ver. z
では動かないとなります。
そのために my-gem.gemspec
で rails のバージョンを指定すればいいのですが、先行して各リポジトリが依存している 社内gemの my-gem
をリリースすることが出来ないということになってしまいます。
- 各リポジトリが社内gemの
my-gem
に依存している - 日々、社内gemの
my-gem
は開発が行われている (差分がどんどん出る)
そんな時に、この gem を使いました。
今回は、社内gem (my-gem という名前とします) で、下記のRailsバージョンでテスト (rspec) を実行できるようにします。
- Rails 5.1.0
- Rails 5.2.3
- Rails 6.0.0
確認環境
$ ruby --version
ruby 2.5.5p157 (2019-03-15 revision 67260) [x86_64-darwin17]
調査
Github にある通りにセットアップしていきます。
Appraisals ファイルの準備
Appraisals
appraise "rails-5.1.0" do
gem "rails", "5.1.0"
end
appraise "rails-5.2.3" do
gem "rails", "5.2.3"
end
appraise "rails-6.0.0" do
gem "rails", "6.0.0"
end
インストール
my-gem.gemspec
group :development do
# 省略...
gem 'appraisal'
end
$ bundle install
$ bundle exec appraisal install
このファイルが出来上がります。 各Railsバージョンごとの依存を管理しています。
gemfiles/rails_5.1.0.gemfile
gemfiles/rails_5.2.3.gemfile
gemfiles/rails_6.0.0.gemfile
テストファイル用意
spec/models/user_spec.rb
require 'rails_helper'
RSpec.describe User, type: :model do
it '計算1 should' do
(1 + 2).should eq 3
end
it '計算2 expect' do
expect(3 + 4).to eq 7
end
end
テスト実行
それではテストを実行してみましょう。
$ RAILS_ENV=test bundle exec appraisal rspec spec/models/user_spec.rb
Rails5.1.0 でのテスト
>> BUNDLE_GEMFILE=/my-gem/gemfiles/rails_5.1.0.gemfile bundle exec rspec spec/models/user_spec.rb
Run options: include {:focus=>true}
All examples were filtered out; ignoring {:focus=>true}
..
1 deprecation warning total
Finished in 0.21237 seconds (files took 5.08 seconds to load)
2 examples, 0 failures
Rails5.2.3 でのテスト
>> BUNDLE_GEMFILE=/my-gem/gemfiles/rails_5.2.3.gemfile bundle exec rspec spec/models/user_spec.rb
Run options: include {:focus=>true}
All examples were filtered out; ignoring {:focus=>true}
..
1 deprecation warning total
Finished in 0.22129 seconds (files took 5.45 seconds to load)
2 examples, 0 failures
Rails6.0.0 でのテスト
>> BUNDLE_GEMFILE=/my-gem/gemfiles/rails_6.0.0.gemfile bundle exec rspec spec/models/user_spec.rb
Run options: include {:focus=>true}
All examples were filtered out; ignoring {:focus=>true}
..
1 deprecation warning total
Finished in 0.07676 seconds (files took 6.03 seconds to load)
2 examples, 0 failures
これで、Appraisals ファイルに記述してある3種類の Rails バージョンで rspec が実行されます。
ちなみに、こんな感じで、Rails 5.2.3
だけをテストすることも可能です。
$ RAILS_ENV=test bundle exec appraisal rails-5.2.3 rspec spec/models/user_spec.rb
はい。これで、特定のRailsバージョンが動かない処理が書かれたときに検知できますね!
みなさん、お気づきかと思いますが、開発する時に自動テストがきちんと書かれているから検知できるのです。
私は、自動テストの効果についてピンと来てませんでしたが、Rails Upgrade のプロジェクトを通じて、自動テストの重要性に気付かされました。
ラジアンと角度の変換をする (C++)
2020-07-11確認環境
$ g++ --version
g++ (Homebrew GCC 9.2.0) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
調査
test.cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
// degree -> radian
double target_degree = 45;
double radian = target_degree * M_PI / 180.0;
printf("radian: %.10f\n", radian);
// radian -> degree
double degree = radian * 180.0 / M_PI;
printf("degree: %.10f\n", degree);
}
出力結果
radian: 0.7853981634
degree: 45.0000000000
std::prev, std::next を使ってみる (C++)
2020-07-09確認環境
$ g++ --version
g++ (Homebrew GCC 9.2.0) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
調査
test.cpp
#include <bits/stdc++.h>
using namespace std;
int main() {
vector<int> v = {5, 3, 2, 9, 8};
auto iter = v.begin();
cout << "begin(): " << *iter << endl;
iter = next(iter);
cout << "begin() + next1回: " << *iter << endl;
iter = next(iter);
cout << "begin() + next2回: " << *iter << endl;
iter = prev(iter);
cout << "begin() + next2回 + prev1回: " << *iter << endl;
// この指定だと2回進む
iter++;
iter += 1;
cout << "begin() + next2回 + prev1回 + 2回進む " << *iter << endl;
}
出力結果
begin(): 5
begin() + next1回: 3
begin() + next2回: 2
begin() + next2回 + prev1回: 3
begin() + next2回 + prev1回 + 2回進む 9