<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>mingyuan0715 (覃明圆)</title>
    <link>https://ruby-china.org/mingyuan0715</link>
    <description>有个想法武汉软件咨询有限公司</description>
    <language>en-us</language>
    <item>
      <title>100 行代码打造最佳体验最佳性能的 Rails 日志监控及分析</title>
      <description>&lt;p&gt;最近在琢磨上点手段来对 Rails 的日志进行处理，之前一直是 tail/grep 直接去服务端看 production.log，但是当请求密集的时候各个请求的记录混杂在一起，根本分不清。于是开始调研日志处理方案。&lt;/p&gt;

&lt;p&gt;着重看了下 fluentd 的方案，我想开始就简单点（我也不想部署额外的视图应用）直接把日志打到 Postgres 里得了，反正用 hotwired 撸几个视图查看日志也是分分钟的事儿。&lt;/p&gt;

&lt;p&gt;Fluentd 的方案是 tail 读取日志，然后通过正则处理，转化为 json ,  然后批量写入 postgres。emm..... 之所以性能比常规好一些，主要就是批量写入 PG 的这块特别优化了下。那我直接把 Rails 日志以 JSON (hash) 直接批量写入 PG 性能不更好？于是开始实操，直接上干货。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 拿到请求的详细信息&lt;/span&gt;
&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;RailsCom::ActionController&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EventSubscriber&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveSupport&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;StructuredEventSubscriber&lt;/span&gt;
    &lt;span class="n"&gt;attach_to&lt;/span&gt; &lt;span class="ss"&gt;:action_controller&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;start_processing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_action&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;payload&lt;/span&gt;
      &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:request&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;raw_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
      &lt;span class="n"&gt;real_headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Com&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;raw_headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="n"&gt;emit_event&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'controller.process_action'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;path: &lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fullpath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;controller_name: &lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:controller&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;action_name: &lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:action&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="ss"&gt;params: &lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:params&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;headers: &lt;/span&gt;&lt;span class="n"&gt;real_headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;session: &lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_h&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;format: &lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:format&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;to_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;ip: &lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remote_ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;session_id: &lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="ss"&gt;created_at: &lt;/span&gt;&lt;span class="no"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;iso8601&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="ss"&gt;uuid: &lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request_id&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 收集日志，每5秒钟批量写入 PG &lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EventJsonSubscriber&lt;/span&gt;
  &lt;span class="no"&gt;COLUMNS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="ss"&gt;:uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:controller_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:action_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:params&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:session&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:ip&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:format&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:session_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:created_at&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;
    &lt;span class="vi"&gt;@queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Concurrent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="vi"&gt;@coder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;PG&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TextEncoder&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;CopyRow&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
    &lt;span class="no"&gt;Concurrent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;TimerTask&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;execution_interval: &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;flush!&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:payload&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="vi"&gt;@queue&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="no"&gt;COLUMNS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="kp"&gt;private&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;flush!&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="vi"&gt;@queue.empty&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
    &lt;span class="n"&gt;buf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@queue.shift&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@queue.size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;SecureRandom&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uuid&lt;/span&gt;

    &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Base&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;raw_connection&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy_data&lt;/span&gt; &lt;span class="s2"&gt;"COPY com_logs(&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="no"&gt;COLUMNS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;', '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, commit_uuid) FROM STDIN"&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;buf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_copy_data&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="vi"&gt;@coder&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# 在 puma.rb 中注册下订阅&lt;/span&gt;
&lt;span class="n"&gt;before_worker_boot&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;subscriber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;EventJsonSubscriber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
  &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscriber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;start_with?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'controller.'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以工作了，看着  com_logs 表里源源不断涌入的完整日志，惊喜又激动！接下来看看对性能的损耗，直接上压测：&lt;/p&gt;
&lt;h2 id="关闭日志入库"&gt;关闭日志入库&lt;/h2&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 30s test @ http://localhost:3000
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.73s   273.31ms   2.00s    93.32%
    Req/Sec    25.73     20.11   180.00     74.08%
  6389 requests in 30.05s, 39.92MB read
  Socket errors: connect 0, read 0, write 0, timeout 1228
Requests/sec:    212.60
Transfer/sec:      1.33MB

Running 30s test @ http://localhost:3000
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.72s   280.64ms   2.00s    93.03%
    Req/Sec    26.61     23.25   230.00     74.67%
  6343 requests in 30.05s, 39.64MB read
  Socket errors: connect 0, read 0, write 0, timeout 1379
Requests/sec:    211.09
Transfer/sec:      1.32MB

Running 30s test @ http://localhost:3000
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.69s   280.33ms   2.00s    91.89%
    Req/Sec    23.69     17.48   130.00     77.07%
  6440 requests in 30.05s, 40.24MB read
  Socket errors: connect 0, read 0, write 0, timeout 1112
Requests/sec:    214.34
Transfer/sec:      1.34MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;连续跑了 3 次，平均每秒请求 212 个&lt;/p&gt;
&lt;h2 id="开启日志入库"&gt;开启日志入库&lt;/h2&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 30s test @ http://localhost:3000
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.75s   295.19ms   2.00s    92.88%
    Req/Sec    26.87     22.48   210.00     74.11%
  6198 requests in 30.04s, 38.73MB read
  Socket errors: connect 0, read 0, write 0, timeout 1535
Requests/sec:    206.29
Transfer/sec:      1.29MB

Running 30s test @ http://localhost:3000
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.75s   289.59ms   2.00s    92.94%
    Req/Sec    30.00     25.83   141.00     74.47%
  6277 requests in 30.06s, 39.22MB read
  Socket errors: connect 0, read 0, write 0, timeout 1458
Requests/sec:    208.84
Transfer/sec:      1.31MB

Running 30s test @ http://localhost:3000
  12 threads and 400 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     1.76s   293.76ms   2.00s    92.86%
    Req/Sec    27.49     22.41   151.00     70.22%
  6228 requests in 30.05s, 38.90MB read
  Socket errors: connect 0, read 0, write 0, timeout 1313
