Ciao a tutti, mi servirebbe una dritta su come implementare
l'autorefresh di una lista di posts.
L'idea e', che se un utente sta guardando una pagina che lista i suoi
post e quelli del suo gruppo, dovrebbe vedere arrivare i nuovi post
eseguiti dagli altri in tempo reale, cioe dinamicamente senza fare il
reload della pagina.
Non ho molta dimestichezza con Ajax, ma dovrebbe avere a che fare con i
metodi di ActionView::Helpers::PrototypeHelper:
* build_callbacks
* build_observer
* button_to_remote
* evaluate_remote_response
* form_remote_for
* form_remote_tag
* link_to_remote
* method_option_to_s
* observe_field
* observe_form
* options_for_ajax
* periodically_call_remote
* remote_form_for
* remote_function
* submit_to_remote
* update_page
* update_page_tag
la pagina che devo refreshare e' l'index dell'home controller:
--------------------------------------------------------------
root@webby2066:/var/rails/flitter# cat ./app/views/home/index.html.erb
...
<%= render :partial => "flits_list", :locals => { :flits => @flits }%>
--------------------------------------------------------------
che sostanzialmente chiama un parziale che implementa ogni singolo post:
--------------------------------------------------------------
root@webby2066:/var/rails/flitter# cat
./app/views/home/_flits_list.html.erb
<ul id="flits_list">
<% flits.each do |flit| %>
<li<% if flits.first == flit %> class="first"<% end %>>
<%= image_tag flit.user.gravatar_url %>
<div class="flit_message_container">
<%= link_to flit.user.username,
user_flits_path(flit.user.username) %>
<%= h flit.message %>
<div class="time_ago">
<%= distance_of_time_in_words_to_now(flit.created_at) %> ago
</div>
</div>
<div class="clear"></div>
</li>
<% end %>
</ul>
--------------------------------------------------------------
in un controller diverso da home, ho un "evento trigger" che e'
flit.save! e dovrebbe scatenare il refresh di home/index.html.erb
Non mi e' del tutto chiaro pero' se c'e' un metodo tra quelli elencati
prima, idoneo a rilevare l'evento trigger flit.save! che in sostanza
salva un nuovo post nel DB.
Ringrazio in anticipo per ogni suggerimento
Ciao Luca
on 2010-03-10 10:51
on 2010-03-10 11:17
Il 10 marzo 2010 10.51, Luca G. Soave <luca.soave@gmail.com> ha scritto: > metodi di ActionView::Helpers::PrototypeHelper: > * observe_form > * options_for_ajax > * periodically_call_remote > * remote_form_for > * remote_function > * submit_to_remote > * update_page > * update_page_tag Ciao, premetto che, dopo un periodo iniziale irto di difficoltà, ho abbandonato del tutto rjs e i PrototypeHelper (e veramente anche Prototype, perché scoprire jQuery è stato un po' come quando cerchi di lavorare a tentoni nel buio e a un certo punto accendono la luce). Quello che ti serve è periodically_call_remote; la cosa più pulita è avere un metodo di un qualche controller che ti restituisca un json contenente una variabile boolean che ti dica se è necessario il refresh oppure no, in modo che la risposta sia sempre rapidissima. Poi, quando ti serve fare il refresh, potresti fare una nuova chiamata all'index di home (ad esempio con remote_function), da cui farti restituire solo il partial da rimpiazzare (la funzione potrebbe anche usare rjs, volendo...). La questione è se vuoi fare tutto con gli helper di rails o se vuoi fare tutto con javascript. pietro
on 2010-03-10 13:01
puoi anche farti spedire direttamente i nuovi post in html e aggiungerli
alla lista oppure ricaricare semplicemente la lista:
new Ajax.PeriodicalUpdater('items', '/items', {
method: 'get', frequency: 3, decay: 2
});
> Poi, quando ti serve fare il refresh, potresti fare una nuova chiamata
> all'index di home (ad esempio con remote_function), da cui farti
> restituire solo il partial da rimpiazzare (la funzione potrebbe anche
> usare rjs, volendo...).
on 2010-03-10 13:28
Il 10 marzo 2010 13.01, Alessandro Scolavino <scolas@gmail.com> ha scritto: > puoi anche farti spedire direttamente i nuovi post in html e aggiungerli > alla lista oppure ricaricare semplicemente la lista: > > new Ajax.PeriodicalUpdater('items', '/items', { > method: 'get', frequency: 3, decay: 2 > }); > Sì, è anche questa una soluzione ma, avendola provata in passato, non fa un bell'effetto, specie se la lista non è leggerissima. Magari prova, in effetti così è molto semplice, ma se vedi che lo sfarfallìo è percettibile, riconsidera l'idea di modificare il dom solo quando serve. pietro
on 2010-03-10 17:04
Grazie a Pietro e Alessandro x le info. Mi pare di capire che sia periodically_call_remote che Ajax.PeriodicalUpdater siano metodi "attivi" di polling, per cosi' dire. Non esiste, che voi sappiate, un Observer di ActiveRecord in grado di mandare una callback per una data commit sul DB (.save)? Oppure una cosa tipo after_create() di ActiveRecord::Callbacks che scatena un evento ajax? Sto solo immaginando, perche' non ho esperienza in questi metodi. Il problema e' che dentro il partial, ./app/views/home/_flits_list.html.erb c'e diversa roba, tra qui una query al db <%= h flit.message %> che verrebbe eseguita nel mio caso ogni 2 secondi x tutto il periodo in cui il browser utente rimane aperto sulla pagina, moltiplicato x tutti gli utenti collegati nell'unita' di tempo ... poco scalabile credo. Sto solo speculando, ma mi piacerebbe innescare un certo numero di soluzioni percorribili e di confronti. Grazie a tutti Luca G.S.
on 2010-03-10 18:04
On 10/03/2010 17:04, Luca G. Soave wrote:
>
in teoria, non ha molto senso mettere mettere una logica che implica
AJAX dentro un modello, altrimenti non sarebbe un pattern MVC :P
in caso puoi farlo nel controller: riprendendo l'esempio di after_create
di ActiveRecord, vorrà dire che nella action 'create' farai qualcosa
dopo aver salvato il modello.
per quanto riguarda il partial, puoi usare una chiamata AJAX
periodicamente, come ti hanno già consigliato. per quanto riguarda la
scalabilità, di sicuro puoi usare il "fragment caching". Dovrai
impostare un observer sul modello per aggiornare il contenuto del
partial *cachato*.
se poi la scalabilità è davvero un problema urgente, potresti valutare
qualcosa tipo Redis (un db chiave/valore che fa anche molte altre cose:
http://code.google.com/p/redis/) + redis_store (plugin Rails per fare il
caching usando Redis: http://github.com/jodosha/redis-store). Tra le
altre cose, entrambe i progetti sono scritti da programmatori italiani
:-)
ciao,
A.
on 2010-03-10 18:35
Luca G. Soave wrote: > Grazie a Pietro e Alessandro x le info. > > Mi pare di capire che sia periodically_call_remote che > Ajax.PeriodicalUpdater siano metodi "attivi" di polling, per cosi' dire. > > Non esiste, che voi sappiate, un Observer di ActiveRecord in grado di > mandare una callback per una data commit sul DB (.save)? Oppure una cosa > tipo after_create() di ActiveRecord::Callbacks che scatena un evento > ajax? Direi che si tratta di polling, un aggiornamento sul server come può avere relazioni con i clients che interrogano il database? Impostando un intervallo di check moderato, non credo si appesantisca troppo il server
on 2010-03-11 00:39
Il 10 marzo 2010 17.04, Luca G. Soave <luca.soave@gmail.com> ha scritto: > Grazie a Pietro e Alessandro x le info. > > Mi pare di capire che sia periodically_call_remote che > Ajax.PeriodicalUpdater siano metodi "attivi" di polling, per cosi' dire. > > Non esiste, che voi sappiate, un Observer di ActiveRecord in grado di > mandare una callback per una data commit sul DB (.save)? Oppure una cosa > tipo after_create() di ActiveRecord::Callbacks che scatena un evento > ajax? Come ti hanno già detto, no, non si può. Alcune applicazioni web implementano eventi push tenendo aperto un socket (java o flash) ma, a parte il fatto di avere una dipendenza in più, non credere che, per il server, mantenere (potenzialmente) migliaia di connessioni aperte sia un carico trascurabile. Altre applicazioni simulano un evento push "trattenendo" le risposte a eventi poll, ma è una cosa ancora più complicata e pesante per il server. La cosa più semplice e leggera è appunto quella suggeritati (vedi sotto). > Sto solo immaginando, perche' non ho esperienza in questi metodi. > > Il problema e' che dentro il partial, > ./app/views/home/_flits_list.html.erb c'e diversa roba, tra qui una > query al db <%= h flit.message %> che verrebbe eseguita nel mio caso > ogni 2 secondi x tutto il periodo in cui il browser utente rimane aperto > sulla pagina, moltiplicato x tutti gli utenti collegati nell'unita' di > tempo ... poco scalabile credo. Ecco, qui c'è un problema grossissimo, indipendentemente da ajax e quant'altro: le view *non devono* fare query. Le view devono solo mostrare, nient'altro. Poi, c'è un altro problema, e cioè che il partial è complesso. E anche stavolta, questo è un problema indipendentemente da ajax. Semplicemente, scomponi il partial in pezzi più piccoli, e uno di questi pezzi si limiterà a mostrare la lista. > una query che verrebbe eseguita nel mio caso ogni 2 secondi Due secondi mi sembra un'esagerazione, in genere cinque (o anche dieci) secondi vanno più che bene; sei proprio sicuro che sia indispensabile? Molte applicazioni mostrano una buona reattività pur tenendosi appunto intorno ai dieci secondi e oltre. Se ripetere tante volte la stessa query è davvero un problema, ci sono tecnologie ad hoc per la persistenza dello stato (che in questo caso è nuovi post sì / nuovi post no), come ti ha suggerito Andrea, che però ha senso indagare solo se è vera almeno una delle due ipotesi: a) hai già centinaia di utenti prenotati che terranno aperto il browser sulla tua applicazione (ma questo probabilmente significa che hai bisogno di una certa infrastruttura, ad esempio una banda "seria" e un server come si deve); b) hai voglia di imparare a usare redis :) Tra l'altro, come dicevo nell'email precedente, usare una chiamata ajax che ridisegni migliaia di volte la stessa identica lista è uno spreco enorme, ed è un carico sia per il server (che finché non finisce di trasmettere una risposta resta impegnato, ed ha così minore capacità di risposta per gli altri) che per il client, e l'utente si ritroverebbe un computer inchiodato da un browser che ridisegna lo stesso pezzo di pagina; anche l'effetto visivo non è il massimo, perché lo sfarfallìo si vede. Ma questo non è necessario: quando mostri la pagina passi al javascript l'ora corrente (lato server), così il javascript può interrogare il server chiedendo: è cambiato niente dall'ora X? Se la risposta è no, non fai niente; se è sì, si ridisegna la pagina. pietro
on 2010-03-11 11:01
Ok Pietro, grazie per i numerosi consigli e le opzioni che mi hai prospettato. Mi sembra di avere un quadro un po piu' completo sullo "stato dell'arte". Provo a distillare il mio compromesso sugli elementi forniti. Ciao Luca
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.