Rails あるある

31,143 views

Published on

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

Published in: Technology
0 Comments
111 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
31,143
On SlideShare
0
From Embeds
0
Number of Embeds
454
Actions
Shares
0
Downloads
73
Comments
0
Likes
111
Embeds 0
No embeds

No notes for slide

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/ 一歩、一歩

×