めるノート

へっぽこWebエンジニアが地団駄を踏んでいる日誌

Railsで、Action Mailerとletter_opener_webを初めて使いました

Action Mailer、すごくベーシックな実装なはずなのに、なんか、今まで縁がなかったんです。
Railsチュートリアルでやったのかな? そのへんも記憶にない...

ということで今回は、ローカル環境でletter_opener_webを使ってメールを確認するところまで、やっていきます。

1. Gemfileletter_opener_webを追加する

group :development do
  gem 'letter_opener_web', '~> 1.0'
end

追加したら、コマンドラインbundle installします。

$ bundle install

メール確認用画面を、config/routes.rb に追加します。

Rails.application.routes.draw do
  root to: "home#index"

  # 省略...

  mount LetterOpenerWeb::Engine, at: '/letter_opener' if Rails.env.development?
end

2. config/environments/development.rb に設定を追加

Rails.application.configure do

  # 省略...

  config.action_mailer.perform_caching = false

  config.action_mailer.default_url_options = { host: 'localhost:3000' }
  config.action_mailer.delivery_method = :letter_opener_web

  # 省略...
  
end

production.rbにはメールの設定なんかを書いたりしますが、今回は割愛。

3. Mailer を作成する

コマンドラインで生成します。

$ rails g mailer UserMailer

生成したメーラーを、必要に応じて調整します。

app/mailers/user_mailer.rb

class UserMailer < ActionMailer::Base
  default from: "hoge@gmail.com"

  def send_message_to_user(user)
    @user = user
    mail to: @user.email,
         subject: "メールの件名が入ります"    
  end
end

4. メール本文を作成する

app/views/user_mailer/send_message_to_user.text.erb を作成します。
メソッド名.text.erb という名前になるので注意しましょう。 ファイルの内容は以下のような感じです。

<%= @user.name %> さま

いつもお世話になっております。
株式会社●●です。
この度は、キャンペーンにご応募いただきまして、ありがとうございました。
当選発表は、商品の発送をもってかえさせていただきます。

株式会社●●

5. メール送信処理

メール送信をしたいタイミングで、Mailerのメソッドを実行します。

class EntryController < ApplicationController

  def create
    @entry = Entry.new(entry_params)
    if @entry.save
      # ここでメールを送信する
      UserMailer.send_message_to_user(@entry.user).deliver_now
      redirect_to root_url
    else
      render 'new'
    end
  end
end

6. メールが送られたのか確認する

最初に config/routes.rb に追加した、メール確認用画面を開きます。 http://localhost:3000/letter_opener を開くと、以下のようが画面が開き、送信処理があったメールを一覧できます。(実際に送信されているわけではありません)

f:id:c5meg1012:20180729195634p:plain

レバテックキャリアさんに、前職在籍時に書いた記事を取り上げていただいた

レバテックキャリアさんの記事はこちらです。 また、取り上げていただいた記事そのものはこちらです。

c5meru.hatenablog.jp

2017年2月21日、ということで、ちょうど1年と5ヶ月前の記事になりますが、取り上げていただけるなんて、ありがたい話ですね。
Web制作会社と自社サービス会社の違いについて、少しでも知る手がかりになれば幸いです。

さて、偶然なことに、まさに今も「転職して4ヶ月くらい経った」タイミングだったりします。
今回は「自社サービス開発」どうしの転職ではあるのですが、「入社当時 社員5名未満の超スタートアップ」から「上場後で社員300名超のミドルベンチャーの転職です(ただし某ブラックホールではありません、フロア違いです 笑)。
エンジニアの規模としては、3人から11人(インターン・アルバイト含む)に変わりました。
ですので、ついでに、今回の転職で変わったことを書いてみようと思います。

以前やっていたこと

上記の記事から抜粋。

現在やっていること

  • CSSレビュアー
  • Vue.js実装・レビュアー
  • Ruby on Rails実装(初期実装ほぼ全体)
  • インフラのお勉強

