简析Rails:active_support 2.3.5

7月 28th, 2014 1,956 留下评论 阅读评论

项目中用到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)

activesupport导入流程

  1. require active_support.rb
  2. 用autoload方法导入大部分独立类库和目录类库导入文件
    1. 目录类库导入文件,导入相应的目录下的文件,并作一些初始化工作
  3. require active_support/{vendor, core_ext, dependencies, json}
    1. 导入core_ext下所有rb文件,当rb文件为导入文件时,再导入core_ext下的目录文件
    2. 使用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的hash
  • each_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模块中
Categories: ruby on rails 标签:
  1. 还没有评论呢。