Requests/sec:    207.23
Transfer/sec:      1.29MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;连续跑了 3 次，平均每秒请求是 207，比开启前少了 5，可以忽略不计吧！
APM 平台的探针应该对性能损耗比这大不少吧，有经验的大佬们可以分享下。&lt;/p&gt;
&lt;h2 id="后续"&gt;后续&lt;/h2&gt;
&lt;p&gt;接下来就是撸几个视图在后台看看日志了，相对于把日志打到 外部的平台，在 Rails 应用内分析日志还有更多的收获，比如：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; 可以直接把请求和具体的用户关联起来，直接分析用户的行为；&lt;/li&gt;
&lt;li&gt; 可以更灵活，之前用 Newrelic 的时候，url 中含有中文的乱码都不给处理（也许外国人对看非英文没有需求吧），自己的应用我自己转；&lt;/li&gt;
&lt;li&gt;可统计的数据维度可以极为丰富，比如所有的 sql, 所有的 render , 每个请求的 gc 对象数，想怎么加指标就怎么加；&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Tue, 28 Oct 2025 11:58:26 +0800</pubDate>
      <link>https://ruby-china.org/topics/44360</link>
      <guid>https://ruby-china.org/topics/44360</guid>
    </item>
    <item>
      <title>开个坑，有喜欢用纯 Markdown + Git 进行写作的么</title>
      <description>&lt;p&gt;这两年在做一个大型开源项目，会写一些项目的周边文档，比如关于理念，实现思路的一些介绍。这些文档，我也很想用于其他平台的推广。除了 Github，还想同步发布到微信公众号、知乎、官网等渠道。&lt;/p&gt;

&lt;p&gt;这是我的纯 markdown 写作项目：&lt;a href="https://github.com/work-design/home" rel="nofollow" target="_blank"&gt;https://github.com/work-design/home&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;基于程序员的尽可能偷懒原则，我很难忍受需要手动将文档 copy 来 copy 去的。这只是一方面，还有一些其他的痛点：&lt;/p&gt;

&lt;p&gt;比如：不同的应用支持的格式也不尽相同。比如官网（或博客）上，我需要支持 纯 HTML，微信公众号自然也不支持 markdown. 这个 markdown 的文档，还需要经过处理一下才能同步。&lt;/p&gt;

&lt;p&gt;比如：我的文档总是处于不断更新之中的，当文档更新之后，其他的平台上发布的怎么同步更新？&lt;/p&gt;

&lt;p&gt;一直也没有找到合适的解决方案。&lt;/p&gt;

&lt;p&gt;一怒之下，我决定自己来实现，简单做了下技术验证：&lt;a href="https://github.com/work-design/rails_markdown" rel="nofollow" target="_blank"&gt;https://github.com/work-design/rails_markdown&lt;/a&gt;，可以以较低的成本先满足自己的基本需求。 &lt;/p&gt;

&lt;p&gt;灵机一动，想着同僚们是否有跟我类似的需求。这个工具应该是一个运营利器。如果对这个工具感兴趣的人多，我准备对这个工具进行下深入开发并运营一下。&lt;/p&gt;

&lt;p&gt;如果有使用这个工具的需求，可以留言告诉下是否愿意接受付费（每年 100 元左右）来使用这个工具。&lt;/p&gt;

&lt;p&gt;当然，&lt;a href="https://github.com/work-design/rails_markdown" rel="nofollow" target="_blank" title=""&gt;rails markdown&lt;/a&gt; 这个组件我会依然维持开源。&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Sun, 27 Jun 2021 12:52:26 +0800</pubDate>
      <link>https://ruby-china.org/topics/41414</link>
      <guid>https://ruby-china.org/topics/41414</guid>
    </item>
    <item>
      <title>来解锁 github 新姿势</title>
      <description>&lt;p&gt;解锁方式：新建一个跟你 github 用户名同名的项目&lt;/p&gt;

&lt;p&gt;&lt;img src="https://l.ruby-china.com/photo/2020/f9134ac1-d9ac-4119-8507-8eb2221916ca.png!large" title="" alt=""&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/qinmingyuan" rel="nofollow" target="_blank" title=""&gt;Demo&lt;/a&gt;&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Thu, 09 Jul 2020 14:20:23 +0800</pubDate>
      <link>https://ruby-china.org/topics/40133</link>
      <guid>https://ruby-china.org/topics/40133</guid>
    </item>
    <item>
      <title>[武汉 可远程] Work Design（有个想法武汉软件咨询有限公司）招聘中级程序员 1 名（暂停接收简历）</title>
      <description>&lt;h2 id="写在前面"&gt;写在前面&lt;/h2&gt;
&lt;p&gt;上个周，我在咱们论坛发了个创业分享：&lt;a href="https://ruby-china.org/topics/39893" title=""&gt;Ruby 程序员该何去何从，33 岁程序员失业后的创业分享&lt;/a&gt;，大概介绍了下我创业的缘起。&lt;/p&gt;

&lt;p&gt;有很多朋友给了我鼓励，也有几位小伙伴加了我的微信跟我交流，在此表示下感谢。在这里我就不多介绍我们的项目了，请移步之前的帖子了解详情。&lt;/p&gt;

&lt;p&gt;这一周市场工作进展比较顺利，确定了一个医疗行业的数字化项目，因为我的医学背景，这个项目会占用我绝大部分的时间。85% 概率将确定一个门店（可能是餐饮、轰趴、桌游、农庄）的数字化项目，因为还有几个项目在持续跟进中，后续应该也还会有新的项目进来。&lt;/p&gt;

&lt;p&gt;说下 Work Design 的收费及盈利模式：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;a href="https://work.design/price" rel="nofollow" target="_blank" title=""&gt;组件使用费&lt;/a&gt;: 可忽略不计；&lt;/li&gt;
&lt;li&gt; 甲方发放的工资，这部分工资实付，也就是说甲方给多少钱，由我公司处理完社保和税务之后，一分不少发给开发人员；&lt;/li&gt;
&lt;li&gt;助力甲方连锁发展，根据甲方利润率系统流水分成，这部分将形成持续的营收。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我们整体的思路是，先尽可能降低甲方数字化的成本和风险，让甲方赚到钱之后，我们再分一杯羹。有人可能会担心 3，甲方赚到钱会把我们踢开，其实我不是很 Care 这个，一是因为我们的技术有一套属于我们自己的体系，也赶上 ruby 技术人员青黄不接，甲方踢开我们的难度也不小，二是 我们并不是独占合作。搞农庄搞餐饮的企业很多，我们有信心谁跟我们持续合作，谁就能发展得更好。目前甲方都还非常认可我们这个思路。&lt;/p&gt;