変わったこと

技術に集中できるようになった

現在の会社では「ディレクター」や「デザイナー」がいらっしゃるので、サービスの方針とか、デザインの方針とかを考えるのはお任せする感じになりました。もちろん、発言権がないわけではなく、意見があれば都度相談することもあります。
ですので以前よりは、技術のことを考える時間が増えました。あとは、コードレビューでレビュアーになることもグッと増えました。

プロジェクト内での役割について考えるようになった

以前はほとんど1人でフロントエンドもサーバーサイドも実装し、軽く上長に相談する程度で開発を進めていたのですが、上記に伴い、いろんな役割の人がいるので、たとえば
「これはディレクターにも相談するべきなんだっけ」
とか、
「ここはデザイナーに調整してもらった方がいいんだっけ」
とか、考えるようになりました。 なので、良くも悪くも「テキトーに、いい感じにやっとけばいいや」っていうふうに進められなくなったな、と思います。

専門家に相談できる強さがある

これも上記に関連するのですが、現在は、フロントエンドも、サーバーサイドも、インフラも、それぞれ専門家がいるので、相談相手に困らなくなりました。
なので、現在自分が関わっているプロジェクトには、ディレクターやデザイナー含め、たくさんの専門家がメンバーをサポートしてくださっています。ありがたい話です。

まとめ

やはり人数がいる強さってすごいな、に尽きます。
ただ、自分は現在ゼネラリストとしてやっているところがあるので、これ以上大きい組織になった時に、専門家でない自分が果たして役に立てるのかな...?なんて迷ったりすることもあります。
インフラお勉強中なので、ひと通り慣れてきたら、自分の道を決めるタイミングになるのかもしれません。
とはいえそれでも優秀なエンジニアさんは、自分の専門外の技術もある程度キャッチアップしていたりするので、そういうところを見習いながらやっていきたいな、と思っています。

PercelでVueを動かしてみた

職場で話題になったので動かしてみました。すごく簡単でした。
調べてみると、流行った頃の日本語の記事がいろいろ記事が出てきたんですが、Percel側もあれから開発が進んで、ますます簡単になっていたようです。

qiita.com

こんな感じなので、日本語の記事を参考にするより、公式ドキュメントを見た方がはるかに簡単でしたw

parceljs.org

ということで、早速やってみます。
まずは、プロジェクトルートのディレクトリを作ります。
今後はその中で作業していきます。

公式ドキュメントに書いてある通り、まずは Vueparcel-bundler をインストールします。

$ npm install --save vue
$ npm install --save-dev parcel-bundler

そして、 package.jsonscript を追加します。

 "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "parcel index.html"
  },

初期設定はこれだけ。
あとは、プロジェクトルートにビルドしたいファイルを追加していきます。

index.html

<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>parcel-vue</title>
</head>

<body>
  <div id="app"></div>
  <script src="app.js"></script>
</body>
</html>

app.js

import Vue from 'vue/dist/vue.esm.js';
import Hello from './vue/hello.vue'

const app = new Vue(Hello);
app.$mount('#app');

vue/hello.vue

<template>
  <p>
    {{ message }}
  </p>  
</template>

<script>
  module.exports = {
    data: function() {
      return {
        message: 'Hello, Percel-Vue!!',
      }
    }
  }
</script>

<style scoped>
  p {
    font-size: 64px;
    text-align: center;
    color: #3eaf7c;
  }
</style>

ビルドしたいファイルを追加したら、先ほど package.json に追加した script で動かしてみましょう。

$ npm start index.html

Server running at http://localhost:1234 の文字が出たら、 http://localhost:1234 から確認できます。

f:id:c5meg1012:20180715143641p:plain

Webpackのconfigには自分も悩まされてきたので、Percelの今後に期待です。

RailsのModelで定義するlengthのvalidationについて、activemodelのコードを見てみる

