<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>a-wing</title>
    <link>https://ruby-china.org/a-wing</link>
    <description></description>
    <language>en-us</language>
    <item>
      <title>一篇超水的 Rails ActiveStorage 源码解读 (后端部分)</title>
      <description>&lt;p&gt;源地址：&lt;a href="https://a-wing.top/ruby/2018/09/26/rails_activestorage_backend.html" rel="nofollow" target="_blank"&gt;https://a-wing.top/ruby/2018/09/26/rails_activestorage_backend.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;ActiveStorage 是 Rails 5.2 的一个新特性，建议你先去读完官方的文档再回看来这个&lt;/p&gt;

&lt;p&gt;&lt;a href="https://guides.rubyonrails.org/active_storage_overview.html" rel="nofollow" target="_blank" title=""&gt;https://guides.rubyonrails.org/active_storage_overview.html&lt;/a&gt;&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;▾ activestorage-5.2.1/
  ▾ app/
    ▸ assets/javascripts/
    ▾ controllers/
      ▸ active_storage/
      ▸ concerns/active_storage/
    ▸ javascript/activestorage/
    ▾ jobs/active_storage/
        analyze_job.rb
        base_job.rb
        purge_job.rb
    ▾ models/active_storage/
      ▾ blob/
          analyzable.rb
          identifiable.rb
          representable.rb
      ▸ filename/
        attachment.rb
        blob.rb
        current.rb
        filename.rb
        preview.rb
        variant.rb
        variation.rb
  ▾ config/
      routes.rb
  ▾ db/migrate/
      20170806125915_create_active_storage_tables.rb
  ▾ lib/
    ▾ active_storage/
      ▾ analyzer/
          image_analyzer.rb
          null_analyzer.rb
          video_analyzer.rb
      ▾ attached/
          macros.rb
          many.rb
          one.rb
      ▾ previewer/
          mupdf_previewer.rb
          poppler_pdf_previewer.rb
          video_previewer.rb
      ▾ service/
          azure_storage_service.rb
          configurator.rb
          disk_service.rb
          gcs_service.rb
          mirror_service.rb
          s3_service.rb
        analyzer.rb
        attached.rb
        downloading.rb
        engine.rb
        errors.rb
        gem_version.rb
        log_subscriber.rb
        previewer.rb
        service.rb
        version.rb
    ▾ tasks/
        activestorage.rake
      active_storage.rb
    CHANGELOG.md
    MIT-LICENSE
    README.md
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="数据结构"&gt;数据结构&lt;/h2&gt;
&lt;p&gt;先看 &lt;code&gt;db/migrate/20170806125915_create_active_storage_tables.rb&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;我们能看到两张表，其中一张表储存的是文件信息。。en，en 文件的基本信息&lt;/p&gt;

&lt;p&gt;另一张表是储存文件的对象信息&lt;/p&gt;
&lt;h4 id="active_storage_blobs"&gt;active_storage_blobs&lt;/h4&gt;
&lt;p&gt;这个表储存了所有文件的基本信息，我们可以看到这个表只有 &lt;code&gt;created_at&lt;/code&gt; ，因为这张表被设计成只能添加和删除。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;key&lt;/code&gt; 是文件唯一的索引。 &lt;code&gt;Disk service&lt;/code&gt; 默认把文件放在 storage 目录下，然后建立了以&lt;code&gt;key&lt;/code&gt;值开头两位命名的两个目录。。。。有点绕）&lt;/p&gt;

&lt;p&gt;就像这样，来解决大量文件的查找问题：&lt;code&gt;storage/qS/gY/qSgYgMNvwQNzpvBsx91QHQwW&lt;/code&gt;&lt;/p&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;rsd_development&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;active_storage_blobs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                                                                                                                                                                                         
 &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;           &lt;span class="k"&gt;key&lt;/span&gt;            &lt;span class="o"&gt;|&lt;/span&gt;  &lt;span class="n"&gt;filename&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="n"&gt;content_type&lt;/span&gt;       &lt;span class="o"&gt;|&lt;/span&gt;              &lt;span class="n"&gt;metadata&lt;/span&gt;               &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;byte_size&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="n"&gt;checksum&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="n"&gt;created_at&lt;/span&gt;                                                                      
