diff --git a/Gemfile b/Gemfile index cfaa680..2e9b44f 100644 --- a/Gemfile +++ b/Gemfile @@ -47,6 +47,8 @@ gem "bootsnap", require: false # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] # gem "image_processing", "~> 1.2" +gem "httpx" + group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem gem "debug", platforms: %i[ mri windows ] diff --git a/Gemfile.lock b/Gemfile.lock index 0a77b24..974a1d8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -103,6 +103,9 @@ GEM erubi (1.12.0) globalid (1.2.1) activesupport (>= 6.1) + http-2-next (1.0.1) + httpx (1.1.1) + http-2-next (>= 1.0.1) i18n (1.14.1) concurrent-ruby (~> 1.0) importmap-rails (1.2.3) @@ -254,6 +257,7 @@ DEPENDENCIES bootsnap capybara debug + httpx importmap-rails jbuilder pg (~> 1.1) diff --git a/app/controllers/quotes_controller.rb b/app/controllers/quotes_controller.rb new file mode 100644 index 0000000..beb60a8 --- /dev/null +++ b/app/controllers/quotes_controller.rb @@ -0,0 +1,6 @@ +class QuotesController < ApplicationController + def show + @quote = Quote.sample + @rating = @quote.ratings.new + end +end diff --git a/app/controllers/ratings_controller.rb b/app/controllers/ratings_controller.rb new file mode 100644 index 0000000..b569f24 --- /dev/null +++ b/app/controllers/ratings_controller.rb @@ -0,0 +1,19 @@ +class RatingsController < ApplicationController + def create + @quote = Quote.find(params[:quote_id]) + @rating = @quote.ratings.new(rating_params) + @rating.ip = request.remote_ip + + if @rating.save + redirect_to root_url + else + render "quotes/show", status: :unprocessable_entity + end + end + + private + + def rating_params + params.require(:rating).permit(:number) + end +end diff --git a/app/lib/ron_swanson_quotes.rb b/app/lib/ron_swanson_quotes.rb new file mode 100644 index 0000000..78674a1 --- /dev/null +++ b/app/lib/ron_swanson_quotes.rb @@ -0,0 +1,5 @@ +module RonSwansonQuotes + def self.get + HTTPX.get("https://ron-swanson-quotes.herokuapp.com/v2/quotes").json.first + end +end diff --git a/app/models/quote.rb b/app/models/quote.rb new file mode 100644 index 0000000..7cf294d --- /dev/null +++ b/app/models/quote.rb @@ -0,0 +1,9 @@ +class Quote < ApplicationRecord + has_many :ratings, dependent: :destroy + + validates :text, presence: true, uniqueness: true + + def self.sample + find_or_create_by!(text: RonSwansonQuotes.get) + end +end diff --git a/app/models/rating.rb b/app/models/rating.rb new file mode 100644 index 0000000..5a279a9 --- /dev/null +++ b/app/models/rating.rb @@ -0,0 +1,8 @@ +class Rating < ApplicationRecord + NUMBER_RANGE = 1..5 + + belongs_to :quote + + validates :number, presence: true, numericality: {in: NUMBER_RANGE} + validates :ip, presence: true, uniqueness: true +end diff --git a/app/views/quotes/show.html.erb b/app/views/quotes/show.html.erb new file mode 100644 index 0000000..e43ff1c --- /dev/null +++ b/app/views/quotes/show.html.erb @@ -0,0 +1,14 @@ +

+ <%= @quote.text %> +

+ +

+ Average rating: + <%= @quote.ratings.average(:number) %> +

+ +<%= form_with model: [@quote, @rating] do |f| %> + <%= f.object.errors.full_messages.to_sentence %> + <%= f.number_field :number, in: Rating::NUMBER_RANGE %> + <%= f.submit %> +<% end %> diff --git a/config/routes.rb b/config/routes.rb index a125ef0..3e1ba46 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -5,6 +5,8 @@ # Can be used by load balancers and uptime monitors to verify that the app is live. get "up" => "rails/health#show", as: :rails_health_check - # Defines the root path route ("/") - # root "posts#index" + resources :quotes, only: :show do + resources :ratings, only: :create + end + root "quotes#show" end diff --git a/db/migrate/20231109024110_initial_migration.rb b/db/migrate/20231109024110_initial_migration.rb new file mode 100644 index 0000000..acc7abd --- /dev/null +++ b/db/migrate/20231109024110_initial_migration.rb @@ -0,0 +1,15 @@ +class InitialMigration < ActiveRecord::Migration[7.1] + def change + create_table :quotes do |t| + t.text :text, null: false, index: { unique: true } + t.timestamps + end + + create_table :ratings do |t| + t.belongs_to :quote, foreign_key: true + t.integer :number, null: false + t.string :ip, null: false, index: { unique: true } + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..ce2860b --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,35 @@ +# 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. +# +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema[7.1].define(version: 2023_11_09_024110) do + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "quotes", force: :cascade do |t| + t.text "text", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["text"], name: "index_quotes_on_text", unique: true + end + + create_table "ratings", force: :cascade do |t| + t.bigint "quote_id" + t.integer "number", null: false + t.string "ip", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["ip"], name: "index_ratings_on_ip", unique: true + t.index ["quote_id"], name: "index_ratings_on_quote_id" + end + + add_foreign_key "ratings", "quotes" +end