Railsのmigrationでテーブル定義やカラム追加を行うときはコメントを入れるようにしよう

こんにちは!kossyです!




さて、今回はRailsのmigrationでテーブル定義やカラム追加を行うときにコメントを入れる方法を
ブログに残してみたいと思います。




環境
Ruby 2.6.3
Rails 6.0.3
MacOS Catalina



commentオプションを使えば簡単に定義できる

Rails5系からcommentオプションを使うことで簡単にschemaにコメントを定義できるようになりました。

以下のように定義します。

# テーブル定義の場合

t.text :action, null: false, comment: 'Behavior such as login is stored in json format.'
t.string :performer, null: false, comment: 'Store polymorphic related class names.'
t.string :performer_type, null: false, comment: 'Store polymorphic related class names.'
t.string :ip_address, comment: 'store remote_ip address in Ipv4 format.'

# カラム追加の場合

add_column :activity_logs, :action, :text, null: false, comment: 'Behavior such as login is stored in json format.'
add_column :activity_logs, :performer, :string, null: false, comment: 'Store polymorphic related class names.'
add_column :activity_logs, :performer_type, :string, null: false, comment: 'Store polymorphic related class names.'
add_column :activity_logs, :ip_address, :string, comment: 'store remote_ip address in Ipv4 format.'


私はチームが多国籍で構成されていることを想定して英語で記載していますが。もちろん日本語でも問題ありません。
チームのコーディングルールに従うべきでしょう。

このようにcommentオプションを用いてマイグレーションを実行すると、

db/schema.rb

  create_table "activity_logs", options: "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4", force: :cascade do |t|
    t.bigint "tenant_id", null: false
    t.text "action", null: false, comment: "Behavior such as login is stored in json format."
    t.string "performer", null: false, comment: "Store polymorphic related class names."
    t.string "performer_type", null: false, comment: "Store polymorphic related class names."
    t.string "ip_address", comment: "store remote_ip address in Ipv4 format."
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["ip_address"], name: "index_activity_logs_on_ip_address"
    t.index ["performer"], name: "index_activity_logs_on_performer"
    t.index ["performer_type"], name: "index_activity_logs_on_performer_type"
    t.index ["tenant_id"], name: "index_activity_logs_on_tenant_id"
  end

schema.rbにcomment定義されます。

1人で個人開発してるくらいのレベルならいらない機能かもしれませんが、
複数人で中〜大規模(テーブル数50 ~ )のアプリケーション開発をするなら、
是非行っておきたい定義ですね。



勉強になりました。



参考にさせていただいたサイト

機能整理のコツについて

こんにちは!kossyです!



さて、今回は一つの機能を実装するときに使えるFWめいたものを
ブログに残してみたいと思います。しれっと更新するつもりです。



以下全てサンプルです。




目的(その機能が必要な背景や機能改修方針を記載する)

  • 販促のためにポイント付与機能を実装したい
  • 既存の機能に大幅な影響を及ぼすことが想定されるので、既存オペレーションとDB設計を念入りに調査し、将来の拡張や仕様変更を見越した設計をする

データ(必要なデータや項目例を記載する、機能改修の場合は既存のtable構造や関連も記載する)

データの役割

ポイントデータ
  • ユーザーが自信のアプリからポイントを確認できる
  • ユーザーはポイントを商品購入の際に使用できる
  • ユーザーはポイントを商品購入の際に獲得できる
商品購入
  • ユーザーは商品をカートに入れられる
  • ユーザーはカートに入れた商品をまとめて購入できる
  • ユーザーはカートに入れずに直接商品を購入できる
管理者機能
  • ユーザーに詫び石(ポイント)を配ることができる(create?, update?)

DB

User has_many points
User has_many products
User has_many 購買履歴
User has_many ポイント付与履歴
User has_many ポイント利用履歴
Products has_many 購買履歴

  • 他にも必要があれば適宜追加
  • ER図もあったほうがいい

CRUD契機(どんな業務・動作があった場合にCRUDが行われるのかを記載する)

ポイント機能

Create
  • ユーザーが商品を購入した時
Read
  • ユーザーが現在のポイントを確認するとき
  • ユーザーが過去に使ったポイントを確認する時
Update
  • ユーザーの現在保有ポイントを更新するとき(buy, use)
  • 管理者機能で考慮?
Destroy

