From 15e2f2a7f1f9daeb7d4325f143e92d1948c9a7c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Sal=C3=A9?= Date: Sat, 20 Feb 2021 11:52:15 +0100 Subject: [PATCH 1/9] Create Model Subscription --- app/models/subscription.rb | 29 ++++++++++ .../20210219142026_create_subscriptions.rb | 12 +++++ db/schema.rb | 13 ++++- test/fixtures/subscriptions.yml | 5 ++ test/models/subscription_test.rb | 54 +++++++++++++++++++ 5 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 app/models/subscription.rb create mode 100644 db/migrate/20210219142026_create_subscriptions.rb create mode 100644 test/fixtures/subscriptions.yml create mode 100644 test/models/subscription_test.rb diff --git a/app/models/subscription.rb b/app/models/subscription.rb new file mode 100644 index 00000000..23a227d5 --- /dev/null +++ b/app/models/subscription.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class Subscription < ApplicationRecord + @monthly_price = 8 + @yearly_price = 80 + + belongs_to :user + + validates :duration, presence: true, numericality: { only_integer: true, greater_than: 0 } + + # Definition of class instance variables + class << self + attr_accessor :monthly_price, :yearly_price + end + + def price + return unless duration + + ((duration / 12) * Subscription.yearly_price + (duration % 12) * Subscription.monthly_price) + end + + def cancelled + !!cancelled_date + end + + def toggle_cancelled + self.cancelled_date = cancelled_date ? nil : DateTime.now + end +end diff --git a/db/migrate/20210219142026_create_subscriptions.rb b/db/migrate/20210219142026_create_subscriptions.rb new file mode 100644 index 00000000..fcf3fe5a --- /dev/null +++ b/db/migrate/20210219142026_create_subscriptions.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class CreateSubscriptions < ActiveRecord::Migration[6.0] + def change + create_table :subscriptions do |t| + t.integer :duration, null: false + t.datetime :cancelled_date + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 6ee394ac..b6fcddad 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: 2020_11_28_153354) do +ActiveRecord::Schema.define(version: 2021_02_19_142026) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -34,6 +34,15 @@ t.index ["user_id"], name: "index_machines_on_user_id" end + create_table "subscriptions", force: :cascade do |t| + t.integer "duration", null: false + t.datetime "cancelled_date" + t.datetime "created_at", precision: 6, null: false + t.datetime "updated_at", precision: 6, null: false + t.bigint "user_id", null: false + t.index ["user_id"], name: "index_subscriptions_on_user_id" + end + create_table "users", force: :cascade do |t| t.string "firstname", null: false t.string "lastname", null: false @@ -41,10 +50,12 @@ t.string "room", null: false t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false + t.datetime "date_end_subscription" t.index ["email"], name: "index_users_on_email", unique: true t.index ["room"], name: "index_users_on_room", unique: true end add_foreign_key "ips", "machines" add_foreign_key "machines", "users" + add_foreign_key "subscriptions", "users" end diff --git a/test/fixtures/subscriptions.yml b/test/fixtures/subscriptions.yml new file mode 100644 index 00000000..62dce91c --- /dev/null +++ b/test/fixtures/subscriptions.yml @@ -0,0 +1,5 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +two_months: + duration: 2 + user: ironman diff --git a/test/models/subscription_test.rb b/test/models/subscription_test.rb new file mode 100644 index 00000000..16f6c311 --- /dev/null +++ b/test/models/subscription_test.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'test_helper' + +class SubscriptionTest < ActiveSupport::TestCase + def setup + @subscription = subscriptions(:two_months) + end + + test 'subscription is valid' do + assert @subscription.valid? + end + + test "duration can't be nil" do + @subscription.duration = nil + assert_not @subscription.valid? + end + + test 'duration must be an integer' do + @subscription.duration = 3.12 + assert_not @subscription.valid? + end + + test 'duration must be strictly positive' do + @subscription.duration = -1 + assert_not @subscription.valid? + @subscription.duration = 0 + assert_not @subscription.valid? + end + + test 'price must be of the correct value' do + @subscription.duration = 4 + assert_equal 4 * Subscription.monthly_price, @subscription.price + @subscription.duration = 15 + assert_equal 1 * Subscription.yearly_price + 3 * Subscription.monthly_price, @subscription.price + end + + test 'cancelled should be false by default' do + subscription = Subscription.new(duration: 3) + assert_not subscription.cancelled + end + + test 'cancelled_date should be set with a method' do + subscription = Subscription.new(duration: 3) + assert_nil subscription.cancelled_date + time_before_cancelled = DateTime.now + subscription.toggle_cancelled + time_after_cancelled = DateTime.now + puts subscription.inspect + puts subscription.cancelled_date + assert time_before_cancelled <= subscription.cancelled_date && subscription.cancelled_date <= time_after_cancelled + assert subscription.cancelled + end +end From fe7bb5beeaa3df2d707bc0056be96d0dc841a96b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Sal=C3=A9?= Date: Sat, 20 Feb 2021 11:56:01 +0100 Subject: [PATCH 2/9] Add new and create method to Subscription controller --- app/assets/stylesheets/subscription.scss | 3 + app/controllers/subscriptions_controller.rb | 30 +++++++ app/helpers/subscription_helper.rb | 4 + app/models/user.rb | 10 +++ app/views/subscriptions/new.html.erb | 10 +++ app/views/users/show.html.erb | 1 + config/routes.rb | 1 + ...55153_add_date_end_subscription_to_user.rb | 7 ++ ...210220095330_add_subscriptions_to_users.rb | 7 ++ db/schema.rb | 2 +- .../subscriptions_controller_test.rb | 78 +++++++++++++++++++ 11 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 app/assets/stylesheets/subscription.scss create mode 100644 app/controllers/subscriptions_controller.rb create mode 100644 app/helpers/subscription_helper.rb create mode 100644 app/views/subscriptions/new.html.erb create mode 100644 db/migrate/20210219155153_add_date_end_subscription_to_user.rb create mode 100644 db/migrate/20210220095330_add_subscriptions_to_users.rb create mode 100644 test/controllers/subscriptions_controller_test.rb diff --git a/app/assets/stylesheets/subscription.scss b/app/assets/stylesheets/subscription.scss new file mode 100644 index 00000000..57657889 --- /dev/null +++ b/app/assets/stylesheets/subscription.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Subscription controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: https://sass-lang.com/ diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb new file mode 100644 index 00000000..794c7221 --- /dev/null +++ b/app/controllers/subscriptions_controller.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +class SubscriptionsController < ApplicationController + before_action :user, only: %i[new create] + def new + @subscription = @user.subscriptions.new + end + + def create + @subscription = @user.subscriptions.new(subscriptions_params) + if @subscription.save + user = User.find(params[:user_id]) + user.handle_new_date_end_subscription(@subscription.duration) + user.save + redirect_to user + else + render 'new' + end + end + + private + + def subscriptions_params + params.require(:subscription).permit(:duration) + end + + def user + @user = User.find(params[:user_id]) + end +end diff --git a/app/helpers/subscription_helper.rb b/app/helpers/subscription_helper.rb new file mode 100644 index 00000000..e5722d64 --- /dev/null +++ b/app/helpers/subscription_helper.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +module SubscriptionHelper +end diff --git a/app/models/user.rb b/app/models/user.rb index 473c1fc8..76ea4d0b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,6 +2,7 @@ class User < ApplicationRecord has_many :machines, dependent: :destroy + has_many :subscriptions, dependent: :destroy before_save :downcase_email before_save :format_room @@ -13,6 +14,15 @@ class User < ApplicationRecord VALID_ROOM_REGEX = /\A([A-F][0-3][0-9]{2}[a-b]?|DF[1-4])\z/i.freeze validates :room, presence: true, format: { with: VALID_ROOM_REGEX }, uniqueness: true + def handle_new_date_end_subscription(duration) + time_now = DateTime.now + self.date_end_subscription = if date_end_subscription && (date_end_subscription > time_now) + date_end_subscription + duration.months + else + time_now + duration.months + end + end + private def downcase_email diff --git a/app/views/subscriptions/new.html.erb b/app/views/subscriptions/new.html.erb new file mode 100644 index 00000000..3db219ee --- /dev/null +++ b/app/views/subscriptions/new.html.erb @@ -0,0 +1,10 @@ +<% provide :button_text, "Create" %> + +

