SlideShare une entreprise Scribd logo
1  sur  54
Télécharger pour lire hors ligne
Effective ActiveRecord
Sam Goldman
@nontrivialzeros
http://github.com/samwgoldman
sam@smartlogic.io

Wednesday, December 18, 13
Review: Models
class User < ActiveRecord::Base
end
foo = User.find(1)
foo.name # "Foo"
foo.email # "foo@example.com"
bar = User.find(2)
bar.name # "Bar
bar.email # "bar@example.com"

Wednesday, December 18, 13

users
id

email

name

1 foo@example.com Foo
2 bar@example.com Bar
Review: Has Many
class Project < AR::Base
has_many :members
end
class Member < AR::Base
belongs_to :project
end
foo_project = Project.find(1)
foo_project.name
# "Foo project"
foo_project.members.
map(&:email)
# ["foo@example.com",
# "bar@example.com"]

Wednesday, December 18, 13

projects
id

name

1 Foo project
2 Bar project

members
id project_id

email

1

1

foo@example.com

2

1

bar@example.com

3

2

baz@example.com

4

2

quux@example.com
Review: Belongs To
class Project < AR::Base
has_many :members
end
class Member < AR::Base
belongs_to :project
end
foo = Member.find(1)
foo.email
# "foo@example.com"
foo.project.name
# "Foo project"

Wednesday, December 18, 13

projects
id

name

1 Foo project
2 Bar project

members
id project_id

email

1

1

foo@example.com

2

1

bar@example.com

3

2

baz@example.com

4

2

quux@example.com
Review: Has Many Through
class User < AR::Base
has_many :members
has_many :projects,
through: :members
end
class Project < AR::Base
has_many :members
end
class Member < AR::Base
belongs_to :user
belongs_to :project
end
foo = User.find(1)
foo.projects.map(&:name)
# ["Foo project",
# "Bar project"]

Wednesday, December 18, 13

users
id

email

name

1 foo@example.com Foo

projects
id

name

1 Foo project
2 Bar project

members
id project_id

user_id

1

1

1

3

2

1
Creating Records
project = Project.create(name: "Project")
(0.3ms) BEGIN
SQL (1.5ms) INSERT INTO "projects" ("name") VALUES ($1)
RETURNING "id"
[["name", "Project"]]
(0.4ms) COMMIT

user = User.create(name: "User", email: "user@example.com")
(0.3ms) BEGIN
SQL (1.3ms) INSERT INTO "users" ("email", "name") VALUES ($1, $2)
RETURNING "id"
[["email", "user@example.com"], ["name", "User"]]
(0.4ms) COMMIT