依存関係(これから実装する機能であれば、この機能を作成することによる影響を記載する、既存機能であれば、この機能がどの業務・tableに依存しているか等を記載する)

ポイント機能

  • ユーザーが商品を購入する際の機能を修正することになると思う
  • キャンペーン機能にも考慮が必要

課題

  • ポイント失効・キャンペーン・ポイントが使えない商品・ポイントが貯まらない商品等の考慮が必要になる可能性あり

必要な要件や設計観点(CRUDで記載するのが無難。まずは必要要件を書くのもあり)

ポイント機能

  • ユーザーが自信のアプリからポイントを確認できる
  • ユーザーはポイントを商品購入の際に使用できる
  • ユーザーはポイントを商品購入の際に獲得できる
  • ポイントは1年間で失効する
  • キャンペーン期間中はポイント○倍にする
  • ポイントが使えない商品がある
  • ポイントが貯まらない商品がある

RailsでActiveRecordのserializeで保存したスナップショットと内部クラスを使ってデータを返す

こんにちは!kossyです!




さて、今回はRailsActiveRecordのserializeで保存したスナップショットと
内部クラスを使ってTemporaryなデータを返す方法をブログに残してみたいと思います。



環境

Ruby 2.6.3
Rails 6.0.3
MacOS Mojave



ActiveRecordのserializeとは

なにはともあれまずは公式ドキュメントをチェック

If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
then specify the name of that attribute using this method and it will be handled automatically.
The serialization is done through YAML. If class_name is specified, the serialized object must be of that class on assignment and retrieval.
Otherwise SerializationTypeMismatch will be raised.

Empty objects as {}, in the case of Hash, or [], in the case of Array, will always be persisted as null.

Keep in mind that database adapters handle certain serialization tasks for you.
For instance: json and jsonb types in PostgreSQL will be converted between JSON object/array syntax and Ruby Hash or Array objects transparently.
There is no need to use serialize in this case.

オブジェクトとしてデータベースに保存し、同じオブジェクトとして取得する必要がある属性がある場合は、
このメソッドを使用してその属性の名前を指定すると、自動的に処理されます。シリアライズYAMLを介して行われます。
class_nameが指定されている場合、シリアライズしたオブジェクトは、割り当てと取得時にそのクラスのものでなければなりません。
そうでない場合、SerializationTypeMismatchが発生します。

ハッシュの場合は {} としての空のオブジェクト、または配列の場合は [ ]として空のオブジェクトは常にnullとして永続化されます。

データベースアダプターは特定のシリアル化タスクを処理することに注意してください。
たとえば、PostgreSQLjsonおよびjsonbタイプは、JSONオブジェクト/配列構文とRubyハッシュまたは配列オブジェクトの間で透過的に変換されます。

出典: https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html

DBにJSONやオブジェクトを保存して、Rubyライクに処理ができるという認識で良さそうです。


内部クラスとは

クラスの中に定義されたクラスのことを内部クラスと言います。
内部クラスを利用することで、クラス間の関係がわかりやすくなり、ソースコードがシンプルになり、可読性が増します。
また、使用場所を限定し、その存在を外部から隠したい場合にも使用します。

出典: https://techacademy.jp/magazine/32398

インナークラスや内部クラスとググると、Javaでの実装の記事が多く出てくるので、Rubyではあまりメジャーな実装方法ではないのかもしれません。

本エントリでは、serializeで保存したスナップショットをクラスとして扱うようにして、責務の分離と可読性向上の達成を試みてみます。

サンプル


例として、オンライン書籍ストアアプリがあったとします。

テーブル構成は、

  • purchases(購買履歴)
  • books(書籍)
  • company(出版社)

で、関連は

  • purchaes has_many books & books belongs_to purchase

一回の購買で複数の書籍を買うこともあるため

  • company has_many books & books belongs_to company

この条件の時に、
「買った当時の書籍の出版社名を表示したい」という要件があった場合、
万が一出版社の名前が変わった場合、book belongs_to company の関連が組まれていたら、現在の情報を元に出版社名を取得するようになってしまいます。

これを防ぐために、purchasesテーブルに用意したbooks_info的なスナップショットが保存されたカラムのデータを元に出版社名を算出するようにします。

