Rails + Vue / Tailwind CSSに日本語適用のAdobe Fontsを設定

自作サービスで日本語のAdobe Fontsを利用するにあたって、Adobe Creative Cloudだと有料のせいか、Nuxt.jsで設定する記事しかなかったので備忘録として残す。
間違っている点などがあればご指摘願います🙇‍♀️

Rails 6.1.6
Ruby 3.2.2
Vue 3.2.47
Tailwind CSS 3.1.8

Tailwind CSSはyarnで入れてます。

Adobe Fontsの埋め込みコード

Adobe Fontsの概要は以下(詳しい説明は割愛します。)

欧文フォントの場合だと、@import url("https://use.typekit.net/●●●.css");のように生成されるが、東アジア用フォントの場合だと、ダイナミックサブセットが自動でセットされる為、以下のようなスクリプトが生成される。

<script>
  (function(d) {
    var config = {
      kitId: '●●●',
      scriptTimeout: 3000,
      async: true
    },
    h=d.documentElement,t=setTimeout(function(){h.className=h.className.replace(/\bwf-loading\b/g,"")+" wf-inactive";},config.scriptTimeout),tk=d.createElement("script"),f=false,s=d.getElementsByTagName("script")[0],a;h.className+=" wf-loading";tk.src='https://use.typekit.net/'+config.kitId+'.js';tk.async=true;tk.onload=tk.onreadystatechange=function(){a=this.readyState;if(f||a&&a!="complete"&&a!="loaded")return;f=true;clearTimeout(t);try{Typekit.load(config)}catch(e){}};s.parentNode.insertBefore(tk,s)
  })(document);
</script>

ダイナミックサブセットとは

Adobe Fonts | ダイナミックサブセットと Web フォント提供

以下抜粋

ダイナミックサブセットは、東アジアの Web フォントを提供することを目的として開発されました。この機能により、サイズの大きな東アジアのフォント(ほとんどの場合グリフ数 10,000 以上)を、読み込み時間に大きな影響を与えずに Web サイトで読み込むことが可能です。

ダイナミックサブセットは、ニュースフィードやコメントセクションなど、DOM(Document Object Model)へのすべての変更を検知し、フォントサブセットに追加する必要のある新しい文字をサーバーにリクエストします。このようにして、フォントを読み込むたびにフォント全体をダウンロードするのではなく、追加のグリフのみをリクエストし、ブラウザーですぐに更新できるようになりました。

欧文フォントだと、フォント数も少ないので容量は軽いが、日本語はフォント数が多いので、ダウンロードに時間がかかる。 フォント全体をダウンロードするかわりに、ブラウザで使用する文字だけを要求して、ブラウザ上でそれを更新することができるので、日本語フォント特有の問題である、表示の延滞を軽減することが出来る。

日本語のAdobe Fontsの導入手順

Adobe Fontsで生成したコードをjsファイルに転機

いくつかeslintに怒られるため/* eslint-disable */を設定。
下記はPrettierでコード整形された状態。
kitId: '●●●'は個々に割り振られるid。

adobe-font.js

/* eslint-disable */
document.addEventListener('DOMContentLoaded', () => {
  ;(function (d) {
    var config = {
        kitId: '●●●',
        scriptTimeout: 3000,
        async: true
      },
      h = d.documentElement,
      t = setTimeout(function () {
        h.className =
          h.className.replace(/\bwf-loading\b/g, '') + ' wf-inactive'
      }, config.scriptTimeout),
      tk = d.createElement('script'),
      f = false,
      s = d.getElementsByTagName('script')[0],
      a
    h.className += ' wf-loading'
    tk.src = 'https://use.typekit.net/' + config.kitId + '.js'
    tk.async = true
    tk.onload = tk.onreadystatechange = function () {
      a = this.readyState
      if (f || (a && a != 'complete' && a != 'loaded')) return
      f = true
      clearTimeout(t)
      try {
        Typekit.load(config)
      } catch (e) {}
    }
    s.parentNode.insertBefore(tk, s)
  })(document)
})

上記のadobe-font.jsファイルをapp/javascript/packs/application.jsにimport。

tailwind.config.jsに設定

今回、Kinto Sansというフォントで太さはThin/Light/Regularで作成。
設定方法は以下を参考にさせていただきました🙏

tailwindcssでfont-familyの設定
Issue #293 · tailwindlabs/discuss

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    './app/views/**/*.html.slim',
    './app/helpers/**/*.rb',
    './app/javascript/**/*.js',
    './app/javascript/**/*.vue',    
  ],
  theme: {
    extend: {
      fontFamily: {
        base: ['kinto-sans']  ← Adobe Fonts / Webプロジェクト の font-familyを転記
      }
    },
  },
  plugins: [],
}

font-baseとしてタグに設定するとkinto-sansのフォントが設定される。

読み込ませたいタグにfont-baseを設定

bodyにfont-baseを設置。
Thinにしたかったので、font-weight: 100;と生成時にfont-style: normal;cssに設定するように指示があったので、Tailwind CSSfont-thinnot-italicを設定。

app/views/layouts/application.html.slim

html
  head
    省略
    = javascript_pack_tag 'application', 'data-turbolinks-track': 'reload'
  body.font-base.font-thin.not-italic ← ここ
    header.max-w-screen-lg.mx-auto
      = render 'layouts/header'
    main.mx-auto class="#{content_for?(:main) ? yield(:main) : 'max-w-screen-sm'}"
      = yield
    footer
      = render 'layouts/footer'

無事、フォントがブラウザで読み込めました🎉

kitIdにdotenvを設定