&lt;span class="c1"&gt;----+--------------------------+------------+--------------------------+-------------------------------------+-----------+--------------------------+----------------------------                                                             &lt;/span&gt;
  &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;mjqZCsJTxwiaiPGzNUraGJ1o&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;a2&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;octet&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;"identified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;"analyzed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;vM3NT8ob8&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;i6vRUFujxB1g&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;738182&lt;/span&gt;                                                           
 &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;gx855a8F8TqQgk53tPGAdyN1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;1536642794&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;octet&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;"identified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;"analyzed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;1412&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="n"&gt;JRyTuP0rvD28H0ydVwfWg&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;485237&lt;/span&gt;                                                              
 &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;qSgYgMNvwQNzpvBsx91QHQwW&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;a2&lt;/span&gt;         &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;octet&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;"identified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;"analyzed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;vM3NT8ob8&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;i6vRUFujxB1g&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;673785&lt;/span&gt;                                                              
 &lt;span class="mi"&gt;13&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;gEVVuvBmrYE6CKCYePMATUv9&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;version&lt;/span&gt;    &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;application&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;octet&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;"identified"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;"analyzed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="mi"&gt;6&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="n"&gt;nv1yrmgG0DBXNusnpSfWA&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;52&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;81699&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h4 id="active_storage_attachments"&gt;active_storage_attachments&lt;/h4&gt;&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="n"&gt;rsd_development&lt;/span&gt;&lt;span class="o"&gt;=#&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;active_storage_attachments&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;record_type&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;record_id&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;blob_id&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="n"&gt;created_at&lt;/span&gt;
&lt;span class="c1"&gt;----+------+-------------+-----------+---------+----------------------------&lt;/span&gt;
  &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Plan&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;       &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;18&lt;/span&gt; &lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;57&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;742653&lt;/span&gt;
 &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Plan&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;10&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;489101&lt;/span&gt;
 &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Plan&lt;/span&gt;        &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;11&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;08&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;36&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;677669&lt;/span&gt;
 &lt;span class="mi"&gt;14&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="k"&gt;map&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;MissionLog&lt;/span&gt;  &lt;span class="o"&gt;|&lt;/span&gt;         &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;      &lt;span class="mi"&gt;13&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="mi"&gt;2018&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="mi"&gt;07&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;52&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;820024&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这张表是通过&lt;code&gt;对象名&lt;/code&gt;和&lt;code&gt;属性名&lt;/code&gt;和&lt;code&gt;对象id&lt;/code&gt;来绑定文件的。。。如果你改了。类名。。那就呵呵了&lt;/p&gt;

&lt;p&gt;不同的&lt;code&gt;attachments&lt;/code&gt;是可以指定同一个&lt;code&gt;blobs&lt;/code&gt;的，但是更新&lt;code&gt;blobs&lt;/code&gt;只有一个会更新（应该是删了重建）。。。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;这里应该算个坑&lt;/strong&gt; &lt;code&gt;blobs&lt;/code&gt;在删除时并不会检测&lt;code&gt;attachments&lt;/code&gt;里是否有包含这个&lt;code&gt;blobs&lt;/code&gt;(我不知道他为什么这么实现啊。喵) 咱觉得这应该算 bug。。。或者每次检查开销太大？留给开发者自己解决？&lt;/p&gt;
&lt;h2 id="云存储 lib/active_storage/service/"&gt;云存储 &lt;code&gt;lib/active_storage/service/&lt;/code&gt;
&lt;/h2&gt;
&lt;p&gt;这里面除了 &lt;code&gt;local&lt;/code&gt; 和 &lt;code&gt;mirrors&lt;/code&gt; 默认还集成了 Amazon S3 Service，Microsoft Azure Storage Service，Google Cloud Storage Service 服务，当然还要引入对应的 SDK&lt;/p&gt;

&lt;p&gt;active_storage 和 active_job 一样，只是提供一个中间层。具体还要引入对应云服务的 sdk&lt;/p&gt;

&lt;p&gt;大概是这样：
active_storage -&amp;gt; active_storage_云服务 -&amp;gt;  云服务 SDK&lt;/p&gt;

&lt;p&gt;国内的可以用 upyun，qiniu, aliyun 都有 gem 可以直接用&lt;/p&gt;
&lt;h2 id="attached 附件"&gt;attached 附件&lt;/h2&gt;
&lt;p&gt;这里面有三个文件，有个叫&lt;code&gt;macros&lt;/code&gt; 值得一看，在注释里说了关于 &lt;code&gt;has_one_attached&lt;/code&gt; 和 &lt;code&gt;has_many_attached&lt;/code&gt; 的 N+1 查询问题的解决办法&lt;/p&gt;

