2012-10-24 22 views
0

首先,让我为一个看似简单的问题表示歉意,但对rails,ruby和编程新手我觉得我已经用尽了“New to Rails”教程。在Ruby和Rails中使用计算

这就是我所反对的。

我有一个“has_many:through =>:company_reps”关系的用户模型和机构模型。

用户有基本的字段(姓名,电子邮件,密码)(我使用devise

该机构有许多领域,但相应的有(客户端=布尔值,铅=布尔值,demo_date =日期/时间) 使每个机构进一步复杂化可以有一个或两个用户,但大多数只有一个。

我们为用户举办比赛,我需要根据demo_date字段和客户端字段向每个用户颁发积分。因此,首先我需要做的是为每个用户提供10个与作为客户的机构相关的点数,除非该机构有2个用户,在这种情况下,我需要给这两个用户各5分。

其次我需要给所有用户1点涉及到具有2012年二月之后演示日期

我使用Ruby 1.9.2的机构来说,Rails 3.2.8和MySQL

  • 那么,我该如何做到这一点?
  • 我应该创建一个新的表格和模型来存储点,如果是的话我该如何保存计算?
  • 我应该把所有的计算都放在用户或机构模型中吗?

一如既往感谢您的帮助。

MySQL的机构信息

CREATE TABLE `institutions` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `state_id` int(11) DEFAULT NULL, 
    `company` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `clientdate` datetime DEFAULT NULL, 
    `street` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `city` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `zip` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `source` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `source2` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL, 
    `demodate1` datetime DEFAULT NULL, 
    `demodate2` datetime DEFAULT NULL, 
    `demodate3` datetime DEFAULT NULL, 
    `client` tinyint(1) DEFAULT NULL, 
    `prospect` tinyint(1) DEFAULT NULL, 
    `alead` tinyint(1) DEFAULT NULL, 
    `notcontacted` tinyint(1) DEFAULT NULL, 
    `created_at` datetime NOT NULL, 
    `updated_at` datetime NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB AUTO_INCREMENT=7805 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

机构模式

class Institution < ActiveRecord::Base 
    attr_accessible :company, :phone, :assets, :clientdate, :street, :city, :state_id, :zip, :source, :source2, :demodate1, :demodate2, :demodate3, :client, :prospect, :alead, :notcontacted 
    belongs_to :state 
    has_many :users, :through => :company_reps 
    has_many :company_reps 

end 

用户模型

class User < ActiveRecord::Base 
    # Include default devise modules. Others available are: 
    # :token_authenticatable, :confirmable, 
    # :lockable, :timeoutable and :omniauthable 
    devise :database_authenticatable, :registerable, 
     :recoverable, :rememberable, :trackable, :validatable 

    # Setup accessible (or protected) attributes for your model 
    attr_accessible :email, :password, :password_confirmation, :remember_me, :first_name, :last_name 
    # attr_accessible :title, :body 

    has_many :states, :through => :rep_areas 
    has_many :institutions, :through => :company_reps 
    has_many :rep_areas 
    has_many :company_reps 

    def name 
    first_name + " " + last_name 
    end 


end 

公司代表型号

class CompanyRep < ActiveRecord::Base 
    belongs_to :user 
    belongs_to :institution 
end 

回答

2

更新(因为我的第一次尝试是错误假设User has_one :institution

最简单的办法是做对Institution模型的基本计算建立该机构有多少个是“值得”,然后和那值来计算用户的积分。

# Institution 
def points 
    points_for_client + points_for_demo_date 
end 

private 

def points_for_client 
    if client? 
    10/users.count 
    else 
    0 
    end 
end 

def points_for_demo_date 
    if demo_date.present? && demo_date >= Date.new(2012, 3, 1) 
    1 
    else 
    0 
    end 
end 

请注意,如果你愿意,你可以凝结这些if语句转换为单行与三元运算符? :。另外请注意,我以为“铁后布鲁里“来表示”从3月1日起“。

demo_date的支票也是一个口味问题。就拿从

# Verbose, but IMO intention-revealing 
demo_date.present? && demo_date >= Date.new(...) 

# Perhaps more idiomatic, since nil is falsy 
demo_date && demo_date >= Date.new(...) 

# Take advantage of the fact that >= is just another method 
# Concise, but I think it's a bit yuk! 
demo_date.try :>=, Date.new(...) 

现在,每个机构都值得一定的点数,这是相当简单的你挑总结起来:

# User 
def points 
    institutions.inject(0) {|sum, institution| sum + institution.points } 
end 

退房the docs for inject如果你不熟悉它,这是一个漂亮的小方法。

就性能而言,这是不理想的。一个基本的改进是memoize的结果:

# Institution 
def points 
    @points ||= points_for_client + points_for_demo_date 
end 

# User 
def points 
    @points ||= institutions.inject ... 
end 

,这样在相同的请求points还呼吁不要重新计算值。这没关系,只要clientdemo_date不改变而User对象还活着:

some_user.points #=> 0 
some_user.institution.client = true 
some_user.points #=> 0 ... oops 

User对象将被重新创建下一个请求,所以这可能不是一个问题(这取决于如何这些领域改变)。

你也可以一个points字段添加到User,从而节省值在数据库中,使用原始版本的update_points方法,而不是

def update_points 
    self.points = institutions.inject ... 
end 

然而,工作了的时候,重新计算值那么将成为一个问题。

我的建议是保持尽可能简单,避免过早优化。这是一个相对简单的计算,所以它不会是一个很大的性能问题,只要你没有大量的用户和机构或大量的请求正在进行。

+0

哇...完美。非常感谢。 – DaveG

+0

还有一个问题。当我做一个rails控制台来尝试代码。我会做一些像u = User.find(1)。然后u.points我得到未定义的方法'客户端?' 。这是由于用户拥有多个机构吗? – DaveG

+0

啊,对不起,我误解了这个问题,我正在研究'User has_one:institution'这个假设。我会更新我的答案。 –

0

积分累积到User S,所以它似乎有道理对User类返回他们已经积累的点数添加一个方法调用。

我只是写一个方法,每次调用时计算总点数,并使用一些单元测试来确保计算是正确的。起初我不会保存结果 - 取决于您拥有多少个对象,需要多长时间计算点数等等,您可能根本不需要保存它。