このままではkitIdがもろばれなので、.envでkitIdを管理することにしました。

dotenv導入

Rails.envを利用しているが、gem 'dotenv-rails'はjsファイルだと利用出来ないので、yarn add dotenvで導入。

% yarn add dotenv
.env

# Adobe FontのIDを追加
KIT_ID = '●●●'
% rails c
irb(main):001:0> ENV['KIT_ID']
=> "●●●"

jsファイルで使用できるように設定

config/webpack/environment.js

require('dotenv').config() ←一番上に設置
adobe-font.js

kitId: process.env.KIT_ID

問題なく、表示されました!

フォントのちらつき

ダイナミックサブセットで設定しているものの、リロードする度にWebフォントが適用される前の状態が一瞬表示されてしまいます。

Adobe Fonts | フォントイベント

いくつかの記事を参考に以下を設定すると、header部のナビ以外のちらつきは解消されました。

html { 
  visibility: hidden;
}

html.wf-active {
  visibility: visible;
}

javascriptで設定する方法もあるようですが、一旦、下記記事を参考にさせてもらい🙏 ちらつきを解消させました。
Adobe Typekitのチラつきをなくしましょう!

body {
  animation: body-fade-in 1s ease 0s 1 normal;
}

@keyframes body-fade-in {
  0% {
    opacity: 0
  }
  100% {
    opacity: 1
  }
}

ちらつき防止については他の設定方法がわかったら、ブログを更新します。
Google Fontsのように@importで生成できなかったので最初戸惑いましたが、設定できてよかった〜。

元ネイリストが、ネイリストのためのデザインメモ「Nailire(ネイリエ)」をリリースしました。

はじめに

フィヨルドブートキャンプ(FBC) の 最終プラクティスで自作サービスに取り組み、この度、ネイリストのためのデザインメモNailire(ネイリエ)をリリースしました。

nailier.net

github.com

Nailire(ネイリエ)について

サービス内容

Nailire(ネイリエ)はネイリスト専用のネイルのデザインメモです。
気になったデザインのコンテンツや調べた情報をデザイン毎に登録して管理出来ます。

特徴

画像・YouTube動画・カラーイメージ・パーツの種類と個数・調べた内容・アイデアをデザイン毎に登録することが出来ます。

image-main

画像は8枚まで登録が可能で、ドラッグアンドドロップで並び替えが出来ます。講習等で撮った画像をデザインの作業手順に並び替えることが出来ます。

カラーピッカーorカラーパレットから簡単にデザインのカラーイメージを登録することが出来ます。

登録したデザイン情報は、簡単に編集・削除が可能です。
一覧からハンド・フット別、タグ検索で簡単に登録したデザイン情報を開くことが出来ます。

こんなことにも使える

  • ネイルのデザインメモと言う名目ですが、ネイル検定用としてもおすすめです。
    検定で使用するポリッシュのメーカー番号をメモしておいたり、手順をまとめたり、撮った画像も8枚まで登録してドラッグアンドドロップできるので、出来栄えを比べることが出来ます。
  • またアカウントを一つ開設すればお店の共有ツールとしても使えます。

作った経緯

以前、私は本業の傍ら、副業でネイリストをしていました。最初は何の気なしに趣味程度で始めたのですが、そこのスクールがサロンも併設しており、技術を積めばサロンで働けるシステムだったので波に乗って、数年ほどネイリストとして働き、その間、ネイル検定2級まで取得しました。
副業でネイリストをしていた頃は、お店のデザインも覚えないといけないし、流行りのデザインに触れることもあったのですが、やめてからは、こんなの流行ってるんだな〜と見るものの、いつも自分がするネイルのデザインは、手元にある、ありあわせのもので似たデザインのものばかりになっていました。サイトで見かける気になったデザインの画像や、YouTubeで見つけた作業工程、してみたいカラーイメージなどを一つにまとめられたら、作業イメージが湧いて、ネイルをもっと楽しめるのではないかと思い、このサービスを形にしようと思いました。
なくても問題はないけれど、あったら嬉しい。そんなサービスを考えました。

アプリ名の由来

ネイルアトリエの造語です。
アトリエはアーティストの方が作品を作る場所なので、このツールをネイルデザインのアトリエとして使ってもらいたいという想いでNailire(ネイリエ)と名付けました。

技術スタック

大変だったこと

VueからRailsへの値の渡し方

画像はActive stage、子モデルはaccepts_nested_attributes_for を使い同時保存できる仕様になっています。親モデルのDesignに、images、1対多、多対多といくつか子モデルがある構成で、それぞれVueからRailsへの値の渡し方が異なるため、登録時と編集時の検証にとても時間がかかりました。いかにRailsがよしなにやってくれているのかがよくわかりました。
また画像については、ドラッグアンドドロップで並び替えするにあたって、登録時は簡単に実装出来たものの編集時に登録済みの画像を削除したり、新たに画像を登録してドラッグアンドドロップでした場合に、並び替えた状態をどうやって保持したらいいのかと言う点についてかなり試行錯誤しました。
VueからRailsへの値の渡し方を何度も検証し徐々に理解を深めたことで、少し自信をつけることが出来ました。

仕事との両立

勉強でこん詰める作業をして寝不足のまま仕事に取り掛かると、思わぬ凡ミスをしてしまい、寝不足だから立ち振る舞いもままならぬ…みたいな状態になって、慣れてるはずの仕事にも気を配らなくてはならなくなりました。 またリモートワークになってからプログラミングの勉強を始めたのですが、在宅ワーク最高と思っていましたが、振り返ると、突然何!?って位、予想だにしない体の不調が去年からやたらと多くなりました。歳か…と思いましたが元気な目上な方はたくさんいるので、通勤時の時にあった抗体力が低くなったのか、、とにかく健康面にも気を遣わなくてはならなくなりました。