&lt;p&gt;我来抄段源码：&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;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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="n"&gt;has_one_attached&lt;/span&gt; &lt;span class="ss"&gt;:avatar&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;# There is no column defined on the model side, Active Storage takes
    # care of the mapping between your records and the attachment.
    #
    # To avoid N+1 queries, you can include the attached blobs in your query like so:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;User&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;with_attached_avatar&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Gallery&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&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="n"&gt;has_many_attached&lt;/span&gt; &lt;span class="ss"&gt;:photos&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;# There are no columns defined on the model side, Active Storage takes
    # care of the mapping between your records and the attachments.
    #
    # To avoid N+1 queries, you can include the attached blobs in your query like so:&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Gallery&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;user: &lt;/span&gt;&lt;span class="no"&gt;Current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;with_attached_photos&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="其他"&gt;其他&lt;/h2&gt;
&lt;p&gt;然后就是一些分析啊，预览啊之类的功能。。。没啥说的。。。&lt;/p&gt;
&lt;h2 id="activestorage 文件存储的理解"&gt;activestorage 文件存储的理解&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;activestorage&lt;/code&gt; 把 &lt;code&gt;Disk service&lt;/code&gt; 也当作云盘来处理，因此不会有 &lt;code&gt;.path&lt;/code&gt;之类的获取路径的方法。所有的获取文件都是通过&lt;code&gt;.download&lt;/code&gt;方法来实现的，如果要传路径，就先存在一个 tmp 的目录中&lt;/p&gt;

&lt;p&gt;还有我觉得官方的文档 &lt;code&gt;.download&lt;/code&gt; 的那个方法的用法太有误导性。只要在 rails 获取文件对象就要用&lt;code&gt;.download&lt;/code&gt;来获取（我当时以为是用户给 url, 然后后台下载。。。。）&lt;/p&gt;

&lt;p&gt;后端部分可以拿出来单独用。。。前端部分我还没仔细研究。。不清楚。。。但是他的开发者发了 npm 的包。前后端分离项目用 activestorage 好像也不是什么难事。。enen。大概不是很难&lt;/p&gt;</description>
      <author>a-wing</author>
      <pubDate>Wed, 26 Sep 2018 17:53:14 +0800</pubDate>
      <link>https://ruby-china.org/topics/37554</link>
      <guid>https://ruby-china.org/topics/37554</guid>
    </item>
    <item>
      <title>[折腾向] 使用 systemd 来管理你的 Rails 应用</title>
      <description>&lt;p&gt;原文地址：
&lt;a href="https://a-wing.top/ruby/2018/09/10/systemd_rails.html" rel="nofollow" target="_blank"&gt;https://a-wing.top/ruby/2018/09/10/systemd_rails.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这篇文章属于没什么卵用系列&lt;/p&gt;

&lt;p&gt;我并不喜欢 &lt;code&gt;capistrano&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;众所周知 rails 的项目启动用 &lt;code&gt;RAILS_ENV=production bundle exec rails server -p 3000 -d&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;如果服务器经常重启。。最简单的办法：那我们就要把这条命令放在 /etc/rc.local 里 (目前 deb 系的发行版是保留的。本质上是用 systemd 来执行这里的命令)。。。&lt;/p&gt;

&lt;p&gt;不过目前。大部分 Linux Distribution 都是 systemd 来启动的（gentoo 除外。gentoo 默认是 openrc，可以换成 systemd）&lt;/p&gt;

&lt;p&gt;在改之前。我们先把 rails 监听的端口换成 unix sock&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s#port        ENV.fetch("PORT") { 3000 }#bind        "unix://\#{Rails.root}/tmp/sockets/puma.sock"#g'&lt;/span&gt; config/puma.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例如我的项目叫 kiss2u 大概是这样：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; ~/.config/systemd/user/kiss2u.service
&lt;/code&gt;&lt;/pre&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Unit]
Description=KISS2U Puma application server
After=network.target

[Service]
WorkingDirectory=/home/a-wing/.srv/KISS2U
PIDFile=/home/a-wing/.srv/KISS2U/tmp/pids/puma.pid
ExecStart=/usr/bin/sh /home/a-wing/.srv/KISS2U/run.sh

