如何在Rails 4中隐藏用户在一段时间后对资源执行操作的能力?



对于我的项目表,我有一个列state,它接受字符串值:"open", "in production", "pending approval"one_answers"completed"。触发这些状态之间更改的操作是发送给用户和来自用户的关于项目或与消息关联的操作的消息。当对象项目从"打开"变为"在生产中"时,在用户收件箱中,用户在与项目相关的对话中会出现一个按钮,上面写着"Drop project"。此外,我还可以将功能连接起来。单击此按钮将解除您与该项目的关联。

我的问题:我怎么能让这个按钮隐藏自己或消失后5天的项目的状态,已经从"打开"到"在生产中"?换句话说,我希望用户有机会在一定的时间内将自己从项目中分离出来。在那之后,他被这个项目困住了,并被鼓励去完成它。

还有,我怎么用rspec测试这个呢?这是严格的集成测试,还是我也可以用单元测试来测试?

我知道这个帖子(Rails 3检查属性是否改变),但它并没有帮助我找出解决我的问题的方法。不过,我觉得它可以作为一种支持。

下面是我的数据库模式和模型:

ActiveRecord::Schema.define(version: 20140514191454) do
  create_table "conversations", force: true do |t|
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer  "user_id"
  end
  create_table "organizations", force: true do |t|
    t.string   "name"
    t.datetime "ruling_year"
    t.text     "mission_statement"
    t.string   "guidestar_membership"
    t.string   "ein"
    t.string   "street1"
    t.string   "street2"
    t.string   "city"
    t.integer  "state_id"
    t.string   "zip"
    t.integer  "ntee_major_category_id"
    t.string   "funding_method"
    t.integer  "user_id"
    t.string   "cause"
  end
  create_table "private_messages", force: true do |t|
    t.integer  "sender_id"
    t.integer  "recipient_id"
    t.string   "subject"
    t.text     "body"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer  "project_id"
    t.integer  "conversation_id"
  end
  create_table "project_users", force: true do |t|
    t.integer  "user_id"
    t.integer  "project_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end
  create_table "projects", force: true do |t|
    t.string   "title"
    t.text     "description"
    t.string   "skills"
    t.string   "causes"
    t.datetime "deadline"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.integer  "user_id"
    t.integer  "organization_id"
    t.integer  "estimated_hours"
    t.string   "state"
  end
  create_table "user_conversations", force: true do |t|
    t.integer  "user_id"
    t.integer  "conversation_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end
  create_table "users", force: true do |t|
    t.integer  "organization_id"
    t.string   "first_name"
    t.string   "last_name"
    t.string   "email"
    t.string   "interests"
    t.string   "skills"
    t.string   "street1"
    t.string   "street2"
    t.string   "city"
    t.integer  "state_id"
    t.integer  "phone_number"
    t.string   "zip"
    t.boolean  "organization_administrator"
    t.boolean  "organization_staff"
    t.boolean  "volunteer"
    t.datetime "created_at"
    t.datetime "updated_at"
    t.string   "password_digest"
    t.string   "position"
    t.integer  "project_id"
    t.string   "time_zone"
  end
end

class Conversation < ActiveRecord::Base
  has_many :private_messages, -> {order('created_at ASC')}
  def sender_user_name_of_recent_message
    message = self.private_messages.last
    user = message.sender_id
    name = User.find_by(id: user)
    "#{name.first_name} #{name.last_name}"
  end
  def the_id_of_sender
    message = self.private_messages.last
    user = message.sender_id
    name = User.find_by(id: user)
    name.id
  end
  def private_message_subject
    message = self.private_messages.last
    message_subject = message.subject
  end
  def private_message_body
    message = self.private_messages.last
    message_body = message.body
  end
  def join_request
    message = self.private_messages.first
    project = Project.find_by(id: message.project_id)
    if project
      project.state == "open"
    end
  end
  def project_complete_request
    message = self.private_messages.first
    project = Project.find_by(id: message.project_id)
    if project
      project.state == "pending approval"
    end
  end
  def opportunity_drop_project
    message = self.private_messages.first
    project = Project.find_by(id: message.project_id)
    if project
      project.state == "in production"
    end
  end
end
用户

class User < ActiveRecord::Base
  has_secure_password validations: false
  belongs_to :organization
  belongs_to :project
  has_many :project_users
  has_many :projects, through: :project_users
  has_many :sent_messages, class_name: 'PrivateMessage', foreign_key: 'sender_id'
  has_many :received_messages, -> {order('created_at DESC')}, class_name: 'PrivateMessage', foreign_key: 'recipient_id'
  has_many :conversations
  def private_messages
    messages = self.sent_messages + self.received_messages
    messages.sort!
  end
  def user_conversations
    collection = self.received_messages.select(:conversation_id).distinct
    all_conversations = collection.map do |member|
      convo_id = member.conversation_id
      Conversation.find_by(id: convo_id)
    end  
    all_conversations.sort
  end
  def organization_name
    organization.name
  end
end
组织

class Organization < ActiveRecord::Base
  belongs_to :organization_administrator, foreign_key: 'user_id', class_name: 'User'  
  has_many :projects
  has_many :users
end

PrivateMessage

class PrivateMessage < ActiveRecord::Base
  belongs_to :recipient, foreign_key: 'recipient_id', class_name: 'User'
  belongs_to :sender, foreign_key: 'sender_id', class_name: 'User'
  belongs_to :conversation
  validates_presence_of :subject, :body
end
项目

class Project < ActiveRecord::Base
  belongs_to :organization
  has_many :project_users
  has_many :users, through: :project_users
  def project_admin
    organization.organization_administrator
    User.find(organization.organization_administrator.id)
  end
  def open
    self.state == "open"
  end
end

ProjectUser

class ProjectUser < ActiveRecord::Base
  belongs_to :user
  belongs_to :project
end

我不认为有一个内置的Rails方法来做到这一点。ActiveModel::Dirty将不起作用,因为它只告诉您对象属性是否在内存中被更改,一旦对象保存到DB,它就不再是脏的了。我认为您只需要为state创建一个时间戳伴随列,并在每次更改state时将该值重置为当前时间。然后您只需检查now和state_changed属性之间的时间差。

您可以通过分配时间来实现这一点。现在是每次分配新状态时的state_changed属性。或者,您也可以在项目对象上使用before_save回调。使用AM::Dirty来检查项目。state已经改变,将state_changed属性设置为Time。如果是这样的话。这样会更DRY。

然后在代码中,你想要时间依赖的动作,你可以把它们包装在一个条件语句中if project.state == "in production" && project.state_changed < 5.days.ago

您还可以为您的视图创建一个助手,它进行相同的计算,并且只在按钮为true时显示按钮

我在这里漏掉了一些大的对象。用户通过志愿申请拥有多个项目,用户通过合同拥有多个项目。

考虑到接受用户对项目的请求发生在用户的收件箱中,其中包含具有外键contract_id的对话数组,因此我在Conversation模型中实现了以下方法:

  def with_opportunity_to_drop_job
    contract = Contract.find(self.contract_id)
    contract.active && contract.work_submitted == false && contract.created_at > 5.days.ago
  end

如果创建日期超过5天,按钮消失——幸运的是,我有测试来验证这一点,所以我不需要等待5天:-)