上記の状態が続き、自作サービスを進めたいのに、思い切って勉強を出来ない、、すべてにおいて気を使わなければならない状態になった時が一番キツかったです。
こういうマイナスなことは書くべきではないとは思いますが、この状態は他の方もなる可能性があると思い書きました。 人によって環境は異なると思うので、私を反面教師にして、前もって策を講じてほしいと思います。
転職後に、この状態に陥ったら、、と思うと恐怖です。このタイミングで経験できて逆に良かったと思いました。

おわりに

まずは、これまで接してくれた方々に心から感謝申し上げます。本当にありがとうございました。

そして、以前フィヨルドブートキャンプでメンターをされていた方とたまたまDiscord上でさしになった時に言われた言葉を、今不安に思っている方に向けて伝播したいと思います。

「絶対大丈夫。」

自分が不安に思ってる就職の事だったり技術の足りなさだったりを吐露した時に、マイナスワードを言う度に、覆い被すように「コードも見たことあるけど、大丈夫です!!! 絶対大丈夫です!!!」 と何度も言ってくれました。

何をどこ見てそこまで言い切ってくれるの…と思いましたが、、有り難かったです。本当に。

正直、自作サービスに取り組んでいる時、私はこの言葉に応えられていないと思って落ち込みました。 ですが、 そんな状況でも自作サービスのプラクティスを提出することが出来たので、、どの人も必ず卒業は出来ます! (現役生向け)

ブラウザを通じて伝わることを祈って、、GOOD LUCK!!

最後までお読み頂き、ありがとうございました。

Rails/Vue 編集時に画像をD&Dで入れ替えした時のActive Storageの保存方法

この記事は[フィヨルドブートキャンプ Part 2 Advent Calendar 2022]の24日目の記事です🎄

昨日23日目は、wataさんのフィヨルドブートキャンプ内で「1分間スピーチ会」を開いてみた 、はるぐち ゆうまさんのためして分かる、N+1問題とその解決方法 - プログラミング漫遊記でした。

フィヨルドブートキャンプ Part 1 Advent Calendar 2022 - Adventar
フィヨルドブートキャンプ Part 2 Advent Calendar 2022 - Adventar

はじめに

saeyamaと申します。
フィヨルドブートキャンプに籍をおいている現役生です。
今、最終課題の自作サービスに取り組んでおります。
この記事は自作サービスでいくつか技術検証をした内の一部をまとめたものです。
色々ご意見あるかと存じますが、レビュー前のため、お手柔らかに見ていただけると幸いです🙇‍♀️

自作サービスの簡単な説明

自作サービスはバックエンドはRails、フロントエンドはVueです。
サービス内容は簡単にいうとネイリストのためのデザインノート(メモ)のようなものです。
機能の一つとして複数の画像と動画が保存できる仕様になっていてActive Storageを使用しています。

そして、登録する画像はフロント側でD&Dで順番の入れ替えができる仕様になっています。
なぜ、順番を入れ替える必要があるかというと、

  • 1枚目の画像を一覧ページのサムネイルに設定するため
  • ネイルの講習等に行って撮影した画像を手順通りに並べられるようにするため

と、いった目的があるためです。

並び替えができるD&Dの機能はVueのライブラリvuedraggableを使用しました。

vuedraggableのサンプル

実装も簡単だしサンプルを見てもわかる通りとても便利なライブラリです。

vuedraggableの問題点

登録する時にはとても便利だし、何も問題はないものの、
編集時に画像の追加や削除をした上で、順番を入れ替えた場合にその状態を

どうやって、どこに記憶させるか

という問題にぶち当たりました。

それらしい記事を見つけたのですが、登録済のstringのカラムを編集時にD&Dで入れ替えた場合に、どう記憶させるかといった内容のもので、対策としてmodelに順番を記憶させるsortカラムを追加してRails側でAPIデータをsortカラムの順番で制御して、フロント側で表示させるといった内容でした。

良さそう!と思ったものの、私の自作サービスの場合、登録済の画像と新たに登録する画像が混在するケースがあります。
また新規で登録する画像にidは付与されていない状態です。
順番を記憶させるカラムを追加する場合、どこのmodelに追加すればいいのか。

  • Active Storageをattachしているmodelにするのか。
  • 新たにsort専用にmodelを作った方がいいのか。
  • Active Storageにカラムって追加できるのか。

プチパニックです。

どうしたか

結論からいうと、カラムの追加はせず、画像をattachする時に、filenameTime.zone.nowとしていたのをD&Dしてsortされたidの配列の添字を当て込めることにしました。

irb(main):022:0> Design.find(1).images[0].blob
=>
#<ActiveStorage::Blob:0x00007f847a2e4068
 id: 1, 
 key: "dk2e7ri3l7idgkwwc5p6e799dshp",
 filename: "0", ⇦ `Time.zone.now`からsortしたidの配列の添字にしました。
 content_type: "image/jpeg",
 metadata: {"identified"=>true, "analyzed"=>true},
 service_name: "local",
 byte_size: 193059,
 checksum: "yaoUEuiXo/Agsj1XOzhD5A==",
 created_at: Thu, 15 Dec 2022 21:34:41.036899000 JST +09:00>

なぜ、どこかしらにsort用のカラムを追加しなかったかというと、編集時にD&Dしないケースが想定されるためです。

そして、なぜ添字にしたのかは次に説明します。