&lt;p&gt;由于现实的原因（公司没有现金储备，商业模式不会抽佣开发人员工资），我们暂时不对岗位做工资担保，也就是说，开发人员的工资全部来自于甲方，我们不做补贴，项目停止，你的工资也会停止，不过我们会尽快找新项目续上，同时我们会要求甲方提前预付一个月工资至我们公司账户，不会出现工资发不上的情况。当然后续现金流稳定后，我们会确保在职员工不会停薪。&lt;/p&gt;
&lt;h2 id="关于岗位和项目"&gt;关于岗位和项目&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt; 我们会完全拥抱远程办公、自由办公，我的兼职项目&lt;a href="https://one.work" rel="nofollow" target="_blank" title=""&gt;One Work&lt;/a&gt;（在不断开发完善中）会用来管控这个过程。大概理念是：结果导向，每一个开发任务，都会进行时间记录，然后我们会根据你的工作数据分析得出你的专业能力水平，并决定你的工资（报价）。&lt;/li&gt;
&lt;li&gt; 我们的开发会基于 &lt;a href="https://github.com/work-design" rel="nofollow" target="_blank" title=""&gt;Work Design&lt;/a&gt; 体系，所以我们希望你能接受 Work Design 的开发思路，当然 Work Design 不是完美的，也是不断进化之中，也欢迎你提出更好的想法，我们也会采纳。&lt;/li&gt;
&lt;li&gt; 开放的岗位偏向于 O2O，如果你有这个领域的经验就更好了。&lt;/li&gt;
&lt;li&gt; 工资我预估是 15k -20k 之间，只会负责一个具体的项目，所以偏向于中级程序员。&lt;/li&gt;
&lt;li&gt;除开工资之外，你负责的项目当公司抽成流水的时候，会给到你其中的 15%-25%。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你对这个岗位感兴趣，请给我发邮件 &lt;code&gt;qmy@one.work&lt;/code&gt;，附上你的简历（pdf 格式最佳）。由于这几天比较忙，我可能不能第一时间回复，出于对每位求职者的尊重，我会确保在 14 天内处理。&lt;/p&gt;
&lt;h2 id="后续"&gt;后续&lt;/h2&gt;
&lt;p&gt;除了以上岗位，我也在考虑一个城市合伙人计划，用来发展当地的中小企业数字化转型业务。对于这个城市合伙人，我希望你至少擅长一下 3 方面之一：&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Rails 专家，能够跟我一起完善 Work Design&lt;/li&gt;
&lt;li&gt; 在当地有资源或者人脉，这样接项目比较轻松；&lt;/li&gt;
&lt;li&gt; 综合能力强；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;对于城市合伙人的待遇是，当地的利润我们会抽成 5%~15%，你拥有 85%-95% 的利润。如果城市合伙人有资金，我也可以出让部分武汉总公司的股份，共享品牌、市场、基础建设等。&lt;/p&gt;

&lt;p&gt;技术人员不用太担心市场问题，只要技术有门槛，产品符合甲方需求，还是没那么难的，回头我也会分享一些做市场的心得。在创业之前，我也只是个每天拼命撸码的程序员，几乎不参与任何圈子和社交。创业之后，被迫自己得谈业务了，也还是能够适应这个角色转变，我认为我也会做得不错。当然，我不会让自己长期扮演这个角色，确实很浪费我宝贵的写代码时间。&lt;/p&gt;

&lt;p&gt;这个事情如何执行我还在思考当中，如果有兴趣的可以加我微信来聊聊（18571856813）。&lt;/p&gt;

&lt;p&gt;目前已经收到了一些简历，已经超出了第一阶段的需求，故暂停接收简历，其他交流依然欢迎加我微信。&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Sat, 30 May 2020 14:02:27 +0800</pubDate>
      <link>https://ruby-china.org/topics/39921</link>
      <guid>https://ruby-china.org/topics/39921</guid>
    </item>
    <item>
      <title> Ruby 程序员该何去何从，33 岁程序员失业后的创业分享</title>
      <description>&lt;h2 id="关于我"&gt;关于我&lt;/h2&gt;
&lt;p&gt;我是覃明圆，非科班出身（中医针灸专业），2011 年毕业，2014 年 1 月 1 日在上海找到第一份 Ruby 程序员工作，2015 年从上海回到武汉，在武汉历经 5 个公司，其中：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 家公司不再使用 Ruby 作为团队主力语言，仅维护老项目；&lt;/li&gt;
&lt;li&gt;1 家公司在武汉不再招聘 ruby；&lt;/li&gt;
&lt;li&gt;1 家公司倒闭；&lt;/li&gt;
&lt;li&gt;上家公司远程，受疫情影响，我所在的项目甲方资金链断裂；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;2020 年 3 月 19 日，我正式失业。&lt;/p&gt;

&lt;p&gt;走访了一圈在武汉的 Ruby 小伙伴们，一半转了其他语言（Go / Nodejs / 大前端 / Elixir），一半继续出走北上广。&lt;/p&gt;
&lt;h2 id="怎么办？"&gt;怎么办？&lt;/h2&gt;
&lt;p&gt;出走北上广肯定是不现实，33 岁了，不想再漂泊。&lt;/p&gt;

&lt;p&gt;转其他语言，代价很大。由于自己非科班出身，不能分摊自己有限的技术精力投入，我早已决定在 Rails 技术栈上尽可能成为专家。加上自己有 2 年产品经理经验，1 年创业经历。我给自己的定位是成为一名“懂产品”的技术人员。&lt;/p&gt;

&lt;p&gt;于是被迫创业。&lt;/p&gt;
&lt;h2 id="怎么做？"&gt;怎么做？&lt;/h2&gt;
&lt;p&gt;在创业这个事情上，我是早有心理准备的，这些年我做了以下准备：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;技术深入：Rails 擅长的领域是快速开发，我的目标是让 Rails 的特长得到充分发挥，深入挖掘 Rails 技术栈快速开发的潜力，通过对 Rails 框架的一些简单改造，相对于常规 Rails 开发，我的代码量减少了 50%  以上（更少的代码大概率等于更高的效率）。&lt;/li&gt;
&lt;li&gt;积累组件：程序员的积累有个原则，即尽量避免重复性的工作，我开发过的功能我会尽量避免再写一遍，于是我将相关功能抽出为 engine，这些年来我总共剥离了约 30 几个通用性的业务组件，&lt;a href="https://github.com/work-design/engine" rel="nofollow" target="_blank" title=""&gt;engine 列表&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;确定创业大概方向，提前准备品牌，注册域名，注册商标。特别是注册商标，不要怕麻烦，我有个商标因为与一个早我 3 个月注册的商标近似被驳回，多花了 2000 多块钱进行驳回复审，特别后悔。&lt;/li&gt;
&lt;li&gt;找合伙人。尽可能的找更多人聊自己创业的想法，一是别人会给你提意见，这会促进你修正你的商业模式。这个期间很可能会有小伙伴非常认可你的理念，把他放进你的候选合伙人里。我有两个合伙人是女的，我女朋友有些不太喜欢，我说实在是没有办法，我跟很多人聊过我的创业想法，目前为止就这俩女的对我的创业想法比较感兴趣。&lt;/li&gt;
&lt;li&gt;验证商业模式。正式创业之前，先找你的准客户聊聊，要聊到他们出钱的环节，我找了 4 个准客户进行了验证，效果还可以，有 1 个拒绝了，3 个表示可以合作。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="介绍我的项目"&gt;介绍我的项目&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://work.design" rel="nofollow" target="_blank" title=""&gt;Work Design&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/work-design" rel="nofollow" target="_blank" title=""&gt;开源地址&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;简单来说，Work Design 是一个开源的由众多组件组成的快速开发体系，Work Design 核心团队负责底层搭建，对 Ruby 程序员进行培训和技术管理。这名 Ruby 程序员我们将提供给我们的甲方，由甲方给 Ruby 程序员发工资。根据甲方使用的 Work Design 的组件，我们按年进行收费，这部分收入用于支持 Work Design 的持续开发以及对合作的甲方所雇佣的 Ruby 程序员进行技术输出和培训。&lt;/p&gt;

