在Ruby和Rails中使用计算

时间:2012-10-24 16:05:12

标签: mysql ruby-on-rails ruby

首先让我为一个看似简单的问题道歉,但是对于rails,ruby和编程我是新手,我觉得我已经用尽了“New to Rails”教程。

这就是我反对的内容。

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

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

机构有很多字段,但相关的字段是(client = boolean,lead = boolean,demo_date = date / time) 为了进一步复杂化,每个机构可以有一个或两个用户,但大多数只有一个。

我们正在为用户举办比赛,我需要根据demo_date字段和客户字段为每位用户奖励积分。

首先,我需要做的是给每个用户10个与客户机构相关的积分,除非该机构有2个用户,在这种情况下我需要给这两个用户各5分。

其次,我需要向所有用户提供1个与2012年2月之后具有演示日期的机构相关的点数。

我正在使用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 个答案:

答案 0 :(得分:2)

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

最简单的选择是对Institution模型进行基本计算,以确定该机构有多少分数&#34;值得&#34;,然后对该值进行求和以计算用户&#34; s点。

# 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将这些? :语句压缩为单行。另请注意,我假设&#34;在2月和#34;之后从3月1日开始意味着&#34;&#34;。

检查零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,这是一个非常棒的小方法。

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

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

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

因此,在同一请求中进一步调用points不会重新计算该值。只要clientdemo_dateUser对象仍然存在时发生变化,那就没问题了:

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

但是,确定何时重新计算该值将成为一个问题。

我的建议是尽可能简化并避免过早优化。这是一个相对简单的计算,因此它不会成为一个重大的性能问题,只要您没有大量的用户和机构或许多请求正在进行。

答案 1 :(得分:0)

积分累积到User s,所以在User类上添加方法调用似乎是有意义的,它会返回它们累积的点数。

我首先要编写一个方法,在每次调用时计算总点数,并进行一些单元测试以确保计算正确。我不会首先保存结果 - 取决于你有多少个对象,你需要计算点的频率等等,你可能根本不需要保存它。