Compare commits
	
		
			10 Commits
		
	
	
		
			71639e0a1d
			...
			06c103e617
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					06c103e617 | ||
| e28751521d | |||
| 0502bc4552 | |||
| 7d8ecfd0e3 | |||
| 78ab27a697 | |||
| 12174b6f20 | |||
| 0d1b64256d | |||
| ac659bef86 | |||
| dd14a96e98 | |||
| 75a0191d40 | 
							
								
								
									
										20
									
								
								Gemfile.lock
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								Gemfile.lock
									
									
									
									
									
								
							@ -230,11 +230,11 @@ GEM
 | 
			
		||||
    parser (3.3.9.0)
 | 
			
		||||
      ast (~> 2.4.1)
 | 
			
		||||
      racc
 | 
			
		||||
    pg (1.6.1)
 | 
			
		||||
    pg (1.6.1-aarch64-linux)
 | 
			
		||||
    pg (1.6.1-arm64-darwin)
 | 
			
		||||
    pg (1.6.1-x86_64-darwin)
 | 
			
		||||
    pg (1.6.1-x86_64-linux)
 | 
			
		||||
    pg (1.6.2)
 | 
			
		||||
    pg (1.6.2-aarch64-linux)
 | 
			
		||||
    pg (1.6.2-arm64-darwin)
 | 
			
		||||
    pg (1.6.2-x86_64-darwin)
 | 
			
		||||
    pg (1.6.2-x86_64-linux)
 | 
			
		||||
    pluck_to_hash (1.0.2)
 | 
			
		||||
      activerecord (>= 4.0.2)
 | 
			
		||||
      activesupport (>= 4.0.2)
 | 
			
		||||
@ -580,11 +580,11 @@ CHECKSUMS
 | 
			
		||||
  ostruct (0.6.2) sha256=6d7302a299e400a2c248d6ce0dad18fc3a5714e8096facc25ffd0c54ee57cfc0
 | 
			
		||||
  parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130
 | 
			
		||||
  parser (3.3.9.0) sha256=94d6929354b1a6e3e1f89d79d4d302cc8f5aa814431a6c9c7e0623335d7687f2
 | 
			
		||||
  pg (1.6.1) sha256=e210a75e5f702954537e73bb82f90dfbe0c6d9273c018cd0e93e779181028e6b
 | 
			
		||||
  pg (1.6.1-aarch64-linux) sha256=2dc057589c4df67240bd52c68303a00a91299329bc7573b7447faee42331e214
 | 
			
		||||
  pg (1.6.1-arm64-darwin) sha256=3b502915de30cf5983d62aabae927cb7c3628b0ca46cdf3a6b888af4ff7f42b3
 | 
			
		||||
  pg (1.6.1-x86_64-darwin) sha256=c8930170622c39ee24b318a2265655b5f8f34628444ee00e4ae44068865309f7
 | 
			
		||||
  pg (1.6.1-x86_64-linux) sha256=6ac0d5c8efafc3f22a7eca2a264300037598fabe27a88e5029bc0e6d90caeb1f
 | 
			
		||||
  pg (1.6.2) sha256=58614afd405cc9c2c9e15bffe8432e0d6cfc58b722344ad4a47c73a85189c875
 | 
			
		||||
  pg (1.6.2-aarch64-linux) sha256=0503c6be5b0ca5ca3aaf91f2ed638f90843313cb81e8e7d7b60ad4bb62c3d131
 | 
			
		||||
  pg (1.6.2-arm64-darwin) sha256=4d44500b28d5193b26674583d199a6484f80f1f2ea9cf54f7d7d06a1b7e316b6
 | 
			
		||||
  pg (1.6.2-x86_64-darwin) sha256=c441a55723584e2ae41749bf26024d7ffdfe1841b442308ed50cd6b7fda04115
 | 
			
		||||
  pg (1.6.2-x86_64-linux) sha256=525f438137f2d1411a1ebcc4208ec35cb526b5a3b285a629355c73208506a8ea
 | 
			
		||||
  pluck_to_hash (1.0.2) sha256=1599906239716f98262a41493dd7d4cb72e8d83ad3d76d666deacfc5de50a47e
 | 
			
		||||
  pp (0.6.2) sha256=947ec3120c6f92195f8ee8aa25a7b2c5297bb106d83b41baa02983686577b6ff
 | 
			
		||||
  prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
 | 
			
		||||
 | 
			
		||||
