5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

PC自作.comの構成

Last updated at Posted at 2020-04-24

自己紹介

わたしはエンジニアではなく、誰かに習ったこともなければ、学校でプログラミングを習ったこともないので、趣味の領域でググって出てくる情報だけで作っています。
(もちろん仕事としてエンジニアをしているわけでもないです。そのへんの学生です...)
たくさんアンチパターンを踏んでいると思いますので、ご指摘いただければと思います。

PC自作.comについて

https://pcjisaku.com/
では互換性のチェックをしながら自作PCのパーツリストを作れます。
もちろん最安値のチェックもありますし、cinebenchやpassmarkでのcpuやgpuのベンチマークスコア情報もあります。
ユーザー登録なしで複数のパーツリスト情報を保存できます。
パーツリストをもとにブログのように自分の自作PCについて記事を書くことができます。

全体の構成

バックエンドは
nginx → unicorn → rails
という形です。

フロントエンドはvueです。

docker

docker-composeでunicornを走らせるrailsコンテナとnginxコンテナとredisコンテナを立ち上げます。

railsの方のDockerfileでは
画像をwebpに変換したいので、そのあたりのライブラリを追加しています。
RUN apt-get install -y imagemagick libjpeg-dev libpng-dev libtiff-dev libwebp-dev webp
gemもキャッシュしています。

rails

次にrailsの大まかな設計に移ります。

テーブル設計

Productテーブル

productsテーブルに全商品を入れています。
product_categoriesテーブルには20程度のカテゴリを保存しています。CPUやGPUなどです。
商品のスペック情報は1. 選択肢から選ぶスペック情報 と 2. テキストや数値などの生情報のスペック情報を別テーブルで保存しています。

Userテーブル

ユーザー情報

user.rb
has_many :part_lists
has_many :comments
has_many :likes
has_many :builds

Partlistテーブル

partlist.rb
has_many :part_list_products, dependent: :destroy
has_many :products, through: :part_list_products
has_many :builds
has_many :comments
has_many :likes

CommentテーブルやLikeテーブル

commentテーブルやlikeテーブルでコメントやライクを管理していますが、
あらゆるテーブルに対してcommentやlikeできるようにpolymorphicを使っています。下のコードみたいな感じです。

like.rb
class Like < ApplicationRecord
  belongs_to :likable, polymorphic: true # 親が削除されたら一緒に削除される
  belongs_to :user, optional: true # likeはユーザーが削除されても残してあげる。
end

ProductShopテーブル

商品(product)の販売店舗情報を保存しています。

Railsに当てているパッチ(webp使うため)

active_storageの_blob.html.erb

_blob.html.erb
<figure class="text-left my-2 attachment attachment--<%= blob.representable? ? "preview" : "file" %> attachment--<%= blob.filename.extension %>">
  <% if blob.representable? %>
    <picture>
      <source type="image/webp" srcset="<%= blob.representation(define: 'webp:lossless=false', convert: 'webp', resize_to_limit: [ 800, 600 ]).processed.url %>">
      <img src="<%= blob.representation(quality: 55, resize_to_limit: [ 800, 600 ]).processed.url %>">
    </picture>
  <% end %>

  <figcaption class="attachment__caption">
    <% if caption = blob.try(:caption) %>
      <%= caption %>
    <% end %>
  </figcaption>
</figure>

actiontextのためにinitializerで

上4行以外のところでは
https://github.com/rails/rails/blob/157920aead96865e3135f496c09ace607d5620dc/activestorage/app/models/active_storage/variant.rb
このvariant.rbにパッチを当てています。

actiontext.rb
ActionText::ContentHelper.allowed_attributes.add 'type'
ActionText::ContentHelper.allowed_attributes.add 'srcset'
ActionText::ContentHelper.allowed_tags.add 'picture'
ActionText::ContentHelper.allowed_tags.add 'source'