&lt;p&gt;目前 Work Design 还处于快速成长中，文档和测试还不太完善，我也正在借助甲方的项目打磨这个体系。在此也邀请对 Work Design 感兴趣的 Ruby 小伙伴加入进来，大家可以基于 Work Design 多年的积累去接自己的项目，同时参与到 Work Design 生态的完善中来。也可以加入我们的技术人员储备中来，当我们有新项目进来的时候，一起合作新项目。&lt;/p&gt;

&lt;p&gt;我相信 Work Design 是一个扩大 Rails 影响力到 Ruby 社区之外的事情，目前采用 Rails 的公司越来越少，工作机会越来越少，ruby 程序员也就越来越少。所以我们要利用 Work Design 先做甲方的市场增量，形成良性循环，让 Rails 爆发第二春。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://work.design/team" rel="nofollow" target="_blank" title=""&gt;官网&lt;/a&gt; 有我的微信二维码，欢迎同道中人添加。&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Sun, 24 May 2020 02:27:20 +0800</pubDate>
      <link>https://ruby-china.org/topics/39893</link>
      <guid>https://ruby-china.org/topics/39893</guid>
    </item>
    <item>
      <title>Webpacker 最佳实践</title>
      <description>&lt;p&gt;最近 rails 6 和 webpacker 的话题比较火，前有 &lt;a href="/lyfi2003" class="user-mention" title="@lyfi2003"&gt;&lt;i&gt;@&lt;/i&gt;lyfi2003&lt;/a&gt; 的高赞帖子，&lt;a href="https://ruby-china.org/topics/38832" rel="nofollow" target="_blank"&gt;https://ruby-china.org/topics/38832&lt;/a&gt;，后有 ruby conf 的 webpacker 主题精彩分享，蹭个热点，写下我对 webpacker 使用中的一些实践。&lt;/p&gt;

&lt;p&gt;自从 Rails 6 开始，官方默认采用 webpacker 来处理 Javascript，保留 Assets Pipeline(Sprockets) 作为 CSS、图片等静态资源处理方案。&lt;/p&gt;

&lt;p&gt;webpack 的引入显然解决了一些 Assets Pipiline 很难解决的痛点，比如对编写下一代 JS 的支持。&lt;/p&gt;

&lt;p&gt;虽然 webpacker 的生态也还在不断完善之中，但是从 assets pipeline 切换到 webpacker 也并非无痛的，最典型的场景就是对 Rails engine 中 assets 自动加载变得很难。&lt;/p&gt;

&lt;p&gt;不过我们可以自己动手，丰衣足食，尽可能减少迁移过程的痛苦，在此分享下我的实践。&lt;/p&gt;
&lt;h2 id="配置 JS 处理方式"&gt;配置 JS 处理方式&lt;/h2&gt;&lt;h3 id="移除 assets pipeline 对 js 处理的配置"&gt;移除 assets pipeline 对 js 处理的配置&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;app/assets/config/manifest.js&lt;/code&gt; 文件默认配置了 assets pipiline 需要处理的静态资源文件，把 js 相关的内容移除；&lt;/p&gt;

&lt;p&gt;如果 &lt;code&gt;app/config/application.rb&lt;/code&gt; 中定义了 assets.js_compressor，也一并移除；&lt;/p&gt;

&lt;p&gt;曾经的 &lt;code&gt;uglifier&lt;/code&gt;, &lt;code&gt;coffee-rails&lt;/code&gt; 等 gem 也可以永远拜拜了，再也不会为配置 execjs 的 runtime 而烦恼。&lt;/p&gt;
&lt;h3 id="修改 webpacker 配置"&gt;修改 webpacker 配置&lt;/h3&gt;
&lt;p&gt;webpacker 默认配置中，js 的路径是 &lt;code&gt;app/javascript/packs&lt;/code&gt;，为了兼容老项目，我们将其路径更改为 &lt;code&gt;app/assets/javascripts&lt;/code&gt;, 修改 &lt;code&gt;app/config/webpacker.yml&lt;/code&gt; 中的如下配置：&lt;/p&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;source_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app/assets&lt;/span&gt;
&lt;span class="na"&gt;source_entry_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;javascripts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="兼容 Rails engine"&gt;兼容 Rails engine&lt;/h2&gt;
&lt;p&gt;由于我们的 Rails 项目采用了组件化开发，引入了多个 engine，大量的 js 代码散布在 engine 的 &lt;code&gt;app/assets/javascripts&lt;/code&gt; 目录下。 &lt;/p&gt;

&lt;p&gt;如何才能让 webpack 在编译的时候能够加载 engine 下的 js 代码呢，webpack 的工作目录是 Rails 主项目，关键点就是如何让 webpack 知道各个 engine 在文件系统中的具体位置，也就是 ruby 和 js 之间分享数据。&lt;/p&gt;