今日はさっぱりめ。
Railsでmodelを書くときって、こんな感じに書くと思うんですが、

class User < ApplicationRecord
  validates :name, presence: true, length: { maximum: 30 }
  validates :email, presence: true
end

昨日お仕事で length のカスタムバリデーションを書きたくなり、このデフォルトの length の実装ってどうなってるんだろう? と思ったので、調べてみました。

該当箇所はこちら。

github.com

7/7 10時現在のソースは以下。

# frozen_string_literal: true

module ActiveModel
  module Validations
    class LengthValidator < EachValidator # :nodoc:
      MESSAGES  = { is: :wrong_length, minimum: :too_short, maximum: :too_long }.freeze
      CHECKS    = { is: :==, minimum: :>=, maximum: :<= }.freeze

      RESERVED_OPTIONS = [:minimum, :maximum, :within, :is, :too_short, :too_long]

      def initialize(options)
        if range = (options.delete(:in) || options.delete(:within))
          raise ArgumentError, ":in and :within must be a Range" unless range.is_a?(Range)
          options[:minimum], options[:maximum] = range.min, range.max
        end

        if options[:allow_blank] == false && options[:minimum].nil? && options[:is].nil?
          options[:minimum] = 1
        end

        super
      end

      def check_validity!
        keys = CHECKS.keys & options.keys

        if keys.empty?
          raise ArgumentError, "Range unspecified. Specify the :in, :within, :maximum, :minimum, or :is option."
        end

        keys.each do |key|
          value = options[key]

          unless (value.is_a?(Integer) && value >= 0) || value == Float::INFINITY || value.is_a?(Symbol) || value.is_a?(Proc)
            raise ArgumentError, ":#{key} must be a nonnegative Integer, Infinity, Symbol, or Proc"
          end
        end
      end

      def validate_each(record, attribute, value)
        value_length = value.respond_to?(:length) ? value.length : value.to_s.length
        errors_options = options.except(*RESERVED_OPTIONS)

        CHECKS.each do |key, validity_check|
          next unless check_value = options[key]

          if !value.nil? || skip_nil_check?(key)
            case check_value
            when Proc
              check_value = check_value.call(record)
            when Symbol
              check_value = record.send(check_value)
            end
            next if value_length.send(validity_check, check_value)
          end

          errors_options[:count] = check_value

          default_message = options[MESSAGES[key]]
          errors_options[:message] ||= default_message if default_message

          record.errors.add(attribute, MESSAGES[key], errors_options)
        end
      end

      private
        def skip_nil_check?(key)
          key == :maximum && options[:allow_nil].nil? && options[:allow_blank].nil?
        end
    end

    module HelperMethods
      # Validates that the specified attributes match the length restrictions
      # supplied. Only one constraint option can be used at a time apart from
      # +:minimum+ and +:maximum+ that can be combined together:
      #
      #   class Person < ActiveRecord::Base
      #     validates_length_of :first_name, maximum: 30
      #     validates_length_of :last_name, maximum: 30, message: "less than 30 if you don't mind"
      #     validates_length_of :fax, in: 7..32, allow_nil: true
      #     validates_length_of :phone, in: 7..32, allow_blank: true
      #     validates_length_of :user_name, within: 6..20, too_long: 'pick a shorter name', too_short: 'pick a longer name'
      #     validates_length_of :zip_code, minimum: 5, too_short: 'please enter at least 5 characters'
      #     validates_length_of :smurf_leader, is: 4, message: "papa is spelled with 4 characters... don't play me."
      #     validates_length_of :words_in_essay, minimum: 100, too_short: 'Your essay must be at least 100 words.'
      #
      #     private
      #
      #     def words_in_essay
      #       essay.scan(/\w+/)
      #     end
      #   end
      #
      # Constraint options:
      #
      # * <tt>:minimum</tt> - The minimum size of the attribute.
      # * <tt>:maximum</tt> - The maximum size of the attribute. Allows +nil+ by
      #   default if not used with +:minimum+.
      # * <tt>:is</tt> - The exact size of the attribute.
      # * <tt>:within</tt> - A range specifying the minimum and maximum size of
      #   the attribute.
      # * <tt>:in</tt> - A synonym (or alias) for <tt>:within</tt>.
      #
      # Other options:
      #
      # * <tt>:allow_nil</tt> - Attribute may be +nil+; skip validation.
      # * <tt>:allow_blank</tt> - Attribute may be blank; skip validation.
      # * <tt>:too_long</tt> - The error message if the attribute goes over the
      #   maximum (default is: "is too long (maximum is %{count} characters)").
      # * <tt>:too_short</tt> - The error message if the attribute goes under the
      #   minimum (default is: "is too short (minimum is %{count} characters)").
      # * <tt>:wrong_length</tt> - The error message if using the <tt>:is</tt>
      #   method and the attribute is the wrong size (default is: "is the wrong
      #   length (should be %{count} characters)").
      # * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>,
      #   <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate
      #   <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
      #
      # There is also a list of default options supported by every validator:
      # +:if+, +:unless+, +:on+ and +:strict+.
      # See <tt>ActiveModel::Validations#validates</tt> for more information
      def validates_length_of(*attr_names)
        validates_with LengthValidator, _merge_attributes(attr_names)
      end

      alias_method :validates_size_of, :validates_length_of
    end
  end
