s4na's blog

s4naのテックブログ

学んだことについて、アウトプットファーストで書いています!

Apache Arrow東京ミートアップ2019参加時のメモ、走り書き

まえがき

銀座RailsRubyコミッターのMurataさんが登壇された際、「Apache Arrowがすごい!近日1.0.0のリリースをするので、それに向けてイベントがある。ぜひ参加してほしい!」と話をされていたので、面白そうなので参加してみました。

素晴らしいイベントに会場を提供してくださったSpeeeさんありがとうございます!!

イベントの内容

  • 今日のイベントについて

  • 発表:Apache Arrowの最新情報 須藤功平さん

  • 発表:Apache Arrowの最新情報 村田賢太さん
    • https://speakerdeck.com/yutannihilation/r-and-apache-arrow
    • Apache Arrow
      • データ処理の効率化
        • 効率的の定義
          • 速度
            • 遅いところを早くすると早くなる
            • 高速化できるところを最適化
          • 実装コスト
        • データ交換処理
        • 交換速度を早くする方法
          • 何もしない
            • 何もしないためのデータフォーマットを定義している
              • メモリ上のフォーマット
                • メモリ上のフォーマットをそのまま渡すから、シリアライズが発生しない
        • モリーマップ機能
        • 大量データの計算の高速化
          • 想定ユースケース
            • OLAP
            • 列単位の処理が多い
          • SIMD
            • データをまとめてアライン
          • 競合リソースを作らない
            • アプローチ
              • リソースを参照にする
          • データをストリームで処理
          • フォーマットの変換
            • CSVとかをArrow形式にする必要がある
              • 読み込んで吐き出す処理を追加している最中
      • データ交換用であれば、既にだいたいの言語で使える
      • データの高速な計算は一部の言語のみ
        • 実装される順番はC++ベースが一番早い
  • 発表:Apache Arrow Datasets C++ 須藤功平さん
  • RとApache Arrow 湯谷啓明さん
    • ggplot2のメンテナ
    • Rユーザーの悩み
      • 他のシステムからデータを取ってくるのが大変(Spark, DB)
        • Java関連のセットアップが難しい
        • コピー・変換が遅い
      • 他のデータ分析者とデータを共有するのが大変
        • CSVで表現できないデータ型、パース方法の差異
        • メモリに乗り切らない巨大なデータ
    • Rユースケース
      • Spark
        • RとSparkとの変換が遅いので、それにApache Arrowを使いたい
        • Sparkと直接やりとりできるから早い
      • Parquet, Feather
        • メモリに乗らなかったデータの読み書きができる
        • data.table::fread()より早い
          • めちゃめちゃ早いで有名なデータテーブル形式(?)
      • dplyrのバックエンド
  • Apache ArrowでRの開発が進んでいる理由
  • 発表:PostgreSQLApache Arrowの利用事 海外浩平さん
    • https://www.slideshare.net/kaigai/20191211apachearrowmeetuptokyo
    • 所属: HeteroDB
    • PG-Strom
      • 大量のデータを処理
    • 工場20TB * 5年
      • 2Uサーバーなら192TB入る
        • 6万ドル
          • この金額ならいけそうだと感じたのでやってみた
    • GPUでIOを高速化
      • 集計処理でフィルタリングするデータもメモリに読み込んでいる
        • ゴミを運ぶためにIOを使っていると言える
      • IOの方法を変える
        • PCIeからSSDのデータをGPUに読み出した結果をメモリで処理すれば早くなる
          • 👆聞き取れてるか怪しい文章
    • Arrow_Fdw 効率的な列データストア
      • Postgresのデータは効率的ではない
        • 背景
          • データの生成
            • データを作成してデータベースに入れる処理が辛い
          • データ構造に無駄が多い
            • 空のブロックがあったりする
        • Apache Arrow
          • FDW
            • CSVのデータソースをテーブルであるかのように取り扱う機能がある
          • SSD-to-GPU Direct SQL
            • 被参照列だけを転送する
    • マルチGPU + SSD-to-GPU Direct SQL, Arrow_Flow, マルチGPU処理の全てを投入
      • レコードベースで見ると
        • 毎秒10億レコード🎉🎉🎉
  • 発表:Apache SparkとApache Arrowの連携 Takeshi Yamamuroさん
    • 所属: NTTのR&D部門
    • Spark
    • 今日のユースケース
      • Pandasの処理を分散処理したい場合
    • 4~100倍早くなる
    • Spark + Arrow Internal
  • 発表:TensorFlowとApache Arrowの連携 石崎一明
  • 発表:TensorFlowとBigQuery Storage APIApache Arrowの連携評価事例 漆山和樹さん
  • 個人的に

あとがき

走り書きなので、誤字脱字などあるかとは思いますが、見つけた際はご連絡いただけるとありがたいです。

フィヨルドブートキャンプのWebアプリで使っている、Vue.jsのコードリーディング

はじめに

今回はVue.jsで書かれたコードを読んでいきたいと思います!

Vue.jsは少ししか触ったことがないので、読み間違えているところなどあったら申し訳ないです😥
また、私は初学者なので、わからないことは「わからない」であるとか「?(疑問符)」を付けて書いています。
教えてくださる方がいらっしゃれば嬉しいです🙇‍♂️
わかったことがあれば随時追記していきます!

本記事にはrequestの結果などが記載されていますが、全てテスト環境のテストデータが元になっています。

今回読むコードについて

今回読んでいくソースは、フィヨルドブートキャンプ本体のWebアプリケーションです。

使っているフレームワークRails + Vue.jsです。また、Railsのテンプレートエンジンはslimを使用しています。そのため今回はslimで書かれたコードが多く登場します。

コード本体がこちら(fjord/bootcamp)で、OSSになっており誰でも読めます。

ライセンスについて

ライセンスはまだついておりませんが、今後付ける予定です。また、フィヨルドブートキャンプへの許可は取得しております。

今回読む箇所

今回はコメントをVue.js化 #1017を読んでいきます。

Vue.jsのコードリーディング

まずは日報(report)機能から読んでいきます。
読みたいのはコメント(comment)機能なのですが、コメント機能は独立で存在していないので、私としてはなじみのある日報のコメントから読んでいこうと思います。

/app/views/reports/show.html.slim

# 略

#js-comments(data-commentable-id="#{@report.id}" data-commentable-type="Report" data-current-user-id="#{current_user.id}")
= render "footprints/footprints", footprints: @footprints
  • #js-commentsdata-commentable-id, data-commentable-type, data-current-user-idを渡す
    • 疑問:Vue.jsのjsがなければ#〜はそのまま表示される?

/app/javascript/packes/application.js

/* eslint no-console:0 */
// This file is automatically compiled by Webpack, along with any other files
// present in this directory. You're encouraged to place your actual application logic in
// a relevant structure within app/javascript and only use these pack files to reference
// that code so it'll be compiled.
//
// To reference this file, add <%= javascript_pack_tag 'application' %> to the appropriate
// layout file, like app/views/layouts/application.html.erb
require('./markdown.js')
require('./markdown-it.js')
require('./autosize.js')
require('../shortcut.js')
require('../learning.js')
require('../learning-status.js')
require('../check.js')
require('../check-stamp.js')
require('../comments.js')
require('../category-select.js')
require('../grass.js')

// 略
  • Rails5.2のため、webpacker(Sprockets)でJavaScriptが管理されている
  • require('../comments.js')により、/app/javascript/comments.jsにアクセスが可能となる

/app/javascript/comments.js

import Vue from 'vue'
import Comments from './comments.vue'

document.addEventListener('DOMContentLoaded', () => {
  const comments = document.getElementById('js-comments')
  if (comments) {
    const commentableId = comments.getAttribute('data-commentable-id')
    const commentableType = comments.getAttribute('data-commentable-type')
    const currentUserId = comments.getAttribute('data-current-user-id')
    new Vue({
      render: h => h(Comments, { props: { commentableId: commentableId, commentableType: commentableType, currentUserId: currentUserId } })
    }).$mount('#js-comments')
  }
})
  • jsでhtmlの情報を取得してvueをレンダリング
    • 疑問:そういえばこれは、サーバー側で処理している?
      • もしそうなら、htmlの出力データをテンプレートエンジン代わりにしている?
  • element.getAttribute
  • 疑問:current-userのidをフロントのjsで判定したら、好きなユーザーとしてコメントを投稿できるのでは?
    • この疑問は、初めレンダリングをフロントで行なっていると想定していたため発生しました。実際はサーバーサイドで行なっているので、書き換えはできない状態でした。
  • 下記部分でcomment.vueを呼び出している
    new Vue({
      render: h => h(Comments, { props: { commentableId: commentableId, commentableType: commentableType, currentUserId: currentUserId } })
    }).$mount('#js-comments')

/app/javascript/comments.vue

/app/javascript/comments.vue

  • 「なんかたくさんあるけどなんだっけ?」と一瞬思ったけど、Vue.jsにはライフサイクルがあったことを思い出した
  • 変数とdate: () => {}の違いってなんだっけ?課題で、dateに書くか書かないかでとても違いが出たことしか覚えていない・・・
    • Vue.js入門を読んだら、date: () => {}はdataプロパティと書いてあった
      • UIの状態を置く場所
  • script以外に、scriptと一緒にtempleteが書かれた.vueファイルを読み書きしたことがないことに気づく😲‼️
    • Vue.js入門を再度読み返した

props

