Compare commits

..

No commits in common. "20cca87cddd9d822678024919596e8ff0adaf8b2" and "c7b9c83f37c6da967f4341480304d3196a372b47" have entirely different histories.

76 changed files with 299 additions and 466 deletions

View File

@ -1,4 +1,4 @@
name: Run unit tests and Rubocop name: Run unit tests
on: on:
push: push:
branches: branches:
@ -24,7 +24,6 @@ jobs:
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
- uses: ruby/setup-ruby@v1.202.0 - uses: ruby/setup-ruby@v1.202.0
- run: bundle install - run: bundle install
- run: bundle exec rubocop --force-exclusion --parallel
- name: Wait until Postgres is ready to accept connections - name: Wait until Postgres is ready to accept connections
run: | run: |
apt-get update && apt-get install -f -y postgresql-client apt-get update && apt-get install -f -y postgresql-client

View File

@ -9,21 +9,9 @@ AllCops:
- 'db/**/*' - 'db/**/*'
- 'config/**/*' - 'config/**/*'
- 'script/**/*' - 'script/**/*'
- 'bin/*' - 'bin/{rails,rake}'
- '*.yml' - '*.yml'
Layout/LineLength: Layout/LineLength:
Max: 120 Max: 120
RSpec/ExampleLength: RSpec/ExampleLength:
Enabled: false Max: 10
Metrics/ModuleLength:
Enabled: false
RSpec/MultipleMemoizedHelpers:
Enabled: false
Style/Documentation:
Enabled: false
Metrics/MethodLength:
Max: 20
Rails/SkipsModelValidations:
Enabled: false
Metrics/AbcSize:
Enabled: false

View File

@ -1,8 +1,6 @@
# frozen_string_literal: true
# Add your own tasks in files placed in lib/tasks ending in .rake, # Add your own tasks in files placed in lib/tasks ending in .rake,
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
require_relative 'config/application' require_relative "config/application"
Rails.application.load_tasks Rails.application.load_tasks

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module ApplicationCable module ApplicationCable

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module ApplicationCable module ApplicationCable

View File

@ -6,27 +6,22 @@ class AffinitiesController < ApplicationController
before_action :set_group before_action :set_group
def index def index
overridden = @group.affinities.each_with_object({}) do |affinity, acc| overridden_affinities = @group.affinities
acc[affinity.another_group(@group).id] = affinity.discomfort .each_with_object({}) { |affinity, acc| acc[affinity.another_group(@group).id] = affinity.discomfort }
end Group.where.not(id: @group.id).pluck(:id).index_with { |group_id| GroupAffinity::MAX_DISCOMFORT - (overridden_affinities[group_id] || GroupAffinity::NEUTRAL) }
Group.where.not(id: @group.id)
.pluck(:id)
.index_with { |group_id| GroupAffinity::MAX_DISCOMFORT - (overridden[group_id] || GroupAffinity::NEUTRAL) }
.then { |affinities| render json: affinities } .then { |affinities| render json: affinities }
end end
def bulk_update def bulk_update
affinities = params.expect(affinities: [%i[group_id affinity]]).map(&:to_h).map do |affinity| params.expect(affinities: [[:group_id, :affinity]]).map(&:to_h).map do |affinity|
{ {
group_a_id: @group.id, group_a_id: @group.id,
group_b_id: affinity[:group_id], group_b_id: affinity[:group_id],
discomfort: GroupAffinity::MAX_DISCOMFORT - affinity[:affinity] discomfort: GroupAffinity::MAX_DISCOMFORT - affinity[:affinity]
} }
end end.then { |affinities| GroupAffinity.upsert_all(affinities) }
GroupAffinity.upsert_all(affinities)
render json: {}, status: :ok render json: {}, status: :ok
rescue ActiveRecord::InvalidForeignKey rescue ActiveRecord::InvalidForeignKey
render json: { error: 'At least one of the group IDs provided does not exist.' }, status: :bad_request render json: { error: 'At least one of the group IDs provided does not exist.' }, status: :bad_request
rescue ActiveRecord::StatementInvalid rescue ActiveRecord::StatementInvalid

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
@ -42,7 +40,7 @@ class ApplicationController < ActionController::Base
end end
def captcha_params def captcha_params
params.expect(captcha: %i[id answer]) params.expect(captcha: [:id, :answer])
end end
def default_url_options(options = {}) def default_url_options(options = {})
@ -55,7 +53,7 @@ class ApplicationController < ActionController::Base
def development_swagger? def development_swagger?
Rails.env.test? || Rails.env.test? ||
(Rails.env.development? && request.headers['referer']&.include?('/api-docs/index.html')) Rails.env.development? && request.headers['referer']&.include?('/api-docs/index.html')
end end
def set_csrf_cookie def set_csrf_cookie

View File

