怖話でネストしたリソースのクラス名がぶつかってしまうので、どうつけるかで悩んでいます。

ぶつかるページ

  1. story 1個づつに紐づくcomments(怖い話1のコメント一覧)
  2. なんらかのstoryに紐づくcomments(怖い話のコメント一覧)

story(怖い話)以外にもcomic(ホラー漫画)とかurban_legend(都市伝説)とかあり、それぞれにもcommentがつく。

# config/routes.rb
Rails.application.routes.draw do
  namespace :stories do
    resources :comments, only: :index
  end

  resources :stories, only: :index do
    resources :comments, only: :index, controller: 'stories/comments'
  end
end
% rake routes        
        Prefix Verb URI Pattern                        Controller#Action
stories_comments GET  /stories/comments          stories/comments#index
 story_comments GET  /stories/:post_id/comments stories/comments#index
         posts GET  /stories                   stories#index

URLの意味的にどちらも上記のようにしたいが、commentsコントローラーが被ってしまう。

これを避けてnamespace無しのcommentsコントローラーを使うと今度はcomicのcommentなどとかぶってしまう。

それを避けるにはAllCommentsとかCommentsByStoryなどといったダサい名前しか思いつかない。まいったなあ。

RailsGuidesにちゃんと書いてありますが、ちょっとハマりました。

こういうのはダメ。

Rails.application.routes.draw do
  constraints subdomain: :api do
    scope module: :api do
      namespace :v1, format: :json do
        resources :posts
      end
    end
  end
end

ちゃんと文字列にする。

Rails.application.routes.draw do
  constraints subdomain: 'api' do
    scope module: 'api' do
      namespace 'v1', format: 'json' do
        resources :posts
      end
    end
  end
end

3.9 リクエスト内容に応じて制限を加える

リクエストベースの制限は、Requestオブジェクトに対してあるメソッドを呼び出すことで実行されます。メソッド呼び出し時にハッシュキーと同じ名前をメソッドに渡し、返された値をハッシュ値と比較します。従って、制限された値は、対応するRequestオブジェクトメソッドが返す型と一致する必要があります。たとえば、constraints: { subdomain: 'api' }という制限はapiサブドメインに期待どおりマッチしますが、constraints: { subdomain: :api }のようにシンボルを使用した場合はapiサブドメインに一致しません。request.subdomainが返す'api'は文字列型であるためです。

ドメインやPATHなど、文字列っぽいものは文字列で指定すべきと考えておけば良さそうです。

Review.appでの無限ステージング環境をクッソwww快適に使わせて頂いてるんですが、メール内のhost名設定で困ってたら @t-kawa さん、@chiastolite さんが教えてくれました。

あざーす!

% heroku config:get HEROKU_APP_NAME -a kulku-pr-173
kulku-pr-173

取れるじゃな〜い。

staging環境用にconfig/environments/staging.rbをこんな感じにしてみました。

require File.join(__dir__, 'production')

Rails.application.configure do
  config.action_mailer.default_url_options = { host: "#{ENV['HEROKU_APP_NAME']}.herokuapp.com" }
end

これで無限ステージング環境でメールもばっちり。

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不要とはいわないけどもっと自動テスト化できますね。