[Install]
WantedBy=default.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;没错。这个是用户级 systemd&lt;/p&gt;

&lt;p&gt;但由于 systemd 要写绝对路径。。但我可不想手动改 QAQ&lt;/p&gt;

&lt;p&gt;然后我们定义一个 template，就像这样。定义几个标签&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ cat config/kiss2u.service.template
[Unit]
Description=KISS2U Puma application server
After=network.target

[Service]
WorkingDirectory=&amp;lt;DIR&amp;gt;
Environment=RAILS_ENV=production
PIDFile=&amp;lt;DIR&amp;gt;/tmp/pids/puma.pid
ExecStart=&amp;lt;BUNDLE&amp;gt; exec rails server

[Install]
WantedBy=default.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在 bin 目录下创建生成真正的 systemd 的配置文件 bin/generate_service.sh&lt;/p&gt;

&lt;p&gt;当然我们是要新建一个用户来跑的。不然 generate_service.sh 就没意义了&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="nv"&gt;SYSTEMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;kiss2u.service

&lt;span class="nb"&gt;cp &lt;/span&gt;config/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SYSTEMD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;.template &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SYSTEMD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; s#&lt;span class="se"&gt;\&amp;lt;&lt;/span&gt;DIR&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;#`pwd`#g ${SYSTEMD}&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; s#&lt;span class="se"&gt;\&amp;lt;&lt;/span&gt;BUNDLE&lt;span class="se"&gt;\&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;#`which bundle`#g ${SYSTEMD}&lt;/span&gt;


&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Install ~/.config/systemd/user/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SYSTEMD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Please Run: systemctl --user start &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SYSTEMD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SYSTEMD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; ~/.config/systemd/user/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SYSTEMD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SYSTEMD&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后来使用 &lt;code&gt;systemctl --user start kiss2u&lt;/code&gt; 就可以启动了&lt;/p&gt;

&lt;p&gt;要开机启动的话 &lt;code&gt;systemctl --user enable kiss2u&lt;/code&gt; 就可以&lt;/p&gt;
&lt;h3 id="不过。这么做仍然有问题"&gt;&lt;strong&gt;不过。这么做仍然有问题&lt;/strong&gt;&lt;/h3&gt;
&lt;p&gt;我在 rbenv 下测试是好使的。。。在有些环境下就会出现启动不了的情况。我也不知道是为什么啊 QAQ&lt;/p&gt;

&lt;p&gt;如果你启动出问题请更改 &lt;code&gt;ExecStart=&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;解决办法：兼容性最好&lt;code&gt;ExecStart=&lt;/code&gt; 来启动一段 shell 脚本。在 shell 脚本里加载必要的变量&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ./config.sh

&lt;span class="nb"&gt;echo &lt;/span&gt;&lt;span class="nv"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
ruby &lt;span class="nt"&gt;-v&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; tmp/pids/server.pid
&lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Restarting'&lt;/span&gt;
  &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;tmp/pids/server.pid&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Starting'&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;bundle &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--path&lt;/span&gt; vendor/bundle
bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rake db:migrate
bundle &lt;span class="nb"&gt;exec &lt;/span&gt;rails server &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;类似这样。。。好吧。我承认这很不优雅。。。不过兼容性最好。。&lt;/p&gt;
&lt;h3 id="对了 kiss2u 是开源项目，你可以直接去看源代码 &lt;-----自买自夸"&gt;对了 kiss2u 是开源项目，你可以直接去看源代码 &amp;lt;-----自买自夸&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/a-wing/KISS2U" rel="nofollow" target="_blank" title=""&gt;https://github.com/a-wing/KISS2U&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;参考文章：&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://wiki.archlinux.org/index.php/Rails" rel="nofollow" target="_blank" title=""&gt;Arch Linux wiki&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.lilydjwg.me/2014/2/2/systemd-user-daemons.42631.html" rel="nofollow" target="_blank" title=""&gt;依云's Blog: systemd 之用户级服务管理&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>a-wing</author>
      <pubDate>Tue, 11 Sep 2018 11:25:24 +0800</pubDate>
      <link>https://ruby-china.org/topics/37470</link>
      <guid>https://ruby-china.org/topics/37470</guid>
    </item>
    <item>
      <title>[闲聊] [吹水]  如果让你去面试 Ruby 的应聘者，你会问什么问题？</title>
      <description>&lt;p&gt;我可能会问动态指派，幽灵方法之类的&lt;/p&gt;