流れ

まずVue側のフロントでは、画像のidの配列の値を送るようにセットします。
削除する画像、新たに追加する画像、そして順番を入れ替える場合、下記のようなidの配列となります。

[5, "", 6, 2, 1, ""]

この場合、""となっているところは親モデルのupdateの時に新規でattachされた画像のidが当て込まれることになります。
新たに付与されたidは、必ず前から順番に当て込まれるので、例えば付与されるidが仮に1112だった場合に""に左記の数値を当て込めれば、sortされたidの配列が出来上がります。

sortされた画像(ActiveStorage::Blob)のid

[5, 11, 6, 2, 1, 12]

上記のidをeach.with_index(1)で回して
ActiveStorage::Blob.find(画像のid)filename: indexにしてupdateすれば順番を保持できると考えました。

[sortされたActiveStorage::Blobのid].each.with_index(1) { |id, index| ActiveStorage::Blob.find(id).update(filename: index) }

ただ、その場合、新たに追加されたActiveStorage::Blobのidが必要になります。
そうなると、attachしている親のidが必要になり、controllerでないとメソッドを設置することが出来ません。

@design = designs.find(params[:id])⇦attachしている親

新たなidの取得が必要
image_ids = @design.images.map(&:id)
new_ids = image_ids.select { |image| sort_image_ids.max.to_i < image }

modelにメソッドを設置したい。
そこで考えたのが、@design.imagesから呼び出してfilenameをupdateする方法です。
その場合、必要な数値は新たに追加するidではなく添字の番号になる(@design.images[添字番号])ので、
""は送られてきたidの中で一番大きい数値に+1した数値をダミーで当て込めようと考えました。

[5, "", 6, 2, 1, ""]

↓

78はダミー番号
[5, 7, 6, 2, 1, 8]

この配列の順番を保持した状態で、添字に変換します。

[5, 7, 6, 2, 1, 8]

↓

[2, 4, 3, 1, 0, 5]

添字に変換した配列をeach.with_index(1)で回して、
@design.images[添字]filename: indexにしてupdateできるようにしました。

[添字に変換した配列].each.with_index(1) { |添字, index| @design.images[添字].blob.update(filename: index) }

そうすることでfilenameに順番の数値を当て込めることができます。

model側では、以下のようなメソッドを作成しました。(リファクタリング前)

def images_set(sort_image_ids)
  return unless sort_image_ids

  if sort_image_ids.include?('')
    blank_ids = sort_image_ids.each_index.select { |id| sort_image_ids[id] == '' }
    blank_ids.size.times { |i| sort_image_ids[blank_ids[i]] = sort_image_ids.map(&:to_i).max + 1 }
  end
  design_images_index = sort_image_ids.map(&:to_i).each_with_object(sort_image_ids.map(&:to_i).sort).map { |id, sorted| sorted.index(id) }
  design_images_index.map(&:to_i).each.with_index(1) { |id, index| images[id].blob.update(filename: index) }
end

以下抜粋
配列の中で""があった場合にif文で現状のidの中で一番大きい数値に+1した数値をダミーで当て込む。

sort_image_idsはVueから送られてきたsortしたidの番号の配列

  if sort_image_ids.include?('')
    blank_ids = sort_image_ids.each_index.select { |id| sort_image_ids[id] == '' }
    blank_ids.size.times { |i| sort_image_ids[blank_ids[i]] = sort_image_ids.map(&:to_i).max + 1 }
  end

そして添字に変換しfilenameをupdateさせています。
添字に変換する方法が思いつかず、悩みましたが、each_with_objectを使って変換する方法を教えていただきました🙇‍♀️
Enumerable#each_with_object (Ruby 3.1 リファレンスマニュアル)

  design_images_index = sort_image_ids.map(&:to_i).each_with_object(sort_image_ids.map(&:to_i).sort).map { |id, sorted| sorted.index(id) }
  design_images_index.map(&:to_i).each.with_index(1) { |id, index| images[id].blob.update(filename: index) }

上記のメソッドをcontrollerでupdateする時に、以下のようにセットしました。

@design.images_set(sort_image_ids) if @design.images.attached?

filenameがsort順に更新できるようになりました。

sort順に並んだ画像の取得方法

現状だと、APIデータの画像はid順で並ぶようになっているのでjbuilderでsort順に並ぶようにします。

画像のURLをrails_blob_urlで取得すると、URLの末尾にfilenameが表示されます。

画像のURL

"http://localhost:3000/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--604e0ac50b10ed8687b0f97bde916011758d3ec2/1"1がfilename

urlをsplitしてfilenameを取得して、新たにindexというkeyを作ってvalueとして設定します。

APIデータ

images: [
  {
    id: 1,
    url: "http://localhost:3000/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--604e0ac50b10ed8687b0f97bde916011758d3ec2/0",
    _destroy: "0",
    index: 0 ⇦追加
  },
  {
    id: 2,
    url: "http://localhost:3000/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCdz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--06a47b05efff72da1924ef77a49a80ffa80b96eb/1",
    _destroy: "0",
    index: 1 ⇦追加
  }
]

indexの順番通りに画像を並び替えれば、フロント側でsortした順番通りに表示されるようになります。

APIデータ

idの12を編集時に入れ替えた場合

images: [
  {
    id: 2,
    url: "http://localhost:3000/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCdz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--06a47b05efff72da1924ef77a49a80ffa80b96eb/0",
    _destroy: "0",
    index: 0
  },
  {
    id: 1,
    url: "http://localhost:3000/rails/active_storage/blobs/redirect/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--604e0ac50b10ed8687b0f97bde916011758d3ec2/1",
    _destroy: "0",
    index: 1
    }
  ]