@ -9,10 +9,10 @@ class TablesArrangementsController < ApplicationController
 | 
			
		||||
    render json: TablesArrangement
 | 
			
		||||
      .order(valid: :desc)
 | 
			
		||||
      .order(discomfort: :asc)
 | 
			
		||||
      .select(:id, :name, :discomfort)
 | 
			
		||||
      .select("digest = '#{current_digest}'::uuid as valid")
 | 
			
		||||
      .select(:id, :name, :discomfort, :status, :progress)
 | 
			
		||||
      .select("digest = '#{current_digest}'::uuid OR discomfort IS NULL as valid")
 | 
			
		||||
      .limit(20)
 | 
			
		||||
      .as_json(only: %i[id name discomfort valid])
 | 
			
		||||
      .as_json(only: %i[id name discomfort valid status progress])
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def show
 | 
			
		||||
@ -25,7 +25,10 @@ class TablesArrangementsController < ApplicationController
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  def create
 | 
			
		||||
    TableSimulatorJob.perform_later(current_tenant.id)
 | 
			
		||||
    ActiveRecord::Base.transaction do
 | 
			
		||||
      tables_arrangement = TablesArrangement.create!(status: :not_started)
 | 
			
		||||
      TableSimulatorJob.perform_later(current_tenant.id, tables_arrangement.id)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    render json: {}, status: :created
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
@ -8,16 +8,35 @@ class TableSimulatorJob < ApplicationJob
 | 
			
		||||
  MIN_PER_TABLE = 8
 | 
			
		||||
  MAX_PER_TABLE = 10
 | 
			
		||||
 | 
			
		||||
  def perform(wedding_id)
 | 
			
		||||
  def perform(wedding_id, tables_arrangement_id) # rubocop:disable Metrics/MethodLength
 | 
			
		||||
    Rails.logger.info "Starting table simulation #{tables_arrangement_id} for wedding #{wedding_id}"
 | 
			
		||||
    ActsAsTenant.with_tenant(Wedding.find(wedding_id)) do
 | 
			
		||||
      engine = VNS::Engine.new
 | 
			
		||||
 | 
			
		||||
      engine.add_optimization(Tables::Swap)
 | 
			
		||||
      engine.add_optimization(Tables::Shift)
 | 
			
		||||
 | 
			
		||||
      initial_solution = Tables::Distribution.new(min_per_table: MIN_PER_TABLE, max_per_table: MAX_PER_TABLE)
 | 
			
		||||
      tables_arrangement = TablesArrangement.find(tables_arrangement_id)
 | 
			
		||||
 | 
			
		||||
      initial_solution = Tables::Distribution.new(
 | 
			
		||||
        min_per_table: MIN_PER_TABLE,
 | 
			
		||||
        max_per_table: MAX_PER_TABLE,
 | 
			
		||||
        tables_arrangement_id:
 | 
			
		||||
      )
 | 
			
		||||
 | 
			
		||||
      initial_solution.random_distribution(Guest.potential.shuffle)
 | 
			
		||||
 | 
			
		||||
      initial_solution.save!
 | 
			
		||||
 | 
			
		||||
      engine.notify_progress do |current_progress|
 | 
			
		||||
        tables_arrangement.update_columns(status: :in_progress, progress: current_progress)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      engine.on_better_solution do |better_solution|
 | 
			
		||||
        better_solution.save!
 | 
			
		||||
        tables_arrangement.update_columns(discomfort: better_solution.discomfort) # TODO: remove?
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      engine.initial_solution = initial_solution
 | 
			
		||||
 | 
			
		||||
      engine.target_function(&:discomfort)
 | 
			
		||||
@ -25,6 +44,8 @@ class TableSimulatorJob < ApplicationJob
 | 
			
		||||
      best_solution = engine.run
 | 
			
		||||
 | 
			
		||||
      best_solution.save!
 | 
			
		||||
 | 
			
		||||
      tables_arrangement.update_columns(status: :completed)
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -29,5 +29,5 @@
 | 
			
		||||
class Seat < ApplicationRecord
 | 
			
		||||
  acts_as_tenant :wedding
 | 
			
		||||
  belongs_to :guest
 | 
			
		||||
  belongs_to :table_arrangement
 | 
			
		||||
  belongs_to :tables_arrangement
 | 
			
		||||
end
 | 
			
		||||
 | 
			
		||||
