Ruby / Rails - цепочка неизвестного числа вызовов методов - PullRequest
0 голосов
/ 07 января 2019

Я хотел бы динамически создавать (потенциально сложные) запросы Active Record из двухмерного массива, передаваемого в метод в качестве аргумента. Другими словами, я хотел бы взять это:

arr = [
    ['join', :comments],
    ['where', :author => 'Bob']
]

И создайте эквивалент этого:

Articles.join(:comments).where(:author => 'Bob')

Один из способов сделать это:

Articles.send(*arr[0]).send(*arr[1])

Но что, если arr содержит 3 вложенных массива, или 4, или 5? Весьма неопознанным способом было бы сделать это:

case arr.length
    when 1
        Articles.send(*arr[0])
    when 2
        Articles.send(*arr[0]).send(*arr[1])
    when 3
        Articles.send(*arr[0]).send(*arr[1]).send(*arr[2])
    # etc.
end

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

Ответы [ 3 ]

0 голосов
/ 07 января 2019

Одним удобным способом было бы использовать хеш вместо 2D-массива.

Примерно так

query = {
  join: [:comments],
  where: {:author => 'Bob'}
}

Этот подход не очень сложен, и вам не нужно беспокоиться, если ключ не указан или является пустым

Article.joins(query[:join]).where(query[:where])
#=> "SELECT  `articles`.* FROM `articles` INNER JOIN `comments` ON `comments`.`article_id` = `articles`.`id` WHERE `articles`.`author` = 'Bob'"

Если ключи пусты или отсутствуют вообще

query = {
  join: []
}

Article.joins(query[:join]).where(query[:where])
#=> "SELECT  `articles`.* FROM `articles`"

Или вложенный

query = {
  join: [:comments],
  where: {:author => 'Bob', comments: {author: 'Joe'}}
}
#=> "SELECT  `articles`.* FROM `articles` INNER JOIN `comments` ON `comments`.`article_id` = `articles`.`id` WHERE `articles`.`author` = 'Bob' AND `comments`.`author` = 'Joe'"
0 голосов
/ 08 января 2019
arr.inject(Articles){|articles, args| articles.send(*args)}
0 голосов
/ 07 января 2019

Я создал следующий запрос, который будет работать для любой модели и связанного массива запросов.

def chain_queries_on(klass, arr)
  arr.inject(klass) do |relation, query|
    begin
      relation.send(query[0], *query[1..-1])
    rescue
      break;
    end
  end
end

Я проверил в местном масштабе для следующего теста,

arr = [['where', {id: [1,2]}], ['where', {first_name: 'Shobiz'}]]

chain_queries_on(Article, arr)

Запрос запущен, как показано ниже, чтобы вернуть правильный вывод,

Article Load (0.9ms)  SELECT `article`.* FROM `article` WHERE `article`.`id` IN (1, 2) AND `article`.`first_name` = 'Shobiz'  ORDER BY created_at desc

Примечание-1: несколько заметных случаев

  1. для пустых arr, он вернет class, который мы передали в качестве первого аргумента в методе.

  2. Возвращает nil в случае ошибки. Ошибка может произойти, если мы используем pluck, который будет возвращать массив (вывод, который не является цепочечным) или если мы не передадим класс в качестве первого параметра и т. Д.

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

Примечание-2: улучшения

Вы можете определить этот метод как метод класса для Object класса также с одним аргументом (т. Е. Массивом) и вызывать непосредственно для класса, как,

# renamed to make concise
Article.chain_queries(arr)
User.chain_queries(arr)

Внутри метода, используйте self вместо klass

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