<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>bjtugun (Wilbur)</title>
    <link>https://ruby-china.org/bjtugun</link>
    <description>https://coding.net/u/hwh008</description>
    <language>en-us</language>
    <item>
      <title>有关产品表，订单表的传统设计问题</title>
      <description>&lt;p&gt;产品表有很多字段，产品全称是由多个字段的值 format 出来的，没有字段存储产品全称。&lt;/p&gt;

&lt;p&gt;订单表下包含多条订单明细，每条订单明细里记录了产品 id 和购买数量。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt;
 &lt;span class="n"&gt;has_many&lt;/span&gt; &lt;span class="ss"&gt;:order_items&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrderItem&lt;/span&gt;
 &lt;span class="c1"&gt;#product_id&lt;/span&gt;
 &lt;span class="c1"&gt;#count&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;OrderItem 和 Product 之间并没有 has 和 belong to 的概念。那么问题来了，我在 view 里显示订单明细的时候，怎么显示产品全称呢？我传订单 json 数据给 app 的时候，app 怎么显示订单明细中的产品全称。&lt;/p&gt;

&lt;p&gt;我就是想问问要把这个问题解决的漂亮一点应该怎么做。&lt;/p&gt;</description>
      <author>bjtugun</author>
      <pubDate>Sat, 21 Nov 2015 10:29:11 +0800</pubDate>
      <link>https://ruby-china.org/topics/28138</link>
      <guid>https://ruby-china.org/topics/28138</guid>
    </item>
    <item>
      <title>Rails 小项目的一些分享</title>
      <description>&lt;p&gt;&lt;strong&gt;Refer&lt;/strong&gt; : &lt;a href="http://blog.magica.me/idea/rails-exp.html" rel="nofollow" target="_blank"&gt;http://blog.magica.me/idea/rails-exp.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;为了满足自己炒股兴趣的需要，根据&lt;a href="http://www.amazon.com/Little-Book-Still-Beats-Market/dp/0470624159" rel="nofollow" target="_blank" title=""&gt;《神奇公式》&lt;/a&gt;，对 A 股市场做了一个排序系统，既完成了一个对自己有效的需求，也从中学习 rails。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt; : &lt;a href="https://coding.net/u/hwh008/p/mss/git" rel="nofollow" target="_blank"&gt;https://coding.net/u/hwh008/p/mss/git&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Site&lt;/strong&gt; : &lt;a href="http://www.magica.me" rel="nofollow" target="_blank"&gt;http://www.magica.me&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="爬虫"&gt;爬虫&lt;/h2&gt;
&lt;p&gt;股票的财务数据都是从 sina 抓的，所以&lt;a href="http://www.nokogiri.org/" rel="nofollow" target="_blank" title=""&gt;Nokogiri&lt;/a&gt;是首选，有了他，做点基础的爬虫实在太容易了。&lt;/p&gt;

&lt;p&gt;用 whenever 每天定点去爬一下收盘价，顺便检测一些需要更新财务数据的股票。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;StockInfo.tick_sheet
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="缓存"&gt;缓存&lt;/h2&gt;
&lt;p&gt;我有一个很耗时的 model 函数，也就是对所有股票进行排序的公式实现：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;StockSheet.calc_better_cheap
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我希望这个公式的结果页面显示后，可以重用这个页面，不要重新计算排序，因此我尝试了 action cache 和 fragment cache，然而并没有什么卵用，因为这两种cache都还是要执行controller#action的，费时的函数调用恰好放在action中。我可以把这个调用放到view里，这样就解决了问题，但是又涉及到请求参数变化的问题，而且这串调用放到view里也带来了限制，不方便在action里对数据做更多的修饰。  &lt;/p&gt;

&lt;p&gt;另外，我也搞不清楚 fragment cache 的实现机制和最佳实践。&lt;/p&gt;

&lt;p&gt;经过搜索我找到了&lt;a href="https://github.com/qor/qor_cache" rel="nofollow" target="_blank" title=""&gt;qor_cache&lt;/a&gt;，还是国人的优秀作品，有 3 点好处：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;针对 Model 函数结果的 cache，正好我就是 model 函数耗时多。&lt;/li&gt;
&lt;li&gt;qor_cache wrap 了我指定的函数，使得我的代码对这个 gem 没有依赖，可以轻易的从 config 中删掉 qor_cache。&lt;/li&gt;
&lt;li&gt;以接口的方式告诉我一条 cache 的最佳实践：
&amp;gt; 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.&lt;/li&gt;
&lt;/ol&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cache_key 'stock_update' do
    StockInfo.first.updated_at
end

scope :stock_sheet do
    cache_class_method :calc_better_cheap, 'stock_update'