&lt;p&gt;我采用了一个比较粗暴的办法，在 rails 项目启动完成的时候，将 engine 的位置信息更新到 config/webpacker.yml 文件当中，然后在 config/webpack 的配置文件中去解析这个文件，获取 engine 的路径信息。&lt;/p&gt;
&lt;h3 id="1. 在 rails 项目中导出 engine 路径信息"&gt;1. 在 rails 项目中导出 engine 路径信息&lt;/h3&gt;
&lt;p&gt;先实现对 webpacker.yml 文件的读写，代码如下：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# https://github.com/work-design/rails_com/blob/master/lib/rails_com/webpacker/yaml_helper.rb&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Webpacker&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;YamlHelper&lt;/span&gt;

    &lt;span class="c1"&gt;# uses config/webpacker_template.yml in rails_com engine as default,&lt;/span&gt;
    &lt;span class="c1"&gt;# config/webpacker_template.yml in Rails project will override this.&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;initialize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;template: &lt;/span&gt;&lt;span class="s1"&gt;'config/webpacker_template.yml'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;export: &lt;/span&gt;&lt;span class="s1"&gt;'config/webpacker.yml'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;template_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;existence&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="no"&gt;RailsCom&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;
      &lt;span class="n"&gt;export_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;root&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;export&lt;/span&gt;

      &lt;span class="vi"&gt;@yaml&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;YAML&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse_stream&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;read&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="vi"&gt;@content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@yaml.children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;children&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;children&lt;/span&gt;
      &lt;span class="vi"&gt;@parsed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@yaml.to_ruby&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="vi"&gt;@io&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;File&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;export_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'w+'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dump&lt;/span&gt;
      &lt;span class="vi"&gt;@yaml.yaml&lt;/span&gt; &lt;span class="vi"&gt;@io&lt;/span&gt;
      &lt;span class="vi"&gt;@io.fsync&lt;/span&gt;
      &lt;span class="vi"&gt;@io.close&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'default'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="no"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="vi"&gt;@parsed.dig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
      &lt;span class="n"&gt;env_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@content.find_index&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scalar?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;env&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="n"&gt;env_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;env_index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;children&lt;/span&gt;
      &lt;span class="n"&gt;key_index&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env_content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find_index&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;scalar?&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="n"&gt;value_content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;env_content&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key_index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value_content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sequence?&lt;/span&gt;
        &lt;span class="n"&gt;value_content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;style&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="c1"&gt;# block style&lt;/span&gt;
        &lt;span class="n"&gt;value_content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;children&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Psych&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Nodes&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Scalar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;

      &lt;span class="n"&gt;value_content&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后在 rails 初始化过程中增加一个回调，如果相应的 engine 下存在 app/assets/javascripts 文件夹，则将这个路径写入到&lt;code&gt;config/webpacker.yml&lt;/code&gt;文件。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# https://github.com/work-design/rails_com/blob/master/lib/rails_com/engine.rb#L30&lt;/span&gt;
&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;after_initialize&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;webpack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Webpacker&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;YamlHelper&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;
  &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subclasses&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
    &lt;span class="n"&gt;engine&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'app/assets'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;existent_directories&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;-&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end_with?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'javascripts'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="n"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt; &lt;span class="s1"&gt;'resolved_paths'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="n"&gt;webpack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id="2. js 通过数据文件 或许相关的路径信息；"&gt;2. js 通过数据文件 或许相关的路径信息；&lt;/h3&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// https://github.com/work-design/rails_com/blob/master/package/index.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;sync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;glob&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;extname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path-complete-extname&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@rails/webpacker/package/config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;extensions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;glob&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`**/*&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`**/*{&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;}`&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolved_paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;rootPath&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ab_paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nx"&gt;ab_paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;namespace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rootPath&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt;
      &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;paths&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里我们导出了所有的 js 文件路径，用于 webpack 的 entry 配置。&lt;/p&gt;
&lt;h3 id="3. 暴露 jquery, rails_ujs 等库"&gt;3. 暴露 jquery, rails_ujs 等库&lt;/h3&gt;
&lt;p&gt;一般的项目都使用了这两个 js 库，为了能够在代码里使用 &lt;code&gt;$('body')&lt;/code&gt;, &lt;code&gt;Rails.ajax&lt;/code&gt; 这样的代码，我们需要增加一点配置；&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt; &lt;span class="nx"&gt;https&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="c1"&gt;//github.com/work-design/rails_com/tree/master/package/loaders&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jquery&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="na"&gt;use&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expose-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jQuery&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;loader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;expose-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我没有对 webpack 和 expose-loader 的代码做深入阅读，不过我认为在 import jquery 的时候直接赋值给 windows.$ 也就解决问题了，不知道 expose-loader 是否还有其他的效果，知晓的朋友可以留言告知下。 &lt;/p&gt;
&lt;h3 id="4. 接下来修改下 config/webpack 下的 配置文件"&gt;4. 接下来修改下 config/webpack 下的 配置文件&lt;/h3&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// config/webpack/environment.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@rails/webpacker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rails_com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;jquery&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rails_com/package/loaders/jquery&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;loaders&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;jquery&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;jquery&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toWebpackConfig&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;modules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;concat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node_modules&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;env&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;config/webpacker.yml 中的 resolved_paths 配置了所有存在 js 文件的路径，用于配置 webpack 的 resolve.modules，告诉 webpack 在解析代码时需要搜索哪些路径，与 assets pipeline 的 assets.paths 配置功能一致；&lt;/p&gt;

&lt;p&gt;同时也配置了 babel-loader 中的生效路径。 &lt;/p&gt;

&lt;p&gt;至此，我们就可以直接编译和使用 engine 下所有的 js 文件了。&lt;/p&gt;
&lt;h2 id="改写 assets pipiline 中的 require 语法"&gt;改写 assets pipiline 中的 require 语法&lt;/h2&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//= require channels  // assets pipeline&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;channels&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="c1"&gt;// webpacker &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="其他提示"&gt;其他提示&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;webpack 相关配置文件是为 nodejs 使用的，所以使用 nodejs 的模块语法：&lt;code&gt;module.exports/require&lt;/code&gt;，前端 js 代码会经过 babel 编译，虽然 webpack 能理解 CommonJS 等多种模块体系，但是推荐使用 ES6 的 &lt;code&gt;export/import&lt;/code&gt; 语法。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;在 Rails 开发模式下，如果没有启动 webpack-dev-server, rails 会将前端代码编译到 public 目录下，此时修改 js 代码是不能立即生效的。所以推荐在开发 js 时，同时启动&lt;code&gt;bin/webpack-dev-server&lt;/code&gt;。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;当新增或删除了 js 文件之后，entry 改变之后，需要重启 bin/webpack-dev-server。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;由于 config/webpacker.yml 会根据项目的实际路径进行更新，建议将其在 git 中忽略。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;更新 Gemfile 配置：gem 'webpacker', require: File.exist?('config/webpacker.yml')，这样可以杜绝 config/webpacker.yml 未生成时的报错。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;config.webpacker.xxx = xx if config.respond_to?(:webpacker) 这个配置主要是解决上述第 5 条配置的副作用。&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;原文发表于： &lt;a href="https://work.design/rails/webpacker%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5" rel="nofollow" target="_blank"&gt;https://work.design/rails/webpacker%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;代码分布在：&lt;a href="https://github.com/work-design/rails_com" rel="nofollow" target="_blank"&gt;https://github.com/work-design/rails_com&lt;/a&gt;&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Sun, 08 Sep 2019 23:55:41 +0800</pubDate>
      <link>https://ruby-china.org/topics/39025</link>
      <guid>https://ruby-china.org/topics/39025</guid>
    </item>
    <item>
      <title>请教一个 each_with_object / inject /tap 相关 更优雅的写法</title>
      <description>&lt;p&gt;有数组 &lt;code&gt;[2,1,4,3]&lt;/code&gt;, 希望得到 &lt;code&gt;[ "2", "2/1", "2/1/4",  "2/1/4/3"]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;实现了个版本，觉得不够优雅。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;each_with_object&lt;/span&gt;&lt;span class="p"&gt;([])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;compact&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# =&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"1/2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"1/2/3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="s2"&gt;"1/2/3/4"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;有更好的实现方法么~&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Thu, 17 May 2018 11:15:14 +0800</pubDate>
      <link>https://ruby-china.org/topics/36789</link>
      <guid>https://ruby-china.org/topics/36789</guid>
    </item>
    <item>
      <title>==,  eql? , ===  核心类 Override 一览表</title>
      <description>&lt;p&gt;基本知识可参考社区多篇文章：&lt;a href="https://ruby-china.org/topics/20334" title=""&gt;https://ruby-china.org/topics/20334&lt;/a&gt;等&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;只检查类本身是否有 Override，有些类的父类做了 Override 了的；&lt;/li&gt;