export default {
  props: ['commentableId', 'commentableType', 'currentUserId'],
  components: {
    'comment': Comment,
    'markdown-textarea': MarkdownTextarea
  },

data

  data: () => {
    return {
      currentUser: {},
      comments: [],
      description: '',
      tab: 'comment'
    }
  },
  • そういえば、props, dataの違いがわからないので、調べてみた
    • Vue.js/props
      • コンポーネントからデータを受け取るためにエクスポートされた属性のリスト/ハッシュです。

      • 疑問:親コンポーネントから一方的にデータを受け取るだけだから、データに変化がない?
    • Vue.js/data
      • Vue インスタンスのためのデータオブジェクトです。

      • Vue.js は再帰的にインスタンスのプロパティを getter/setter に変換し、”リアクティブ” にします。

      • オブジェクトはプレーンなものでなければなりません。 ブラウザの API オブジェクトのようなネイティブオブジェクトやプロトタイププロパティは無視されます。経験則としては、データはデータになるべきです。

      • 自身で状態を持つ振舞いによってオブジェクトを監視することは推奨されません。

      • Vue.js入門にあったように、UIの状態を置く場所

import

import Comment from './comment.vue'
import MarkdownTextarea from './markdown-textarea.vue'
import MarkdownIt from 'markdown-it'
import MarkdownItEmoji from 'markdown-it-emoji'
import MarkdownItMention from './packs/markdown-it-mention'
  • 疑問:なぜcomments.vueの中で、自分自身をCommentにimportしている?
    • comments.vuecomment.vueが別ファイルでした(この時点では気づかず、だいぶ後にになって気づく・・・)

created

  created: function() {
    fetch(`/api/users/${this.currentUserId}.json`, {
      method: 'GET',
      headers: {
        'X-Requested-With''XMLHttpRequest',
      },
      credentials: 'same-origin',
      redirect: 'manual'
    })
      .then(response => {
        return response.json()
      })
      .then(json => {
        for(var key in json){
          this.$set(this.currentUser, key, json[key])
        }
      })
      .catch(error => {
        console.warn('Failed to parsing', error)
      })

    fetch(`/api/comments.json?commentable_type=${this.commentableType}&commentable_id=${this.commentableId}`, {
      method: 'GET',
      headers: {
        'X-Requested-With''XMLHttpRequest',
      },
      credentials: 'same-origin',
      redirect: 'manual'
    })
      .then(response => {
        return response.json()
      })
      .then(json => {
        json.forEach(c => { this.comments.push(c) });
      })
      .catch(error => {
        console.warn('Failed to parsing', error)
      })
  },
  • fetch
    • MDN/Fetch API
      • request, responseするためのAPI
    • MDN/Fetch を使う
      • 従来、このような機能は XMLHttpRequest を使用して実現されてきました。 Fetch はそれのより良い代替となるもので、サービスワーカーのような他の技術から簡単に利用することができます。 Fetch は CORS や HTTP 拡張のような HTTP に関連する概念をまとめて定義する場所でもあります。

      • つまり、XMLHttpRequestとかAJaxは違う?🤔
      • fetch の仕様は jQuery.ajax() とは主に二つの点で異なっています。

        • fetch() から返される Promise は レスポンスが HTTP 404 や 500 を返して HTTP エラーステータスの場合でも拒否されません。代わりに (ok ステータスが false にセットされて) 正常に解決し、拒否されるのはネットワークのエラーや、何かがリクエストの完了を妨げた場合のみです。
        • 既定では、 fetch はサーバーとの間で cookies を送受信しないため、サイトがユーザーセッションの維持に頼っている場合は未認証のリクエストになります (cookie を送るには、認証情報の init オプションを設定しておく必要があります)。2017年8月25日に、既定の認証情報のポリシーが same-origin に変更になり、 Firefox は 61.0b13 から変更しました。
  • then
    • 初めてPromiseの実装コードを見た(MDNで軽く読んだことしかなかった)
    • 前から順番に流れていくので便利
    • Promiseを使う
  • /api/users/${this.currentUserId}.jsonから情報を取得している。
    • /api/users/:idは名前の通り、api
  • credentials: 'same-origin'とは?
  • 流れ
    • currentUserIdでユーザー情報取得
    • commentable_idcommentable_typeでコメントの親クラスについているコメントの情報を取得している

createdで呼び出しているAPIで受信する内容

  • http://localhost:3000/api/users/459775584.jsonの結果
{
id: 459775584,
login_name: "komagata",
url: "http://localhost:3000/users/459775584",
role: "admin",
avatar_url: "http://localhost:3000/rails/active_storage/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDRG9JYTJWNVNTSWRibEJ0UlhsV2VVSkdObXBRUjBRemFsRjZaWE5VVjBaakJqb0dSVlE2RUdScGMzQnZjMmwwYVc5dVNTSkRhVzVzYVc1bE95Qm1hV3hsYm1GdFpUMGlhMjl0WVdkaGRHRXVhbkJuSWpzZ1ptbHNaVzVoYldVcVBWVlVSaTA0SnlkcmIyMWhaMkYwWVM1cWNHY0dPd1pHT2hGamIyNTBaVzUwWDNSNWNHVkpJZzVwYldGblpTOXdibWNHT3daVSIsImV4cCI6IjIwMTktMTAtMzFUMTM6Mjk6NTQuNzc2WiIsInB1ciI6ImJsb2Jfa2V5In19--def46e9c9ea6a11afef8c3c0234562fbbf2d54f1/komagata.jpg?content_type=image%2Fpng&disposition=inline%3B+filename%3D%22komagata.jpg%22%3B+filename%2A%3DUTF-8%27%27komagata.jpg"
}
  • http://localhost:3000/api/comments.json?commentable_type=Report&commentable_id=1017786020の結果
    • コメントだけじゃなくて、reactionのデータも持っていた!発見
[
{
id: 1064126206,
description: "te",
created_at: "2019-10-31T15:25:28.271+09:00",
updated_at: "2019-10-31T15:25:28.271+09:00",
commentable_type: "Report",
commentable_id: 1017786020,
commentable: {
created_at: "2019-10-31T14:56:12.076+09:00"
},
user: {
id: 459775584,
login_name: "komagata",
url: "http://localhost:3000/users/459775584",
role: "admin",
avatar_url: "http://localhost:3000/rails/active_storage/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDRG9JYTJWNVNTSWRibEJ0UlhsV2VVSkdObXBRUjBRemFsRjZaWE5VVjBaakJqb0dSVlE2RUdScGMzQnZjMmwwYVc5dVNTSkRhVzVzYVc1bE95Qm1hV3hsYm1GdFpUMGlhMjl0WVdkaGRHRXVhbkJuSWpzZ1ptbHNaVzVoYldVcVBWVlVSaTA0SnlkcmIyMWhaMkYwWVM1cWNHY0dPd1pHT2hGamIyNTBaVzUwWDNSNWNHVkpJZzVwYldGblpTOXdibWNHT3daVSIsImV4cCI6IjIwMTktMTAtMzFUMTM6MzM6MjAuODc0WiIsInB1ciI6ImJsb2Jfa2V5In19--894cdfc20c4bedc6ba2c9f3276eef453b212bccb/komagata.jpg?content_type=image%2Fpng&disposition=inline%3B+filename%3D%22komagata.jpg%22%3B+filename%2A%3DUTF-8%27%27komagata.jpg"
},
reaction: [ ],
reaction_count: [
{
kind: "thumbsup",
value: "👍",
count: 0,
login_names: [ ]
},
// 略

{
id: 1064126207,
description: "a",
created_at: "2019-10-31T22:27:13.414+09:00",
updated_at: "2019-10-31T22:27:13.414+09:00",
commentable_type: "Report",
commentable_id: 1017786020,
commentable: {
created_at: "2019-10-31T14:56:12.076+09:00"
},
user: {
id: 459775584,
login_name: "komagata",
url: "http://localhost:3000/users/459775584",
role: "admin",
avatar_url: "http://localhost:3000/rails/active_storage/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDRG9JYTJWNVNTSWRibEJ0UlhsV2VVSkdObXBRUjBRemFsRjZaWE5VVjBaakJqb0dSVlE2RUdScGMzQnZjMmwwYVc5dVNTSkRhVzVzYVc1bE95Qm1hV3hsYm1GdFpUMGlhMjl0WVdkaGRHRXVhbkJuSWpzZ1ptbHNaVzVoYldVcVBWVlVSaTA0SnlkcmIyMWhaMkYwWVM1cWNHY0dPd1pHT2hGamIyNTBaVzUwWDNSNWNHVkpJZzVwYldGblpTOXdibWNHT3daVSIsImV4cCI6IjIwMTktMTAtMzFUMTM6MzM6MjAuODgyWiIsInB1ciI6ImJsb2Jfa2V5In19--a45376882bdb1078327ba7d83abfc021f2ada912/komagata.jpg?content_type=image%2Fpng&disposition=inline%3B+filename%3D%22komagata.jpg%22%3B+filename%2A%3DUTF-8%27%27komagata.jpg"
},

// 略

createdで呼び出しているAPIの送信コード

APIの構成の確認
  • APIjbuilderで作成されている
  • コメント機能に関係がありそうなAPIの構成
    • /app
      • /controllers
        • /api
          • comments_controller.rb
          • users_controller.rb
      • /views
        • /api
          • /comments
            • _comment.json.jbuilder
            • create.json.jbuilder
            • index.json.jbuilder
          • /users
            • _user.json.jbuilder
            • show.json.jbuilder
/app/controllers/api/users_controller.rb
# frozen_string_literal: true

class API::UsersController < API::BaseController
  def index
    users = User.select(:login_name, :first_name, :last_name)
      .order(updated_at: :desc)
      .as_json(except: :id)
    render json: users
  end

  def show
    @user = User.find(params[:id])
  end
end
  • index, showが来ると、それぞれ_users.json.jbuilder,show.json.jbuilderにリクエストを飛ばしている
    • 疑問:なぜindexに飛ばさない?
  • render json: XXX
    • jsonでrenderに渡す
/api/views/api/users/_user.json.jbuilder
json.(user, :id, :login_name, :url, :role)
json.avatar_url user.avatar_url
  • APIが簡単に書けるjbuilder素晴らしい
  • amatsuda/jbも良いという話を聞くので、今度触ってみたい
    • たしか「jbuilderと違い、DLSではなく生のRubyで書けるのが良い」みたいな話だったと記憶しています・・・
  • メモ:avatarのように、画像の場合はurlだけを渡す
/api/views/api/users/show.json.jbuilder
json.partial! "api/users/user", user: @user
  • 疑問:json.partial!とは?
    • パーシャルが展開できる
    • この場合、user毎に/api/views/api/users/_user.json.jbuilderを呼び出している
  • 疑問:コントローラーのusers@userで呼び出せているのはなぜ?どこで展開している?

You can use partials as well. The following will render the file views/comments/_comments.json.jbuilder, and set a local variable comments with all this message's comments, which you can use inside the partial. ruby json.partial! 'comments/comments', comments: @message.comments

rails/jbuilder https://github.com/rails/jbuilder

mounted

  • Vue.js/mounted
  • 疑問:textareaAutoSize()謎のメソッドが呼ばれている
    • importされているmarkdown-textarea.vueが怪しい
      • import 'textarea-autosize/dist/jquery.textarea_autosize.min'5行目でそれらしいjQueryを呼んでいた
        • javierjulio/textarea-autosizeのUsageにtextareaAutoSize()について記述があったのでビンゴ
        • 概要
          • This is a jQuery plugin for vertically adjusting a textarea based on user input and controlling any presentation in CSS.

          • Google翻訳:ユーザー入力に基づいてテキストエリアを垂直方向に調整し、CSSのプレゼンテーションを制御するためのjQueryプラグインです。

methods

  methods: {
    token () {
      const meta = document.querySelector('meta[name="csrf-token"]')
      return meta ? meta.getAttribute('content') : ''
    },
    isActive: function(tab) {
      return this.tab == tab
    },
    changeActiveTab: function(tab) {
      this.tab = tab
    },
    createComment: function(event) {
      if (this.description.length < 1) { return null }
      let params = {
        'comment': { 'description': this.description },
        'commentable_type': this.commentableType,
        'commentable_id': this.commentableId
      }
      fetch(`/api/comments`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json; charset=utf-8',
          'X-Requested-With': 'XMLHttpRequest',
          'X-CSRF-Token': this.token()
        },
        credentials: 'same-origin',
        redirect: 'manual',
        body: JSON.stringify(params)
      })
        .then(response => {
          return response.json()
        })
        .then(json=> {
          this.comments.push(json);
          this.description = '';
        })
        .catch(error => {
          console.warn('Failed to parsing', error)
        })
    },
    deleteComment: function(id) {
      fetch(`/api/comments/${id}.json`, {
        method: 'DELETE',
        headers: {
          'X-Requested-With': 'XMLHttpRequest',
          'X-CSRF-Token': this.token()
        },
        credentials: 'same-origin',
        redirect: 'manual'
      })
        .then(response => {
          this.comments.forEach((comment, i) => {
            if (comment.id == id) { this.comments.splice(i, 1); }
          });
        })
        .catch(error => {
          console.warn('Failed to parsing', error)
        })
    }
  },

createComment

    createComment: function(event) {
      if (this.description.length < 1) { return null }
      let params = {
        'comment': { 'description': this.description },
        'commentable_type': this.commentableType,
        'commentable_id': this.commentableId
      }
      fetch(`/api/comments`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json; charset=utf-8',
          'X-Requested-With': 'XMLHttpRequest',
          'X-CSRF-Token': this.token()
        },
        credentials: 'same-origin',
        redirect: 'manual',
        body: JSON.stringify(params)
      })
        .then(response => {
          return response.json()
        })
        .then(json=> {
          this.comments.push(json);
          this.description = '';
        })
        .catch(error => {
          console.warn('Failed to parsing', error)
        })
    },
  • プログラムの内容
    • nullなら何もしない
    • paramsでデータ受け取り
    • fetchでcommentsのレスポンス取得
    • レスポンスjson
    • commentsにデータ保管
    • エラーが発生したらキャッチし、consoleに出力

computed

  computed: {
    markdownDescription: function() {
      const md = new MarkdownIt({
        html: true,
        breaks: true,
        linkify: true,
        langPrefix: 'language-'
      });
      md.use(MarkdownItEmoji).use(MarkdownItMention)
      return md.render(this.description);
    },
    validation: function() {
      return this.description.length > 0
    }
  }
  • Vue.js/computed
    • Vueインスタンスの算出プロパティ
      • { [key: string]: Function | { get: Function, set: Function } }
  • markdownDescription
    • 疑問:markdownが使えるようにしていそうだけど、どうやって?
    • 絵文字を使えるようにしている

templete

<template lang="pug">
  .thread-comments-container
    h2.thread-comments-container__title コメント
    .thread-comments
      comment(v-for="(comment, index) in comments"
        :key="comment.id"
        :comment="comment",
        :currentUser="currentUser",
        @delete="deleteComment")
      .thread-comment-form
        .thread-comment__author
          img.thread-comment__author-icon(:src="currentUser.avatar_url")
        .thread-comment-form__form.a-card
          .thread-comment-form__tabs.js-tabs
            .thread-comment-form__tab.js-tabs__tab(:class="{'is-active': isActive('comment')}" @click="changeActiveTab('comment')")
              | コメント
            .thread-comment-form__tab.js-tabs__tab(:class="{'is-active': isActive('preview')}" @click="changeActiveTab('preview')")
              | プレビュー
          .thread-comment-form__markdown-parent.js-markdown-parent
            .thread-comment-form__markdown.js-tabs__content(:class="{'is-active': isActive('comment')}")
              markdown-textarea(v-model="description" id="js-new-comment" class="a-text-input js-warning-form thread-comment-form__textarea js-markdown" name="comment[description]")
            .thread-comment-form__markdown.js-tabs__content(:class="{'is-active': isActive('preview')}")
              .js-preview.is-long-text.thread-comment-form__preview(v-html="markdownDescription")
          .thread-comment-form__actions
            .thread-comment-form__action
              button#js-shortcut-post-comment.a-button.is-lg.is-warning.is-block(@click="createComment" :disabled="!validation")
                | コメントする
</template>
  • templeteタグの中はslim(RubyDSL)。
  • Vue.jsが処理した後、処理後の文字列をRailsに渡していて、slimとして処理されてます

投稿されたコメントの表示

      comment(v-for="(comment, index) in comments"
        :key="comment.id"
        :comment="comment",
        :currentUser="currentUser",
        @delete="deleteComment")
  • 投稿されたコメントの表示はcomment()内での処理が行なっています
  • comments自体のデータは、
    • data: () => { comments: [] }で定義され、
    • createdfetch/api/comments.jsonからの返りを受け取ったものをthis.comments.push(c)しています
  • 実際の表示自体はcomment.vueが行なっています

コメント新規投稿用フォーム

      .thread-comment-form
        .thread-comment__author
          img.thread-comment__author-icon(:src="currentUser.avatar_url")
        .thread-comment-form__form.a-card
          .thread-comment-form__tabs.js-tabs
            .thread-comment-form__tab.js-tabs__tab(:class="{'is-active': isActive('comment')}" @click="changeActiveTab('comment')")
              | コメント
            .thread-comment-form__tab.js-tabs__tab(:class="{'is-active': isActive('preview')}" @click="changeActiveTab('preview')")
              | プレビュー
          .thread-comment-form__markdown-parent.js-markdown-parent
            .thread-comment-form__markdown.js-tabs__content(:class="{'is-active': isActive('comment')}")
              markdown-textarea(v-model="description" id="js-new-comment" class="a-text-input js-warning-form thread-comment-form__textarea js-markdown" name="comment[description]")
            .thread-comment-form__markdown.js-tabs__content(:class="{'is-active': isActive('preview')}")
              .js-preview.is-long-text.thread-comment-form__preview(v-html="markdownDescription")
          .thread-comment-form__actions
            .thread-comment-form__action
              button#js-shortcut-post-comment.a-button.is-lg.is-warning.is-block(@click="createComment" :disabled="!validation")
                | コメントする
.thread-comment-form__tab.js-tabs__tab
          .thread-comment-form__tabs.js-tabs
            .thread-comment-form__tab.js-tabs__tab(:class="{'is-active': isActive('comment')}" @click="changeActiveTab('comment')")
              | コメント
            .thread-comment-form__tab.js-tabs__tab(:class="{'is-active': isActive('preview')}" @click="changeActiveTab('preview')")
              | プレビュー
  • 「コメント」と「プレビュー」の表示を制御しています
    • アクティブな方にis-activeを付与して表示を制御しているみたいです
.thread-comment-form__markdown-parent.js-markdown-parent
          .thread-comment-form__markdown-parent.js-markdown-parent
            .thread-comment-form__markdown.js-tabs__content(:class="{'is-active': isActive('comment')}")
              markdown-textarea(v-model="description" id="js-new-comment" class="a-text-input js-warning-form thread-comment-form__textarea js-markdown" name="comment[description]")
            .thread-comment-form__markdown.js-tabs__content(:class="{'is-active': isActive('preview')}")
              .js-preview.is-long-text.thread-comment-form__preview(v-html="markdownDescription")
  • 「コメント」「プレビュー」の表示を行なっています
  • 両方ともデータは常に持っていて、アクティブな方にis-activeをつけて、表示しているみたいです

.thread-comment-form__actions

          .thread-comment-form__actions
            .thread-comment-form__action
              button#js-shortcut-post-comment.a-button.is-lg.is-warning.is-block(@click="createComment" :disabled="!validation")
                | コメントする
  • コメント投稿機能です
  • #js-shortcut-post-comment

    • もしかしたら、comment.vueをimportしていたのはこのためかも?
  • :disabled="!validation"で空白だったら、computedvalidation: cunftion() {}が呼び出されて、投稿ボタンがdisabledされる

  computed: {
    // 略

    validation: function() {
      return this.description.length > 0
    }
  }

/app/javascript/comment.vue

/app/javascript/comment.vue

import

import Reaction from './reaction.vue'
import MarkdownTextarea from './markdown-textarea.vue'
import MarkdownIt from 'markdown-it'
import MarkdownItEmoji from 'markdown-it-emoji'
import MarkdownItMention from './packs/markdown-it-mention'
import Tribute from 'tributejs'
import TextareaAutocomplteEmoji from 'classes/textarea-autocomplte-emoji'
import TextareaAutocomplteMention from 'classes/textarea-autocomplte-mention'
import moment from 'moment'
  • reaction.vuemarkdown-textarea.vueはコメント機能への追加機能なので、今回は追わないことにします

props

  props: ['comment', 'currentUser', 'availableEmojis'],
  components: {
    'reaction': Reaction,
    'markdown-textarea': MarkdownTextarea
  },
  • props
    • コメント、ユーザー、絵文字を保持している
  • components
    • メモ:Vueインスタンス?もpropsに入れることがあるのか・・・知らなかった。
      • もし、変化するVueインスタンス(存在するかはわからない)があったら、dataに入るのかも?🤔

data

  data: () => {
    return {
      description: '',
      editing: false,
      tab: 'comment'
    }
  },

templete

<template lang="pug">
  .thread-comment
    .thread-comment__author
      a.thread-comment__author-link(:href="comment.user.url" itempro="url")
        img.thread-comment__author-icon(:src="comment.user.avatar_url" v-bind:class="userRole")
    .thread-comment__body.a-card(v-if="!editing")
      header.thread-comment__body-header
        h2.thread-comment__title
          a.thread-comment__title-link(:href="comment.user.url" itempro="url")
            | {{ comment.user.login_name }}
        time.thread-comment__created-at(:datetime="commentableCreatedAt" pubdate="pubdate")
          | {{ updatedAt }}
      .thread-comment__description.js-target-blank.is-long-text(v-html="markdownDescription")
      reaction(
        v-bind:reactionable="comment",
        v-bind:currentUser="currentUser")
      footer.card-footer(v-if="comment.user.id == currentUser.id")
        .card-footer-actions
          ul.card-footer-actions__items
            li.card-footer-actions__item
              button.card-footer-actions__action.a-button.is-md.is-primary.is-block(@click="editComment")
                i.fas.fa-pen
                | 編集
            li.card-footer-actions__item
              button.card-footer-actions__action.a-button.is-md.is-danger.is-block(@click="deleteComment")
                i.fas.fa-trash-alt
                | 削除
    .thread-comment-form__form.a-card(v-show="editing")
      .thread-comment-form__tabs.js-tabs
        .thread-comment-form__tab.js-tabs__tab(v-bind:class="{'is-active': isActive('comment')}" @click="changeActiveTab('comment')")
          | コメント
        .thread-comment-form__tab.js-tabs__tab(v-bind:class="{'is-active': isActive('preview')}" @click="changeActiveTab('preview')")
          | プレビュー
      .thread-comment-form__markdown-parent.js-markdown-parent
        .thread-comment-form__markdown.js-tabs__content(v-bind:class="{'is-active': isActive('comment')}")
          markdown-textarea(v-model="description" :class="classCommentId" class="a-text-input js-warning-form thread-comment-form__textarea js-comment-markdown" name="comment[description]")
        .thread-comment-form__markdown.js-tabs__content(v-bind:class="{'is-active': isActive('preview')}")
          .js-preview.is-long-text.thread-comment-form__preview(v-html="markdownDescription")
      .thread-comment-form__actions
        .thread-comment-form__action
          button.a-button.is-md.is-warning.is-block(@click="updateComment" v-bind:disabled="!validation")
            | 保存する
        .thread-comment-form__action
          button.a-button.is-md.is-secondary.is-block(@click="cancel")
            | キャンセル
</template>

.thread-comment__author

    .thread-comment__author
      a.thread-comment__author-link(:href="comment.user.url" itempro="url")
        img.thread-comment__author-icon(:src="comment.user.avatar_url" v-bind:class="userRole")
  • 投稿者アイコン(avatar)の表示

.thread-comment__body.a-card(v-if="!editing")

  • comments.vueとほぼ変わらず、コメント表示機能
    .thread-comment__body.a-card(v-if="!editing")
      header.thread-comment__body-header
        h2.thread-comment__title
          a.thread-comment__title-link(:href="comment.user.url" itempro="url")
            | {{ comment.user.login_name }}
        time.thread-comment__created-at(:datetime="commentableCreatedAt" pubdate="pubdate")
          | {{ updatedAt }}
      .thread-comment__description.js-target-blank.is-long-text(v-html="markdownDescription")
      reaction(
        v-bind:reactionable="comment",
        v-bind:currentUser="currentUser")
      footer.card-footer(v-if="comment.user.id == currentUser.id")
        .card-footer-actions
          ul.card-footer-actions__items
            li.card-footer-actions__item
              button.card-footer-actions__action.a-button.is-md.is-primary.is-block(@click="editComment")
                i.fas.fa-pen
                | 編集
            li.card-footer-actions__item
              button.card-footer-actions__action.a-button.is-md.is-danger.is-block(@click="deleteComment")
                i.fas.fa-trash-alt
                | 削除
  • v-bindとは?

  • .thread-comment-form__form.a-card(v-show="editing")

    • comments.vueとほぼ変わらず、コメント投稿機能
  • li.card-footer-actions__item

    • 「編集ボタン」、「削除ボタン」を提供しています
    • ボタンをクリックすると、editComment, deleteCommentが呼び出されます

methods

updateComment

    updateComment: function() {
      if (this.description.length < 1) { return null }
      let params = {
        'comment': { 'description': this.description }
      }
      fetch(`/api/comments/${this.comment.id}`, {
        method: 'PUT',
        headers: {
          'Content-Type': 'application/json; charset=utf-8',
          'X-Requested-With': 'XMLHttpRequest',
          'X-CSRF-Token': this.token()
        },
        credentials: 'same-origin',
        redirect: 'manual',
        body: JSON.stringify(params)
      })
        .then(response => {
          this.editing = false;
        })
        .catch(error => {
          console.warn('Failed to parsing', error)
        })
    },
  • nullの時は何もしない
  • descriptioncommentの内容として保管

その他

  • あまり変わらなさそうなので、軽くだけ目を通した

おわりに

思ったこと

結構読むのに時間がかかりました。
途中、コードを書き始めながら実装しなかったので、issue消化がこの記事を投稿してからになります・・・(これは明確な失敗だ・・・)

アウトプットはしていきたいですが、進捗も出したいので、今後も工夫して上手く両立させていきたいです。

アジャイルソフトウェア開発とスクラムについてまとめた

まえがき

今週からフィヨルドブートキャンプのスクラムチームに参加します。
そのため、スクラムチームに参加する前に、「アジャイルソフトウェア開発とは?」「スクラムとは?」という概念について、知っていることを調べ補足しつつまとめたので公開します。

アジャイルソフトウェア開発

  • ソフトウェア開発手法の一種
  • 通称アジャイル
  • アジャイルソフトウェア開発はソフトウェア開発手法群の総称であり、スクラム、XP開発など複数の開発手法が属する。そのため、スクラムだけがアジャイルではない
  • ウォーターフォール開発手法とよく比較される
  • ウォーターフォール開発手法が「計画、設計〜テスト、リリース」までのフェーズを1段階ずつこなしていく手法。動くソフトウェアは一番最後にならないと見れない
  • 動くソフトウェアとは、フィーチャ(ユーザー側の視点で記述された、ユーザーにとってのソフトウェアの価値を表現したもの)を提供するソフトウェア。1レイヤー単位の実装(モジュール単位など)ではなく、1部分だけでもいいので、全レイヤーを通したソフトウェアを作成すること
  • アジャイルは1イテレーション(開発単位)ごとに動くソフトウェアをインクリメント(成果物)として出していく
  • アジャイルは1イテレーションごとにインクリメントを出すことで、適宜計画や方針を検討できる
  • アジャイルはプロダクトオーナーと開発チームとの情報非対称性を減らす
  • アジャイルはチーム内での情報非対称性を減少させる

価値観と原則

  • アジャイルの考え方は価値観と原則により規定されている
  • アジャイルアジャイルのチーム全員が価値観と原則を共有し実践することで、品質の高いソフトウェア開発が行える
  • ここでいう品質とは、ユーザーに対して価値を提供できるソフトウェア開発のことを指し、絶対的な価値観ではない

4つの価値(左より右に価値を置く。※左に価値がないということではない)

  • 「プロセスやツール」よりも「個人と対話」
    • アジャイルでは、プロセスやツールにのったコミュニケーションではなく、個人を一個人として尊敬し尊重し対話することに重きが置かれる。また、対面でのコミュニケーションによる相互理解で、より良いチームが作られる
  • 「包括的なドキュメント」より「動くソフトウェア」
    • 例えばウォーターフォールでは、工程毎に包括的なドキュメントが作成できる。アジャイルでは、それよりも実際に動くソフトウェアをインクリメントとして提示していく
  • 「契約交渉」より「顧客との協調」
  • 「計画に従うこと」よりも「変化への対応」
    • 計画していた当初と前提条件が変化することもある。アジャイルでは、変化に合わせて顧客にフィーチャを提供していく

12の原則

  • 顧客の満足を求め続ける
  • 要求の本質を見抜き、変更を前向きに
  • 成果物を2,3週間で、リリースし続ける
  • 全員で共通の目標に向かおう
  • 人の意欲は信頼から
  • 顧客も開発チームも直接対話で
  • 進捗も品質も現物で
  • 一定のペースでプロジェクトにリズムを
  • よい技術、よい設計、よい品質の追求
  • 無駄=価値を生まない、を探してやめる
  • よいモノはよいチームから
  • 自分たちのやり方を毎週、調整する

スクラム

  • アジャイルソフトウェア開発の一種
  • アジャイルの価値観、原則に則った上で、具体的なソフトウェア開発手法がまとまっている
  • スクラムの考え方は「スクラムを支える3本柱」と「価値基準」によって規定されている

スクラムを支える3本柱

価値基準

  • 確約
    • 個人はスクラムチームのゴールの達成を確約しなければならない
  • 勇気
    • スクラムチームのメンバーは正しいことをする勇気を持ち、困難な問題に取り組まなければならない
  • 集中
  • 公開
  • 尊敬
    • スクラムチームのメンバーはお互いを能力のある独立した個人として尊敬しなければならない

スクラムチームの役割

  • プロダクトオーナー
  • スクラムマスター
    • プロジェクトを円滑に進める責任を持つ人
  • 開発チーム
    • 動くソフトウェアを開発する人
    • 1チームは10人以下が望ましい
  • スクラムチーム

スクラム開発の流れ

  1. プロダクトバックログからバックログを選び、スプリントバックログに入れる
  2. スプリントバックログをもとにスプリント

  3. プロダクトバックログの意味: プロダクトの機能を記述したもの。プロダクトオーナーが優先順位を変更する

  4. スプリントバックログの意味: スプリント期間中に行うタスクのリスト

スプリントの流れ

注意

  • スプリント期間中タスクの追加はできない

スプリントの流れ

  1. スプリントプランニング
    1. スプリント期間中に何を行うか決める
  2. デイリースクラム
    1. 毎日1日の作業開始前に開発チームで行う活動
    2. 誰がどのスプリントバックログに着手するか、問題が発生していないかを15分で行う
  3. スプリント実施
    1. スプリントバックログを消化する
  4. スプリントレビュー
    1. スプリントで作成したインクリメントの検査、プロダクトバックログの適応を行う
  5. スプリントレトロスペクティブ
    1. チームの成長、プロセスの改善を目的に、振り返りを行う
  6. リファインメント(旧名称: グルーミング)
    1. 優先順位の見積もり、修正を行う

作業単位

  • スクラムではユーザーストーリー単位で作業を行う
  • 結局チームはユーザーストーリーをタスクに分解するが、分解するのはストーリー開発を開始するイテレーションの開始時点

ユーザーストーリー

  • ユーザーにとって実現したいこと、価値のあること

ストーリーの書き方

  • ユーザーストーリー
  • DONEの定義(完了の定義)
  • ストーリーポイント

タスク

  • ユーザーストーリーを実現するために分解したもの
  • タスクをこなすことでユーザーストーリーの実現に近くが、ユーザーストーリー全体を完了させなければユーザーにとって価値が生まれない

タスクの書き方

  • タイトル
  • 作業内容
  • 割り込みかどうか
  • 誰が担当するか
  • ポイント

見積もり方法

  • 見積もりは複数作らない。

  • プランニングポーカー

    • やり方
      • 参加者にフィボナッチ数列が書かれたカードを配布
      • 基準となる数値を決めるため、簡単なタスクに1を割り当て、共通の基準とする
      • タスクを一つ選び、仕様がわからない人がいれば、その場で説明
      • 他のタスクは基準のポイントを基準に、一斉にカードを出す
      • 最小値、最大値を出した人の意見に聞く
      • 全員が納得できるまで続ける
  • ポイント振りが難しい場合、スパイクする

    • スパイクの意味: タスクを分解すること

参考

銀座Rails#12@DeNAに参加してきました!

銀座Rails#12@DeNA

TechRachoの@hachi8833さん、@morimorihogeさん

  • TechRachoを書いたり、RAILS GUIDESを翻訳している方々
  • いつも見ているサイトを書いている人がいらっしゃった。
  • 事前アンケートの結果について(Rails,Rubyの使い方、使っている用途など)
  • Rails 6.0
    • 複数DBがgemの追加なしでできるようになった
  • 話題のGem
    • Fullstaq Ruby
      • 早いと話題
    • Ruby on Jets
      • Railsっぽく書いたものがAWSに載せられる
    • Serveo / Ngrok

Rails使いのNuxt.js @ssh_nofさん

  • Nuxt.js
    • Vue.jsのためのフレームワーク
    • SSRをやるためにもともとできた。
    • 今回はSPAの説明
    • 実際にタスクアプリを作成したコードの説明

プログラマーがコードを書きながら考えること @jnchitoさん

  • プロを目指す人のためのRuby入門(通称:チェリー本)の著者。私ももちろん持っている。
  • ライブコーディング(録画)での発表で、考えていることを字幕や補足を交えつつ説明してくださった。
  • また、ライブコーディングを見るにあたって、「自分と同じところ」「自分と違うところ」などを探すようにアドバイスしてくださったりした。
  • ライブコーディングで考えたこと
    • 自分と同じだと思ったところ
      • 命名に悩む
      • gemを書く順番に悩む
    • 自分と違うなと思ったところ
      • Gemの知識が多い。広い
        • vcr
        • webmock
      • Rubymine, Webコンソールを上手に組み合わせて使っている
      • testでselfはtest ".XXX" do ~ endみたいな書き方をしていた
      • コスト意識が高い
        • 今回は書き捨てのコードとはいいつつも、「動作確認があるのでテストは書く」「テストがあるので関数の行数が増えても気にしない」など、優先度付けが上手いと感じた。
  • 今回のプレゼンテーションで使用した動画などは後日何か下の形でアップロードを予定しているそうです。気になる。

# あとがき

参加中に次回のアナウンスがあったんですけど、もう既に13回目の予約が定員超えているなど人気が凄かったです。

実際に参加してみて、普段読んでいる本やサイトの著名など、情報の発信源とも言えるような方々が実際目の前で話をしてくれたり、質問に答えてくださったりするので、大変ありがたい機会だなと感じました。

また、そういったこともあり普段仕事でRailsを書いている方々と直接交流でき、今回であれば「どういう仕事・業界があるか」「新人の育成方法」「ペアプロの効果」「おすすめのサイト・本・イベント」などの話が出来て良かったです。

Shinjuku.rb #74 Extreme Fish Bowlに参加してきました!

Shinjuku.rbに参加

Shinjuku.rb #74 Extreme Fish Bowlに参加してきました。

やったこと

  • 自己紹介
  • テーマ説明
  • Extreme Fish Bowl
    • モブプロ形式でペアプロをする
    • 時間は5分交代
    • 交代方法
      • 初めは指名制
      • ナビゲーターがドライバーに移動
      • ドライバーが次のナビゲーターを指名
  • 備考
    • ペアプロ
      • ナビゲーター(指示者)とドライバー(コードを書く人)で別れて
    • モブプロ
      • プロジェクターなどに写しながらプログラミングをすること
      • 周りの人はプログラムについて意見を雑談形式で述べる
      • 登壇者は周りの意見を踏まえた上で考えながらコードを書く
  • 感想戦
    • Extreme Fish Bowlのコミットログを見ながら、雑談する
  • 振り返り
    • KPTで振り返りつつ、次回のテーマ決め
      • 次回はあっち向いてホイになりました!
  • 参考

あとがき

長年プログラムを書いてきた方々と、一緒に考えながらプログラムが書けて大変貴重な時間でした。

私は人とリアルタイムで一緒にプログラムを書いた経験がほぼ皆無だったので、人前でコードを書く緊張感を感じつつ、時間を分単位で気にして書くのが大変刺激的で楽しかったです。

また、二次会ということで、Shinjuku.rbのメンバーでRepro株式会社様ボードゲームをお借りして楽しめたのも良かったです。

コードネーム 日本語版というゲームで、プログラミングでいうところの抽象クラスを作るゲームなので、エンジニアにはぴったりな遊びだなと思いました。

ただ、人数は6人以上いた方が楽しめそうなので、その点だけ気をつけておく必要があると思います。

Extreme Fish Bowl初回参加時、事前にしておきたかったこと

  • 事前に作成するプログラムのテーマ(ゲーム)が提示されていたので、ルールを詳しく把握しておく
    • ローカルルールなどあり限られた時間内で作るとなると、素早い判断が求められるため
  • 変数名、関数名などを事前に想定しておく
    • 名付け慣れている人なら別かもしれないが、人前で緊張する&時間が限られており中で、作っていくのは大変。特にお酒も可な勉強会なので・・・

「プロになるためのRuby入門」を読みながら「TDDで改札機プログラムを実装」してみた


test-unit実践

今回は昨日学んだテスト駆動開発(以下、TDD)について、test-unitを使いつつ実装してみます。 内容は「プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで (Software Design plusシリーズ)」に書いてある改札機のプログラムです。

単にコピペしても勉強にならないと思ったので、なるべく本を読まないようにして作ってみました。(とはいえわからないところ含め、変数名などはだいぶ似てしまっている)

まずは要件を文章化

# 改札機プログラム

## 作りたいもの

改札機のプログラム
- チケットをお金で買うことができる
- 改札機から入ることができる
- 料金が正しければ、降りた駅で改札機から出れる
- 料金が間違っていれば、改札機から出れない

## 要件

- 山手線
  - 駅
    - 池袋
    - 新宿
    - 渋谷
  - 値段
    - 120yen: 池袋 〜 新宿
    - 120yen: 新宿 〜 渋谷
    - 200yen: 池袋 〜 渋谷

## 動作する仕様

``
ikebukuro = Gate.new(:ikebukuro)
sibuya = Gate.new(:sibuya)

ticket = Ticket.new(120)
ikebukuro.enter(ticket)
sibuya.exit(ticket) #=> false

ticket = Ticket.new(200)
ikebukuro.enter(ticket)
sibuya.exit(ticket) #=> true
``

要件を満たすシナリオの作成

シナリオ1

  • 120円の切符を購入
  • 池袋で入場、新宿で出場
  • 出場できる

シナリオ2

  • 120円切符を購入
  • 池袋で入場、渋谷で出場
  • 出場できない

シナリオ3

  • 200円の切符を購入
  • 池袋で入場、渋谷で出場
  • 出場できる

シナリオ4

  • 120円の切符を購入
  • 新宿で入場、渋谷で出場
  • 出場できる

ディレクトリ準備

ruby_ticket_machine
- lib
  - gate.rb
- test
  - gate_test.rb

クラスオブジェクトの作成

Redにする(テストから書き始める)

クラスオブジェクト作成の確認から

# frozen_string_literal: true

require "test/unit"
require "./lib/gate"

class GateTest < Test::Unit::TestCase
  def test_gate
    assert Gate.new
  end
end

Greenにする

# frozen_string_literal: true

class Gate
end

Refactor

今回は特に必要がなさそうなので終了

シナリオ1の実装

テストメソッドに処理の順番を仮置きする

  def test_gate
    # 前準備
    # 実行
    # 検証
    # 後片付け(状況により必要)
  end

実装したいコードの確認

ikebukuro = Gate.new(:ikebukuro)
sinjyuku = Gate.new(:sinjyuku)

ticket = Ticket.new(120)
ikebukuro.enter(ticket)
sinjyuku.exit(ticket) #=> true

シナリオ1のタスク細分化

  1. Gate.new(:ikebukuro)できる
  2. Gate.new(:sinjyuku)できる
  3. Ticket.new(120)できる
  4. ikebukuro.enter(ticket)できる
  5. ticket.stamp(駅名)で乗車駅を保存
  6. ikebukuro.enter(ticket)した後、sinjyuku.exit(ticket) #=> falseになる
  7. ticket.stamped_atで乗車駅を参照できるように
  8. Gatecalc_fireで生産できるかテスト(あとでプライベート化するかもしれないがその前にテスト)

Task1. Gate.new(:ikebukuro)できる, 2. Gate.new(:sinjyuku)できる

過去のコードで対応できているので省略

Task3. Ticket.new(120)できる

Red

# ./test/ticket_test.rb
# frozen_string_literal: true

require "test/unit"
require "./lib/ticket"

class TicketTest < Test::Unit::TestCase
  def test_new
    assert Ticket.new(120)
  end
end

Green

# ./lib/ticket.rb
# frozen_string_literal: true

class Ticket
  def initialize(fare)
    @fare = fare
  end
end

Refactor

なし

Task4. ikebukuro.enter(ticket)できる

Red

# ./test/gate_test.rb
# 省略

class GateTest < Test::Unit::TestCase
  # 中略

  def test_enter
    ikebukuro = Gate.new(:ikebukuro)
    ticket = Ticket.new(120)
    ikebukuro.enter(ticket)
  end
# 省略

Green

# ./lib/gate.rb
# frozen_string_literal: true

class Gate
  # 中略
  def enter(ticket)
    true
  end
end

Refactor

なし

Task5. ticket.stamp(駅名)で乗車駅を保存

Red

# ./test/ticket_test.rb
# 中略

class TicketTest < Test::Unit::TestCase
  # 中略
  def test_stamp
    ticket = Ticket.new(120)
    assert ticket.stamp(:ikebukuro)
  end

Green

# ./lib/ticket.rb
# 中略

class Ticket
  # 中略
  def stamp(station)
    @boarding_station = station
  end
end

Refactor

なし

Task6. ikebukuro.enter(ticket)した後、sinjyuku.exit(ticket) #=> falseになる

Red

# ./test/gate_test.rb
# 中略

class GateTest < Test::Unit::TestCase
  # 中略

  def test_exit
    # 前準備
    ikebukuro = Gate.new(:ikebukuro)
    sinjyuku = Gate.new(:sinjyuku)
    # 実行
    ticket = Ticket.new(120)
    ikebukuro.enter(ticket)
    # 検証
    assert sinjyuku.exit(ticket)
    # 後片付け(状況により必要)
  end
end

Green

# ./lib/gate.rb
# 中略

class Gate
  # 中略
  def exit(ticket)
    true
  end
end

Refactor

なし

Task7. ticket.stamped_atで乗車駅を参照できるように

Red

# ./test/ticket_test.rb
# 中略

class TicketTest < Test::Unit::TestCase
  # 中略

  def test_stamped_at
    ticket = Ticket.new(120)
    ticket.stamp(:ikebukuro)
    assert_equal(:ikebukuro, ticket.stamped_at)
  end
end

Green

# ./lib/ticket.rb
# 中略

class Ticket
  # 中略

  def stamped_at
    @boarding_station
  end
end

Refactor

なし

Task8. Gatecalc_fireで生産できるかテスト(あとでプライベート化するかもしれないがその前にテスト)

Red

あとでプライベート化するかもしれないがその前にGate.calc_fireのテスト

# ./test/gate_test.rb
# 中略

class GateTest < Test::Unit::TestCase
  # 中略

  def test_calc_fire
    # 前準備
    ikebukuro = Gate.new(:ikebukuro)
    sinjyuku = Gate.new(:sinjyuku)
    # 実行
    ticket = Ticket.new(120)
    ikebukuro.enter(ticket)
    # 検証
    assert sinjyuku.calc_fire(:sinjyuku)
  end
end

Green

※追加行は+、削除行は-を先頭に付与しています

# frozen_string_literal: true

class Gate
  STATIONS = [:ikebukuro, :sinjyuku, :sibuya]
  FARES = [120, 200]

  # 中略


  def exit(ticket)
-   true
+   calc_fire(ticket.stamped_at)
  end

  def calc_fire(exit_station)
    distance = (STATIONS.index(@station) - STATIONS.index(exit_station)).abs
    FARES[distance - 1]
  end
end

Refactor

  • Gate.exitが成否ではなく、計算結果を返しているようになっているので修正
# ./lib/gate.rb
# 中略

class Gate
  # 中略

  def exit(ticket)
    pay_of(ticket)
  end

  private
    def pay_of(ticket)
      0 <= (ticket.fare - calc_fire(ticket.stamped_at)) ? true : false
    end
end
# ./lib/ticket.rb
# 中略

class Ticket
  attr_reader :fare

  # 中略
end

シナリオ2

# ./test/gate_test.rb
# 中略

class GateTest < Test::Unit::TestCase
  # 中略

  def test_exit_scenario_2
    # 前準備
    ikebukuro = Gate.new(:ikebukuro)
    sibuya = Gate.new(:sibuya)
    # 実行
    ticket = Ticket.new(120)
    ikebukuro.enter(ticket)
    # 検証
    assert_equal(false, sibuya.exit(ticket))
    # 後片付け(状況により必要)
  end

  def test_exit_scenario_3
    # 前準備
    ikebukuro = Gate.new(:ikebukuro)
    sibuya = Gate.new(:sibuya)
    # 実行
    ticket = Ticket.new(200)
    ikebukuro.enter(ticket)
    # 検証
    assert sibuya.exit(ticket)
    # 後片付け(状況により必要)
  end

  def test_exit_scenario_4
    # 前準備
    sinjyuku = Gate.new(:sinjyuku)
    sibuya = Gate.new(:sibuya)
    # 実行
    ticket = Ticket.new(120)
    sinjyuku.enter(ticket)
    # 検証
    assert sibuya.exit(ticket)
    # 後片付け(状況により必要)
  end
end

疑問点の検討(自問自答)

  • 他のクラスをテストする必要があったらどうすればいい?
      • シナリオ1でgateクラスのtestをしている際、tickeクラスの話が登場してきた
        • しょうがない時もある
        • どういう場合はどっちのクラスに書いたほうがいいなどルールはある?

参考にした本について

プロを目指す人のためのRuby入門 言語仕様からテスト駆動開発・デバッグ技法まで (Software Design plusシリーズ)

めっちゃオススメです!
Ruby初学者の方にオススメなので、本屋さんなどでぜひ手にとってみてはいかがでしょうか?

著者の方について、ブログなどにもたくさん情報が載っているので貼っておきます。
give IT a try - 伊藤淳一 (id:JunichiIto)

あとがき

やり方が悪かったのか、正直「TDDのサイクルが回っている実感」はありませんでした。
もう少しテストの粒度を大きくした方がいいのか(今回は初めてなのであえて小さくしましたが)、それともRefactorできるところがあったのか。

テスト駆動開発について書かれた本ではないので、そういった本に書かれているプログラムを作ってみたらまた違ったのかもしれません。

明日以降はRailsのテストを書く予定なので、そこでまた検討してみたいと思います。

テスト駆動開発とは


本文について

昨日テスト駆動開発について学んだので、その内容をまとめてみました。
わからないところもあるので、その点については正直に「わからない旨」と記載しております。
テスト駆動開発」に入る前に、知っておいた方が良い「テスト」について書き、その後に「テスト駆動開発」について書いております。

誤字脱字ご指摘など連絡先

@s4na_penguin


テストとは

1. テストの最小単位

  • 不安
    • 正しく動かないことに対する不安の解消

2. 注意事項

  • テストの状態
    • Greenにするか消すかの二択
      • 放置してはいけない
  • テストのメンテナンスコスト
    • プロジェクト後半に発生する

3. 資産価値

  • 大きいテストの方が価値がある
    • 小さいテストは大きいテストをGreenにするためにある
  • 大きいテストの価値=小さいテストを積み上げていったもの

4. 分類

5. テストという言葉の持つ障害

  • 各々がテストという単語に対して、それぞれの立場で別の認識があるため、目的を明確にしていないと方向性にズレが出やすい
    • プロジェクトでテストという言葉は再定義しなければならない

6. テストの書き方

  • 2つの方針
    • 重複はあっても良い
      • テストコードはその部分だけ見ればある程度何をやってるかわかるコード,重複があってもいいから何をやってるかわかるノッペリとした書かれ方がいい
    • 重複はない方が良い
      • テストコードもコードはコード,テストコードにもプロダクトコードと同様にDRY原則が満たされなければならない
  • 重複があること
    • メリット
      • テストだけでやってることがわからないと、どこを探したら良いかわからないと大変
    • デメリット
      • 何か一つ修正したタイミングで、一気にRedが出ることがある
        • 修正にも時間がかかる

テスト駆動開発とは

1. 背景

  • 歴史
  • 用途
  • 支える環境
    • 軽快なテスト
    • 簡潔なテストコードでテストが実装・実行できる
    • 明快にテスト結果をみれる
    • 製品コードと開発環境の切り替えが容易

2. 概要

3. 目的

4. 目標(ゴール)

  • ゴール
    • 動作するきれいなコード
  • 「動作する」「きれいなコード」
    • 道は2つある
      • 「きれい」にしてから「動作する」
        • 伝統的なソフトウェア開発手法なこれを採用してきた
        • きれいな設計を作ってから実装する
        • 作り始めてから設計が複雑であるなど気づいたりする
        • 作ってみたら遅くて使い物にならないなどあったりする
      • 「動作する」にしてから「きれい」にする
        • TDDが採用している手法
        • 現在は予測可能性が低いので、「きれいな設計」を作ってからだと「動かしてみてからわからないこと」についていけない
          • できるできないではなく、正しく見ていかないといけない

5. テスト駆動開発は「恐れ」を突破するための技術

  • テストの罠
    • 堕落
      • 動いたからよしとしたい感情がある
    • 焦り
      • 他にも作らなきゃいけないものがあるから後回しになる
    • 恐れ
      • せっかく動くところまで来たのに、綺麗にするためにコードを書いたら動かなくならなくなるので、コードに手を入れたくない
      • 「動くコードに触れるな」みたいな言葉はここに由来する
  • テスト駆動開発は「テストが安心を担保してくれる」

6. 注意点

  • TDDのテストは品質を担保するわけではない
    • 結果的に品質は高まるが、目的ではない
  • テストのポイント
    • 特定の条件下で検証する
    • 本来はどうあるべきか?
    • 実際にどうなっているか?
  • リファクタリング
    • privateテストのテストは行わない

7. メリット・デメリット

メリット

デメリット

  • まだ不明

8. 構成

9. TDDでのテストの分類

  • 開発促進・設計行為
    • Developer Test
    • 目的
      • 開発者が開発者のために行うテスト
  • 進捗管理・機能要件
    • Customer Test
    • 目的
      • お客様から見て機能がどれくらいできあがっているか示す
  • 品質保証・非機能要件
    • QA Test
    • 目的
      • ソフトウェアの信頼性をあげるテスト

10. サイクル

  • 用語の説明

    • RED
      • どう動いて欲しいかという(失敗する)テストを書く
    • GREEN
      • テストを成功させる最低限のコードを書く
    • REFACTOR
  • 実際のサイクル

    • 次の目標を考える
    • その目標のテストを書く
    • そのテストを実行して失敗させる(Red)
    • 目的のコードを書く
    • テストを成功させる(Green)
    • テストが通るまでリファクタリングを行う(Refactor)
    • 繰り返す
  • アンチパターン(起こりがちな失敗)
    • リファクタリングのサイクルがなくなりがち
    • 理由
      • リファクタリングと名のついたタスクには呪いがかかる
        • 直近ではないように見える
          • ビジネス上の価値が感じられない
            • 技術者のエゴに見える
            • 技術者が非技術者に説明するのは難しい

11. リファクタリング

  • リファクタリングをいつまでやるか
    • 重複がなくなるまで
    • 不整合が解消されたという感情が得られた時
    • タイムボックス化

12. 実際のやり方

  • まずは実装を箇条書き
  • 優先順位を決める
  • 次にテストを書こうとする
    • テストの区切りを考えるのは難しい
      • どこまでやるのか?プリントできるかなど考えると難しい
    • 「明白な実装」といって、わざわざ細かく書く必要はない。
      • 実装が見えているなら書いてしまう
  • テストコードから書く
    • テストを書くには具体的に考えないといけない
      • テストを書くと掘り下げの甘さに気づける
    • 初めは重複しても良い
      • 重複はリファクタリングで消す
        • 重複数がいくつでアウトかは開発のルールに従う
          • だいたい2か3
  • テストは予想通り落ちるのが大事
    • そのためにはモックオブジェクト技法を使っても良い
      • モックオブジェクト技法
        • 偽物のオブジェクトをテストに使う技法
        • モックオブジェクト
          • テスト対象と協調動作するオブジェクトの偽物を使う
        • 利点
          • 開発前にテストができる
          • テストのテストができる

13. アンチパターン

  • テスト熱中症
    • 手段の目的化
    • GUI」「データベース」のテストが難しい
  • データベースのテスト
    • テストを動かす度にデータベースの中身が変わってしまって、Greenなのにテスト実行中はRedになったりする
      • テスト同士が暗黙の依存関係を持ってしまうことがある
    • ツール探しが大事
  • GUIのテスト
    • 問題点
      • 画面仕様は変わりやすいので資産価値が低い
      • 複雑になりやすくテストしにくい
      • ユーザビリティや画面のデザインはテストできないことも多々ある

14. How to

  • テストを書き始められなかったら
    • ゴール(検証)から書く
      • テストコードに慣れないうちや、何をテストしていいかわからなければ
  • テストメソッド
    • テストメソッドの中身の構成
      • 前準備
      • 実行
      • 検証
      • 後片付け(状況により必要)
  • テストコードの書き方
    • 個別の具体的なコードしか書かないと「仕様が読み取れない」
      • 名前から仕様が読み取れるようにしないといけない
        • 案1:テスト名は長くてもいいから仕様を示す長い名前にする
        • 案2:テストコードを入れ子にする
  • テストコードにバグがあったら
    • 実装側にやらせる
    • ミューテーションテスティング
    • テストコードのテストを一番最初にしとめる
      • 必ず成功する状態で実行する
  • 最小テストの方向性を正したい
    • 三角測量テスト
      • 最小テストを書いた後で、テストの方向性を正すために他のテストを書くこと
      • テスト毎にテストは1つ
      • 三角測量テストは「対称性の破れ」があるので後で消すようにする
    • 対称性の破れ
      • 三角測量のテストは消す
        • テストのテストは確認できた時点で消せるので
  • テストは消すのが難しい。消せるタイミングがあれば消す
  • 公布済みインタフェースのリファクタリング
    • 公布済み
      • publishedインタフェース
      • 他のチームや他の組織に使ってもらうためのインタフェース
    • 手順
      • 変更のアナウンス
      • 旧インタフェースが呼ばれたら新インタフェースにそのまま呼び出しを行うようにする
    • TDD
      • 大きいプロジェクトになると適応しにくくなる
        • 「先にクライアントとサーバの取り決めから始める」となるとpublishedインタフェースの中だけでしかTDDで自由に開発できなくなる

参考

Sendagaya.rb #286に参加してきました

そろそろ地域.rb(地域毎のRubyコミュニティ)に参加したいと思っていたところ、フィヨルドミートアップで@machidaさんがSendagaya.rbをおすすめしていたので参加してきました

Sendagaya.rb #286の概要について

Sendagaya.rbでやっていること

※私が参加したのは今回初ですが、毎回やっている流れはほぼ同じそうです。今後参加される方にはご参考になるかと思います。

  • 初めに自己紹介をする
    • 名前・ニックネーム
    • 今やっていること
    • 今気になっていること
    • 今日話をしたいこと
  • その中からテーマを決め、30分程度毎に話し合ったり問題解決していく

Sendagaya.rb #286

今回は私を含め初参加の方が何名か居たので、初参加メンバーのテーマを優先させていただけました。

話をした内容は以下の通りです。個別のテーマについて、話したことを簡単に書いてみました。

  • フォロー・フォロワー機能について相談
  • viewに表示するmodelの件数についてどこに書くと嬉しいか
  • APIを使ったシステムの設計方法について相談・検討
  • プロジェクトの提案方法について

フォロー・フォロワー機能について相談

viewに表示するmodelの件数についてどこに書くと嬉しいか

  • viewに関することはcontrollerに定数として書くことが多い
  • showメソッドでしか使わない定数の場合は、showメソッドの真上に書いたりすることもある(とはいえ定数は一番上にまとめて書くことが多い)
  • ただ数字を代入するような、マジックナンバーは作らないようにする
  • 情報をまとめる時は、まとめた結果何が嬉しいのか(どんな価値があるのか)考えるのが大事

APIを使ったシステムの設計方法について相談・検討

  • 全体の構成について
  • エラーハンドリングについて
  • DBの設計について
    • メモ帳やエディターで書いてみて、シュミレーションしてみる
    • 頭だけで考えると抜け漏れが出てしまうかも
  • 外部APIへの接続IPアドレスを固定したい場合、プロキシサーバーを使うとIPアドレスが固定できる

プロジェクトの提案方法について

  • MVPを作る
    • 顧客への提案方法について、まずは小さいところから作って見せて確認を取っていく
    • プロジェクトの進行は1週間毎に成果を見せれるように作ることで、顧客と随時方針を修正しながら作り込める(顧客の仕様がまとまりきってないこともありうるので)
    • リーンスタートアップが参考になる
  • 良い機能はあるけど、必要だけど動かない機能があるというのは困る
  • 一番複雑な例を聞くことが大事。最初に一番複雑な例を知っておきたい
  • 何より動くことが大事。動くということはある程度設計が保証されているという証でもある

あとがき

分からないことや悩んでることについて一緒に考えてくれるコミュニティなので、とってもありがたかったです。

定期的に参加したいです😊

Ruby on RailsでSpringの全プロセスを一行で終了するコマンド

使用上の注意事項

springというキーワードのプロセスがkillされてしまうので、その点ご理解の上、ご使用ください。
使用する際に終了するプロセスの確認方法やコマンドの動作については、下記「仕組みについて」をご参照ください

環境

Mac

結論

下記コードが成果物になります。
下記説明を理解の上ご使用ください。
特に kill コマンドは強力なコマンドなので、コマンドのアクションを理解しておく必要があります。

$ pgrep -f spring | xargs kill -9

仕組みについて

まずはターミナルを起動しましょう

次に、下記コマンドを入力しましょう。

$ pgrep -lf spring

入力するとプロセス名が表示されます。 ※画像は$ ps -ef | grep springの実行結果だが、似たような情報が表示される

f:id:s4na:20190706161740p:plain

次に下記コードを入力しましょう

$ pgrep -f spring

入力すると、プロセスIDだけが表示されることが確認できます ※スペルミスなどで誤ったプロセスを終了しないように確認できる。安心

f:id:s4na:20190706161843p:plain

そして上記コードで表示できた数値を元にプロセスを強制終了させるのが下記コードです

$ pgrep -f spring | xargs kill -9

参考

uxmilk.jp

qiita.com

おまけ

こういう一行で実行してくれるコマンドをのことをワンライナーと言います。

他にもワンライナーが知りたい場合は、下記リンクに乗っているので、ご参考にされても良いかもしれません

employment.en-japan.com

さくらVPSのDebian 9にPostgreSQLを導入するまでの手順書を作ってみた【環境セットアップ手順書その2】

前回に書いた手順書に引き続きまして、PostgreSQLを導入する手順書を作成しました!

手順書

さくらVPS設定その2:PostgreSQLの導入.md · GitHub

あとがき

松本亮介さんの記事を見て、アウトプットは質より量だ!ということで、とりあえず公開してみました!
公開しないまま時間が過ぎていくなら、公開してから編集です!

まずは公開!公開!公開というスタンスでやっていこうかなと思ってます。

logmi.jp

さくらVPSのVNCコンソールで、記号が入力できなくなってしまったら

背景

さくらVPSで作業していて、「キーボードから記号の入力ができない!」
そういった声をよく聞くので、対処法をまとめました。

概要

VNCコンソールやめて、シリアルコンソールを使おう

手順

まずはさくらVPSの「コントロールパネル」にログインして、「サーバー」を選択

f:id:s4na:20190623144500p:plain

次に「コンソール」から「シリアルコンソール」を選択

f:id:s4na:20190623145132p:plain

こんな画面が表示されます

f:id:s4na:20190623145431p:plain

少し待つと下記のように「ユーザIDを用いたログイン要求(login:)」が表示されるので、ここから作業ができるようになります!

f:id:s4na:20190623145555p:plain

あとがき

リモートの作業となると、SSHが基本になってくると思うのですが、それまでの繋ぎとして「Webサービス備え付けのコンソール」を使うことって多々あると思います。
私はパソコンのキーボードが日本語配列だったこともあり、だいぶ苦しめられたので、後進の一助になればと思います。

今後も情報共有は積極的にやっていきたい(やれたらいいな)と思っているので、今後も当ブログをよろしくお願いいたします。

以上、簡単な説明でした。
誤字脱字や意見などがございましたら、お気軽にコメント頂きたいです!(待ってます😆)

さくらVPSにDebian 9をインストールしてからsudo、root、SSHを設定するまでの手順書を作ってみた【環境セットアップ手順書その1】

はじめに

Linuxに限らず、CLIで操作する時一番怖いのは、「適応されていない設定に気づけないこと」だと思います。

なので私は「コマンド実行後のチェック作業付き」で手順書を作成しました。

この手順書を使うことで、みなさんの「コマンドを実行したが、上手くいってるかわからない」「手順書通りに実行したが、どこで手順を間違えたかわからず環境セットアップがいつまでも終了しない」といった問題を解決できればなと思います。

手順書

成果物はGistで公開しています!いつでも更新できて、履歴も残るので良いですよね!

さくらVPS設定手順書その1:Debian 9をインストールしてからsudo、root、SSHを設定するまで.md

目的別、次に学んだら面白そうなこと

  • データベースの勉強がしたい
  • 静的Webサイトを公開したい
    • Nginx
  • Webサイトを作りたい
    • HTML/CSS -> Nginx

あとがき

前回のブログ投稿から少し日付が空いてしまいました。 記事の内容や手順書はほとんど完成していたのですが、どこまで品質をあげたら出そうか考えたら、まだまだだな・・・と悩んで出すのが遅れました。 結果として品質はあまり向上しませんでしたが、それも1つの結果として受け止めて今後に繋げていきたいなと思っています。

逆引きターミナル(Terminal)コマンド一覧 ※Macユーザー向け

まえがき

今回はWebデザイナーの為の「本当は怖くない」“黒い画面”入門シリーズ一覧 - FJORDで勉強した内容をまとめます。

はじめに

パソコンの黒い画面といえば、コマンドを入力することでパソコンが処理をしてくれる様子」を思い浮かべると思います。
ですが、「実際何を行なっているか理解できているか?」という話になってくると、 「よくわからない」となってしまう人が大半なのではないでしょうか?

結論から言ってしまえば、できることはコマンドによる操作になります。
操作できる対象は、コマンドによってまちまちです。

今回はその中でも入門者向けに知っておくと便利なコマンドを一覧化して紹介したいと思います。

対象読者

  • Macでターミナルを触ってみたいけど、踏み出せない人
  • Macのターミナルで何ができるか知りたい人

記載しない内容

本文

できること一覧(目的別、コマンド逆引き辞典)

目的 コマンド URL
フォルダやファイルを表示したい ls link
パスを知りたい pwd link
文字を表示する echo link
マニュアルを表示する man link
ディレクトリの移動 cd link
ネットからファイルをダウンロード cURL link
ファイルを開く open link
かな文字の読み上げを行う SayKana link
ファイルの作成 touch link
ディレクトリの作成 mkdir link
ファイルのコピ cp link
ファイルの移動 mv link
ファイルの削除 rm link
ファイル内容の表示 cat link
ファイル・フォルダの権限変更 chmod link

備考

homebrewのインストール

目的別、次に学んだら面白いこと

- 「もっとコマンドについて理解を深めたい!」
  - シェルスクリプトについて学習
- 「プログラムに興味ができた!プログラムついて勉強したい!」
  - C言語、Java、Pythonなどの言語を興味のある分野に合わせて・・・
- 「ターミナルがどういう原理で動いてるのか知りたい!」
  - "bash"で検索

あとがき

ブログはまだ書き始めたこともあり、正直慣れないな〜というのが本音です。
はじめ想定していたより、書いてみたら内容としては薄くなってしまいますし、文章の体裁も少し硬いかなと(これはもともと私自身が人から言わがちな印象でもあるんですけど)

書き続けることが大事だと思うので、まずは描き続けて、一定期間(1ヶ月とか)続けられたら大きく改善していきたいと思います。

最後まで読んでいただきありがとうございます。

ターミナル、コマンドプロンプト、コマンドライン、CUI...など「単語の意味と違い」が曖昧になることがあったので、まとめてみた

はじめに

Macだとターミナルだけど、Windowsだとコマンドプロンプトって書いてある。違いはなんだろう?」

MacWindowsを両方使ったことがある人は、疑問に思って調べたことがあると思います。
私も疑問に思って調べたことが何度かあります。
都度調べると時間もかかるし、情報が分散されていてまとめるのが大変なので、
今回は内容をまとめて1つの記事にしました。

単語の意味

- ターミナル
  - 意味: MacのCUIシェル
    - 広義: 電車などの終点。空港。
- シェル
  - 意味: コマンドラインを入力するためにユーザが使用するプログラム
- コマンド
  - 意味: コンピュータに特定の機能の実行を指示する命令
- コマンドプロンプト
  - 意味: コマンド入力待ち状態であることを表す記号
  - 由来: コマンド + プロンプト(入力を促すもの)
- コマンドライン
  - 意味: コンピュータへの命令をキーボードだけで行う時に使う、入力先のアプリケーション
- CUI(コマンドライン ユーザー インターフェイス)
  - 意味: UIの一種で、UIがコマンドラインベースであること

参考

- ターミナル - Wikipedia
  - https://ja.wikipedia.org/wiki/%E3%82%BF%E3%83%BC%E3%83%9F%E3%83%8A%E3%83%AB
- コマンド - コトバンク
  - https://kotobank.jp/word/%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89-3447
- コマンドプロンプト - Wikipedia
  - https://ja.wikipedia.org/wiki/%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%83%97%E3%83%AD%E3%83%B3%E3%83%97%E3%83%88
- コマンドライン - 「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典
  - https://wa3.i-3-i.info/word11158.html
- キャラクタユーザインタフェース - Wikipedia
  - https://ja.wikipedia.org/wiki/%E3%82%AD%E3%83%A3%E3%83%A9%E3%82%AF%E3%82%BF%E3%83%A6%E3%83%BC%E3%82%B6%E3%82%A4%E3%83%B3%E3%82%BF%E3%83%95%E3%82%A7%E3%83%BC%E3%82%B9
- Unix シェル - Wikipedia
  - https://ja.wikipedia.org/wiki/Unix%E3%82%B7%E3%82%A7%E3%83%AB