class ActiveStorage::Variant
  private
    def specification
      blob_content_type = variation.transformations[:convert] == "webp" ? "image/webp" : blob.content_type
      @specification ||=
        if WEB_IMAGE_CONTENT_TYPES.include?(blob.content_type)
          Specification.new \
            filename: blob.filename,
            content_type: blob_content_type,
            format: variation.transformations[:convert] == "webp" ? "webp" : nil
        else
          Specification.new \
            filename: ActiveStorage::Filename.new("#{blob.filename.base}.png"),
            content_type: "image/png",
            format: "png"
        end
    end
end

actiontext content

==の定義がto_sになっているとactiontextのcontentのupdateの際に毎回renderされてしまいます。
それだと非常に時間がかかるのでto_htmlでrender _layoutさせていません。

module ActionText
  class Content
    def ==(other)
      if other.is_a?(self.class)
        to_html == other.to_html
      end
    end
  end
end

actiontextの問題

actiontextでは、画像のdirect uploadの際にそのフォーマットの生画像だけ保存して終わりです。その際にvariant変換(resizeやwebpへの変換,cropなど)は行ってくれません。
これだと10枚の画像を一度に投稿された際 or 保存後はじめてそのコンテンツが表示された際に、10枚の画像のvariantの変換作業が走るので表示が遅れます。
そのため、direct uploadの直後に必ず走るActiveStorage::BlobsController showメソッドのところで

if @blob.created_at > DateTime.now - 1.minute && WEB_IMAGE_CONTENT_TYPES.include?(@blob.content_type)
  @blob.variant(define: 'webp:lossless=false', convert: 'webp', resize_to_limit: [ 800, 600 ]).processed
  @blob.variant(quality: 55, resize_to_limit: [ 800, 600 ]).processed
end

変換処理を呼び出しています。
1分以内にアップロードされたものに対してだけかけてあげています。

CMSについて

pc自作.comではcmsがあります。
パーツの選び方の記事や、おすすめのパーツリストなどを表示するブログ作成のためです。
cmsはspina cmsをベースにしていますが、実際のproductsテーブルとrelationを作るためにgemとして使うのではなく、すべて本体に移植しています。

trix.js editorについて

railsだとactiontextとtrix editorのセットっぽかったので、それを使いましたが、正直後悔しかありません。生のhtmlで書けないからです。
もちろんそのほうが安全ではあるわけですが、独自のタグであったり、iframe埋め込みなどをカスタマイズしようと思うと非常に面倒でした。
trix.jsのコードをほぼすべて読んで、パッチを当てまくっています。

ckeditorのほうがよっぽど使いやすいです。

Vueについて

とくに言うことはありませんが、vueを使っているページと使わないページがあります。
それはユーザー動線を考えて、シームレスにヌルサクで動いて欲しいところ(パーツリストを作成している画面)はvueを使い、googleの検索(「動画編集 自作pc」など)からやってくるであろうユーザーに対して見せる記事のページではvueを使わずに静的なファイルなげています。
これはもちろんseoのためです。
vuexでパーツリストを管理しています。
デザインパターンはatomic designのしょぼい版みたいな感じです。

fontawesome の軽量化

https://icomoon.io/app/#/select
に import icons で fontawesome の svg をアップロードする。
必要なものを pickup してダウンロードする
style.css を fontawesome.css に名前を変更。
fontawesome のベース(回転や大きさなどに対応)を使うために svg-with-js.min.css を fontawesome の本体から持ってくる。
fontawesome.css にライセンスとdisplay: inline-block;を加える

[class^="fa-"],
[class*=" fa-"] {
  /* use !important to prevent issues with browser extensions that change fonts */
  font-family: "icomoon" !important;
  speak: none;
  font-style: normal;
  font-weight: normal;
  font-variant: normal;
  text-transform: none;
  line-height: 1;
  display: inline-block;

  /* Better Font Rendering =========== */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

を追記する。

5
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?