RailsでDBのデータ変更はどこに書く? - komagataのブログ

ブクマ・閲覧が多いようなので僕の結論書いときます。

  • データ変更はrakeのタスクとして書く。
  • migrationには書かない。
  • 初期データはseedに書く。

例えば、運営側しか触らないCategoryが増えたとしたら、seedに追記し、rakeタスクとしてcategory追加するタスクを書き、デプロイ時に実行します。このrakeタスクは一回実行するだけなのでしばらく立ったらリポジトリからも消しちゃいますね。

DBの状態が秘伝のタレ化しないようにseed整備は大事。

実データ(ほとんど本番と同じ)で開発すべきというのは別の話題。

railsらしいやり方、turbolinksともマッチするjavascriptの書き方ができるturboctrlというgemをリリースしました。

komagata/turboctrl

これは何?

/posts/1にアクセスしたらapp/assets/javascripts/controllers/posts_controller.js.coffeeにあるPostsControllerクラスのshowメソッドが呼ばれるというものです。
# app/assets/javascripts/controllers/posts_controller.js.coffee:
class @PostsController
  show: ->
    console.log "Hey!"

なぜ作ったのか

「さっさとwebアプリを書きたい。」

「RailsのRailから外れる面倒なことはしたくない。」

「Railsはjsの書き方についてRailがなさすぎる。」

「Railsっぽいままjsを書きたい」

「Sprockets捨てるとかではなく、既存の仕組みのまま行きたい」

というのがモチベーションです。

「coffeeの1ファイルに1クラスだけ書く」というルールすら共有されてないと行くプロジェクト行くプロジェクトで辛いんですよね。

class @Foo

sprocketsを使う以上、上記のようなクラスの書き方しますよってとこも共通認識にしたい。

ご意見、ご要望、Issue、PR歓迎です。

質問いただいたのでブログに書いときます。(同一LAN内のiPadで確認するときも使えますよー。)

ネットワークユーティリティで自分のMacのIPアドレスを確認します。

上記のIPアドレスを指定してrailsを起動します。(bindのbです)

$ rails s -b 192.168.1.13

そのIPにGenymotionからアクセスすれば見れます。

やったね。

毎回ゲストとペアプロする画面を公開するペアプロキャストの2回目の後編を公開しました。

@kjirouがドライバー、@komagataがナビゲーターでkowabana.jp(rails)をペアプロしています。rails4.1でdeprecatedになった機能を外していくという非常に地味な内容になっています。

railsでkowabana.jpを開発 - ペアプロキャストep2後編@kjirou - YouTube

railsでkowabana.jpを開発 - ペアプロキャストep2後編@kjirou - ニコニコ動画:GINZA

flunkとか使ってます?ニコ動コメント等でフィードバックいただけるとありがたいです。

ペアプロキャスト - YouTube再生リスト

ペアプロキャスト - ニコニコ動画マイリスト

ActionController::RoutingError: No route matches [POST] "/stories/2992/trackback"

みたいな存在しないURLで、人間のユーザーには影響無く、botがアクセスしてくるエラーってどうすればいいのかな。

routes.rbの一番下に404へ飛ばすの書いておけばいいけど、人間のユーザーにも影響のあるNo route matchesのエラーもある気がするし…。

でもエラー管理サービス(rollbar)の無料枠のエラー数を消費してしまうので何とかしたいところです。

rails4.2系にアップグレード中に嬉しい情報が。

~/code/kowabana[upgrade-to-rails4.2.1]% rake test:all
DEPRECATION WARNING: rake test:all is deprecated and will be removed in Rails 5. Use rake test to run all tests in test directory. (called from mon_synchronize at /usr/local/var/rbenv/versions/2.1.2/lib/ruby/2.1.0/monitor.rb:211)

$ rakeで済むところが$ rake test:allって書かなきゃいけないのクッソうざかったんですよね。デフォルトテストスイートの嬉しさ減的な。

CIの設定でも$ rakeで済んだら素敵なのにと思ってたんです。

例えばコメント投稿のテスト。

# test/integration/post_comment_to_comic_test.rb:
require 'test_helper'

class PostCommentToComicTest < ActionDispatch::IntegrationTest
  test 'Post a comment' do
    visit comic_path comics(:comic)
    within('#new_comment') do
      fill_in 'comment[body]', with: 'コメントのテスト'
    end
    click_button '規約に同意してコメントする'
    assert has_selector? '.content-comment__body-text',
      text: 'コメントのテスト'
  end
end

よくあるこういうのを実行したら下記のエラーが。

% ruby -Itest test/integration/post_comment_to_comic_test.rb -n PostCommentToComicTest#test_Post_a_comment
Run options: -n PostCommentToComicTest#test_Post_a_comment --seed 29141

# Running:

E

Finished in 5.497046s, 0.1819 runs/s, 0.0000 assertions/s.

  1) Error:
PostCommentToComicTest#test_Post_a_comment:
Capybara::Webkit::ClickFailed: Failed to click element /html/body/div[@id='content']/div[@id='comments']/div/div[1]/form[@id='new_comment']/div[@id='comment_action']/input because of overlapping element /html/body/div[@id='content']/nav/div/ul/li[2]/a/span at position 831, 1025; 
A screenshot of the page at the time of the failure has been written to /var/folders/nb/5jp6psyd7h19my68chb0svvh0000gn/T/click_failed_W60479.png
    test/integration/post_comment_to_comic_test.rb:23:in `block in '

1 runs, 0 assertions, 0 failures, 1 errors, 0 skips

クリックできなかったElementのxpathを表示してくれる。そして何故クリックできなかったのか、overlapしているElementのpositionとxpathも表示してくれる。更にscreenshotここに撮っておいたからってどこまで至れり尽くせりだよ。

この場合、要はこんな感じになっててコメントボタンがクリックできない。

こんな方法でいいのかどうかわかりませんが、テスト前にexecute_scriptでoverlapしてるメニューをhideして対処。

# test/integration/post_comment_to_comic_test.rb:
require 'test_helper'

class PostCommentToComicTest < ActionDispatch::IntegrationTest
  test 'Post a comment' do
    visit comic_path comics(:comic)
    execute_script("$('nav.other-pages-nav').hide()") # It's workaround.
    within('#new_comment') do
      fill_in 'comment[body]', with: 'コメントのテスト'
    end
    click_button '規約に同意してコメントする'
    assert has_selector? '.content-comment__body-text',
      text: 'コメントのテスト'
  end
end

更に背景と同色の文字問題とかもサポートしてくれたら手動QA不要とはいわないけどもっと自動テスト化できますね。

sass-railsの5.0から、foo.css.sassfoo.sassに変えよというWARNINGが出るようになってました。(エラーが出たらとりあえずGoogle検索欄に放り込む人=俺用のエントリータイトル)
DEPRECATION WARNING: Extra .css in SASS file is unnecessary. Rename /Users/komagata/code/kowabana/app/assets/stylesheets/blocks/footer/_footer-pages-nav.css.sass to /Users/komagata/code/kowabana/app/assets/stylesheets/blocks/footer/_footer-pages-nav.sass
...
...
...

デザイナーの作業とコンフリクトする予感!

% ls app/assets/stylesheets/**/*.css.sass | wc -l
     107

すげーたくさんあってやんなりますよぉ~

% zmv app/assets/stylesheets/**/*.css.sass app/assets/stylesheets/**/*.sass
% zmv app/assets/stylesheets/*.css.sass app/assets/stylesheets/*.sass
% git add 'app/assets/stylesheets/**/*.sass'
% git add 'app/assets/stylesheets/*.sass'
% git rm 'app/assets/stylesheets/**/*.css.sass'
% git rm 'app/assets/stylesheets/*.css.sass'

zmv使うとちょっと楽。

参照:zsh の zmv を使って簡単に複数ファイルを一括リネームする - mollifier delta blog

こちらの記事の解決編です。

ActiveRecord::RecordNotFoundのエラーは放置すべきか? - komagata

@hiroshi3110さんからズバリな答えをいただきました。

怖話で実装

# lib/record_not_found_by_trustless_param.rb:
class RecordNotFoundByTrustlessParam < StandardError; end
# app/controllers/comics_controller.rb:
class ComicsController < ApplicationController
  before_action :set_comic, only: :show
  
  def show
  end

  private
  def set_comic
    unless @comic = Comic.find_by(id: params[:id])
      raise RecordNotFoundByTrustlessParam                                                                                                                                                                  
    end
  end
end
# app/controllers/application_controller.rb:
class ApplicationController < ActionController::Base
  rescue_from RecordNotFoundByTrustlessParam, with: :not_found
  
  private
  def not_found
    render file: "#{Rails.root}/public/404.html", layout: false, status: 404
  end
end

@hiroshi3110さんの言うとおり、信頼出来ない外部からのidを元にしたfindで見つからない場合はRecordNotFoundByTrustlessParamにし、rescue_fromで拾って404を出すようにしました。(404を動的テンプレートで出すのは良くないと思うのでpublic以下の静的ファイルを読む)

自分の中のベストプラクティスにしようと思いました。

Rollbarとかのエラー管理サービス使ってるとActiveRecord::RecordNotFound がWARNINGレベルのエラーでたくさん残るので気になる。

idに適当な数字入れれば必ず出るんだから。rollbarの無料プランは月のエラー数に上限があるのでもったいない。

しかし、特定の存在しないidが何度もアクセスされるのは問題の兆候だったりするから完全にmuteするのも気が引ける。皆さんどうしてるんでしょう?

追記:

ActiveRecord::RecordNotFoundエラーを防ぐ - komagata