简析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模块中