member = Member.create(user: user, project: project)
(0.5ms) BEGIN
SQL (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
Updating Records
project.update_attributes(name: "Updated Project")
(0.2ms) BEGIN
SQL (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) BEGIN
SQL (0.9ms) UPDATE "users" SET "name" = $1
WHERE "users"."id" = 1
[["name", "Updated User"]]
(0.4ms) COMMIT

Wednesday, December 18, 13
Autosave
class Member < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
project = Project.new(name: "Project")
user = User.new(name: "User", email: "user@example.com")
member = Member.create(user: user, project: project)
Guess the result.

Wednesday, December 18, 13
Autosave
class Member < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
project = Project.new(name: "Project")
user = User.new(name: "User", email: "user@example.com")
member = Member.create(user: user, project: project)
(0.4ms) BEGIN
SQL (2.7ms) INSERT INTO "users" ("email", "name") VALUES ($1, $2)
RETURNING "id"
[["email", "user@example.com"], ["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
Autosave
class Member < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
member = Member.new
member.build_user(name: "User", email: "user@example.com")
member.build_project(name: "Project")
member.save
(0.4ms) BEGIN
SQL (2.7ms) INSERT INTO "users" ("email", "name") VALUES ($1, $2)
RETURNING "id"
[["email", "user@example.com"], ["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
Autosave
class Member < ActiveRecord::Base
belongs_to :user, autosave: false
belongs_to :project, autosave: false
end
member = Member.new
member.build_user(name: "User", email: "user@example.com")
member.build_project(name: "Project")
member.save
Guess the result.

Wednesday, December 18, 13
Autosave
class Member < ActiveRecord::Base
belongs_to :user, autosave: false
belongs_to :project, autosave: false
end
member = Member.new
member.build_user(name: "User", email: "user@example.com")
member.build_project(name: "Project")
member.save
PG::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
Autosave
class Member < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
member.user.name = "Updated User"
member.project.name = "Updated Project"
member.save
Guess the result.

Wednesday, December 18, 13
Autosave
class Member < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
member.user.name = "Updated User"
member.project.name = "Updated Project"
member.save
(0.2ms) BEGIN
(0.2ms) COMMIT

Wednesday, December 18, 13
Autosave
class Member < ActiveRecord::Base
belongs_to :user, autosave: true
belongs_to :project, autosave: true
end
member.user.name = "Updated User"
member.project.name = "Updated Project"
member.save
Guess the result.

Wednesday, December 18, 13
Autosave
class Member < ActiveRecord::Base
belongs_to :user, autosave: true
belongs_to :project, autosave: true
end
member.user.name = "Updated User"
member.project.name = "Updated Project"
member.save
(0.2ms) BEGIN
SQL (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
Autosave
class Project < ActiveRecord::Base
has_many :members
end
user = User.new(name: "User", email: "user@example.com")
project = Project.new(name: "Project")
project.members << Member.new(user: user)
project.save
Guess the result.

Wednesday, December 18, 13
Autosave
class Project < ActiveRecord::Base
has_many :members
end
user = User.new(name: "User", email: "user@example.com")
project = Project.new(name: "Project")
project.members << Member.new(user: user)
project.save
(0.7ms) BEGIN
SQL (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", "user@example.com"], ["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
Autosave
class Project < ActiveRecord::Base
has_many :members
end
user = User.new(name: "User", email: "user@example.com")
project = Project.new(name: "Project")
project.members.build(user: user)
project.save
(0.7ms) BEGIN
SQL (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", "user@example.com"], ["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
Autosave
class Project < ActiveRecord::Base
has_many :members, autosave: false
end
user = User.new(name: "User", email: "user@example.com")
project = Project.new(name: "Project")
project.members.build(user: user)
project.save
Guess the result.

Wednesday, December 18, 13
Autosave
class Project < ActiveRecord::Base
has_many :members, autosave: false
end
user = User.new(name: "User", email: "user@example.com")
project = Project.new(name: "Project")
project.members.build(user: user)
project.save
(0.4ms) BEGIN
SQL (1.6ms) INSERT INTO "projects" ("name") VALUES ($1)
RETURNING "id"
[["name", "Project"]]
(0.4ms) COMMIT

Wednesday, December 18, 13
Inverses
class Project < ActiveRecord::Base
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :project
end
project = Project.new(name: "Project")
task = project.tasks.build
project.save
p project.object_id
p task.project.object_id
Guess the result.

Wednesday, December 18, 13
Inverses
class Project < ActiveRecord::Base
has_many :tasks
end
class Task < ActiveRecord::Base
belongs_to :project
end
project = Project.new(name: "Project")
task = project.tasks.build
project.save
p project.object_id
p task.project.object_id

Not just an
extra query. Split
brain!

70236648295560
Project Load (1.4ms) SELECT "projects".* FROM "projects"
WHERE "projects"."id" = $1
ORDER BY "projects"."id" ASC
LIMIT 1
[["id", 1]]
70236645304160

Wednesday, December 18, 13
Inverses
class Project < ActiveRecord::Base
has_many :tasks, inverse_of: :project
end
class Task < ActiveRecord::Base
belongs_to :project
end
project = Project.new(name: "Project")
task = project.tasks.build
project.save
p project.object_id
p task.project.object_id
Guess the result.

Wednesday, December 18, 13
Inverses
class Project < ActiveRecord::Base
has_many :tasks, inverse_of: :project
end
class Task < ActiveRecord::Base
belongs_to :project
end
project = Project.new(name: "Project")
task = project.tasks.build
project.save
p project.object_id
p task.project.object_id
70259515608140
70259515608140

Wednesday, December 18, 13
Summary
• Use autosave and inverse associations
• Inspect the generated SQL for sanity
• Avoid explicit transactions

Wednesday, December 18, 13
Authorization
class 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)
end
end

Wednesday, December 18, 13
Authorization
class ProjectMembersController < ApplicationController
# POST /project/:project_id/members
def create
member = Member.create(member_params.merge(project_id: project_id))
respond_with member
end

Anyone can
def project_id
add any user to any
params.require(:project_id)
end
project!
private

def member_params
params.require(:member).permit(:user_id)
end
end

Wednesday, December 18, 13
Authorization
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)
end
end
class User < ActiveRecord::Base
has_many :members
has_many :projects, through: :members

May only add
members to my own
projects.

def create_project_member(project_id, member_params)
project = projects.find(project_id)
project.members.create(member_params)
end
end

Wednesday, December 18, 13
Authorization
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

Separate build vs.
create

def member_params
params.require(:member).permit(:user_id)
end
end
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)
end
end
Wednesday, December 18, 13
Authorization
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)
end
end

What if I am
not a member of this
class User < ActiveRecord::Base
has_many :members
has_many :projects, through: :members
project?
def build_project_member(project_id, member_params)
project = projects.find(project_id)
project.members.build(member_params)
end
end
Wednesday, December 18, 13
Authorization
Couldn't find Project with id=1
(ActiveRecord::RecordNotFound)

Wednesday, December 18, 13
Authorization
class 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
end
end

Wednesday, December 18, 13
Authorization
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
end
end
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)
end
end

Wednesday, December 18, 13
Authorization
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
end
end
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
end
end

Wednesday, December 18, 13
Authorization
class 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)
end
end

Wednesday, December 18, 13
Authorization
class 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

Which error
happened?

def project_id
params.require(:project_id)
end
def member_params
params.require(:member).permit(:user_id)
end
end

Wednesday, December 18, 13
Authorization
Failure = Struct.new(:error) do
def success?
false
end
end
Success = Struct.new(:value) do
def success?
true
end
end
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
end
end

Wednesday, December 18, 13
Authorization
class 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
end
end

Wednesday, December 18, 13
Authorization
class 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)
end
end

Wednesday, December 18, 13
Authorization
project = Project.create(name: "Project")
alice = User.create(name: "Alice", email: "alice@example.com")
bob = User.create(name: "Bob", email: "bob@example.com")
p alice.build_project_member(project.id, {
user_id: bob.id,
role: "member"
})
#<struct Failure error=:member_not_found>

Wednesday, December 18, 13
Authorization
project = Project.create(name: "Project")
alice = User.create(name: "Alice", email: "alice@example.com")
bob = User.create(name: "Bob", email: "bob@example.com")
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>

Wednesday, December 18, 13
Authorization
project = Project.create(name: "Project")
alice = User.create(name: "Alice", email: "alice@example.com")
bob = User.create(name: "Bob", email: "bob@example.com")
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">>

Wednesday, December 18, 13
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
Refactoring
class 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
end
end

Wednesday, December 18, 13
Refactoring
class 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
end
end

Wednesday, December 18, 13

We need a way to
combine results.
Refactoring
Failure = Struct.new(:error) do
def success?
false
end
def map
self
end
def bind
self
end
end

Wednesday, December 18, 13

Success = Struct.new(:value) do
def success?
true
end
def map
Success.new(yield value)
end
def bind
yield value
end
end
Refactoring
class 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

Build compound
results.

def build_project_member(project_id, member_params)
member(project_id).bind do |member|
member.build_project_member(member_params)
end
end
end

Wednesday, December 18, 13
Serialize
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
end
end

Wednesday, December 18, 13
Serialize
class 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)
end
end

