From c21be9d1cdcc2117f5e743339c76cfa88cc00bfd Mon Sep 17 00:00:00 2001 From: hpjaj Date: Sun, 1 Apr 2018 14:09:16 -0600 Subject: [PATCH 01/11] Adds Role table and model --- app/models/role.rb | 5 +++++ db/migrate/20180401034441_create_roles.rb | 11 +++++++++++ db/schema.rb | 10 +++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 app/models/role.rb create mode 100644 db/migrate/20180401034441_create_roles.rb diff --git a/app/models/role.rb b/app/models/role.rb new file mode 100644 index 00000000..e8677c20 --- /dev/null +++ b/app/models/role.rb @@ -0,0 +1,5 @@ +class Role < ApplicationRecord + has_many :users + + validates :title, presence: true, uniqueness: true +end diff --git a/db/migrate/20180401034441_create_roles.rb b/db/migrate/20180401034441_create_roles.rb new file mode 100644 index 00000000..45748efc --- /dev/null +++ b/db/migrate/20180401034441_create_roles.rb @@ -0,0 +1,11 @@ +class CreateRoles < ActiveRecord::Migration[5.0] + def change + create_table :roles do |t| + t.string :title + + t.timestamps + end + + add_reference :users, :role, index: true + end +end diff --git a/db/schema.rb b/db/schema.rb index b5e8abd1..dde05ddc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180204235116) do +ActiveRecord::Schema.define(version: 20180401034441) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -152,6 +152,12 @@ t.datetime "updated_at", null: false end + create_table "roles", force: :cascade do |t| + t.string "title" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "scholarship_applications", force: :cascade do |t| t.text "reason" t.boolean "terms_accepted" @@ -258,8 +264,10 @@ t.string "education_level" t.string "interests" t.boolean "scholarship_info", default: false + t.integer "role_id" t.index ["email"], name: "index_users_on_email", unique: true, using: :btree t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree + t.index ["role_id"], name: "index_users_on_role_id", using: :btree end create_table "votes", force: :cascade do |t| From 30dfffe3624dfb6b1adc146026789b10a98b8b25 Mon Sep 17 00:00:00 2001 From: hpjaj Date: Sun, 1 Apr 2018 14:10:06 -0600 Subject: [PATCH 02/11] Adds test coverage for Role validations --- test/factories/roles.rb | 5 +++++ test/models/role_test.rb | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 test/factories/roles.rb create mode 100644 test/models/role_test.rb diff --git a/test/factories/roles.rb b/test/factories/roles.rb new file mode 100644 index 00000000..970404c4 --- /dev/null +++ b/test/factories/roles.rb @@ -0,0 +1,5 @@ +FactoryGirl.define do + factory :role do + title "standard_user" + end +end diff --git a/test/models/role_test.rb b/test/models/role_test.rb new file mode 100644 index 00000000..3fb99689 --- /dev/null +++ b/test/models/role_test.rb @@ -0,0 +1,20 @@ +require 'test_helper' + +class RoleTest < ActiveSupport::TestCase + test 'role requires a title' do + role = build(:role, title: '') + + refute role.valid? + end + + test 'title must be unique' do + standard_role = create :role, title: 'standard' + assert standard_role.valid? + + duplicate_role = build :role, title: 'standard' + duplicate_role.save + + refute duplicate_role.valid? + assert duplicate_role.errors.full_messages.include? "Title has already been taken" + end +end From 79679ded549614a27121968d9af63d492230da95 Mon Sep 17 00:00:00 2001 From: hpjaj Date: Sun, 1 Apr 2018 14:10:44 -0600 Subject: [PATCH 03/11] Updates strong params for new user attr --- app/controllers/api/v1/users_controller.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/controllers/api/v1/users_controller.rb b/app/controllers/api/v1/users_controller.rb index 0b6607ad..267a59ae 100644 --- a/app/controllers/api/v1/users_controller.rb +++ b/app/controllers/api/v1/users_controller.rb @@ -79,6 +79,7 @@ def user_params :company_name, :education_level, :scholarship_info, + :role_id, interests: [] ) end From 81f520ef2acbb350f3958705b432e4da131a6681 Mon Sep 17 00:00:00 2001 From: hpjaj Date: Sun, 1 Apr 2018 14:11:12 -0600 Subject: [PATCH 04/11] First draft of rake task to seed the initial role types --- lib/tasks/roles.rake | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 lib/tasks/roles.rake diff --git a/lib/tasks/roles.rake b/lib/tasks/roles.rake new file mode 100644 index 00000000..19863b3e --- /dev/null +++ b/lib/tasks/roles.rake @@ -0,0 +1,12 @@ +namespace :roles do + desc "Creates the initial set of roles" + task initial_set: :environment do + ['admin', 'board_member'].each do |role| + begin + Role.find_or_create_by!(title: role) + rescue ActiveRecord::RecordInvalid => e + p e.record.errors + end + end + end +end From 1c97fa641c7cedde6cc7367f6a09c778a8538ea1 Mon Sep 17 00:00:00 2001 From: hpjaj Date: Sun, 1 Apr 2018 14:13:09 -0600 Subject: [PATCH 05/11] Favors a role method over a belongs_to association Reason being, with the belongs_to association, every single user record would *have to* have an associated role. That is not the use case for us. Standard users are authenticated, and do not need a named role. Authentication grants them the access level they need. The role will come into play for users that need increased access. For example an admin, or board member, or super admin, etc., etc. --- app/models/user.rb | 8 +- ..._an_id_of_user_role_id_if_there_is_one.yml | 106 ++++++++++++++++++ test/models/user_test.rb | 18 +++ 3 files changed, 130 insertions(+), 2 deletions(-) create mode 100644 test/cassettes/user_test/role_gets_the_role_with_an_id_of_user_role_id_if_there_is_one.yml diff --git a/app/models/user.rb b/app/models/user.rb index 7ef49771..b32fc5c2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -194,12 +194,16 @@ def has_tag?(tag) tag_list.include? tag end + def role + Role.find_by id: role_id + end + private def zip_code_exists - return if longitude && latitude + return if longitude && latitude - errors.add(:zip_code, 'not found') + errors.add(:zip_code, 'not found') end def upcase_state diff --git a/test/cassettes/user_test/role_gets_the_role_with_an_id_of_user_role_id_if_there_is_one.yml b/test/cassettes/user_test/role_gets_the_role_with_an_id_of_user_role_id_if_there_is_one.yml new file mode 100644 index 00000000..ffd3f64a --- /dev/null +++ b/test/cassettes/user_test/role_gets_the_role_with_an_id_of_user_role_id_if_there_is_one.yml @@ -0,0 +1,106 @@ +--- +http_interactions: +- request: + method: get + uri: http://maps.googleapis.com/maps/api/geocode/json?address=80112&language=en&sensor=false + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json; charset=UTF-8 + Date: + - Sun, 01 Apr 2018 18:46:22 GMT + Expires: + - Mon, 02 Apr 2018 18:46:22 GMT + Access-Control-Allow-Origin: + - "*" + Server: + - mafe + Content-Length: + - '499' + X-Xss-Protection: + - 1; mode=block + X-Frame-Options: + - SAMEORIGIN + Cache-Control: + - public, max-age=86400 + Age: + - '73' + body: + encoding: ASCII-8BIT + string: | + { + "results" : [ + { + "address_components" : [ + { + "long_name" : "80112", + "short_name" : "80112", + "types" : [ "postal_code" ] + }, + { + "long_name" : "Englewood", + "short_name" : "Englewood", + "types" : [ "locality", "political" ] + }, + { + "long_name" : "Colorado", + "short_name" : "CO", + "types" : [ "administrative_area_level_1", "political" ] + }, + { + "long_name" : "United States", + "short_name" : "US", + "types" : [ "country", "political" ] + } + ], + "formatted_address" : "Englewood, CO 80112, USA", + "geometry" : { + "bounds" : { + "northeast" : { + "lat" : 39.62408689999999, + "lng" : -104.791368 + }, + "southwest" : { + "lat" : 39.53471589999999, + "lng" : -104.923045 + } + }, + "location" : { + "lat" : 39.5835785, + "lng" : -104.8571368 + }, + "location_type" : "APPROXIMATE", + "viewport" : { + "northeast" : { + "lat" : 39.62408689999999, + "lng" : -104.791368 + }, + "southwest" : { + "lat" : 39.53471589999999, + "lng" : -104.923045 + } + } + }, + "place_id" : "ChIJV6Vm65GFbIcR6nFrHQASDWk", + "postcode_localities" : [ "Centennial", "Englewood", "Greenwood Village" ], + "types" : [ "postal_code" ] + } + ], + "status" : "OK" + } + http_version: + recorded_at: Sun, 01 Apr 2018 18:47:35 GMT +recorded_with: VCR 3.0.3 diff --git a/test/models/user_test.rb b/test/models/user_test.rb index 4f6b6426..bdaf2140 100644 --- a/test/models/user_test.rb +++ b/test/models/user_test.rb @@ -33,6 +33,24 @@ class UserTest < ActiveSupport::TestCase assert_equal 'update_email@example.com', u.email end + test "role gets the Role with an id of user#role_id, if there is one" do + user = User.create( + first_name: 'Jack', + last_name: 'Smith', + email: 'jack@example.com', + zip: '80112', + password: 'password', + password_confirmation: 'password' + ) + + assert user.valid? + assert user.role.nil? + + some_role = create :role + user.update! role_id: some_role.id + assert user.role == some_role + end + test 'doesnt geocode until we save' do u = build(:user, latitude: nil, longitude: nil) assert u.valid? From 79c86194cc44afc0699c355e21acab9b217f2e99 Mon Sep 17 00:00:00 2001 From: hpjaj Date: Sun, 1 Apr 2018 14:13:41 -0600 Subject: [PATCH 06/11] A little citizenship of the code base --- app/policies/request_policy.rb | 1 - db/seeds.rb | 11 +++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/policies/request_policy.rb b/app/policies/request_policy.rb index 0fa6b3b0..8fbd6b74 100644 --- a/app/policies/request_policy.rb +++ b/app/policies/request_policy.rb @@ -13,5 +13,4 @@ def index? def show? user.mentor? end - end diff --git a/db/seeds.rb b/db/seeds.rb index fe867a86..32fc80e4 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -32,3 +32,14 @@ # Create Admin (development only) AdminUser.create!(email: 'admin@example.com', password: 'password', password_confirmation: 'password') if Rails.env.development? + +users = User.count +requests = Request.count +services = Service.count +team_members = TeamMember.count + +puts 'Seeding complete. Created:' +p "#{users} users" +p "#{requests} requests" +p "#{services} services" +p "#{team_members} team members" From d63a26ab96efb723fcb02a0cefa84f6d46ccb3de Mon Sep 17 00:00:00 2001 From: hpjaj Date: Sun, 1 Apr 2018 14:33:34 -0600 Subject: [PATCH 07/11] Adds role title to the user serializer --- app/serializers/user_serializer.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index d6cdcf69..23ddbfff 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -1,4 +1,9 @@ class UserSerializer < ActiveModel::Serializer attributes :id, :email, :zip, :first_name, :last_name, :slack_name, :mentor, :verified + attributes :role + + def role + object.role&.title + end end From d85c8e8edaa716f1f75040fb9fb282d6af492958 Mon Sep 17 00:00:00 2001 From: hpjaj Date: Sun, 1 Apr 2018 20:24:16 -0600 Subject: [PATCH 08/11] Saves extra db call when nil --- app/models/user.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/user.rb b/app/models/user.rb index b32fc5c2..6249f795 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -195,6 +195,8 @@ def has_tag?(tag) end def role + return if role_id.blank? + Role.find_by id: role_id end From 6228db464ba40bf3e39360e3b490193ad0d79468 Mon Sep 17 00:00:00 2001 From: hpjaj Date: Sat, 7 Apr 2018 13:37:13 -0600 Subject: [PATCH 09/11] Favors factory usage for user creation --- ..._an_id_of_user_role_id_if_there_is_one.yml | 63 ++++++++++--------- test/models/user_test.rb | 10 +-- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/test/cassettes/user_test/role_gets_the_role_with_an_id_of_user_role_id_if_there_is_one.yml b/test/cassettes/user_test/role_gets_the_role_with_an_id_of_user_role_id_if_there_is_one.yml index ffd3f64a..29f0cd57 100644 --- a/test/cassettes/user_test/role_gets_the_role_with_an_id_of_user_role_id_if_there_is_one.yml +++ b/test/cassettes/user_test/role_gets_the_role_with_an_id_of_user_role_id_if_there_is_one.yml @@ -2,7 +2,7 @@ http_interactions: - request: method: get - uri: http://maps.googleapis.com/maps/api/geocode/json?address=80112&language=en&sensor=false + uri: http://maps.googleapis.com/maps/api/geocode/json?address=97201&language=en&sensor=false body: encoding: US-ASCII string: '' @@ -21,23 +21,21 @@ http_interactions: Content-Type: - application/json; charset=UTF-8 Date: - - Sun, 01 Apr 2018 18:46:22 GMT + - Sat, 07 Apr 2018 19:35:47 GMT Expires: - - Mon, 02 Apr 2018 18:46:22 GMT + - Sun, 08 Apr 2018 19:35:47 GMT + Cache-Control: + - public, max-age=86400 Access-Control-Allow-Origin: - "*" Server: - mafe Content-Length: - - '499' + - '495' X-Xss-Protection: - 1; mode=block X-Frame-Options: - SAMEORIGIN - Cache-Control: - - public, max-age=86400 - Age: - - '73' body: encoding: ASCII-8BIT string: | @@ -46,18 +44,28 @@ http_interactions: { "address_components" : [ { - "long_name" : "80112", - "short_name" : "80112", + "long_name" : "97201", + "short_name" : "97201", "types" : [ "postal_code" ] }, { - "long_name" : "Englewood", - "short_name" : "Englewood", + "long_name" : "Southwest Portland", + "short_name" : "Southwest Portland", + "types" : [ "neighborhood", "political" ] + }, + { + "long_name" : "Portland", + "short_name" : "Portland", "types" : [ "locality", "political" ] }, { - "long_name" : "Colorado", - "short_name" : "CO", + "long_name" : "Multnomah County", + "short_name" : "Multnomah County", + "types" : [ "administrative_area_level_2", "political" ] + }, + { + "long_name" : "Oregon", + "short_name" : "OR", "types" : [ "administrative_area_level_1", "political" ] }, { @@ -66,41 +74,40 @@ http_interactions: "types" : [ "country", "political" ] } ], - "formatted_address" : "Englewood, CO 80112, USA", + "formatted_address" : "Portland, OR 97201, USA", "geometry" : { "bounds" : { "northeast" : { - "lat" : 39.62408689999999, - "lng" : -104.791368 + "lat" : 45.519965, + "lng" : -122.665103 }, "southwest" : { - "lat" : 39.53471589999999, - "lng" : -104.923045 + "lat" : 45.497957, + "lng" : -122.7193571 } }, "location" : { - "lat" : 39.5835785, - "lng" : -104.8571368 + "lat" : 45.505603, + "lng" : -122.6882145 }, "location_type" : "APPROXIMATE", "viewport" : { "northeast" : { - "lat" : 39.62408689999999, - "lng" : -104.791368 + "lat" : 45.519965, + "lng" : -122.665103 }, "southwest" : { - "lat" : 39.53471589999999, - "lng" : -104.923045 + "lat" : 45.497957, + "lng" : -122.7193571 } } }, - "place_id" : "ChIJV6Vm65GFbIcR6nFrHQASDWk", - "postcode_localities" : [ "Centennial", "Englewood", "Greenwood Village" ], + "place_id" : "ChIJi8MjqD8KlVQRsyr5ZtaBLMI", "types" : [ "postal_code" ] } ], "status" : "OK" } http_version: - recorded_at: Sun, 01 Apr 2018 18:47:35 GMT + recorded_at: Sat, 07 Apr 2018 19:35:47 GMT recorded_with: VCR 3.0.3 diff --git a/test/models/user_test.rb b/test/models/user_test.rb index bdaf2140..572e06c8 100644 --- a/test/models/user_test.rb +++ b/test/models/user_test.rb @@ -34,15 +34,7 @@ class UserTest < ActiveSupport::TestCase end test "role gets the Role with an id of user#role_id, if there is one" do - user = User.create( - first_name: 'Jack', - last_name: 'Smith', - email: 'jack@example.com', - zip: '80112', - password: 'password', - password_confirmation: 'password' - ) - + user = create :user assert user.valid? assert user.role.nil? From 8246f283e6ef8c7425a61f76a9dfd7088f1c4f20 Mon Sep 17 00:00:00 2001 From: hpjaj Date: Sun, 8 Apr 2018 10:02:18 -0600 Subject: [PATCH 10/11] Updates seeds with all models that are created --- db/seeds.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/db/seeds.rb b/db/seeds.rb index 32fc80e4..59113101 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -10,6 +10,7 @@ User.destroy_all Service.destroy_all TeamMember.destroy_all +AdminUser.destroy_all FactoryGirl.create(:user) FactoryGirl.create(:user) @@ -37,9 +38,11 @@ requests = Request.count services = Service.count team_members = TeamMember.count +admin_users = AdminUser.count puts 'Seeding complete. Created:' p "#{users} users" p "#{requests} requests" p "#{services} services" p "#{team_members} team members" +p "#{admin_users} admin users" From 065b5b790dc0ae904cea8aac4143f9d959da0771 Mon Sep 17 00:00:00 2001 From: hpjaj Date: Sun, 8 Apr 2018 10:02:46 -0600 Subject: [PATCH 11/11] Favors three initial roles --- lib/tasks/roles.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/roles.rake b/lib/tasks/roles.rake index 19863b3e..b8a7225e 100644 --- a/lib/tasks/roles.rake +++ b/lib/tasks/roles.rake @@ -1,7 +1,7 @@ namespace :roles do desc "Creates the initial set of roles" task initial_set: :environment do - ['admin', 'board_member'].each do |role| + ['super_admin', 'admin', 'board_member'].each do |role| begin Role.find_or_create_by!(title: role) rescue ActiveRecord::RecordInvalid => e