Subscription#new

+ +<%= form_with(model: [@user, @subscription], local: true) do |f| %> + <%= render 'utils/error_messages', object: f.object %> + <%= f.label :duration %> + <%= f.text_field :duration, required: true %> + <%= f.submit yield(:button_text) %> +<% end %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index f30c7349..115e83a5 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -4,6 +4,7 @@ <%= @user.lastname %>, <%= @user.email %>, <%= @user.room %>, + <%= @user.date_end_subscription %>

Machines

<%= link_to "create a new machine", new_user_machine_url(@user) %> <%= render @user.machines %> diff --git a/config/routes.rb b/config/routes.rb index 34b2cf99..3808c59a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,6 +3,7 @@ Rails.application.routes.draw do resources :users do resources :machines, shallow: true + resources :subscriptions, only: %i[new create edit] end # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html diff --git a/db/migrate/20210219155153_add_date_end_subscription_to_user.rb b/db/migrate/20210219155153_add_date_end_subscription_to_user.rb new file mode 100644 index 00000000..de929c7d --- /dev/null +++ b/db/migrate/20210219155153_add_date_end_subscription_to_user.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddDateEndSubscriptionToUser < ActiveRecord::Migration[6.0] + def change + add_column :users, :date_end_subscription, :datetime + end +end diff --git a/db/migrate/20210220095330_add_subscriptions_to_users.rb b/db/migrate/20210220095330_add_subscriptions_to_users.rb new file mode 100644 index 00000000..f76fc4e5 --- /dev/null +++ b/db/migrate/20210220095330_add_subscriptions_to_users.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddSubscriptionsToUsers < ActiveRecord::Migration[6.0] + def change + add_reference :subscriptions, :user, foreign_key: true, null: false # rubocop:disable Rails/NotNullColumn + end +end diff --git a/db/schema.rb b/db/schema.rb index b6fcddad..f757000c 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: 2021_02_19_142026) do +ActiveRecord::Schema.define(version: 2021_02_20_095330) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/test/controllers/subscriptions_controller_test.rb b/test/controllers/subscriptions_controller_test.rb new file mode 100644 index 00000000..6c48559b --- /dev/null +++ b/test/controllers/subscriptions_controller_test.rb @@ -0,0 +1,78 @@ +# frozen_string_literal: true + +require 'test_helper' + +class SubscriptionsControllerTest < ActionDispatch::IntegrationTest + def setup + @subscription = subscriptions(:two_months) + @user = @subscription.user + end + + test 'should render new' do + get new_user_subscription_path(@user) + assert_template 'subscriptions/new' + end + + test 'should create a subscription and redirect to user' do + assert_difference 'Subscription.count', 1 do + post user_subscriptions_url(@user), params: { + subscription: { + duration: 5 + } + } + end + assert_redirected_to @user + end + + test 'should increment user subscription date when internet is still on' do + date_end_subscription = DateTime.now + 3.months + @user.date_end_subscription = date_end_subscription + @user.save + post user_subscriptions_url(@user), params: { + subscription: { + duration: 5 + } + } + @user.reload + assert_equal (date_end_subscription + 5.months).localtime.round, @user.date_end_subscription.localtime.round + end + + test 'should set user subscription date when internet is off' do + @user.date_end_subscription = DateTime.now - 3.months + @user.save + time_before_subscription = DateTime.now + post user_subscriptions_url(@user), params: { + subscription: { + duration: 5 + } + } + @user.reload + time_after_subscription = DateTime.now + assert (time_before_subscription + 5.months).localtime.round <= @user.date_end_subscription.localtime.round + assert (time_after_subscription + 5.months).localtime.round >= @user.date_end_subscription.localtime.round + end + + test 'should set user subscription date when internet was never given' do + @user.date_end_subscription = nil + @user.save + time_before_subscription = DateTime.now + post user_subscriptions_url(@user), params: { + subscription: { + duration: 5 + } + } + @user.reload + time_after_subscription = DateTime.now + assert (time_before_subscription + 5.months).localtime.round <= @user.date_end_subscription.localtime.round + assert (time_after_subscription + 5.months).localtime.round >= @user.date_end_subscription.localtime.round + end + + test 'should re-render new if subscription is invalid' do + post user_subscriptions_url(@user), params: { + subscription: { + duration: -1 + } + } + assert_template 'subscriptions/new' + end +end From b164d335117e7ff2348f0de19f215763b1369d96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Sal=C3=A9?= Date: Sat, 20 Feb 2021 13:53:09 +0100 Subject: [PATCH 3/9] Add destroy method to Subscription controller --- app/controllers/subscriptions_controller.rb | 16 ++++++++- app/views/users/show.html.erb | 1 + config/routes.rb | 1 + .../subscriptions_controller_test.rb | 35 +++++++++++++++++++ test/fixtures/users.yml | 1 + 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index 794c7221..6776dba1 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true class SubscriptionsController < ApplicationController - before_action :user, only: %i[new create] + before_action :user, only: %i[new create destroy] + def new @subscription = @user.subscriptions.new end @@ -18,6 +19,19 @@ def create end end + def destroy + if @user.subscriptions.count > 1 + last_subscription = @user.subscriptions.last + @user.handle_new_date_end_subscription(-last_subscription.duration) + last_subscription.destroy! + elsif @user.subscriptions.count == 1 + @user.subscriptions.last.destroy! + @user.date_end_subscription = nil + end + @user.save + redirect_to @user + end + private def subscriptions_params diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 115e83a5..f86f4c80 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -5,6 +5,7 @@ <%= @user.email %>, <%= @user.room %>, <%= @user.date_end_subscription %> + <%= link_to "delete last subscription",user_delete_subscription_url(@user) ,method: :delete %>

