From 4591a6239c2cb2e22cffe27224a3571bf34b98b5 Mon Sep 17 00:00:00 2001 From: Jon Allured Date: Mon, 4 Mar 2024 23:33:08 -0600 Subject: [PATCH 1/2] Visitor creates crank user --- app/controllers/crank_users_controller.rb | 15 ++++ app/models/crank_user.rb | 19 +++++ app/views/crank_users/new.html.haml | 6 ++ app/views/crank_users/show.html.haml | 3 + config/routes.rb | 2 + .../20240305034656_create_crank_users.rb | 10 +++ spec/factories/crank_user.rb | 5 ++ spec/models/crank_user_spec.rb | 78 +++++++++++++++++++ .../visitor_creates_crank_user_spec.rb | 12 +++ .../admin_views_model_counts_spec.rb | 5 +- 10 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 app/controllers/crank_users_controller.rb create mode 100644 app/models/crank_user.rb create mode 100644 app/views/crank_users/new.html.haml create mode 100644 app/views/crank_users/show.html.haml create mode 100644 db/migrate/20240305034656_create_crank_users.rb create mode 100644 spec/factories/crank_user.rb create mode 100644 spec/models/crank_user_spec.rb create mode 100644 spec/system/crank_users/visitor_creates_crank_user_spec.rb diff --git a/app/controllers/crank_users_controller.rb b/app/controllers/crank_users_controller.rb new file mode 100644 index 0000000..0b03b27 --- /dev/null +++ b/app/controllers/crank_users_controller.rb @@ -0,0 +1,15 @@ +class CrankUsersController < ApplicationController + skip_before_action :ensure_admin + + expose(:crank_user, build: -> { CrankUser.new_with_code }, find_by: :code, id: :code) + + def create + if crank_user.save + flash.notice = "Crank User created" + redirect_to crank_user_path(crank_user) + else + flash.alert = crank_user.errors.full_messages.to_sentence + redirect_to new_crank_user_path + end + end +end diff --git a/app/models/crank_user.rb b/app/models/crank_user.rb new file mode 100644 index 0000000..a53c476 --- /dev/null +++ b/app/models/crank_user.rb @@ -0,0 +1,19 @@ +class CrankUser < ApplicationRecord + validates :code, presence: true, uniqueness: true + + def self.generate_codes + 3.times.lazy.map do + candidate = SecureRandom.hex(4) + candidate unless CrankUser.where(code: candidate).exists? + end.compact + end + + def self.new_with_code + code = generate_codes.first + new(code: code) + end + + def to_param + code + end +end diff --git a/app/views/crank_users/new.html.haml b/app/views/crank_users/new.html.haml new file mode 100644 index 0000000..5178e9b --- /dev/null +++ b/app/views/crank_users/new.html.haml @@ -0,0 +1,6 @@ +%h1 Create Crank User + +%p Wow you seem like you can really crank - click the create button to get started! + += form_with model: crank_user do |f| + = f.button "create" diff --git a/app/views/crank_users/show.html.haml b/app/views/crank_users/show.html.haml new file mode 100644 index 0000000..6f4ef2b --- /dev/null +++ b/app/views/crank_users/show.html.haml @@ -0,0 +1,3 @@ +%h1 Crank User #{crank_user.code} + +%p OMG you did it! diff --git a/config/routes.rb b/config/routes.rb index 396352c..385ef1e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -24,6 +24,8 @@ resources :gift_ideas, only: %i[update] + resources :crank_users, only: %i[create new show], param: :code + namespace :crud do resources :books, only: %i[create edit new update] resources :csv_uploads diff --git a/db/migrate/20240305034656_create_crank_users.rb b/db/migrate/20240305034656_create_crank_users.rb new file mode 100644 index 0000000..473807d --- /dev/null +++ b/db/migrate/20240305034656_create_crank_users.rb @@ -0,0 +1,10 @@ +class CreateCrankUsers < ActiveRecord::Migration[7.1] + def change + create_table :crank_users do |t| + t.string :code, null: false + t.timestamps + end + + add_index :crank_users, :code, unique: true + end +end diff --git a/spec/factories/crank_user.rb b/spec/factories/crank_user.rb new file mode 100644 index 0000000..0a2c6da --- /dev/null +++ b/spec/factories/crank_user.rb @@ -0,0 +1,5 @@ +FactoryBot.define do + factory :crank_user do + code { "abcd1234" } + end +end diff --git a/spec/models/crank_user_spec.rb b/spec/models/crank_user_spec.rb new file mode 100644 index 0000000..bd3678b --- /dev/null +++ b/spec/models/crank_user_spec.rb @@ -0,0 +1,78 @@ +require "rails_helper" + +describe CrankUser do + describe ".new_with_code" do + let!(:existing_crank_user) do + CrankUser.create(code: "existing code") + end + + context "unique code on first try" do + let(:codes) do + [ + "unique code" + ] + end + + it "is valid" do + expect(SecureRandom).to receive(:hex).with(4).exactly(1).and_return(*codes) + crank_user = CrankUser.new_with_code + + expect(crank_user).to be_valid + expect(crank_user.code).to eq "unique code" + end + end + + context "unique code on second try" do + let(:codes) do + [ + "existing code", + "unique code" + ] + end + + it "is valid" do + expect(SecureRandom).to receive(:hex).with(4).exactly(2).and_return(*codes) + crank_user = CrankUser.new_with_code + + expect(crank_user).to be_valid + expect(crank_user.code).to eq "unique code" + end + end + + context "unique code on third try" do + let(:codes) do + [ + "existing code", + "existing code", + "unique code" + ] + end + + it "is valid" do + expect(SecureRandom).to receive(:hex).with(4).exactly(3).and_return(*codes) + crank_user = CrankUser.new_with_code + + expect(crank_user).to be_valid + expect(crank_user.code).to eq "unique code" + end + end + + context "fails to find unique code" do + let(:codes) do + [ + "existing code", + "existing code", + "existing code" + ] + end + + it "is not valid" do + expect(SecureRandom).to receive(:hex).with(4).exactly(3).and_return(*codes) + crank_user = CrankUser.new_with_code + + expect(crank_user).to_not be_valid + expect(crank_user.code).to eq nil + end + end + end +end diff --git a/spec/system/crank_users/visitor_creates_crank_user_spec.rb b/spec/system/crank_users/visitor_creates_crank_user_spec.rb new file mode 100644 index 0000000..1826bd7 --- /dev/null +++ b/spec/system/crank_users/visitor_creates_crank_user_spec.rb @@ -0,0 +1,12 @@ +require "rails_helper" + +describe "visitor creates crank user" do + scenario "create successfully" do + visit "/crank_users/new" + click_on "create" + expect(page).to have_content "Crank User created" + crank_user = CrankUser.last + expect(page).to have_content crank_user.code + expect(page.current_path).to eq crank_user_path(crank_user) + end +end diff --git a/spec/system/model_counts/admin_views_model_counts_spec.rb b/spec/system/model_counts/admin_views_model_counts_spec.rb index 7df9bdc..aaf470f 100644 --- a/spec/system/model_counts/admin_views_model_counts_spec.rb +++ b/spec/system/model_counts/admin_views_model_counts_spec.rb @@ -11,6 +11,7 @@ expected_rows = [ "Artwork 0", "Book 0", + "CrankUser 0", "CsvUpload 0", "FinancialAccount 0", "FinancialStatement 0", @@ -32,6 +33,7 @@ scenario "with some models" do FactoryBot.create(:book) + FactoryBot.create(:crank_user) FactoryBot.create(:csv_upload) FactoryBot.create(:gift_idea) FactoryBot.create(:killswitch) @@ -63,6 +65,7 @@ expected_rows = [ "Artwork 1", "Book 1", + "CrankUser 1", "CsvUpload 1", "FinancialAccount 1", "FinancialStatement 1", @@ -81,6 +84,6 @@ expect(actual_rows).to match_array(expected_rows) - expect(page.find("tfoot tr").text).to eq "Total 16" + expect(page.find("tfoot tr").text).to eq "Total 17" end end From c928a5e57d03e2d03d8b6de15dab792a57ea6164 Mon Sep 17 00:00:00 2001 From: Jon Allured Date: Mon, 11 Mar 2024 19:35:14 -0500 Subject: [PATCH 2/2] Visitor creates crank count --- app/controllers/crank_counts_controller.rb | 22 +++++++++++++++++++ app/models/crank_count.rb | 6 +++++ app/models/crank_user.rb | 1 + app/views/crank_counts/new.html.haml | 7 ++++++ app/views/crank_counts/show.html.haml | 4 ++++ config/routes.rb | 4 +++- .../20240308221817_create_crank_counts.rb | 10 +++++++++ spec/factories/crank_count.rb | 7 ++++++ .../visitor_creates_crank_count_spec.rb | 14 ++++++++++++ .../admin_views_model_counts_spec.rb | 10 +++++++-- 10 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 app/controllers/crank_counts_controller.rb create mode 100644 app/models/crank_count.rb create mode 100644 app/views/crank_counts/new.html.haml create mode 100644 app/views/crank_counts/show.html.haml create mode 100644 db/migrate/20240308221817_create_crank_counts.rb create mode 100644 spec/factories/crank_count.rb create mode 100644 spec/system/crank_counts/visitor_creates_crank_count_spec.rb diff --git a/app/controllers/crank_counts_controller.rb b/app/controllers/crank_counts_controller.rb new file mode 100644 index 0000000..0c5257c --- /dev/null +++ b/app/controllers/crank_counts_controller.rb @@ -0,0 +1,22 @@ +class CrankCountsController < ApplicationController + skip_before_action :ensure_admin + + expose(:crank_user, find_by: :code, id: :crank_user_code) + expose(:crank_count, parent: :crank_user) + + def create + if crank_count.save + flash.notice = "Crank Count created" + redirect_to crank_user_crank_count_path(crank_user, crank_count) + else + flash.alert = crank_count.errors.full_messages.to_sentence + redirect_to new_crank_user_crank_count_path(crank_user) + end + end + + private + + def crank_count_params + params.require(:crank_count).permit(:ticks).merge(cranked_on: Date.today) + end +end diff --git a/app/models/crank_count.rb b/app/models/crank_count.rb new file mode 100644 index 0000000..70150be --- /dev/null +++ b/app/models/crank_count.rb @@ -0,0 +1,6 @@ +class CrankCount < ApplicationRecord + belongs_to :crank_user + + validates :cranked_on, presence: true + validates :ticks, presence: true +end diff --git a/app/models/crank_user.rb b/app/models/crank_user.rb index a53c476..ab2a7ac 100644 --- a/app/models/crank_user.rb +++ b/app/models/crank_user.rb @@ -1,4 +1,5 @@ class CrankUser < ApplicationRecord + has_many :crank_counts validates :code, presence: true, uniqueness: true def self.generate_codes diff --git a/app/views/crank_counts/new.html.haml b/app/views/crank_counts/new.html.haml new file mode 100644 index 0000000..f95b1b0 --- /dev/null +++ b/app/views/crank_counts/new.html.haml @@ -0,0 +1,7 @@ +%h1 Create Crank Count for Crank User #{crank_user.code} + +%p Had enough for today? Go ahead and click create to log #{params[:ticks]} ticks! + += form_with model: [crank_user, crank_count] do |f| + = f.hidden_field :ticks, value: params[:ticks] + = f.button "create" diff --git a/app/views/crank_counts/show.html.haml b/app/views/crank_counts/show.html.haml new file mode 100644 index 0000000..7000c39 --- /dev/null +++ b/app/views/crank_counts/show.html.haml @@ -0,0 +1,4 @@ +%h1 Crank Count for Crank User #{crank_user.code} + +%p #{crank_count.ticks} ticks +%p cranked on #{crank_count.cranked_on} diff --git a/config/routes.rb b/config/routes.rb index 385ef1e..b89bc6c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -24,7 +24,9 @@ resources :gift_ideas, only: %i[update] - resources :crank_users, only: %i[create new show], param: :code + resources :crank_users, only: %i[create new show], param: :code do + resources :crank_counts, only: %i[create new show] + end namespace :crud do resources :books, only: %i[create edit new update] diff --git a/db/migrate/20240308221817_create_crank_counts.rb b/db/migrate/20240308221817_create_crank_counts.rb new file mode 100644 index 0000000..2058aa4 --- /dev/null +++ b/db/migrate/20240308221817_create_crank_counts.rb @@ -0,0 +1,10 @@ +class CreateCrankCounts < ActiveRecord::Migration[7.1] + def change + create_table :crank_counts do |t| + t.belongs_to :crank_user + t.integer :ticks, null: false + t.date :cranked_on, null: false + t.timestamps + end + end +end diff --git a/spec/factories/crank_count.rb b/spec/factories/crank_count.rb new file mode 100644 index 0000000..c01ccfe --- /dev/null +++ b/spec/factories/crank_count.rb @@ -0,0 +1,7 @@ +FactoryBot.define do + factory :crank_count do + crank_user + ticks { 7 } + cranked_on { Date.today } + end +end diff --git a/spec/system/crank_counts/visitor_creates_crank_count_spec.rb b/spec/system/crank_counts/visitor_creates_crank_count_spec.rb new file mode 100644 index 0000000..5deec95 --- /dev/null +++ b/spec/system/crank_counts/visitor_creates_crank_count_spec.rb @@ -0,0 +1,14 @@ +require "rails_helper" + +describe "visitor creates crank count" do + scenario "create successfully" do + crank_user = FactoryBot.create(:crank_user) + visit "/crank_users/#{crank_user.code}/crank_counts/new?ticks=77" + click_on "create" + expect(page).to have_content "Crank Count created" + crank_count = CrankCount.last + expect(page).to have_content crank_user.code + expect(page.current_path).to eq crank_user_crank_count_path(crank_user, crank_count) + expect(page).to have_content "77 ticks" + end +end diff --git a/spec/system/model_counts/admin_views_model_counts_spec.rb b/spec/system/model_counts/admin_views_model_counts_spec.rb index aaf470f..e5a5495 100644 --- a/spec/system/model_counts/admin_views_model_counts_spec.rb +++ b/spec/system/model_counts/admin_views_model_counts_spec.rb @@ -11,6 +11,7 @@ expected_rows = [ "Artwork 0", "Book 0", + "CrankCount 0", "CrankUser 0", "CsvUpload 0", "FinancialAccount 0", @@ -33,7 +34,6 @@ scenario "with some models" do FactoryBot.create(:book) - FactoryBot.create(:crank_user) FactoryBot.create(:csv_upload) FactoryBot.create(:gift_idea) FactoryBot.create(:killswitch) @@ -41,6 +41,11 @@ FactoryBot.create(:project) FactoryBot.create(:work_day) + FactoryBot.create( + :crank_count, + crank_user: FactoryBot.create(:crank_user) + ) + financial_account = FactoryBot.create(:financial_account) FactoryBot.create(:financial_statement, financial_account: financial_account) FactoryBot.create(:financial_transaction, financial_account: financial_account) @@ -65,6 +70,7 @@ expected_rows = [ "Artwork 1", "Book 1", + "CrankCount 1", "CrankUser 1", "CsvUpload 1", "FinancialAccount 1", @@ -84,6 +90,6 @@ expect(actual_rows).to match_array(expected_rows) - expect(page.find("tfoot tr").text).to eq "Total 17" + expect(page.find("tfoot tr").text).to eq "Total 18" end end