koheitakahashiのブログ

2020.07.01にプログラマーとして生を受けた私が学んだことや、日常について徒然に書いていきます。

ActionCableとVue.jsを使ってリアルタイム分報機能を実装しました(リリースはまだです)

はじめに

私が2020年6月まで参加させていただいたフィヨルドブートキャンプでは、スクラム開発を学ぶプラクティスとして、実際にスクラム開発の手法に則ってアプリを開発するというプラクティスがあります。

フィヨルドブートキャンプ受講生が日頃学習に使っているeラーニングアプリを実際にスクラム開発の手法に則って開発していきます。

スクラム開発のプラクティスについて詳しく知りたい方は、以前書いた拙文がありますので、そちらをご覧ください。

docs.koheitakahashi.com

そのプラクティスの中で、私が最後に任されたissueが以下の分報機能の実装でした。

github.com

そして、以下が、私が作成したPRです。

github.com

PRの内容としては、ActionCableとVue.jsを使用して、上記のリアルタイムでCRUDができる分報機能を実装したというものになります。

大きな機能のため、私は最小限の機能の実装し、大枠を作ってその他必要な機能の追加は他の受講生の方がやってくださっているという状態です(本当は私が必要な機能を全て実装できれば良かったのですが…)。

正直なところ、このissueに着手し始めた当初は、「Vue.jsナニモワカラン」「ActionCableナニソレワカラン」という感じで四苦八苦していました。

日本語・英語で検索しても、ActionCableの具体的な実践例が載っている記事をあまり見ませんでした。

そのため、この記事では私が書いたAction CableとVue.jsのコードを解説します。
設計が悪い・書き方が悪いというところは多々あると思いますが、あくまで一例として捉えていただき、ActionCableの実践例として少しでも参考になれば幸いです。

また、本記事で以下に解説するコードの例は私がPRを出してレビューが通った時点のコードになります。
そのため、上記ブランチの最新版とはコードが異なることをご了承ください。

この記事について

対象としている読者

この記事は以下のような方々を対象として書きました。

  • ActionCableの概要は掴めたけど、具体的に、どのように書いていけば良いのか分からない。
  • ActionCableを使用したコードの例を知りたい。

本記事が上記のような方々にとって、少しでも参考になれば幸いです。

実装した機能について

前提

この分報機能はFJORD BOOT CAMPのeラーニングアプリの中で使われる機能の一部として実装しました。 このeラーニングアプリは以下のような機能を備えております。

  • アプリ内にメンターの方々が作成したプラクティス(カリキュラム)があり、それを受講生が見て、提出物を提出できる。
  • 受講生が毎日の学習の記録を綴った日報を作成できる。
  • 提出物や日報に対してメンターの方や受講生の方がコメントをつけられる…など。

そのため、今回の分報機能は受講生がリアルタイムに学習の様子を呟いたりできるという、ブートキャンプ内のTwitterのような用途で使われることを想定して実装しました。

概要

ユーザーが分報の投稿・更新・編集・削除をリアルタイムでできる機能を実装しました。

別々のユーザーがログインしている時、あるユーザーが分報を投稿・編集・削除すると、リアルタイム(ページのリロードせず)にそれが反映されているのが確認できます。 Image from Gyazo

フレームワークなどのバージョン

  • Ruby 2.6.5
  • Ruby on Rails 6.0.3.1
  • Node.js 12.2.0
  • Vue.js 2.6.10

書いたコードの解説

全体的な処理の流れ

ユーザーが分報ページを訪れて、過去の分報一覧が表示されるまでの流れは以下のようになります。

  1. ユーザーが分報ページを開いた時に、Vueコンポーネントがcreateされます。
  2. Vueコンポーネントがcreateされたタイミングで、websocketコネクションを確立しようとします。
  3. ユーザーがログインユーザーであれば、websocketコネクションを確立し、subscribeを開始します。
  4. subscribeされたら、Rails側からVue.js側に過去の分報一覧がbroadcastされます。
  5. Vue.js側はbroadcastされた分報一覧を受け取り、それを描画します。

上記のような流れにより、ユーザーは分報ページを訪れた時に過去の分報一覧が見れます。

Image from Gyazo

また、分報をCRUDするときの処理の流れは以下のようになります。

  1. Vueコンポーネントがユーザーの操作を受け取ります。
  2. Vueコンポーネントは、その操作に応じて、app/channels/timelines_channel.rbで定義されているCRUDメソッドを呼び出して実行します(CRUDメソッド実行後は、そのデータをbroadcastするという処理を行います)。
  3. broadcastされたデータをVue.js側が受け取って、後述するデータと一緒にbroadcastされるeventという文字列を見て、そのイベントに対応した処理を行います。

Image from Gyazo

コードを解説

上記で全体的な構成と処理の流れを説明しました。
これを踏まえて、各ファイルに記述されたコードを見ていきたいと思います。

Rails側

app/channels/application_cable/connection.rb

websocketコネクションを管理します。
今回は、以下のように記述しました。

# app/channels/application_cable/connection.rb

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    # このように書くことで、確立されるwebsocketコネクションの識別子がcurrent_userになります。
    # さらに、後述するchannelの中でcurrent_userオブジェクトを使えるようになります。
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    private

      # websocketコネクションの確立を求めてきているuser_idがログインしているユーザーIDと一致する場合は、コネクションを確立。そうでなければ、コネクションを拒否します。
      def find_verified_user
        if verified_user = User.find_by(id: session_data["user_id"])
          verified_user
        else
          reject_unauthorized_connection
        end
      end

      def session_data
        cookies.encrypted[Rails.application.config.session_options[:key]]
      end
  end
end

app/channels/timelines_channel.rb

ActionCableを使用したwebsocket通信における、controllerの役割を担うのがこのchannelになります。
そのため以下のようなことを記述しています。

  • クライアントがsubscribeしたときの処理。
  • 分報のCRUDと、CRUD後にデータをbroadcastする。

加えて、ここに定義されたメソッドは後述するVueコンポーネント側で呼び出すことができます。

# app/channels/timelines_channel.rb
class TimelinesChannel < ApplicationCable::Channel
  before_subscribe :set_host_for_disk_storage

  # ここでchannelがsubscribeされたときの処理を記述することができます。
  # [以下、書いたコードに込めた自分の意図]
  # ここでは、timelines_channleがsubsrcibeされた時に、過去の分報データをbroadcastするように処理を書いています。
  # broadcastメソッドではなく、transmitメソッドを使っているのは、subscribedの中でbroadcastメソッドを使用すると、処理の実行タイミングの関係で、subscriberが特定されない内にbroadcastすることになってしまい、エラーが発生しました。
  # そのため、subscriberが特定されていない状態でもbroadcastが実行できるtransmitメソッドを使用しています。
  # また、websocket通信はHPPTのようにステータスコードのように一般化されているものがないはずです。そのため、`event`という文字列を送ることで、broadcastに成功したとき、失敗したときがVue.js側で明確に分かるようになります。
  def subscribed
    stream_from "timelines_channel"
    if !subscription_rejected?
      transmit({ event: "subscribe", current_user: decorated(current_user).format_to_channel, timelines: formatted_timelines })
    else
      transmit({ event: "failed_to_subscribe" })
    end
  end

  def create_timeline(data)
    # [以下、書いたコードに込めた自分の意図]
    # 分報をcreateする処理と、createした分報をbroadcastする処理を記述しています。
    set_host_for_disk_storage
    @timeline = Timeline.new(permitted(data))
    @timeline.user_id = current_user.id
    if @timeline.save
      broadcast_to_timelines_channel("create_timeline", @timeline)
    else
      broadcast_to_timelines_channel("failed_to_create_timeline", nil)
    end
  end

  def update_timeline(data)
    set_host_for_disk_storage
    @timeline = Timeline.find_by(id: data["id"])
    if @timeline.update(permitted(data))
      broadcast_to_timelines_channel("update_timeline", @timeline)
    else
      broadcast_to_timelines_channel("failed_to_update_timeline", nil)
    end
  end

  def delete_timeline(data)
    set_host_for_disk_storage
    @timeline = Timeline.find_by(id: data["id"])
    if @timeline.destroy
      broadcast_to_timelines_channel("delete_timeline", @timeline)
    else
      broadcast_to_timelines_channel("failed_to_delete_timeline", nil)
    end
  end

  private

    def permitted(data)
      params = ActionController::Parameters.new(data)
      params.require(:timeline).permit(:description)
    end

    def broadcast_to_timelines_channel(event, timeline)
      ActionCable.server.broadcast "timelines_channel", { event: event, timeline: decorated(timeline).format_to_channel }
    end

    def decorated(obj)
      ActiveDecorator::Decorator.instance.decorate(obj)
    end

    def formatted_timelines
      Timeline.order(created_at: :asc).map { |timeline| decorated(timeline).format_to_channel }
    end

    # local・test環境でActiveStorage::Current.hostが定義されずユーザーアイコンのservice_urlを取得することができなかったため、以下のように定義しています。
    def set_host_for_disk_storage
      if %i(local test).include? Rails.application.config.active_storage.service
        ActiveStorage::Current.host = Rails.application.config.action_cable.url.gsub(/cable/, "")
      end
    end
end

Vue.js側

Vue.js側はchannelへのsubscribeや、channel内のデータを取得して処理するということを管理するための、timelines-channel.vueというコンポーネントを作成ました。
加えて、その子コンポーネントで、1つ1つの分報に対する処理を管理するtimeline.vueというコンポーネントを作成しました。

