Вопросы архитектуры Rails - PullRequest
4 голосов
/ 01 мая 2010

Я создаю сайт Rails, который, помимо прочего, позволяет пользователям создавать свои собственные хранилища рецептов. Рецепты вводятся либо вручную, либо по ссылке на другой сайт (например, epicurious , cooks.com и т. Д.). Я пишу сценарии, которые будут вычищать рецепт с этих сайтов по ссылке от пользователя, и пока (несмотря на юридические вопросы) эта часть не доставляет мне никаких хлопот.

Однако я не уверен, куда поместить код, который я пишу для этих скриптов-скребков. Моей первой мыслью было включить ее в модель рецептов, но она кажется слишком сложной, чтобы идти туда; библиотека или помощник были бы более подходящими?

Кроме того, как я уже упоминал, я создаю несколько разных скребков для разных сайтов о еде. Мне кажется, что элегантный способ сделать это состоит в том, чтобы определить интерфейс (или абстрактный базовый класс), который определяет набор методов для создания объекта рецепта по заданной ссылке, но я не уверен, что будет лучшим подходом здесь тоже. Как я могу построить эти ОО-отношения и куда должен идти код?

Ответы [ 7 ]

2 голосов
/ 01 мая 2010

У вас есть две стороны этой вещи, которые очевидны. Во-первых, как вы будете хранить рецепты, которые будут моделями. Очевидно, что Модели не будут очищать другие сайты, так как на них лежит одна обязанность: хранить действительные данные. Контроллеры, которые будут инициировать процесс очистки и хранения, также не должны содержать код очистки (хотя они будут его вызывать).

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

Вы положите свои скребки - и вот неудачный ответ - куда хотите. lib хорошо, но если вы хотите создать плагин, это может быть и неплохой идеей. См. Мой вопрос здесь - с потрясающим ответом известного парня из Rails Иегуды Каца - для некоторых других идей, но в целом: нет правильного ответа. Но есть и неправильные.

1 голос
/ 02 мая 2010

Попробуйте сосредоточиться на том, чтобы все работало в первую очередь, прежде чем перемещать его в гем / плагин. Кроме того, забудьте об интерфейсе / абстрактном классе - просто напишите код, который делает это. Единственная вещь, которую должна знать ваша модель, - это удаленный рецепт и какой URL. Вы можете поместить весь код очистки в приложение / скребки. Вот пример схемы реализации:

class RecipePage
  def new(url)
    @url = url
    @parser = get_parser
  end

  def get_attributes
    raise "trying to scrape unknown site" unless @parser        
    @parser.recipe_attributes(get_html)
  end

  private       
  def get_html
    #this uses your favorite http library to get html from the @url
  end

  def get_parser(url)
    #this matches url to your class, ie returns domain camelized, or nil if you are not handling particular site yet
    return EpicurusComParser
  end
end

class EpicurusComParser

  def self.recipe_attributes(html)
    # this does the hard job of querying html and moving
    # all the code to get title, text, image of recipe and return hash
    {
      :title => "recipe title",
      :text => "recipe text",
      :image => "recipe_image_url",
    }
  end
end

тогда в вашей модели

class Recipe
  after_create :scrape_recipe, :if => :recipe_url

  private 

  def scrape_recipe
    # do that in background - ie in DelayedJob
    recipe_page = RecipePage.new(self.recipe_url)
    self.update_attributes(recipe_page.get_attributes.merge(:scraped => true))
  end
end

Затем вы можете создать больше парсера, т.е. CookComParser и т. Д.

1 голос
/ 01 мая 2010

Скребковый движок должен быть отдельным плагином или плагином для драгоценных камней. Для грязных и быстрых, вы можете поместить его в lib. Это обычное соглашение в любом случае. Вероятно, он должен реализовать фабричный класс, который создает различные типы скребков в зависимости от URL, поэтому для использования клиентом это будет так просто:

Scraper.scrape(url)

Кроме того, если это длительная задача, вы можете рассмотреть возможность использования заданий resque или отложенных заданий для разгрузки задачи на отдельные процессы.

0 голосов
/ 01 мая 2010

Я бы создал в lib папку с именем scrapers. Затем в этой папке создайте один файл для каждого скребка. Вызовите их как epicurious, cooks и т. Д. Затем вы можете определить базовый класс скребков, который содержит любые общие методы, которые будут общими для всех скребков. Похоже на следующее

Библиотека / скребки / base.rb

class Scrapers::base
  def shared_1()
  end
  def shared_2()
  end
  def must_implement1
    raise NotImplemented
  end
  def must_implement2
    raise NotImplemented
  end
end

Библиотека / скребки / epicurious.rb

Class Epicurious < Base
  def must_implement1
  end
  def must_implement2
  end
end

Затем вызовите соответствующий класс из вашего контроллера, используя Scrapers::Epicurious.new, или вызовите метод класса внутри Scrapers::Base, который вызывает соответствующую реализацию на основе переданного аргумента.

0 голосов
/ 01 мая 2010

Я бы поставил задачу rake, чтобы очистить сайт и создать новую задачу rake. Как только это сработает, я буду использовать фоновый процессор или задание cron для запуска задачи rake.

0 голосов
/ 01 мая 2010

Не все в приложении / модели должно быть моделью ActiveRecord. Поскольку они напрямую относятся к бизнес-логике вашего приложения, они принадлежат каталогу приложения, а не каталогу lib. Они также не являются ни контролерами, ни представлениями, ни помощниками (существуют помощники, помогающие только представлениям и представлениям). Итак, они принадлежат в приложении / модели. Я бы удостоверился, что пространство имен их, просто для организационных целей, в app / models / scrapers или что-то в этом роде.

0 голосов
/ 01 мая 2010

Часто служебные классы, которые на самом деле не являются частью дизайна MVC, помещаются в папку lib. Я также видел, как люди помещали их в папку models, но lib действительно "правильное" место.

Затем вы можете при необходимости создать экземпляр скребка для рецептов в контроллере, подавая данные в модель.

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