class Purchase < ApplicationRecord

  serialize :book_info, JSON

  class BookInfo
    attr_reader :company, :count

    def initialize(company:, count:)
      @company = company
      @count = count
    end
  end

end


app/views/purchaes/_purchaes.json.jbuilder

# book_jsonはこの仕様の場合は配列を想定すべきですが、手抜きしています ←

json.extract! purchase, :id, :created_at,
book_info = BookInfo.new(purchase.book_info['company'], purchase.book_info['count'])
json.extract! book_info, :company, :count,

これぐらいの情報量ならpurchasesに履歴データを表すカラムを追加すればいいのでは?と思われるかもしれませんが、
要件が変わってより多くのデータを扱いたくなった場合、際限なくカラムを追加することになるのはちと厳しいです。
より多く扱う必要が出た場合はJSONで保存する項目を増やし、都度attr_readerを生やす形を取ればpurchasesの責務が膨らまずに済みます。

Railsのrobots.txtの役割とは?

こんにちは!kossyです!




さて、今回はRailsのpublicリポジトリ下にデフォルトで作成されるrobots.txtの役割について、
ブログに残してみたいと思います。



アプリケーションをクローリング対象から外すため

平たく言うと、アプリをググれなくするためですね。

クローリングについては以下の記事が詳しかったです。

「クローリング」とは、クローラーと呼ばれるロボットがインターネット上にあるWebサイトの情報を集めることを言います。

出典: https://www.seo-pro.jp/seo/how-search-engine-works

クローラーに検知されないようにするには、robots.txtを適切に運用する必要があるわけです。



クローリング対象から外すにはどうしたらいい?

アプリケーションの全てのURLを検索対象から外したいなら、以下の記述をすればOKです。

/public/robots.txt

User-agent: * # 全てのクローラーが対象
Disallow: /  # アプリ内の全てのURLが対象


User-agentはGoogleクローラーのみ除外する、等の設定が可能です。

Disallowも、特定のパスだけアクセス不可のように設定ができます。

詳しくは以下のドキュメントをご確認ください。



勉強になりました。

RubyでXMLファイルを作成したい

こんにちは!kossyです!



さて、今回はRubyの標準モジュールである rexml/document を使って、XMLファイルを作成する方法について、
ブログに残してみたいと思います。



XMlとは

Extensible Markup Language(エクステンシブル マークアップ ランゲージ)は、
基本的な構文規則を共通とすることで、任意の用途向けの言語に拡張することを容易としたことが特徴のマークアップ言語の総称である。
一般的にXML(エックスエムエル)と略称で呼ばれる。JISによる訳語は「拡張可能なマーク付け言語」と定義している。

XMLの最も重要な目的は、異なる情報システムの間で、特にインターネットを介して、
構造化された文書や構造化されたデータの共有を、容易にすることである。
XMLを使うと、文書を構造化して記述できるし、コンピュータのデータを直列化 (シリアライズ) できる。

XMLは、ユーザが定義したタグを用いて文章構造を記述するマークアップ言語である。
HTMLが、Webページを記述するための言語であるのに対して、XMLは、データ交換のための汎用のデータ形式である。
HTMLで使用するタグはあらかじめ定義済みのものだが、XMLではユーザが新しくタグを定義して、データの意味や構造を記述することが可能である。

出典: https://ja.wikipedia.org/wiki/Extensible_Markup_Language

要はインターネット上で構造かされたデータをやりとりしたいときに用いるもの、という理解で問題なさそうです。

Ruby気象庁防災XMLファイルを作成してみる

以下のサイトにあったXMLのサンプルファイルをRubyで作ってみたいと思います。

基本的には、

① REXML::Document.newで雛形オブジェクトを作成

② REXML::XMLDecl.newで宣言要素を追加

③ add_elementやadd_textを駆使してXMLタグやテキストを記述していく

の流れになります。

より複雑なことをしたい場合は、

を参考にしてみてください。

以下、コードを全晒しします。

require 'rexml/document'