Machines

<%= link_to "create a new machine", new_user_machine_url(@user) %> <%= render @user.machines %> diff --git a/config/routes.rb b/config/routes.rb index 3808c59a..e32da46b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,6 +4,7 @@ resources :users do resources :machines, shallow: true resources :subscriptions, only: %i[new create edit] + delete '/subscriptions', to: 'subscriptions#destroy', as: :delete_subscription end # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html diff --git a/test/controllers/subscriptions_controller_test.rb b/test/controllers/subscriptions_controller_test.rb index 6c48559b..f2caa226 100644 --- a/test/controllers/subscriptions_controller_test.rb +++ b/test/controllers/subscriptions_controller_test.rb @@ -75,4 +75,39 @@ def setup } assert_template 'subscriptions/new' end + + test 'should destroy the last subscription and redirect to user' do + last_subscription = @user.subscriptions.last + assert_difference 'Subscription.count', -1 do + delete user_delete_subscription_url(@user) + end + @user.reload + assert_not_equal last_subscription, @user.subscriptions.last + end + + test 'should update user subscription date on delete' do + sub = @user.subscriptions.new(duration: 7) + sub.save + + @user.handle_new_date_end_subscription(sub.duration) + subscription_date_before_deletion = @user.date_end_subscription + @user.save + + delete user_delete_subscription_url(@user) + + @user.reload + assert_equal (subscription_date_before_deletion - 7.months).utc.round, + @user.date_end_subscription.utc.round + end + + test 'should set date end subscription to nil if the last subscriptions is deleted' do + assert_equal 1, @user.subscriptions.count + + assert_not_nil @user.date_end_subscription + + delete user_delete_subscription_url(@user) + + @user.reload + assert_nil @user.date_end_subscription + end end diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml index 8ab4a9cb..d6317189 100644 --- a/test/fixtures/users.yml +++ b/test/fixtures/users.yml @@ -5,4 +5,5 @@ ironman: lastname: "Stark" email: "tony@avengers.com" room: "A200" + date_end_subscription: <%= DateTime.now + 2.months%> From 3d6b807710af53727e63a71317847cb8ac6444f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Sal=C3=A9?= Date: Sat, 20 Feb 2021 15:17:59 +0100 Subject: [PATCH 4/9] Add edit and update method to Subscription controller --- app/controllers/subscriptions_controller.rb | 36 ++++++++-- .../subscriptions/_subscription.html.erb | 6 ++ app/views/subscriptions/edit.html.erb | 10 +++ app/views/users/show.html.erb | 10 ++- config/routes.rb | 6 +- .../subscriptions_controller_test.rb | 70 ++++++++++++++++++- 6 files changed, 126 insertions(+), 12 deletions(-) create mode 100644 app/views/subscriptions/_subscription.html.erb create mode 100644 app/views/subscriptions/edit.html.erb diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index 6776dba1..968c8a89 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true class SubscriptionsController < ApplicationController - before_action :user, only: %i[new create destroy] + before_action :user, only: %i[new create edit update destroy] + before_action :last_subscription, only: %i[edit update destroy] def new @subscription = @user.subscriptions.new @@ -19,14 +20,30 @@ def create end end + def edit + @subscription = @last_subscription + end + + def update + @subscription = @user.subscriptions.new(subscriptions_params) + if @subscription.save + @last_subscription.toggle_cancelled + @last_subscription.save + @user.handle_new_date_end_subscription(@subscription.duration - @last_subscription.duration) + @user.save + redirect_to @user + else + render 'edit' + end + end + def destroy if @user.subscriptions.count > 1 - last_subscription = @user.subscriptions.last - @user.handle_new_date_end_subscription(-last_subscription.duration) - last_subscription.destroy! + @user.handle_new_date_end_subscription(-@last_subscription.duration) + @last_subscription.destroy! elsif @user.subscriptions.count == 1 - @user.subscriptions.last.destroy! @user.date_end_subscription = nil + @last_subscription.destroy! end @user.save redirect_to @user @@ -41,4 +58,13 @@ def subscriptions_params def user @user = User.find(params[:user_id]) end + + def current_subscription + @subscription = Subscription.find(params[:id]) + end + + def last_subscription + @last_subscription = @user.subscriptions.last + redirect_to @user unless @last_subscription + end end diff --git a/app/views/subscriptions/_subscription.html.erb b/app/views/subscriptions/_subscription.html.erb new file mode 100644 index 00000000..cefa9c3a --- /dev/null +++ b/app/views/subscriptions/_subscription.html.erb @@ -0,0 +1,6 @@ +

