Подтвердить что ты не робот

Внедрение ассоциаций ActiveRecord для обертки API

Недавно я написал ParseResource, который является оболочкой API Ruby для Parse.com's REST api.

Здесь используется базовое использование:

class Post < ParseResource
  fields :title, :author, :body
end
p = Post.create(:title => "Hello world", :author => "Alan", :body => "ipso lorem")

Проект довольно молодой, и функция, которую я действительно хочу реализовать, - это ассоциации. Что-то вроде этого:

class Author < ParseResource
  has_many :posts
  fields :name, :email
end
class Post < ParseResource
  belongs_to :author
  fields :title, :body
end
a = Author.create(:name => "Alan", :email => "[email protected]")
p = Post.create(:title => "Associated!", :body => "ipso lorem", :author => a)
p.author.class #=> Author
p.author.name #=> "Alan"
a.posts #=> an array of Post objects

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

4b9b3361

Ответ 1

Я нашел, используя DataMapper (http://datamapper.org), довольно легко заставить его работать практически с любым хранилищем данных. Вы можете написать адаптер, который говорит с вашим хранилищем данных, а затем использовать всю мощь DataMapper напрямую, как если бы ваши данные были в SQL. Вот ссылка, которая немного объясняет, как писать один из этих адаптеров. http://www.killswitchcollective.com/articles/55_datamapperabstractadapter_101

Ответ 2

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

Для создания таких ассоциаций, как ActiveRecord, вам нужен первичный ключ (например, Author.id) и внешний ключ (например, Post.author_id). Author.id прост - просто сделайте его идентификатором объекта Parse. Затем сохраните идентификатор автора для сообщения внутри сообщения, с ключом "author_id". Так что сторона данных.

В коде есть несколько уровней реализации. Для поиска вы пытаетесь сделать такие методы:

class Author
  def posts
    @posts ||= Post.find(:all, :id => id)
  end
end

class Post
  def author
    @author ||= Author.find(author_id)
  end
end

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

class Author
  def after_save
    super
    posts.each do |p|
      p.author_id = id
      p.save
    end
  end
end

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

a = Author.find(1)
a.name = "Joe"
a.save

Как написано after_save будет загружать существующие сообщения (он проходит через posts, который устанавливает @posts), задайте author_id для каждого сообщения (что в этом случае не нужно делать), а затем сохраните сообщения, хотя ничего не было изменили их. Более того, что, если сообщение не удается во время сохранения? В этом случае необходимы транзакции, чтобы вы могли отбросить все это и предотвратить несогласованное состояние.

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

Мой совет таков. Решите, действительно ли вам нужны гладкие и прозрачные ассоциации. Если нет, то метапрограммируйте несколько методов доступа и удобства и оставьте это на этом. В противном случае, потратьте время на изучение кода ассоциации ActiveRecord напрямую или рассмотрите DataMapper, который AFAIK предоставляет вам интерфейс с ActiveRecord, включая ассоциации, с возможность изменения хранилищ данных.