end
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;函数的输入参数是一个 hash obj，qor_cache 将参数作为 cache key 的一部分，但是这个 hash obj 中，其实有些 key 并不影响 cache，因此我根据 qor_cache 做了一个 hack：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hash_obj.instance_eval do
        # 为了优化qor cache，不是每个选项都影响cache key
        def inspect
            %Q(inspect redefine:#{self["mktcap_min"]},#{self["mktcap_max"]})
        end
        self
    end
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="翻页"&gt;翻页&lt;/h2&gt;
&lt;p&gt;我没有使用 will_paginate，因为 will_paginate 只包装 relation 对象，但我要做的是对数据进行排序计算，已经将数据全部都取出来了，因此找了一个支持 page array 的 gem：&lt;a href="https://github.com/amatsuda/kaminari" rel="nofollow" target="_blank" title=""&gt;kaminari&lt;/a&gt;，听这名字像日本人写的 gem。&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def better_cheap
  stocks = Kaminari.paginate_array StockSheet.calc_better_cheap(calc_params)
  @stocks_page = stocks.page(params.fetch(:page, 1)).per 100
end
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="函数式FP"&gt;函数式 FP&lt;/h2&gt;
&lt;p&gt;函数式编程其实就是好看，可以把对集合的一连串操作都写在一行里，长长的。&lt;/p&gt;

&lt;p&gt;神奇公式的排序算法就是，将集合所有元素根据指标 A 排序，取排序顺序为分数 A，再根据指标 B 排序，取排序顺序为分数 B，根据分数 AB 之和再排序。写到一行里是这样的：&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def self.calc_better_cheap(opt)
    available(opt).sort { |a, b| a.better_v &amp;lt;=&amp;gt; b.better_v }.reverse.map.with_index(1) { |el, i| el.better_ord = i ; el}.sort { |a,b| a.cheap_v &amp;lt;=&amp;gt; b.cheap_v }.reverse.map.with_index(1) { |el, i| el.cheap_ord = i; el}.sort { |a,b| a.bc_value &amp;lt;=&amp;gt; b.bc_value }
  end
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="nginx + passenger"&gt;nginx + passenger&lt;/h2&gt;
&lt;p&gt;生产部署用的是 passenger，很简单，安装就好了。就是环境变量遇上了一些问题，后来通过 dotenv-rails 解决。&lt;/p&gt;
&lt;h2 id="Helper让View漂亮"&gt;Helper 让 View 漂亮&lt;/h2&gt;
&lt;p&gt;有两种不漂亮的写法，一种是在 view 里写表达式，另一种是在 action 里把表达式算好的结果放到 instance var。&lt;/p&gt;

&lt;p&gt;不过 erb 模版对 Helper 的支持还是不够漂亮，我比较倾向的是那种管道式的，这样可以把一串 helper 像羊肉串一样串起来。&lt;/p&gt;</description>
      <author>bjtugun</author>
      <pubDate>Mon, 16 Nov 2015 21:49:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/28080</link>
      <guid>https://ruby-china.org/topics/28080</guid>
    </item>
    <item>
      <title>rspec 和 capybara 的 cookies 是不同的</title>
      <description>&lt;p&gt;其他人遇到的相同问题：
&lt;a href="http://ruby-china.org/topics/13913" rel="nofollow" target="_blank"&gt;http://ruby-china.org/topics/13913&lt;/a&gt; &lt;a href="/RainFlying" class="user-mention" title="@RainFlying"&gt;&lt;i&gt;@&lt;/i&gt;RainFlying&lt;/a&gt;
&lt;a href="http://ruby-china.org/topics/12411" rel="nofollow" target="_blank"&gt;http://ruby-china.org/topics/12411&lt;/a&gt; &lt;a href="/diguage" class="user-mention" title="@diguage"&gt;&lt;i&gt;@&lt;/i&gt;diguage&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;在学习 &lt;strong&gt;Ruby on Rails Tutorial&lt;/strong&gt; 的例子中，有一段测试混合了 rspec 和 capybara。
rails 4, capybara 2.2.1,  rspec 2.14.1&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;visit&lt;/span&gt; &lt;span class="n"&gt;signin_path&lt;/span&gt;  &lt;span class="c1"&gt;#访问登陆页面&lt;/span&gt;
  &lt;span class="n"&gt;click_button&lt;/span&gt; &lt;span class="s1"&gt;'Sign In'&lt;/span&gt;  &lt;span class="c1"&gt;#登陆，服务端设置cookie&lt;/span&gt;
  &lt;span class="n"&gt;patch&lt;/span&gt; &lt;span class="n"&gt;user_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrong_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;#试图修改其他玩家的信息&lt;/span&gt;
  &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt; &lt;span class="n"&gt;redirect_to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;#服务端重定向到root&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;多次测试下来，总是重定向到 signin_path。我也纠结了一天，最终查看 log/test.log，看到patch时的cookie和登陆后的是不一样的，接下来的多次google和随便尝试，发现rspec和capybara并不共用一套cookie instance。&lt;/p&gt;

&lt;p&gt;测试通过的方法：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;#设置patch方法所依赖的cookies对象&lt;/span&gt;
&lt;span class="n"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:remember_token&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cookies&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'remember_token'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
&lt;span class="n"&gt;patch&lt;/span&gt; &lt;span class="n"&gt;user_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrong_user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>bjtugun</author>
      <pubDate>Thu, 15 May 2014 22:01:45 +0800</pubDate>
      <link>https://ruby-china.org/topics/19285</link>
      <guid>https://ruby-china.org/topics/19285</guid>
    </item>
  </channel>
</rss>