@ -10,6 +10,8 @@
 | 
			
		||||
#  digest     :uuid             not null
 | 
			
		||||
#  discomfort :integer
 | 
			
		||||
#  name       :string           not null
 | 
			
		||||
#  progress   :float            default(0.0), not null
 | 
			
		||||
#  status     :string           default("complete"), not null
 | 
			
		||||
#  created_at :datetime         not null
 | 
			
		||||
#  updated_at :datetime         not null
 | 
			
		||||
#  wedding_id :uuid             not null
 | 
			
		||||
 | 
			
		||||
@ -12,13 +12,14 @@ module Tables
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    attr_accessor :tables, :min_per_table, :max_per_table, :hierarchy
 | 
			
		||||
    attr_accessor :tables, :min_per_table, :max_per_table, :hierarchy, :tables_arrangement_id
 | 
			
		||||
 | 
			
		||||
    def initialize(min_per_table:, max_per_table:, hierarchy: AffinityGroupsHierarchy.new)
 | 
			
		||||
    def initialize(min_per_table:, max_per_table:, tables_arrangement_id:, hierarchy: AffinityGroupsHierarchy.new)
 | 
			
		||||
      @min_per_table = min_per_table
 | 
			
		||||
      @max_per_table = max_per_table
 | 
			
		||||
      @hierarchy = hierarchy
 | 
			
		||||
      @tables = []
 | 
			
		||||
      @tables_arrangement_id = tables_arrangement_id
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def random_distribution(people, random: Random.new)
 | 
			
		||||
@ -42,15 +43,23 @@ module Tables
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def deep_dup
 | 
			
		||||
      self.class.new(min_per_table: @min_per_table, max_per_table: @max_per_table,
 | 
			
		||||
                     hierarchy: @hierarchy).tap do |new_distribution|
 | 
			
		||||
      self.class.new(
 | 
			
		||||
        min_per_table: @min_per_table,
 | 
			
		||||
        max_per_table: @max_per_table,
 | 
			
		||||
        hierarchy: @hierarchy,
 | 
			
		||||
        tables_arrangement_id: @tables_arrangement_id
 | 
			
		||||
      ).tap do |new_distribution|
 | 
			
		||||
        new_distribution.tables = @tables.map(&:dup)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def save!
 | 
			
		||||
      ActiveRecord::Base.transaction do
 | 
			
		||||
        arrangement = TablesArrangement.create!
 | 
			
		||||
        arrangement = TablesArrangement.find(tables_arrangement_id)
 | 
			
		||||
 | 
			
		||||
        self.tables_arrangement_id = arrangement.id
 | 
			
		||||
 | 
			
		||||
        arrangement.seats.delete_all
 | 
			
		||||
 | 
			
		||||
        records_to_store = []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@
 | 
			
		||||
module VNS
 | 
			
		||||
  class Engine
 | 
			
		||||
    PERTURBATION_SIZES = [1, 1, 1, 2, 2, 3].freeze
 | 
			
		||||
    ITERATIONS = 50
 | 
			
		||||
    class << self
 | 
			
		||||
      def sequence(elements)
 | 
			
		||||
        elements = elements.to_a
 | 
			
		||||
@ -12,6 +13,10 @@ module VNS
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def initialize
 | 
			
		||||
      @perturbations = Set.new
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def target_function(&function)
 | 
			
		||||
      @target_function = function
 | 
			
		||||
    end
 | 
			
		||||