@ -1,12 +1,10 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
class CaptchaController < ApplicationController class CaptchaController < ApplicationController
skip_before_action :authenticate_user! skip_before_action :authenticate_user!
skip_before_action :set_tenant skip_before_action :set_tenant
def create def create
id = LibreCaptcha.new.id id = LibreCaptcha.new.get_id
render json: { render json: {
id:, id:,
media_url: media_captcha_index_url(id:) media_url: media_captcha_index_url(id:)

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
class ExpensesController < ApplicationController class ExpensesController < ApplicationController
@ -8,7 +6,7 @@ class ExpensesController < ApplicationController
end end
def index def index
render json: Expense.order(pricing_type: :asc, amount: :desc).as_json(only: %i[id name amount pricing_type]) render json: Expense.all.order(pricing_type: :asc, amount: :desc).as_json(only: %i[id name amount pricing_type])
end end
def create def create

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
class GroupsController < ApplicationController class GroupsController < ApplicationController
@ -41,6 +39,6 @@ class GroupsController < ApplicationController
end end
def group_params def group_params
params.expect(group: %i[name icon color]) params.expect(group: [:name, :icon, :color])
end end
end end

View File

@ -1,12 +1,10 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'csv' require 'csv'
class GuestsController < ApplicationController class GuestsController < ApplicationController
def index def index
render json: Guest.includes(:group) render json: Guest.all.includes(:group)
.left_joins(:group) .left_joins(:group)
.order('groups.name' => :asc, name: :asc) .order('groups.name' => :asc, name: :asc)
.as_json(only: %i[id name status], include: { group: { only: %i[id name] } }) .as_json(only: %i[id name status], include: { group: { only: %i[id name] } })

View File

@ -1,33 +1,11 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
class SummaryController < ApplicationController class SummaryController < ApplicationController
def index def index
render json: {
expenses:,
guests:
}
end
private
def guests
guest_summary = Guest.group(:status).count
{
total: guest_summary.except('considered').values.sum,
confirmed: guest_summary['confirmed'].to_i,
declined: guest_summary['declined'].to_i,
tentative: guest_summary['tentative'].to_i,
invited: guest_summary['invited'].to_i
}
end
def expenses
expense_summary = Expenses::TotalQuery.new(wedding: ActsAsTenant.current_tenant).call expense_summary = Expenses::TotalQuery.new(wedding: ActsAsTenant.current_tenant).call
guest_summary = Guest.group(:status).count
{ render json: {
expenses: {
projected: { projected: {
total: expense_summary['total_projected'], total: expense_summary['total_projected'],
guests: expense_summary['projected_guests'] guests: expense_summary['projected_guests']
@ -39,6 +17,14 @@ class SummaryController < ApplicationController
status: { status: {
paid: 0 paid: 0
} }
},
guests: {
total: guest_summary.except('considered').values.sum,
confirmed: guest_summary['confirmed'].to_i,
declined: guest_summary['declined'].to_i,
tentative: guest_summary['tentative'].to_i,
invited: guest_summary['invited'].to_i
}
} }
end end
end end

View File

@ -1,10 +1,8 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
class TablesArrangementsController < ApplicationController class TablesArrangementsController < ApplicationController
def index def index
render json: TablesArrangement.order(discomfort: :asc).limit(3).as_json(only: %i[id name discomfort]) render json: TablesArrangement.all.order(discomfort: :asc).limit(3).as_json(only: %i[id name discomfort])
end end
def show def show

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
class TokensController < ApplicationController class TokensController < ApplicationController

View File

@ -1,9 +1,6 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module Users class Users::ConfirmationsController < Devise::ConfirmationsController
class ConfirmationsController < Devise::ConfirmationsController
clear_respond_to clear_respond_to
respond_to :json respond_to :json
@ -23,5 +20,4 @@ module Users
return return
end end
end end
end
end end

View File

@ -1,9 +1,6 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module Users class Users::RegistrationsController < Devise::RegistrationsController
class RegistrationsController < Devise::RegistrationsController
clear_respond_to clear_respond_to
respond_to :json respond_to :json
@ -28,5 +25,4 @@ module Users
def set_tenant def set_tenant
set_current_tenant(nil) set_current_tenant(nil)
end end
end
end end

View File

@ -1,10 +1,6 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module Users class Users::SessionsController < Devise::SessionsController
class SessionsController < Devise::SessionsController
clear_respond_to clear_respond_to
respond_to :json respond_to :json
end
end end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module TreeNodeExtension module TreeNodeExtension

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module ApplicationHelper module ApplicationHelper

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module ExpensesHelper module ExpensesHelper

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module GroupsHelper module GroupsHelper

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module GuestsHelper module GuestsHelper

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module TablesArrangementsHelper module TablesArrangementsHelper

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
class ApplicationJob < ActiveJob::Base class ApplicationJob < ActiveJob::Base

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
class TableSimulatorJob < ApplicationJob class TableSimulatorJob < ApplicationJob

View File

@ -1,8 +1,6 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
class ApplicationMailer < ActionMailer::Base class ApplicationMailer < ActionMailer::Base
default from: 'from@example.com' default from: "from@example.com"
layout 'mailer' layout "mailer"
end end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
class ApplicationRecord < ActiveRecord::Base class ApplicationRecord < ActiveRecord::Base

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
# == Schema Information # == Schema Information

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
# == Schema Information # == Schema Information
@ -33,7 +31,7 @@ class Group < ApplicationRecord
validates :name, uniqueness: true validates :name, uniqueness: true
validates :name, :order, presence: true validates :name, :order, presence: true
has_many :children, class_name: 'Group', foreign_key: 'parent_id', dependent: :nullify, inverse_of: :parent has_many :children, class_name: 'Group', foreign_key: 'parent_id'
belongs_to :parent, class_name: 'Group', optional: true belongs_to :parent, class_name: 'Group', optional: true
before_create :set_color before_create :set_color
@ -43,7 +41,10 @@ class Group < ApplicationRecord
has_many :guests, dependent: :nullify has_many :guests, dependent: :nullify
def colorize_children(generation = 1) def colorize_children(generation = 1)
children.zip(palette(generation)) do |child, raw_color| derived_colors = generation == 1 ? color.paint.palette.analogous(size: children.count) : color.paint.palette.decreasing_saturation
children.zip(derived_colors) do |child, raw_color|
final_color = raw_color.paint final_color = raw_color.paint
final_color.brighten(60) if final_color.dark? final_color.brighten(60) if final_color.dark?
@ -59,14 +60,6 @@ class Group < ApplicationRecord
private private
def palette(generation)
if generation == 1
color.paint.palette.analogous(size: children.count)
else
color.paint.palette.decreasing_saturation
end
end
def set_color def set_color
return if color.present? return if color.present?

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
# == Schema Information # == Schema Information
@ -32,8 +30,7 @@ class GroupAffinity < ApplicationRecord
belongs_to :group_a, class_name: 'Group' belongs_to :group_a, class_name: 'Group'
belongs_to :group_b, class_name: 'Group' belongs_to :group_b, class_name: 'Group'
validates :discomfort, validates :discomfort, numericality: { greater_than_or_equal_to: MIN_DISCOMFORT, less_than_or_equal_to: MAX_DISCOMFORT }
numericality: { greater_than_or_equal_to: MIN_DISCOMFORT, less_than_or_equal_to: MAX_DISCOMFORT }
def another_group(group) def another_group(group)
return nil if group != group_a && group != group_b return nil if group != group_a && group != group_b

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
# == Schema Information # == Schema Information
@ -41,8 +39,8 @@ class Guest < ApplicationRecord
scope :potential, -> { where.not(status: %i[declined considered]) } scope :potential, -> { where.not(status: %i[declined considered]) }
after_destroy :recalculate_simulations
after_save :recalculate_simulations, if: :saved_change_to_status? after_save :recalculate_simulations, if: :saved_change_to_status?
after_destroy :recalculate_simulations
has_many :seats, dependent: :delete_all has_many :seats, dependent: :delete_all

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
# == Schema Information # == Schema Information

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
# == Schema Information # == Schema Information
@ -23,7 +21,7 @@
# #
class TablesArrangement < ApplicationRecord class TablesArrangement < ApplicationRecord
acts_as_tenant :wedding acts_as_tenant :wedding
has_many :seats, dependent: :delete_all has_many :seats
has_many :guests, through: :seats has_many :guests, through: :seats
before_create :assign_name before_create :assign_name

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
# == Schema Information # == Schema Information

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
# == Schema Information # == Schema Information

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module Expenses module Expenses
@ -18,7 +16,7 @@ module Expenses
private private
def query def query
<<~SQL.squish <<~SQL
WITH guest_count AS (#{guest_count_per_status}), WITH guest_count AS (#{guest_count_per_status}),
expense_summary AS (#{expense_summary}) expense_summary AS (#{expense_summary})
SELECT guest_count.confirmed as confirmed_guests, SELECT guest_count.confirmed as confirmed_guests,
@ -30,7 +28,7 @@ module Expenses
end end
def expense_summary def expense_summary
<<~SQL.squish <<~SQL
SELECT coalesce(sum(amount) filter (where pricing_type = 'fixed'), 0) as fixed, SELECT coalesce(sum(amount) filter (where pricing_type = 'fixed'), 0) as fixed,
coalesce(sum(amount) filter (where pricing_type = 'per_person'), 0) as variable coalesce(sum(amount) filter (where pricing_type = 'per_person'), 0) as variable
FROM expenses FROM expenses
@ -39,7 +37,7 @@ module Expenses
end end
def guest_count_per_status def guest_count_per_status
<<~SQL.squish <<~SQL
SELECT COALESCE(count(*) filter(where status = #{Guest.statuses['confirmed']}), 0) as confirmed, SELECT COALESCE(count(*) filter(where status = #{Guest.statuses['confirmed']}), 0) as confirmed,
COALESCE(count(*) filter(where status IN (#{Guest.statuses.values_at('confirmed', 'invited', 'tentative').join(',')})), 0) as projected COALESCE(count(*) filter(where status IN (#{Guest.statuses.values_at('confirmed', 'invited', 'tentative').join(',')})), 0) as projected
FROM guests FROM guests

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module Groups module Groups
@ -11,21 +9,13 @@ module Groups
:icon, :icon,
:parent_id, :parent_id,
:color, :color,
*count_expressions
)
end
private
def count_expressions
[
Arel.sql('count(*) filter (where status IS NOT NULL) as total'), Arel.sql('count(*) filter (where status IS NOT NULL) as total'),
Arel.sql('count(*) filter (where status = 0) as considered'), Arel.sql('count(*) filter (where status = 0) as considered'),
Arel.sql('count(*) filter (where status = 10) as invited'), Arel.sql('count(*) filter (where status = 10) as invited'),
Arel.sql('count(*) filter (where status = 20) as confirmed'), Arel.sql('count(*) filter (where status = 20) as confirmed'),
Arel.sql('count(*) filter (where status = 30) as declined'), Arel.sql('count(*) filter (where status = 30) as declined'),
Arel.sql('count(*) filter (where status = 40) as tentative') Arel.sql('count(*) filter (where status = 40) as tentative'),
] )
end end
end end
end end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
class SerializableGroup < JSONAPI::Serializable::Resource class SerializableGroup < JSONAPI::Serializable::Resource

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
class SerializableGuest < JSONAPI::Serializable::Resource class SerializableGuest < JSONAPI::Serializable::Resource

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
class AffinityGroupsHierarchy < Array class AffinityGroupsHierarchy < Array

View File

@ -1,20 +1,20 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
class LibreCaptcha class LibreCaptcha
def id def get_id
HTTParty.post('http://libre-captcha:8888/v2/captcha', HTTParty.post("http://libre-captcha:8888/v2/captcha",
body: { body: {
input_type: 'text', input_type: "text",
level: :hard, level: :hard,
media: 'image/png', media: 'image/png',
size: '350x100' size: '350x100'
}.to_json).then { |raw| JSON.parse(raw)['id'] } }.to_json
).then { |raw| JSON.parse(raw)['id'] }
end end
def valid?(id:, answer:) def valid?(id:, answer:)
HTTParty.post('http://libre-captcha:8888/v2/answer', HTTParty.post("http://libre-captcha:8888/v2/answer",
body: { id:, answer: }.to_json).then { |raw| JSON.parse(raw)['result'] == 'True' } body: { id:, answer: }.to_json
).then { |raw| JSON.parse(raw)['result'] == 'True' }
end end
end end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module Tables module Tables
@ -9,7 +7,6 @@ module Tables
distance = AffinityGroupsHierarchy.instance.distance(id_a, id_b) distance = AffinityGroupsHierarchy.instance.distance(id_a, id_b)
return 1 if distance.nil? return 1 if distance.nil?
Rational(distance, distance + 1) Rational(distance, distance + 1)
end end
end end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require_relative '../../extensions/tree_node_extension' require_relative '../../extensions/tree_node_extension'

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module Tables module Tables

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module Tables module Tables

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module Tables module Tables

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module VNS module VNS

View File

@ -1,8 +1,6 @@
# frozen_string_literal: true
# This file is used by Rack-based servers to start the application. # This file is used by Rack-based servers to start the application.
require_relative 'config/environment' require_relative "config/environment"
run Rails.application run Rails.application
Rails.application.load_server Rails.application.load_server

View File

@ -1,10 +1,8 @@
# frozen_string_literal: true
# This rake task was added by annotate_rb gem. # This rake task was added by annotate_rb gem.
# Can set `ANNOTATERB_SKIP_ON_DB_TASKS` to be anything to skip this # Can set `ANNOTATERB_SKIP_ON_DB_TASKS` to be anything to skip this
if Rails.env.development? && ENV['ANNOTATERB_SKIP_ON_DB_TASKS'].nil? if Rails.env.development? && ENV["ANNOTATERB_SKIP_ON_DB_TASKS"].nil?
require 'annotate_rb' require "annotate_rb"
AnnotateRb::Core.load_rake_tasks AnnotateRb::Core.load_rake_tasks
end end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'rails_helper' require 'rails_helper'
@ -7,66 +5,66 @@ require 'rails_helper'
module Tree module Tree
RSpec.describe TreeNode do RSpec.describe TreeNode do
describe '#distance_to_common_ancestor' do describe '#distance_to_common_ancestor' do
def assert_distance(node1, node2, distance) def assert_distance(node_1, node_2, distance)
aggregate_failures do aggregate_failures do
expect(node1.distance_to_common_ancestor(node2)).to eq(distance) expect(node_1.distance_to_common_ancestor(node_2)).to eq(distance)
expect(node2.distance_to_common_ancestor(node1)).to eq(distance) expect(node_2.distance_to_common_ancestor(node_1)).to eq(distance)
end end
end end
context 'when the two nodes are the same' do context 'when the two nodes are the same' do
it 'returns 0 when comparing the root itself' do it 'returns 0 when comparing the root itself' do
root = described_class.new('root') root = Tree::TreeNode.new('root')
assert_distance(root, root, 0) assert_distance(root, root, 0)
end end
it 'returns 0 when comparing a child to itself' do it 'returns 0 when comparing a child to itself' do
root = described_class.new('root') root = Tree::TreeNode.new('root')
child = root << described_class.new('child') child = root << Tree::TreeNode.new('child')
assert_distance(child, child, 0) assert_distance(child, child, 0)
end end
end end
context 'when the two nodes are siblings' do context 'when the two nodes are siblings' do
it 'returns 1 when comparing siblings' do it 'returns 1 when comparing siblings' do
root = described_class.new('root') root = Tree::TreeNode.new('root')
child1 = root << described_class.new('child1') child1 = root << Tree::TreeNode.new('child1')
child2 = root << described_class.new('child2') child2 = root << Tree::TreeNode.new('child2')
assert_distance(child1, child2, 1) assert_distance(child1, child2, 1)
end end
end end
context 'when one node is parent of the other' do context 'when one node is parent of the other' do
it 'returns 1 when comparing parent to child' do it 'returns 1 when comparing parent to child' do
root = described_class.new('root') root = Tree::TreeNode.new('root')
child = root << described_class.new('child') child = root << Tree::TreeNode.new('child')
assert_distance(root, child, 1) assert_distance(root, child, 1)
end end
end end
context 'when one node is grandparent of the other' do context 'when one node is grandparent of the other' do
it 'returns 2 when comparing grandparent to grandchild' do it 'returns 2 when comparing grandparent to grandchild' do
root = described_class.new('root') root = Tree::TreeNode.new('root')
child = root << described_class.new('child') child = root << Tree::TreeNode.new('child')
grandchild = child << described_class.new('grandchild') grandchild = child << Tree::TreeNode.new('grandchild')
assert_distance(root, grandchild, 2) assert_distance(root, grandchild, 2)
end end
end end
context 'when the two nodes are cousins' do context 'when the two nodes are cousins' do
it 'returns 2 when comparing cousins' do it 'returns 2 when comparing cousins' do
root = described_class.new('root') root = Tree::TreeNode.new('root')
child1 = root << described_class.new('child1') child1 = root << Tree::TreeNode.new('child1')
child2 = root << described_class.new('child2') child2 = root << Tree::TreeNode.new('child2')
grandchild1 = child1 << described_class.new('grandchild1') grandchild1 = child1 << Tree::TreeNode.new('grandchild1')
grandchild2 = child2 << described_class.new('grandchild2') grandchild2 = child2 << Tree::TreeNode.new('grandchild2')
assert_distance(grandchild1, grandchild2, 2) assert_distance(grandchild1, grandchild2, 2)
end end
end end
context 'when the two nodes are not related' do context 'when the two nodes are not related' do
it 'returns nil' do it 'returns nil' do
root = described_class.new('root') root = Tree::TreeNode.new('root')
another_root = described_class.new('another_root') another_root = Tree::TreeNode.new('another_root')
assert_distance(root, another_root, nil) assert_distance(root, another_root, nil)
end end
end end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'rails_helper' require 'rails_helper'
@ -63,8 +61,8 @@ module Expenses
end end
it 'returns the sum of fixed and variable expenses', :aggregate_failures do it 'returns the sum of fixed and variable expenses', :aggregate_failures do
expect(response['total_confirmed']).to eq(100 + 200 + (50 * 2)) expect(response['total_confirmed']).to eq(100 + 200 + 50 * 2)
expect(response['total_projected']).to eq(100 + 200 + (11 * 50)) expect(response['total_projected']).to eq(100 + 200 + 11 * 50)
expect(response['confirmed_guests']).to eq(2) expect(response['confirmed_guests']).to eq(2)
expect(response['projected_guests']).to eq(2 + 4 + 5) expect(response['projected_guests']).to eq(2 + 4 + 5)
end end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'rails_helper' require 'rails_helper'
@ -7,7 +5,7 @@ require 'rails_helper'
module Groups module Groups
RSpec.describe SummaryQuery do RSpec.describe SummaryQuery do
describe '#call' do describe '#call' do
subject(:result) { described_class.new.call } subject { described_class.new.call }
context 'when there are no groups' do context 'when there are no groups' do
it { is_expected.to eq([]) } it { is_expected.to eq([]) }
@ -19,7 +17,7 @@ module Groups
context 'when there are no guests' do context 'when there are no guests' do
it 'returns the summary of groups' do it 'returns the summary of groups' do
expect(result).to contain_exactly( is_expected.to contain_exactly(
{ 'id' => parent.id, { 'id' => parent.id,
'name' => 'Friends', 'name' => 'Friends',
'icon' => 'icon-1', 'icon' => 'icon-1',
@ -60,11 +58,11 @@ module Groups
create_list(:guest, 8, group: child, status: :invited) create_list(:guest, 8, group: child, status: :invited)
create_list(:guest, 9, group: child, status: :confirmed) create_list(:guest, 9, group: child, status: :confirmed)
create_list(:guest, 10, group: child, status: :declined) create_list(:guest, 10, group: child, status: :declined)
create_list(:guest, 11, group: child, status: :tentative) # rubocop:disable FactoryBot/ExcessiveCreateList create_list(:guest, 11, group: child, status: :tentative)
end end
it 'returns the summary of groups' do it 'returns the summary of groups' do
expect(result).to contain_exactly( is_expected.to contain_exactly(
{ {
'id' => parent.id, 'id' => parent.id,
'name' => 'Friends', 'name' => 'Friends',

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
# This file is copied to spec/ when you run 'rails generate rspec:install' # This file is copied to spec/ when you run 'rails generate rspec:install'
@ -7,7 +5,7 @@ require 'spec_helper'
ENV['RAILS_ENV'] ||= 'test' ENV['RAILS_ENV'] ||= 'test'
require_relative '../config/environment' require_relative '../config/environment'
# Prevent database truncation if the environment is production # Prevent database truncation if the environment is production
abort('The Rails environment is running in production mode!') if Rails.env.production? abort("The Rails environment is running in production mode!") if Rails.env.production?
require 'rspec/rails' require 'rspec/rails'
# Add additional requires below this line. Rails is not loaded until this point! # Add additional requires below this line. Rails is not loaded until this point!

View File

@ -4,7 +4,7 @@
require 'swagger_helper' require 'swagger_helper'
RSpec.describe 'affinities' do RSpec.describe 'affinities', type: :request do
path '/{slug}/groups/{group_id}/affinities' do path '/{slug}/groups/{group_id}/affinities' do
parameter Swagger::Schema::SLUG parameter Swagger::Schema::SLUG
parameter name: 'group_id', in: :path, type: :string, format: :uuid, description: 'group_id' parameter name: 'group_id', in: :path, type: :string, format: :uuid, description: 'group_id'
@ -46,7 +46,7 @@ RSpec.describe 'affinities' do
} }
} }
response_empty200 response_empty_200
end end
end end
end end

View File

@ -1,11 +1,10 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'swagger_helper' require 'swagger_helper'
RSpec.describe 'captcha' do RSpec.describe 'captcha', type: :request do
path '/captcha' do path '/captcha' do
post('create a CAPTCHA challenge') do post('create a CAPTCHA challenge') do
tags 'CAPTCHA' tags 'CAPTCHA'
consumes 'application/json' consumes 'application/json'
@ -16,7 +15,7 @@ RSpec.describe 'captcha' do
required: %i[id], required: %i[id],
properties: { properties: {
id: { type: :string, format: :uuid }, id: { type: :string, format: :uuid },
media_url: { type: :string, format: :uri } media_url: { type: :string, format: :uri },
} }
xit xit
end end

View File

@ -1,10 +1,8 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'swagger_helper' require 'swagger_helper'
RSpec.describe 'expenses' do RSpec.describe 'expenses', type: :request do
path '/{slug}/expenses' do path '/{slug}/expenses' do
get('list expenses') do get('list expenses') do
tags 'Expenses' tags 'Expenses'
@ -44,8 +42,8 @@ RSpec.describe 'expenses' do
} }
} }
response_empty201 response_empty_201
response422 response_422
regular_api_responses regular_api_responses
end end
end end
@ -62,9 +60,9 @@ RSpec.describe 'expenses' do
properties: Swagger::Schema::EXPENSE properties: Swagger::Schema::EXPENSE
} }
response_empty200 response_empty_200
response422 response_422
response404 response_404
regular_api_responses regular_api_responses
end end
@ -73,8 +71,8 @@ RSpec.describe 'expenses' do
produces 'application/json' produces 'application/json'
parameter Swagger::Schema::SLUG parameter Swagger::Schema::SLUG
parameter Swagger::Schema::ID parameter Swagger::Schema::ID
response_empty200 response_empty_200
response404 response_404
regular_api_responses regular_api_responses
end end
end end

View File

@ -1,10 +1,8 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'swagger_helper' require 'swagger_helper'
RSpec.describe 'groups' do RSpec.describe 'groups', type: :request do
path '/{slug}/groups' do path '/{slug}/groups' do
get('list groups') do get('list groups') do
tags 'Groups' tags 'Groups'
@ -102,7 +100,7 @@ RSpec.describe 'groups' do
parameter Swagger::Schema::SLUG parameter Swagger::Schema::SLUG
parameter name: :id, in: :path, type: :string, format: :uuid parameter name: :id, in: :path, type: :string, format: :uuid
response_empty200 response_empty_200
regular_api_responses regular_api_responses
end end
end end

View File

@ -1,10 +1,8 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'swagger_helper' require 'swagger_helper'
RSpec.describe 'guests' do RSpec.describe 'guests', type: :request do
path '/{slug}/guests' do path '/{slug}/guests' do
get('list guests') do get('list guests') do
tags 'Guests' tags 'Guests'
@ -53,8 +51,8 @@ RSpec.describe 'guests' do
} }
} }
response_empty201 response_empty_201
response422 response_422
regular_api_responses regular_api_responses
end end
end end
@ -82,9 +80,9 @@ RSpec.describe 'guests' do
} }
} }
response_empty200 response_empty_200
response422 response_422
response404 response_404
regular_api_responses regular_api_responses
end end
@ -94,8 +92,8 @@ RSpec.describe 'guests' do
parameter Swagger::Schema::SLUG parameter Swagger::Schema::SLUG
parameter name: 'id', in: :path, type: :string, format: :uuid parameter name: 'id', in: :path, type: :string, format: :uuid
response_empty200 response_empty_200
response404 response_404
regular_api_responses regular_api_responses
end end
end end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module Swagger module Swagger
@ -9,13 +7,13 @@ module Swagger
email: { type: :string, format: :email }, email: { type: :string, format: :email },
created_at: SwaggerResponseHelper::TIMESTAMP, created_at: SwaggerResponseHelper::TIMESTAMP,
updated_at: SwaggerResponseHelper::TIMESTAMP updated_at: SwaggerResponseHelper::TIMESTAMP
}.freeze }
ID = { # rubocop:disable Style/MutableConstant -- rswag modifies in: :path parameters ID = {
name: 'id', name: 'id',
in: :path, in: :path,
type: :string, type: :string,
format: :uuid format: :uuid,
} }
GROUP = { GROUP = {
@ -23,15 +21,15 @@ module Swagger
icon: { type: :string, example: 'pi pi-crown', description: 'The CSS classes used by the icon' }, icon: { type: :string, example: 'pi pi-crown', description: 'The CSS classes used by the icon' },
parent_id: { type: :string, format: :uuid }, parent_id: { type: :string, format: :uuid },
color: { type: :string, pattern: '^#(?:[0-9a-fA-F]{3}){1,2}$' } color: { type: :string, pattern: '^#(?:[0-9a-fA-F]{3}){1,2}$' }
}.freeze }
EXPENSE = { EXPENSE = {
name: { type: :string }, name: { type: :string },
amount: { type: :number, minimum: 0 }, amount: { type: :number, minimum: 0 },
pricing_type: { type: :string, enum: Expense.pricing_types.keys } pricing_type: { type: :string, enum: Expense.pricing_types.keys }
}.freeze }
SLUG = { # rubocop:disable Style/MutableConstant -- rswag modifies in: :path parameters SLUG = {
name: 'slug', name: 'slug',
in: :path, in: :path,
type: :string, type: :string,
@ -49,6 +47,6 @@ module Swagger
answer: { type: :string } answer: { type: :string }
} }
} }
}.freeze }
end end
end end

