第十五章 通过编程改进 ActiveRecord 模型
一、软模型改进与硬模型改进
1. 当在 ActiveRecord 模型类中编写一个新方法时,可以把方法区分为:被动方法(即那些仅仅获取数据并返回数据的方法)和主动方法(即那些生成新的数据结构的方法)
class Composer < ActiveRecord::Base
end
2. 软模型改进与硬模型改进:本质区别
本质区别在于:软改进描述给 ActiveRecord 提供辅助;硬改进涉及产生新的数据。
二、模型的软编程改进
1. 通过软改进细化 Work 模型
(1)哪些发行商发行过该作品的版本
def publishers
end
(2)该作品来自哪个国家
def country
end
(3)哪些顾客订购过该作品
def ordered_by
end
(4)该作品的基调是什么
def key
end
2. 为顾客的业务建模
(1)该顾客有哪些未完成的订单
def open_orders
end
(2)该顾客当前订单中包含哪些版本
def editions_on_order
end
(3)该顾客曾经订购过哪些版本
def edition_history
end
(4)该顾客当前订单中包含哪些作品
def works_on_order
end
(5)该顾客曾经订购过哪些作品
def work_history
end
3. 改进 composer
(1)该作曲者的作品出现在哪些版本中
def editions
end
(2)哪些发行商发行过包含该作曲者作品的版本
def publishers
end
4. 对比软改进过程中的 Ruby 和 SQL
最高效、最快速的数据库记录提取方式是 SQL 代码。ActiveRecord 做的很多工作就是私底下将你的 Ruby 代码翻译为 SQL 语句,然后使用这些语句查询数据库。
为了提高执行速度,ActiveRecord 允许任何时候都可以使用 SQL。可是这样就失去了良好的 Ruby 外观,却获得了效率。
作为对比 Ruby/SQL 的例子,我们来看一下 Composer#editions 方法:
def editions
end
该方法首先调用 works,无条件地搜集该作曲者的所有作品。调用 works 方法之后,ActiveRecord 的任务就完成了;该方法也是唯一的一个数据库查询,返回该作曲者的所有作品。剩下的都是纯 Ruby 代码:获取作品的所有版本(使用map),这些信息被存放在一个数组的数组中,然后使用 flatten 和 uniq 对该数组进行处理。最后,得到一个新数组。
下面是实现 editions 方法的另一种方式,使用 SQL 语句
def editions
end
Ruby 程序员在使用 Ruby 编写程序时,知道它不是特别快的语言;在碰到严重的性能瓶颈时,他们把程序的某些部分编写为 C 扩展。而在 Rails 应用开发中,SQL 也扮演着类似的角色。
三、硬模型改进
1. 美化字符串属性(Work 类)
(1)调整作品的各乐器名的格式
def nice_instruments
end
代码中使用了 %w{…} 构造一个字符串数组,用于存放规范的乐器名顺序
ordered = %w{ flute oboe violin viola cello piano orchestra }
接着,根据 ordered 数组中各个乐器的出现顺序(乐器名在数组中的索引序号)对 instrs 排序,如果出现不存在的乐器名,则它将出现在排序结果的最后面
instrs = instrs.sort_by {|i| ordered.index(i) || 0 }
(2) 调整作品号的格式
def nice_opus
end
(3) 美化的作品标题
def nice_title
end
(4) 美化的版本标题(Edition.rb)
def nice_title
end
2. 计算作品的年代(Work 类)
(1)作品出自哪个世纪
def century
end
(2)作品的更富描述性的时代信息
在 Work 类中定义一个常量
PERIODS = { [1650..1750, %w{ EN DE FR IT ES NL}] => "Baroque",
然后加入该方法
def period
end
3. 剩余的顾客业务(Customer 类)
(1) 顾客喜好排名
def rank(list)
end
def composer_rankings
end
def instrument_rankings
end
(2)计算订购的副本的数目
def copies_of(edition)
end
(3)未偿余额
方法一:
def balance
end
方法二(递增的累计计算可以使用 inject 方法自动完成):
def balance
end
(4)顾客结帐
方法一:
def check_out
end
方法二:
def check_out
end
四、用类方法扩展模型功能
1. 确定一组作品的所有版本(app/models/edition.rb)
def Edition.of_works(works)
end
2. 获取库存作品的所有时代(app/models/work.rb)
def Work.all_periods
end
3. 确定作品的销售排名(app/models/work.rb)
def Work.sales_rankings
end
散列是无序的,要使用这个散列,还需要对它进行排序:
rankings = Work.sales_rankings
r_sorted = rankings.sort_by {|key,value| value }
4. 确定作曲者的销售排名(app/models/composer.rb)
def Composer.sales_rankings
end