また、エントリーポイントとなるtimeline.jsを作成しました。


app/javascript/channels/timelines.js

このtimeline.jsでは以下のようなことを行っています。

  • js-timelinesとうい要素が存在しなければconsumer(subscribeする前のクライアントのこと)を定義します。
  • timelines-channel.vuejs-timelinesという要素にマウントします。
// app/javascript/channels/timelines.js
// JavaScriptがActionCableと協調できるようにするライブラリです。
import ActionCable from 'actioncable'
import Vue from 'vue'
import Timelines from './timelines-channel.vue'

document.addEventListener('DOMContentLoaded', () => {
  const timelines = document.getElementById('js-timelines')
  if (timelines) {
    const cable = ActionCable.createConsumer()
    Vue.prototype.$cable = cable

    new Vue({
      render: h => h(Timelines)
    }).$mount('#js-timelines')
  }
})

app/javascript/channels/timelines-channel.vue

timelines-channel.vueでは以下のようなことを行っています。

  • このcreatedのタイミングで、TimelinesChannelにsubscribeします。
  • TimelinesChannelにデータがbroadcastされたのを検知して、データと一緒にbroadcastされたeventという文字列を見て、それに応じた処理を行います。
  • app/channels/timelines_channel.rbで定義されたCRUDメソッドをイベントに応じて実行します。
// app/javascript/channels/timelines-channel.vue
<template lang="pug">
  .thread-timeline-container
    .thread-timeline-form.a-card
      .thread-timeline__author
        img.thread-timeline__author-icon.a-user-icon(:src="currentUser.avatar_url" :title="currentUser.icon_title")
      .thread-timeline-form__form.a-card
        .thread-timeline-form__markdown-parent.js-markdown-parent
          markdown-textarea(v-model="description" id="js-new-timeline" class="a-text-input js-warning-form thread-timeline-form__textarea js-markdown" name="new_timeline[description]")
        .thread-timeline-form__actions
          .thread-timeline-form__action
            button(v-on:click="createTimeline" :disabled="!validation || buttonDisabled")
              | 投稿する
    .thread-timelines
      timeline(v-for="(timeline, index) in timelines"
        :key="timeline.id"
        :timeline="timeline"
        :currentUser="currentUser"
        @update="updateTimeline"
        @delete="deleteTimeline")
</template>
<script>
  import Timeline from './timeline.vue'
  import MarkdownTextarea from '../markdown-textarea'

  export default {
    components: {
      'timeline': Timeline,
      'markdown-textarea': MarkdownTextarea
    },
    data: () => {
      return {
        currentUser: {},
        description: '',
        timelines: [],
        timelinesChannel: null,
        buttonDisabled: false
      }
    },
    created () {
      this.timelinesChannel = this.$cable.subscriptions.create("TimelinesChannel", {
        connected: () => {
          console.log('connected successfully');
        },

        // 以下がTimelinesChannelにbroadcastされたデータを受け取って処理をする部分になります。
        received: (data) => {
          switch (data.event) {
            case 'subscribe':
              this.currentUser = data.current_user
              this.timelines = []
              data.timelines.forEach((timeline) => {
                this.timelines.unshift(timeline)
              });
              break
            case 'failed_to_subscribe':
              console.warn('Failed to subscribe');
              break
            case 'create_timeline':
              this.timelines.unshift(data.timeline);
              this.description = '';
              break
            case 'failed_to_create_timeline':
              console.warn('Failed to create timeline');
              break
            case 'update_timeline':
              this.timelines.forEach((timeline) => {
                if (timeline.id === data.timeline.id) {
                  timeline.description = data.timeline.description
                }
              });
              break
            case 'failed_to_update_timeline':
              console.warn('Failed to update timeline');
              break
            case 'delete_timeline':
              this.timelines.forEach((timeline, i) => {
                if (timeline.id === data.timeline.id) {
                  this.timelines.splice(i, 1)
                }
              });
              break
            case 'failed_to_delete_timeline':
              console.warn('Failed to delete timeline');
              break
          }
        }
      })
    },
    methods: {
      createTimeline: function () {
        if (this.description.length < 1) { return null }
        this.buttonDisabled = true;
        let params = {
          'timeline': { 'description': this.description },
        }
        this.timelinesChannel.perform('create_timeline', params);
        this.buttonDisabled = false;
      },
      updateTimeline: function (data) {
        this.timelinesChannel.perform('update_timeline', data);
      },
      deleteTimeline: function (data) {
        this.timelinesChannel.perform('delete_timeline', data);
      }
    },
    computed: {
      validation: function () {
        return this.description.length > 0
      }
    }
  }
</script>

app/javascript/channels/timeline.vue

最後にtimelines-channel.vueの子コンポーネントである、timeline.vueについて説明します。
ここでは以下のようなことを行っています。

  • 親コンポーネントからtimelineのデータを受け取って、それを描画します。
  • timelineの編集・削除します。
// app/javascript/channels/timeline.vue
<template lang="pug">
  .thread-timeline.a-card
    .thread-timeline__author
      a.thread-timeline__author-link(:href="timeline.user.path" itempro="url")
        img.thread-timeline__author-icon.a-user-icon(:src="timeline.user.avatar_url" :title="timeline.user.icon_title"  v-bind:class="userRole")
    .thread-timeline__body.a-card(v-if="!editing")
      header.thread-timeline__body-header
        a.thread-timeline__title-link(:href="timeline.user.path" itempro="url")
          | {{ timeline.user.login_name }}
        time.thread-timeline__created-at(:datetime="createdAt" pubdate="pubdate")
          | {{ createdAt }}
        .card-header-actions(v-if="this.timeline.user.id === this.currentUser.id")
          ul.card-header-actions__items
            li.card-header-actions__item
              button.card-header-actions__action.a-button(@click="editing = !editing")
                i.fas.fa-pen
                  | 編集
            li.card-header-actions__item
              button.card-header-actions__action.a-button(@click="deleteTimeline")
                i.fas.fa-trash-alt
                  | 削除
      .thread-timeline__description.js-target-blank.is-long-text(v-if="!editing" v-html="markdownDescription")
    .thread-timeline-form__form.a-card(v-show="editing")
      markdown-textarea(v-model="description" :class="classTimelineId" class="a-text-input js-warning-form thread-timeline-form__textarea js-timeline-markdown" name="timeline[description]")
      button(v-on:click="updateTimeline" v-bind:disabled="!validation")
        | 保存する
      button(v-on:click="cancel")
        | キャンセル
</template>
<script>
  import MarkdownTextarea from '../markdown-textarea'
  import MarkdownIt from 'markdown-it'
  import MarkdownItEmoji from 'markdown-it-emoji'
  import moment from 'moment'

  export default {
    props: ['timeline', 'currentUser'],
    components: {
      'markdown-textarea': MarkdownTextarea
    },
    data: () => {
      return {
        description: '',
        editing: false,
      }
    },
    created: function() {
      this.description = this.timeline.description;
    },
    mounted: function() {
      $('textarea').textareaAutoSize();
    },
    methods: {
      editTimeline: function() {
        this.editing = true;
      },
      cancel: function() {
        this.description = this.timeline.description;
        this.editing = false;
      },
      updateTimeline: function () {
        if (this.description.length < 1) { return null}
        let params = {
          'id' : this.timeline.id,
          'timeline' : { 'description': this.description }
        }
        this.$emit('update', params);
        this.editing = false;
      },
      deleteTimeline: function () {
        let params = {
          'id' : this.timeline.id
        }
        if (window.confirm('削除してよろしいですか?')) {
          this.$emit('delete', params);
        }
      }
    },
    computed: {
      markdownDescription: function() {
        const md = new MarkdownIt({
          html: true,
          breaks: true,
          linkify: true,
          langPrefix: 'language-'
        });
        md.use(MarkdownItEmoji)
        return md.render(this.timeline.description);
      },
      classTimelineId: function() {
        return `timeline-id-${this.timeline.id}`
      },
      userRole: function(){
        return `is-${this.timeline.user.role}`
      },
      createdAt: function() {
        return moment(this.timeline.updated_at).format('YYYY年MM月DD日(dd) HH:mm')
      },
      validation: function () {
        return this.description.length > 0
      }
    }
  }
</script>

まとめ

上記に各ファイルの解説をさせていただきました。

改めてまとめるならば、この分報機能のポイントは以下だと思います。

  • ActionCableを使用したwebsocket通信におけるcontrollerの役割はchannelが担うものと考えて、app/channels/timelines_channel.rbにCRUDメソッドの定義をしています。
  • データをbroadcastする際にeventという文字列を渡して、Vue.js側でデータのbroadcastが成功したのか失敗したのか、CRUDのいずれかなどを明示することで処理を分けて記述しやすくしました。
  • ActionCableの特徴として、channelに定義されたメソッドをフロントエンド側で呼ぶことができます。

テストなどの解説は割愛させていただきましたが、そちらについてはコードの方を参照していただければと思います。

最後に

私自身、設計や書き方がこれで良いのかという思いは拭きれないため、これはあくまでActionCableとVue.jsを用いた機能実装の一例と捉えていただければと思います。

また、issueを担当することになった当初は、「Vue.jsの書き方が分からない…」「そもそもActionCableとは…」と分かないことだらけという状態でした。