Wednesday, December 18, 13
Serialize
class 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
end
end

Wednesday, December 18, 13
Serialize
class 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)
end
end

Wednesday, December 18, 13
Authorization
class 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)
end
end

Wednesday, December 18, 13
Questions?
http://smartlogic.io
http://twitter.com/smartlogic
http://github.com/smartlogic
http://facebook.com/smartlogic

Wednesday, December 18, 13

Contenu connexe

En vedette

Franc, Oriol I Adria Relatos
Franc, Oriol I Adria RelatosFranc, Oriol I Adria Relatos
Franc, Oriol I Adria Relatos4esopalamos
 
An Agile Approach to Content Planning
An Agile Approach to Content PlanningAn Agile Approach to Content Planning
An Agile Approach to Content PlanningAJi
 
Presentación sobre el curso de asignatura estatal
Presentación sobre el curso de asignatura estatalPresentación sobre el curso de asignatura estatal
Presentación sobre el curso de asignatura estatalKarina Serrano Jimenez
 
Remarkable Retail
Remarkable RetailRemarkable Retail
Remarkable RetailTable19
 
SEMESTER 1: English Writing Brief - Essay Question (individual)
SEMESTER 1: English Writing Brief - Essay Question (individual)SEMESTER 1: English Writing Brief - Essay Question (individual)
SEMESTER 1: English Writing Brief - Essay Question (individual)University of Nottingham
 
Nelson farias-acordes-arpejos-e-escalas
Nelson farias-acordes-arpejos-e-escalasNelson farias-acordes-arpejos-e-escalas
Nelson farias-acordes-arpejos-e-escalasNando Costa
 
PhRMA Report 2012: Medicines in Development for COPD
PhRMA Report 2012: Medicines in Development for COPDPhRMA Report 2012: Medicines in Development for COPD
PhRMA Report 2012: Medicines in Development for COPDPhRMA
 
Lista de-precios-compugreiff-agosto-23-2012
Lista de-precios-compugreiff-agosto-23-2012Lista de-precios-compugreiff-agosto-23-2012
Lista de-precios-compugreiff-agosto-23-2012xxxxx
 
how to make money blogging
how to make money blogginghow to make money blogging
how to make money bloggingBillieHillier
 
Where Traditional Media is Headed
Where Traditional Media is HeadedWhere Traditional Media is Headed
Where Traditional Media is HeadedCharlie Ray
 
#GetsmART Lessons from Artists #ipadpalooza16
#GetsmART Lessons from Artists #ipadpalooza16#GetsmART Lessons from Artists #ipadpalooza16
#GetsmART Lessons from Artists #ipadpalooza16Amy Burvall
 
Impacto de las TICs en mi vida
Impacto de las TICs en mi vidaImpacto de las TICs en mi vida
Impacto de las TICs en mi vidaMarinita93
 
React.js for Rails Developers
React.js for Rails DevelopersReact.js for Rails Developers
React.js for Rails DevelopersArkency
 
Why MAII Stratford University?
Why MAII Stratford University?Why MAII Stratford University?
Why MAII Stratford University?Prashant Pandey
 

En vedette (18)

E learning technologies
E learning technologiesE learning technologies
E learning technologies
 
www.clickprime8.com FALE COMIGO
www.clickprime8.com FALE COMIGO www.clickprime8.com FALE COMIGO
www.clickprime8.com FALE COMIGO
 
Franc, Oriol I Adria Relatos
Franc, Oriol I Adria RelatosFranc, Oriol I Adria Relatos
Franc, Oriol I Adria Relatos
 
An Agile Approach to Content Planning
An Agile Approach to Content PlanningAn Agile Approach to Content Planning
An Agile Approach to Content Planning
 
Presentación sobre el curso de asignatura estatal
Presentación sobre el curso de asignatura estatalPresentación sobre el curso de asignatura estatal
Presentación sobre el curso de asignatura estatal
 
Remarkable Retail
Remarkable RetailRemarkable Retail
Remarkable Retail
 
SEMESTER 1: English Writing Brief - Essay Question (individual)
SEMESTER 1: English Writing Brief - Essay Question (individual)SEMESTER 1: English Writing Brief - Essay Question (individual)
SEMESTER 1: English Writing Brief - Essay Question (individual)
 
Nelson farias-acordes-arpejos-e-escalas
Nelson farias-acordes-arpejos-e-escalasNelson farias-acordes-arpejos-e-escalas
Nelson farias-acordes-arpejos-e-escalas
 
PhRMA Report 2012: Medicines in Development for COPD
PhRMA Report 2012: Medicines in Development for COPDPhRMA Report 2012: Medicines in Development for COPD
PhRMA Report 2012: Medicines in Development for COPD
 
Lista de-precios-compugreiff-agosto-23-2012
Lista de-precios-compugreiff-agosto-23-2012Lista de-precios-compugreiff-agosto-23-2012
Lista de-precios-compugreiff-agosto-23-2012
 
how to make money blogging
how to make money blogginghow to make money blogging
how to make money blogging
 
Where Traditional Media is Headed
Where Traditional Media is HeadedWhere Traditional Media is Headed
Where Traditional Media is Headed
 
#GetsmART Lessons from Artists #ipadpalooza16
#GetsmART Lessons from Artists #ipadpalooza16#GetsmART Lessons from Artists #ipadpalooza16
#GetsmART Lessons from Artists #ipadpalooza16
 
