Rails 跨控制器的单例对象清除

发布于 2024-12-08 13:27:08 字数 2140 浏览 0 评论 0原文

我有一个 Rails 3 应用程序,并且正在实现配置文件完整性之类的功能。当用户登录时,应用程序应显示他/她制作“完整”个人资料的进度。也就是说,我正在使用一个单例类,该类在应用程序初始化时填充了要求。单例有一个数组@requirements。使用我的初始化程序正确填充它。当我点击 ProfileController 时,就会显示需求。但是,在第一次之后,后续ProfileController#completeness 的请求不会列出 @requirements。单例上的数组为空。我相信单例不会跨控制器请求返回相同的实例。我这里哪里出错了?

注意:此类仅保存需求,而不是特定用户实现这些需求的进度。需求很少改变,所以我想避免数据库查找。

# lib/profile_completeness.rb
require 'singleton'

class ProfileCompleteness

  include Singleton
  include Enumerable

  attr_reader :requirements

  def add_requirement(args)
    b = Requirement.new(args)
    @requirements << b
    b
  end


  def clear
    @requirements = []
  end


  def each(&block)
    @requirements.each { |r| block.call(r) }
  end


  class Requirement
    # stuff
  end

end

-

# config/initializers/profile_completeness.rb
collection = ProfileCompleteness.instance()
collection.clear

collection.add_requirement({ :attr_name => "facebook_profiles",
                             :count => 1,
                             :model_name => "User",
                             :instructions => "Add a Facebook profile" })

-

class ProfileController < ApplicationController
  def completeness
    @requirements = ProfileCompleteness.instance
  end

end

-

<!-- app/views/profile/completeness.html.erb -->
<h2>Your Profile Progress</h2>
<table>
  <%- @requirements.each do |requirement|
        complete_class = requirement.is_fulfilled_for?(current_user) ? "complete" : "incomplete" -%>

    <tr class="profile_requirement <%= complete_class -%>">

      <td>
        <%- if requirement.is_fulfilled_for?(current_user) -%>
          &#10003;
        <%- end -%>
      </td>

      <td><%= raw requirement.instructions %></td>

    </tr>
  <%- end -%>
</table>
<p><%= link_to "Profile", profile_path -%></p>

I have a Rails 3 app and am implementing a profile completeness sort of feature. When the user is logged in the app should show him/her progress on making a "complete" profile. To wit I am using a singleton class populated with requirements at app initialization. The singleton has an array, @requirements. It is correctly populated using my initializer. When I hit the ProfileController the requirements display. However, after the first, subsiquent requests to ProfileController#completeness list no @requirements. The array on the singleton is empty. I believe the singleton is not returning the same instance across controller requests. Where am I going wrong here?

Note: this class just holds rquirements, not a particular user's progress towards fulfilling them. Requirements rarely change so I want to avoid a database lookup.

# lib/profile_completeness.rb
require 'singleton'

class ProfileCompleteness

  include Singleton
  include Enumerable

  attr_reader :requirements

  def add_requirement(args)
    b = Requirement.new(args)
    @requirements << b
    b
  end


  def clear
    @requirements = []
  end


  def each(&block)
    @requirements.each { |r| block.call(r) }
  end


  class Requirement
    # stuff
  end

end

-

# config/initializers/profile_completeness.rb
collection = ProfileCompleteness.instance()
collection.clear

collection.add_requirement({ :attr_name => "facebook_profiles",
                             :count => 1,
                             :model_name => "User",
                             :instructions => "Add a Facebook profile" })

-

class ProfileController < ApplicationController
  def completeness
    @requirements = ProfileCompleteness.instance
  end

end

-

<!-- app/views/profile/completeness.html.erb -->
<h2>Your Profile Progress</h2>
<table>
  <%- @requirements.each do |requirement|
        complete_class = requirement.is_fulfilled_for?(current_user) ? "complete" : "incomplete" -%>

    <tr class="profile_requirement <%= complete_class -%>">

      <td>
        <%- if requirement.is_fulfilled_for?(current_user) -%>
          ✓
        <%- end -%>
      </td>

      <td><%= raw requirement.instructions %></td>

    </tr>
  <%- end -%>
</table>
<p><%= link_to "Profile", profile_path -%></p>

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

红衣飘飘貌似仙 2024-12-15 13:27:08

这是行不通的(多线程、不同的 Rails Worker 等),你不能指望每个请求都会进入同一个 Rails 应用程序线程。如果您的服务器崩溃,所有进度都会丢失!因此,跨请求/会话(永久)保存数据的方法是数据库。

将您的完整性跟踪器建模为模型并将其存储在数据库中。

另一个解决方案是使用 Rails 应用程序缓存。

设置键/值对:

Rails.cache.write('mykey', 'myvalue');

阅读:

cached_value = Rails.cache.read('mykey');

阅读有关 Rails Cache 的更多信息

如果如果你想要一个大数据集和快速访问的解决方案,我推荐你使用redis:

这是一篇好文章,尤其是“使用 Redis 作为您的 Rails 缓存存储”并浏览“Redis 相关宝石”部分。

重要的是键/值数据结构,我会选择像这样的键

progress:user_id:requirements = [{ ...requirement 1 hash...}, {..requirement 2 hash.. }]

this is not going to work (multi-threading, different rails workers etc.) you can't expect to land in the same rails app thread on every request. If your server crashes, all progress is lost! So the way to go for persisting data (permanently) across requests/sessions is a database.

Model your completeness tracker as a Model and store it in your database.

Another solution is to use the Rails Application Cache.

Set a Key/Value pair:

Rails.cache.write('mykey', 'myvalue');

Reading:

cached_value = Rails.cache.read('mykey');

Read more about Rails Cache

If you want a solution for big data sets and fast access, I recommend you to use redis:

Here is a good article especially the section "Using Redis As Your Rails Cache Store" and look through the section "Redis Related Gems".

The important thing is the key/value data structure, I'd go for keys like

progress:user_id:requirements = [{ ...requirement 1 hash...}, {..requirement 2 hash.. }]
笨死的猪 2024-12-15 13:27:08

您不能在 Rails 环境中使用单例,因为它们与单个进程隔离,而单个进程可能有很多,而且在开发模式下,这些类会根据每个请求故意重新初始化。

这就是为什么您会看到保存在其中的任何内容消失。

如果您必须在请求之间保留这样的数据,请使用会话工具。

总体思路是创建某种您可以通过此处引用的持久记录,例如创建一个表来存储 ProfileCompleteness 记录。然后,您可以根据每个请求重新加载它,根据需要更新它,然后保存更改。

You can't use singletons in the Rails environment because these are isolated to individual processes, of which there may be many, and further as in development mode these classes are deliberately re-initialized upon each request.

This is why you see anything saved into them disappear.

If you must persist data like this between requests use the session facility.

The general idea is to create some kind of persistent record you can reference through here, such as creating a table to store ProfileCompleteness records. Then you would re-load this on each request, update it as required, and save the changes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文