end

パッと見「ハテ?」という気持ちにもなりますが(笑)、自分たちが普段Modelで定義しているのは、最初の方に書いてある以下の部分ですね。

MESSAGES  = { is: :wrong_length, minimum: :too_short, maximum: :too_long }.freeze
CHECKS    = { is: :==, minimum: :>=, maximum: :<= }.freeze

この MESSAGESCHECKS が使われているところから読んでみたらわかりやすかったです。

あとは、 validate_each メソッドの中にある、以下の部分でデータの length を出しているんだな、とか。

value_length = value.respond_to?(:length) ? value.length : value.to_s.length

そしてこの後に続いて、値のチェックをしたり、エラーを出したりしてるんだなー、とか。

CHECKS.each do |key, validity_check|
  next unless check_value = options[key]

  if !value.nil? || skip_nil_check?(key)
    case check_value
    when Proc
      check_value = check_value.call(record)
    when Symbol
      check_value = record.send(check_value)
    end
    next if value_length.send(validity_check, check_value)
  end

  errors_options[:count] = check_value

  default_message = options[MESSAGES[key]]
  errors_options[:message] ||= default_message if default_message

  record.errors.add(attribute, MESSAGES[key], errors_options)
end

RailsにおけるModelのvalidationは日々当たり前に使っている部分ですが、より身近なものに感じることができました。

Pythonの素人がDjangoアプリケーションをlocalhostで立ち上げるまで

これまで全くPythonは触ったことなかったのですが、機械学習ブームだったり、Ruby25のイベントでMatzさんがPythonの話をしてたりしたので、すこし興味がありました。
RubyPHPJavaScriptと同じスクリプト言語だし、ちょっと見てみたいな、と思ったところで、こんな本が出ていました。

www.instagram.com

スラスラ読める Pythonふりがなプログラミング

「ふりがなプログラミング」シリーズは話題になっていたので個人的に欲しいなと思っていたのですが、せっかくなら未履修の言語をやってみたいと思ったので、JavaScriptではなくPythonの方を買ってみました。

内容はプログラミング未経験者向けなので、変数や関数、リストやループなどの基本的なしくみが、Pythonの文法も含めて、しっかり解説されていました。
このあたり、自分は学生時代にJavaの入門書で独学したのですが、ここまでやさしくは説明されていなかったので、結構大変だった記憶があります。いい時代になったなあ。