@ -22,36 +27,45 @@ module VNS
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def add_perturbation(klass)
 | 
			
		||||
      @perturbations ||= Set.new
 | 
			
		||||
      @perturbations << klass
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def notify_progress(&block)
 | 
			
		||||
      @progress_notifier = block
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def on_better_solution(&block)
 | 
			
		||||
      @better_solution_notifier = block
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    attr_writer :initial_solution
 | 
			
		||||
 | 
			
		||||
    def run
 | 
			
		||||
      raise 'No target function defined' unless @target_function
 | 
			
		||||
      raise 'No optimizations defined' unless @optimizations
 | 
			
		||||
      raise 'No initial solution defined' unless @initial_solution
 | 
			
		||||
 | 
			
		||||
      @perturbations ||= Set.new
 | 
			
		||||
      check_preconditions!
 | 
			
		||||
 | 
			
		||||
      @current_solution = @initial_solution
 | 
			
		||||
      @best_score = @target_function.call(@current_solution)
 | 
			
		||||
 | 
			
		||||
      run_all_optimizations
 | 
			
		||||
 | 
			
		||||
      @progress_notifier&.call(Rational(1, ITERATIONS + 1))
 | 
			
		||||
 | 
			
		||||
      best_solution = @current_solution
 | 
			
		||||
 | 
			
		||||
      50.times do
 | 
			
		||||
      (1..ITERATIONS).each do |iteration|
 | 
			
		||||
        @current_solution = Tables::WheelSwap.new(best_solution).call(PERTURBATION_SIZES.sample)
 | 
			
		||||
        @best_score = @target_function.call(@current_solution)
 | 
			
		||||
        Rails.logger.debug { "After perturbation: #{@best_score}" }
 | 
			
		||||
 | 
			
		||||
        run_all_optimizations
 | 
			
		||||
 | 
			
		||||
        @progress_notifier&.call(Rational(iteration + 1, ITERATIONS + 1))
 | 
			
		||||
 | 
			
		||||
        next unless best_solution.discomfort > @current_solution.discomfort
 | 
			
		||||
 | 
			
		||||
        best_solution = @current_solution
 | 
			
		||||
        @better_solution_notifier&.call(best_solution)
 | 
			
		||||
 | 
			
		||||
        Rails.logger.debug do
 | 
			
		||||
          "Found better solution after perturbation optimization: #{@current_solution.discomfort}"
 | 
			
		||||
        end
 | 
			
		||||
@ -62,6 +76,12 @@ module VNS
 | 
			
		||||
 | 
			
		||||
    private
 | 
			
		||||
 | 
			
		||||
    def check_preconditions!
 | 
			
		||||
      raise 'No target function defined' unless @target_function
 | 
			
		||||
      raise 'No optimizations defined' unless @optimizations
 | 
			
		||||
      raise 'No initial solution defined' unless @initial_solution
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    def run_all_optimizations
 | 
			
		||||
      self.class.sequence(@optimizations).each do |optimization|
 | 
			
		||||
        optimize(optimization)
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										1
									
								
								bin/jobs
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								bin/jobs
									
									
									
									
									
								
							@ -3,4 +3,5 @@
 | 
			
		||||
require_relative "../config/environment"
 | 
			
		||||
require "solid_queue/cli"
 | 
			
		||||
 | 
			
		||||
SolidQueue.logger = ActiveSupport::Logger.new($stdout)
 | 
			
		||||
SolidQueue::Cli.start(ARGV)
 | 
			
		||||
 | 
			
		||||
@ -0,0 +1,5 @@
 | 
			
		||||
class AddStatusColumnToTablesArrangements < ActiveRecord::Migration[8.0]
 | 
			
		||||
  def change
 | 
			
		||||
    add_column :tables_arrangements, :status, :string, default: :complete, null: false
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
@ -0,0 +1,5 @@
 | 
			
		||||
class AddProgressToTablesArrangements < ActiveRecord::Migration[8.0]
 | 
			
		||||
  def change
 | 
			
		||||
    add_column :tables_arrangements, :progress, :float, default: 0, null: false
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
							
								
								
									
										4
									
								
								db/schema.rb
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										4
									
								
								db/schema.rb
									
									
									
										generated
									
									
									
								
							@ -10,7 +10,7 @@
 | 
			
		||||
#
 | 
			
		||||
# It's strongly recommended that you check this file into your version control system.
 | 
			
		||||
 | 
			
		||||
ActiveRecord::Schema[8.0].define(version: 2025_06_08_181054) do
 | 
			
		||||
ActiveRecord::Schema[8.0].define(version: 2025_09_08_145119) do
 | 
			
		||||
  # These are extensions that must be enabled in order to support this database
 | 
			
		||||
  enable_extension "pg_catalog.plpgsql"
 | 
			
		||||
 | 
			
		||||
