Effective ActiveRecord
-
Upload
smartlogic -
Category
Technology
-
view
654 -
download
1
description
Transcript of Effective ActiveRecord
![Page 1: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/1.jpg)
Effective ActiveRecordSam Goldman
@nontrivialzeroshttp://github.com/samwgoldman
Wednesday, December 18, 13
![Page 2: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/2.jpg)
Review: Models
id email name1 [email protected] Foo2 [email protected] Bar
usersclass User < ActiveRecord::Baseend
foo = User.find(1)foo.name # "Foo"foo.email # "[email protected]"
bar = User.find(2)bar.name # "Barbar.email # "[email protected]"
Wednesday, December 18, 13
![Page 3: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/3.jpg)
Review: Has Many
id name1 Foo project2 Bar project
projectsclass Project < AR::Base has_many :membersend
class Member < AR::Base belongs_to :projectend
foo_project = Project.find(1)
foo_project.name# "Foo project"
foo_project.members. map(&:email)# ["[email protected]",# "[email protected]"]
id project_id email1 1 [email protected] 1 [email protected] 2 [email protected] 2 [email protected]
members
Wednesday, December 18, 13
![Page 4: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/4.jpg)
Review: Belongs To
id name1 Foo project2 Bar project
projectsclass Project < AR::Base has_many :membersend
class Member < AR::Base belongs_to :projectend
foo = Member.find(1)
foo.email# "[email protected]"
foo.project.name# "Foo project"
id project_id email1 1 [email protected] 1 [email protected] 2 [email protected] 2 [email protected]
members
Wednesday, December 18, 13
![Page 5: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/5.jpg)
Review: Has Many Through
id name1 Foo project2 Bar project
projects
class User < AR::Base has_many :members has_many :projects, through: :membersend
class Project < AR::Base has_many :membersend
class Member < AR::Base belongs_to :user belongs_to :projectend
foo = User.find(1)foo.projects.map(&:name)# ["Foo project",# "Bar project"]
id project_id user_id1 1 13 2 1
members
id email name1 [email protected] Foo
users
Wednesday, December 18, 13
![Page 6: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/6.jpg)
Creating Recordsproject = Project.create(name: "Project") (0.3ms) BEGINSQL (1.5ms) INSERT INTO "projects" ("name") VALUES ($1) RETURNING "id" [["name", "Project"]] (0.4ms) COMMIT
user = User.create(name: "User", email: "[email protected]") (0.3ms) BEGINSQL (1.3ms) INSERT INTO "users" ("email", "name") VALUES ($1, $2) RETURNING "id" [["email", "[email protected]"], ["name", "User"]] (0.4ms) COMMIT
member = Member.create(user: user, project: project) (0.5ms) BEGINSQL (3.7ms) INSERT INTO "members" ("project_id", "user_id") VALUES ($1, $2) RETURNING "id" [["project_id", 1], ["user_id", 1]] (0.3ms) COMMIT
Wednesday, December 18, 13
![Page 7: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/7.jpg)
Updating Recordsproject.update_attributes(name: "Updated Project") (0.2ms) BEGINSQL (0.9ms) UPDATE "projects" SET "name" = $1 WHERE "projects"."id" = 1 [["name", "Updated Project"]] (0.4ms) COMMIT
user.update_attributes(name: "Updated User") (0.1ms) BEGINSQL (0.9ms) UPDATE "users" SET "name" = $1 WHERE "users"."id" = 1 [["name", "Updated User"]] (0.4ms) COMMIT
Wednesday, December 18, 13
![Page 8: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/8.jpg)
Autosaveclass Member < ActiveRecord::Base belongs_to :user belongs_to :projectend
project = Project.new(name: "Project")user = User.new(name: "User", email: "[email protected]")member = Member.create(user: user, project: project)Guess the result.
Wednesday, December 18, 13
![Page 9: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/9.jpg)
Autosaveclass Member < ActiveRecord::Base belongs_to :user belongs_to :projectend
project = Project.new(name: "Project")user = User.new(name: "User", email: "[email protected]")member = Member.create(user: user, project: project) (0.4ms) BEGINSQL (2.7ms) INSERT INTO "users" ("email", "name") VALUES ($1, $2) RETURNING "id" [["email", "[email protected]"], ["name", "User"]]SQL (1.2ms) INSERT INTO "projects" ("name") VALUES ($1) RETURNING "id" [["name", "Project"]]SQL (3.5ms) INSERT INTO "members" ("project_id", "user_id") VALUES ($1, $2) RETURNING "id" [["project_id", 1], ["user_id", 1]] (0.5ms) COMMIT
Wednesday, December 18, 13
![Page 10: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/10.jpg)
class Member < ActiveRecord::Base belongs_to :user belongs_to :projectend
member = Member.newmember.build_user(name: "User", email: "[email protected]")member.build_project(name: "Project")member.save (0.4ms) BEGINSQL (2.7ms) INSERT INTO "users" ("email", "name") VALUES ($1, $2) RETURNING "id" [["email", "[email protected]"], ["name", "User"]]SQL (1.2ms) INSERT INTO "projects" ("name") VALUES ($1) RETURNING "id" [["name", "Project"]]SQL (3.5ms) INSERT INTO "members" ("project_id", "user_id") VALUES ($1, $2) RETURNING "id" [["project_id", 1], ["user_id", 1]] (0.5ms) COMMIT
Autosave
Wednesday, December 18, 13
![Page 11: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/11.jpg)
class Member < ActiveRecord::Base belongs_to :user, autosave: false belongs_to :project, autosave: falseend
member = Member.newmember.build_user(name: "User", email: "[email protected]")member.build_project(name: "Project")member.saveGuess the result.
Autosave
Wednesday, December 18, 13
![Page 12: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/12.jpg)
Autosaveclass Member < ActiveRecord::Base belongs_to :user, autosave: false belongs_to :project, autosave: falseend
member = Member.newmember.build_user(name: "User", email: "[email protected]")member.build_project(name: "Project")member.savePG::NotNullViolation:ERROR: null value in column "user_id" violates not-null constraint (ActiveRecord::StatementInvalid)DETAIL: Failing row contains (1, null, null).: INSERT INTO "members" DEFAULT VALUES RETURNING "id"
Wednesday, December 18, 13
![Page 13: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/13.jpg)
Autosaveclass Member < ActiveRecord::Base belongs_to :user belongs_to :projectend
member.user.name = "Updated User"member.project.name = "Updated Project"member.saveGuess the result.
Wednesday, December 18, 13
![Page 14: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/14.jpg)
Autosaveclass Member < ActiveRecord::Base belongs_to :user belongs_to :projectend
member.user.name = "Updated User"member.project.name = "Updated Project"member.save(0.2ms) BEGIN(0.2ms) COMMIT
Wednesday, December 18, 13
![Page 15: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/15.jpg)
class Member < ActiveRecord::Base belongs_to :user, autosave: true belongs_to :project, autosave: trueend
member.user.name = "Updated User"member.project.name = "Updated Project"member.saveGuess the result.
Autosave
Wednesday, December 18, 13
![Page 16: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/16.jpg)
Autosaveclass Member < ActiveRecord::Base belongs_to :user, autosave: true belongs_to :project, autosave: trueend
member.user.name = "Updated User"member.project.name = "Updated Project"member.save (0.2ms) BEGINSQL (1.1ms) UPDATE "users" SET "name" = $1 WHERE "users"."id" = 1 [["name", "Updated User"]]SQL (1.2ms) UPDATE "projects" SET "name" = $1 WHERE "projects"."id" = 1 [["name", "Updated Project"]] (0.4ms) COMMIT
Wednesday, December 18, 13
![Page 17: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/17.jpg)
Autosaveclass Project < ActiveRecord::Base has_many :membersend
user = User.new(name: "User", email: "[email protected]")project = Project.new(name: "Project")project.members << Member.new(user: user)project.saveGuess the result.
Wednesday, December 18, 13
![Page 18: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/18.jpg)
Autosaveclass Project < ActiveRecord::Base has_many :membersend
user = User.new(name: "User", email: "[email protected]")project = Project.new(name: "Project")project.members << Member.new(user: user)project.save (0.7ms) BEGINSQL (1.6ms) INSERT INTO "projects" ("name") VALUES ($1) RETURNING "id" [["name", "Project"]]SQL (1.2ms) INSERT INTO "users" ("email", "name") VALUES ($1, $2) RETURNING "id" [["email", "[email protected]"], ["name", "User"]]SQL (3.4ms) INSERT INTO "members" ("project_id", "user_id") VALUES ($1, $2) RETURNING "id" [["project_id", 1], ["user_id", 1]] (0.5ms) COMMIT
Wednesday, December 18, 13
![Page 19: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/19.jpg)
class Project < ActiveRecord::Base has_many :membersend
user = User.new(name: "User", email: "[email protected]")project = Project.new(name: "Project")project.members.build(user: user)project.save (0.7ms) BEGINSQL (1.6ms) INSERT INTO "projects" ("name") VALUES ($1) RETURNING "id" [["name", "Project"]]SQL (1.2ms) INSERT INTO "users" ("email", "name") VALUES ($1, $2) RETURNING "id" [["email", "[email protected]"], ["name", "User"]]SQL (3.4ms) INSERT INTO "members" ("project_id", "user_id") VALUES ($1, $2) RETURNING "id" [["project_id", 1], ["user_id", 1]] (0.5ms) COMMIT
Autosave
Wednesday, December 18, 13
![Page 20: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/20.jpg)
class Project < ActiveRecord::Base has_many :members, autosave: falseend
user = User.new(name: "User", email: "[email protected]")project = Project.new(name: "Project")project.members.build(user: user)project.saveGuess the result.
Autosave
Wednesday, December 18, 13
![Page 21: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/21.jpg)
Autosaveclass Project < ActiveRecord::Base has_many :members, autosave: falseend
user = User.new(name: "User", email: "[email protected]")project = Project.new(name: "Project")project.members.build(user: user)project.save (0.4ms) BEGINSQL (1.6ms) INSERT INTO "projects" ("name") VALUES ($1) RETURNING "id" [["name", "Project"]] (0.4ms) COMMIT
Wednesday, December 18, 13
![Page 22: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/22.jpg)
Inversesclass Project < ActiveRecord::Base has_many :tasksend
class Task < ActiveRecord::Base belongs_to :projectend
project = Project.new(name: "Project")task = project.tasks.buildproject.save
p project.object_idp task.project.object_idGuess the result.
Wednesday, December 18, 13
![Page 23: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/23.jpg)
Inversesclass Project < ActiveRecord::Base has_many :tasksend
class Task < ActiveRecord::Base belongs_to :projectend
project = Project.new(name: "Project")task = project.tasks.buildproject.save
p project.object_idp task.project.object_id70236648295560Project Load (1.4ms) SELECT "projects".* FROM "projects" WHERE "projects"."id" = $1 ORDER BY "projects"."id" ASC LIMIT 1 [["id", 1]]70236645304160
Not just an extra query. Split
brain!
Wednesday, December 18, 13
![Page 24: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/24.jpg)
class Project < ActiveRecord::Base has_many :tasks, inverse_of: :projectend
class Task < ActiveRecord::Base belongs_to :projectend
project = Project.new(name: "Project")task = project.tasks.buildproject.save
p project.object_idp task.project.object_idGuess the result.
Inverses
Wednesday, December 18, 13
![Page 25: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/25.jpg)
Inversesclass Project < ActiveRecord::Base has_many :tasks, inverse_of: :projectend
class Task < ActiveRecord::Base belongs_to :projectend
project = Project.new(name: "Project")task = project.tasks.buildproject.save
p project.object_idp task.project.object_id7025951560814070259515608140
Wednesday, December 18, 13
![Page 26: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/26.jpg)
Summary
• Use autosave and inverse associations
• Inspect the generated SQL for sanity
• Avoid explicit transactions
Wednesday, December 18, 13
![Page 27: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/27.jpg)
Authorizationclass ProjectMembersController < ApplicationController # POST /project/:project_id/members def create member = Member.create(member_params.merge(project_id: project_id)) respond_with member end
private
def project_id params.require(:project_id) end
def member_params params.require(:member).permit(:user_id) endend
Wednesday, December 18, 13
![Page 28: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/28.jpg)
Authorizationclass ProjectMembersController < ApplicationController # POST /project/:project_id/members def create member = Member.create(member_params.merge(project_id: project_id)) respond_with member end
private
def project_id params.require(:project_id) end
def member_params params.require(:member).permit(:user_id) endend
Anyone can add any user to any
project!
Wednesday, December 18, 13
![Page 29: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/29.jpg)
class ProjectMembersController < ApplicationController # POST /project/:project_id/members def create member = current_user.create_project_member(project_id, member_params) respond_with member end
private
def project_id params.require(:project_id) end
def member_params params.require(:member).permit(:user_id) endend
class User < ActiveRecord::Base has_many :members has_many :projects, through: :members
def create_project_member(project_id, member_params) project = projects.find(project_id) project.members.create(member_params) endend
Authorization
May only add members to my own
projects.
Wednesday, December 18, 13
![Page 30: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/30.jpg)
class ProjectMembersController < ApplicationController # POST /project/:project_id/members def create member = current_user.build_project_member(project_id, member_params) member.save respond_with member end
private
def project_id params.require(:project_id) end
def member_params params.require(:member).permit(:user_id) endend
class User < ActiveRecord::Base has_many :members has_many :projects, through: :members
def build_project_member(project_id, member_params) project = projects.find(project_id) project.members.build(member_params) endend
Authorization
Separate build vs. create
Wednesday, December 18, 13
![Page 31: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/31.jpg)
class ProjectMembersController < ApplicationController # POST /project/:project_id/members def create member = current_user.build_project_member(project_id, member_params) member.save respond_with member end
private
def project_id params.require(:project_id) end
def member_params params.require(:member).permit(:user_id) endend
class User < ActiveRecord::Base has_many :members has_many :projects, through: :members
def build_project_member(project_id, member_params) project = projects.find(project_id) project.members.build(member_params) endend
Authorization
What if I am not a member of this
project?
Wednesday, December 18, 13
![Page 32: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/32.jpg)
Authorization
Couldn't find Project with id=1 (ActiveRecord::RecordNotFound)
Wednesday, December 18, 13
![Page 33: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/33.jpg)
Authorizationclass User < ActiveRecord::Base has_many :members, inverse_of: :user has_many :projects, through: :members
def build_project_member(project_id, member_params) project = projects.find_one(project_id) if project project.members.build(member_params) end endend
Wednesday, December 18, 13
![Page 34: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/34.jpg)
Authorizationclass User < ActiveRecord::Base has_many :members, inverse_of: :user has_many :projects, through: :members
def member(project_id) members.find_by(project_id: project_id) end
def build_project_member(project_id, member_params) member = member(project_id) if member member.build_project_member(member_params) end endend
class Member < ActiveRecord::Base belongs_to :user, inverse_of: :members belongs_to :project, inverse_of: :members
def build_project_member(member_params) project.members.build(member_params) endend
Wednesday, December 18, 13
![Page 35: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/35.jpg)
class User < ActiveRecord::Base has_many :members, inverse_of: :user has_many :projects, through: :members
def member(project_id) members.find_by(project_id: project_id) end
def build_project_member(project_id, member_params) member = member(project_id) if member member.build_project_member(member_params) end endend
class Member < ActiveRecord::Base belongs_to :user, inverse_of: :members belongs_to :project, inverse_of: :members
def build_project_member(member_params) if role == "admin" project.members.build(member_params) end endend
Authorization
Wednesday, December 18, 13
![Page 36: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/36.jpg)
Authorizationclass ProjectMembersController < ApplicationController # POST /project/:project_id/members def create member = current_user.build_project_member(project_id, member_params) if member.nil? # handle error else member.save respond_with member end end
private
def project_id params.require(:project_id) end
def member_params params.require(:member).permit(:user_id) endend
Wednesday, December 18, 13
![Page 37: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/37.jpg)
Authorizationclass ProjectMembersController < ApplicationController # POST /project/:project_id/members def create member = current_user.build_project_member(project_id, member_params) if member.nil? # handle error else member.save respond_with member end end
private
def project_id params.require(:project_id) end
def member_params params.require(:member).permit(:user_id) endend
Which error happened?
Wednesday, December 18, 13
![Page 38: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/38.jpg)
AuthorizationFailure = Struct.new(:error) do def success? false endend
Success = Struct.new(:value) do def success? true endend
class Member < ActiveRecord::Base belongs_to :user, inverse_of: :members belongs_to :project, inverse_of: :members
def build_project_member(member_params) if role == "admin" Success.new(project.members.build(member_params)) else Failure.new(:not_authorized) end endend
Wednesday, December 18, 13
![Page 39: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/39.jpg)
Authorizationclass User < ActiveRecord::Base has_many :members, inverse_of: :user has_many :projects, through: :members
def member(project_id) member = members.find_by(project_id: project_id) if member Success.new(member) else Failure.new(:member_not_found) end end
def build_project_member(project_id, member_params) result = member(project_id) if result.success? result.value.build_project_member(member_params) else result end endend
Wednesday, December 18, 13
![Page 40: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/40.jpg)
Authorizationclass ProjectMembersController < ApplicationController # POST /project/:project_id/members def create result = current_user.build_project_member(project_id, member_params) if result.success? member = result.value member.save respond_with member else result.error # handle error end end
private
def project_id params.require(:project_id) end
def member_params params.require(:member).permit(:user_id) endend
Wednesday, December 18, 13
![Page 41: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/41.jpg)
Authorizationproject = Project.create(name: "Project")alice = User.create(name: "Alice", email: "[email protected]")bob = User.create(name: "Bob", email: "[email protected]")
p alice.build_project_member(project.id, { user_id: bob.id, role: "member"})#<struct Failure error=:member_not_found>
Wednesday, December 18, 13
![Page 42: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/42.jpg)
project = Project.create(name: "Project")alice = User.create(name: "Alice", email: "[email protected]")bob = User.create(name: "Bob", email: "[email protected]")
alice.members.create(project: project, role: "member")
p alice.build_project_member(project.id, { user_id: bob.id, role: "member"})#<struct Failure error=:not_authorized>
Authorization
Wednesday, December 18, 13
![Page 43: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/43.jpg)
project = Project.create(name: "Project")alice = User.create(name: "Alice", email: "[email protected]")bob = User.create(name: "Bob", email: "[email protected]")
alice.members.create(project: project, role: "admin")
p alice.build_project_member(project.id, { user_id: bob.id, role: "member"})#<struct Success value=#<Member user_id: 2, project_id: 1, role: "member">>
Authorization
Wednesday, December 18, 13
![Page 44: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/44.jpg)
Summary
• Use the relations
• Move beyond ActiveRecord’s API
• Use result objects to represent possible failures
• Separate building vs. creating APIs
Wednesday, December 18, 13
![Page 45: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/45.jpg)
Refactoringclass User < ActiveRecord::Base has_many :members has_many :projects, through: :members
def member(project_id) member = members.find_by(project_id: project_id) if member Success.new(member) else Failure.new(:member_not_found) end end
def build_project_member(project_id, member_params) result = member(project_id) if result.success? result.value.build_project_member(member_params) else result end endend
Wednesday, December 18, 13
![Page 46: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/46.jpg)
Refactoringclass User < ActiveRecord::Base has_many :members has_many :projects, through: :members
def member(project_id) member = members.find_by(project_id: project_id) if member Success.new(member) else Failure.new(:member_not_found) end end
def build_project_member(project_id, member_params) result = member(project_id) if result.success? result.value.build_project_member(member_params) else result end endend
We need a way to combine results.
Wednesday, December 18, 13
![Page 47: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/47.jpg)
RefactoringFailure = Struct.new(:error) do def success? false end
def map self end
def bind self endend
Success = Struct.new(:value) do def success? true end
def map Success.new(yield value) end
def bind yield value endend
Wednesday, December 18, 13
![Page 48: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/48.jpg)
Refactoringclass User < ActiveRecord::Base has_many :members has_many :projects, through: :members
def member(project_id) member = members.find_by(project_id: project_id) if member Success.new(member) else Failure.new(:member_not_found) end end
def build_project_member(project_id, member_params) member(project_id).bind do |member| member.build_project_member(member_params) end endend
Build compound results.
Wednesday, December 18, 13
![Page 49: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/49.jpg)
Serializeclass Member < ActiveRecord::Base belongs_to :user, inverse_of: :members belongs_to :project, inverse_of: :members
def build_project_member(member_params) if role == "admin" Success.new(project.members.build(member_params)) else Failure.new(:not_authorized) end endend
Wednesday, December 18, 13
![Page 50: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/50.jpg)
Serializeclass Member < ActiveRecord::Base belongs_to :user, inverse_of: :members belongs_to :project, inverse_of: :members
serialize :role, Role
def build_project_member(member_params) role.build_project_member(project, member_params) endend
Wednesday, December 18, 13
![Page 51: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/51.jpg)
Serializeclass Role Unknown = Object.new def Unknown.name nil end
MAP = {} MAP.default = Unknown
def self.load(name) MAP[name] end
def self.dump(role) role.name end
attr_reader :name
def initialize(name, &block) @name = name instance_eval(&block) MAP[name] = self endend
Wednesday, December 18, 13
![Page 52: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/52.jpg)
Serializeclass Role Admin = Role.new("admin") do def build_project_member(project, member_params) Success.new(project.members.build(member_params)) end end
Member = Role.new("member") do def build_project_member(project, member_params) Failure.new(:not_authorized) end end
Null = Role.new(nil) do def build_project_member(project, member_params) Failure.new(:missing_role) end end
def Unknown.build_project_member(project, member_params) Failure.new(:unknown_role) endend
Wednesday, December 18, 13
![Page 53: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/53.jpg)
Authorizationclass ProjectMembersController < ApplicationController # POST /project/:project_id/members def create result = current_user.build_project_member(project_id, member_params) if result.success? member = result.value member.save respond_with member else result.error # handle error end end
private
def project_id params.require(:project_id) end
def member_params member_params = params.require(:member).permit(:user_id, :role) role = Role.load(member_params[:role].presence) member_params.merge(:role => role) endend
Wednesday, December 18, 13
![Page 54: Effective ActiveRecord](https://reader033.fdocuments.in/reader033/viewer/2022061221/54bd189a4a7959766e8b4597/html5/thumbnails/54.jpg)
Questions?
http://smartlogic.io
http://twitter.com/smartlogic
http://github.com/smartlogic
http://facebook.com/smartlogic
Wednesday, December 18, 13