Creando un Tinder
Parte 1, Modelos y Test
- Definir el modelo y los requisitos
- Crear el modelo de usuarios
- Crear el modelo de interacciones
- Creando el modelo de match
Definir el modelo y los requisitos
Vamos a crear un clon básico de Tinder, en esta guía abordaremos el tema de los likes y matchs.
Tinder es un aplicación que te permite hacer like o dislike a muchos usuarios y si dos usuarios se hacen like mutuamente estos pueden comunicarse.
Primero necesitamos un modelo que sea capaz de guardar a los usuarios y sus fotos.
Además debemos guardar la información de las interacciones, una interacción puede ser de like o dislike, y necesitamos guardar ambas para no mostrar nuevamente a los usuarios con las que ya hemos interactuado.
Y por último sería útil guardar los matches, no es necesario que estos estén relacionados con las interacciones puesto que una vez que ya identifcamos y creamos el match no las necesitamos.
Crear el modelo en rails de usuarios
El modelo de usuario lo crearemos directamente con devise.
Primero instalamos la gema
gem 'devise'
Luego corremos el gerador de devise
rails g devise:install
rails g devise user
Corremos las migraciones
rake db:migrate
Generamos las vistas
rails g model devise:views
Crear el modelo en rails de interacciones
Aquí es donde se pone un poco más difícil, primero tenemos que crear el modelo con el generador, pero tenemos que tener cuidado con las referencias a usuario, puesto que hay dos y por lo mismo hay que nombrarlas.
rails g model interaction user_one:integer user_two:integer like:boolean
Antes de correr las migraciones vamos a editar el archivo para agregar las claves foraneas
La migración de interacciones debería quedar así
class CreateInteractions < ActiveRecord::Migration
def change
create_table :interactions do |t|
t.integer :user_one_id, index: true, foreign_key: true
t.integer :user_two_id, index:true, foreign_key: true
t.timestamps null: false
end
add_foreign_key :interactions, :users, column: :user_one_id
add_foreign_key :interactions, :users, column: :user_two_id
end
end
El paso siguiente es crear tests para validar las reglas del negocio, particularmente nos interesa que:
Un usuario no puede interactuar consigo mismo:
test 'cant interact with myself' do
i = Interaction.new(user_one:users(:one), user_two:users(:one))
assert_not i.valid?, "cant interact with myself"
end
Un usuario si puede interactuar con otros usuarios:
test 'can interact with other user' do
i = Interaction.new(user_one:users(:one), user_two:users(:two))
assert i.valid?, "can interact with other user"
end
Un usuario no pueda interactuar dos veces con otro usuario:
test 'cant interact twice with the same user' do
i = Interaction.new(user_one:users(:one), user_two:users(:two))
i2 = Interaction.new(user_one:users(:one), user_two:users(:two))
assert i2.valid?, "can interact with other user"
end
Si dos usuarios interactúan y se gustan entonces debe generarse un match:
test 'if two users like each other a match must exist' do
i = Interaction.create(user_one:users(:one), user_two:users(:two), like:true)
i2 = Interaction.create(user_one:users(:two), user_two:users(:one), like:true)
m = Match.where(user_one:i.user_one, user_two:i.user_two)
assert_not_nil m, "a match must exist"
end
Y si dos usuarios interactúan y no se gustan entonces no debe generarse un match:
test 'if two users interact with each other but one doesnt like two, the is no match' do
i = Interaction.create(user_one:users(:one), user_two:users(:two), like:true)
i2 = Interaction.create(user_one:users(:two), user_two:users(:one), like:false)
m = Match.where(user_one:i.user_one, user_two:i.user_two)
assert_empty m, "there must be no match"
end
Creando el modelo en rails de match
Ahora estamos hablando de matches y no tenemos un modelo para match, ahora la idea aquí es muy similar, son las mismas relaciones.
rails g model interaction user_one:integer user_two:integer like:boolean
Debemos hacer la misma modificación que hicimos previamente con las interacciones.
class CreateMatches < ActiveRecord::Migration
def change
create_table :matches do |t|
t.integer :user_one_id, index: true, foreign_key: true
t.integer :user_two_id, index: true, foreign_key: true
t.timestamps null: false
end
add_foreign_key :matches, :users, column: :user_one_id
add_foreign_key :matches, :users, column: :user_two_id
end
end
Con los modelos listos ya podemos correr los tests, pero hasta que no hagamos las validaciones y las relaciones no lograremos pasarlos.
Para el modelo user.rb:
has_many :interactions_one, class_name: "Interaction", foreign_key: :user_one_id, dependent: :destroy
has_many :interactions_two, class_name: "Interaction", foreign_key: :user_two_id, dependent: :destroy
has_many :matches_one, class_name: "Match", foreign_key: :user_one_id, dependent: :destroy
has_many :matches_two, class_name: "Match", foreign_key: :user_two_id, dependent: :destroy
Para el modelo interaction.rb
class Interaction < ActiveRecord::Base
belongs_to :user_one, class_name: 'User'
belongs_to :user_two, class_name: 'User'
validates :user_one_id, uniqueness: {scope: :user_two_id, message: "cant interact twice with the same user"}
validate :cant_interact_myself
after_save :check_match
def cant_interact_myself
if self.user_one.id == self.user_two.id
errors.add(:expiration_date, "can't interact with myself")
end
end
def check_match
i = Interaction.where(user_one: self.user_two, user_two:self.user_one)
unless i.empty?
Match.create(user_one_id: self.user_one.id, user_two_id: self.user_two.id) end
end
end
La línea 2 y 3 crean las relaciones con el modelo de usuario, es necesario especificar la clase puesto que al cambiarle el nombre rails no es capaz de deducirlo.
La línea 5 valida que la interacción sea única.
La línea 6 y el método cantinteractmyself validan que el usuario no pueda interactuar con el mismo
Finalmente la línea 7 y el método check_match ingresan automáticamente un match cuando existe la interacción de like del usuario 1 al 2 y del usuario 2 al 1.
Para el modelo de match.rb
class Match < ActiveRecord::Base
belongs_to :user_one, class_name: 'User', foreign_key: :user_one_id
belongs_to :user_two, class_name: 'User', foreign_key: :user_two_id
def get_user(id)
if self.user_one_id == id
return self.user_two
else
return self.user_one
end
end
end
En los registros de match no nos importa quien hizo match con quien, por lo mismo agregaremos un método que necesitaremos después que nos permite obtener quien es la persona a la que se hizo match dado el otro usuario.
Si quieres aprender a hacer más cosas como éstas y dedicarte de lleno del desarrollo con RoR, te recomiendo te sumes a nuestro bootcamp.