@ -216,6 +216,8 @@ ActiveRecord::Schema[8.0].define(version: 2025_06_08_181054) do
 | 
			
		||||
    t.string "name", null: false
 | 
			
		||||
    t.uuid "wedding_id", null: false
 | 
			
		||||
    t.uuid "digest", default: -> { "gen_random_uuid()" }, null: false
 | 
			
		||||
    t.string "status", default: "complete", null: false
 | 
			
		||||
    t.float "progress", default: 0.0, null: false
 | 
			
		||||
    t.index ["wedding_id"], name: "index_tables_arrangements_on_wedding_id"
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -86,7 +86,9 @@ ActsAsTenant.with_tenant(wedding) do
 | 
			
		||||
 | 
			
		||||
  # TODO: Clean up invitations with no guests
 | 
			
		||||
 | 
			
		||||
  ActiveJob.perform_all_later(3.times.map { TableSimulatorJob.new(wedding.id) })
 | 
			
		||||
  3.times { TablesArrangement.create! }
 | 
			
		||||
    .map { |arrangement| TableSimulatorJob.new(wedding.id, arrangement.id) }
 | 
			
		||||
    .then { |jobs| ActiveJob.perform_all_later }
 | 
			
		||||
 | 
			
		||||
  "red".dup.paint.palette.triad(as: :hex).zip(Group.roots).each { |(color, group)| group.update!(color: color.paint.desaturate(40)) }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,14 @@ namespace :vns do
 | 
			
		||||
 | 
			
		||||
      engine.target_function(&:discomfort)
 | 
			
		||||
 | 
			
		||||
      engine.notify_progress do |current_progress|
 | 
			
		||||
        Rails.logger.info "Progress: #{(current_progress * 100.0).round(2)}%"
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      engine.on_better_solution do |better_solution|
 | 
			
		||||
        Rails.logger.info "New best solution found with discomfort: #{better_solution.discomfort}"
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      solution = Rails.benchmark('VNS Benchmarking') { engine.run }
 | 
			
		||||
 | 
			
		||||
      Rails.logger.info "Best solution found with discomfort: #{solution.discomfort}"
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,11 @@ server {
 | 
			
		||||
        proxy_set_header Host $http_host;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    location /jobs/ {
 | 
			
		||||
        proxy_pass http://backend:3000/jobs/;
 | 
			
		||||
        proxy_set_header Host $http_host;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    location /captcha/v2/media/ {
 | 
			
		||||
        proxy_pass http://libre-captcha:8888/v2/media/;
 | 
			
		||||
        proxy_set_header Host $http_host;
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,8 @@ RSpec.describe 'tables_arrangements' do
 | 
			
		||||
                   id: { type: :string, format: :uuid },
 | 
			
		||||
                   name: { type: :string },
 | 
			
		||||
                   discomfort: { type: :integer },
 | 
			
		||||
                   valid: { type: :boolean }
 | 
			
		||||
                   valid: { type: :boolean },
 | 
			
		||||
                   status: { type: :string, enum: %w[complete in_progress] }
 | 
			
		||||
                 }
 | 
			
		||||
               }
 | 
			
		||||
        xit
 | 
			
		||||
 | 
			
		||||
@ -6,8 +6,43 @@ require 'rails_helper'
 | 
			
		||||
 | 
			
		||||
module Tables
 | 
			
		||||
  RSpec.describe Distribution do
 | 
			
		||||
    let(:tables_arrangement) { TablesArrangement.create! }
 | 
			
		||||
 | 
			
		||||
    around do |example|
 | 
			
		||||
      ActsAsTenant.with_tenant(create(:wedding)) do
 | 
			
		||||
        example.run
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    describe '#save!' do
 | 
			
		||||
      let(:people) { create_list(:guest, 2, status: :invited) }
 | 
			
		||||
      let(:distribution) do
 | 
			
		||||
        described_class.new(min_per_table: 5, max_per_table: 10, tables_arrangement_id: tables_arrangement.id)
 | 
			
		||||
                       .tap { |d| d.random_distribution(people) }
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      context 'when tables_arrangement_id is nil' do
 | 
			
		||||
        it { expect { distribution.save! }.to change(TablesArrangement, :count).by(1) }
 | 
			
		||||
        it { expect { distribution.save! }.to change(Seat, :count).by(2) }
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      context 'when tables_arrangement_id is set' do
 | 
			
		||||
        before do
 | 
			
		||||
          existing_arrangement = TablesArrangement.create!
 | 
			
		||||
 | 
			
		||||
          existing_arrangement.seats.create!(guest: people.first, table_number: 1)
 | 
			
		||||
          distribution.tables_arrangement_id = existing_arrangement.id
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
        it { expect { distribution.save! }.not_to(change(TablesArrangement, :count)) }
 | 
			
		||||
        it { expect { distribution.save! }.to change(Seat, :count).by(1) }
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    describe '#random_distribution' do
 | 
			
		||||
      subject(:distribution) { described_class.new(min_per_table: 5, max_per_table: 10) }
 | 
			
		||||
      subject(:distribution) do
 | 
			
		||||
        described_class.new(min_per_table: 5, max_per_table: 10, tables_arrangement_id: tables_arrangement.id)
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      context 'when there are fewer people than the minimum per table' do
 | 
			
		||||
        it 'creates one table' do
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@ module Tables
 | 
			
		||||
 | 
			
		||||
      context 'when there are two tables with two people each' do
 | 
			
		||||
        let(:initial_solution) do
 | 
			
		||||
          Distribution.new(min_per_table: 2, max_per_table: 2).tap do |distribution|
 | 
			
		||||
          Distribution.new(min_per_table: 2, max_per_table: 2, tables_arrangement_id: nil).tap do |distribution|
 | 
			
		||||
            distribution.tables << Set[:a, :b].to_table
 | 
			
		||||
            distribution.tables << Set[:c, :d].to_table
 | 
			
		||||
          end
 | 
			
		||||
@ -35,7 +35,7 @@ module Tables
 | 
			
		||||
 | 
			
		||||
      context 'when there are two tables with three people each' do
 | 
			
		||||
        let(:initial_solution) do
 | 
			
		||||
          Distribution.new(min_per_table: 3, max_per_table: 3).tap do |distribution|
 | 
			
		||||
          Distribution.new(min_per_table: 3, max_per_table: 3, tables_arrangement_id: nil).tap do |distribution|
 | 
			
		||||
            distribution.tables << Set[:a, :b, :c].to_table
 | 
			
		||||
            distribution.tables << Set[:d, :e, :f].to_table
 | 
			
		||||
          end
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,7 @@ module Tables
 | 
			
		||||
 | 
			
		||||
      context 'when there are two tables with two people each' do
 | 
			
		||||
        let(:initial_solution) do
 | 
			
		||||
          Distribution.new(min_per_table: 2, max_per_table: 2).tap do |distribution|
 | 
			
		||||
          Distribution.new(min_per_table: 2, max_per_table: 2, tables_arrangement_id: nil).tap do |distribution|
 | 
			
		||||
            distribution.tables << Set[:a, :b].to_table
 | 
			
		||||
            distribution.tables << Set[:c, :d].to_table
 | 
			
		||||
          end
 | 
			
		||||
@ -35,7 +35,7 @@ module Tables
 | 
			
		||||
 | 
			
		||||
      context 'when there are two tables with three people each' do
 | 
			
		||||
        let(:initial_solution) do
 | 
			
		||||
          Distribution.new(min_per_table: 3, max_per_table: 3).tap do |distribution|
 | 
			
		||||
          Distribution.new(min_per_table: 3, max_per_table: 3, tables_arrangement_id: nil).tap do |distribution|
 | 
			
		||||
            distribution.tables << Set[:a, :b, :c].to_table
 | 
			
		||||
            distribution.tables << Set[:d, :e, :f].to_table
 | 
			
		||||
          end
 | 
			
		||||
@ -58,7 +58,7 @@ module Tables
 | 
			
		||||
 | 
			
		||||
      context 'when there are three tables with two people each' do
 | 
			
		||||
        let(:initial_solution) do
 | 
			
		||||
          Distribution.new(min_per_table: 2, max_per_table: 2).tap do |distribution|
 | 
			
		||||
          Distribution.new(min_per_table: 2, max_per_table: 2, tables_arrangement_id: nil).tap do |distribution|
 | 
			
		||||
            distribution.tables << Set[:a, :b].to_table
 | 
			
		||||
            distribution.tables << Set[:c, :d].to_table
 | 
			
		||||
            distribution.tables << Set[:e, :f].to_table
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ module Tables
 | 
			
		||||
  RSpec.describe WheelSwap do
 | 
			
		||||
    context 'when the solution has three tables' do
 | 
			
		||||
      let(:initial_solution) do
 | 
			
		||||
        Distribution.new(min_per_table: 3, max_per_table: 3).tap do |distribution|
 | 
			
		||||
        Distribution.new(min_per_table: 3, max_per_table: 3, tables_arrangement_id: nil).tap do |distribution|
 | 
			
		||||
          distribution.tables << Set[:a, :b, :c].to_table
 | 
			
		||||
          distribution.tables << Set[:d, :e, :f].to_table
 | 
			
		||||
          distribution.tables << Set[:g, :h, :i].to_table
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user