並び替えする時も、なかなかうまくいかずに困りました。
sortメソッドと<=>演算子を使って並び替えをすることができました。

Array#sort (Ruby 3.1 リファレンスマニュアル)

jbuilder

image_urls = @design.images.map { |image| { 'id': image.id, 'url': rails_blob_url(image), '_destroy': '0', 'index': rails_blob_url(image).split('/')[8].to_i } }
sort_images = image_urls.sort {|x, y| x[:index] <=> y[:index] } if @design.images.attached?
json.images sort_images

苦肉の策って感じでもあるし、他にやり方があるのでは?とお思いかもしれませんが、自作サービスなのでちょっと探求してみました。
冒頭でもお伝えしましたが、レビュー前のため、お手柔らかに見ていただけると幸いです🙇‍♀️
また、端折った内容にもなっているので他の技術検証含め、今後まとめて書いていきたいと思っております。

終わりに

この前、胃腸炎にかかり、数日間、過去1レベルの激痛を味わいました。
そしてちょっと回復した時に近くのセブンに行ったら、
目の前に並んでいた人が、本能の赴くままに、ななチキを頼んでいたのを見た時、

「あぁ、この苦しみも含め、報われる日が来るといいなぁ…」

と、思った瞬間、セブンでガチ泣きしそうになったちょうど一ヶ月程前。

勉強するにしても、お金を稼ぐにしても、美味しいケーキを食すにしても、健康でなければ何も出来ないということを思い知らされた、2022年・初冬。

皆様もお身体には、十分ご留意ください。

最後までお読みいただきありがとうござました。

明日、最後となるフィヨルドブートキャンプ Advent Calendar 2022 25日目は、mhさん、ラスボスkomagataさんです🎄

Gitもオブジェクトだった…!

Gitを一言で言うなら
スナップショットをバージョン毎に管理できるもの。
とざっくりとしか認識していなかった。 そして、構造がいまいちわかりづらくて難解。。
が、チーム開発になると否が応でもコンフリクトにぶち当たるので、理解を深めざる得ない…!
大変そして面倒😱そんな感覚でしかいなかったが、Gitの内部構造を知り、少しだけ面白さを感じた。

Gitの内部

Gitは洗練されたオブジェクトモデルを、雑多なコマンド群が包み込む構造になっている。

オブジェクトモデル  
プログラムと関連するデータをまとめたオブジェクトを、ひとつのモデルとして把握する考え方。オブジェクト指向プログラミングでは、個々のモデルを組み合わせてプログラミングを行うため、ソースコードの作成後も全体を整理して把握できる。  
Gitでいうとオブジェクトを単位としたデータ構造のこと。  

Gitのリポジトリ作業ディレクト.git ディレクトで構成されている。
直接操作できるのは作業ディレクト
作業ディレクトリの一番上の階層に.gitディレクトが位置しておりリポジトリの本体になる。

.gitの中身

  • Gitオブジェクト
  • リファレンス
  • インデックス

主に上記で構成されている。

Gitオブジェクトの構成

  • .git/objectsディレクトリに格納されている。
  • Git オブジェクトの集合をGitオブジェクトデータベースと呼ぶ。
  • 1 個のGit オブジェクトは 1 個のファイルに書いてある。
  • オブジェクトの数が増えてくると、複数のオブジェクトが pack 形式と呼ばれるファイルにまとめられる。

Gitオブジェクトは4種類ある

  • tree → ディレクト
  • blob → ファイル
  • commit → コミット
  • tag → タグ

Git オブジェクトは、ID (オブジェクト名)で区別されている。
ID は40 文字の 16 進数 (0〜9 と a〜f)になっている。
ID の先頭2文字分ディレクトリがあって、のこり38 文字がファイル名となる。

.git/objects/2文字/38文字

ID だけ見てもオブジェクトの種類 (tree、blob、commit、tag) は区別がつかない。
4種類のオブジェクトがごちゃ混ぜに.git/objects ディレクトリに格納されている。

Gitオブジェクトを見てみる

試しに、bootcampのGit オブジェクトをlsコマンドで確認。

bootcamp % ls .git/objects     
00      11      22      33      44      55      66      78      89      9a      ae      bf      d1      e3      f5
01      12      23      34      45      56      67      79      8a      9b      af      c0      d2      e5      f6
02      13      24      35      46      57      68      7a      8b      9c      b0      c1      d4      e6      f7
03      14      25      36      47      58      69      7b      8c      9d      b1      c2      d5      e7      f8
04      15      26      37      48      59      6a      7c      8d      9e      b2      c3      d6      e8      f9
05      16      27      38      49      5a      6c      7d      8e      9f      b3      c4      d7      e9      fa
06      17      28      39      4a      5b      6d      7e      8f      a0      b4      c5      d8      ea      fb
07      18      29      3a      4b      5c      6e      7f      90      a3      b5      c6      d9      eb      fc
08      19      2a      3b      4c      5d      6f      80      91      a4      b6      c7      da      ec      fd
09      1a      2b      3c      4d      5e      70      81      92      a5      b7      c8      db      ed      fe
0a      1b      2c      3d      4e      5f      71      82      93      a6      b8      c9      dc      ee      ff
0b      1c      2d      3e      4f      60      72      83      94      a7      b9      ca      dd      ef      info
0c      1d      2e      3f      50      61      73      84      95      a8      ba      cb      de      f0      pack
0d      1e      2f      40      51      62      74      85      96      a9      bb      cc      df      f1
0e      1f      30      41      52      63      75      86      97      aa      bc      cd      e0      f2
0f      20      31      42      53      64      76      87      98      ab      bd      ce      e1      f3
10      21      32      43      54      65      77      88      99      ac      be      cf      e2      f4