class CreateXml

  def initialize
    # 雛形オブジェクトの作成
    @doc = REXML::Document.new

    # 宣言要素の記述
    @doc << REXML::XMLDecl.new('1.1', 'UTF-8')

    # 要素の追加
    # add_element 要素名, 要素の属性
    # xmlns属性は名前空間を宣言するための属性
    # https://www.mitsue.co.jp/glossary/html4_xhtml1/attribute/xmlns.html
    @report_el = @doc.add_element 'Report', {
      'xmlns' => 'http://xml.kishou.go.jp/jmaxml1/',
      'xmlns:jmx' => 'http://xml.kishou.go.jp/jmaxml1/'
    }
  end

  def create_xml
    insert_control_part
    insert_head_part

    File.open(Rails.root + 'output.xml', 'w') do |file|
      # ファイル書き出し
      # インデントは2を指定
      @doc.write(file, indent=2)
    end
  end

  private

  def insert_control_part
    control_el = @report_el.add_element 'Control'

    title_el = control_el.add_element 'Title'

    # テキストの挿入
    title_el.add_text '季節観測'

    date_time_el = control_el.add_element 'DateTime'
    date_time_el.add_text '2009-01-09T02:02:05Z'

    status_el = control_el.add_element 'Status'
    status_el.add_text '通常'

    editor_office_el = control_el.add_element 'EditorialOffice'
    editor_office_el.add_text '熊谷地方気象台'

    publish_office_el = control_el.add_element 'PublishingOffice'
    publish_office_el.add_text '熊谷地方気象台'
  end

  def insert_head_part
    head_el = @report_el.add_element 'Head', {
      'xmlns' => 'http://xml.kishou.go.jp/jmaxml1/informationBasis1/'
    }

    title_el = head_el.add_element 'Title'
    title_el.add_text '季節観測'

    report_date_time_el = head_el.add_element 'ReportDateTime'
    report_date_time_el.add_text '2009-01-09T11:00:00+09:00'

    target_date_time = head_el.add_element 'TargetDateTime'
    target_date_time.add_text '2009-01-09T00:00:00+09:00'

    event_id_el = head_el.add_element 'EventID'
    event_id_el.add_text '20090109110000_初雪'

    info_type_id = head_el.add_element 'InfoType'
    info_type_id.add_text '発表'

    head_el.add_element 'Serial'

    info_kind_id = head_el.add_element 'InfoKind'
    info_type_id.add_text '特殊気象報'

    info_kind_version_el = head_el.add_element 'InfoKindVersion'
    info_kind_version_el.add_text '1.0_0'

    head_line_el = head_el.add_element 'Headline'
    head_el.add_element 'Text'
  end
end


生成されるXMLファイルはこんな感じ。
f:id:kossy-web-engineer:20200922020243p:plain



勉強になりました。



参考にさせていただいたサイト
class REXML::Document (Ruby 2.7.0 リファレンスマニュアル)
REXML で XML ファイルを作成する | まくまくRubyノート
気象庁防災情報XMLフォーマット | 技術資料
xmlns属性 | 用語集 | ミツエーリンクス
Extensible Markup Language - Wikipedia

UCSとCSI

こんにちは!kossyです!




さて、今回はプログラミングにおけるテキストデータ取り扱い時の用語であるUCSとCSIについて、
ブログに残してみたいと思います。



UCS方式

UCSはUniversal Character Setの略で、日本語にすると汎用文字集合という意味になります。
入出力時にプログラミング言語がテキストデータをUCSに変換し、内部ではテキストデータを統一的に扱う方式です。

利点

  • 仕組みがシンプルで実装が容易であ
  • 変換以外は処理コストが低い
  • 利用実績が多い

欠点

  • 不要な変換が発生することがある
  • 変換に曖昧な部分が残る
  • 外字・機種依存文字などの問題がある
  • UCSに含まれない文字は絶対に扱えない

CSI方式

CSIはCharacter Set Independentの略で、文字集合独立と訳すことができます。
各種文字集合と符号化方式を変換せずにそのまま扱う方式で、UCS方式が内部的には1種類の文字符号化方式を処理するのに対して、CSI方式は各種符号化方式をそのまま扱います。
CSI方式は複数の符号化方式を扱えるため、UCS方式のスーパーセット(上位集合)になります。
ちなみに、RubyCSI方式を採用している言語で、非常に稀有な存在になっています。
Rubyの作者のまつもとゆきひろさんは、主要な言語でUCS方式が採用され、
CSI方式があまり採用されない現状に、
「将来ロストテクノロジーになってしまいそう」と残念がっておられました。
RubyではなぜUCS正規化を採用していないのでしょうか? - Quora