Ejercicios 2
Ejercicios 2Ejercicios 2
Ejercicios 2
 
Impacto de las TICs en mi vida
Impacto de las TICs en mi vidaImpacto de las TICs en mi vida
Impacto de las TICs en mi vida
 
React.js for Rails Developers
React.js for Rails DevelopersReact.js for Rails Developers
React.js for Rails Developers
 
Why MAII Stratford University?
Why MAII Stratford University?Why MAII Stratford University?
Why MAII Stratford University?
 
Mma col 2016 banano
Mma col 2016 bananoMma col 2016 banano
Mma col 2016 banano
 

Similaire à Effective ActiveRecord

Building Beautiful REST APIs in ASP.NET Core
Building Beautiful REST APIs in ASP.NET CoreBuilding Beautiful REST APIs in ASP.NET Core
Building Beautiful REST APIs in ASP.NET CoreStormpath
 
Building Beautiful REST APIs in ASP.NET Core
Building Beautiful REST APIs in ASP.NET CoreBuilding Beautiful REST APIs in ASP.NET Core
Building Beautiful REST APIs in ASP.NET CoreNate Barbettini
 
Asciidoctor New, Noteworthy and Beyond Devoxx-2017
Asciidoctor New, Noteworthy and Beyond Devoxx-2017Asciidoctor New, Noteworthy and Beyond Devoxx-2017
Asciidoctor New, Noteworthy and Beyond Devoxx-2017Alex Soto
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the FinishYehuda Katz
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overviewYehuda Katz
 
RubyBarCamp “Полезные gems и plugins”
RubyBarCamp “Полезные gems и plugins”RubyBarCamp “Полезные gems и plugins”
RubyBarCamp “Полезные gems и plugins”apostlion
 
React.js workshop by tech47.in
React.js workshop by tech47.inReact.js workshop by tech47.in
React.js workshop by tech47.inJaikant Kumaran
 
Coming to Terms with GraphQL
Coming to Terms with GraphQLComing to Terms with GraphQL
Coming to Terms with GraphQLBruce Williams
 
Ejb3 Struts Tutorial En
Ejb3 Struts Tutorial EnEjb3 Struts Tutorial En
Ejb3 Struts Tutorial EnAnkur Dongre
 
Ejb3 Struts Tutorial En
Ejb3 Struts Tutorial EnEjb3 Struts Tutorial En
Ejb3 Struts Tutorial EnAnkur Dongre
 
Simplifying Persistence for Java and MongoDB with Morphia
Simplifying Persistence for Java and MongoDB with MorphiaSimplifying Persistence for Java and MongoDB with Morphia
Simplifying Persistence for Java and MongoDB with MorphiaMongoDB
 
Basic Gradle Plugin Writing
Basic Gradle Plugin WritingBasic Gradle Plugin Writing
Basic Gradle Plugin WritingSchalk Cronjé
 
Rails Model Basics
Rails Model BasicsRails Model Basics
Rails Model BasicsJames Gray
 
Desarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosDesarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosEdgar Suarez
 
The state of your own hypertext preprocessor
The state of your own hypertext preprocessorThe state of your own hypertext preprocessor
The state of your own hypertext preprocessorAlessandro Nadalin
 
Python Code Camp for Professionals 3/4
Python Code Camp for Professionals 3/4Python Code Camp for Professionals 3/4
Python Code Camp for Professionals 3/4DEVCON
 
Golang slidesaudrey
Golang slidesaudreyGolang slidesaudrey
Golang slidesaudreyAudrey Lim
 
GPars For Beginners
GPars For BeginnersGPars For Beginners
GPars For BeginnersMatt Passell
 
Google App Engine with Gaelyk
Google App Engine with GaelykGoogle App Engine with Gaelyk
Google App Engine with GaelykChoong Ping Teo
 

Similaire à Effective ActiveRecord (20)

Building Beautiful REST APIs in ASP.NET Core
Building Beautiful REST APIs in ASP.NET CoreBuilding Beautiful REST APIs in ASP.NET Core
Building Beautiful REST APIs in ASP.NET Core
 
Building Beautiful REST APIs in ASP.NET Core
Building Beautiful REST APIs in ASP.NET CoreBuilding Beautiful REST APIs in ASP.NET Core
Building Beautiful REST APIs in ASP.NET Core
 
Asciidoctor New, Noteworthy and Beyond Devoxx-2017
Asciidoctor New, Noteworthy and Beyond Devoxx-2017Asciidoctor New, Noteworthy and Beyond Devoxx-2017
Asciidoctor New, Noteworthy and Beyond Devoxx-2017
 
Rails 3: Dashing to the Finish
Rails 3: Dashing to the FinishRails 3: Dashing to the Finish
Rails 3: Dashing to the Finish
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overview
 
RubyBarCamp “Полезные gems и plugins”
RubyBarCamp “Полезные gems и plugins”RubyBarCamp “Полезные gems и plugins”
RubyBarCamp “Полезные gems и plugins”
 
React.js workshop by tech47.in
React.js workshop by tech47.inReact.js workshop by tech47.in
React.js workshop by tech47.in
 
Coming to Terms with GraphQL
Coming to Terms with GraphQLComing to Terms with GraphQL
Coming to Terms with GraphQL
 
Ejb3 Struts Tutorial En
Ejb3 Struts Tutorial EnEjb3 Struts Tutorial En
Ejb3 Struts Tutorial En
 
Ejb3 Struts Tutorial En
Ejb3 Struts Tutorial EnEjb3 Struts Tutorial En
Ejb3 Struts Tutorial En
 
Simplifying Persistence for Java and MongoDB with Morphia
Simplifying Persistence for Java and MongoDB with MorphiaSimplifying Persistence for Java and MongoDB with Morphia
Simplifying Persistence for Java and MongoDB with Morphia
 
