Rails: как и где добавить этот метод - PullRequest
1 голос
/ 13 марта 2012

У меня есть приложение, в котором я получаю список пользователей из определенной страны.

Я сделал это в UsersController:

@fromcanada = User.find(:all, :conditions => { :country => 'canada' })

, а затем превратили его в прицел на User модели

scope :canada, where(:country => 'Canada').order('created_at DESC')

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

module ActiveRecord
  class Base
    def self.random
      if (c = count) != 0
        find(:first, :offset =>rand(c))
      end
    end
  end
end

Однако у меня есть несколько вопросов о том, как добавить его и как работает синтаксис.

  1. Где бы я положил этот код? Прямо в User модели?

  2. Синтаксис: чтобы я не использовал код, который я не понимаю, вы можете объяснить, как работает синтаксис? Я не понимаю (c = count). Что значит count? Что делает rand(c)? Находит ли первый, начинающийся со смещения? Если rand является дорогим методом (следовательно, необходимо создать другой, более эффективный random метод), зачем использовать дорогой 'rand' в этом новом более эффективном random методе?

  3. Как я могу добавить вызов к random в моем find методе в UsersController? Как добавить его в рамки в модели?

  4. Опираясь на вопрос 3, есть ли способ получить двух или трех случайных пользователей?

Ответы [ 2 ]

0 голосов
/ 13 марта 2012
  1. Этот код внедряет новый метод в ActiveRecord :: Base.Я бы положил его в lib / ext / activerecord / base.rb.Но вы можете поместить его в любое место.

  2. count - это метод, вызываемый для self.self будет некоторым классом, наследуемым от ActiveRecord :: Base, например.Пользователь.User.count возвращает количество записей пользователя (sql: SELECT count(*) from users;).rand - это метод ruby ​​stdlib Kernel # rand .rand(c) возвращает случайное целое число в диапазоне 0...c, и c был предварительно вычислен путем вызова #count.rand не дорогой.

  3. Вы не вызываете random с помощью find, User # random - это поиск, он возвращает одну случайную запись из всех записей пользователя.В вашем контроллере вы говорите User.random и он возвращает одну случайную запись (или ноль, если пользовательских записей вообще нет).

  4. изменить метод AR :: Base :: randomвот так:

    module ActiveRecord
      class Base
        def self.random( how_many = 1 )
          if (c = count) != 0
            res = (0..how_many).inject([]) do |m,i|
              m << find(:first, :offset =>rand(c))
            end
            how_many == 1 ? res.first : res
          end
        end
      end
    end
    
    User.random(3)  # => [<User Rand1>,<User Rand2>,<User Rand3>]
    
0 голосов
/ 13 марта 2012

Я бы не стал делать это (или что-нибудь еще!) В ActiveRecord, добавив это вашему пользователю.

count считает количество элементов в вашей таблице исохраняя этот номер в c.Тогда rand(c) дает случайное целое число в интервале [0,c) (то есть 0 <= rand(c) < c).:offset работает так, как вы думаете.

rand не очень дорого, но выполнение order by random() внутри базы данных может быть очень дорогим.Рассматриваемый метод random - это просто удобный способ получить случайную запись / объект из базы данных.

Добавление его к вашему собственному пользователю будет выглядеть примерно так:

def self.random
  n = scoped.count
  scoped.offset(rand(n)).first
end

Это позволит вам связать random после нескольких областей видимости:

u = User.canadians_eh.some_other_scope.random

, но результатом random будет один пользователь, поэтому ваша цепочка на этом остановится.

Если вам нужно несколько пользователей, вам нужно будет звонить random несколько раз, пока вы не наберете нужное количество пользователей.Вы можете попробовать это:

def self.random
  n = scoped.count
  scoped.offset(rand(n))
end

us = User.canadians_eh.random.limit(3)

, чтобы получить трех случайных пользователей, но пользователи будут объединены в кластеры в любом порядке, в котором база данных окажется после ваших других областей, и это, вероятно, не то, что вам нужно.Если вы хотите три, вам лучше использовать что-то вроде этого:

# In User...
def self.random
  n = scoped.count
  scoped.offset(rand(n)).first
end

# Somewhere else...
scopes = User.canadians_eh.some_other_scope
users  = 3.times.each_with_object([]) do |_, users|
  users << scopes.random
  scopes = scopes.where('id != :latest', :latest => users.last.id)
end

Вы просто захватите случайного пользователя, обновите цепочку областей действия, чтобы исключить их, и повторяйте, пока не закончите.Вы, конечно, захотите сначала убедиться, что у вас было три пользователя.

Возможно, вы захотите вывести порядок из своей области действия canada: одна область действия, одна задача.

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