利点

  • 無駄な変換が発生しない
  • 変換に由来する問題が発生しない
  • 外字などの問題が発生しにくい(発生しないように対処可能)
  • 原理的に扱えない文字がない
  • 必要に応じてアプリケーション独自の文字集合を扱うことも可能

欠点

  • 文字列処理が複雑化しがち
  • 処理性能が低くなると予想できる
  • 実績が少ない



参考にさせていただいた書籍・サイト

https://tatsu-zine.com/books/codenosekai

JavaScriptの歴史を学べる動画

こんにちは!kossyです!




さて、今回は「しまぶー」さんという方のJavaScriptの歴史についての動画の
要点をまとめたものをブログに残してみたいと思います。



しまぶーさんの動画はこちら

第1回

第2回

第3回

第4回

第5回



以下、要点をまとめたメモになります。


第1回

【Howの知識よりもWhyの知識が重要】

  • フロントエンド全般を理解する方が大事
  • How(使い方・やり方)の知識だけだと、応用が効きにくい
  • Why(なぜその技術が必要なのか)がわかると、技術の移り変わりに強くなれる

第2回

JavaScriptの誕生】

【IE3に搭載される】

  • 1996年にInternetExplorer3.0に搭載され、急速に普及
  • しかし、NetscapeのブラウザとIEで独自にブラウザの機能拡張を行なっていたため、それぞれに合うサイトを作る必要があった

ECMAScript(エクマスクリプト)の誕生】

  • JavaScriptの中核となる言語仕様のこと
  • NetscapeJavaScriptの仕様を一つにまとめ標準化するために、国際的な標準化団体EcmaInternationalに提出。結果1997年6月にECMAScriptの第1版が公開
  • 2015年に第6版が公開。そのリリースから年号が用いられるようになった。ES6は誤りで、正解はES2015である

JavaScriptの環境の話】

  • いろんな実行環境がある。例としてNode.jsがあるが、Node.jsにはDOM操作をするための機能はない

ECMAScriptJavaScript

  • ECMAScriptはどの実行環境でも共通な動作のみが定義されている
  • Node.jsやブラウザの違いなどを意識せずにECMAScriptで開発することができる(実際には実行速度に差があるため、気をつけるポイントがある)

【第一次ブラウザ戦争】

  • 1990年代後半はNetscapeとInternetExplorerが激しいシェア争いを繰り広げ、結果はご存知の通りIEの一人勝ちに。要因はWin98にIE4が標準搭載されたこと

JavaScriptへの失望】

  • JavaScript起因のクラッシュや悪用ウィルス被害が多発。「JavaScriptは危ないからOFFに」という対策も

JavaScript復権

  • 2005年、Microsoftが開発したAjaxによって、高機能なWebアプリケーション開発言語として再び注目を集める
  • GoogleMapsは当時非常に話題になった

jQueryの誕生】

  • 2006年にjQueryが誕生。少ない記述で多くの実装ができた
  • ブラウザ間の差異を吸収できたのも普及した要因

【第二次ブラウザ戦争】

【ServerSideJavaScriptの動き】

  • 2009年1月、MozillaのエンジニアKevin DangoorがServerJSというプロジェクトを立ち上げ
  • ServerJSはサーバーサイドで使うには機能不足で、APIを作る必要があった
  • 2009年8月、より広い範囲のAPIを対象とすることを示すために、現在のCommonJSへと改名

【モジュールAPIについて】

  • exportやimportを使用してファイル間で関数を呼び出したりできる
  • モジュール化を行うことで、巨大なjsファイルの分割が可能になった
  • JavaScript名前空間が一つしかない問題をモジュールが、依存関係問題をnpmが解決した

第3回

【今回伝えたいこと】

  • ブラウザでモジュールを使うために模索した結果、コードを事前に変換することが主流になった
  • コードを事前に変換すると、モジュールを使える以外にもいろんな恩恵がある

【Node.jsの誕生】

  • 2009年にRyanDahlによって作られる
  • 最初はCommonJSのモジュールAPIに準拠

【Node.jsとCommonJS】

  • CommonJSのコミュニティがうまく機能しなかったこともあり、Node.jsは独自の進化を遂げる。
  • CommonJSのプロジェクトはもう動いてない状態
  • JavaScriptいはモジュールの仕様が複数存在しており、CommonJSと検索しても分かりづらいのは、この歴史に起因している