Basic Gradle Plugin Writing
Basic Gradle Plugin WritingBasic Gradle Plugin Writing
Basic Gradle Plugin Writing
 
Rails Model Basics
Rails Model BasicsRails Model Basics
Rails Model Basics
 
Desarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutosDesarrollando aplicaciones web en minutos
Desarrollando aplicaciones web en minutos
 
The state of your own hypertext preprocessor
The state of your own hypertext preprocessorThe state of your own hypertext preprocessor
The state of your own hypertext preprocessor
 
MongoDB With Style
MongoDB With StyleMongoDB With Style
MongoDB With Style
 
Python Code Camp for Professionals 3/4
Python Code Camp for Professionals 3/4Python Code Camp for Professionals 3/4
Python Code Camp for Professionals 3/4
 
Golang slidesaudrey
Golang slidesaudreyGolang slidesaudrey
Golang slidesaudrey
 
GPars For Beginners
GPars For BeginnersGPars For Beginners
GPars For Beginners
 
Google App Engine with Gaelyk
Google App Engine with GaelykGoogle App Engine with Gaelyk
Google App Engine with Gaelyk
 

Plus de SmartLogic

Writing Game Servers with Elixir
Writing Game Servers with ElixirWriting Game Servers with Elixir
Writing Game Servers with ElixirSmartLogic
 
All Aboard The Stateful Train
All Aboard The Stateful TrainAll Aboard The Stateful Train
All Aboard The Stateful TrainSmartLogic
 
DC |> Elixir Meetup - Going off the Rails into Elixir - Dan Ivovich
DC |> Elixir Meetup - Going off the Rails into Elixir - Dan IvovichDC |> Elixir Meetup - Going off the Rails into Elixir - Dan Ivovich
DC |> Elixir Meetup - Going off the Rails into Elixir - Dan IvovichSmartLogic
 
Monitoring Your Elixir Application with Prometheus
Monitoring Your Elixir Application with PrometheusMonitoring Your Elixir Application with Prometheus
Monitoring Your Elixir Application with PrometheusSmartLogic
 
Going Multi-Node
Going Multi-NodeGoing Multi-Node
Going Multi-NodeSmartLogic
 
Kubernetes and docker
Kubernetes and dockerKubernetes and docker
Kubernetes and dockerSmartLogic
 
Serializing Value Objects-Ara Hacopian
Serializing Value Objects-Ara HacopianSerializing Value Objects-Ara Hacopian
Serializing Value Objects-Ara HacopianSmartLogic
 
Guide to food foraging by SmartLogic's Kei Ellerbrock
Guide to food foraging by SmartLogic's Kei EllerbrockGuide to food foraging by SmartLogic's Kei Ellerbrock
Guide to food foraging by SmartLogic's Kei EllerbrockSmartLogic
 
Introduction to Type Script by Sam Goldman, SmartLogic
Introduction to Type Script by Sam Goldman, SmartLogicIntroduction to Type Script by Sam Goldman, SmartLogic
Introduction to Type Script by Sam Goldman, SmartLogicSmartLogic
 
How SmartLogic Uses Chef-Dan Ivovich
How SmartLogic Uses Chef-Dan IvovichHow SmartLogic Uses Chef-Dan Ivovich
How SmartLogic Uses Chef-Dan IvovichSmartLogic
 
A Few Interesting Things in Apple's Swift Programming Language
A Few Interesting Things in Apple's Swift Programming LanguageA Few Interesting Things in Apple's Swift Programming Language
A Few Interesting Things in Apple's Swift Programming LanguageSmartLogic
 
An Introduction to Reactive Cocoa
An Introduction to Reactive CocoaAn Introduction to Reactive Cocoa
An Introduction to Reactive CocoaSmartLogic
 
iOS Development Methodology
iOS Development MethodologyiOS Development Methodology
iOS Development MethodologySmartLogic
 
CSS Preprocessors to the Rescue!
CSS Preprocessors to the Rescue!CSS Preprocessors to the Rescue!
CSS Preprocessors to the Rescue!SmartLogic
 
Deploying Rails Apps with Chef and Capistrano
 Deploying Rails Apps with Chef and Capistrano Deploying Rails Apps with Chef and Capistrano
Deploying Rails Apps with Chef and CapistranoSmartLogic
 
From Slacker to Hacker, Practical Tips for Learning to Code
From Slacker to Hacker, Practical Tips for Learning to CodeFrom Slacker to Hacker, Practical Tips for Learning to Code
From Slacker to Hacker, Practical Tips for Learning to CodeSmartLogic
 
The Language of Abstraction in Software Development
The Language of Abstraction in Software DevelopmentThe Language of Abstraction in Software Development
The Language of Abstraction in Software DevelopmentSmartLogic
 
Android Testing: An Overview
Android Testing: An OverviewAndroid Testing: An Overview
Android Testing: An OverviewSmartLogic
 
Intro to DTCoreText: Moving Past UIWebView | iOS Development
Intro to DTCoreText: Moving Past UIWebView | iOS DevelopmentIntro to DTCoreText: Moving Past UIWebView | iOS Development
Intro to DTCoreText: Moving Past UIWebView | iOS DevelopmentSmartLogic
 
Logstash: Get to know your logs
Logstash: Get to know your logsLogstash: Get to know your logs
Logstash: Get to know your logsSmartLogic
 

Plus de SmartLogic (20)

Writing Game Servers with Elixir
Writing Game Servers with ElixirWriting Game Servers with Elixir
Writing Game Servers with Elixir
 
All Aboard The Stateful Train
All Aboard The Stateful TrainAll Aboard The Stateful Train
All Aboard The Stateful Train
 
