【发布时间】:2014-11-24 19:29:30
【问题描述】:
虽然我可能已经为这个网站上的每个问题添加了书签,但我几乎从不发布我自己的问题,因为我害怕一个尖刻的回答。所以,如果我忽略了一些完全明显的事情或者只是以错误的方式处理这个问题,请提前道歉,但是我已经到了互联网的尽头,我的脑袋正在转动,所以我呼吁 Rails 社区的增援。
如果有任何关于在我的 Rails 4 应用程序中不断刷新数据表中行的好方法的建议,我将不胜感激。
我最初试图解决的问题是我的同事需要同时访问每月生成的 Excel 文档。我非常不喜欢 Excel,所以我在 Rails 4 应用程序中创建了这些文档的更好版本,我的同事现在可以同时编辑它。
我目前正在尝试解决的问题是他们需要实时查看彼此的更新。表格单元格由我的同事(最多 40 个同时用户)在我公司的内部 web 应用程序上更新。 (Google 电子表格不是一个可行的解决方案,因为数据是相关的,并且在 mySql 数据库中维护得更好。)
这是我的第一个 Rails 应用程序之一,但自 Hello World 以来它已经走过了漫长的道路。在当前状态下,我使用 Rails 4.1.6 和 JQuery dataTables 结合 best_in_place gem 进行就地编辑。
该应用程序目前运行良好,但我不断收到有关在同一行的其他单元格中输入新数据时依赖单元格未更新的电子邮件(我的同事习惯于他们的数据采用 Excel 格式并立即看到一个单元格更新基于任何公式)。我厌倦了(并且真的很尴尬)告诉他们“刷新页面”来解决他们的问题。此外,我希望每个人都能实时看到更新。
我已经尝试过sync gem(使用 Faye 和 Thin),但我终其一生都无法让它在任何环境中工作,而且实际上只有两个教程(sync_example 和 RailsConf video with no code),两者都没有解释如何在生产环境中实现它。
我目前通过TenderLove's Is it live? blog post 的 ActionController::Live(使用 Puma 和 Nginx)使用服务器发送的事件/事件源。这种方法的问题是,在客户端断开连接或导航到另一个页面后,我无法关闭连接,因此我的所有数据库连接都被用完,然后数据库连接被阻止。 This seems to be a known issue for many others 但我想知道我是不是完全走错了路。
不断循环会不会是个问题?如果我把它改回n.times,我会不会失去连接并回到第一格??
这是我的代码。 #products 是我的 jQuery 数据表,每一行都以具有唯一 ID 的部分呈现。
在我的产品/展示视图中:
<tbody>
<% @products.each do |product| %>
<%= render partial: '/products/product', :collection => @products %>
<% end %>
</tbody>
</table>
. . .
<script type='text/javascript'>
jQuery(document).ready(function() {
setTimeout(function() {
var source = new EventSource('/application/events');
source.addEventListener('refresh_product_row', function(event) {
var id = JSON.parse(event.data).id;
$.getScript('/products/show.js?id=' + id);
$('#products').dataTable();
});
}, 1);
});
</script>
show.js.erb
$('#products tbody #product_row_<%= @product.id %>').hide();
$('#products tbody #product_row_<%= @product.id %>').replaceWith('<%= escape_javascript(raw render(:partial => "products/product", :object => @product))%>');
$('#products tbody #product_row_<%= @product.id %>').fadeIn('slow');
// Re-activating Best In Place
$('.best_in_place').best_in_place();
在我的应用程序控制器中:
def events
unless ENV['RAILS_ENV'] == 'development'
# SSE expects the `text/event-stream` content type
response.headers['Content-Type'] = 'text/event-stream'
sse = Reloader::SSE.new(response.stream)
start = Time.zone.now
begin
# 10.times do
loop do
Product.uncached do
Product.where('updated_at > ?', start).each do |product|
# data will be streamed to the client every time we call the write method.
sse.write({ :id => product.id }, :event => 'refresh_product_row')
sleep 0.0001
start = product.updated_at
end
end
sleep 1
end
render nothing: true
rescue IOError
# When the client disconnects, we'll get an IOError on write
logger.info "Client disconnected. Stream closed"
ensure
sse.close
end
end
end
在 /lib/reloader/sse.rb 中
require 'json'
module Reloader
class SSE
def initialize io
@io = io
end
def write object, options = {}
options.each do |k,v|
@io.write "#{k}: #{v}\n"
end
@io.write "data: #{JSON.dump(object)}\n\n"
end
def close
@io.close
end
end
end
如果您需要更多信息,请告诉我。谢谢!!
【问题讨论】:
标签: ruby-on-rails-4 synchronization datatables server-sent-events best-in-place