diff --git a/Gemfile b/Gemfile index 6bcb96a..bea2d7b 100644 --- a/Gemfile +++ b/Gemfile @@ -1,73 +1,34 @@ -source "https://rubygems.org" +source 'https://rubygems.org' -ruby "3.3.4" +ruby '3.3.4' +gem 'acts-as-taggable-on' +gem 'bootsnap', require: false +gem 'importmap-rails' +gem 'jbuilder' +gem 'money' +gem 'pg', '~> 1.1' +gem 'puma', '>= 5.0' +gem 'rails', '~> 7.1.3', '>= 7.1.3.2' +gem 'redis', '>= 4.0.1' +gem 'sprockets-rails' +gem 'stimulus-rails' +gem 'turbo-rails' +gem 'tzinfo-data', platforms: %i[windows jruby] -# Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" -gem "rails", "~> 7.1.3", ">= 7.1.3.2" - -# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails] -gem "sprockets-rails" - -# Use postgresql as the database for Active Record -gem "pg", "~> 1.1" - -# Use the Puma web server [https://github.com/puma/puma] -gem "puma", ">= 5.0" - -# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails] -gem "importmap-rails" - -# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev] -gem "turbo-rails" - -# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev] -gem "stimulus-rails" - -# Build JSON APIs with ease [https://github.com/rails/jbuilder] -gem "jbuilder" - -# Use Redis adapter to run Action Cable in production -gem "redis", ">= 4.0.1" - -# Use Kredis to get higher-level data types in Redis [https://github.com/rails/kredis] -# gem "kredis" - -# Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword] -# gem "bcrypt", "~> 3.1.7" - -# Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem "tzinfo-data", platforms: %i[ windows jruby ] - -# Reduces boot times through caching; required in config/boot.rb -gem "bootsnap", require: false - -# Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] -# gem "image_processing", "~> 1.2" +gem 'jsonapi-rails' +gem 'rack-cors' +gem 'react-rails' +gem 'rubytree' group :development, :test do - # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem - gem "debug", platforms: %i[ mri windows ] - gem 'rspec-rails', '~> 6.1.0' + gem 'debug', platforms: %i[mri windows] + gem 'factory_bot_rails' gem 'faker' gem 'pry' - gem "factory_bot_rails" + gem 'rspec-rails', '~> 6.1.0' end group :development do - # Use console on exceptions pages [https://github.com/rails/web-console] - gem "web-console" - - # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler] - # gem "rack-mini-profiler" - - # Speed up commands on slow machines / big apps [https://github.com/rails/spring] - # gem "spring" + gem 'rubocop' + gem 'web-console' end - -gem "money" -gem 'acts-as-taggable-on' - -gem "rubytree" -gem 'react-rails' -gem 'rack-cors' -gem 'jsonapi-rails' \ No newline at end of file diff --git a/Gemfile.lock b/Gemfile.lock index 90dbe8c..0e23ca2 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -77,6 +77,7 @@ GEM tzinfo (~> 2.0) acts-as-taggable-on (10.0.0) activerecord (>= 6.1, < 7.2) + ast (2.4.2) babel-source (5.8.35) babel-transpiler (0.7.0) babel-source (>= 4.0, < 6) @@ -133,6 +134,7 @@ GEM jsonapi-renderer (0.2.2) jsonapi-serializable (0.3.1) jsonapi-renderer (~> 0.2.0) + language_server-protocol (3.17.0.3) loofah (2.22.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -171,6 +173,10 @@ GEM racc (~> 1.4) nokogiri (1.16.7-x86_64-linux) racc (~> 1.4) + parallel (1.26.2) + parser (3.3.4.2) + ast (~> 2.4.1) + racc pg (1.5.7) pry (0.14.2) coderay (~> 1.1) @@ -219,6 +225,7 @@ GEM rake (>= 12.2) thor (~> 1.0, >= 1.2.2) zeitwerk (~> 2.6) + rainbow (3.1.1) rake (13.2.1) rdoc (6.7.0) psych (>= 4.0.0) @@ -232,8 +239,10 @@ GEM redis-client (>= 0.22.0) redis-client (0.22.2) connection_pool + regexp_parser (2.9.2) reline (0.5.9) io-console (~> 0.5) + rexml (3.2.8) rspec-core (3.12.3) rspec-support (~> 3.12.0) rspec-expectations (3.12.4) @@ -251,6 +260,20 @@ GEM rspec-mocks (~> 3.12) rspec-support (~> 3.12) rspec-support (3.12.2) + rubocop (1.65.1) + json (~> 2.3) + language_server-protocol (>= 3.17.0) + parallel (~> 1.10) + parser (>= 3.3.0.2) + rainbow (>= 2.2.2, < 4.0) + regexp_parser (>= 2.4, < 3.0) + rexml (>= 3.2.5, < 4.0) + rubocop-ast (>= 1.31.1, < 2.0) + ruby-progressbar (~> 1.7) + unicode-display_width (>= 2.4.0, < 3.0) + rubocop-ast (1.32.0) + parser (>= 3.3.1.0) + ruby-progressbar (1.13.0) rubytree (2.0.3) json (~> 2.0, > 2.3.1) sprockets (4.2.1) @@ -272,6 +295,7 @@ GEM railties (>= 6.0.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) + unicode-display_width (2.5.0) web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) @@ -309,6 +333,7 @@ DEPENDENCIES react-rails redis (>= 4.0.1) rspec-rails (~> 6.1.0) + rubocop rubytree sprockets-rails stimulus-rails diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 461a67f..0f489fb 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -1,5 +1,6 @@ class GroupsController < ApplicationController def index - render jsonapi: Group.where(parent_id: nil), include: [children: [children: [:children]]] + roots = Group.where(parent_id: nil) + render jsonapi: roots, include: [children: [children: [:children]]] end end diff --git a/app/controllers/guests_controller.rb b/app/controllers/guests_controller.rb index 77298d6..925770f 100644 --- a/app/controllers/guests_controller.rb +++ b/app/controllers/guests_controller.rb @@ -6,14 +6,10 @@ class GuestsController < ApplicationController # GET /guests or /guests.json def index @guests = Guest.all - .left_outer_joins(:affinity_groups) - .order('tags.name' => :asc) - .includes(:affinity_groups, :unbreakable_bonds) + .joins(:group) + .order('groups.name' => :asc) - respond_to do |format| - format.html - format.json { render jsonapi: @guests } - end + render jsonapi: @guests end # GET /guests/1 or /guests/1.json diff --git a/app/models/group.rb b/app/models/group.rb index 5a2e844..afcadaa 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -1,7 +1,9 @@ class Group < ApplicationRecord - validates :name, uniqueness: true - validates :name, :order, presence: true + validates :name, uniqueness: true + validates :name, :order, presence: true - has_many :children, class_name: 'Group', foreign_key: 'parent_id' - belongs_to :parent, class_name: 'Group', optional: true + has_many :children, class_name: 'Group', foreign_key: 'parent_id' + belongs_to :parent, class_name: 'Group', optional: true + + has_many :guests end diff --git a/app/models/guest.rb b/app/models/guest.rb index 60e3bc2..927a589 100644 --- a/app/models/guest.rb +++ b/app/models/guest.rb @@ -1,6 +1,6 @@ class Guest < ApplicationRecord acts_as_taggable_on :affinity_groups, :unbreakable_bonds - + belongs_to :group def full_name "#{first_name} #{last_name}" end diff --git a/app/serializers/serializable_group.rb b/app/serializers/serializable_group.rb index e90dc52..f832475 100644 --- a/app/serializers/serializable_group.rb +++ b/app/serializers/serializable_group.rb @@ -4,4 +4,8 @@ class SerializableGroup < JSONAPI::Serializable::Resource attributes :name, :icon has_many :children + + attribute :guest_count do + @object.guests.count + end end diff --git a/app/serializers/serializable_guest.rb b/app/serializers/serializable_guest.rb index c1e4bcc..3311d8b 100644 --- a/app/serializers/serializable_guest.rb +++ b/app/serializers/serializable_guest.rb @@ -1,9 +1,13 @@ class SerializableGuest < JSONAPI::Serializable::Resource type 'guest' - attributes :id, :email + attributes :id, :email, :group_id attribute :name do "#{@object.first_name} #{@object.last_name}" end + + attribute :group_name do + @object.group.name + end end diff --git a/db/migrate/20240811154115_add_group_to_guest.rb b/db/migrate/20240811154115_add_group_to_guest.rb new file mode 100644 index 0000000..d4e2bd4 --- /dev/null +++ b/db/migrate/20240811154115_add_group_to_guest.rb @@ -0,0 +1,5 @@ +class AddGroupToGuest < ActiveRecord::Migration[7.1] + def change + add_reference :guests, :group, null: false, foreign_key: true, type: :uuid + end +end diff --git a/db/schema.rb b/db/schema.rb index 00b4b60..47376a1 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[7.1].define(version: 2024_08_11_143801) do +ActiveRecord::Schema[7.1].define(version: 2024_08_11_154115) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -44,6 +44,8 @@ ActiveRecord::Schema[7.1].define(version: 2024_08_11_143801) do t.string "phone" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.uuid "group_id", null: false + t.index ["group_id"], name: "index_guests_on_group_id" end create_table "seats", id: :uuid, default: -> { "gen_random_uuid()" }, force: :cascade do |t| @@ -94,6 +96,7 @@ ActiveRecord::Schema[7.1].define(version: 2024_08_11_143801) do end add_foreign_key "groups", "groups", column: "parent_id" + add_foreign_key "guests", "groups" add_foreign_key "seats", "guests" add_foreign_key "seats", "tables_arrangements", on_delete: :cascade add_foreign_key "taggings", "tags" diff --git a/db/seeds.rb b/db/seeds.rb index 5103a7d..53c3376 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -22,82 +22,44 @@ Expense.create!(name: 'Transportation', amount: 3000, pricing_type: 'fixed') Expense.create!(name: 'Invitations', amount: 200, pricing_type: 'fixed') Expense.create!(name: 'Cake', amount: 500, pricing_type: 'fixed') -Group.create!(name: "Jim's guests", icon: "pi pi-heart").tap do |parent| - parent.children.create!(name: "Jim's family", icon: "pi pi-users").tap do |family| - family.children.create!(name: "Jim's close family", icon: "pi pi-home") - family.children.create!(name: "Jim's cousins", icon: "pi pi-home") - family.children.create!(name: "Jim's relatives", icon: "pi pi-home") +Group.create!(name: "Jim's guests", icon: 'pi pi-heart').tap do |parent| + parent.children.create!(name: "Jim's family", icon: 'pi pi-users').tap do |family| + family.children.create!(name: "Jim's close family", icon: 'pi pi-home') + family.children.create!(name: "Jim's cousins", icon: 'pi pi-home') + family.children.create!(name: "Jim's relatives", icon: 'pi pi-home') end - parent.children.create!(name: "Jim's friends", icon: "pi pi-bullseye") - parent.children.create!(name: "Jim's work", icon: "pi pi-desktop").tap do |work| - work.children.create!(name: "Jim's besties at work", icon: "pi pi-briefcase") - end -end - -Group.create!(name: "Pam's guests", icon: "pi pi-heart-fill").tap do |parent| - parent.children.create!(name: "Pam's family", icon: "pi pi-users").tap do |family| - family.children.create!(name: "Pam's close family", icon: "pi pi-home") - family.children.create!(name: "Pam's cousins", icon: "pi pi-home") - family.children.create!(name: "Pam's relatives", icon: "pi pi-home") + parent.children.create!(name: "Jim's friends", icon: 'pi pi-bullseye') + parent.children.create!(name: "Jim's work", icon: 'pi pi-desktop').tap do |work| + work.children.create!(name: "Jim's besties at work", icon: 'pi pi-briefcase') end - parent.children.create!(name: "Pam's friends", icon: "pi pi-bullseye") - parent.children.create!(name: "Pam's work", icon: "pi pi-desktop").tap do |work| - work.children.create!(name: "Pam's besties at work", icon: "pi pi-briefcase") - end end -Group.create!(name: "Common guests", icon: "pi pi-users").tap do |parent| - parent.children.create!(name: "College friends", icon: "pi pi-calculator") - parent.children.create!(name: "High school friends", icon: "pi pi-crown") - parent.children.create!(name: "Childhood friends", icon: "pi pi-envelope") +Group.create!(name: "Pam's guests", icon: 'pi pi-heart-fill').tap do |parent| + parent.children.create!(name: "Pam's family", icon: 'pi pi-users').tap do |family| + family.children.create!(name: "Pam's close family", icon: 'pi pi-home') + family.children.create!(name: "Pam's cousins", icon: 'pi pi-home') + family.children.create!(name: "Pam's relatives", icon: 'pi pi-home') + end + parent.children.create!(name: "Pam's friends", icon: 'pi pi-bullseye') + parent.children.create!(name: "Pam's work", icon: 'pi pi-desktop').tap do |work| + work.children.create!(name: "Pam's besties at work", icon: 'pi pi-briefcase') + end end -samples = { - close_family_a: 10, - close_family_b: 10, - cousins_a: 20, - cousins_b: 15, - relatives_a: 15, - relatives_b: 10, - work_a: 10, - work_b: 10, - besties_work_a: 5, - besties_work_b: 5, - college_friends_a: 10, - college_friends_b: 10, - high_school_friends_a: 10, - high_school_friends_b: 10, - childhood_friends_a: 10, - childhood_friends_b: 10, - basket_team_a: 10, - football_team_a: 15, - dance_club: 10 -}.each_with_object([]) do |(affinity_group, count), acc| - count.times { acc << affinity_group } +Group.create!(name: 'Common guests', icon: 'pi pi-users').tap do |parent| + parent.children.create!(name: 'College friends', icon: 'pi pi-calculator') + parent.children.create!(name: 'High school friends', icon: 'pi pi-crown') + parent.children.create!(name: 'Childhood friends', icon: 'pi pi-envelope') end +groups = Group.all + NUMBER_OF_GUESTS.times do - guest = Guest.create!(first_name: Faker::Name.first_name, - last_name: Faker::Name.last_name, - email: Faker::Internet.email, - phone: Faker::PhoneNumber.cell_phone) - - guest.affinity_group_list.add(samples.sample) - guest.save! -end - -# Add unbreakable bonds -Guest.affinity_group_counts.each do |group| - couples = (group.taggings_count / 4).floor - - guests_involved = Guest.tagged_with(group.name).limit(couples * 2) - guests_involved.each_slice(2) do |a, b| - bond_name = "#{a.full_name} & #{b.full_name}" - - a.unbreakable_bond_list.add(bond_name) - b.unbreakable_bond_list.add(bond_name) - - a.save! - b.save! - end + Guest.create!( + first_name: Faker::Name.first_name, + last_name: Faker::Name.last_name, + email: Faker::Internet.email, + phone: Faker::PhoneNumber.cell_phone, + group: groups.sample + ) end diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index a8fe677..9bfade0 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -1,7 +1,6 @@ FactoryBot.define do factory :group do - name { "MyString" } - icon { "MyString" } + sequence(:name) { |i| "Group #{i}" } order { 1 } end end diff --git a/spec/factories/guest.rb b/spec/factories/guest.rb index f761dd9..7ff9066 100644 --- a/spec/factories/guest.rb +++ b/spec/factories/guest.rb @@ -1,5 +1,7 @@ FactoryBot.define do factory :guest do + association :group + first_name { Faker::Name.first_name } last_name { Faker::Name.last_name } email { Faker::Internet.email }