しかし、1つ1つ頭を悩ませながら理解して、そしてできる方々に教えていただきながら少しずつ実装を進めていくことで、なんとかPRを出すところまで進めることができました。
教えてくださった方々ありがとうございました。

私の実装では必要な機能を全て備えることができなかったため、まだリリースには至っていませんが、フィヨルドブートキャンプで後からこの分報機能の機能拡張を担当する方の参考にもなればという意味も込めて本記事を書きました。

私は上記のようにAction Cableを理解して実装しましたが、理解や記述が誤っているところなどがあれば教えてくださると大変有り難いです。

参考にさせていただいた資料

FJORD BOOT CAMPを卒業できました!!

はじめに

2019年6月16日から入会させていただいた、FJORD BOOT CAMPですが、私の就職に伴い2020年6月30日付けで卒業できました。
都内の受託開発企業で2020年7月1日からプログラマーとして働いております。

FJORD BOOT CAMPには1年と少し勉強させていただいていたので、とても感慨深いです。

先日開催されたブートキャンプのオンラインでのミートアップで卒業式をしていただいた時に、ブートキャンプでの経験を振り返りたいと思ったため、今回この記事を書きました。
この記事は誰に向けたという訳ではなく、個人的な振り返りをまとめたというものになっておりますので悪しからず。

FJORD BOOT CAMPに入るまでの背景

自分は元々地方の国立大学・大学院で臨床心理学を専攻していました。
実習が非常に多い大学院で、実際に大学院の相談室に来室されたクライアントの方に、カウンセリングやソーシャルスキルトレーニング・遊戯療法・各種心理検査を行うなど実践の中で臨床心理学を学んでおりました。

しかし、修士2年目にして自分は対人援助職に向いていないと挫折し、大学院を中退しました。

「どんな仕事に就けば良いのだろう」と色々仕事を探していたときに、たまたまプログラマーという職業があるのを知りました。
そして、雰囲気的に良さそうで、他のプログラミングスクールに比べてリーズナブルなFJORD BOOT CAMPに入会したという運びです。

入会した時のレベル感としては、ProgateのHTMLとCSSの中級をやったくらいでして、Webプログラマーに必要な知識は全くといって良いほど備えておりませんでした。

FJORD BOOT CAMPを振り返って

初めの頃

slackとはなんぞや。ネット上での振る舞いがわからない…。nginxわからない…、とわからないことだらけでした。

しかし、学んでいた臨床心理学というもの自体がわからないことだらけだったため、わからないことに囲まれるのはそれなりに慣れておりなんとかやっていけていたという感じです。

そのようになんとかやっていく中で、徐々にnginxやRubyを触るようになりました。
そこで感動したことが、自分の行動(記述したコードや設定)に、すぐにフィードバック(意図した通りに動いた・意図しない動き・エラーなど)が返ってくることでした。

対人援助という職種は、自分のとった行動に対して、それがよかったのか・悪かったのかというフィードバックが得られにくいものだと思います。 そのため、プログラミングという簡単ではないけど明快な世界にとても感動して、徐々に惹かれていったという感じです。

(ここら辺の感動は以下の記事にまとめているのでよければご覧ください)

nmp300.hatenablog.com

「lsコマンドを作成する」という壁にぶち当たる

ブートキャンプでは、「Rubyでlsコマンドを作成する」という課題が、受講生の方々の中で難関プラクティスとして挙げられています。

私がこの課題に取り組んでいたときは、プラクティスの構成的に、この課題が自分でちゃんとしたプログラムを書く初めての機会でした。

(このプラクティスより前のプラクティスは、HTML・CSS、nginx、Gitなどが中心だったため)

そのため、「どのような処理の流れで組み上げていけば良いのだろうか…」と、途方に暮れてしまいました。
1行書いては「多分こうじゃないんだよなぁ」と消し、書いては消しとやっているうちに最初の1週間が過ぎました。

しかし、もがいている内に、「こうすると良いのかもしれない」というものが徐々にわかるようになってきました。
そのことに加えて諸先輩方にハマったところを教えていただいきました。

そして、1ヶ月という長い時間がかかりましたが、無事にコードを提出できました。

このプラクティスがあったことで、「全くわからないことでも時間をかけて、もがき苦しんでいればいずれわかる時がくるのかもしれない」という考え方を持てるようになりました。

このような考え方を持つことができたおかげで、以降のプラクティスについては時間こそかかったものの「辛かった、キツかった」という感じはなかったように思います。

スクラム開発を体験

ブートキャンプでは、プラクティスの終盤にスクラム開発の手法で、ブートキャンプのアプリを開発していくというプラクティスがあります。 詳しくは以下の記事をご覧ください。

nmp300.hatenablog.com

そのプラクティスを通して「スクラム開発の流れ」、「自分の携わった機能のデモの仕方」、「issueやPR上のやりとりの仕方」ということを体感できました。

みんなで1つのアプリを開発していくという感じが楽しかったのですが、自分のレベルの低さを痛感して、悔しかったという気持ちもあります。

どんな力が身についたのか

自分で調べて、考えて、問題を解決するという力の素地が身についたのではないかと思います。

カリキュラムで紹介されている参考文献・資料はほぼ全て誰でもアクセスできるオープンなもので、「この通りやれば課題をクリアできる」という手順が明記されているドキュメントが用意されているわけではありません。

そのため、自分で調べて問題を解決するという力の、素地が身についたのではないかと思います。

具体的に、どのような技術を学ぶことができるのかは、ブートキャンプのページに記載されているため、そちらを参考にしていただけると良いかと思います。

どうして卒業することができたのか

FJORD BOOT CAMPは卒業するのが難しいと、スクールのページに記載があります。
実際受講生の方々からもそのようなお話を聞きますし、入会しても卒業できずに途中で辞めていかれた方も多い印象です。

ましてや、自分はIT初心者で、学習速度や開発速度も決して速いわけではありませんでした。
それでも、なぜ卒業できたかを考えた時に、その要因は大きく分けて以下の3つにあると思います。

  • 「分らなくても時間をかけて、もがいていればいずれ分かるときがくる」という考え方を持てたこと。
  • 「決して頑張らない」ことを心がけたこと。
  • 人に恵まれたこと。

「わからなくても時間をかけて、もがいていればいずれわかるときがくるかもしれない」という考え方を持てたこと

上述しましたが、この考え方をプラクティスをを進める中で持てたことで、分らないことが分らない状態に陥った時にそこまで精神的に落ち込むことなくやれたと思います。

「決して頑張らない」こと

私は「頑張ること」 = 「無理をして120%」を出すことだと思っています。
私は「頑張ってしまう」と、どこかでしわ寄せがきて、最終的には著しくパフォーマンスを発揮できない状態になりやすかったです(これは大学院時代に考えたことでした)。

そのため、私は「頑張って勉強する」のではなく、「70%くらいで淡々と勉強し続ける」ということを意識していました。

具体的には、「今日は調子良くて12時間勉強したけど、翌日は疲れて3時間しか勉強できなかった。翌々日も3時間しか勉強できなかった」というよりも、「今日も翌日も翌々日も6時間勉強した」ということの方が、私にとっては精神的にも疲労が少なく合っているように思えました。

ただ、この考え方や取り組み方は実務や現場では合ってないかもしれないので、今後変えていく必要があるのかもしれません。

人に恵まれたこと

ブートキャンプには日報というシステムがあり、受講生の方々がその日学んだことや、感想などを書いて投稿します。
そのおかげで同じ時期に入会した方や、同じプラクティスを学んでいる方の存在を強く感じることができました。

それにより「なんとかやっていこう」という気持ちを保つことができたという面もあります。

また、ハマったところや悩んでいるところはkomagataさん・machidaさんを初めとしたメンターの方々に加えて、受講生の諸先輩方も教えて下さりました。

ブートキャンプの外でも、Rubyコミュニティで多くの人に技術的なことや、キャリア、就職活動についてなど色々なことを教えていただきました。
ありがたいことに現在働いている会社は、Rubyコミュニティに参加している中でmachidaさんに教えていただいた企業です(試用期間を終えることができたら入社エントリを書きたいと思っています)。

このように多くの方々に助けていただいて、今プログラマーとして生を受けることができたと思っています。

まとめ

FJORD BOOT CAMPで勉強する段階が終わり、プログラマーとして働いていくという新たな段階に入るという、節目ということで振り返りとしてこの記事を書きました。

この先、自分がどうなっていくのかは分らないですが、もし一端のプログラマーになっていくことができたなら、お世話になったコミュニティに少しずつ恩を返していければと思っております。

補足

  • 先日いただいた卒業記念Tシャツと、卒業証書です。すごく嬉しい…
    f:id:NMP300:20200801221412j:plain
    卒業Tシャツと証書

『プレゼンテーションZen』を読了しました

はじめに

今までプレゼンテーションについて学んだことがなく、我流で取り組んでおりました。

しかし、会社で定期的にLTをする機会をいただき、これを機に自分のプレゼンテーションを見直そうと思い本書を読みました。

本書を読んで学んだこと・感想をまとめたいと思います。

本書の章立て

本書は以下のような章立てになっておりました。

第1章 今日のプレゼンテーション

第2章 創造性と制約

第3章 アナログ式に計画を練ろう

第4章 ストーリーを作り上げる

第5章 シンプルであることの大切さ

第6章 プレゼンテーションのデザイン:原理とテクニック

第7章 サンプルスライド:画像とテキスト