DC |> Elixir Meetup - Going off the Rails into Elixir - Dan Ivovich
DC |> Elixir Meetup - Going off the Rails into Elixir - Dan IvovichDC |> Elixir Meetup - Going off the Rails into Elixir - Dan Ivovich
DC |> Elixir Meetup - Going off the Rails into Elixir - Dan Ivovich
 
Monitoring Your Elixir Application with Prometheus
Monitoring Your Elixir Application with PrometheusMonitoring Your Elixir Application with Prometheus
Monitoring Your Elixir Application with Prometheus
 
Going Multi-Node
Going Multi-NodeGoing Multi-Node
Going Multi-Node
 
Kubernetes and docker
Kubernetes and dockerKubernetes and docker
Kubernetes and docker
 
Serializing Value Objects-Ara Hacopian
Serializing Value Objects-Ara HacopianSerializing Value Objects-Ara Hacopian
Serializing Value Objects-Ara Hacopian
 
Guide to food foraging by SmartLogic's Kei Ellerbrock
Guide to food foraging by SmartLogic's Kei EllerbrockGuide to food foraging by SmartLogic's Kei Ellerbrock
Guide to food foraging by SmartLogic's Kei Ellerbrock
 
Introduction to Type Script by Sam Goldman, SmartLogic
Introduction to Type Script by Sam Goldman, SmartLogicIntroduction to Type Script by Sam Goldman, SmartLogic
Introduction to Type Script by Sam Goldman, SmartLogic
 
How SmartLogic Uses Chef-Dan Ivovich
How SmartLogic Uses Chef-Dan IvovichHow SmartLogic Uses Chef-Dan Ivovich
How SmartLogic Uses Chef-Dan Ivovich
 
A Few Interesting Things in Apple's Swift Programming Language
A Few Interesting Things in Apple's Swift Programming LanguageA Few Interesting Things in Apple's Swift Programming Language
A Few Interesting Things in Apple's Swift Programming Language
 
An Introduction to Reactive Cocoa
An Introduction to Reactive CocoaAn Introduction to Reactive Cocoa
An Introduction to Reactive Cocoa
 
iOS Development Methodology
iOS Development MethodologyiOS Development Methodology
iOS Development Methodology
 
CSS Preprocessors to the Rescue!
CSS Preprocessors to the Rescue!CSS Preprocessors to the Rescue!
CSS Preprocessors to the Rescue!
 
Deploying Rails Apps with Chef and Capistrano
 Deploying Rails Apps with Chef and Capistrano Deploying Rails Apps with Chef and Capistrano
Deploying Rails Apps with Chef and Capistrano
 
From Slacker to Hacker, Practical Tips for Learning to Code
From Slacker to Hacker, Practical Tips for Learning to CodeFrom Slacker to Hacker, Practical Tips for Learning to Code
From Slacker to Hacker, Practical Tips for Learning to Code
 
The Language of Abstraction in Software Development
The Language of Abstraction in Software DevelopmentThe Language of Abstraction in Software Development
The Language of Abstraction in Software Development
 
Android Testing: An Overview
Android Testing: An OverviewAndroid Testing: An Overview
Android Testing: An Overview
 
Intro to DTCoreText: Moving Past UIWebView | iOS Development
Intro to DTCoreText: Moving Past UIWebView | iOS DevelopmentIntro to DTCoreText: Moving Past UIWebView | iOS Development
Intro to DTCoreText: Moving Past UIWebView | iOS Development
 
Logstash: Get to know your logs
Logstash: Get to know your logsLogstash: Get to know your logs
Logstash: Get to know your logs
 

Dernier

What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024Stephanie Beckett
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxLoriGlavin3
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxLoriGlavin3
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersRaghuram Pandurangan
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embeddingZilliz
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024BookNet Canada
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfLoriGlavin3
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxLoriGlavin3
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersNicole Novielli
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsSergiu Bodiu
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024Lorenzo Miniero
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxLoriGlavin3
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.Curtis Poe
 

Dernier (20)

What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024What's New in Teams Calling, Meetings and Devices March 2024
What's New in Teams Calling, Meetings and Devices March 2024
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptxDigital Identity is Under Attack: FIDO Paris Seminar.pptx
Digital Identity is Under Attack: FIDO Paris Seminar.pptx
 
The State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptxThe State of Passkeys with FIDO Alliance.pptx
The State of Passkeys with FIDO Alliance.pptx
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
Generative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information DevelopersGenerative AI for Technical Writer or Information Developers
Generative AI for Technical Writer or Information Developers
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embedding
 
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: Loan Stars - Tech Forum 2024
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
Moving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdfMoving Beyond Passwords: FIDO Paris Seminar.pdf
Moving Beyond Passwords: FIDO Paris Seminar.pdf
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptxThe Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
The Fit for Passkeys for Employee and Consumer Sign-ins: FIDO Paris Seminar.pptx
 
A Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software DevelopersA Journey Into the Emotions of Software Developers
A Journey Into the Emotions of Software Developers
 
DevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platformsDevEX - reference for building teams, processes, and platforms
DevEX - reference for building teams, processes, and platforms
 
SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024SIP trunking in Janus @ Kamailio World 2024
SIP trunking in Janus @ Kamailio World 2024
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptxThe Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
The Role of FIDO in a Cyber Secure Netherlands: FIDO Paris Seminar.pptx
 
How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.How AI, OpenAI, and ChatGPT impact business and software.
How AI, OpenAI, and ChatGPT impact business and software.
 

