久しぶりにRailsで開発して感じたこと
数年ぶりにガッツリRailsで開発するプロジェクトに参画している。その中で感じたことを徒然なるままに書こうと思う。なお、「それRailsじゃなくてRubyの話っしょ?」という内容も多分に含んでいると思うが、そこはご勘弁。
久しぶりにRailsで開発して感じたこと Read More »
数年ぶりにガッツリRailsで開発するプロジェクトに参画している。その中で感じたことを徒然なるままに書こうと思う。なお、「それRailsじゃなくてRubyの話っしょ?」という内容も多分に含んでいると思うが、そこはご勘弁。
久しぶりにRailsで開発して感じたこと Read More »
まずはcapybaraのセットアップからです。selenium-webdriver経由でヘッドレスchromeを操れるように設定します。最初に必要なライブラリをhomebrewでインストールしておきます。
$ brew install chromedriver
次に必要なgemをインストールするため、Gemfileを作ります。
source "https://rubygems.org" git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } gem 'capybara' gem 'selenium-webdriver' gem 'launchy'
lauchyというのはスクリーンショットを取ったと同時にプレビュー.appで開くようにしてくれるgemです。いつも通りbundle installしてください。
これでcapybaraを使用する準備ができたので、capybaraの初期設定をします。
require 'capybara' require 'selenium-webdriver' Capybara.register_driver :selenium do |app| Capybara::Selenium::Driver.new(app, browser: :chrome, desired_capabilities: Selenium::WebDriver::Remote::Capabilities.chrome( chrome_options: { args: %w(headless disable-gpu window-size=1280,800), }, ) ) end Capybara.javascript_driver = :selenium
chrome_options の詳しい説明などは こちら で確認してみてください。
ひとまず、これでcapybaraを使用する準備が整いました。
ではgoogleのトップページにアクセスして、スクリーンショットを保存してみましょう。先程のscrape.rbに次のコードを追記します。
... def start_scraping(url, &block) Capybara::Session.new(:selenium).tap { |session| session.visit url session.instance_eval(&block) } end start_scraping 'https://www.google.com/' do # ここにスクレイピングのコードを書く p title #=> "Google" save_and_open_screenshot end
Capybara::Sessionを開始して、Googleのページのスクリーンショットを保存します。ヘッドレスなので実際にChromeは可視化しませんが、ちゃんとアクセスできているのがスクリーンショットを見れば分かると思います。
あとは煮るなる焼くなり好きにして下さい。capybaraの使い方に関しては解説サイトもたくさんあるので、ググってみてください。
サンプルプロジェクトをpushしていますので、よかったら使ってください。
capybaraを使ってWebページをスクレイピングする(テストじゃなくて) Read More »
たとえばUserクラスとConfigクラスがあったとして、それぞれ次のような関連を持ってるとする。
class User < ApplicationRecord has_one :config end class Config < ApplicationRecord belongs_to :user end
各ユーザーが各々設定を持つというシチュエーションを想定している。で、この場合に親のファクトリをcreate(:config)のような感じで作るときに、まとめてUser側の属性も設定したいということ。
通常の書き方だと、Configのインスタンスを得るにはこんな感じになるはず。(たぶん)
config = create(:user. name: '...', email: '...', config: build(:config, attr1: '...', attr2: '...')).config
それを次のように書けるようにする。
config = create(:config, attr1: '...', attr2: '...', user: { name: '...', email: '...' })
「なんだ、ほとんど変わんねえじゃねーか」とお思いになるかもしれない。しかし前者は、Configのインスタンスが欲しいんだということがひと目で分からない。また、build :config してるのもいささか冗長だ。後者の方がテストの関心がConfigにあることが一目瞭然だし、タイプ量も少なくてすむ。細かいことだけど、こういうのって結構大事だと思うんだな。
で、肝心のファクトリの定義だが、私はこんな感じに定義している。
FactoryGirl.define do factory :config do attr1 '...' attr2 '...' transient do user { {} } end after(:build) do |config, evaluator| config.user = build(:user, evaluator.user) if evaluator.user.present? end end end
userをtransientにしておきuserの属性(Hash)を渡してもらう。after(:build)の中でuserのファクトリに横流してbuildしているのでuserのファクトリ定義を上書きすることができるというわけ。なお親から子の属性を設定する場合も同様の方法が使えると思う。
これがベストの解かは分からない。もっといい方法があれば教えて欲しいッス。
【factory_girl】子のファクトリ生成時に親の属性をまとめて設定する Read More »
まずは rails 5 のコードから。次のコードを適宜編集して、config/initializersの下に置く。
unless Rails.env.production? reloader = Rails.application.config.file_watcher.new(['path_to_file']) do # do_something end Rails.application.reloaders << reloader ActiveSupport::Reloader.to_prepare do # reloader.execute <=他のファイルが更新されても実行するならこちら reloader.execute_if_updated end end
なにをしているかというと、まずRails.application.config.file_watcherでファイルの更新監視クラスを取ってきている。普通はconfig/environments/development.rb
には次のような設定がしてあるはずなので、
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
ActiveSupport::EventedFileUpdateCheckerクラスが取得できるというわけだ。こいつに監視対象のファイルパスを渡し、ブロック内に実施したい処理を書けばよい。
次に、ActiveSupport::Reloader.to_prepare の部分だが、何らかのファイルに更新があった次のリクエスト処理時に、このブロックが実行されることになっている。ここで先ほど生成したreloaderを実行してやればよい。
reloader.execute_if_updatedというのは監視対象のファイルが更新された場合にだけreloaderを実行する。通常はこのメソッドを使えばよいと思う。他のファイルが更新された場合でもreloaderを実行したい場合はreloader.executeだ。
基本的にはrails 5と同じなのだが、少しだけ使用するクラスが違う。
unless Rails.env.production? reloader = ActiveSupport::FileUpdateChecker.new(['path_to_file']) do # do_something end Rails.application.reloaders << reloader ActionDispatch::Reloader.to_prepare do reloader.execute_if_updated end end
やってることはrails 5と同じなので、説明は省略。
ymls = Dir.glob("#{Rails.root}/app/dsls/config/search/*.rb") ymls.each { |yml| load yml } unless Rails.env.production? ymls.each do |yml| reloader = Rails.application.config.file_watcher.new([yml]) do load yml end Rails.application.reloaders << reloader ActiveSupport::Reloader.to_prepare do reloader.execute end end end
実際に私が使用している例。自作DSLがyamlファイルを読み込むのだが、yamlを変更する際にサーバを再起動しないでいいように、上のようにしてみた。
もともとは、settingslogicという定数管理用のgemを使用していたのだが、yamlを変更するとサーバを再起動しないといけないのが嫌で、解決策を探してみた。
ちなみに拙作の定数管理gemは、サーバを再起動しなくても、定数を記述するyamlを変更したら自動でリロードするようにしてある。
その中のここのソースでrails4と5を切り替えてreloaderを登録するようにしているので、gemを作る人なんかには参考になるかもしれない。
【Rails】リロード時に特定の処理を実行する Read More »
まずはrspecようのヘルパーモジュールを書く。私の場合はsupportディレクトリ内に置いて読みこむようにしてるので、spec/support/decorator_spec_helper.rb に書く。
module DecoratorSpecHelper def self.included(base) base.class_eval do before do controller = ApplicationController.new controller.request = ActionDispatch::TestRequest.create ActiveDecorator::ViewContext.push controller.view_context end end end end
テスト用のコントローラとリクエストを生成して、そこからViewContextをガメるという作戦。
続いてrails_helper.rb に以下のコードを追加する。
config.include DecoratorSpecHelper, type: :decorator
あとはテストしたいデコレーターにtype: :decorator を忘れずに付けるだけ。
require 'rails_helper' RSpec.describe HogeDecorator, type: :decorator do ... let(:video) do ActiveDecorator::Decorator.instance.decorate(Hoge.new) end ...
以上。
【Rails 5】ヘルパーを使用しているactive_decoratorをrspecでテストする Read More »