第8章 完全にその場に集中すること

第9章 聴衆と心を通い合わせる

第10章 長い旅が始まる

章ごとの感想

第1章今日のプレゼンテーション

今日のプレゼンテーションの問題について言及されており、それを踏まえて本書の方向性が示されている章でした。 本書のアプローチはハイ・コンセプトの概念を背景にしているそうです。
「デザイン」・「物語」・「調和」・「共感」・「遊び心」・「生きがい」の6つの要素を盛り込んで、プレゼンテーションを作り上げていくというのが本書のアプローチのようです。

「既存のプレゼンテーションの手法を捨てて、新しい手法を受け入れていくこと」という旨の文言に納得して、自分の方法を変えて行こうとやる気が高まりました。

第2章創造性と制約

プレゼンテーションはそもそも創造的な作業であるということが述べられている章でした。

プレゼンテーションの準備で意識することは、「シンプル」・「明快」・「簡潔」という旨が記載されており、自分のLTやプレゼンは内容を盛り込みすぎだったと思いました。 限られた時間で伝えたいことを全て伝えるのは不可能だと割り切って、伝えることを取捨選択して本当に伝えたいことを伝えることを意識していかなければならないと考えました。

第3章アナログ式に計画を練ろう

プレゼンテーションのアイデア出しは、PCを離れて紙やペンなどでアナログな方法で取り組むべきということが述べられている章でした。

確かに手書きでアイデアを出していくことで頭が整理されたり、まとまりがよくなったりするということは経験したことがあるので、これはなるほどいった感じでした。

また、プレゼンテーションを作り上げていく中で「何が言いたいのか?」・「なぜそれが必要なのか?」の質問に答えられないコンテンツは全て切り捨てることとが肝要という旨が記載されていました。

やはり、シビアにシンプルさを突き詰めていく必要があると感じました。

第4章ストーリーを作り上げる

聴いている人の心に残るアイデアの法則。
実際に素晴らしかったプレゼンテーションについて、それがなぜ素晴らしかったのかを分析。
といった内容がまとめられている章でした。

「本物の言葉」でストーリーを語ることがプレゼンテーションにおいて大切であるということが述べられています。 経験として、自分の理解と実感(経験)がなければ、話す内容が薄っぺらくなってしまうということがありました。

「本物の言葉」を使う、もしくは「本物の言葉」で話せない内容は話さないということが必要だと考えました。

第5章シンプルであることの大切さ

簡素なことは、既存の要素を増幅する作用があるという内容でした。

日本の美意識である、簡素・自然・渋みがスライドを改善する上で目安になる概念だとも述べられており、純粋に興味深い内容でした。 自分の場合は、スライドのデザインも含めて、シンプルすぎるくらいで良いのかなと考えました。

第6章プレゼンテーションのデザイン:原理とテクニック

スライドのデザインについて述べられていた章です。
「デザインは足し算ではなく、引き算」という内容が印象的でした。

しかし、デザインについては生半な気持ちで取り組むと物凄い時間がかかったり、返って変になってしまったりすると思うのでとりあえず今はあまり意識しない方が良いのかもしれないと考えました。

第7章サンプルスライド:画像とテキスト

画像とテキストの使い方について、サンプルとなるスライドが紹介されていた章です。

私は「高橋メソッド」というものがあるとは知っていたのですが、本書で改めて紹介されていて、やはりプレゼンテーション手法として衝撃的なものだったのだと感じました。

第8章完全にその場に集中すること

プレゼンターは完全に自分のプレゼンに集中しなければならないということが述べられていた章です。

次章の内容にも関連しますが、プレゼンテーション中でやっていることを分解すると以下のようになると思います。

  • スライドをもとに話す
  • 聴いている人のリアクションを観察
  • そのリアクションをもとに話すことを修正

このように高度なことをしているため、確かに集中力を欠いてプレゼンを行うことはできないと思いました。 集中せずプレゼンに臨むことはないとは思いますが、気をつけたいと思いました。

第9章聴衆と心を通い合わせる

プレゼンテーションは聴衆とのコミュニケーションであり、それを円滑に進めるための方法がいくつか紹介されている章でした。

上述しましたが、プレゼンテーションは聴衆とのコミュニケーションであるということを学びました。
今まで自分は「適切な情報を伝えよう」「正しく伝えよう」と意識しており、聴衆とのコミュニケーションという側面があるということを意識できていませんでした。

「プレゼンテーションはコミュニケーション」この認識を持つことで、自分のプレゼンテーションの仕方が色々と変わりそうだと思いました。

第10章長い旅が始まる

プレゼンテーション力向上法が述べられています。

やはりプレゼンテーションはスキルなので、黙っていても向上するものではないと思いました。とにかく実践を重ねることが向上のために必要なのだと気が引き締まる思いでした。

全体を通しての感想

本書を読んで、プレゼンテーションの認識が変わりました。

  • プレゼンテーションは情報を伝える機会ではなく、コミュニケーションの機会であること。
  • 事実と情報を全てプレゼンテーションに盛り込めば良い訳ではなく、配布資料を活用すること。
  • 不要な情報を盛り込まないこと。

など、これまで自分の良いものとして目指していたプレゼンテーションの形が実は良いものではなかったと考えました。

よりシンプル、内容を削ぎ落とすことを意識したいです。
そして、今後は話したい内容を出していき、伝えたいメッセージを据えて、そのメッセージに必要のないことは全て削ぎ落とすという方法でプレゼンテーションを作っていきたいと思います。

本書を読んで、目指すプレゼンテーションの形がなんとなく分かってきたので、それを目指して地道に実践していきたいと思いました。

Railsアプリに、後からJavaScriptを導入する方法

背景

rails newする時に、「いらないファイル群を生成したくないな」と思い、rails new -JでRailsアプリを作成したのですが、CRUDを実装している中で削除機能が実装できないという問題に直面しました。

なぜdeleteができないかというと、以下のようにlink_toヘルパーで用意されている書き方にしたがって、リンクを踏んだ時にHTTPのDELTEリクエストが飛ばされるように記述したつもりでした。

<%= link_to 'Destroy', book, method: :delete, data: { confirm: 'Are you sure?' } %>

そもそもリンクを踏んだ時に飛ばされるリクエストはGETが基本であり、それ以外のリクエストを飛ばすためにオーバーライドをする必要があります。

しかし、この役割を担っているのがJavaScriptだと知らずにJavaScript関連ファイルの生成をスキップしてしまったため、このオーバーライドができず、結果として削除を行うことができなかったという経緯です。

そのため、今回はrails new -JでJavaScript関連ファイルの生成をスキップしてしまった際に、後からJavaScriptを導入する方法についてまとめます。

環境

  • Ruby 2.6.6
  • Ruby on Rails 6.0.3.2

rails new -JでJavaScript関連のファイルが生成されていないRailsプロジェクトが作成されていることが前提で説明を進めていきます

実際の手順

webpackerのインストール

まずはwebpackerをインストールする必要があります(webpackerについては後述)。

Gemfile

gem 'webpacker', '~> 4.0'
❯  bundle install

yarnのインストール

そして、Rails6では標準でyarnを用いてパッケージ管理をしているため、yarnをインストールしておく必要があります。 以下でyarnをインストールします(もうすでにyarnがインストールされている場合は飛ばして大丈夫です)。

❯  brew install yarn

JavaScript関連ファイルを生成

以下で、JavaScript関連ファイルをRailsプロジェクト内に生成します。

❯  bin/rails webpacker:install

上記により、以下のようなファイル群が生成されます。

.browserslistrc
app/javascript/packs/application.js
babel.config.js
bin/webpack
bin/webpack-dev-server
config/webpack/development.js
config/webpack/environment.js
config/webpack/production.js
config/webpack/test.js
config/webpacker.yml
package.json
postcss.config.js
yarn.lock

rails/ujsをインストール

上記の、HTTPメソッドをオーバーライドするというのはrails/ujsというyarnパッケージが担っています(デフォルトでRailsに同梱されています)。そのため、以下でインストールします。

yarn add @rails/ujs

ViewでJavaScriptが適用されるように

インストールが終わったら下記のように記述を追加します。

app/javascript/packs/application.js

require("@rails/ujs").start()

app/views/layouts/application.html.erb

 <%= javascript_pack_tag 'application' %>

上記により、HTTPのメソッドオーバーライドができて、無事に上記のlink_toのコードで、削除を行うことができました。

もう少し深堀り

そもそもwebpackerとは?

webpackerを説明するためには、webpackを説明する必要があります。

webpackとは、JavaScriptやCSS、画像などのフロントエンド関連のデータを管理してくれるツールです。

  • ファイルやライブラリ同士の依存性を自動的に解決できる。
  • モジュール単位で細かく分割することができるので、見通しの良い構造にすることができる。
  • コンパイル・圧縮などを行ってくれる。

などのことを行ってくれるようです。

このwebpackをラッパーしてRails用に作成されたwebpackerというライブラリがあり、Rails6では、このwebpackerが標準で搭載されています。

参考にさせていただいた資料

ストレングスファインダーを受けてみました

はじめに

少し前に、「高橋さん、ストレングスファインダー受けてみて下さいよ!!」とおすすめされました。

ストレングスファインダーの存在は知っていたのですが、これまで受けたことがなかったので、受けてみることにしました。

