简析Rails:active_support 2.3.5
项目中用到Rails2.3.5,就以此版本研究。和Rails本身相关的,大概有actionmailer(发邮件相关)、actionpack(HTTP和controller相关)、activerecord(ORM框架)、activeresource、activesupport(基础扩展库)。
activesupport库算是一个基础扩展API库,主要分为两部分,一部分是可供选择的OOP形式的类和API,还有一部分是以Module形式存在的API以打开类的方式直接扩展进入Ruby源代码(即原有基础类)中。打开类扩展的API有一些应该被纳入Rurby源码,写惯Rails代码的脱离了activesupport再去写Ruby估计很不适应。
在po主本机上,库位于:`xx\Ruby193\lib\ruby\gems\1.9.1\gems\activesupport-2.3.5\lib`
activesupport库的结构
- activesupport.rb 已被废弃,require此文件会转require active_support.rb文件,并产生一个warning
 - active_support.rb 用来导入activesupport库中所有类和模块
 - active_support目录
- core_ext目录                    基础扩展库
- 具体模块目录
 - 模块目录导入文件
 - 简单独立模块.rb
 
 - vendor目录
- 具体模块目录
 
 - 具体功能目录…(如json)
 - 功能导入文件.rb(如json.rb),用以导入同名具体功能目录下所有的文件
 - 其他功能独立文件.rb(如base64.rb、ordered_hash.rb)
 
 - core_ext目录                    基础扩展库
 
activesupport导入流程
- require active_support.rb
 - 用autoload方法导入大部分独立类库和目录类库导入文件
- 目录类库导入文件,导入相应的目录下的文件,并作一些初始化工作
 
 - require active_support/{vendor, core_ext, dependencies, json}
- 导入core_ext下所有rb文件,当rb文件为导入文件时,再导入core_ext下的目录文件
 - 使用gem安装导入vender下的一些类库(builder、memcache-client、tzinfo、i18n)
 
 
active_support库的分层加载机制值得模仿学习,每一个子目录都有对应的一个导入rb文件。
activesupport的core_ext基础库扩展
按照功能类别排序划分。下面每个子目录下大概有2-10几个rb文件,都是对同一个模块或者类进行扩展,按照功能和意义的不同划分不同的类管理API。
- class目录
 - object目录
 - module目录
 - kernel目录
 
- integer目录
 - numeric目录
 - bigdecimal目录
 - float目录
 
- array目录
 - enumerable.rb
 - hash目录
 - range目录
 - string目录
 - proc.rb
 - symbol.rb
 
- date目录
 - time目录
 - datetime目录
 
- base64目录
 - file目录
 - cgi目录
 - process目录
 - pathname目录
 
Class、Object、Module、Kernel
对Class的扩展:
- cattr_accessor :xxx,用来定义@@类变量,同时定义类方法和实例方法
 - read_inheritable_attribute、write_inheritable_attribute:用名为@inheritable_attributes的实例变量保存了一个hash,用来存储
 - class_inheritable_accessor :xxx,用类实例变量存储hash,实现同上
 - subclasses、remove_subclasses、remove_class
 
对Object的扩展:
blank?和present?- acts_like?(duck):与具体模块中acts_like_duck?结合使用
 - with_options:调用可以传入hash键值的函数时,统一加入指定的键值对
 - tap:在调用链中用来遍历,本身什么也不干,一般用来输出调试
 - instance_values:将实例变量设置成hash返回
 - instance_variable_defined?:是否定义了某个实例变量
 - subclasses_of、extended_by、extend_with_included_modules_from:继承相关的API
 
对Module的扩展:
- attr_internal :xxx和attr_internal_accessor :xxx,基本和attr_accessor一致,以@_xxx命名格式存储,不容易引起冲突(尤其在继承中)
 - mattr_accessor :xxx,定义@@xxx的读写方法。和Class的cattr_accessor的区别是,mattr用在module中,cattr用在Class中,混用可能导致错误
 - alias_method_chain(target, feature):定义target_without_feature和target_with_feature函数,并将target alias 为feature。此函数在Rails源码中被用到很多
 - alias_attribute(new_name, old_name):复制一套属性访问方法
 - included_in_classes:被included的Class集合
 
对Kernel的扩展:
- daemonize:调用Process.daemon
 
Array、Enumerable、Hash、Range、String
对Array的扩展:
- 访问元素用的API(access.rb):from(pos)、to(pos)、second、third、forty_two(这个API有点意思)
 - 格式化输出的API(conversions.rb):to_param、to_query、to_formatted_s代替to_s、to_xml
 - extract_options!(extract_options.rb):取出函数参数中位于最后的hash
 - 将元素分组的API(grouping.rb):
in_groups_of(num)每个组有num个元素、in_groups(num)分成num个组、split(value)按元素分组 - rand(random_access.rb):随机访问元素
 - wrap(wrapper.rb):将其他对象包装成Array
 
对Enumerable的扩展:
index_by:用元素的某个属性建立hash,最常见就是用id作为键group_by:用元素的某个属性建立元素为array的hasheach_with_object:Ruby1.9中自带的方法- none?
 - sum?
 - many?
 
