对于我的项目表,我有一个列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天:-)