正直、受ける前までは、「自記式のテストで、自分の強みなんて分かるのかなぁ」と懐疑的だったのですが、結果を見て、自分の行動や考え方がどうしてそうなのか、色々腑に落ちた感じがあり、とても良い経験だったので、ブログに残しておこうと思いました。

自分の結果

自分の結果は以下のようになりました。

  1. 収集心
  2. 回復志向
  3. 個別化
  4. 共感性
  5. ポジティブ

上から順番に簡単な解説と所感を述べていきます。

収集心

情報や物を収集するのが好きみたいです。

これについては、確かに、情報をストックしてしまいたくなりますね。ただ、物はそこまで収集するのは好きじゃないですね。

例えば、自分はメモ魔だということに最近気付いたのですが、打ち合わせや人から教えてもらったことなどはなんでもメモしたくなります。また、業務以外のところで自分の日報のようなものを書くようにしていて、そこで、今日はどんなことをしたのか、気になった情報はあったのかをまとめるようにしています。

これは、大学院で多くの論文を読んだり、「教えられたことはメモを取れ」と厳しくご指導いただいたことで、備わったのかなぁとも思いました。

回復志向

問題を解決することが好きみたいです。

確かに、他の人が抱えている問題を解決する手助けができれば、とても嬉しいです。また、自分の生活やアプリを作っていくときも、細かくissueを分けて、それが解決できた時に達成感を持ちやすいです。

個別化

人間一人一人の個性に興味が惹かれるみたいです。

確かに、人に会ったときには、「この人はどういう人なのだろうか」、「どういう歴史を持った人なのだろうか」ということが気になります。 また、人には向き不向きがあると思っていて、自分ができることは他の人にはできるとは限らないし、自分ができないことは他人の人ができるのではという考え方を持っています。

このストレングスのルーツを考えてみると、やはり大学院でのご指導いただいたことがルーツだと思われます。

大学院では臨床心理学を学び、カウンセリングの実習などで、色々なクライアントの方にお会いしてきましたが、その人の苦手なことと長所を観察して把握できるようにというご指導を受けて、訓練していました。

共感性

周囲の人の感情を察することができるみたいです。

これは全く自信がないのですが、これもルーツを辿れば大学院でのご指導がルーツなのかなと思います。

そもそも、これに関しては私は全く得意でなかったのですが、「共感性を鍛えるために、フィクションに触れて、想像力を養いなさい」と指導をいただいていたので、とりあえず時間がある時は、映画や小説、ドラマなどを見るようにしていました。

一応、訓練はしていましたが、ちょっと共感性があるかは自信がないです・・・。

ポジティブ

これは、人をよく褒めたり、笑ったりというコミュニケーション上のことと、どんな状況でもポジティブな面を探すという考え方のようです。

これについて、確かに私はコミュニケーション上では人を褒めたり、笑う方だとは思うのですが、楽天的かと言われるとそうではないと思ってます。

特に自分の書いたコードについては自信がないですし、「バグったらどうしよう・・・」と結構不安に思ってしまうので、楽天的ではないのかなーと思います。

ただ、私は臨床心理学の中で、解決志向という考え方が結構好きでして、これはクライアントがすでに持っているリソースを把握して、それを生かすことでクライアントの抱えている問題の解決を目指すアプローチなのですが(結構ざっくばらんに説明しているので厳密には違いますが)、その考え方が活きているなぁという瞬間がたまにあるような気がします。

感想

総じて、受けてみてとても良い体験だったと思いました。

まず、自分があまり意識せずにやっている行動や考え方に名前がついた感じでとても腑に落ちました。そして、そのような行動や考え方が長所だと言われると、とても元気が出る感じがしますね。

そもそも、大学院では心理検査などを勉強していたため、自分でもそれらを受けて、結果を出してみていたのですが、どれも具合が悪い結果ばかりが返ってきて、落ち込むようなものでした(そもそも、勉強していた心理検査が病院で使われるようなもので、精神病傾向があるかどうかを測るようなものが多かったので、必然的にそのような結果が返ってくるのですが・・・)。

そのため、今回のように前向きになれるテストは良いものだなぁと思いました。

「大事なのは、そこに「ハーフレーン」という名前をつけてまで、意識することなんだ。」 『アオアシ』18巻より

上記は私が好きな『アオアシ』というサッカー漫画のセリフなんですが、自分があまり意識せずにやっていたことに名前がついて、意識することで、多分、自分に合った仕事の進め方や取り組み方ができるのではと思っています。

ストレングスファインダーには、個別のストレングスの活かし方も載っておりました。

しかし、記載されているのは、個別のストレングスの活かし方であって、私の5つのストレングを合わせた仕事の活かし方までは記載されておりません。

そのため、上記の5つのストレングスを合わせた仕事の取り組み方などは自分で考えて実践していきたいと思いました。

FJORD BOOT CAMPの質問・雑談タイムで心がけていたこと

はじめに

私が参加しておりました、FJORD BOOT CAMPというプログラミングスクールでは、毎日16:00~17:00までの質問・雑談タイムという時間がありました。

オンライン(whereby)でメンターの方々や、受講生の方々、時には卒業生の方々が集まり、自由に雑談したり、抱えている悩みについて意見交換するという時間を設けてもらっておりました。

私は、質問・雑談タイムが始まってから3ヶ月くらい、できるだけ毎日ログインしていたのですが、いつの間にかMCと呼ばれるようになってしまいました。

この度、FJORD BOOT CAMPをなんとか卒業することができ、件の質問・雑談タイムのMCからも卒業ということになったのですが、有り難いことに受講生の方々から「MC力を見習いたい」というお声をいただきました。

そのため、僭越ながら質問・雑談タイムで私が心がけていたことや、参加してみた感想などをまとめたいと思います。

ちょっとまとまりの内容になってしまったかもしれませんが、ご了承下さい。

質問・雑談タイムの経緯について

そもそも、質問・雑談タイムは、新型コロナウイルスの影響でリモートワークが続き、受講生が雑談や相談がしにくくなっていたため設けられたものでした。

fjord.jp

元々は、komagataさんとmachidaさんがwherebyで雑談部屋を設けてくださり、そこに入れば気軽にkomagataさんやmachidaさんとお話できるという感じでした。

しかし、お忙しいお二人なので、その時間を確保するのが難しくなったのだと思います。ある日、「高橋さん、雑談部屋に居て下さいよー🙇‍♂️」とmachidaさんにお願いされて、できるだけ毎日ログインするようにしていた感じです。

最初は、私が1日中ログインしっぱなしだったのですが、それだと他の方々は集まりづらいだろうということで、komagataさんから提案をいただいて、時間を固定にしたという経緯です。

質問・雑談タイムの様子

基本的には、FJORD BOOT CAMPのプラクティスを進めている中で、分らないことや、聞きたいことを持ち寄って、それについて受講生同士で意見交換をしたり、メンターの方から直接教えていただくという感じです。

時には、画面共有やVScodeのlive shareを用いて、みんなでコードを見ながらモブプロみたいなこともやっていました。

上記のようなことがなければ、自由に雑談という感じでした。

なぜ、私がMCと呼ばれるようになったかというと、Slackで「今質問・雑談部屋に入りましたー」と皆さんに伝えたり、「今日は質問したいことや聞きたいことはありますかー?」などとログインした人に積極的に聞いてたりしていたからだと思います。

質問・雑談タイムが始まったばかりの頃は、私もログインされた受講生の方々も勝手が分らない感じだったので、ある程度、場を仕切ったり、話を振ったりした方が皆さんも話しやすいのかと思って、積極的にそういうことをやっていました。

質問・雑談タイムで心がけていたこと

話している方が話やすい雰囲気を作る

質問・雑談タイムでは、話している人の話を邪魔しないように聞いてる方々が気遣って相槌を控えて下さっておりました。

後は、カメラをオフにしている方々も多い感じでした。その点については、個人の事情で「お部屋はちょっと見せられない・・・」という方も多いと思うので、全然構いません。

しかし、話している側としては、話している最中に、相手から相槌やノンバーバルなリアクションが得られないと、とても話しづらいと感じたので、私は積極的に声を出した相槌とカメラをオンにしてリアクションをとるようにしていました。

ただ、ビデオチャットで、声を出して相槌をする人が多いと、今度は話が聞こえないということもあるので、そこはさじ加減だと思っています。

様子をSlackに残した

質問・雑談タイムでの話が、とても勉強になることが多かったので、どこかに残しておきたいと思い、個人的にSlackに質問・雑談タイムの内容を投稿していました。

これについて、「Slackを質問・雑談タイムのチャットのように使っては?」というアドバイスをいただき、ログインした人が積極的にSlackでワイワイやることにしました。

そうしたところ、知見が残せるようになった以外にも、質問・雑談タイムにログインしてない人にも様子を伝えることができたりして、少しは質問・雑談タイムに入りやすくなったのではないかと思いました。

初めての人がいるときは、積極的に自己紹介をした

FJORD BOOT CAMPはリモートで勉強されている方や新しく入会される人が多いので、みんながみんな知り合いという訳ではありませんでした。

そのため、初めて質問・雑談タイムに入ったという方がいらっしゃった時は、積極的に自分から自己紹介をするようにしておりました。ログインしている人の人数が少ない時は、「自己紹介をお願いしますー」という感じで、ログインしてくださっている方、みんなに自己紹介していただいたのですが、人数が多いときや、話題が多い時にはその限りではありませんでした。

今思い返すと、チャットに自己紹介を書くでも良かったのかなーとも思いました。