&lt;li&gt;Module/Symbol虽然有Override &lt;code&gt;==&lt;/code&gt;方法，但是代码跟 BasicObject 的一样，可认为没有 Override；&lt;/li&gt;
&lt;li&gt;Range 中的 &lt;code&gt;===&lt;/code&gt;并不完全等于 &lt;code&gt;include?&lt;/code&gt;, 参数处理上略有差异；&lt;/li&gt;
&lt;/ul&gt;
&lt;table class="table table-bordered table-striped"&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;th&gt;Class/Module&lt;/th&gt;
&lt;th&gt;Override &lt;code&gt;==&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;Override &lt;code&gt;eql?&lt;/code&gt; (Same as &lt;code&gt;==&lt;/code&gt;)&lt;/th&gt;
&lt;th&gt;Override &lt;code&gt;===&lt;/code&gt; (Same as &lt;code&gt;==&lt;/code&gt;)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Array&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;√（×）&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hash&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;√（√）&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Range&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;√（×）&lt;/td&gt;
&lt;td&gt;√（&lt;code&gt;include?&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comparable&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Exception&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Numeric&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Complex&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Float&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;√（×）&lt;/td&gt;
&lt;td&gt;√（√）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integer&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;√（√）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rational&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MatchData&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;√（√）&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Method&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;√（√）&lt;/td&gt;
&lt;td&gt;√（&lt;code&gt;call&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Proc&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;√（&lt;code&gt;call&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Module&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Random&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regexp&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;√（√）&lt;/td&gt;
&lt;td&gt;√（×）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;String&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;√（×）&lt;/td&gt;
&lt;td&gt;√（√）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Struct&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;td&gt;√（×）&lt;/td&gt;
&lt;td&gt;×&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Symbol&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FalseClass&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TrueClass&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NilClass&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;x&lt;/td&gt;
&lt;td&gt;√&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;纯体力活，每一个我都点开 c 实现源码看了。很多文章说 &lt;code&gt;eql&lt;/code&gt;可认为是 &lt;code&gt;==&lt;/code&gt; 的别名，大部分类有差异的。&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Sat, 05 May 2018 20:58:27 +0800</pubDate>
      <link>https://ruby-china.org/topics/36688</link>
      <guid>https://ruby-china.org/topics/36688</guid>
    </item>
    <item>
      <title>MySQL 跨服务器同步某张表的数据过来，最佳实践是什么?</title>
      <description>&lt;p&gt;需求，其实只需要读某几张表的数据，不过数据库主服务器在国外，很慢；&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Triggers      &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Event Scheduler    &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Views   &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;主从复制 &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;federated 引擎（是否可以缓存）&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;有实践过的兄弟可以分享分享么？ &lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Tue, 13 Mar 2018 18:46:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/35226</link>
      <guid>https://ruby-china.org/topics/35226</guid>
    </item>
    <item>
      <title>给 activestorage 顺手撸了个七牛的 service</title>
      <description>&lt;p&gt;本来 &lt;a href="https://github.com/refile/refile" rel="nofollow" target="_blank" title=""&gt;Refile&lt;/a&gt; 是我心目中最接近完美的 ruby 文件上传的工具了，不过还是有些细节让人比较不爽。
现在 ActiveStorage 取代了 refile 在我心目中的地位，试用下来，比较满意（除了不能设置默认图片这个不爽以外），顺手为其写了个 qiniu 的 service &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/qinmingyuan/activestorage_qiniu" rel="nofollow" target="_blank"&gt;https://github.com/qinmingyuan/activestorage_qiniu&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;欢迎取用。&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Fri, 27 Oct 2017 16:31:48 +0800</pubDate>
      <link>https://ruby-china.org/topics/34445</link>
      <guid>https://ruby-china.org/topics/34445</guid>
    </item>
    <item>
      <title>Erubis 重生了</title>
      <description>&lt;p&gt;按说 Erubis 功能和性能上已经足够使了，不过一个自从 2011 年就不维护的项目，总给人感觉有点不踏实。&lt;/p&gt;

&lt;p&gt;新项目名字叫：Erubi&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jeremyevans/erubi" rel="nofollow" target="_blank" title=""&gt;项目地址&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rails/rails/pull/27757" rel="nofollow" target="_blank" title=""&gt;Rails Pull Request&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;新的维护者便是大名鼎鼎的&lt;code&gt;sequel&lt;/code&gt;的作者，与&lt;code&gt;Erubis&lt;/code&gt;原作者貌似都是&lt;a href="http://kuwata-lab.com" rel="nofollow" target="_blank" title=""&gt;Kuwata&lt;/a&gt;的成员。&lt;/p&gt;

&lt;p&gt;我不知道&lt;code&gt;Kuwata&lt;/code&gt;是个什么性质的组织，有知道的可以爆料一下。&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Sat, 21 Jan 2017 11:58:33 +0800</pubDate>
      <link>https://ruby-china.org/topics/32186</link>
      <guid>https://ruby-china.org/topics/32186</guid>
    </item>
    <item>
      <title>[武汉] 网化招聘 Ruby 中高级工程师 (欢迎回家)</title>
      <description>&lt;h2 id="[武汉] 网化招聘Ruby中高级工程师（欢迎回家）"&gt;[武汉] 网化招聘 Ruby 中高级工程师（欢迎回家）&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;工作地点：武汉·武昌·楚河汉街&lt;/li&gt;
