コピペコードで快適生活

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

Rubyでたまにしか使わないけど便利なメソッドメモ

備忘のため。随時追加していく。

Ruby

# フィールドをもつモックオブジェクトを作る
Struct.new(:name, :tel).new('hogeo', '090xxxxxxxx')
# => #<struct name="hogeo", tel="090xxxxxxxx">

Rails

# 引数の文字列で指定した名前で定数を探す
'::MyApp::User'.constantize
# => MyApp::User(id: integer, first_name: string, last_name: 

DeveloperConsoleに貼って、さっとCORSのチェックをやりたいとき用JS

xhrReq = function(url) {
  let xhr = new XMLHttpRequest();
  xhr.open('GET', url, true);
  // プリフライト(OPIONSメソッドによる事前確認)をする場合は、
  // カスタムヘッダをセットする
  xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
  
  xhr.onreadystatechange = function(){
    if (xhr.readyState === 4 && xhr.status === 200){
      console.log(xhr.responseText);
    }
  };
  xhr.send(null);
};

自動ログインするブックマークレット

↓のコードを適宜修正して、ブックマークのURL欄にコピペする。

javascript:(function(){document.querySelector('input[name=login_id]').value = 'LOGIN_ID'; document.querySelector('input[name=password]').value = 'PASSWORD'; document.querySelector('input[name=submit]').click(); }());

prependでクラスメソッドを拡張する

singleton_classにprependすればOK。

class Hoge
  def self.hogehoge(*args)
    puts 'hogehoge'
  end
end

Hoge.hogehoge
# => hogehoge

Hoge.singleton_class.prepend Module.new {
  def hogehoge(*args)
    super(*args)
    puts 'extended hogehoge'
  end
}

Hoge.hogehoge
# => hogehoge
# => extended hogehoge

# 継承ツリー
Hoge.ancestors
# => [Hoge, Object, Kernel, BasicObject]
Hoge.singleton_class.ancestors
# => [#<Module:0x007fc15e090f70>, #<Class:Hoge>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]

class SomeClass
  class << self
    def some_classmethod
      "Hello"
    end
  end
end

SomeClass.some_classmethod
#=> Hello

singleton_class(特異クラス)とは、
すべてのオブジェクトが持つ自分だけの隠しクラスのこと。
インスタンスに独自に定義したメソッド(特異メソッド)は、
この特異クラスに定義しているという扱いらしい。

参考
https://qiita.com/ponoda/items/bfcf5533b532a6d32111
https://allabout.co.jp/gm/gc/453836/#note1

複数のサーバで一度にsshコマンドを実行したい

メモ。

HOSTS="host01 host02 host03"
for host in ${HOSTS}; do
echo $host
ssh -t $host <<EOC
hostname
sudo service td-agent restart
sleep 1
ps aux | grep td-agent
EOC
echo "--------------------"
done

参考
SSHで一気にコマンドいろいろ投げたい

既存RailsアプリにRspecを入れる

既存RailsアプリにRspecを入れたときのメモ。

インストール

Gemfileに追記

group :development, :test do
  gem 'rspec-rails'
  gem 'factory_bot_rails'
  gem 'database_cleaner'
end

RSpecの設定
必要なファイルが作成される。

bundle install
bundle exec rails generate rspec:install

設定をする

config/application.rb

# config/application.rb
config.generators do |g|
  # 不要なファイルを生成しない
  g.test_framework :rspec,
    fixtures: true,
    view_specs: false,
    helper_specs: false,
    routing_specs: false,
    controller_specs: true,
    request_specs: false

  # テストデータのパス設定
  g.fixture_replacement :factory_bot, dir: "spec/factories"
end

spec/rails_helper.rb

# spec/rails_helper.rb
RSpec.configure do |config|
  # 略

  # user = FactoryBot.create(:user) -> user = create(:user) とかけるようにする
  config.include FactoryBot::Syntax::Methods

  # Time.zone.nowを、スライドできるようにする
  config.include ActiveSupport::Testing::TimeHelpers
end

spec/spec_helper.rb

# spec/spec_helper.rb
require 'database_cleaner'
# 略
RSpec.configure do |config|
  # 略

  # テスト後にDBのデータを消す設定
  config.before(:suite) do
    DatabaseCleaner[:active_record].strategy = :transaction
    DatabaseCleaner.clean_with(:truncation)
  end
  
  config.around(:each) do |example|
    DatabaseCleaner.cleaning do
      example.run
    end
  end

  # 略
end

テストデータ/テストコードを書く

development環境のDB構造をtest環境へクローンする

bundle exec rake db:test:clone

テストデータを作る

#  spec/factories/my_app/subscription.rb
FactoryBot.define do
  # class: でどのクラスのインスタンスを作るか指定できる
  factory :subscription, class: ::MyApp::Subscription do
    user_id 1
    course_id 1
    status nil

    # 引数で与えられた値で属性を変えることができる
    trait :trial do
      status :trial
    end

    trait :contracted do
      status :contracted
    end

    # 関連テーブルも一緒に作る場合
    trait :with_entitlements do
      after(:build) do |subscription|
        subscription.entitlements << FactoryBot.build(:entitlement__svod)
      end
    end
  end
end

テストコードを書く

# spec/models/my_app/subscription_spec.rb
require 'rails_helper'

describe ::MyApp::Subscription do
  before do
    # createはDB保存された状態のインスタンスを作る
    # beforeで定義した変数はインスタンス変数にしないと内部で参照できない
    @user = create(:user)
  end

  describe 'お試し契約する (テスト対象を書く)' do
    context '契約可能期間のとき (条件を書く)' do
      before do
        # 引数つきで属性を変える / buildeだとDB保存はしない
        @subscription = build(:subscription, :trial, :with_entitlements)
      end
      it '保存できる (期待する結果を書く)' do 
        result = @subscription.save
        expect(result).to be_truthy
        # expect(result).to eq true でもOK
      end
    end

    context '契約可能でない期間のとき' do
      before do
        @subscription = build(:subscription, :trial, :with_entitlements)
      end
      # 時間移動はaroundでも書ける
      # around do |e|
      #  travel_to('2016-2-29 10:00'.to_time){ e.run }
      # end
      it '保存できない' do 
        # このブロック内でTime.zone.nowが1年後になる
        travel 1.year
        result = @subscription.save
        expect(result).to be_falsey
        # travel_back すると Time.zone.nowが戻る
      end
    end
  end
end

実行する

# ファイル指定
bundle exec rspec spec/models/my_app/subscription_spec.rb

# ディレクトリ以下実行
bundle exec rspec spec/