From 37c65fa5c86bca6dce9167bf2284534513cf42b9 Mon Sep 17 00:00:00 2001 From: Nate Date: Wed, 2 Dec 2015 19:51:28 -0500 Subject: [PATCH 1/5] create user model --- app/models/user.rb | 2 ++ db/migrate/20151203004651_create_users.rb | 15 +++++++++++ db/schema.rb | 31 +++++++++++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 app/models/user.rb create mode 100644 db/migrate/20151203004651_create_users.rb create mode 100644 db/schema.rb diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 0000000..4a57cf0 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,2 @@ +class User < ActiveRecord::Base +end diff --git a/db/migrate/20151203004651_create_users.rb b/db/migrate/20151203004651_create_users.rb new file mode 100644 index 0000000..1662110 --- /dev/null +++ b/db/migrate/20151203004651_create_users.rb @@ -0,0 +1,15 @@ +class CreateUsers < ActiveRecord::Migration + def change + create_table :users do |t| + t.string :name + t.string :email + t.string :password_digest + t.boolean :email_confirmed + t.string :confirm_digest + t.string :reset_digest + t.datetime :reset_sent_at + + t.timestamps null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..670bfe6 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,31 @@ +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20151203004651) do + + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "users", force: :cascade do |t| + t.string "name" + t.string "email" + t.string "password_digest" + t.boolean "email_confirmed" + t.string "confirm_digest" + t.string "reset_digest" + t.datetime "reset_sent_at" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + +end From 107ba7ae2eff3771f65b6809518c754b99058c8e Mon Sep 17 00:00:00 2001 From: Nate Date: Wed, 2 Dec 2015 21:50:13 -0500 Subject: [PATCH 2/5] red user specs --- spec/factories/user.rb | 23 +++++++++++++++ spec/models/user_spec.rb | 60 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+) create mode 100644 spec/factories/user.rb create mode 100644 spec/models/user_spec.rb diff --git a/spec/factories/user.rb b/spec/factories/user.rb new file mode 100644 index 0000000..2ca9d40 --- /dev/null +++ b/spec/factories/user.rb @@ -0,0 +1,23 @@ +FactoryGirl.define do + factory :confirmed_user, class: User do + first_name Faker::Name.first_name + last_name Faker::Name.last_name + email Faker::Internet.email + city Faker::Address.city + state Faker::Address.state + country Faker::Address.country + email_confirmed true + end + + factory :unconfirmed_user, class: User do + first_name Faker::Name.first_name + last_name Faker::Name.last_name + email Faker::Internet.email + city Faker::Address.city + state Faker::Address.state + country Faker::Address.country + password "helloworld" + password_confirmation "helloworld" + email_confirmed false + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb new file mode 100644 index 0000000..a86c0fa --- /dev/null +++ b/spec/models/user_spec.rb @@ -0,0 +1,60 @@ +require "rails_helper" + +RSpec.describe User do + describe "attributes" do + it "has the correct attributes" do + %w( + first_name + last_name + email + password_digest + email_confirmed + confirm_digest + reset_digest + reset_sent_at + city + state + country + ).each do |attribute| + expect(described_class.column_names.include?(attribute)).to be true + end + end + end + + describe "validations" do + it "rejects a new user without a first_name" do + user = FactoryGirl.build(:unconfirmed_user, first_name: nil) + expect(user.save).to be false + end + + it "rejects a new user without a last_name" do + user = FactoryGirl.build(:unconfirmed_user, last_name: nil) + expect(user.save).to be false + end + + it "rejects a new user without a password" do + user = FactoryGirl.build(:unconfirmed_user, password: nil) + expect(user.save).to be false + end + + it "rejects a new user without a password_confirmation" do + user = FactoryGirl.build(:unconfirmed_user, password_confirmation: nil) + expect(user.save).to be false + end + + it "validates password length" do + user = FactoryGirl.build(:unconfirmed_user, password: "xxx") + expect(user.save).to be false + end + + it "rejects a new user without an email" do + user = FactoryGirl.build(:unconfirmed_user, email: nil) + expect(user.save).to be false + end + + it "valudates email format" do + user = FactoryGirl.build(:unconfirmed_user, email: "xxx.xxx") + expect(user.save).to be false + end + end +end From 1647ffc68c227b69a11ec4ed409736c481583459 Mon Sep 17 00:00:00 2001 From: Nate Date: Wed, 2 Dec 2015 22:03:28 -0500 Subject: [PATCH 3/5] break "location" attribute out to city, state, country MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also rename “name” to “first_name” and add “last_name” attr. --- .../20151203025432_rename_user_name_to_first_name.rb | 5 +++++ db/migrate/20151203030029_add_last_name_to_user.rb | 5 +++++ db/migrate/20151203030116_add_city_to_user.rb | 5 +++++ db/migrate/20151203030132_add_state_to_user.rb | 5 +++++ db/migrate/20151203030146_add_country_to_user.rb | 5 +++++ db/schema.rb | 8 ++++++-- 6 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 db/migrate/20151203025432_rename_user_name_to_first_name.rb create mode 100644 db/migrate/20151203030029_add_last_name_to_user.rb create mode 100644 db/migrate/20151203030116_add_city_to_user.rb create mode 100644 db/migrate/20151203030132_add_state_to_user.rb create mode 100644 db/migrate/20151203030146_add_country_to_user.rb diff --git a/db/migrate/20151203025432_rename_user_name_to_first_name.rb b/db/migrate/20151203025432_rename_user_name_to_first_name.rb new file mode 100644 index 0000000..0646686 --- /dev/null +++ b/db/migrate/20151203025432_rename_user_name_to_first_name.rb @@ -0,0 +1,5 @@ +class RenameUserNameToFirstName < ActiveRecord::Migration + def change + rename_column :users, :name, :first_name + end +end diff --git a/db/migrate/20151203030029_add_last_name_to_user.rb b/db/migrate/20151203030029_add_last_name_to_user.rb new file mode 100644 index 0000000..046ef33 --- /dev/null +++ b/db/migrate/20151203030029_add_last_name_to_user.rb @@ -0,0 +1,5 @@ +class AddLastNameToUser < ActiveRecord::Migration + def change + add_column :users, :last_name, :string + end +end diff --git a/db/migrate/20151203030116_add_city_to_user.rb b/db/migrate/20151203030116_add_city_to_user.rb new file mode 100644 index 0000000..7b5a5cd --- /dev/null +++ b/db/migrate/20151203030116_add_city_to_user.rb @@ -0,0 +1,5 @@ +class AddCityToUser < ActiveRecord::Migration + def change + add_column :users, :city, :string + end +end diff --git a/db/migrate/20151203030132_add_state_to_user.rb b/db/migrate/20151203030132_add_state_to_user.rb new file mode 100644 index 0000000..8154c21 --- /dev/null +++ b/db/migrate/20151203030132_add_state_to_user.rb @@ -0,0 +1,5 @@ +class AddStateToUser < ActiveRecord::Migration + def change + add_column :users, :state, :string + end +end diff --git a/db/migrate/20151203030146_add_country_to_user.rb b/db/migrate/20151203030146_add_country_to_user.rb new file mode 100644 index 0000000..d17a270 --- /dev/null +++ b/db/migrate/20151203030146_add_country_to_user.rb @@ -0,0 +1,5 @@ +class AddCountryToUser < ActiveRecord::Migration + def change + add_column :users, :country, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 670bfe6..524c606 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,13 +11,13 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20151203004651) do +ActiveRecord::Schema.define(version: 20151203030146) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" create_table "users", force: :cascade do |t| - t.string "name" + t.string "first_name" t.string "email" t.string "password_digest" t.boolean "email_confirmed" @@ -26,6 +26,10 @@ t.datetime "reset_sent_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "last_name" + t.string "city" + t.string "state" + t.string "country" end end From 4bfbb72bf991ea58c4cf161fff2fd9945de70bbe Mon Sep 17 00:00:00 2001 From: Nate Date: Wed, 2 Dec 2015 22:10:10 -0500 Subject: [PATCH 4/5] add location validation specs for user --- spec/models/user_spec.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index a86c0fa..3df1a0f 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -42,6 +42,21 @@ expect(user.save).to be false end + it "rejects a new user without a city" do + user = FactoryGirl.build(:unconfirmed_user, city: nil) + expect(user.save).to be false + end + + it "rejects a new user without a state" do + user = FactoryGirl.build(:unconfirmed_user, state: nil) + expect(user.save).to be false + end + + it "rejects a new user without a country" do + user = FactoryGirl.build(:unconfirmed_user, country: nil) + expect(user.save).to be false + end + it "validates password length" do user = FactoryGirl.build(:unconfirmed_user, password: "xxx") expect(user.save).to be false From 078d85ecf03f85f2f4d90a9052bd86d30f95a370 Mon Sep 17 00:00:00 2001 From: Nate Date: Fri, 4 Dec 2015 19:10:07 -0500 Subject: [PATCH 5/5] green specs --- app/models/user.rb | 12 ++++++ spec/factories/user.rb | 1 - spec/models/user_spec.rb | 86 ++++++++++++++-------------------------- 3 files changed, 42 insertions(+), 57 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index 4a57cf0..924c503 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,2 +1,14 @@ class User < ActiveRecord::Base + has_secure_password + + validates :first_name, presence: true + validates :last_name, presence: true + validates :email, presence: true + validates :city, presence: true + validates :state, presence: true + validates :country, presence: true + validates :password, length: { minimum: 8 } + validates :email, format: { + with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i, on: :create + } end diff --git a/spec/factories/user.rb b/spec/factories/user.rb index 2ca9d40..1cc3e8f 100644 --- a/spec/factories/user.rb +++ b/spec/factories/user.rb @@ -17,7 +17,6 @@ state Faker::Address.state country Faker::Address.country password "helloworld" - password_confirmation "helloworld" email_confirmed false end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 3df1a0f..9e42481 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,75 +1,49 @@ require "rails_helper" RSpec.describe User do - describe "attributes" do - it "has the correct attributes" do - %w( - first_name - last_name - email - password_digest - email_confirmed - confirm_digest - reset_digest - reset_sent_at - city - state - country - ).each do |attribute| - expect(described_class.column_names.include?(attribute)).to be true + required_props = %i( + first_name + last_name + email + city + state + country + password + ) + + properties = required_props + %i( + password_digest + email_confirmed + confirm_digest + reset_digest + reset_sent_at + ) + + describe "properties" do + properties.each do |prop| + it "has an accessor for #{prop}" do + expect { subject.send(prop) }.to_not raise_error end end end describe "validations" do - it "rejects a new user without a first_name" do - user = FactoryGirl.build(:unconfirmed_user, first_name: nil) - expect(user.save).to be false - end - - it "rejects a new user without a last_name" do - user = FactoryGirl.build(:unconfirmed_user, last_name: nil) - expect(user.save).to be false - end - - it "rejects a new user without a password" do - user = FactoryGirl.build(:unconfirmed_user, password: nil) - expect(user.save).to be false - end - - it "rejects a new user without a password_confirmation" do - user = FactoryGirl.build(:unconfirmed_user, password_confirmation: nil) - expect(user.save).to be false - end - - it "rejects a new user without a city" do - user = FactoryGirl.build(:unconfirmed_user, city: nil) - expect(user.save).to be false - end - - it "rejects a new user without a state" do - user = FactoryGirl.build(:unconfirmed_user, state: nil) - expect(user.save).to be false - end - - it "rejects a new user without a country" do - user = FactoryGirl.build(:unconfirmed_user, country: nil) - expect(user.save).to be false + required_props.each do |prop| + it "rejects a new user without a `#{prop}`" do + user = FactoryGirl.build(:unconfirmed_user) + user.send("#{prop}=", nil) + expect(user.save).to be_falsy + end end it "validates password length" do user = FactoryGirl.build(:unconfirmed_user, password: "xxx") - expect(user.save).to be false - end - - it "rejects a new user without an email" do - user = FactoryGirl.build(:unconfirmed_user, email: nil) - expect(user.save).to be false + expect(user.save).to be_falsy end it "valudates email format" do user = FactoryGirl.build(:unconfirmed_user, email: "xxx.xxx") - expect(user.save).to be false + expect(user.save).to be_falsy end end end