&lt;li&gt;职位：Ruby 工程师（中级 3 名，高级 2 名）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="关于网化商城"&gt;关于网化商城&lt;/h2&gt;
&lt;p&gt;上海网化化工科技有限公司，是全球领先的专用化学品全品类免费撮合交易平台，专注于小批量化学品交易。我们的用户遍布全球 40 多个国家，在国内覆盖了几乎所有的大学实验室。&lt;/p&gt;
&lt;h3 id="旗下网站"&gt;旗下网站&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;whmall.com（网化商城），国内最大的化学品撮合交易平台；&lt;/li&gt;
&lt;li&gt;ichemical.com，面向境外的化学品交易平台；&lt;/li&gt;
&lt;li&gt;ichemical.in，立足于印度的化学品交易平台；&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="融资情况"&gt;融资情况&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;2016 年：A+ 轮，数千万元，普华资本；&lt;/li&gt;
&lt;li&gt;2015 年：A 轮，数千万元；&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="关于团队"&gt;关于团队&lt;/h2&gt;
&lt;p&gt;目前公司共有员工 100 多人，分布在上海、武汉、南京、重庆、印度、美国、荷兰各地，研发团队主要位于武汉，Ruby 团队共有成员 9 名，是武汉地区当之无愧的第一大以 ruby 技术为主导的公司。&lt;/p&gt;
&lt;h3 id="关于职位"&gt;关于职位&lt;/h3&gt;
&lt;p&gt;由于业务快速增长，B2B 领域业务的复杂性，现诚邀 Ruby 好手加盟。&lt;/p&gt;

&lt;p&gt;基本要求如下：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;2 年以上工作经验，能力突出者不限制此项；&lt;/li&gt;
&lt;li&gt;熟练掌握 Ruby，熟悉 Rails，Web 开发相关技术；&lt;/li&gt;
&lt;li&gt;熟练使用 Git；&lt;/li&gt;
&lt;li&gt;编程基础良好，追求优雅的代码（性能良好，易于他人理解，简约不绕弯子）；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;加分项：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;有写博客的习惯；&lt;/li&gt;
&lt;li&gt;维护或参与过开源项目；&lt;/li&gt;
&lt;li&gt;阅读过 Ruby on Rails 的源代码；&lt;/li&gt;
&lt;li&gt;有 TDD/BDD 的开发习惯；&lt;/li&gt;
&lt;li&gt;有重构的相关经验；&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;注重能力包括：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;解决问题能力。积极主动，做事有条理，目标导向；&lt;/li&gt;
&lt;li&gt;学习能力。乐于学习，善于总结分享，渴望每天都在进步；&lt;/li&gt;
&lt;li&gt;团队协作能力。坦诚，有责任心，尊重并包容团队成员；&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="福利待遇（该有的都有）"&gt;福利待遇（该有的都有）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;中级 8~13k，高级 13~18k；&lt;/li&gt;
&lt;li&gt;工作时间 9:00 - 17:30，双休（在武汉地区也许好像算是福利吧~）；&lt;/li&gt;
&lt;li&gt;带薪年假；&lt;/li&gt;
&lt;li&gt;五险一金；&lt;/li&gt;
&lt;li&gt;年度奖金（不少于 1 个月薪水，与业绩考核挂钩）；&lt;/li&gt;
&lt;li&gt;弹性工作制（暂为 Ruby 团队的专属福利，需申请，因为涉及到与业务部分协作，主要为来公司较远的同学提供便利）；&lt;/li&gt;
&lt;li&gt;全员 Mac 办公；&lt;/li&gt;
&lt;li&gt;月度团建基金，定期组织爬山，打球等活动，年度旅游（今年去咸宁泡了温泉）；&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="团队文化："&gt;团队文化：&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;开放：拥抱变化，有开源精神；&lt;/li&gt;
&lt;li&gt;高效：目标导向，结果驱动。善于找到最重要的工作并优先完成它们，有计划有条理地安排工作；&lt;/li&gt;
&lt;li&gt;成长：每周三 16:30-17:30 内部技术分享；&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="简历投放地址：andy_gxl@whmall.com"&gt;简历投放地址：andy_gxl@whmall.com&lt;/h3&gt;
&lt;p&gt;可年前面试，可远程视频面试，可年后入职。&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Mon, 09 Jan 2017 19:39:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/32083</link>
      <guid>https://ruby-china.org/topics/32083</guid>
    </item>
    <item>
      <title>Rails 的一个让人惊喜的重构，form_for 和 form_tag 终于不分裂了。</title>
      <description>&lt;h2 id="Provide form_with as a new alternative to form_for/form_tag"&gt;Provide form_with as a new alternative to form_for/form_tag&lt;/h2&gt;
&lt;p&gt;我是一直觉得 form_tag 没啥用，都可以用 form_for 解决，唯一的不足就是 form_for 生成的参数总是要带一个 scope。这下终于不用分裂了。&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rails/rails/issues/25197" rel="nofollow" target="_blank" title=""&gt;链接&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;已经 merge 了。
&lt;a href="https://github.com/rails/rails/pull/26976" rel="nofollow" target="_blank" title=""&gt;Pull Request&lt;/a&gt;&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Mon, 05 Dec 2016 09:52:34 +0800</pubDate>
      <link>https://ruby-china.org/topics/31794</link>
      <guid>https://ruby-china.org/topics/31794</guid>
    </item>
    <item>
      <title>我从 Vue.js 回到了 jQuery</title>
      <description>&lt;p&gt;之前用 Vue 做过一个纯前端的项目，与后端通过 api 交互数据，深深的感觉到了 Vue 的简约和强大。&lt;/p&gt;

&lt;p&gt;最近做一个 rails 项目也尝试着用上了 Vue,  发现 jQuery + turbolinks + ujs + erb 足够简单了，性能也足够好，很多用 DOM 操作很简单的事情，要换个思路硬是分离出数据和视图反而变复杂了。&lt;/p&gt;

&lt;p&gt;正好也不用担心客户端渲染引起的其他比如 SEO 的问题。&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Wed, 19 Oct 2016 17:25:46 +0800</pubDate>
      <link>https://ruby-china.org/topics/31383</link>
      <guid>https://ruby-china.org/topics/31383</guid>
    </item>
    <item>
      <title>透彻理解 Ruby 中的 return</title>
      <description>&lt;p&gt;在 ruby 中，有三个关键字可以从一段代码中返回，分别是&lt;code&gt;return&lt;/code&gt;、&lt;code&gt;next&lt;/code&gt;、&lt;code&gt;break&lt;/code&gt;，今天主要研究一下 return。&lt;/p&gt;