+ <%= subscription.id %>, + <%= subscription.duration %>, + <%= subscription.updated_at %>, + <%= subscription.cancelled ? "cancelled" : nil %> +

diff --git a/app/views/subscriptions/edit.html.erb b/app/views/subscriptions/edit.html.erb new file mode 100644 index 00000000..54b90c42 --- /dev/null +++ b/app/views/subscriptions/edit.html.erb @@ -0,0 +1,10 @@ +<% provide :button_text, "Edit" %> + +

Subscriptions#Edit

+ +<%= form_with(model: [@user, @subscription],url: user_edit_subscription_url ,local: true) do |f| %> + <%= render 'utils/error_messages', object: f.object %> + <%= f.label :duration %> + <%= f.text_field :duration, required: true %> + <%= f.submit yield(:button_text) %> +<% end %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index f86f4c80..87f2b2c2 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -5,8 +5,14 @@ <%= @user.email %>, <%= @user.room %>, <%= @user.date_end_subscription %> - <%= link_to "delete last subscription",user_delete_subscription_url(@user) ,method: :delete %> +

+<%= link_to "edit last subscription",user_edit_subscription_url(@user) %> +<%= link_to "delete last subscription",user_subscription_url(@user) ,method: :delete %>

Machines

<%= link_to "create a new machine", new_user_machine_url(@user) %> <%= render @user.machines %> -

+ +

Subscriptions