Effective ActiveRecord

  • 2. Review: Models class User < ActiveRecord::Base end foo = User.find(1) foo.name # "Foo" foo.email # "foo@example.com" bar = User.find(2) bar.name # "Bar bar.email # "bar@example.com" Wednesday, December 18, 13 users id email name 1 foo@example.com Foo 2 bar@example.com Bar
  • 3. Review: Has Many class Project < AR::Base has_many :members end class Member < AR::Base belongs_to :project end foo_project = Project.find(1) foo_project.name # "Foo project" foo_project.members. map(&:email) # ["foo@example.com", # "bar@example.com"] Wednesday, December 18, 13 projects id name 1 Foo project 2 Bar project members id project_id email 1 1 foo@example.com 2 1 bar@example.com 3 2 baz@example.com 4 2 quux@example.com
  • 4. Review: Belongs To class Project < AR::Base has_many :members end class Member < AR::Base belongs_to :project end foo = Member.find(1) foo.email # "foo@example.com" foo.project.name # "Foo project" Wednesday, December 18, 13 projects id name 1 Foo project 2 Bar project members id project_id email 1 1 foo@example.com 2 1 bar@example.com 3 2 baz@example.com 4 2 quux@example.com
  • 5. Review: Has Many Through class User < AR::Base has_many :members has_many :projects, through: :members end class Project < AR::Base has_many :members end class Member < AR::Base belongs_to :user belongs_to :project end foo = User.find(1) foo.projects.map(&:name) # ["Foo project", # "Bar project"] Wednesday, December 18, 13 users id email name 1 foo@example.com Foo projects id name 1 Foo project 2 Bar project members id project_id user_id 1 1 1 3 2 1
  • 6. Creating Records project = Project.create(name: "Project") (0.3ms) BEGIN SQL (1.5ms) INSERT INTO "projects" ("name") VALUES ($1) RETURNING "id" [["name", "Project"]] (0.4ms) COMMIT user = User.create(name: "User", email: "user@example.com") (0.3ms) BEGIN SQL (1.3ms) INSERT INTO "users" ("email", "name") VALUES ($1, $2) RETURNING "id" [["email", "user@example.com"], ["name", "User"]] (0.4ms) COMMIT member = Member.create(user: user, project: project) (0.5ms) BEGIN SQL (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
  • 7. Updating Records project.update_attributes(name: "Updated Project") (0.2ms) BEGIN SQL (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) BEGIN SQL (0.9ms) UPDATE "users" SET "name" = $1 WHERE "users"."id" = 1 [["name", "Updated User"]] (0.4ms) COMMIT Wednesday, December 18, 13
  • 8. Autosave class Member < ActiveRecord::Base belongs_to :user belongs_to :project end project = Project.new(name: "Project") user = User.new(name: "User", email: "user@example.com") member = Member.create(user: user, project: project) Guess the result. Wednesday, December 18, 13
  • 9. Autosave class Member < ActiveRecord::Base belongs_to :user belongs_to :project end project = Project.new(name: "Project") user = User.new(name: "User", email: "user@example.com") member = Member.create(user: user, project: project) (0.4ms) BEGIN SQL (2.7ms) INSERT INTO "users" ("email", "name") VALUES ($1, $2) RETURNING "id" [["email", "user@example.com"], ["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
  • 10. Autosave class Member < ActiveRecord::Base belongs_to :user belongs_to :project end member = Member.new member.build_user(name: "User", email: "user@example.com") member.build_project(name: "Project") member.save (0.4ms) BEGIN SQL (2.7ms) INSERT INTO "users" ("email", "name") VALUES ($1, $2) RETURNING "id" [["email", "user@example.com"], ["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
  • 11. Autosave class Member < ActiveRecord::Base belongs_to :user, autosave: false belongs_to :project, autosave: false end member = Member.new member.build_user(name: "User", email: "user@example.com") member.build_project(name: "Project") member.save Guess the result. Wednesday, December 18, 13
  • 12. Autosave class Member < ActiveRecord::Base belongs_to :user, autosave: false belongs_to :project, autosave: false end member = Member.new member.build_user(name: "User", email: "user@example.com") member.build_project(name: "Project") member.save PG::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
  • 13. Autosave class Member < ActiveRecord::Base belongs_to :user belongs_to :project end member.user.name = "Updated User" member.project.name = "Updated Project" member.save Guess the result. Wednesday, December 18, 13
  • 14. Autosave class Member < ActiveRecord::Base belongs_to :user belongs_to :project end member.user.name = "Updated User" member.project.name = "Updated Project" member.save (0.2ms) BEGIN (0.2ms) COMMIT Wednesday, December 18, 13
  • 15. Autosave class Member < ActiveRecord::Base belongs_to :user, autosave: true belongs_to :project, autosave: true end member.user.name = "Updated User" member.project.name = "Updated Project" member.save Guess the result. Wednesday, December 18, 13
  • 16. Autosave class Member < ActiveRecord::Base belongs_to :user, autosave: true belongs_to :project, autosave: true end member.user.name = "Updated User" member.project.name = "Updated Project" member.save (0.2ms) BEGIN SQL (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
  • 17. Autosave class Project < ActiveRecord::Base has_many :members end user = User.new(name: "User", email: "user@example.com") project = Project.new(name: "Project") project.members << Member.new(user: user) project.save Guess the result. Wednesday, December 18, 13
  • 18. Autosave class Project < ActiveRecord::Base has_many :members end user = User.new(name: "User", email: "user@example.com") project = Project.new(name: "Project") project.members << Member.new(user: user) project.save (0.7ms) BEGIN SQL (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", "user@example.com"], ["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
  • 19. Autosave class Project < ActiveRecord::Base has_many :members end user = User.new(name: "User", email: "user@example.com") project = Project.new(name: "Project") project.members.build(user: user) project.save (0.7ms) BEGIN SQL (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", "user@example.com"], ["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
  • 20. Autosave class Project < ActiveRecord::Base has_many :members, autosave: false end user = User.new(name: "User", email: "user@example.com") project = Project.new(name: "Project") project.members.build(user: user) project.save Guess the result. Wednesday, December 18, 13
  • 21. Autosave class Project < ActiveRecord::Base has_many :members, autosave: false end user = User.new(name: "User", email: "user@example.com") project = Project.new(name: "Project") project.members.build(user: user) project.save (0.4ms) BEGIN SQL (1.6ms) INSERT INTO "projects" ("name") VALUES ($1) RETURNING "id" [["name", "Project"]] (0.4ms) COMMIT Wednesday, December 18, 13
  • 22. Inverses class Project < ActiveRecord::Base has_many :tasks end class Task < ActiveRecord::Base belongs_to :project end project = Project.new(name: "Project") task = project.tasks.build project.save p project.object_id p task.project.object_id Guess the result. Wednesday, December 18, 13
  • 23. Inverses class Project < ActiveRecord::Base has_many :tasks end class Task < ActiveRecord::Base belongs_to :project end project = Project.new(name: "Project") task = project.tasks.build project.save p project.object_id p task.project.object_id Not just an extra query. Split brain! 70236648295560 Project Load (1.4ms) SELECT "projects".* FROM "projects" WHERE "projects"."id" = $1 ORDER BY "projects"."id" ASC LIMIT 1 [["id", 1]] 70236645304160 Wednesday, December 18, 13
  • 24. Inverses class Project < ActiveRecord::Base has_many :tasks, inverse_of: :project end class Task < ActiveRecord::Base belongs_to :project end project = Project.new(name: "Project") task = project.tasks.build project.save p project.object_id p task.project.object_id Guess the result. Wednesday, December 18, 13
  • 25. Inverses class Project < ActiveRecord::Base has_many :tasks, inverse_of: :project end class Task < ActiveRecord::Base belongs_to :project end project = Project.new(name: "Project") task = project.tasks.build project.save p project.object_id p task.project.object_id 70259515608140 70259515608140 Wednesday, December 18, 13
  • 26. Summary • Use autosave and inverse associations • Inspect the generated SQL for sanity • Avoid explicit transactions Wednesday, December 18, 13
  • 27. Authorization class 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) end end Wednesday, December 18, 13
  • 28. Authorization class ProjectMembersController < ApplicationController # POST /project/:project_id/members def create member = Member.create(member_params.merge(project_id: project_id)) respond_with member end Anyone can def project_id add any user to any params.require(:project_id) end project! private def member_params params.require(:member).permit(:user_id) end end Wednesday, December 18, 13
  • 29. Authorization 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) end end class User < ActiveRecord::Base has_many :members has_many :projects, through: :members May only add members to my own projects. def create_project_member(project_id, member_params) project = projects.find(project_id) project.members.create(member_params) end end Wednesday, December 18, 13
  • 30. Authorization 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 Separate build vs. create def member_params params.require(:member).permit(:user_id) end end 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) end end Wednesday, December 18, 13
  • 31. Authorization 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) end end What if I am not a member of this class User < ActiveRecord::Base has_many :members has_many :projects, through: :members project? def build_project_member(project_id, member_params) project = projects.find(project_id) project.members.build(member_params) end end Wednesday, December 18, 13
  • 32. Authorization Couldn't find Project with id=1 (ActiveRecord::RecordNotFound) Wednesday, December 18, 13
  • 33. Authorization class 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 end end Wednesday, December 18, 13
  • 34. Authorization 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 end end 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) end end Wednesday, December 18, 13
  • 35. Authorization 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 end end 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 end end Wednesday, December 18, 13
  • 36. Authorization class 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) end end Wednesday, December 18, 13
  • 37. Authorization class 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 Which error happened? def project_id params.require(:project_id) end def member_params params.require(:member).permit(:user_id) end end Wednesday, December 18, 13
  • 38. Authorization Failure = Struct.new(:error) do def success? false end end Success = Struct.new(:value) do def success? true end end 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 end end Wednesday, December 18, 13
  • 39. Authorization class 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 end end Wednesday, December 18, 13
  • 40. Authorization class 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) end end Wednesday, December 18, 13
  • 41. Authorization project = Project.create(name: "Project") alice = User.create(name: "Alice", email: "alice@example.com") bob = User.create(name: "Bob", email: "bob@example.com") p alice.build_project_member(project.id, { user_id: bob.id, role: "member" }) #<struct Failure error=:member_not_found> Wednesday, December 18, 13
  • 42. Authorization project = Project.create(name: "Project") alice = User.create(name: "Alice", email: "alice@example.com") bob = User.create(name: "Bob", email: "bob@example.com") 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> Wednesday, December 18, 13
  • 43. Authorization project = Project.create(name: "Project") alice = User.create(name: "Alice", email: "alice@example.com") bob = User.create(name: "Bob", email: "bob@example.com") 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">> Wednesday, December 18, 13
  • 44. 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
  • 45. Refactoring class 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 end end Wednesday, December 18, 13
  • 46. Refactoring class 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 end end Wednesday, December 18, 13 We need a way to combine results.
  • 47. Refactoring Failure = Struct.new(:error) do def success? false end def map self end def bind self end end Wednesday, December 18, 13 Success = Struct.new(:value) do def success? true end def map Success.new(yield value) end def bind yield value end end
  • 48. Refactoring class 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 Build compound results. def build_project_member(project_id, member_params) member(project_id).bind do |member| member.build_project_member(member_params) end end end Wednesday, December 18, 13
  • 49. Serialize 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 end end Wednesday, December 18, 13
  • 50. Serialize class 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) end end Wednesday, December 18, 13
  • 51. Serialize class 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 end end Wednesday, December 18, 13
  • 52. Serialize class 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) end end Wednesday, December 18, 13
  • 53. Authorization class 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) end end Wednesday, December 18, 13