&lt;p&gt;或者 Rails 各模块&lt;/p&gt;

&lt;p&gt;《《----我就是个渣渣。&lt;/p&gt;

&lt;p&gt;有人会问算法吗？&lt;/p&gt;</description>
      <author>a-wing</author>
      <pubDate>Sat, 25 Aug 2018 10:19:30 +0800</pubDate>
      <link>https://ruby-china.org/topics/37386</link>
      <guid>https://ruby-china.org/topics/37386</guid>
    </item>
    <item>
      <title>rails 关于 send_file 方法</title>
      <description>&lt;p&gt;开发环境下正常&lt;/p&gt;

&lt;p&gt;Sent file public/plans/1523708711 (1.3ms)&lt;/p&gt;

&lt;p&gt;到产品环境下就报错&lt;/p&gt;

&lt;p&gt;ActionController::MissingFile (Cannot read file public/plans/1524803824):&lt;/p&gt;

&lt;p&gt;可能和这个地方有关。。还是必须用 NGINX 之类的设置服务目录。。。（下载文件这个功能用的少。我不需要优化啊？）&lt;/p&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;
&lt;span class="c1"&gt;# Disable serving static files from the `/public` folder by default since&lt;/span&gt;
  &lt;span class="c1"&gt;# Apache or NGINX already handles this.&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;public_file_server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'RAILS_SERVE_STATIC_FILES'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;present?&lt;/span&gt;

  &lt;span class="c1"&gt;# Specifies the header that your server uses for sending files.&lt;/span&gt;
  &lt;span class="c1"&gt;# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache&lt;/span&gt;
  &lt;span class="c1"&gt;# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;</description>
      <author>a-wing</author>
      <pubDate>Fri, 25 May 2018 13:33:35 +0800</pubDate>
      <link>https://ruby-china.org/topics/36826</link>
      <guid>https://ruby-china.org/topics/36826</guid>
    </item>
    <item>
      <title>关于 ruby 程序 Ctrl-C</title>
      <description>&lt;p&gt;在进程结束之前要执行一个函数&lt;/p&gt;

&lt;p&gt;有没有结束之前的钩子函数之类的东西？&lt;/p&gt;

&lt;p&gt;还是只能捕获 Ctrl-C 信号，来运行函数之后退出？&lt;/p&gt;

&lt;p&gt;ruby 如果要捕获 Ctrl-C 信号，要用哪个模块？&lt;/p&gt;</description>
      <author>a-wing</author>
      <pubDate>Thu, 17 May 2018 16:39:34 +0800</pubDate>
      <link>https://ruby-china.org/topics/36793</link>
      <guid>https://ruby-china.org/topics/36793</guid>
    </item>
    <item>
      <title>如何优雅的在 rails 动态的添加表单项目</title>
      <description>&lt;p&gt;比如我有个表单&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;name&lt;/li&gt;
&lt;li&gt;item

&lt;ul&gt;
&lt;li&gt;1&lt;/li&gt;
&lt;li&gt;2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;点一下按钮添加在 item 里添加（减少）一项。&lt;/p&gt;

&lt;p&gt;第一反应是用 JavaScript 不过感觉里面写一坨 JavaScript 感觉好不优雅。。。。&lt;/p&gt;

&lt;p&gt;目前在按钮里添加一个路由连接传递一个参数，重新渲染这个页面来实现的&lt;/p&gt;
&lt;h4 id="有没有什么优雅的写法？"&gt;有没有什么优雅的写法？&lt;/h4&gt;</description>
      <author>a-wing</author>
      <pubDate>Sat, 28 Apr 2018 16:46:43 +0800</pubDate>
      <link>https://ruby-china.org/topics/36400</link>
      <guid>https://ruby-china.org/topics/36400</guid>
    </item>
    <item>
      <title>关于在 rails 库里使用 ruby gem 该怎么配置</title>
      <description>&lt;p&gt;我说的是这个库原本是 ruby 的。我想在 rails 里使用&lt;/p&gt;

&lt;p&gt;我用的是 net-ssh&lt;/p&gt;

