Перечислять лениво, пока блок не станет ложным - PullRequest
0 голосов
/ 21 сентября 2018

Я написал класс Enumerable для плавного и ленивого извлечения всех страниц запроса API.

class Pager
  include Enumerable

  def initialize(&fetch_next_page)
    @fetch_next_page = fetch_next_page
    reset
  end

  def reset
    @page_number = 0
  end

  private def next_page
    @page_number += 1

    return @fetch_next_page.call(@page_number)
  end

  def each(&block)
    if block_given?
      while resp = next_page
        resp.each(&block)
      end
      reset
    else
      to_enum(:each)
    end
  end
end

Вот пример того, как его можно использовать.

pager = Pager.new do |page_number|
  response = fetch_page( page: page_number, **some_options )

  response.page <= response.total_pages ? response.stuff : false
end

Но я пришел к выводу, что все, что это делает, - это выполнение блока, который возвращает Enumerable до его false, и это выравнивает Enumerables.

pager = Pager.new { |page_number|
  page_number <= 3 ? 1.upto(page_number) : false
}

# [1, 1, 2, 1, 2, 3]
puts pager.to_a.inspect

Есть ли более простой способ сделать это?Я близко подошел к Enumerator, но не могу заставить работать выравнивание.

def paginate(&fetch_next)
  return Enumerator.new do |yielder|
    page_number = 1
    while ret = fetch_next.call(page_number)
      yielder.yield(*ret)
      page_number += 1
    end
  end
end

pager = paginate { |page_number|
  page_number <= 3 ? 1.upto(page_number) : false
}

# [1, [1, 2], [1, 2, 3]]
puts pager.to_a.inspect

1 Ответ

0 голосов
/ 21 сентября 2018

Причина, по которой выходные данные перечислителя не верны, действительно связана с оператором splat.

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

{ |page_number| page_number <= 3 ? 1.upto(page_number) : false }

Это приведет к 3 доходам.Первый с аргументами 1, второй с аргументами 1, 2 и третий с аргументами 1, 2, 3.Если вы хотите использовать их как отдельные выходы, вам нужно изменить следующее:

yielder.yield(*ret)

# should be changed to

ret.each { |e| yielder.yield e }
# or
ret.each { |e| yielder << e }
# depending on your preference

pager.to_a
#=> [1, 1, 2, 1, 2, 3]
...