diff --git a/Gemfile b/Gemfile index 2f263d7..8224856 100644 --- a/Gemfile +++ b/Gemfile @@ -15,3 +15,5 @@ group :development, :test do gem "rspec-rails", "~> 5.1" gem "factory_bot_rails", "~> 6.2" end + +gem "faker", "~> 2.19" diff --git a/Gemfile.lock b/Gemfile.lock index 489d888..2a9fd44 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -82,6 +82,8 @@ GEM factory_bot_rails (6.2.0) factory_bot (~> 6.2.0) railties (>= 5.0.0) + faker (2.19.0) + i18n (>= 1.6, < 2) globalid (1.0.0) activesupport (>= 5.0) i18n (1.10.0) @@ -174,8 +176,6 @@ GEM rspec-mocks (~> 3.10) rspec-support (~> 3.10) rspec-support (3.11.0) - shoulda-matchers (5.1.0) - activesupport (>= 5.2.0) strscan (3.0.1) thor (1.2.1) timeout (0.2.0) @@ -193,12 +193,12 @@ DEPENDENCIES bootsnap debug factory_bot_rails (~> 6.2) + faker (~> 2.19) jbuilder pg (~> 1.1) puma (~> 5.0) rails (~> 7.0.2, >= 7.0.2.2) rspec-rails (~> 5.1) - shoulda-matchers (~> 5.1) RUBY VERSION ruby 3.1.1p18 diff --git a/app/controllers/user_follows_controller.rb b/app/controllers/user_follows_controller.rb new file mode 100644 index 0000000..c683931 --- /dev/null +++ b/app/controllers/user_follows_controller.rb @@ -0,0 +1,53 @@ +class UserFollowsController < ApplicationController + before_action :set_user_follow, only: %i[ show update destroy ] + + # GET /user_follows + # GET /user_follows.json + def index + @user_follows = UserFollow.all + end + + # GET /user_follows/1 + # GET /user_follows/1.json + def show + end + + # POST /user_follows + # POST /user_follows.json + def create + @user_follow = UserFollow.new(user_follow_params) + + if @user_follow.save + render :show, status: :created, location: @user_follow + else + render json: @user_follow.errors, status: :unprocessable_entity + end + end + + # PATCH/PUT /user_follows/1 + # PATCH/PUT /user_follows/1.json + def update + if @user_follow.update(user_follow_params) + render :show, status: :ok, location: @user_follow + else + render json: @user_follow.errors, status: :unprocessable_entity + end + end + + # DELETE /user_follows/1 + # DELETE /user_follows/1.json + def destroy + @user_follow.destroy + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_user_follow + @user_follow = UserFollow.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def user_follow_params + params.require(:user_follow).permit(:follower_id, :followed_id) + end +end diff --git a/app/models/user_follow.rb b/app/models/user_follow.rb new file mode 100644 index 0000000..2c53da6 --- /dev/null +++ b/app/models/user_follow.rb @@ -0,0 +1,4 @@ +class UserFollow < ApplicationRecord + belongs_to :follower, class_name: 'User' + belongs_to :followed, class_name: 'User' +end diff --git a/app/views/user_follows/_user_follow.json.jbuilder b/app/views/user_follows/_user_follow.json.jbuilder new file mode 100644 index 0000000..7f04c71 --- /dev/null +++ b/app/views/user_follows/_user_follow.json.jbuilder @@ -0,0 +1,2 @@ +json.extract! user_follow, :id, :follower_id, :followed_id, :created_at, :updated_at +json.url user_follow_url(user_follow, format: :json) diff --git a/app/views/user_follows/index.json.jbuilder b/app/views/user_follows/index.json.jbuilder new file mode 100644 index 0000000..7acac8f --- /dev/null +++ b/app/views/user_follows/index.json.jbuilder @@ -0,0 +1 @@ +json.array! @user_follows, partial: "user_follows/user_follow", as: :user_follow diff --git a/app/views/user_follows/show.json.jbuilder b/app/views/user_follows/show.json.jbuilder new file mode 100644 index 0000000..6997d71 --- /dev/null +++ b/app/views/user_follows/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! "user_follows/user_follow", user_follow: @user_follow diff --git a/config/routes.rb b/config/routes.rb index 9fe51af..4a94fda 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,5 @@ Rails.application.routes.draw do + resources :user_follows, only: [:create, :destroy] resources :users, only: [:show] # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html diff --git a/db/migrate/20220227175028_create_user_follows.rb b/db/migrate/20220227175028_create_user_follows.rb new file mode 100644 index 0000000..62c4d79 --- /dev/null +++ b/db/migrate/20220227175028_create_user_follows.rb @@ -0,0 +1,10 @@ +class CreateUserFollows < ActiveRecord::Migration[7.0] + def change + create_table :user_follows do |t| + t.references :follower, references: :users, foreign_key: { to_table: :users } + t.references :followed, references: :users, foreign_key: { to_table: :users } + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index e4d1bb4..afe9957 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,10 +10,19 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2022_02_27_172543) do +ActiveRecord::Schema[7.0].define(version: 2022_02_27_175028) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "user_follows", force: :cascade do |t| + t.bigint "follower_id" + t.bigint "followed_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["followed_id"], name: "index_user_follows_on_followed_id" + t.index ["follower_id"], name: "index_user_follows_on_follower_id" + end + create_table "users", force: :cascade do |t| t.string "username", limit: 14, null: false t.datetime "created_at", null: false @@ -21,4 +30,6 @@ ActiveRecord::Schema[7.0].define(version: 2022_02_27_172543) do t.index ["username"], name: "index_users_on_username" end + add_foreign_key "user_follows", "users", column: "followed_id" + add_foreign_key "user_follows", "users", column: "follower_id" end diff --git a/db/seeds.rb b/db/seeds.rb index 83e1657..5f4f465 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,3 +1,6 @@ -User.find_or_create_by(username: 'xpto') User.find_or_create_by(username: 'admin') -User.find_or_create_by(username: 'geonizeli') \ No newline at end of file +xpto = User.find_or_create_by(username: 'xpto') +geonizeli = User.find_or_create_by(username: 'geonizeli') + +UserFollow.find_or_create_by(follower_id: xpto.id, followed_id: geonizeli.id) +UserFollow.find_or_create_by(follower_id: geonizeli.id, followed_id: xpto.id) diff --git a/spec/factories/user_follows.rb b/spec/factories/user_follows.rb new file mode 100644 index 0000000..3453211 --- /dev/null +++ b/spec/factories/user_follows.rb @@ -0,0 +1,6 @@ +FactoryBot.define do + factory :user_follow do + follower { nil } + followed { nil } + end +end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 99925c3..039a390 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -1,5 +1,5 @@ FactoryBot.define do factory :user do - username { "xpto" } + username { Faker::Internet.username.gsub(/[^0-9a-z ]/i, '') } end end diff --git a/spec/requests/user_follows_spec.rb b/spec/requests/user_follows_spec.rb new file mode 100644 index 0000000..8b7ff95 --- /dev/null +++ b/spec/requests/user_follows_spec.rb @@ -0,0 +1,67 @@ +require 'rails_helper' + +RSpec.describe "/user_follows", type: :request do + let(:followed) { create(:user) } + let(:follower) { create(:user) } + + let(:valid_attributes) { + { + followed_id: followed.id, + follower_id: follower.id + } + } + + let(:invalid_attributes) { + { + followed_id: followed.id, + follower_id: 'follower.id' + } + } + + let(:valid_headers) { + {} + } + + describe "POST /create" do + context "with valid parameters" do + it "creates a new UserFollow" do + expect { + post user_follows_url, + params: { user_follow: valid_attributes }, headers: valid_headers, as: :json + }.to change(UserFollow, :count).by(1) + end + + it "renders a JSON response with the new user_follow" do + post user_follows_url, + params: { user_follow: valid_attributes }, headers: valid_headers, as: :json + expect(response).to have_http_status(:created) + expect(response.content_type).to match(a_string_including("application/json")) + end + end + + context "with invalid parameters" do + it "does not create a new UserFollow" do + expect { + post user_follows_url, + params: { user_follow: invalid_attributes }, as: :json + }.to change(UserFollow, :count).by(0) + end + + it "renders a JSON response with errors for the new user_follow" do + post user_follows_url, + params: { user_follow: invalid_attributes }, headers: valid_headers, as: :json + expect(response).to have_http_status(:unprocessable_entity) + expect(response.content_type).to match(a_string_including("application/json")) + end + end + end + + describe "DELETE /destroy" do + it "destroys the requested user_follow" do + user_follow = UserFollow.create! valid_attributes + expect { + delete user_follow_url(user_follow), headers: valid_headers, as: :json + }.to change(UserFollow, :count).by(-1) + end + end +end diff --git a/spec/routing/user_follows_routing_spec.rb b/spec/routing/user_follows_routing_spec.rb new file mode 100644 index 0000000..bbd2846 --- /dev/null +++ b/spec/routing/user_follows_routing_spec.rb @@ -0,0 +1,13 @@ +require "rails_helper" + +RSpec.describe UserFollowsController, type: :routing do + describe "routing" do + it "routes to #create" do + expect(post: "/user_follows").to route_to("user_follows#create") + end + + it "routes to #destroy" do + expect(delete: "/user_follows/1").to route_to("user_follows#destroy", id: "1") + end + end +end