+ <%= link_to "add a new subscription", new_user_subscription_url(@user) %> + <%= render @user.subscriptions %> + diff --git a/config/routes.rb b/config/routes.rb index e32da46b..68b728b2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,8 +3,10 @@ Rails.application.routes.draw do resources :users do resources :machines, shallow: true - resources :subscriptions, only: %i[new create edit] - delete '/subscriptions', to: 'subscriptions#destroy', as: :delete_subscription + resources :subscriptions, only: %i[new create], shallow: true + get '/subscriptions/edit', to: 'subscriptions#edit', as: :edit_subscription + patch '/subscriptions', to: 'subscriptions#update', as: :subscription + delete '/subscriptions', to: 'subscriptions#destroy' end # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html diff --git a/test/controllers/subscriptions_controller_test.rb b/test/controllers/subscriptions_controller_test.rb index f2caa226..2a2108dd 100644 --- a/test/controllers/subscriptions_controller_test.rb +++ b/test/controllers/subscriptions_controller_test.rb @@ -79,7 +79,7 @@ def setup test 'should destroy the last subscription and redirect to user' do last_subscription = @user.subscriptions.last assert_difference 'Subscription.count', -1 do - delete user_delete_subscription_url(@user) + delete user_subscription_url(@user) end @user.reload assert_not_equal last_subscription, @user.subscriptions.last @@ -93,7 +93,7 @@ def setup subscription_date_before_deletion = @user.date_end_subscription @user.save - delete user_delete_subscription_url(@user) + delete user_subscription_url(@user) @user.reload assert_equal (subscription_date_before_deletion - 7.months).utc.round, @@ -105,9 +105,73 @@ def setup assert_not_nil @user.date_end_subscription - delete user_delete_subscription_url(@user) + delete user_subscription_url(@user) @user.reload assert_nil @user.date_end_subscription end + + test 'should render edit' do + get user_edit_subscription_url(@user) + assert_template 'subscriptions/edit' + end + + test 'should redirect if updates are valid' do + patch user_subscription_url(@user), params: { + subscription: { + duration: 8 + } + } + assert_redirected_to @user + end + + test 'should cancelled last subscription and add a new one' do + last_subscription = @user.subscriptions.last + patch user_subscription_url(@user), params: { + subscription: { + duration: 8 + } + } + last_subscription.reload + assert last_subscription.cancelled + end + + test 'should update subscription end date on edit' do + @user.subscriptions.new(duration: 9) + date_end_subscription_before_edit = DateTime.now + 9.months + @user.date_end_subscription = date_end_subscription_before_edit + @user.save + + patch user_subscription_url(@user), params: { + subscription: { + duration: 6 + } + } + + @user.reload + assert_equal (date_end_subscription_before_edit - 3.months).utc.round, + @user.date_end_subscription.utc.round + end + + test "can't edit when no subscriptions" do + @user.subscriptions.destroy_all + @user.save + + patch user_subscription_url(@user), params: { + subscription: { + duration: 6 + } + } + + assert_redirected_to @user + end + + test 'should re-render edit if updates are invalid' do + patch user_subscription_url(@user), params: { + subscription: { + duration: -1 + } + } + assert_template 'subscriptions/edit' + end end From b2424969bf19e0150e9b2cd15878a162bf89dba6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Sal=C3=A9?= Date: Sat, 20 Feb 2021 15:29:03 +0100 Subject: [PATCH 5/9] Add index method to Subscription controller --- app/controllers/subscriptions_controller.rb | 4 ++++ app/views/subscriptions/_subscription.html.erb | 3 ++- app/views/subscriptions/index.html.erb | 5 +++++ config/routes.rb | 2 ++ test/controllers/subscriptions_controller_test.rb | 5 +++++ 5 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 app/views/subscriptions/index.html.erb diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index 968c8a89..2740f262 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -4,6 +4,10 @@ class SubscriptionsController < ApplicationController before_action :user, only: %i[new create edit update destroy] before_action :last_subscription, only: %i[edit update destroy] + def index + @subscriptions = Subscription.all + end + def new @subscription = @user.subscriptions.new end diff --git a/app/views/subscriptions/_subscription.html.erb b/app/views/subscriptions/_subscription.html.erb index cefa9c3a..10063548 100644 --- a/app/views/subscriptions/_subscription.html.erb +++ b/app/views/subscriptions/_subscription.html.erb @@ -1,6 +1,7 @@

<%= subscription.id %>, - <%= subscription.duration %>, + <%= pluralize(subscription.duration, 'month') %>, + <%= subscription.price %> €, <%= subscription.updated_at %>, <%= subscription.cancelled ? "cancelled" : nil %>

diff --git a/app/views/subscriptions/index.html.erb b/app/views/subscriptions/index.html.erb new file mode 100644 index 00000000..e8faa69a --- /dev/null +++ b/app/views/subscriptions/index.html.erb @@ -0,0 +1,5 @@ +

Subscriptions#index

+ +
    + <%= render @subscriptions %> +
diff --git a/config/routes.rb b/config/routes.rb index 68b728b2..129c6524 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -9,5 +9,7 @@ delete '/subscriptions', to: 'subscriptions#destroy' end + get '/subscriptions', to: 'subscriptions#index', as: :subscriptions + # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html end diff --git a/test/controllers/subscriptions_controller_test.rb b/test/controllers/subscriptions_controller_test.rb index 2a2108dd..5c45b9f3 100644 --- a/test/controllers/subscriptions_controller_test.rb +++ b/test/controllers/subscriptions_controller_test.rb @@ -8,6 +8,11 @@ def setup @user = @subscription.user end + test 'should render index' do + get subscriptions_url + assert_template 'subscriptions/index' + end + test 'should render new' do get new_user_subscription_path(@user) assert_template 'subscriptions/new' From 83f957219aa5b0cf7388151acdc1e070bd56271c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Sal=C3=A9?= Date: Sat, 20 Feb 2021 15:44:41 +0100 Subject: [PATCH 6/9] Do some refactor --- app/controllers/subscriptions_controller.rb | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index 2740f262..50e6c14d 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -44,11 +44,10 @@ def update def destroy if @user.subscriptions.count > 1 @user.handle_new_date_end_subscription(-@last_subscription.duration) - @last_subscription.destroy! - elsif @user.subscriptions.count == 1 + else @user.date_end_subscription = nil - @last_subscription.destroy! end + @last_subscription.destroy! @user.save redirect_to @user end @@ -63,10 +62,6 @@ def user @user = User.find(params[:user_id]) end - def current_subscription - @subscription = Subscription.find(params[:id]) - end - def last_subscription @last_subscription = @user.subscriptions.last redirect_to @user unless @last_subscription From e85cf92576f9f936af811a326a4c37bdf9a62296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Sal=C3=A9?= Date: Sun, 21 Feb 2021 16:01:47 +0100 Subject: [PATCH 7/9] Remove edit option from subscriptions --- app/controllers/subscriptions_controller.rb | 21 +----- app/views/subscriptions/edit.html.erb | 10 --- app/views/users/show.html.erb | 1 - config/routes.rb | 4 +- .../subscriptions_controller_test.rb | 64 ------------------- 5 files changed, 3 insertions(+), 97 deletions(-) delete mode 100644 app/views/subscriptions/edit.html.erb diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index 50e6c14d..edaa153a 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true class SubscriptionsController < ApplicationController - before_action :user, only: %i[new create edit update destroy] - before_action :last_subscription, only: %i[edit update destroy] + before_action :user, only: %i[new create destroy] + before_action :last_subscription, only: %i[destroy] def index @subscriptions = Subscription.all @@ -24,23 +24,6 @@ def create end end - def edit - @subscription = @last_subscription - end - - def update - @subscription = @user.subscriptions.new(subscriptions_params) - if @subscription.save - @last_subscription.toggle_cancelled - @last_subscription.save - @user.handle_new_date_end_subscription(@subscription.duration - @last_subscription.duration) - @user.save - redirect_to @user - else - render 'edit' - end - end - def destroy if @user.subscriptions.count > 1 @user.handle_new_date_end_subscription(-@last_subscription.duration) diff --git a/app/views/subscriptions/edit.html.erb b/app/views/subscriptions/edit.html.erb deleted file mode 100644 index 54b90c42..00000000 --- a/app/views/subscriptions/edit.html.erb +++ /dev/null @@ -1,10 +0,0 @@ -<% provide :button_text, "Edit" %> - -