話せてない人がいる時には、タイミングを見つけて話を振ってみる

これは私だけかも知れませんが、学校の授業などのような、話を聞くだけの機会(双方向のコミュニケーションが取れない・取りにくい機会)に参加すると、とても疲れてしまいます。

質問・雑談タイムでも、他の人の話を聞くだけで、双方向のコミュニケーションを取れないという形になると、ログインしている方もつまらなく感じてしまうのではないかと思いました。

そのため、話せてない人がいる時には、タイミングを見つけて話を振ってみました。

これがいいかどうかは人によると思いますが、具体的には、会話が落ち着いた時に、「〇〇さんは聞きたいことはありませんか?」などと名指しで質問してみました。

また、他の人がプラクティスを進める上で悩んでいることについて、ログインされている方が知見を持っていそうだという時にも、名指しで振ってみて、知見を話していただいたりということは意識してやっていました。

とりあえず続けてみた

ログインされる方の人数が少ない時もあったので、「需要あるのかなー」と心配に思った時もありましたが、とりあえず可能な限り毎日続けてみました。

『ピープルウエア』にも「コミュニティを作る最善の方法は誰にも分らない。コミュニティは一日にして成らず」という旨が書かれていたと思いますが、その通りだと思って、できる限り続けてみようと思って、続けてみました。

続けていると、メンターの方や、受講生の方以外にも、卒業生の方、企業からFJORD BOOT CAMPに研修で参加されている方や、その企業のエンジニアの方々など色々な方々が参加して下さるようになり、とても有り難かったです。

3ヶ月くらい続けてみての感想

ほぼ毎日、質問・雑談タイムにログインしていましたが、とても楽しかったです。

元々、人と話すことは好きな方なので、みなさんから色んな話を聞いたり、自分が気になっていることを話したりということが楽しかったです。新型コロナウイルスの影響で、人と話すこと自体が減っていたので、良い気分転換・ストレス発散になったという面もあります。

また、メンターの方々や、企業のエンジニアの方々に、自分の気になっていること(技術的なところや、エンジニアとしての心構え的なところまで)のお話を聞けて、とても勉強になりました。

人と人のコミュニケーションについては、自分は結構考え込んでしまうため、上記に書いた内容について、色々考えすぎなところもあるかと思いますが、何かの参考になれば幸いです。

『コーディングを支える技術――成り立ちから学ぶプログラミング作法』を読了しました

はじめに

先日7月1日から、プログラマーとして生を受けて、就業しているのですが、まだまだペーペーのペーなので、おすすめされた本を片っ端から読んでいきたいと思っております(そんなに読むのが早い方じゃないので、時間がかかってしまうと思いますが・・・)。

そこで、「どうしてこの構文が生まれたか、などの背景が分かるようになるので、おすすめです」とおすすめいただいたのが、本書でした。

読んだ感想・学んだことをまとめていきたいと思います。

本書の章立て

第1章 言語を深く効率的に学ぶには

第2章 プログラミング言語を俯瞰する

第3章 文法の誕生

第4章 処理の流れのコントロール

第5章 関数

第6章 エラー処理

第7章 名前とスコープ

第8章 型

第9章 コンテナと文字列

第10章 並行処理

第11章 オブジェクトとクラス

第12章 継承によるコードの再利用

章ごとの感想

第1章 言語を深く効率的に学ぶには

本書の導入部分です。

特に印象に残った部分が「言語に依存しない、普遍的な理解力を養う」という内容でした。

そのような理解力を養うためには

  • 比較から学ぶ
  • 歴史から学ぶ
  • 作ることで学ぶ

ということが必要だと述べられています。

複数の言語を比較したり、その言語の設計思想や歴史を把握することで、新しく言語を習得するとき、その言語を理解するための素地が備わっている状態になり、結果として習得が早いということだと思います。

私はRubyJavaScriptしか学んだことがないので、あまり実感はできないところなのですが、確かに、技術の背景を学ぶことで、その技術の理解がしやすくなるという経験があるので、上記の学び方は実践していきたいと思いました。

第2章 プログラミング言語を俯瞰する

プログラミングがなぜ生まれたのかというお話でした。

プログラミングは何かの作業などを楽にするために書くもの。その言語が何を楽にしたいかを把握することで、言語の概観も掴みやすくなるということでした。これにもとても納得です。

第3章 文法の誕生

文法の歴史のお話でした。

今まで、「構文木」という言葉を度々聞く機会があったのですが、「結局、構文木ってなんなのだろう?」という状態でした。

本書を読んで、構文木というものがなんなのかわかったような気がします。

つまり、プログラミング処理をそのまま木構造で表したもので、処理を最小単位に分けて、後に後に処理をはめ込んでいくことで、出来上がっていく木の根っこのような構造が構文木なのかなーと理解しました。

第4章 処理の流れのコントロール

なぜ、if...elsewhileなどが生まれたのかというお話でした。

結論としては読みやすさのためだそうです。プログラムに頻出のパターンを読みやすい構文にすることで、より生産的にプログラミングができるということです。

自分はまだ経験がないので、「読みにくいなー」というコードにあまり出会ったことがないのですが、本書以外でも『リーダブルコード』などでもコードの読みやすさが重要視されていたため、改めて可読性は大事なことなのだと認識しました。

第5章 関数

そもそも関数とは?、なぜ関数が必要になったのか、技術的に実現に至った背景がまとめられていた章でした。

とても面白かったのは、「関数という機能が実装されるには、関数が呼び出された元の場所を記憶されていなければならない」というお話でした。

「言われてみれば、確かにそうだよなぁ」と思いましたが、本書を読むまで、そのようなことを意識したことがなかったので、新鮮でした。

その問題を解決するスタックという概念についても、少しだけ理解することができた気がしています。

第6章 エラー処理

プログラムの失敗をどのように伝えるのか、その方法や技術的背景についてまとめられた章でした。

ここで、興味深かったのは「どういう時に例外を投げるかの正解はない」というところでした。各言語ごとに、どのタイミングで何を投げるのかが違うみたいです。

エラー処理について、私はあまり書かずにきてしまったので、あまり考えたことはなかったのですが、正解がないとのことのなので、自分なりの理由を持ってエラー処理を記述していきたいと思いました。

第7章 名前とスコープ

名前やスコープの歴史についてまとめられていた章です。

名前とスコープについては、日頃から頭を悩ませたり、問題に直面する機会が多いので、実感を持って理解できる内容でしたが、その中でもスコープには動的スコープと静的スコープというものがあると初めて知りました。

どちらにも問題点があり、言語開発者の方は頭を悩ませたのではないかと想像するのですが、そのような開発者の方が直面した問題とその対応方法について知ることは、とても興味深かったです。

第8章 型

型についてのお話。

私はRubyを主に使っているため、Ruby3.0で型検査が導入されるというお話は聞いており、都度都度「型検査って何ですか?」と聞いていた記憶がありますが、結局自分の中に蓄積される情報が断片的になってしまっていて、あまり理解したとは言えない状態でした。

そのため、本書のように簡潔ながらも体系的にまとめてくださっていると、とても理解しやすかったです。

自分のなんとなくの理解が文章になって腑に落ちたという感覚がありました。

つまりは、型検査ができるようになると、プログラムを動かす前に型の不整合が分かり、問題が起きそうなコードをすぐに見つけられるようになるということなんですね。

第9章 コンテナと文字列

データを入れるためのコンテナと、文字列の歴史についてまとめられた内容でした。

木構造から、データを検索するのと、ハッシュからデータを検索するのは計算量的に大きな差があるというお話があり、これも納得という感じでした。

文字列についても、ASCIIやUnicodeって「なんとなくよく見るよなー」といった認識でしたが、それが本書を気にどういうものなのか知ることができて良かったです。

ASCIIは、アメリカ標準の符号化方式で、UnicodeはASCIIよりも後に登場して、国際標準となった符号化方式なんですね。

第10章 並行処理

並行処理の実装方法、競合状態をどのように防いでいくか、その解決策を実現したプログラムが紹介されている章でした。

並列処理の方法として、協調的マルチタスクとプリエンプティマルチタスクがあり、開発者にとっては時間で指定できる後者の方が良いけど、競合状態がおきやすくて・・・という感じで、順序立てて説明されており、分かりやすい印象でした。

複数の選択肢がある中で、どれにもメリット、デメリットがあり、全て解決できる手段はないのだと再認識しました。

その中で、自分なりに「どうしてその方法を選んだのか」という理由を持って、コードを書いていかなければならないのだと強く思いました。

第11章 オブジェクトとクラス・第12章 継承によるコードの再利用

私が受講していたFJORD BOOT CAMPで、最近オブジェクト指向について考える機会があり、このあたりの内容は比較的実感を伴って理解できていった感じです。

その中でも、特に曖昧だったプロトタイプの考え方が分かりやすく明文化されていたのが有り難かったです。

プロトタイプの考え方がないと、関数が関数を作り出すようなプログラムでは実行される度に新たな関数が生成されることとなってしまいますが、プロトタイプでは、そのオブジェクトのプロトタイプに問い合わされることにより、同じ関数が呼び出されることになり、新たな関数が何個も生まれることがなくメモリの節約ができるというメリットがあるようです。

全体を通しての感想

私はRubyJavaScriptしか触ったことがありませんでしたが、本書を読んで、他のプログラミング言語を触ってみたいという気持ちが高まりました。