Pythonの文法は、SlimやSass(not Scss)、CoffeeScriptなどのように基本閉じなくてよくて、インデントやスペースで制御されている感じなのが楽でいいなーと思いました。
あとは前述の通りスクリプト言語なので、普段RubyJavaScriptをやっている自分でも、ハードルに感じた内容は特になかったように思います。解説もやさしかったし。

Djangoを使ってみる

さて、本題に戻ります。Djangoですね。
今のところ自分は機械学習に対してハードルを感じているので、RailsやLaravelみたいなアプリケーションなら触れそうだと思ったのが、Djangoを触ってみようと思ったきっかけです。

ただ、Ruby on RailsやVue CLI、Nuxt.jsみたいな爆速initに慣れてしまった身には、Djangoの公式ドキュメントが少しハードル高めに感じたので(笑)、最終的にはDjango Girls Tutorialを参考に進めていくのが良かったです。

並行して、日本語では以下の記事を参考にさせていただきました。

qiita.com


pythonのインストール

自分は別のプロジェクトの関係で direnvpyenv を使っていました。 なので、まずは pyenv を使って3系のPythonをインストールしました。

$ pyenv install 3.6.1

次に、プロジェクト用のディレクトリを作成します。

$ mkdir django-tutorial
$ cd django-tutorial

direnv を使うために、以下の記述のある .envrc を作成します。

pyenv local 3.6.1

あとは、以下のコマンドを使えば、このプロジェクトの中だけでpython3.6.1を動かすことができるようになります。

$ direnv allow

Python仮想環境のためのvirtualenv導入

ここでは以下の記事を参考にさせていただきました。

qiita.com

上記の通りに進めていきます。

$ sudo easy_install virtualenv

# すでにプロジェクト用のディレクトリ内にいるため、カレントディレクトリで実行
$ virtualenv --no-site-packages .
$ source bin/activate

Djangoプロジェクトの作成

Django Girls Tutorialにしたがって、Djangoをインストールします。
Django自体は、もっと新しいバージョンが出ているようですね。

$ pip install django~=1.11.0

Djangoのプロジェクトを作成します。

$ django-admin.py startproject mysite .

migrateします。

$ cd mysite
$ python manage.py migrate

ここまで進んだら、以下のコマンドでローカルサーバーを立ち上げることができます。 http://127.0.0.1:8000/ から確認してみましょう。

$ python manage.py runserver

ローカル環境を立ち上げるのはここまでですが、Django Girls Tutorialではmodelやviewの作成など、実際にPythonを用いて定義していく部分も含まれますので、Pythonを文法をサッと学んだ身としては、ここからが醍醐味だなあ、という感じです。

Electronのwebviewでブラウザ版Slackを見ようと挑戦して詰んだ話

f:id:c5meg1012:20180624123438p:plain

公式アプリがあるのに、どうして車輪の再発明なことをわざわざやろうと思ったのか?
それは、これがやりたかったから。

qiita.com

もちろん、会社Slackなどの重要事項が流れてくるTeamではやりませんが、
基本ROMってるTeamで、SNS疲れみたいな気持ちになりたくないなーというのがありまして。
あと、Electronが未履修なので一回触っておきたいな、と思いました。

そういうことで、まずはElectronを動かしてみます。

electronjs.org

上記の公式サイトでQuick Startをやってみます。

# Clone the Quick Start repository
$ git clone https://github.com/electron/electron-quick-start

# Go into the repository
$ cd electron-quick-start

# Install the dependencies and run
$ npm install && npm start

こんな感じで動きました。超簡単!

f:id:c5meg1012:20180624123605p:plain

続いて、webviewを試してみましょう。
以下は公式ドキュメントです。

electronjs.org

こちらにある以下の部分を、ルート直下にある index.html にコピーしてみます。

<webview id="foo" src="https://www.github.com/" style="display:inline-flex; width:640px; height:480px"></webview>