Subscriptions#Edit

- -<%= form_with(model: [@user, @subscription],url: user_edit_subscription_url ,local: true) do |f| %> - <%= render 'utils/error_messages', object: f.object %> - <%= f.label :duration %> - <%= f.text_field :duration, required: true %> - <%= f.submit yield(:button_text) %> -<% end %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 87f2b2c2..0fa60eb5 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -6,7 +6,6 @@ <%= @user.room %>, <%= @user.date_end_subscription %>

-<%= link_to "edit last subscription",user_edit_subscription_url(@user) %> <%= link_to "delete last subscription",user_subscription_url(@user) ,method: :delete %>

Machines

<%= link_to "create a new machine", new_user_machine_url(@user) %> diff --git a/config/routes.rb b/config/routes.rb index 129c6524..e03599f7 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,9 +4,7 @@ resources :users do resources :machines, shallow: true resources :subscriptions, only: %i[new create], shallow: true - get '/subscriptions/edit', to: 'subscriptions#edit', as: :edit_subscription - patch '/subscriptions', to: 'subscriptions#update', as: :subscription - delete '/subscriptions', to: 'subscriptions#destroy' + delete '/subscriptions', to: 'subscriptions#destroy', as: :subscription end get '/subscriptions', to: 'subscriptions#index', as: :subscriptions diff --git a/test/controllers/subscriptions_controller_test.rb b/test/controllers/subscriptions_controller_test.rb index 5c45b9f3..d56f075a 100644 --- a/test/controllers/subscriptions_controller_test.rb +++ b/test/controllers/subscriptions_controller_test.rb @@ -115,68 +115,4 @@ def setup @user.reload assert_nil @user.date_end_subscription end - - test 'should render edit' do - get user_edit_subscription_url(@user) - assert_template 'subscriptions/edit' - end - - test 'should redirect if updates are valid' do - patch user_subscription_url(@user), params: { - subscription: { - duration: 8 - } - } - assert_redirected_to @user - end - - test 'should cancelled last subscription and add a new one' do - last_subscription = @user.subscriptions.last - patch user_subscription_url(@user), params: { - subscription: { - duration: 8 - } - } - last_subscription.reload - assert last_subscription.cancelled - end - - test 'should update subscription end date on edit' do - @user.subscriptions.new(duration: 9) - date_end_subscription_before_edit = DateTime.now + 9.months - @user.date_end_subscription = date_end_subscription_before_edit - @user.save - - patch user_subscription_url(@user), params: { - subscription: { - duration: 6 - } - } - - @user.reload - assert_equal (date_end_subscription_before_edit - 3.months).utc.round, - @user.date_end_subscription.utc.round - end - - test "can't edit when no subscriptions" do - @user.subscriptions.destroy_all - @user.save - - patch user_subscription_url(@user), params: { - subscription: { - duration: 6 - } - } - - assert_redirected_to @user - end - - test 'should re-render edit if updates are invalid' do - patch user_subscription_url(@user), params: { - subscription: { - duration: -1 - } - } - assert_template 'subscriptions/edit' - end end From f0bb1a4e821168462553ec63f783d3631ff73c89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Sal=C3=A9?= Date: Sun, 21 Feb 2021 16:07:37 +0100 Subject: [PATCH 8/9] Destroy controller now cancel subscription instead of delete it --- app/controllers/subscriptions_controller.rb | 3 ++- test/controllers/subscriptions_controller_test.rb | 11 +++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index edaa153a..bb511597 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -30,7 +30,8 @@ def destroy else @user.date_end_subscription = nil end - @last_subscription.destroy! + @last_subscription.toggle_cancelled + @last_subscription.save @user.save redirect_to @user end diff --git a/test/controllers/subscriptions_controller_test.rb b/test/controllers/subscriptions_controller_test.rb index d56f075a..cbe24ece 100644 --- a/test/controllers/subscriptions_controller_test.rb +++ b/test/controllers/subscriptions_controller_test.rb @@ -81,16 +81,15 @@ def setup assert_template 'subscriptions/new' end - test 'should destroy the last subscription and redirect to user' do - last_subscription = @user.subscriptions.last - assert_difference 'Subscription.count', -1 do + test 'should set the last subscription as cancelled and redirect to user' do + assert_difference 'Subscription.count', 0 do delete user_subscription_url(@user) end @user.reload - assert_not_equal last_subscription, @user.subscriptions.last + assert @user.subscriptions.last.cancelled end - test 'should update user subscription date on delete' do + test 'should update user subscription date when last subscription is cancelled' do sub = @user.subscriptions.new(duration: 7) sub.save @@ -105,7 +104,7 @@ def setup @user.date_end_subscription.utc.round end - test 'should set date end subscription to nil if the last subscriptions is deleted' do + test 'should set date end subscription to nil if the last subscriptions is cancelled' do assert_equal 1, @user.subscriptions.count assert_not_nil @user.date_end_subscription From 7445349412b29fa92d785f47e895f823d81e2db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Sal=C3=A9?= Date: Sun, 21 Feb 2021 19:45:20 +0100 Subject: [PATCH 9/9] Add json format handler to Subscription controller --- app/controllers/subscriptions_controller.rb | 42 +++++----- app/models/subscription.rb | 2 + app/models/user.rb | 20 +++++ .../subscriptions/_subscription.json.jbuilder | 4 + app/views/subscriptions/show.json.jbuilder | 4 + app/views/users/_user.json.jbuilder | 2 +- .../subscriptions_controller_test.rb | 80 +++++++++++++++++-- test/controllers/users_controller_test.rb | 19 ++++- 8 files changed, 142 insertions(+), 31 deletions(-) create mode 100644 app/views/subscriptions/_subscription.json.jbuilder create mode 100644 app/views/subscriptions/show.json.jbuilder diff --git a/app/controllers/subscriptions_controller.rb b/app/controllers/subscriptions_controller.rb index bb511597..c1d46d88 100644 --- a/app/controllers/subscriptions_controller.rb +++ b/app/controllers/subscriptions_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class SubscriptionsController < ApplicationController + protect_from_forgery unless: -> { request.format.json? } + before_action :user, only: %i[new create destroy] before_action :last_subscription, only: %i[destroy] @@ -13,32 +15,29 @@ def new end def create - @subscription = @user.subscriptions.new(subscriptions_params) - if @subscription.save - user = User.find(params[:user_id]) - user.handle_new_date_end_subscription(@subscription.duration) - user.save - redirect_to user - else - render 'new' + @subscription = @user.add_subscription(subscription_params) + respond_to do |format| + if @subscription.save + format.html { redirect_to @user } + format.json { render 'show', status: :created } + else + format.html { render 'new' } + format.json { render json: @subscription.errors, status: :unprocessable_entity } + end end end def destroy - if @user.subscriptions.count > 1 - @user.handle_new_date_end_subscription(-@last_subscription.duration) - else - @user.date_end_subscription = nil + @user.cancel_subscription(@last_subscription) + respond_to do |format| + format.html { redirect_to @user } + format.json { head :no_content } end - @last_subscription.toggle_cancelled - @last_subscription.save - @user.save - redirect_to @user end private - def subscriptions_params + def subscription_params params.require(:subscription).permit(:duration) end @@ -47,7 +46,12 @@ def user end def last_subscription - @last_subscription = @user.subscriptions.last - redirect_to @user unless @last_subscription + @last_subscription = @user.subscriptions.not_cancelled.last + return if @last_subscription + + respond_to do |format| + format.html { redirect_to @user } + format.json { render json: { error: 'There is no subscription' }, status: :unprocessable_entity } + end end end diff --git a/app/models/subscription.rb b/app/models/subscription.rb index 23a227d5..042fdf65 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -4,6 +4,8 @@ class Subscription < ApplicationRecord @monthly_price = 8 @yearly_price = 80 + scope :not_cancelled, -> { where(cancelled_date: nil) } + belongs_to :user validates :duration, presence: true, numericality: { only_integer: true, greater_than: 0 } diff --git a/app/models/user.rb b/app/models/user.rb index 76ea4d0b..6bf79fca 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -23,6 +23,26 @@ def handle_new_date_end_subscription(duration) end end + def add_subscription(subscription_params) + subscription = subscriptions.new(subscription_params) + if subscription.valid? + handle_new_date_end_subscription(subscription.duration) + save + end + subscription + end + + def cancel_subscription(last_subscription) + if subscriptions.count > 1 + handle_new_date_end_subscription(-last_subscription.duration) + else + self.date_end_subscription = nil + end + last_subscription.toggle_cancelled + last_subscription.save + save + end + private def downcase_email diff --git a/app/views/subscriptions/_subscription.json.jbuilder b/app/views/subscriptions/_subscription.json.jbuilder new file mode 100644 index 00000000..25bbf76b --- /dev/null +++ b/app/views/subscriptions/_subscription.json.jbuilder @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +json.extract! subscription, :id, :duration, :price, :cancelled, :cancelled_date, :created_at, :updated_at +# json.url subscription_url(subscription, format: :json) diff --git a/app/views/subscriptions/show.json.jbuilder b/app/views/subscriptions/show.json.jbuilder new file mode 100644 index 00000000..5f765e94 --- /dev/null +++ b/app/views/subscriptions/show.json.jbuilder @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +json.partial! 'subscriptions/subscription', subscription: @subscription +json.user @subscription.user, partial: 'users/user', as: :user diff --git a/app/views/users/_user.json.jbuilder b/app/views/users/_user.json.jbuilder index 5ddb6edd..a89df6bc 100644 --- a/app/views/users/_user.json.jbuilder +++ b/app/views/users/_user.json.jbuilder @@ -1,4 +1,4 @@ # frozen_string_literal: true -json.extract! user, :id, :firstname, :lastname, :email, :room, :created_at, :updated_at +json.extract! user, :id, :firstname, :lastname, :date_end_subscription, :email, :room, :created_at, :updated_at json.url user_url(user, format: :json) diff --git a/test/controllers/subscriptions_controller_test.rb b/test/controllers/subscriptions_controller_test.rb index cbe24ece..7ff9d411 100644 --- a/test/controllers/subscriptions_controller_test.rb +++ b/test/controllers/subscriptions_controller_test.rb @@ -18,9 +18,9 @@ def setup assert_template 'subscriptions/new' end - test 'should create a subscription and redirect to user' do + test 'should create a subscription and redirect to user with html' do assert_difference 'Subscription.count', 1 do - post user_subscriptions_url(@user), params: { + post user_subscriptions_url(@user, format: :html), params: { subscription: { duration: 5 } @@ -29,6 +29,23 @@ def setup assert_redirected_to @user end + test 'should create a subscription and redirect to user with json' do + assert_difference 'Subscription.count', 1 do + post user_subscriptions_url(@user, format: :json), params: { + subscription: { + duration: 5 + } + } + end + subscription = @response.parsed_body + assert_template('subscriptions/show') + assert_response(:created) + assert_equal 5, subscription['duration'] + assert_equal 40, subscription['price'] + assert_not subscription['cancelled'] + assert_equal @user.id, subscription['user']['id'] + end + test 'should increment user subscription date when internet is still on' do date_end_subscription = DateTime.now + 3.months @user.date_end_subscription = date_end_subscription @@ -72,8 +89,8 @@ def setup assert (time_after_subscription + 5.months).localtime.round >= @user.date_end_subscription.localtime.round end - test 'should re-render new if subscription is invalid' do - post user_subscriptions_url(@user), params: { + test 'should re-render new if subscription is invalid with html' do + post user_subscriptions_url(@user, format: :html), params: { subscription: { duration: -1 } @@ -81,12 +98,31 @@ def setup assert_template 'subscriptions/new' end - test 'should set the last subscription as cancelled and redirect to user' do + test 'should send errors if subscription is invalid with json' do + post user_subscriptions_url(@user, format: :json), params: { + subscription: { + duration: -1 + } + } + assert_response(:unprocessable_entity) + end + + test 'should set the last subscription as cancelled and redirect to user with html' do assert_difference 'Subscription.count', 0 do - delete user_subscription_url(@user) + delete user_subscription_url(@user, format: :html) end @user.reload assert @user.subscriptions.last.cancelled + assert_redirected_to @user + end + + test 'should set the last subscription as cancelled and send a 204 with json' do + assert_difference 'Subscription.count', 0 do + delete user_subscription_url(@user, format: :json) + end + @user.reload + assert @user.subscriptions.last.cancelled + assert_response :no_content end test 'should update user subscription date when last subscription is cancelled' do @@ -104,7 +140,7 @@ def setup @user.date_end_subscription.utc.round end - test 'should set date end subscription to nil if the last subscriptions is cancelled' do + test 'should set date end subscription to nil if the last subscription is cancelled' do assert_equal 1, @user.subscriptions.count assert_not_nil @user.date_end_subscription @@ -114,4 +150,34 @@ def setup @user.reload assert_nil @user.date_end_subscription end + + test 'should cancel the last not cancelled subscription' do + @user.subscriptions.new(duration: 6) + @user.subscriptions.new(duration: 4) + @user.save + + delete user_subscription_url(@user) + delete user_subscription_url(@user) + @user.reload + + last_subscription = @user.subscriptions.last + second_to_last_subscription = @user.subscriptions.second_to_last + assert last_subscription.cancelled + assert second_to_last_subscription.cancelled + end + + test 'should redirect to users if there is no subscription to cancel with html' do + @user.subscriptions.destroy_all + assert_nil @user.subscriptions.last + delete user_subscription_url(@user, format: :html) + assert_redirected_to @user + end + + test 'should send an error if there is no subscription to cancel with json' do + @user.subscriptions.destroy_all + assert_nil @user.subscriptions.last + delete user_subscription_url(@user, format: :json) + assert_response :unprocessable_entity + assert_not_nil @response.parsed_body['error'] + end end diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb index 954b79fd..11b57e8b 100644 --- a/test/controllers/users_controller_test.rb +++ b/test/controllers/users_controller_test.rb @@ -7,20 +7,30 @@ def setup @user = users(:ironman) end - # TODO: when another template index has been made, try to render it and test assert_template 'index' - # to see if it only check a template index or check that it is the users' index template test 'should get index' do get users_path assert_template 'users/index' end - test 'should get show' do - get user_path @user + test 'should get show with html' do + get user_path(@user, format: :html) assert_template 'users/show' assert_match @user.email, @response.body assert_match @user.room, @response.body end + test 'should get show with json' do + get user_path(@user, format: :json) + user = @response.parsed_body + assert_template('users/show') + assert_response(:ok) + assert_equal 'Tony', user['firstname'] + assert_equal 'Stark', user['lastname'] + assert_not_nil user['date_end_subscription'] + assert_equal 'tony@avengers.com', user['email'] + assert_equal 'A200', user['room'] + end + test 'should get new' do get new_user_path assert_template 'users/new' @@ -57,6 +67,7 @@ def setup assert_response(:created) assert_equal 'patrick', user['firstname'] assert_equal 'bar', user['lastname'] + assert_nil user['date_end_subscription'] assert_equal 'patrickbar@bar.com', user['email'] assert_equal 'E125', user['room'] end