Rails Rails 小项目的一些分享

bjtugun · 2015年11月16日 · 最后由 xiagood 回复于 2016年01月02日 · 3645 次阅读

Refer : http://blog.magica.me/idea/rails-exp.html

为了满足自己炒股兴趣的需要,根据《神奇公式》,对 A 股市场做了一个排序系统,既完成了一个对自己有效的需求,也从中学习 rails。

Source : https://coding.net/u/hwh008/p/mss/git

Site : http://www.magica.me

爬虫

股票的财务数据都是从 sina 抓的,所以Nokogiri是首选,有了他,做点基础的爬虫实在太容易了。

用 whenever 每天定点去爬一下收盘价,顺便检测一些需要更新财务数据的股票。

StockInfo.tick_sheet

缓存

我有一个很耗时的 model 函数,也就是对所有股票进行排序的公式实现:

StockSheet.calc_better_cheap

我希望这个公式的结果页面显示后,可以重用这个页面,不要重新计算排序,因此我尝试了 action cache 和 fragment cache,然而并没有什么卵用,因为这两种 cache 都还是要执行 controller#action 的,费时的函数调用恰好放在 action 中。我可以把这个调用放到 view 里,这样就解决了问题,但是又涉及到请求参数变化的问题,而且这串调用放到 view 里也带来了限制,不方便在 action 里对数据做更多的修饰。

另外,我也搞不清楚 fragment cache 的实现机制和最佳实践。

经过搜索我找到了qor_cache,还是国人的优秀作品,有 3 点好处:

  1. 针对 Model 函数结果的 cache,正好我就是 model 函数耗时多。
  2. qor_cache wrap 了我指定的函数,使得我的代码对这个 gem 没有依赖,可以轻易的从 config 中删掉 qor_cache。
  3. 以接口的方式告诉我一条 cache 的最佳实践: > The cache key is the fluid part and the cache content is the fixed part. A given key should always return the same content. You never update the content after it’s been written and you never try to expire it either.
cache_key 'stock_update' do
    StockInfo.first.updated_at
end

scope :stock_sheet do
    cache_class_method :calc_better_cheap, 'stock_update'
end

函数的输入参数是一个 hash obj,qor_cache 将参数作为 cache key 的一部分,但是这个 hash obj 中,其实有些 key 并不影响 cache,因此我根据 qor_cache 做了一个 hack:

hash_obj.instance_eval do
        # 为了优化qor cache,不是每个选项都影响cache key
        def inspect
            %Q(inspect redefine:#{self["mktcap_min"]},#{self["mktcap_max"]})
        end
        self
    end

翻页

我没有使用 will_paginate,因为 will_paginate 只包装 relation 对象,但我要做的是对数据进行排序计算,已经将数据全部都取出来了,因此找了一个支持 page array 的 gem:kaminari,听这名字像日本人写的 gem。

def better_cheap
  stocks = Kaminari.paginate_array StockSheet.calc_better_cheap(calc_params)
  @stocks_page = stocks.page(params.fetch(:page, 1)).per 100
end

函数式 FP

函数式编程其实就是好看,可以把对集合的一连串操作都写在一行里,长长的。

神奇公式的排序算法就是,将集合所有元素根据指标 A 排序,取排序顺序为分数 A,再根据指标 B 排序,取排序顺序为分数 B,根据分数 AB 之和再排序。写到一行里是这样的:

def self.calc_better_cheap(opt)
    available(opt).sort { |a, b| a.better_v <=> b.better_v }.reverse.map.with_index(1) { |el, i| el.better_ord = i ; el}.sort { |a,b| a.cheap_v <=> b.cheap_v }.reverse.map.with_index(1) { |el, i| el.cheap_ord = i; el}.sort { |a,b| a.bc_value <=> b.bc_value }
  end

nginx + passenger

生产部署用的是 passenger,很简单,安装就好了。就是环境变量遇上了一些问题,后来通过 dotenv-rails 解决。

Helper 让 View 漂亮

有两种不漂亮的写法,一种是在 view 里写表达式,另一种是在 action 里把表达式算好的结果放到 instance var。

不过 erb 模版对 Helper 的支持还是不够漂亮,我比较倾向的是那种管道式的,这样可以把一串 helper 像羊肉串一样串起来。

不错,不过这个没有多大必要,所有的财经网站都能进行筛选。

def calc_better_cheap 这个函数一行太长了。。。不好读

arr.sort { |a, b| a.better_v <=> b.better_v }.reverse
arr.sort { |a, b| b.better_v <=> a.better_v }

我对《神奇公式》比较感兴趣。

#3 楼 @flowerwrong 对的,我完全没想到:)

#4 楼 @qichunren x 东 x 宝都有中文版可以买

#2 楼 @jackxu 我觉得还是能读的,像一种管道的感觉

#1 楼 @jasontang168 不太熟悉财经网站,应该不行吧,涉及的一定的自定义计算呢

不错啊。可以学习学习下。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号