不要なところは削除して、こんな感じ。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World!</title>
  </head>
  <body>
    <webview id="foo" src="https://www.github.com/" style="display:inline-flex; width:640px; height:480px"></webview>
    <script>
      // You can also require other files to run in this process
      require('./renderer.js')
    </script>
  </body>
</html>

再度 npm start で動かしてみます。

f:id:c5meg1012:20180624123720p:plain

おお、GitHubのページが見られましたね!
webviewの幅や高さが微妙なので、CSSを追加します。

html {
  width: 100%;
  height: 100%;
}
body {
  margin: 0;
  padding: 0
  height: 100%;
}

CSSを読み込むため、 index.html<link rel="stylesheet" href="style.css"> を追加します。
そして、webviewの src 属性を、 https://www.github.com/ から https://slack.com/signin に変更してあげます。

これで再度 npm start してみましょう。

f:id:c5meg1012:20180624123803p:plain

おお、なんかそれっぽくなってきました。
試しにログインしてみます。けれど、サインインした瞬間にフリーズしますね...😭

ひとまず表題の件については今回はここまで。
フリーズの原因がわかったら続きをやっていきたいと思います。笑

Vuepressをサッとビルドして、Netlifyでサッとデプロイしてみた

すごく今更感があるのですが、先日Vuepressを立ち上げてみたので、雑にその様子を書いてみます。
最初はVuepressで、こっそり掃き溜めブログでも作ろうかなって思ったんだけど、Kibunlogというアプリが掃き溜めにとてもよかったので、Vuepressでの公開は特にしていません。

KibunLog:気分ログ日記-楽しく続くメンタルケアSNS

KibunLog:気分ログ日記-楽しく続くメンタルケアSNS

  • Haruka Murakami
  • ヘルスケア/フィットネス
  • 無料

Vuepressのビルドについては公式サイトの通り。

# install
yarn global add vuepress # OR npm install -g vuepress

# create a markdown file
echo '# Hello VuePress' > README.md

# start writing
vuepress dev

# build to static files
vuepress build

これでサッとビルドができます。

ディレクトリ構成や設定ファイルについては以下が参考にできます。
.md ファイルが記事なので、ファイルを増やすと記事も増やせます。

vuepress.vuejs.org

デプロイについても公式ドキュメントがあります。
自分は今回Netlifyを使いました。

vuepress.vuejs.org

まず、Netlifyにデプロイするために、Vuepressのプロジェクトで git init して、GitHubリポジトリにPushします。
また、今回自分はNetlifyを使うのが初めてだったので、GutHubのアカウントと連携して、Netlifyのアカウントを作成しました。

www.netlify.com

Netlifyでアカウントを作成してログインをしたら、右上に「new site from Git」ボタンがあるのでそれをクリックします。

f:id:c5meg1012:20180609141408p:plain

デプロイしたいVuepressのリポジトリを選択して、デプロイ設定に進みます。

ヘッダーメニューの「Settings」に進んで、左サイドバーの「Build & deploy」の「Edit settings」からデプロイの設定ができます。

f:id:c5meg1012:20180609141442p:plain

Vuepress公式ドキュメントのこちらを参考に、Netlifyを設定します。

vuepress.vuejs.org

f:id:c5meg1012:20180609141511p:plain

これで、GitHubリポジトリにpushをすると自動デプロイされるようになっていると思います。
pushしてデプロイがうまくいったかどうかは、Netlifyのヘッダーメニューの「Overview」から確認できます。

f:id:c5meg1012:20180609141528p:plain

以上、サッとビルドしてサッとデプロイしたときの雑なメモでした。
Vuepressについての日本語記事でとてもいいなーと思ったのは、有料記事にはなってしまいますがCodeGridの@nakajmgさんの記事です。
サッと作るだけではなく、Vuepressをきちんと作り込みたいなって思った時に参考になると思います!

app.codegrid.net

ダイエットアプリ、睡眠アプリ、英語アプリの話

ゆるゆるダイエットをはじめました

きっかけはこれです。