たくさんある〜!IDの最初の2文字ってことか。00を見てみる。

bootcamp % ls .git/objects/00
104a70195ba867f524aee7f8b224ae9e9ee362  
3ce4707a48f048eee30f3bee2121c974629d7e
242b0e61d9767f99bc64dbacdb8164e9e12809  
9e52d50a8b6fd6f14529e64445ee45cfabab70

まとめられているpackも見てみる。

bootcamp % ls .git/objects/pack                                                                
pack-0c613c3e2fb0c90b7a5f7cbbe754016f70a4b8d5.idx       
pack-789e4c7b0e70a09e94edf046238faf579c2405ab.idx
pack-0c613c3e2fb0c90b7a5f7cbbe754016f70a4b8d5.pack      
pack-789e4c7b0e70a09e94edf046238faf579c2405ab.pack
pack-0ed1f058b24a2b64c158ef1fb3494dd476bdeff7.idx       
pack-7a904cea9764cf4c9d5ce0386c6e4966f3dc14a5.idx
pack-0ed1f058b24a2b64c158ef1fb3494dd476bdeff7.pack      
pack-7a904cea9764cf4c9d5ce0386c6e4966f3dc14a5.pack
pack-1bc504594b6625758798d584835e59059fa4a7e3.idx       
pack-7e1f9731e9f8fa5c93d63952a67b74d25ac80928.idx
pack-1bc504594b6625758798d584835e59059fa4a7e3.pack      
pack-7e1f9731e9f8fa5c93d63952a67b74d25ac80928.pack

以下永遠につづく

たくさんまとまってる〜!

拡張子のpackidxを調べるとgit gcと打つと手動で圧縮する方法があるようだ。

まずはGitのオブジェクトを確認して  
`find .git/objects/ -type f`  

ファイル容量も確認して    
`du .git/objects/`  

オブジェクトをpack
`git gc`

すると、ファイルがまとめられて容量を減らすことができる。  
よくできている…!

idx,、packの中身を確認したい場合は以下で確認できる。