JavaScriptのパッケージ管理システム登場の背景】

  • パッケージとは、package.jsonで記述されたファイルやディレクトリのこと。共有したい機能の単位
  • モジュールのおかげで名前空間問題が解決されて、機能を細かく分割できるようになった
  • 機能の細分化により、プロジェクト間で機能を共有して使用したいというニーズが生まれた

【パッケージ管理システム】

  • ローカル環境にインストールしたパッケージを更新できる
  • パッケージを検索できる
  • パッケージを指定してローカルにインストールできる
  • インストールしたパッケージを削除できる
  • そのパッケージを使用するにあたって必要になる別のパッケージもまとめて自動でインストールや更新ができる
  • パッケージ設定の管理もできる
  • 2010年にIsaac Z.SchlueterによってNode.jsのパッケージ管理システムであるnpm(Node Package Manager)が誕生
  • 上記の話はあくまでサーバーサイドJavaScriptの話で、ブラウザではモジュールも使えないしnpmのパッケージも使えない

第4回

【ブラウザ側のモジュール】

  • 昔からモジュールのパターン(4つ)はあったが、現在はあまり必要ない
  • IIFE(即時実行関数式)
  • 言語仕様としてのモジュールではないので、名前空間の問題も完全に解決されるわけではなかった

AMD & RequireJSの誕生】

  • ブラウザでのモジュールの扱いを改善するために、AMDという仕様が誕生。その仕様を実装しているものがRequireJS
  • AMDは、ブラウザ環境での実行を考慮し、依存関係の解決および遅延ロードに対応した仕様

【Bowerの誕生】

  • 2012年に誕生したクライアントサイド(ブラウザ)開発むけのパッケージ管理システム
  • ほとんどのBowerのパッケージは、IIFEモジュールかAMDモジュールを利用している
  • ところが、AMD形式でモジュールが使えたが、サーバー側の機能との互換性がなく、AMD形式の構文はCommonJS形式と比較して冗長だった
  • 依存関係が多いとメンテナンスが大変で、パフォーマンス面でも問題があった
  • どのパッケージがどう依存しているかを、開発者が手作業で定義する必要があった
  • 同一ページ内にある同じパッケージの異なるバージョンをサポートしていなかった。
  • npmとBowerで2重管理になる問題もあった

第5回

パラダイムシフト】

  • CommonJS形式で書かれたものを事前にブラウザ向けに変換するようにシフト
  • 今まではランタイム(実際にブラウザで動かすこと)のコードと書いたコードが一緒だった
  • 開発者が書いたコードと実際にブラウザで動作するコードが異なる

【Bundleとは】

  • モジュールの依存関係を解決して1ファイルに変換すること
  • 開発時はCommonJSモジュールで開発、ブラウザで動かすときはBundleした1ファイルを動かす
  • Bundleされたjsファイルはいつも通りscriptタグで読み込む

【Browserifyの誕生】

  • CommonJS形式で書かれたコードをブラウザ向けにBundleするツールで、2011年に誕生
  • 全ての依存関係を束ねて、ブラウザで require('modules')を使用可能にする
  • ブラウザにはrequireメソッドが定義されていないが、Browserifyを使うと、requireを使うコードが使用できるようになる
  • Browserifyの登場で、サーバーサイド向けだったNode.jsのパッケージがブラウザ向けに移植できるようになった