あと、温泉入る前に体重計に乗ったら増えすぎててビックリしました。

ということで、こちらを導入しました。

Noom - AI とコーチが健康管理、食事記録、歩数計連携

Noom - AI とコーチが健康管理、食事記録、歩数計連携

  • Noom, Inc.
  • ヘルスケア/フィットネス
  • 無料

やってることとしては以下です。

  • 大江戸線六本木駅で階段を使う
  • AppleWatchとアプリを連携して運動量を記録
  • 体重を毎日記録
  • 食事を毎回記録
    • 朝はカロリーメイトハーフサイズ
    • 昼はサラダと焼き魚
    • 夜は自炊(ライザップご飯のレシピ本を参考)

こんな感じ。
まだはじめたばかりだけど、2キロくらい減っています。
まあ、まだ誤差の範囲内なので、油断せずに続けます。

AppleWatchで睡眠を測りはじめました

ここしばらく寝つき / 寝起きがあまり良くない気がしているので、睡眠を測りはじめました。

AutoSleep Watchを使って睡眠を自動で追跡します

AutoSleep Watchを使って睡眠を自動で追跡します

  • Tantsissa
  • ヘルスケア/フィットネス
  • ¥360

睡眠の質は悪くなさそうだと安心したら、すこし寝つき / 寝起きが改善された気がします。
上手く眠れてないと思い込んでて悪循環だったのかもしれません。

英語の勉強を習慣づけています

それから、英語の勉強もサボり気味だったので、リマインダー通知できちんと行動管理するようにしました。
英語の勉強は以下のアプリでやっています。
ある程度慣れてきたら英作文もできるようなものを試して、そのあと英会話をやってみようと思っています。

発音の練習

ELSA Speak - Accent Reduction

ELSA Speak - Accent Reduction

  • Elsa Corp
  • 教育
  • 無料

聞き取り・ディクテーション

英単語

スタディサプリ 英単語

スタディサプリ 英単語

  • Recruit Co.,Ltd.
  • 教育
  • 無料

リマインダー

HabitMinder

HabitMinder

  • Funn Media, LLC
  • ヘルスケア/フィットネス
  • 無料

一気にいろいろはじめすぎて、全部破綻しそうな予感がめっちゃしているけれど、うまくいくと楽しいし、ひとつくらいできなくても落ち込みません。

最近、なにかの数字を見るのが好きだな、と思うのです。 家計簿とか、カロリーとか。

内定を辞退してTwitterで転職活動をしました

先駆者であるひよこ大佐(@hiyoko_taisa)さまに敬意と感謝をこめて。


こんばんは。めるです。表題の通りです。

年末の時点で次に行く(はずだった)会社が決まりまして、1月末で退職をしていました。
しかし、最終出社1日を残したあたりで状況が一転、内定を辞退して転職活動を再開することになりました。
内定を辞退した理由については、直接聞いてもらうか、時期でお察しください。。

有給を使いきっていたのもあり、もともとバッファで2週間だけ無職になることにしていましたが、それにしても早く職に就かねばならない、ということでTwitterで求職してみることにしました。

Twitterで求職しようと思ったのは

  • 一度内定が出ていて、個人的に満足のいく年収だったので、それベースで年収交渉できそうだった
  • もう退職することになっているので、現職に内緒で転職活動をやる必要がなかった

という理由があります。これがなかったらやってないです。

思い切ってツイートしてみたところ、リプライ、DM、Eメール、Facebookなどで、30社ほどの企業さまよりお声がけいただきました。
お声がけいただいた皆さま、本当にありがとうございました。

まずはテキストベースで

  • 技術スタック
  • 組織体制
  • 想定されるポジション

などをヒアリングさせていただいて、実際にお会いさせていただいた企業さまは10社くらいになります。

もともと「バッファの2週間」だったので、沖縄行ったり、親知らず抜いたり、デブサミに行ったりしていたため、全部で3週間くらいかかりましたが、そういうのがなければ2週間でいけたんじゃないかな、と思っています。