他のプログラミング言語を触ることでプログラム設計の幅が広がる(komagataさんからいただいたお言葉)ということもあるようです。

『達人プログラマー』にも年に一つは新しい言語を習得しようとありましたし、プログラマーとして生きていく上で複数の言語を学んでいくことがいかに大事なことなのかということを再認識しました。

ただ、私は複数の物事を一気に習得することが難しいタイプなので、Rubyがある程度書けるようになってからだとは思いますが・・・。

また、「ぼんやりと頭の中に入っていた情報が、本書によって体系化されたり、文書となって提示されることで腑に落ちた」という感覚が結構ありました。 そのような点でも、本書を読んで良かったと思いました。

最後に、本書で触れられている、

  • 比較から学ぶ
  • 歴史から学ぶ
  • 作ることで学ぶ

という学び方を自分の中での方針にして、実践していきたいと思いました。

『Everyday Rails - RSpec による Rails テスト入門』を読了しました

はじめに

実は、私はRSpecをしっかりと学んだことがなく、今までminitestでテストを書き続けておりました。

現在、自分のアプリの開発でRSpecを採用しては見たものの、行き当たりばったりで全体像が掴めていないと感じました。

そのため、RSpecを体系的に学びたいと思い、『Everyday Rails - RSpec による Rails テスト入門 -テスト駆動開発の習得に向けた実践的アプローチ-』を読みました。

本書を読んで、学んだこと、感想をまとめたいと思います。

leanpub.com

本書の章立て

第1章 イントロダクション

第2章 RSpecのセットアップ

第3章 モデルスペック

第4章 意味のあるテストデータの作成

第5章 コントローラースペック

第6章 フィーチャースペックでUIをテストする

第7章 リクエストスペックでAPIをテストする

第8章 スペックをDRYに保つ

第9章 速くテストを書き、速いテストを書く

第10章 その他のテスト

第11章 テスト駆動開発に向けて

第12章 最後のアドバイス

付録A システムスペックに移行する

章ごとの感想

第2章 RSpecのセットアップ

章のタイトルの通り、プロジェクトにRSpecを導入するとき方法や設定がまとめられた章でした。

ここで、初めて知ったのですが、以下を記述すると、RSpec.rspecファイルに--format documentationと記述することで、出力を読みやすい形式にすることができるようです。

実際に自分の既存のプロジェクトで出力の違いを見てみると、以下のようになりました。

❯ be rspec             
.....

Finished in 3.54 seconds (files took 4.25 seconds to load)
5 examples, 0 failures

--format documentationを記述したとき

❯ be rspec

AppelMusicRepositoryのmodelテスト
  APIレスポンス(mock)を取得する
    #search

SpotifyRepositoryのmodelテスト
  APIレスポンス(mock)を取得する
    #search

YoutubeRepositoryのmodelテスト
  APIレスポンス(mock)を取得する
    #search

楽曲検索機能
  検索フォームに入力した楽曲をSpotify APIを介して検索
    検索結果を表示する
    SpotifyとYoutube、Apple MusicのURLを表示する

Finished in 3.14 seconds (files took 3.31 seconds to load)
5 examples, 0 failures

これは、確かに読みやすいので、早速記述を追加したいと思いました。

第3章 モデルスペック

モデルスペック、いわゆるモデルの単体テストについてまとめられた章です。

モデルスペックに含めるべきテスト内容や、具体的なベストプラクティスが示されていて、とても参考になりました。

特に、describecontextitの書き方がまだしっくりきていない私にとっては、その使い方や意味が明文化されたことで、書き方の方針が少し掴めたような気がしています。

本書を読んで理解したところをざっくり書いてみると・・・

  • describeにはクラスやシステムの機能のざっくりとした内容を説明文に書きます。
  • contextは場合分けをする時に使い、特定の状態に関する説明文を書きます。
  • itは期待する結果一つだけを書きます。ここの説明文は動詞で始まると分かりやすいものになります。

という感じなるでしょうか。なんとなく使い方が掴めたので、私にとってはとてもありがたい内容でした。

第4章 意味のあるテストデータの作成

FactoryBotを使ったテスデータの作成についてまとめられている章です。

私はFixturesしか触れてこなかったため、FactoryBotが簡単に関連づけされたモデルのテストデータを用意することができるというところにFactoryBotの利点を感じました。

また、作成したFactoryを継承することができるという点もFixturesとは異なっていて、「助かる場面が多そうだなー」と思いました。

第5章 コントローラースペック

コントローラーのテストに関する章でした。

そもそも、コントローラーの役割はパラメーターを受け取って、他のところに渡しているというものなので、そもそも複雑なことはやっておらず、テストも複雑なことをしなくて良いという感じでした。

ただ、実際にコントローラースペックもコントローラー同様に肥大化しやすいそうなので、テストであっても誰の責務なのかということを意識し、積極的に切り分けをしていかなければならないと思いました。

第6章 フィーチャースペックでUIをテストする

フィーチャースペックで、UIを通してモデル・コントローラーの協調をテストする方法についてまとめられています。

ただ、RSpecではシステムスペックが使えたなーと思い出し、システムスペックとフィーチャースペックとどう違うのかというところに疑問を持ちました。

そこで、実際にFJORD BOOT CAMPでjnchitoさんにお聞きしたところ、基本的には書き方などはほとんど同じで、これから学んだり、使っていくのであればシステムスペックで良いそうです。

元々、フィーチャースペックはminitestにシステムテストが実装される以前からあったRSpec独自のインテグレーションテストだったようです。

しかし、minitestにシステムテストが実装されたため、RSpecもそれに倣った形で実装されたという経緯があるようです。

以上のようなことをjnchitoさんからお聞きしたのですが、実はそれに関連することが本書の「付録A」に記載されているという。。。

第7章 リクエストスペックでAPIをテストする

APIをどのようにテストするかということがまとめられている章でした。

そもそも、コントローラーテストでAPIのテストもできるのではという話もありますが、もっとシンプルにHTTPリクエストを使ってテストすることができるのが、リクエストスペックということで私は理解しました。

第8章 スペックをDRYに保つ

スペックをDRYにするための、テクニックがまとめられた章です。

ここで印象的だったのは、RSpecでは自分でマッチャを作成できるということです。

ただ自分はデフォルトのものがあればそれを使ってなんとかしたいと考えてしまうので、あまり使う機会はなさそうだなーとは思うのですが、実際に多く使われるものなのでしょうか🤔

また、ある部分でテストが落ちてもテストを続けることができるaggregate_failuresも印象的でした。 ただ、一度、aggregate_failuresを使ってしまうと、全てのテストに使ってしまいたくなりそうなので、使い所の見極めと、しっかりとした理由づけが必要そうです。

第9章 速くテストを書き、速いテストを書く

実行時間が短く、綺麗なスペックをいかに書いていくためのgemやテクニックがまとめられた章でした。

この章で印象に残ったのは、モックとスタブのお話です。 自分はWebMockを使ったことはあるのですが、モックとスタブの定義が毎度ごっちゃになってしまっていて、ここでは以下のように明文化されていて、とても分かりやすかったです。

  • モックとは、本物のオブジェクトのふりをするオブジェクト。
  • スタブはオブジェクトのメソッドをオーバーライドして決められた値を返すようにすること。

そして、モック化に関しては

「自分で管理していないコードをモック化するな」

という言葉が、モックの使い方に関する指針を示してくれていて良かったです。

第10章 その他のテスト

上記以外の、ファイルアップロードや外部APIに対するテストの方法などがまとめられている章でした。

外部APIに関するテストのところで、VCRというgemがあることをしりました。

これは、リクエストとレスポンスをファイルに記録しておいて、記録されたリクエストが送られた時は、記録されたレスポンスを返すということをやってくれるようです。

上述するようにWebmockを使ったことがあったのですが、こちらは初めて知りました。

