Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Rails あるある

32,769 views

Published on

札幌市中央区Ruby会議01 での発表資料です。

Published in: Technology
  • Be the first to comment

Rails あるある

  1. 1. Rails あるある 現場での悩みとアンチパターン http://www.nce.co.uk/features/transport/network-rail-changing-track/8622890.article 札幌市中央区Ruby会議01 2014.02.08 Ruby札幌 佐藤 竜之介(Ryunosuke SATO)
  2. 2. 提供 From Sapporo, with Love for Ruby. Ruby札幌
  3. 3. 自己紹介
  4. 4. @tricknotes I am a software developer who love JavaScript and Ruby. http://tricknotes.hateblo.jp/
  5. 5. I love OSS
  6. 6. 札幌市中央区Ruby会議01
  7. 7. Sapporo.js http://sapporojs.org/
  8. 8. よろしく お願いします
  9. 9. Rails あるある 現場での悩みとアンチパターン http://www.nce.co.uk/features/transport/network-rail-changing-track/8622890.article 札幌市中央区Ruby会議01 2014.02.08 Ruby札幌 佐藤 竜之介(Ryunosuke SATO)
  10. 10. 今日の話 Rails には便利で魅力的な機能がたくさんあります それらを使えば、簡単にアプリケーションを作ること ができます しかし、使いどころを間違えると、あとで変更に弱く なってしまい開発が苦しくなることがあります。 自分が体験した あるある ネタを紹介しつつ、 メンテナンスしやすいアプリケーションについて 考えてみます
  11. 11. すでに Rails をやっているひと、これからやろう としているひとにとって、快適に開発をするため のヒントになれば嬉しい * 状況設定は架空のものです *
  12. 12. 対象バージョン * Ruby 2.0, 2.1 * Rails 3.2, 4.0
  13. 13. あるある集
  14. 14. あるある① ‘ 社員レコードは論理削除で... default_scope
  15. 15. 状況 * 社員の勤怠システムを考える * 社員は退職することができる * 社員が退職した場合、社員は社員一覧に表示されない * ただ、社員の勤怠履歴を参照することはできる必要がある * そのため、社員レコードに対しては論理削除を適用する * `default_scope` !!
  16. 16. default_scope とは “デフォルト” の検索条件を指定できる機能 User.where(deleted_at: nil) class User < ActiveRecord::Base default_scope lambda { where(deleted_at: nil) } end User.all
  17. 17. default_scope とは “デフォルト” の検索条件を指定できる機能 @user.destroy @user.update_attribute :deleted_at, DateTime.now http://api.rubyonrails.org/classes/ActiveRecord/Scoping/Default/ClassMethods.html#method-i-default_scope
  18. 18. Staff 1 n Attendance
  19. 19. 問題 @attendance.staff #=> nil Staff.unscoped { @attendance.staff #=> <#Staff> } そもそも、”削除” ではないのでは...??
  20. 20. 解決1 退会/移動などを state で持っておいて、必要に 応じて scope をかける class Staff < ActiveRecord::Base scope :only_tenured, lambda { where(state: :tenured) } end Staff.all Staff.only_tenured
  21. 21. 解決2 まったく参照しない = 不要なデータ 不要なデータは実際に消してしまう Staff.destroy
  22. 22. ポイント * “default” は “default” * ある条件のときに解除したくなるものは “default” ではない * まったく参照しないなら DB に残っている 必要はない
  23. 23. あるある② ‘ ブログ記事にタグをつけたい serialize
  24. 24. 状況 * ブログシステムを考える * 記事にはタグを登録することができる * タグは自由入力のテキストで、 ひとつの記事に複数のタグを付けることができる * すでに多くのテーブルが存在していて、 極力テーブルを増やしたくない * `serialize` !!
  25. 25. serialize Ruby のオブジェクトを YAML にシリアライズし て、データベースのカラムに保存する class Post < ActiveRecord::Base serialize :tags end @post = Post.new @post.tags = ['Ruby', 'chuork01'] @post.save Post.last.tags #=> ['Ruby', 'chuork01'] http://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html#method-i-serialize
  26. 26. 問題 特定のタグをもっている記事だけを取得したい タグ毎に記事を一覧したいときに不便 YAML なので、 SQL で検索できない Post.where(tags: 'LIKE %Ruby%') Text 型なので Like 検索はできるけど...
  27. 27. 解決1 タグを別のテーブルに分ける Post 1 n Tagging n 1 Tag
  28. 28. 解決1 タグを別のテーブルに分ける class Post < ActiveRecord::Base has_many :taggings has_many :tags, through: :taggings end class Tagging < ActiveRecord::Base belongs_to :post belongs_to :tag end class Tag < ActiveRecord::Base has_many :taggings has_many :posts, through: :taggings end @tag.posts
  29. 29. 解決2 配列型を利用する (データベースがサポートしていれば) PostgreSQL の例 class AddTagsToPosts < ActiveRecord::Migration def change add_column :posts, :tags, :array end end Post.where("'Ruby' = ANY (tags)")
  30. 30. ポイント * Ruby の世界でしか扱えないデータは扱いづらい * 適切なデータモデルを選択しましょう
  31. 31. あるある③ ‘ 一時保存のときは入力チェック をしたくない save(validate: false)
  32. 32. 状況 * 会員登録できるサービスを考える * Email だけあれば仮登録できるが、 本登録では名前などその他の情報が必要 * ひとまずレコードだけ作りたい * `save(validate: false)`
  33. 33. save(validate: false) validation をスキップして保存することができる http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-save
  34. 34. 問題 * 不完全な状態のデータが保存される * 一部の validation だけ実行するのが困難 * データベースに制約をかけられなくなる
  35. 35. 解決1 * 状況に応じた validation を行なう class Staff < ActiveRecord::Base validates :name, presence: { on: :registration } end @staff.save(context: :registration)
  36. 36. 解決2 * “仮登録”/”本登録” 状態を持たせて validation class Staff < ActiveRecord::Base validates :name, presence: { if: :registration? } def registration? state == 'registration' end end @staff.state = 'registration' @staff.save
  37. 37. ポイント * データを保存するために、 チェックが必須な項目をスキップしてしまう * 不完全なデータが登録されてしまう
  38. 38. あるある④ ‘ 他のシステムと連携するための JSON の API を提供したい as_json
  39. 39. 状況 * 人事評価システムを考える * 社員の評価を他システムに対しても提供する * 連携のためのデータを JSON で出力する * `as_json`
  40. 40. as_json モデルを JSON へに変換した場合のデータフォ ーマットを定義する Rails が as_json を呼び出して JSON に出力して くれる http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html#method-i-as_json
  41. 41. as_json class User < AvtiveRecord::Base def as_json { id: id, name: name, evaluations: evaluations.as_json } end end class UsersController < ApplicationController def index @users = User.all end end
  42. 42. as_json GET /users.json [{ }] “id”: 1, “name”: “tricknotes”, “evaluations”: [{ ... }, { ... }] http://api.rubyonrails.org/classes/ActiveModel/Serializers/JSON.html#method-i-as_json
  43. 43. 問題 データの表示/非表示の扱いが難しい * ログインしていると見える情報 * 本人だけ見える情報 * 権限によって見える情報 * 一覧だと不要で、詳細画面だと必要な情報 例: 部長は部下の評価を閲覧できるが、部員は 本人の評価のみを閲覧できる
  44. 44. 問題 current_user を参照したくなるので、 Model#as_json だと扱いが難しい context に依存する変換はモデルの仕事ではな い JSON とはデータの表現形式 -> View 層の仕事
  45. 45. 問題 その他のデータ形式をサポートしたくなった場合、 似たようなメソッドが並ぶ as_csv * as_json ... *
  46. 46. 解決 View として JSON を出力する jbuilder * app/views/users/index.json.jbuilder json.array!(@users) do |user| json.extract! user, :id, :name, :evaluations end
  47. 47. ポイント * Context によって変化するロジックを モデルに持たせない * データの表示形式はモデルに含めない
  48. 48. 他にもまだまだ...
  49. 49. * * * * * * STI session にオブジェクトを保存 最終更新日 = updated_at monkey patch before/after callback gem の version 固定 ...
  50. 50. for more information...
  51. 51. もっと複雑な現実問題に対応するためのヒント
  52. 52. Rails AntiPatterns http://www.amazon.co.jp/dp/0321604814
  53. 53. Ruby on Rails: The Bad Parts http://magazine.rubyist.net/?0041-RailsTheBadParts
  54. 54. まとめ
  55. 55. 最初から完璧な設計をするのは困難 アカンと思ったら引き返す/直す勇気を! 機能自体が悪かというとそうでもなくて、 使いドコロを間違うと辛い、という話 Rails の機能自体を理解すること、 作るものを理解することが大事!!
  56. 56. パッと見て便利そうな機能でも、 その機能の意味と、 ドメインを考えて組み立てるの大事!! 状況によって適切な選択かどうかは変わってくる
  57. 57. Rails の機能自体への理解 対象領域への理解 作ってわかることもある 変更する勇気
  58. 58. http://www.flickr.com/photos/sakura-kame/479871795/ 一歩、一歩

×