2011-10-09 4 views
11

Ho recentemente scritto ParseResource, che è un wrapper API Ruby per Parse.com's API REST.Implementazione di associazioni ActiveRecord-like per un wrapper API

Ecco un qualche uso di base:

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

Il progetto è abbastanza giovane, e una caratteristica voglio davvero implementare è associazioni. Qualcosa di simile a questo:

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 

Mi piacerebbe eventuali consigli, puntatori, e le insidie ​​da chi ha implementato qualcosa di simile come pure da chiunque abbia una conoscenza di API REST di Parse.

+0

Hai sentito parlare di [nulldb] (https://github.com/nulldb/nulldb)? –

risposta

0

Sembra che Parse funzioni memorizzando gli oggetti come un hash di coppie chiave-valore. Quindi in pratica hai un id e poi un hash con cui puoi lasciare correre la tua immaginazione.

Per associare associazioni come ActiveRecord è necessaria una chiave primaria (ad es. Author.id) e una chiave esterna (ad es. Post.author_id). Author.id è semplice: basta renderlo l'id dell'oggetto Parse. Quindi archivia l'id dell'autore per un post all'interno del post, digitato da 'author_id'. Quindi questo è il lato dei dati.

Nel codice ci sono diversi livelli di implementazione da considerare. Per il recupero si sta puntando a rendere i metodi come questo:

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

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

che non è troppo difficile e può essere fatto in molti modi, ad esempio con metaprogrammazione. Più duro è il salvataggio. Quello che stai puntando, almeno dal lato Autore, è qualcosa di simile:

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

O piuttosto dovrei dire che è quello che si potrebbe essere puntando a seconda dello scenario. Una delle trappole nell'implementazione delle associazioni è decidere quando fare cose. Non vuoi complicarti la vita, ma non vuoi impazzire con le chiamate API. Considerate semplicemente aggiornando il nome di un autore:

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

Come scritto after_save caricherà i messaggi esistenti (si passa attraverso posts che definisce @posts), impostare author_id su ogni post (che non deve essere fatto in questo caso), e quindi salva i post anche se nulla li ha cambiati. Inoltre cosa succede se un post fallisce durante il salvataggio? In tal caso sono necessarie transazioni in modo da poter eseguire il rollback dell'intero processo e impedire uno stato incoerente.

È possibile vedere nell'ActiveRecord code c'è un sacco di logica che circonda il problema di come gestire i bambini quando un genitore viene salvato. Il risultato sono associazioni chiare e trasparenti, ma tutti i tipi di altre cose vengono coinvolte; proxies, association classes, ecc.

Il mio consiglio è questo. Decidi se hai davvero bisogno di associazioni chiare e trasparenti. In caso contrario, metaprogrammo alcuni metodi di accesso e di convenienza e lascia perdere. Altrimenti, dedicare del tempo a studiare direttamente il codice di associazione ActiveRecord o considerare DataMapper che, AFAIK, offre un'interfaccia simile a ActiveRecord, incluse le associazioni, con la possibilità di modificare gli archivi di dati.