Ruby on Rails - пользовательский ввод для сортировки - PullRequest
0 голосов
/ 12 июня 2018

Для изучения Ruby on Rails я пишу веб-приложение, которое будет использоваться для сортировки команд в турнире с учетом их производительности на сегодняшний день.

Сложность в том, что я хочу, чтобы каждый организатор турнира (системный пользователь)) иметь возможность использовать различные метрики в произвольном порядке.

Выражается как SQL (мой фон). Я хочу, чтобы пользователь 1 мог выбирать:

ORDER BY
   METRIC1
  ,METRIC2
  ,METRIC3

В то время как пользователь 2 мог выбирать:

ORDER BY
   METRIC2
  ,METRIC3
  ,METRIC1

Как бы я принял этот пользовательский ввод и использовал его для создания запроса к таблице Team?


Редактировать 1 Не упомянул (извините), чтоСами метрики рассчитываются на лету.В настоящее время они являются экземплярами методов (например, @team.metric1 и т. Д.).Все безуспешные попытки, которые я предпринял до сих пор, включают в себя попытку преобразования пользовательских строк в имена методов, которые кажутся просто неправильными (и я не смог заставить его работать).

Редактировать 2 пример кода в groups_controller.rb:

class Team < ApplicationRecord
  belongs_to :tournament
  has_many :matches

  def score_for
    matches.sum(:score_for)
  end
  def score_diff
    matches.sum(:score_for) - matches.sum(:score_against)
  end
end

Ответы [ 2 ]

0 голосов
/ 09 июля 2018

Рабочее, но, вероятно, ужасное решение:

class Tournament < ApplicationRecord

  has_many :teams

  serialize :tiebreaker, Array

  TIEBREAKER_WHITELIST = %w[score opponent_score possession].freeze

  def sorted_teams
    list = teams.shuffle
    (TIEBREAKER_WHITELIST & tiebreaker).reverse.each do |metric|
      list = list.sort_by { |team| [team.send(metric), list.find_index(team)] }
    end
    list.reverse
  end
end

Каждый tournament имеет множество teams.Экземпляр tournament имеет сериализованное поле с именем tiebreaker.Он содержит массив строк, например, ["score", "possession"], где каждая строка соответствует имени метода открытого экземпляра в team.Каждый из этих методов возвращает число.

Поле tiebreaker находится в порядке убывания приоритета, поэтому для приведенного выше примера я бы ожидал, что possession повлияет на сортировку только для команд с равным score.

list = teams.shuffle - это случайный список для начала, в случае если команды связаны для всех следующих тай-брейков.

(TIEBREAKER_WHITELIST & tiebreaker) - это возвращает только строки, которые появляются как в поле tiebreaker, так и в константе белого списка для защиты от конечных пользователей, использующих произвольные методы.

.reverse.each do |metric| - это инвертирует массив метриктак что список сначала сортируется по метрике с наименьшим приоритетом.

[team.send(metric), list.find_index(team)] - это сортировка для каждой метрики.send превращает строку в вызов метода.Я обнаружил, что find_index было необходимо для сохранения порядка сортировки из предыдущих сортировок.т. е. если бы я сначала отсортировал по possession, это сохранило бы порядок для команд с одинаковым score.

list.reverse - переверните список и верните его.Это было потому, что я хотел, чтобы в моем списке сначала были команды с более высокой оценкой / владением и sort_by сортировки по возрастанию.

Я хотел, чтобы некоторые метрики сортировались по возрастанию (opponent_score), а другие - по убыванию (score), поэтому я обработал этов соответствующих методах, например, возвращая отрицательные значения для opponent_score.

Я не совсем доволен решением, как есть, но, похоже, оно работает!

0 голосов
/ 12 июня 2018

ActiveRecord позволяет передавать несколько аргументов методу order.Таким образом, вы можете сделать что-то вроде:

Team.order(:metric2, :metric3, metric1: :desc)

Другой вариант - вы также можете использовать ActiveRecord для динамического построения запроса.Запросы ActiveRecord обрабатываются лениво, поэтому SQL не будет выполняться до тех пор, пока вы не вызовете операцию, требующую загрузки записей.

Например, вы можете создать область в Team следующим образом:

class Team < ApplicationRecord

  scope :custom_order, lambda { |sorting_order|
    sorting_order.each do |metric|
      order(metric)
    end
  }
end

Затем вам нужно будет просто ввести набор атрибутов в том порядке, в котором вы хотите, чтобы выполнялся порядок по пунктам.Например:

Team.custom_order([:metric2, :metric3, :metric1])

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...