git verify-pack -v .git/objects/pack/**ID**

試しに一つのidxの中身を見てみる。

bootcamp % git verify-pack -v .git/objects/pack/pack-0c613c3e2fb0c90b7a5f7cbbe754016f70a4b8d5.idx
c4603ba969b392ba7a6a6dac72e7ab1f4c684dc3 commit 280 226 12
09e6e7f86cb94ce2ac88ef1422757978b1d0121b commit 243 197 238
9e01dc433b28108371798bfa6cba2afa6688356d commit 318 262 435
dcd74a9552f8cda899a6c3f150112a17a3d34711 commit 278 232 697
44efdaadde5f2b9c3cb3748fb392f0f989f6c164 commit 312 247 929
b33d1cd5267e581ff1c4cb616b16ae295b88ca8f commit 303 244 1176
780e1c51e7ad14f551d32025c2dc403492a5a0a9 commit 309 252 1420
3cd6019f8df001e2c8b43ae52b7963b1c859a1b0 commit 257 206 1672
48a4629a60149651b79fc71d1de837d31c28af67 commit 318 250 1878
0983b094c0547ba858bffffac043057f05d37625 commit 350 292 2128
84b94cc51f600e2291f79d72077c623ee660afba commit 284 235 2420
0d816d71291a6e352b90150365ef3848d42df0ab commit 273 224 2655
afd2ac185987ee8a3cc801c350accb9ba170b9be commit 274 246 2879
6f2f7f1099a8e413fd252c2c7051acc3b5e3c909 commit 314 270 3125
fd3b0190596e8a8eb45049c61fb4d8b1b48a1386 commit 321 271 3395
fd2708a15f4f0437897bc30869fd0e1565ca79bd commit 294 233 3666
f85e9fdb4c96e074aae01766b2c915597c1ce050 commit 291 224 3899
774a37ea47fdb8b2317d9a650f1d81296bf908b9 commit 288 233 4123
ceb790b23430275fba4b0399c6a2c65034fdeb5d commit 273 226 4356
bb22d9d690e7707206001fba48b4cac04235a9f8 commit 248 221 4582
6dc4b2df2543a18a3ab1da844f617cc35da565b2 commit 236 205 4803
d834f02f53a3a40514314b4081965931fc738072 commit 301 263 5008
8a1a744d8fd418b8889e6b7195dc712bf01a0714 commit 255 225 5271
1f45da55c184b7116ed8c8cad574e68a1bd8ef14 commit 271 242 5496
e0dfad2f266e3ab13a245b25794afea7d089cd4d commit 296 240 5738
9ec51e90b77e126a0d845571fc54a67c3e4c9546 commit 372 303 5978
67c96a46f69eef4b6659f431d0ca4b0c726959e5 commit 217 172 6281
af71f50d7c719935dd99fb36ce6a5473ca2c6e9f tree   33 64 6453 1 ebc30cd0e2bc22c72d42e91e7374c5a836cec0a0
fb0349903e3d30108e2b2c39a82dd91d92532ac5 tree   28 61 6517 1 996b77faf34b5643c31426ee14fa49a2a0313833
36a255c4a3d418fd5dcbd34e15e8df332bd7acba tree   33 64 6578 1 7f503601a9a7e8e82ab25499228f762a6857b3d9
9fe2c91017c131cd3f4afe318b85184d8172d095 tree   30 63 6642 1 21f207ac39d2cba0c49c2f6fd043906a3ae7b38e
c3d6eba6cf2593bcdb154d847737eef59dabead2 blob   23 53 6705 1 5d39bd10a377c480643546514f2be8ce243083fc
8b492ea3533f18738ef106e0df2c0bdb81fa3b06 tree   33 64 6758 1 6778d6eb5aa42a4c06af532fe013e1d6b39515ea
e5accb59f2cb37daa6580d18313b69017b63638a tree   30 63 6822 1 9993a1087312f7ef3a2e1b4a67abf20b8f0f9b22
ac4989bbbf5063dab67f59da9c025b94a8310fb3 tree   33 64 6885 1 602346e945773889900153c4b2a10cafaf1daaa4
0595c85bde812255718d508a7d3f64f367312cfd blob   16 47 6949 1 f93438b27abffa135b04edfed6814b07eefd957d
dd49a973e68fb47beeb8de82c311388621e0e426 tree   112 145 6996 1 6778d6eb5aa42a4c06af532fe013e1d6b39515ea
fbe929118560ce53f10970b1a3a3c3bf01a1111b tree   125 154 7141 1 9993a1087312f7ef3a2e1b4a67abf20b8f0f9b22
731774490dad4c22b11bc1eabe9e882f24da7555 tree   27 58 7295 1 e98486c2d799354fedcf078626a4272ba4bf94bc
86ba499e0e64f12b5d940b0120c466a4b8b2092d tree   85 118 7353 1 8ae59f919da34341bd1d096e1f6e8e85700ab713
9e44807393cccfd009105d84945f9f468680aa41 tree   33 64 7471 1 8e6df3565dd9eef9158575da69f50c4bacf6c56e
40bca6b4d380cc88bd3f07579160b24cbd24699b blob   88 102 7535 1 117561673e4f048677c30ef38c3d20111b9e44b8
9530fc0635c9983fcf5bdf5742065af3a76f3571 tree   67 97 7637 1 4bcdaa99e55c575719ab06dc9e542ba01bab24b0

以下永遠につづく

この先、Gitを圧縮する機会なんて来るのだろうか?と思いつつもGitを深堀することができて楽しかった😃

参考にさせて頂きました🙏
Git の仕組み (1) - こせきの技術日記
Gitコマンド入門::Gitオブジェクト(Packfile)第五十九回

そんな、私が参加しているコミュニティは…

bootcamp.fjord.jp

以後、お見知り置きを。

最後までお読み頂きありがとうございました。

VirtualBoxダウンロードとインストール

Big Sur 11.4
VirtualBox 6.1.14

Oracle VM VirtualBox - ダウンロード | Oracle 日本
上記サイトからVirtualBoxをダウンロードしインストール試みたところ出来なかった💦

試したこと

  • システム環境設定→セキュリティとプライバシーから許可をして再起動後インストール試みるも出来ず。

  • PCのバージョンアップの警告が出ていたのでバージョンアップをしてから試みるも変わらず。

  • SPIを一時的に無効にすればインストールできたとの情報を見つけたのでリカバリーモードでターミナルにcsrutil disableを設定して再起動後、試してみるも出来ず。許可の警告も出ない。csrutil disableを打った時にSuccessfully disabled System Integrity Protectionとは出ず、再起動しろとの内容だったので、再起動後、csrutil statusで確認したらcsrutil disableになっていたことは確認できたものの出来なかった…。

最終的に…

Homebrewからインストールしたら出来た🎉
インストールは以下を参考にさせて頂きました🙏
Virtualboxのインストール
いつの間にかbrew caskが使用できなくなっていた話

なぜ、Oracleからは出来なかったのか… 🤔? 最終手段のcsrutil disableしてもなぜダメだったのか…謎!

そんな、私が参加しているコミュニティは…

bootcamp.fjord.jp

以後、お見知り置きを。

最後までお読み頂きありがとうございました。

HTMLのアウトライン

はじめに

普段HTMLを書く時、アウトラインを意識していますか?

私は、仕事で書くことがあるのですが一切気にしたことがありませんでした…。

自己紹介

私は今現在、卸小売業の小さな会社でECの担当として働いております。 今の会社に入社する前まではコールセンターや事務といった仕事をしていました。

でも年半ばで、ものを作る仕事に就きたいと思うようになり、WEBデザイナーという肩書きに憧れ(笑)、HTML、CSSPhotoshopIllustratorを学んだ後、今の会社にご縁があって入社しました。

とても小さな会社で、WEB制作とはまるで無縁の会社ではあるのですが、自社のサイトと大手のモールで商品を売っているので、商品の登録やら画像加工といった事をしています。

ECを担当している直の上司が唯一一人いるのですが、その人も私と同じくらいのスキルの方で、本当なら制作会社に入社したかったであろう私を気遣い、あるモールのサイトのリニューアルを任せてくれたり、LPを制作させてくれたりと、本来なら任せてもらえないであろう事をさせてもらえたので、個人的には本当にラッキーといった感じでした。

ただ、導いてくれる人がいないのです…。上司もコードがそんなに書けないので。 それ以外の人は基本パソコンが苦手という方々しかいないのです。 大手では考えられないと思います。それが小さな会社のミラクルなところ。

なので、色々調べました。勉強したことを復習しながらゆっくりとコードを書く日々。 自社なので期限をあまり気にしなくてもいいというところもありがたかった。 ちょっと気難しいところのある上司ではあるのですが、本当に私はラッキーだと思います。

ただ、また言っちゃいますけど、導いてくれる人がいない…。独走状態です。

そうなるとどうなったか。

  • とりあえず何でもdivで囲む。
  • とりあえずaタグでdivを囲む。
  • とりあえず動いているから、これでいいか。(これ以上直せない…!! )
    (三拍子のまとまりの悪さよ…他にも色々…)

「とりあえず」独走状態です。

何ならフォトショも切り抜き下手〜

jQueryで動きを入れたくてちょっとコードを勉強した時に、コード書くの楽しい♪と思うようになって、気がついたらJavaScriptを少し勉強するようになって、その内バックエンドに興味を持つようになり、気がついたらRubyRailsを勉強するようになってました。現時点での私がここです。

本題のアウトラインとは

で、話は戻って。

今の加入しているコミュニティでHTMLとCSSのプラクティスがあったときに、HTMLのアウトラインを知りました。

アウトラインとは…

  • 文書の階層(ツリー)構造
  • ブラウザがどの文章の区切りかを見極める非常に大事なもの。

書き方としては方法は2つ

  • hタグのhレベルでアウトラインを作る。
  • articlesectionaside などのブロックの階層構造でアウトラインを作る。

例えばドラえもんのサブキャラをツリー構造にしてみると以下のようになります。

- ドラえもんサブキャラ
    - のび太
        - 性格:怠け者
        - 特技:昼寝
    - ジャイアン
        - 性格:凶暴
        - 特技:歌

ツリー構造は「の」を付けて矛盾しないで成立すること。

ドラえもんサブキャラの、のび太
のび太の性格
のび太の特技

ツリー構造をhタグとブロックでそれぞれ書いてみると以下のようになります。 まずはhタグから。

hタグ

<article>
  <h1>ドラえもんサブキャラ</h1>
  <h2>のび太</h2>
  <h3>性格</h3>
  <p>怠け者</p>
  <h3>特技</h3>
  <p>昼寝</p>
  <h2>ジャイアン</h2>
  <h3>性格</h3>
  <p>凶暴</p>
  <h3>特技</h3>
  <p></p>
</article>

hタグで書いた場合、ブラウザには以下のように解釈されます。

<article>
  <h1>ドラえもんサブキャラ</h1>

  <section>
    <h2>のび太</h2>
    <section>
      <h3>性格</h3>
      <p>怠け者</p>
    </section>
    <section>
      <h3>特技</h3>
      <p>昼寝</p>
    </section>
  </section>

  <section>
    <h2>ジャイアン</h2>
    <section>
      <h3>性格</h3>
      <p>凶暴</p>
    </section>
    <section>
      <h3>特技</h3>
      <p></p>
    </section>
  </section>
</article>

ブラウザは暗示的setionタグ生成してアウトラインを作ります。

一方、ブロックの階層構造に従い書いた場合、

ブロック

<article>
  <h1>ドラえもんサブキャラ</h1>

  <section>
    <h2>のび太</h2>
    <section>
      <h3>性格</h3>
      <p>怠け者</p>
    </section>
    <section>
      <h3>特技</h3>
      <p></p>
    </section>
  </section>

  <section>
    <h2>ジャイアン</h2>
    <section>
      <h3>性格</h3>
      <p>凶暴</p>
    </section>
    <section>
      <h3>特技</h3>
      <p></p>
    </section>
  </section>
</article>

明示的に自分で section タグでブロックを区切り、section入れ子構造を作って階層構造を表現します。

ブラウザは、書いたコードに対しsection= 章 区切りで解釈をしています。
全然知りませんでした。

他にも、
hタグ(見出し)の下には文章(pタグ)が必ずぶら下がる。hタグを連続(例えばh2にすぐh3)しても良い。その場合は下に文章(pタグ)続く。

これ全然わかってなくてコード書いてました….。

そして勉強していて気づいたのです。

ブラウザがsectionタグで自分のコードを解釈した時に、意図してるものと違う階層になってしまったとして、、SEOに多大なる影響を与えてるとしたら…一件の売り上げに左右される可能性があったとしたら!…アウトライン、大事じゃないか!?

そんな大事な事を疎かにしていたのか、自分…!

ということで、簡単まとめ

  • アウトラインには暗示的と明示的な書き方がある。
  • ブラウザはsectionタグを使って章を区切って解釈している。

HTML、奥が深いですね。

今後少しづつ勉強した事をアップしていこうと思っています。 書いた内容が間違っていることもあるので公式サイトや他の方が書かれた記事も合わせて参考にして頂く事をお勧めします。 もし書いたことがその方にとって間違っていたら、責任も取れないし申し訳がないので。 (さして、あんまりネタもためていないですが。)

このブログにたどり着いたということは、同じコミュニティの方、もしくは相当困って相当調べた方だと思うのです。 私はPaizaの問題に挑戦していたとき、わからないことをとにかく調べに調べて、誰かに見られているなんて1mmも思っていないであろう、個人の方のgithubにたどり着いたことがあります(Paizaさんからお叱りを受けない事を祈るばかりです。)

なので、このブログに書かれている気になったキーワードを検索して、正しい答えに辿り着けたらいいのでは!という思いで、橋渡し?的にこれからブログを書いていこうと思います。

本来なら、今のコミュニティに参加した時から書き始めたら良かったのですが、正直後回しになってしまったのと、日記ではないので、読む方の心持ちを考えたいと思い、加入後100日目にして初投稿となりました。

さして読まれない前提で書いてるので大分饒舌です。

そんな、私が参加しているコミュニティは…

bootcamp.fjord.jp

以後、お見知り置きを。

最後までお読み頂きありがとうございました。