&lt;h2 id="方法中的return"&gt;方法中的 return&lt;/h2&gt;
&lt;p&gt;在 ruby 中调用一个方法，默认的情况下，会一行一行依次执行方法中的代码，方法的值是最后一行的值。&lt;/p&gt;

&lt;p&gt;return 可以改变这个方法一行一行执行的行为，当遇到 return，方法会直接返回（rescue、ensure 例外）。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'first puts'&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'next puts'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在上面这个例子中，&lt;code&gt;puts 'next puts'&lt;/code&gt;这行代码永远不会执行。同时我们可以显示指定返回值，默认为 nil。例如上面这个例子没有指定返回值，其返回值则为 nil。&lt;/p&gt;
&lt;h2 id="proc中的return"&gt;proc 中的 return&lt;/h2&gt;
&lt;p&gt;代码胜千言，先看几个例子：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test1&lt;/span&gt;
  &lt;span class="nb"&gt;proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'puts here'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test2&lt;/span&gt;
  &lt;span class="nb"&gt;proc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nb"&gt;proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'puts here'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test3&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'puts here'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="n"&gt;test4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Proc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;test4&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在上面的例子中：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;test1&lt;/code&gt;方法，&lt;code&gt;puts 'puts here'&lt;/code&gt;能够执行，返回值为最后一行代码的返回值。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;test2&lt;/code&gt;方法，返回值为 10。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;test3&lt;/code&gt;方法的行为和&lt;code&gt;test2&lt;/code&gt;是等价的。&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;test4&lt;/code&gt;在 call 调用的时候会报错，错误信息是：&lt;code&gt;LocalJumpError: unexpected return&lt;/code&gt;;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;为什么会这样，其实&lt;code&gt;test2&lt;/code&gt;和&lt;code&gt;test3&lt;/code&gt;已经告诉我们答案了，在 proc 中使用 return 和在方法本身中使用 return 的效果是一样的。&lt;/p&gt;

&lt;p&gt;而&lt;code&gt;test4&lt;/code&gt;的例子就相当于你直接执行&lt;code&gt;return 10&lt;/code&gt;，报的错误也同样是：&lt;code&gt;LocalJumpError: unexpected return&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;LocalJumpError&lt;/code&gt;这个错误其实很有意思，如果拦截这个错误，是依然可以拿到 return 的返回值的。
下面是我在 pry 中做的实验。&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; LocalJumpError: unexpected return&lt;/span&gt;

&lt;span class="n"&gt;_ex_&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; &amp;lt;LocalJumpError: unexpected return&amp;gt;&lt;/span&gt;

&lt;span class="n"&gt;_ex_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; LocalJumpError &amp;lt; StandardError&lt;/span&gt;

&lt;span class="n"&gt;_ex_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit_value&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; 10&lt;/span&gt;

&lt;span class="n"&gt;_ex_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reason&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; :return&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="lambda 中的return"&gt;lambda 中的 return&lt;/h2&gt;
&lt;p&gt;还是继续看例子：&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;test1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'first puts'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'next puts'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;test1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;

&lt;span class="c1"&gt;# first puts&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; 10&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test2&lt;/span&gt;
  &lt;span class="n"&gt;la&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'first puts'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'next puts'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;la&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s1"&gt;'puts here'&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# first puts&lt;/span&gt;
&lt;span class="c1"&gt;# puts here&lt;/span&gt;
&lt;span class="c1"&gt;#=&amp;gt; nil&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;lambda 对象，跟 proc 对象不一样，在 lambda 中，return 是从 lambda 代码块中返回。在 proc 中，return 是从 proc 代码块所在的上下文环境中返回。&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Sun, 22 Mar 2015 23:07:13 +0800</pubDate>
      <link>https://ruby-china.org/topics/24783</link>
      <guid>https://ruby-china.org/topics/24783</guid>
    </item>
    <item>
      <title>小米和京东的在线咨询是用的什么搭建的？</title>
      <description>&lt;p&gt;如题。
感觉挺简单清新的。&lt;/p&gt;

&lt;p&gt;&lt;a href="http://chat.kefu.xiaomi.com/" rel="nofollow" target="_blank"&gt;http://chat.kefu.xiaomi.com/&lt;/a&gt;&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Tue, 20 Aug 2013 11:01:34 +0800</pubDate>
      <link>https://ruby-china.org/topics/13452</link>
      <guid>https://ruby-china.org/topics/13452</guid>
    </item>
    <item>
      <title>难道 api.rubyonrails.org 被墙了？</title>
      <description>&lt;p&gt;从昨天下午开始就没能打开，翻墙 OK，大家能打开么？
就指着它活着了啊。。
fuck gfw&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Fri, 02 Aug 2013 11:02:13 +0800</pubDate>
      <link>https://ruby-china.org/topics/12998</link>
      <guid>https://ruby-china.org/topics/12998</guid>
    </item>
    <item>
      <title>有哪种程序语言同时拥有原型继承和类继承的特性的么？</title>
      <description>&lt;p&gt;js 的部分设计思维很先进，原型继承和类继承分别体现了真实世界里的两种事物继承关系。不知道有没有哪种程序语言同时有这两种特性的？&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Mon, 15 Jul 2013 10:38:42 +0800</pubDate>
      <link>https://ruby-china.org/topics/12509</link>
      <guid>https://ruby-china.org/topics/12509</guid>
    </item>
    <item>
      <title>gem paths 怎么设置啊？</title>
      <description>&lt;p&gt;我要更改 gem 安装的地址。
试了好多种办法也不知道怎么改。&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Wed, 03 Jul 2013 08:49:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/12202</link>
      <guid>https://ruby-china.org/topics/12202</guid>
    </item>
    <item>
      <title>使用 Turbolinks 过程遇到的小坑</title>
      <description>&lt;p&gt;rails4 出来了嘛
先慢慢过渡，用上 Turbolinks 的插件。
结果页面加载之后需要再刷新一遍才能显示。&lt;/p&gt;

&lt;p&gt;问题解决：
 If you have any script tags in the body you do not want to be re-evaluated then you can set the data-turbolinks-eval attribute to&lt;code&gt;false&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;script type="text/javascript" data-turbolinks-eval=false&amp;gt;
  console.log("I'm only run once on the initial page load");
&amp;lt;/script&amp;gt;&lt;/code&gt;&lt;/p&gt;</description>
      <author>mingyuan0715</author>
      <pubDate>Tue, 02 Jul 2013 11:31:16 +0800</pubDate>
      <link>https://ruby-china.org/topics/12177</link>
      <guid>https://ruby-china.org/topics/12177</guid>
    </item>
  </channel>
</rss>