&lt;p&gt;应该在 rails 初始化时 &lt;code&gt;require 'net/scp'&lt;/code&gt;。在 rails 框架中应该写在那？&lt;/p&gt;

&lt;p&gt;我尝试写在 boot.rb 里。然后处理 rails server 之外的都不好使了。。。。&lt;/p&gt;</description>
      <author>a-wing</author>
      <pubDate>Mon, 26 Mar 2018 21:36:57 +0800</pubDate>
      <link>https://ruby-china.org/topics/35328</link>
      <guid>https://ruby-china.org/topics/35328</guid>
    </item>
    <item>
      <title>分享__水。。从开发到上线 Rails 项目自动部署</title>
      <description>&lt;h4 id="一个项目从开发到上线。该记录点什么。"&gt;一个项目从开发到上线。该记录点什么。&lt;/h4&gt;
&lt;p&gt;大概在几个月前开始开发一个管理系统，中间为了练手又写了个开源程序 &lt;a href="https://github.com/MoePlayer/RailsGun" rel="nofollow" target="_blank" title=""&gt;RailsGun&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;我觉得值得记录的就是从 push 之后的自动化流程了&lt;/p&gt;

&lt;p&gt;私有项目也在 github&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1.push 提交代码触发 github webhook&lt;/li&gt;
&lt;li&gt;2.jenkins 跑持续集成，跑测试单元（公司项目用 jenkins。练手项目用 travis-ci.org。开源项目用 org 结尾的域名。闭源项目用 com 结尾的域名）&lt;/li&gt;
&lt;li&gt;3.自动升级部署脚本&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="github 触发没啥说的。github 有jenkins 应用插件"&gt;github 触发没啥说的。github 有 jenkins 应用插件&lt;/h3&gt;&lt;h3 id="jenkins"&gt;jenkins&lt;/h3&gt;
&lt;p&gt;jenkins 要装 github 插件。接收 github 的触发 hook&lt;/p&gt;

&lt;p&gt;当然检测发布分支&lt;/p&gt;

&lt;p&gt;为 jenkins 服务器单独添加 deploy keys. 把公钥传给 github 以确保可以 clone 代码&lt;/p&gt;

&lt;p&gt;jenkins 有 rvm 插件。ruby 环境就变得很容易了。（刚开始不知道 rvm，手动配环境。终于明白 rvm 有多复杂了。手动配建议用 rbenv）&lt;/p&gt;

&lt;p&gt;rails test 我知道我测试单元写的很烂。。对集成测试单元测试的理解还不够&lt;/p&gt;
&lt;h3 id="自动部署。（这里是重头戏）"&gt;自动部署。（这里是重头戏）&lt;/h3&gt;
&lt;p&gt;先上代码：&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;RAILS_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;production
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;POSTGRESQL_DATABASE_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;97b841a29f151ed58e64c
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SECRET_KEY_BASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;97b841a29f151ed58e64cc2d7aa6c666360a8a470fde108fec2aff7ed103db38ecbaa85a533bd39890b3f83adcb0000274a7e6b3fc36b9b0830dc30b97a2818d


&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.rbenv/bin:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/.rbenv/shims"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$RAILS_ENV&lt;/span&gt;
&lt;span class="nb"&gt;pwd
&lt;/span&gt;ruby &lt;span class="nt"&gt;-v&lt;/span&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;程序路径/
&lt;span class="nb"&gt;pwd