ただ、適切な使い方をしないと記録しているファイルにどんどんと色んなリクエストとレスポンスが溜まっていくような気がして、使うならばしっかり使わなければいけないのかなと想像しました(色々設定などはあるでしょうが・・・。

第11章 テスト駆動開発に向けて

テスト駆動開発を実際にテストコードを書きながら進めていくという章でした。

新鮮だったのは「外から中へ」テストを実装していくということでした。最初にフィーチャースペックを書いて、その中でテストが必要だと感じた内容にについてはコントローラースペックとして実装、そして、モデルスペックを実装という内容です。

私は、ユニットテスト→統合テスト→UIテストという順番でテストは書いていくものだと思っていました。この考え方は、Jonathan Rasmusson著 『初めての自動テスト Webシステムのための自動テスト基礎』で述べられていたと思います。

しかし、本書の「外から中へ」の考え方は、上記の考え方とは真逆のものだと思われます。

イマイチ、私の中で両者の考え方の利点を把握できていないので、実際に両者を試してみて、その利点を実感できればと思いました。

第12章 最後のアドバイス

テストを書くときの心構えがまとめられた章でした。

自分はモデルテストは実装したメソッド全てに対応するテストを書こうと意識しているのですが、システムテストのレベルになるとどこまでテストして良いものか・・・と悩むことが往々にしてあります。

そのため、この章で述べられている「自分の取り組んでいるものが実装できるようにスペックを追加する」という観点でテストを考えてみると、少し考えやすくなるのかなーと思いました。

全体を通しての感想

これまで、行き当たりばったりでRSpecのテストを書いていきたのですが、本書を読んで今までなんとなく使っていた構文を使うための指針が明文化されており、とても有り難かったです。

ただ、やはり、分かりやすいテストを早く書くということは一朝一夕でできるものではないと感じました。そのため、本書を指針として、テストを書いていくことで、テストの経験値を高められればと思いました。

『ピープルウエア』を読了しました

はじめに

大分前になってしまいますが、FJORD BOOT CAMPのSlackでkomagataさんが『ピープルウエア』を勧めて下さいました。

時間はかかってしまいましたが、ようやく読み切ることができたので、感想と学んだことをまとめたいと思います。

本書の章立て

第1部 人材を活用する

第2部 オフィス環境と生産性

第3部 人材を揃える

第4部 生産性の高いチームを育てる

第5部 肥沃な土壌

第6部 きっとそこは楽しいところ

章ごとの感想

第1部 人材を活用する

マネジメントのアンチパターン、陥りやすい思考がまとめられている章でした。

「長時間働かせることが成果に結びつく」、「生産性を飛躍的に向上させる方法があるはずだ」という思考がそもそも間違いであり、人をやる気にさせることこそマネジメントの本質であると述べられています。

少し本書から離れた話かもしれませんが、臨床心理学の分野では「解決志向」という考え方があります。これは問題の原因を考えるのではなく、解決に目を向けるというものですが、その中に現在のリソースを把握するというものがあります。

ここでリソースとは、クライアントの持っている長所や、クライアントを取り囲んでいる頼れる資源などを指しますが、マネジメントの分野においても、ないものねだりをしていくよりも、現状のリソースを確認するところから始まるのかなーとふと思いながら読み進めていました。

第2部 オフィス環境と生産性

プログラマーはどのような環境で働くと生産性が低くなるのか、高くなるのか。そもそも生産性に関連する要因はなんなのかということを探った章でした。

非常に面白かったのは、オフィスの設計の話で、「ドアから距離があればあるほど、個人的空間であるという意識が高くなるため、ドアの近くに共有スペースを作ろう」というお話です。

プログラマーを取り巻く物理環境にまで言及しているのは、とても興味深かったです。 やはり仕事をする時の、外的要因は生産性にとても大事なのだと知りました。

第3部 人材を揃える

「標準を押し付けることの弊害」、「リーダーシップ」や「会社における人的資産の評価」など人材について幅広く書かれた章でした。

特に面白かったのは、リストラや退職させることはコスト軽減にならないということです。退職した人の業務を引き継ぐ人がいて、その人が退職した人と同じレベルまで作業できるようになるまでの損失や教育コストを考えると、決してリストラなどは人的コストの軽減になりません。

人的資源を考える際には、短期的な目線でのみ考えるのではなく、長期的な目線で考える方が、コストや効果を適切に認識できるのかなーと思いました。

第4部 生産性の高いチームを育てる

チームの生産性を損ねる手法を「チーム殺し」として紹介している章です。

印象的だったのはチームにおける競争が実は生産性を損なうものであるということ。なぜなら、競争があると、自分の価値を評価されたいがために仕事を属人化したくなり、チームメンバーに対して何かを教えるということが行われにくくなるからだそうです。

これについて、最近、FJORD BOOT CAMPスクラム開発のプラクティスに取り組んでいて思うところがありました。

自分より素早くissueを解決できるプログラマーに劣等感を持って悩んでいるよりも、教えていただくことで、チームとしての生産性はあがるのだと、スクラム開発のプラクティスを通して実感しました(詳細は以下のページ)。

nmp300.hatenablog.com

個人間が競争して、個人の価値をいかに証明していくのではなく、協調して、チームの生産性をどのように上げるのかが重要なことなのだと、ここでも改めて考えさせられました。

第5部 肥沃な土壌

メソドロジーの話に始まり、意味のない会議、社内スパム、コミュニティ形成など、幅広く書かれた章でした。

メソドロジーに傾倒することは作業の標準化を押し進めることであり、そもそもの問題に意識が向かなくなる。あるメソッドを広めるのは、そのメソッドを用いて効果が上がってからで遅くないという部分はとても納得しました。

効率の良い手法を考えすぎると、そもそもの問題が解決されないことが往々にしてありました。

また、良いコミュニティを形成するのに最適な手法はなく、時間をかけて築かれるものだという内容は勇気が出る内容でした。

第6部 きっとそこは楽しいところ

「秩序が全てではなく、秩序の中に混沌状態を作り出すことで、モチベーションや生産性があがる」という内容でした。

ここら辺のお話は、実際に業務に携わっていないので、あまり納得することができませんでした。 業務に携わって、プログラミングコンテストや開発合宿など、そういう機会があると、やはり生産性が変わってくるもなのかなーとぼんやりと理解しました。

全体を通しての感想

自分が元々臨床心理学を勉強し、人間関係、コミュニケーションについて考えてきたということもあってか、読む前からワクワクしていました。

自分はまだ実務についてないし、チームをマネジメントする立場でもないので、本書の言わんとしていることを実感を持って理解できてはいないと思います。

ただ、コードを書く以外でも、ソフトウェア開発を取り巻くチームというものにも気を配って立ち回って行けたら、自分の培ってきたものが発揮できて、自分の武器になるのかなーともぼんやりと考えました。

ある程度経験を積み、チームで働いているんだということを意識できるようになったら、本書に書かれていることを少しずつ実践していきたいと思いました。

追記

誤って第2版のリンクを貼ってしまっていたため、ご指摘をいただいて、第3版のリンクを貼り直しました🙇‍♂️

『UNIXという考え方―その設計思想と哲学』読了しました

はじめに

UNIXという考え方―その設計思想と哲学』読了しました。

booklog.jp

何気なくターミナルを使って操作をしてはいるものの、UNIXがどのような考えのもとで作られたのかということは、あまり知りませんでした。

これを機に勉強したいという思いで本書を読みましたが、とても面白かったです。

学んだこと、読んだ感想をまとめたいと思います。

章立て

第1章 UNIXの考え方:たくさんの登場人物たち

第2章 人類にとっての小さな一歩

第3章 楽しみと実益をかねた早めの試作

第4章 移植生の優先順位

第5章 これこそ梃子の効果!

第6章 対話的プログラムの危険性

第7章 さらなる10のUNIXの考え方

第8章 一つのことをうまくやろう

第9章 UNIXとその他のオペレーティングシステムの考え方

各章ごとの感想

第2章 人類にとっての小さな一歩

設計思想の一つ、「スモール・イズ・ビューティフル」に関する内容でした。

部品をたくさん作り、小さなプログラムの集合としてOSを作っていくことで、保守しやすくなり、他のツールと組み合わせやすくなったりという利点が述べられていました。

この小さな部品を作って、それを組み合わせていくという考え方は「オブジェクト指向」に通じるものがあると思い、その利点を納得しながら読むことができました。

第3章 楽しみと実益をかねた早めの試作

人間による3つのシステムのお話でした。

人間に若年期、成熟期、老年期と3つのライフステージがあるように、システムにもそのような段階があるのだというお話。

システムにもライフステージがあるということは、私自身全く考えたことがなく、とても興味深い話でした。

第1のシステムから第3のシステムになるまで、その段階に応じてシステムに集まってくる人が異なるということで、「システムは人が作る」ということを改めて意識させられました。

第5章 これこそ梃子の効果!

プログラムによる梃子の原理を意識するというお話。

「偉大なプログラマはコードを借りてきて、それにより1時間働いたら、5時間や100時間分の成果を上げる」という内容でした。

自分には「コードを借りてくる」という意識をあまり持ててないなーと思いました。 例えば、便利なgemがあっても、RubyRailsの標準機能で実装することができたら、それでいいのではと思ってしまいます。

車輪の再発明をしない」、「コードを借りてくる」という意識をもっと持った方が良いのかなーと考えました。

また、自分は独自技術症候群になりやすそうだなーと思ったので、そこも注意していきたいと思いました。

第6章 対話的プログラムの危険性

全てのプログラムはフィルタであるというお話が印象的でした。

自分は経験がほとんどありませんが、自分の考えうる範囲で、Railsアプリを考えてみると、DB上のデータをどのように出力するかの違いに帰着するのかなーと思いました。

第7章 さらなる10のUNIXの考え方

その他のUNIXの設計思想のお話でした。

「森林を守る」、「沈黙は金である」などの言葉はユーモラスですが、その背景には「紙に出力した時点で、そのデータを操作することはできなくなるため、やめよう」、「不要なコメントを表示させないことで、パイプラインを使ったプログラム同士の連携がしやすくなる」など、とても納得できるものでした。

全体を通しての感想

総じて、とても興味深く、面白い本でした。

「小さなプログラムの集合を作り、組み合わせることで、最終的には大きなプログラムよりも有用なシステムとなる」という考え方はオブジェクト指向に通じるものがあると思いました。

また、「試作は早く行い、ユーザーの反応を確かめるべき」という考え方は「リーンスタートアップ」や「アジャイル開発」の考え方に近いと思いました。

このように昔に作られてシステムであっても、その背景にある考え方は現代の考え方に通じるということが、とても面白いと思いました。

技術や考え方は日進月歩で変わっていくものだと思いますが、良い考え方や優れた考え方は残り続けて、応用されていくものなのだと考えました。

そのため、時には昔の考え方を学ぶことで、現代の考え方がより理解できることもあるのだろうと思いました。

こういうのを温故知新というのでしょうか🤔