【webpackの誕生】

  • 2012年に誕生。Browserifyよりも高機能で、現在でも主流。(最新安定板は4.4.0。5系も開発中)
  • 対応するローダーがあれば、HTML・CSS・画像もBundleできる(ローダーについて: https://reffect.co.jp/html/webpack-loader-setting-for-beginner#webpackLoader)
  • Browserifyと異なるのは、JSに限らずなんでもBundleできる点
  • Code Splittingの機能も備えており、コードを分割して非同期で読み込むことにより、最初のロード時間を短縮することができる

【ES Modulesの誕生】

  • ECMAScript2015でJavaScriptの言語仕様としてモジュールの仕組みが導入(require ではなく import・export で書けるようになった!)
  • ところが、ES Modulesができても、全てのブラウザで対応しているわけではない
  • webpack Ver2でES Modulesに対応

【Compileとは】

  • 書いたコードをブラウザで動くように変換すること
  • 開発時はブラウザでは動作しないが、開発に便利な機能を使ってコードが書ける
  • 変換したコードはいつも通りscriptタグで読み込む

【Babel(6to5)の誕生】

  • 2014年に誕生。元々は6 to 5という名称だった。ES2015のような新しい仕様のJavaScriptを、IEのような非対応ブラウザでも動くように変換するコンパイラ
  • webpackと一緒に使うことができる(BundleとCompileが両方可能に!!)
  • コードを事前に変換する環境が整った結果、ReactやVue.js、TypeScriptといったパッケージが流行

【まとめ】

  • 開発環境で書いたコードをそのままブラウザで動かすのではなく、事前に変換するという大きなパラダイムシフト
  • jQueryは上記が理由であまり使われなくなった。(リアルDOMを操作するのと仮想DOMを動かすのでは考え方が異なるのもある)


参考
https://www.youtube.com/watch?v=De9PH3EAz7c
https://www.youtube.com/watch?v=PuomDgRbllw
https://www.youtube.com/watch?v=RdFE03K9B08
https://www.youtube.com/watch?v=vuWAhqgRI3M
https://www.youtube.com/watch?v=PFeR332LurM

Effective Ruby 第7章まとめ

こんにちは!kossyです!




さて、今回はEffective Rubyの第7章の覚えておくべき事項を
ブログにまとめてみたいと思います。

前回までの章をまとめたものはこちら
Effective Ruby 第1章まとめ - その辺にいるWebエンジニアの備忘録
Effective Ruby 第2章まとめ - その辺にいるWebエンジニアの備忘録
Effective Ruby 第3章まとめ - その辺にいるWebエンジニアの備忘録
Effective Ruby 第4章まとめ - その辺にいるWebエンジニアの備忘録
Effective Ruby 第5章まとめ - その辺にいるWebエンジニアの備忘録
Effective Ruby 第6章まとめ - その辺にいるWebエンジニアの備忘録


第7章 ツールとライブラリ

7-1. Ruby ドキュメントの扱い方を覚えよう

  • ドキュメントを読むためにはriユーティリティ、ドキュメントを生成するためにはrdocユーティリティを使う。
  • "doc"ディレクトリでドキュメントを探すようにriユーティリティに指示するには、コマンドラインオプションとして"-d doc"を指定する。
  • riで読めるドキュメントを生成するには、コマンドラインオプションとして"-f ri"を指定してrdocを実行する。
  • rdoc整形ルールの完全なドキュメントは、RDoc::Markupクラスにある

7-2. irbの高度な機能を使えるようになろう

  • IRB::ExtendCommandBundleモジュールか、IRB::ExtendCommandBundleにインクルードされるモジュールでirbカスタムコマンドを定義できる。
  • アンダースコア変数("_")を使えば、最後の式の結果にアクセスできる。
  • irbコマンドを使えば新しいセッションを開始し、評価コンテキストを任意のオブジェクトに変えることができる。
  • irbの代わりに使えるREPLとして人気のあるPryを検討してみよう。

7-3. Bundlerでgemの依存関係を管理しよう

  • Bundlerをロードしたあと、Bundler.requireを使えば、柔軟性は少し失われるが、Gemfileで指定した全てのGemをロードできる。
  • アプリケーションを開発するときには、Gemfileに使っているgemのリストを書き、バージョン管理システムにGemfile.lockファイルを追加しよう。
  • RubyGemを開発するときには、gem仕様ファイルにgemの依存コードを書き、バージョン管理システムにGemfile.lockファイルを入れないようにしよう。

7-4. 依存gemのバージョンの上限を指定しよう

  • バージョン要件の上限を省略するのは、自分のアプリケーションやライブラリが依存コードの将来の全てのバージョンをサポートすると公言するようなものだ。
  • 悲観的バージョン演算子ではなく、バージョン番号の範囲の明示的に示す方法を使うようにしよう。
  • gemを一般公開するときには、安全な範囲でできる限り広い範囲のバージョン要件を指定するようにしよう。上限は次に互換性がなくなる可能性のあるリリースの直前までにするとよい。


出典: https://www.shoeisha.co.jp/book/detail/9784798139821

Railsで人名が入ったCSVをopenする時のencodingはShift_JISではなくcp932にすべし!

こんにちは!kossyです!




さて、今回はRailsで人名が入ったCSVをopenする時のencodingはShift_JISではなくcp932にすべし、
という自戒を込めた備忘録をブログに残してみたいと思います。




環境
Ruby 2.6.3
Rails 6.0.3
MacOS Mojave



Shift_JISとcp932の違いについて

・CP932 は、Shift_JIS の独自実装
・符号化文字集合がいろいろ拡張されている
・拡張文字の有無で、Shift_JIS と判別可能

出典: https://qiita.com/kasei-san/items/cfb993786153231e5413


以下サイトも独自実装の経緯を図解しておりためになりました。

http://una.soragoto.net/topics/13.html

ここではShift_JISの符号化等の仕組みは説明しませんが、
要はShift_JISでは判別できない文字が存在するという事です。


旧字に悩まされる

「﨑」という字を含んだCSVファイルを作成しようとしたところ、
Encoding::UndefinedConversionErrorが発生して処理が中断してしまう事案がありました。

参考: https://docs.ruby-lang.org/ja/latest/class/Encoding=3a=3aUndefinedConversionError.html

これは「﨑」という字がShift_JISエンコードできずに発生したエラーでした。
https://www.kawa.net/works/jcode/uni-escape.html

そのため、Shift_JISではなくcp932をencodingに指定したところ、問題なく作成ができました。

このように、Shift_JISでは旧字に対応できない場合があるため、人名や地名が含まれるCSVを作成する場合は、
文字コードにcp932を指定することをお勧めします。



参考にさせていただいた記事
Shift_JIS に含まれない文字をエスケープ (Jcode.pm編)
Microsoftコードページ932 - Wikipedia
Shift_JIS、CP932、MS932、Windows-31J(文字コード関連) | 読み物 | ウナのIT資格一問一答
本当は怖くないCP932 - Qiita
class Encoding::UndefinedConversionError (Ruby 2.7.0 リファレンスマニュアル)
本当は怖くないCP932 - Qiita

Effective Ruby 第6章まとめ

こんにちは!kossyです!




さて、今回はEffective Rubyの第6章の覚えておくべき事項を
ブログにまとめてみたいと思います。

前回までの章をまとめたものはこちら
Effective Ruby 第1章まとめ - その辺にいるWebエンジニアの備忘録
Effective Ruby 第2章まとめ - その辺にいるWebエンジニアの備忘録
Effective Ruby 第3章まとめ - その辺にいるWebエンジニアの備忘録
Effective Ruby 第4章まとめ - その辺にいるWebエンジニアの備忘録
Effective Ruby 第5章まとめ - その辺にいるWebエンジニアの備忘録



第6章 テスティング

6-1. MiniTestユニットテストに慣れよう

  • テストメソッドの名前には、"test_"というプレフィックスを付けなければならない。
  • トラブルシューティングやメンテナンスを楽にするために、テストメソッドは短くする。
  • エラー時により良いメッセージを表示させるために、最も適したアサーションを使うようにする。
  • アサーションとその逆(refute_)は、MiniTest::Assertionsモジュールでドキュメントされている。

6-2. MiniTestスペックテストに慣れよう

  • テストクラスの作成にはdescribeメソッド、テスティングの定義にはitメソッドを使う。
  • アサーションメソッドも使えるが、スペックテストでは一般にObjectクラスに注入されたエクスペクテーションメソッドを使う。
  • エクスペクテーションは、MiniTest::Expectationsモジュールでドキュメントされている。

6-3. モックオブジェクトで決定論をシミュレートしよう

  • 外の世界の非決定性からテストを切り離したいときにはモックを使うとよい。
  • モックテストなどのメソッドを交換するテストは、本番でエラーを起こす未テストコードを残す危険がある。
  • テストメソッドを終える前に、必ずMiniTest::Mock#verifyを呼び出す。

6-4. 効果的なテストを追求しよう

  • コードのハッピーパスと例外パスの両方を試すためにはファズテスト、プロパティテストツールを使おう。
  • テストで実行されたからといって必ずしも正しいコードと言うわけではないため、コードカバレッジツールは間違った安心感を与える可能性がある。
  • 機能を書いているときにテストをする方があとでテストをするよりもずっと簡単だ。
  • バグの根本原因を探し始める前にそのバグのために失敗するテストを書こう。
  • できる限りテストを自動化しよう。


出典: https://www.shoeisha.co.jp/book/detail/9784798139821