自分はコードレビューの記事の件もあり、とても残念なことに、技術力よりもSNS力や知名度のほうが先行してしまっている傾向があるため(早くなんとかしないと...)、普通では難しいと思われる企業さまからも、Twitterのおかげでお声がけいただけたりしました。
あとは、互いのニーズがある程度一致したうえで初回面談できるのがよかったです。

改めて決まった転職先については落ち着いたらまた書きますが、実は、年末に一度内定が出たくらいの時期に、旦那(@polidog)経由でお声がけいただいていた企業さまなので、Twitterきっかけではないところで決まってしまったという。。

でも、Twitter経由でお会いした企業さまも、全部本当に素敵な企業さまだったので、今までの転職活動の中で一番有意義なものになったと思います!!ほんとに!!

改めて、今回の転職活動でお声がけくださった皆さま、リツイートしてくださった皆さま、そして旦那さまも(笑)、本当に本当にありがとうございました。

Vue.jsでインクリメンタルサーチを作った

これは Vue.js #2 Advent Calendar 2017 の24日目です。

qiita.com

こんばんは、める(@c5meru)です。
日付まわっちゃいました、ごめんなさい…><
今回、あまり技術的なことを細かく書けない題材にしてしまったので、Qiitaではなく、はてブでカンタンに書かせていただきます。

なんでインクリメンタルサーチを作ったの?

自分が現在のお仕事で作っているサービスは、『JOB LIST』という、アルバイト・パートを中心とした求人情報のサイトです。
タウ◯ワークやバイ◯ルさんのような一般的な求人広告と、それに加えて、ユーザー投稿の「求人張り紙」が掲載されています。

あ、よかったらアプリから「求人張り紙」投稿してみてくださいね、ポイント集めるとAmazonギフト券にできるので。
それから、求人を出したい方は、こちらからセルフで作って無料で出すこともできますので、よかったらどうぞ。
と、サービスの宣伝はおいといて。

前々から、このサービスの検索部分で、インクリメンタルサーチを採用してみたいなーと思っていたのですが、諸般の事情で出来ずにいます。

とはいえ、いろんな予約サイトとかフリマ系アプリなどは、インクリメンタルサーチをガンガン使っているので、即時で検索ワードが見られるのって結構メリットなんじゃないかなーと思うのです。

インタラクティブなUIを、お仕事に関連するところで作ってみたいなーという気持ちはずっとあって、今年のうちに消化しちゃいたいな、と思って、作ってみました。

作ったもの

苦労したポイントは?

取り込むデータ形式

まあ、本当にお仕事でやるならAPIになるんでしょうけど、その場合も、どういうデータをやりとりするべきなのか、きっちり考える必要がありそう。
今回は漢字だけのサジェストで作りましたが、実際やるなら、ふりがなとかもサジェストできた方がいいですよね。

マルチキーワード対応

今回は愚直にsplitと、マルチキーワードかどうかのフラグを用意して、、という感じで作りました。
時間がなくて実装しきれなかった、キーワード削除機能でも、結構ややこしくなりそうなところです。
複数のキーワード管理、もうちょっとうまいやり方がありそう。経験のある方に、知見をいただきたいです。

まとめ

インクリメンタルサーチを実装するのは初めてでしたが、Vue.js公式のドキュメントと、Codegridの記事がとても参考になりました。

パッと思いついたワードを打ち込むとソレっぽいワードが出てくるのは嬉しいし、制作陣が意図する検索方法と、ユーザーがやりたい検索方法が、うまい感じにマージできるのかな、と思いました。

あとは、まあ、時間がなかったので、けっこう突貫工事になってしまったな、という感じです。
最近Rubyに重きを置いていたので、もともとないJS力がさらに下がっており、些細な事で詰んでたりしました。反省。
来年はもっとレベルアップして、いい記事が書けるようにがんばります(`・ω・´)