View File

@ -1,10 +1,8 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'swagger_helper' require 'swagger_helper'
RSpec.describe 'summary' do RSpec.describe 'summary', type: :request do
path '/{slug}/summary' do path '/{slug}/summary' do
get('list summaries') do get('list summaries') do
tags 'Summary' tags 'Summary'

View File

@ -1,10 +1,8 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'swagger_helper' require 'swagger_helper'
RSpec.describe 'tables_arrangements' do RSpec.describe 'tables_arrangements', type: :request do
path '/{slug}/tables_arrangements' do path '/{slug}/tables_arrangements' do
get('list tables arrangements') do get('list tables arrangements') do
tags 'Tables Arrangements' tags 'Tables Arrangements'

View File

@ -1,5 +1,15 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'swagger_helper' require 'swagger_helper'
RSpec.describe 'tokens', type: :request do
path '/token' do
get('get a cookie with CSRF token') do
tags 'CSRF token'
consumes 'application/json'
produces 'application/json'
response_empty_200
end
end
end

View File

@ -1,10 +1,9 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'swagger_helper' require 'swagger_helper'
RSpec.describe 'users/confirmations' do RSpec.describe 'users/confirmations', type: :request do
path '/{slug}/users/confirmation' do path '/{slug}/users/confirmation' do
get('confirm user email') do get('confirm user email') do
tags 'Users' tags 'Users'
@ -18,7 +17,7 @@ RSpec.describe 'users/confirmations' do
xit xit
end end
response422 response_422
end end
end end
end end