from、to、index_by、group_by这几个函数太常用了,一开始不知道还自己写了一套通用的实现。
对Hash的扩展:
- 格式化输入和输出的API(conversions.rb):Hash.from_xml、to_xml、to_query
 - deep_merge和deep_merge!(deep_merge.rb):当同一key两value都是Hash时递归式merge键值对
 - diff(diff.rb):两个hash之间不同的键值对
 - except和except!(except.rb):去除指定的key
 slice和slice!(slice.rb):只留下指定的key- with_indifferent_access方法和HashWithIndifferentAccess类(indifferent_access.rb):字符串和符号键无区别对待
 - reverse_merge和reverse_merge!(reverse_merge.rb):反方向merge
 - 格式化key的API(keys.rb):stringify_keys、stringify_keys!和symbolize_keys、symbolize_keys!,前两个将所有key转化为字符串形式,后两个转为为符号
 
对String的扩展:
文件太多,就列举一下API。
- 访问类API:at(pos)用来解决Ruby1.8中返回int值的结构、
from(pos)、to(pos)、first、last - 转化为时间类API:
to_time(form = :utc)、to_date、to_datetime - squish和squish!:去掉字符串前后空格,并将内部的连续空格合并为1个
 - 格式化字符串类API:使用外部的Inflector类实现
- pluralize:复数形式
 - singularize:单数形式
 - camelize:驼峰形式
 - underscore:下划线形式
 - dasherize:中划线形式
 - demodulize:去除模块名之前的前导模块,得到最后的模块名
 - titleize:首字母大写
 - humanize:最佳阅读形式
 - classify:类名形式
 
 - each_char:遍历每个字符
 - Ruby1.8字节串到字符串的转化API:
mb_chars、is_utf8? starts_with?和end_with?- 升级+和<<、add_with_safety、html_safe?:将+和<<升级用add_with_safety实现
 
对Range的扩展:
- include?扩展即include_with_range?(include_range.rb):升级为同时支持Range对象的include?判断
 - overlaps?(overlaps.rb):判断是否覆盖
 
Proc和Symbol
对Proc的扩展:只有一个bind函数,讲Proc绑定到一个object作为一个实例方法
对Symbol的扩展:只有一个to_proc函数(即对&操作符的处理),配合起来index_by、group_by方法不要太给力。
hash_by_id = list.index_by(&:id) hash_group_by_type = list.group_by(&:type)
Integer、Numeric、Float
很大程度配合Date、Time、DateTime模块。
对Integer的扩展:
- even?、odd?
 - ordinalize:给数字末尾加上序号串,1=>1st,102=>102nd
 - months、years:配合日期的加减或者直接打印,返回值是Duration对象
 
对Numeric的扩展:
- bytes、kilobytes、megabytes、gigabytes、terabytes、petabytes、exabytes和所有的单数形式,节省计算各个1024
 - seconds、minutes、hours、days、weeks、fortnights:同Integer的months
 - ago(time)和untile(time)、since(time)和from_now(time):前者time – self,后者time + self
 
对Float的扩展:
- round_with_precision(precision = nil):按照精度四舍五入
 - years、months
 
Date、Time和DateTime
要结合core_ext同目录下的duration.rb中的Duration类实现。给这三个类增加的API都比较类似,就大致举例一些API和用法好了。
以下方法基本为Date和Time类共享:
- 升级+和-为可以计算Duration类型的方法plus_with_duration(other)、minus_with_duration(other)
 - yesterday、tomorrow
 - past?、today?、future?
 - beginning_of_day、midnight、at_midnight、end_of_day、beginning_of_week、end_of_week、beginning_of_month、end_of_month、beginning_of_quarter、end_of_quarter、beginning_of_year、end_of_year
 - months_ago、months_since、years_ago、years_since
 - last_year、next_year、last_month、next_month、next_week
 - to_s(:db):date对象直接生成%Y-%m-d形式,time对象生成%Y-%m-%d %H:%M:%S
 
对Time加上时区的扩展:都是静态函数
- zone的读写
 - zone_default的读写
 - use_zone(time_zone):修改时区
 - current
 
其他
activesupport的vendor
现在只有builder、i18n、tzinfo、memcache-client四个模块
activesupport的可选功能库
按照个人觉得有用度排序,有些感觉没啥用的和暂时看不懂得没有列出来
- ordered_hash.rb和ordered_options.rb:保持插入顺序的Hash;OrderedOptions可以用hash.xxx = ‘xxx’的方式存储键值对(method_missing实现)
 - duration.rb:实现1.month.ago这样的时间跨度,用于对基础库Date和Time的扩展
 - callbacks.rb:define_callbacks :before_save, :after_save这样的钩子方法
 - rescuable.rb:捕获所有指定类型的异常,做统一处理
 - time_with_zone.rb:对Time类扩展时区功能
 - base64.rb:加密、解密
 - gzip.rb:用gzip算法压缩和读取string
 - json目录
 - multibyte目录:对Ruby1.8支持Unicode
 - cache目录:定了好几种存储方式,
- drb_store
 - file_store
 - mem_cache_store
 - memory_store,就是个Hash
 - synchronized_memory_store,同步的memory_store
 - compressed_mem_cache_store
 
 - locale目录
 - testing目录
 - values目录
 - xml_mini目录
 
其他学习到的地方
- Ruby1.9+和Ruby1.8差别非常大,导致active_support代码中要各种判断Ruby版本
- 字符串用Unicode实现
 - Hash用OrderedHash实现
 - 加入了BaseObject作为白板类
 
 - Object的act_like?(xxx)方法和其他模块分别定义act_like_xxx?方法,配合实现类似Java的接口机制,一般单独写在behavior.rb文件的Behavior模块中
 
 浙公网安备 33010902001071号