&lt;/span&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; tmp/pids/server.pid
&lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Restarting'&lt;/span&gt;
    &lt;span class="nb"&gt;kill&lt;/span&gt; &lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;tmp/pids/server.pid&lt;span class="sb"&gt;`&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'Starting'&lt;/span&gt;
&lt;span class="k"&gt;fi

&lt;/span&gt;bundle &lt;span class="nb"&gt;install
&lt;/span&gt;rake db:migrate
rails assets:precompile
rails server &lt;span class="nt"&gt;-d&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;每次覆盖代码运行这个脚本&lt;/p&gt;

&lt;p&gt;原理：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;程序执行之后会创建./tmp/pids/server.pid 文件。并把 pid 写入里面&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;我在测试的时候加了这段代码，不然会找不到编译后的 js 和 css&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/config.assets.compile = false/config.assets.compile = true/g'&lt;/span&gt; config/environments/production.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我不太理解 rails 这个默认参数。以后有待补充&lt;/p&gt;

&lt;p&gt;摘自个人博客 &lt;a href="https://a-wing.top/ruby/2018/03/13/rails_production_deployment.html" rel="nofollow" target="_blank"&gt;https://a-wing.top/ruby/2018/03/13/rails_production_deployment.html&lt;/a&gt;&lt;/p&gt;
&lt;h2 id="求各位大佬轻点喷"&gt;求各位大佬轻点喷&lt;/h2&gt;
&lt;p&gt;如果觉得我的思路有问题请一定要指出，感谢&lt;/p&gt;</description>
      <author>a-wing</author>
      <pubDate>Tue, 13 Mar 2018 21:27:49 +0800</pubDate>
      <link>https://ruby-china.org/topics/35227</link>
      <guid>https://ruby-china.org/topics/35227</guid>
    </item>
    <item>
      <title>type 好像是保留字段，但前端用了 type 字段该怎么办</title>
      <description>&lt;p&gt;要在控制器里做一层转换？？&lt;/p&gt;

&lt;p&gt;前后分离，前端提交的 json 里有 type 字段&lt;/p&gt;

&lt;p&gt;返回的数据也要有 type 字段。。。。。&lt;/p&gt;

&lt;p&gt;怎么把这么常用的字段保留。。。。。。&lt;/p&gt;</description>
      <author>a-wing</author>
      <pubDate>Fri, 09 Feb 2018 11:46:21 +0800</pubDate>
      <link>https://ruby-china.org/topics/35017</link>
      <guid>https://ruby-china.org/topics/35017</guid>
    </item>
    <item>
      <title>萌新问题，关于 ActiveRecord</title>
      <description>&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="vi"&gt;@nodes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Node&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;.....................&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="nf"&gt;render&lt;/span&gt; &lt;span class="ss"&gt;json: &lt;/span&gt;&lt;span class="vi"&gt;@nodes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后我想在&lt;a href="/nodes" class="user-mention" title="@nodes"&gt;&lt;i&gt;@&lt;/i&gt;nodes&lt;/a&gt;里加一个字段
比如：&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="/nodes.video" class="user-mention" title="@nodes.video"&gt;&lt;i&gt;@&lt;/i&gt;nodes.video&lt;/a&gt; = "aaa"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;该怎么写？？？&lt;/p&gt;

&lt;p&gt;反正就是这个意思，不知道有没有表达清楚&lt;/p&gt;

&lt;p&gt;我知道在 ruby 里 &lt;a href="/nodes" class="user-mention" title="@nodes"&gt;&lt;i&gt;@&lt;/i&gt;nodes&lt;/a&gt; 是作为 ActiveRecord 的对象的&lt;/p&gt;

&lt;p&gt;还是应该先把对象转换成散列，再赋值那？&lt;/p&gt;</description>
      <author>a-wing</author>
      <pubDate>Wed, 31 Jan 2018 21:15:17 +0800</pubDate>
      <link>https://ruby-china.org/topics/34982</link>
      <guid>https://ruby-china.org/topics/34982</guid>
    </item>
    <item>
      <title>Rails 命令自动补全</title>
      <description>&lt;p&gt;大神勿喷。rails 自动补全我找了好久的资料，最后折腾找出来了一套办法&lt;/p&gt;

&lt;p&gt;先介绍我的系统 archlinux
还是要说 arch 大法好&lt;/p&gt;

&lt;p&gt;bash 下有补全脚本，但很久不更新了&lt;/p&gt;

&lt;p&gt;所以采用 zsh 和 oh-my-zsh 的插件&lt;/p&gt;

&lt;p&gt;还是以通用的发型版 debian 为例吧&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install &lt;/span&gt;zsh
chsh
sh &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;wget https://raw.githubusercontent.com/robbyrussell/oh-my-zsh/master/tools/install.sh &lt;span class="nt"&gt;-O&lt;/span&gt; -&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后编辑~/.zshrc 来启用插件&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vim ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改这句来启用插件&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;git bower gem bundler ruby rake rails&lt;span class="o"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;然后就可以自动补全命令了&lt;/p&gt;</description>
      <author>a-wing</author>
      <pubDate>Sat, 03 Jun 2017 18:57:19 +0800</pubDate>
      <link>https://ruby-china.org/topics/33132</link>
      <guid>https://ruby-china.org/topics/33132</guid>
    </item>
  </channel>
</rss>