View File

@ -1,10 +1,9 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'swagger_helper' require 'swagger_helper'
RSpec.describe 'users/registrations' do RSpec.describe 'users/registrations', type: :request do
path '/{slug}/users' do path '/{slug}/users' do
post('create registration') do post('create registration') do
tags 'Users Registrations' tags 'Users Registrations'
@ -14,13 +13,13 @@ RSpec.describe 'users/registrations' do
parameter Swagger::Schema::SLUG parameter Swagger::Schema::SLUG
parameter name: :body, in: :body, schema: { parameter name: :body, in: :body, schema: {
type: :object, type: :object,
required: %i[user wedding], required: [:user, :wedding],
properties: { properties: {
user: { user: {
type: :object, type: :object,
required: %i[email password password_confirmation], required: %i[email password password_confirmation],
properties: { properties: {
email: { type: :string, format: :email }, email: { type: :string, format: :email},
password: SwaggerResponseHelper::PASSWORD, password: SwaggerResponseHelper::PASSWORD,
password_confirmation: SwaggerResponseHelper::PASSWORD password_confirmation: SwaggerResponseHelper::PASSWORD
} }

View File

@ -1,11 +1,11 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'swagger_helper' require 'swagger_helper'
RSpec.describe 'users/sessions' do RSpec.describe 'users/sessions', type: :request do
path '/{slug}/users/sign_in' do path '/{slug}/users/sign_in' do
post('create session') do post('create session') do
tags 'Users Sessions' tags 'Users Sessions'
consumes 'application/json' consumes 'application/json'
@ -32,7 +32,7 @@ RSpec.describe 'users/sessions' do
xit xit
end end
response401(message: 'Invalid Email or password.') response_401(message: 'Invalid Email or password.')
end end
end end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'rails_helper' require 'rails_helper'
@ -14,7 +12,8 @@ module Tables
describe '#calculate' do describe '#calculate' do
before do before do
allow(calculator).to receive_messages(table_size_penalty: 2, cohesion_discomfort: 3) allow(calculator).to receive(:table_size_penalty).and_return(2)
allow(calculator).to receive(:cohesion_discomfort).and_return(3)
end end
let(:table) { Table.new(create_list(:guest, 6)) } let(:table) { Table.new(create_list(:guest, 6)) }
@ -30,7 +29,6 @@ module Tables
table.min_per_table = 5 table.min_per_table = 5
table.max_per_table = 7 table.max_per_table = 7
end end
context 'when the number of guests is in the lower bound' do context 'when the number of guests is in the lower bound' do
let(:table) { Table.new(create_list(:guest, 5)) } let(:table) { Table.new(create_list(:guest, 5)) }
@ -90,7 +88,6 @@ module Tables
allow(AffinityGroupsHierarchy.instance).to receive(:distance).with(friends.id, school.id).and_return(4) allow(AffinityGroupsHierarchy.instance).to receive(:distance).with(friends.id, school.id).and_return(4)
allow(AffinityGroupsHierarchy.instance).to receive(:distance).with(work.id, school.id).and_return(5) allow(AffinityGroupsHierarchy.instance).to receive(:distance).with(work.id, school.id).and_return(5)
end end
context 'when the table contains just two guests' do context 'when the table contains just two guests' do
context 'when they belong to the same group' do context 'when they belong to the same group' do
let(:table) { create_list(:guest, 2, group: family) } let(:table) { create_list(:guest, 2, group: family) }
@ -105,7 +102,6 @@ module Tables
create(:guest, group: friends) create(:guest, group: friends)
] ]
end end
it { expect(calculator.send(:cohesion_discomfort)).to eq(1) } it { expect(calculator.send(:cohesion_discomfort)).to eq(1) }
end end
@ -209,7 +205,7 @@ module Tables
end end
it 'returns the sum of the penalties for each pair of guests' do it 'returns the sum of the penalties for each pair of guests' do
expect(calculator.send(:cohesion_discomfort)).to eq((4 * 1) + (4 * Rational(1, 2)) + (4 * Rational(2, 3))) expect(calculator.send(:cohesion_discomfort)).to eq(4 * 1 + 4 * Rational(1, 2) + 4 * Rational(2, 3))
end end
end end
@ -223,7 +219,7 @@ module Tables
end end
it 'returns the sum of the penalties for each pair of guests' do it 'returns the sum of the penalties for each pair of guests' do
expect(calculator.send(:cohesion_discomfort)).to eq((6 * 1) + (2 * Rational(1, 2)) + (3 * Rational(2, 3))) expect(calculator.send(:cohesion_discomfort)).to eq(6 * 1 + 2 * Rational(1, 2) + 3 * Rational(2, 3))
end end
end end
end end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'rails_helper' require 'rails_helper'
@ -7,19 +5,19 @@ require 'rails_helper'
module Tables module Tables
RSpec.describe Distribution do RSpec.describe Distribution do
describe '#random_distribution' do describe '#random_distribution' do
subject(:distribution) { described_class.new(min_per_table: 5, max_per_table: 10) } let(:subject) { described_class.new(min_per_table: 5, max_per_table: 10) }
context 'when there are fewer people than the minimum per table' do context 'when there are fewer people than the minimum per table' do
it 'creates one table' do it 'creates one table' do
distribution.random_distribution([1, 2, 3, 4]) subject.random_distribution([1, 2, 3, 4])
expect(distribution.tables.count).to eq(1) expect(subject.tables.count).to eq(1)
end end
end end
context 'when there are more people than the maximum per table' do context 'when there are more people than the maximum per table' do
it 'creates multiple tables' do it 'creates multiple tables' do
distribution.random_distribution([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) subject.random_distribution([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
expect(distribution.tables.count).to be > 1 expect(subject.tables.count).to be > 1
end end
end end
end end

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'rails_helper' require 'rails_helper'
@ -9,7 +7,7 @@ module Tables
describe '#each' do describe '#each' do
let(:shifts) do let(:shifts) do
acc = [] acc = []
described_class.new(initial_solution).each do |solution| # rubocop:disable Style/MapIntoArray -- #map is not implemented described_class.new(initial_solution).each do |solution|
acc << solution.tables.map(&:dup) acc << solution.tables.map(&:dup)
end end
acc acc

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'rails_helper' require 'rails_helper'
@ -9,7 +7,7 @@ module Tables
describe '#each' do describe '#each' do
let(:swaps) do let(:swaps) do
acc = [] acc = []
described_class.new(initial_solution).each do |solution| # rubocop:disable Style/MapIntoArray -- #map is not implemented described_class.new(initial_solution).each do |solution|
acc << solution.tables.map(&:dup) acc << solution.tables.map(&:dup)
end end
acc acc

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
require 'rails_helper' require 'rails_helper'

View File

@ -1,5 +1,3 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
# This file was generated by the `rails generate rspec:install` command. Conventionally, all # This file was generated by the `rails generate rspec:install` command. Conventionally, all
@ -48,49 +46,51 @@ RSpec.configure do |config|
# triggering implicit auto-inclusion in groups with matching metadata. # triggering implicit auto-inclusion in groups with matching metadata.
config.shared_context_metadata_behavior = :apply_to_host_groups config.shared_context_metadata_behavior = :apply_to_host_groups
# The settings below are suggested to provide a good initial experience # The settings below are suggested to provide a good initial experience
# with RSpec, but feel free to customize to your heart's content. # with RSpec, but feel free to customize to your heart's content.
# # This allows you to limit a spec run to individual examples or groups =begin
# # you care about by tagging them with `:focus` metadata. When nothing # This allows you to limit a spec run to individual examples or groups
# # is tagged with `:focus`, all examples get run. RSpec also provides # you care about by tagging them with `:focus` metadata. When nothing
# # aliases for `it`, `describe`, and `context` that include `:focus` # is tagged with `:focus`, all examples get run. RSpec also provides
# # metadata: `fit`, `fdescribe` and `fcontext`, respectively. # aliases for `it`, `describe`, and `context` that include `:focus`
# config.filter_run_when_matching :focus # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
# config.filter_run_when_matching :focus
# # Allows RSpec to persist some state between runs in order to support
# # the `--only-failures` and `--next-failure` CLI options. We recommend # Allows RSpec to persist some state between runs in order to support
# # you configure your source control system to ignore this file. # the `--only-failures` and `--next-failure` CLI options. We recommend
# config.example_status_persistence_file_path = "spec/examples.txt" # you configure your source control system to ignore this file.
# config.example_status_persistence_file_path = "spec/examples.txt"
# # Limits the available syntax to the non-monkey patched syntax that is
# # recommended. For more details, see: # Limits the available syntax to the non-monkey patched syntax that is
# # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ # recommended. For more details, see:
# config.disable_monkey_patching! # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/
# config.disable_monkey_patching!
# # Many RSpec users commonly either run the entire suite or an individual
# # file, and it's useful to allow more verbose output when running an # Many RSpec users commonly either run the entire suite or an individual
# # individual spec file. # file, and it's useful to allow more verbose output when running an
# if config.files_to_run.one? # individual spec file.
# # Use the documentation formatter for detailed output, if config.files_to_run.one?
# # unless a formatter has already been configured # Use the documentation formatter for detailed output,
# # (e.g. via a command-line flag). # unless a formatter has already been configured
# config.default_formatter = "doc" # (e.g. via a command-line flag).
# end config.default_formatter = "doc"
# end
# # Print the 10 slowest examples and example groups at the
# # end of the spec run, to help surface which specs are running # Print the 10 slowest examples and example groups at the
# # particularly slow. # end of the spec run, to help surface which specs are running
# config.profile_examples = 10 # particularly slow.
# config.profile_examples = 10
# # Run specs in random order to surface order dependencies. If you find an
# # order dependency and want to debug it, you can fix the order by providing # Run specs in random order to surface order dependencies. If you find an
# # the seed, which is printed after each run. # order dependency and want to debug it, you can fix the order by providing
# # --seed 1234 # the seed, which is printed after each run.
# config.order = :random # --seed 1234
# config.order = :random
# # Seed global randomization in this process using the `--seed` CLI option.
# # Setting this allows you to use `--seed` to deterministically reproduce # Seed global randomization in this process using the `--seed` CLI option.
# # test failures related to randomization by passing the same `--seed` value # Setting this allows you to use `--seed` to deterministically reproduce
# # as the one that triggered the failure. # test failures related to randomization by passing the same `--seed` value
# Kernel.srand config.seed # as the one that triggered the failure.
Kernel.srand config.seed
=end
end end

View File

@ -3,10 +3,10 @@
# frozen_string_literal: true # frozen_string_literal: true
require 'rails_helper' require 'rails_helper'
require_relative 'swagger_response_helper' require_relative './swagger_response_helper'
require_relative 'requests/schemas' require_relative './requests/schemas.rb'
include SwaggerResponseHelper # rubocop:disable Style/MixinUsage include SwaggerResponseHelper
RSpec.configure do |config| RSpec.configure do |config|
# Specify a root folder where Swagger JSON files are generated # Specify a root folder where Swagger JSON files are generated

View File

@ -1,19 +1,18 @@
# frozen_string_literal: true
# Copyright (C) 2024 Manuel Bustillo # Copyright (C) 2024 Manuel Bustillo
module SwaggerResponseHelper module SwaggerResponseHelper
TIMESTAMP_FORMAT = '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z' TIMESTAMP_FORMAT = '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z'
TIMESTAMP_EXAMPLE = Time.zone.now.iso8601(3) TIMESTAMP_EXAMPLE = Time.zone.now.iso8601(3)
TIMESTAMP = { type: :string, pattern: TIMESTAMP_FORMAT, example: TIMESTAMP_EXAMPLE }.freeze TIMESTAMP = {type: :string,pattern: TIMESTAMP_FORMAT,example: TIMESTAMP_EXAMPLE}.freeze
PASSWORD = { type: :string, minLength: User.password_length.begin, maxLength: User.password_length.end }.freeze PASSWORD = { type: :string, minLength: User.password_length.begin, maxLength: User.password_length.end }
def regular_api_responses def regular_api_responses
response401 response_401
end end
def response422 def response_422
response(422, 'Validation errors in input parameters') do response(422, 'Validation errors in input parameters') do
produces 'application/json' produces 'application/json'
error_schema error_schema
@ -21,7 +20,7 @@ module SwaggerResponseHelper
end end
end end
def response_empty200 def response_empty_200
response(200, 'Success') do response(200, 'Success') do
produces 'application/json' produces 'application/json'
schema type: :object schema type: :object
@ -29,7 +28,7 @@ module SwaggerResponseHelper
end end
end end
def response_empty201 def response_empty_201
response(201, 'Created') do response(201, 'Created') do
produces 'application/json' produces 'application/json'
schema type: :object schema type: :object
@ -37,7 +36,7 @@ module SwaggerResponseHelper
end end
end end
def response404 def response_404
response(404, 'Record not found') do response(404, 'Record not found') do
produces 'application/json' produces 'application/json'
error_schema error_schema
@ -45,7 +44,7 @@ module SwaggerResponseHelper
end end
end end
def response401(message: nil) def response_401(message: nil)
response(401, 'Unauthorized') do response(401, 'Unauthorized') do
produces 'application/json' produces 'application/json'
schema type: :object, schema type: :object,