Madogiwa Blog

主に技術系の学習メモに使っていきます。

【開発効率UP】Emacsキーバインドでコンソール操作を快適にしよう!

みなさん、こんにちは。まどぎわです(・∀・)
コンソール操作って結構めんどくさいですよね。。。

例えばコマンドを打ち間違えてしまった場合、普通にやると下記のようになるんじゃないかな?と思います。

f:id:madogiwa0124:20180819183742g:plain

しかしEmacsキーバインドを使うと、下記のように修正できます(・∀・)

f:id:madogiwa0124:20180819183715g:plain

上と比べてみても結構快適そうですよね?🙌

実際に開発してるときは、コンソールで操作してるときも多いと思うので、コンソール操作を効率的に行えると開発効率も上がるんじゃないかなと思います。

Emacsキーバインドは、MacのPCであればで特にインストール等を不要で使えますし、コンソールだけじゃなくてブラウザ操作やEditorでも使えるので、ぜひ使ってみてください(・∀・)

Emacsのよく使うコマンド

よく使いそうなコマンドを下記に記載してみました、Emacsにはまだまだいろいろなコマンドがありますが、とりあえずコレだけ覚えておくだけでも大分快適になるんじゃないかな?と思います👀

コマンド 動作
ctrl + f 次の文字へ
ctrl + b 前の文字へ
ctrl + n 次の行へ
ctrl + p 前の行へ
ctrl + a 行頭へ
ctrl + e 行末へ
ctrl + k カーソル以降を削除

fとかbとかnとか一見覚えにくいなぁと思いますが、fはforward、bはback、nはnextと、意味から推測出来る単語の頭文字を取っているので、使っていると自然に覚えやすいと思います🙌

ちなみに、最初の例は下記のような操作を行っています。

  1. 入力ミス
  2. ctrl + aで行頭へ移動
  3. ctrl + fで修正部分まで移動
  4. 訂正し、再実行

f:id:madogiwa0124:20180819183715g:plain

EmacsキーバインドをEditorで使う

Emacsキーバインドを覚えるとEditorでも使いたくなってしまうのですが、メジャーなEditorにはだいたいEmacsキーバインド拡張機能があるようです。みなさんも使ってみては?👀

おわりに

今日は、コンソール操作を効率化する方法としてEmacsキーバインドを紹介しました。

コマンドも意味のある名前の頭文字になっていて、インストール不要で使えるので、そこまで学習コストを掛けずに覚えられるので、開発効率UPになるんじゃないかと思います!(・∀・)

みなさんも、是非使ってみてください🙌

参考

Emacsのキーバインド覚書

qiita.com

qiita.com

Ruby:ログ出力を支援するsppというgemを作ってみた💎✨

こんにちは、まどぎわです(・∀・)
今回はsppというログ出力を支援するGemを公開したので、それについて書こうと思います🙌
(ちなみに初めてRubyGemsにコードを公開してみました...!)

ちなみに今回リリースしたGemは下記です。

spp | RubyGems.org | your community gem host

github.com

使い方はこんな感じ

Spp::spp('hoge')
=>
========== START ==========
"hoge"
========== E N D ==========

使う前には、Gemfileに下記を追記するか、gem install sppを実行するだけです👀

gem 'spp'

ログを見ながらデバッグするときとか、こんな感じに便利かなぁと思います(..)
下記のような感じでやると取得したユーザー一覧の取得処理をログ上で目立たせることができます🙌

  def index
    @quizzes = if params[:search_text]
                 Quiz.includes(:choices).search(params[:search_text])
               else
                 Quiz.includes(:choices)
               end
    Spp::spp(@quizzes.pluck(:title).uniq)
  end

こんな感じで目立つ(・∀・)

========== START ==========
["テスト", "タイトル", "HUNTERXHUNTERクイズ", "プロレスクイズ", "テストあああ", "私についてのQuiz"]
========== E N D ==========

ちなみに下記のような感じで、修飾をカスタマイズすることもできますφ(..)

  def index
    @quizzes = if params[:search_text]
                 Quiz.includes(:choices).search(params[:search_text])
               else
                 Quiz.includes(:choices)
               end
    Spp::spp(@quizzes.pluck(:title).uniq, '開始', '終了', '' * 5)
  end

こんな感じ(・∀・)

⚡⚡⚡⚡⚡ 開始 ⚡⚡⚡⚡⚡
["テスト", "タイトル", "HUNTERXHUNTERクイズ", "プロレスクイズ", "テストあああ", "私についてのQuiz"]
⚡⚡⚡⚡⚡ 終了 ⚡⚡⚡⚡⚡

内部的にはシンプルで、下記コードが実行されているだけなのですが、毎回書くのがめんどくさかったのでGemにしてみました🙌

puts "========== START =========="
pp 'hoge'
puts "========== E N D =========="

ちなみにこんな感じで書くと、

  def index
    Spp::spp_bench do
      @quizzes = if params[:search_text]
                  Quiz.includes(:choices).search(params[:search_text])
                else
                  Quiz.includes(:choices)
                end
      @quizzes.pluck(:title).uniq
    end
  end

ベンチマークも取れます💪

========== START(2018-08-09 22:12:16 +0900) ==========
   (5.1ms)  SELECT "quizzes"."title" FROM "quizzes" LEFT OUTER JOIN "choices" ON "choices"."quiz_id" = "quizzes"."id"
  ↳ app/controllers/quizzes_controller.rb:12
["テスト", "タイトル", "HUNTERXHUNTERクイズ", "プロレスクイズ", "テストあああ", "私についてのQuiz"]
========== E N D(2018-08-09 22:12:16 +0900) ==========

同じように修飾もできます(・∀・)

⚡⚡⚡ START(2018-08-09 22:15:06 +0900) ⚡⚡⚡
   (1.3ms)  SELECT "quizzes"."title" FROM "quizzes" LEFT OUTER JOIN "choices" ON "choices"."quiz_id" = "quizzes"."id"
  ↳ app/controllers/quizzes_controller.rb:12
["テスト", "タイトル", "HUNTERXHUNTERクイズ", "プロレスクイズ", "テストあああ", "私についてのQuiz"]
⚡⚡⚡ E N D(2018-08-09 22:15:06 +0900) ⚡⚡⚡

みなさんも良かったら使ってみてくださいm( )m

ソースコードや詳しい使い方はこちら github.com

Gemの作り方

Gemの作り方は下記記事を参考にさせて頂きましたm( )m
意外と簡単に作れるので、みなさんも作ってみては?🙌

morizyun.github.io

qiita.com

masarakki.github.io

ハマったところ

一応gem公開時にハマった箇所もメモしておきますφ(..)メモメモ

rake release時の権限Error

bundle exec rake release実行時に下記のようなErrorが出てハマりました(T_T)

You do not have permission to push to this gem. Ask...

原因は、単純にgemの名前が重複していて、既存のgemにpushしようとして権限Errorになってました。。。
重複しないようにコードの内のgemの名前を指定している箇所を置換して解決しました。

Webエンジニア転職4ヶ月目振り返り

みなさん、こんにちは!(・∀・) 4月からSIerからWeb系の企業に転職して、あっという間に4ヶ月ぐらいが経ちました。

やっと試用期間が明けて、すこしホッとしていますが、最近は自分がメインエンジニアで関わるサービスもできて、いろいろと大変なことも多いですが、毎日楽しくやってます。

つくったPRの一覧

増加STEP数 削減STEP数 作成PR数
3745 1490 38

入社からいままでの合計STEP数(増加+削減)は5235でした!稼働日(80日)で割ると1日に65STEPぐらいは1日にコード書いて過ごしてます(・∀・)

振り返り

前にも書いたけど、給料をもらいながら毎日コードをかけるのは幸せなことだなぁと思いますね。 徐々になれてきたので、今まで迷惑をかけて来た分ぐらいは、チームやサービスに貢献できるようになりたいです。

出来るようになったこと

  • ruby / railsを使った基礎的なプログラミング
  • active jobを使った非同期処理
  • shrineを使った画像アップロード機能
  • rspec / capybaraを使った自動テスト
  • Vue.jsを使った簡単なcomponentの作成

まだできていないこと

  • webpack等のフロント関連技術への理解
  • オブジェクト指向を深く意識したプログラミング
  • サービス成長への貢献

次の3ヶ月間でやること

  • フロント関連への理解
  • サービスを成長させるための施策(グロースハック等)
    • グロースハック、GA関連の書籍を2冊以上読む
  • オブジェクト指向を意識せずとも実践出来るように

焦らずにしっかりと成長して、今年中に一人前のエンジニアになりたい・・・!φ(..)

Ruby:mini_magickを使って画像に文字(テキスト)を合成する

みなさん、こんにちは。まどぎわです(・∀・)
今日は、mini_magicと使って画像にテキストを合成する方法をメモしておきますφ(..)

mini_magicを使うと某匿名質問サービスみたいに画像に文字を合成する機能を結構簡単に作ることが出来ます🙌

↓公式のリポジトリはこちら↓

github.com

mini_magicの使い方

準備

mini_magicを使うには、ImageMagickというアプリがインストールされている必要があります。 まずはインストールされているかどうか確認してください。

$ convert -version
Version: ImageMagick 6.9.9-40 Q16 x86_64 2018-07-14 http://www.imagemagick.org
Copyright: © 1999-2018 ImageMagick Studio LLC
License: http://www.imagemagick.org/script/license.php
Features: Cipher DPC Modules
Delegates (built-in): bzlib freetype jng jpeg ltdl lzma png tiff xml zlib

インストールされていなかったかたは、「ImageMagick インストール」等でググッってインストールしてください(..)

mini_magicのインストール

mini_magicも通常のGemと同じようにGemfileに追記するか、下記コマンドを実行することでインストールすることが出来ます。

gem 'mini_magick'

文字列を受け取って画像を生成してみる

背景画像を準備

まずは文字列を埋め込む背景画像を用意します。今回は、こんな感じのを用意しました。 f:id:madogiwa0124:20180721163042p:plain

文字列を受け取って画像に合成する処理を作ってみる

実際の実装を下記に記載しておきますφ(..)

class ImageHelper
  require 'mini_magick'
  require 'securerandom'

  BASE_IMAGE_PATH = './app/assets/images/bg_image.png'.freeze
  GRAVITY = 'center'.freeze
  TEXT_POSITION = '0,0'.freeze
  FONT = './app/assets/fonts/komorebi-gothic.ttf'.freeze
  FONT_SIZE = 65
  INDENTION_COUNT = 11
  ROW_LIMIT = 8

  class << self
    # 合成後のFileClassを生成
    def build(text)
      text = prepare_text(text)
      @image = MiniMagick::Image.open(BASE_IMAGE_PATH)
      configuration(text)
    end

    # 合成後のFileの書き出し
    def write(text)
      build(text)
      @image.write uniq_file_name
    end

    private

    # uniqなファイル名を返却
    def uniq_file_name
      "#{SecureRandom.hex}.png"
    end

    # 設定関連の値を代入
    def configuration(text)
      @image.combine_options do |config|
        config.font FONT
        config.gravity GRAVITY
        config.pointsize FONT_SIZE
        config.draw "text #{TEXT_POSITION} '#{text}'"
      end
    end

    # 背景にいい感じに収まるように文字を調整して返却
    def prepare_text(text)
      text.scan(/.{1,#{INDENTION_COUNT}}/)[0...ROW_LIMIT].join("\n")
    end
  end
end

こんな感じで、使うことが出来ます(・∀・)

# 生成した画像のFileClassを取得
ImageHelper.build('何かしらの文字列を合成してみる').tempfile.open.read
# 生成した画像の書き出し
ImageHelper.write('何かしらの文字列を合成してみる')

↓出来た画像がこちら↓ f:id:madogiwa0124:20180721164012p:plain

おわりに

設定周りのやり方を調べるのに結構苦労しましたが、実装は意外とシンプルですね(・∀・)
ちなみに私も最近作ったサービスでも使ってみました🙌
↓みたいなQuizを投稿して画像でシェア出来るサービスです(・∀・) quiz-quiq.herokuapp.com

参考資料

keruuweb.com

github.com

RubyonRails:current_userへのメソッド呼び出しで発生したN+1に対応する

まどぎわです(・∀・)今日は、current_userへのN+1問題の対応についてです!
railsで開発していると、N+1問題にぶつかって対応することが多いかと思います。 普通であれば、DBアクセス時にincludes等を使って関連モデルをキャッシュしておくことで対応することが多いかと思います。

しかし、current_userのメソッド呼び出しでN+1問題が発生してしまった場合、だいたいDBアクセス処理はライブラリで行われているため、DBアクセス時に関連モデルをキャッシュしようとしても、「どうすれば、いいんや・・・」という気持ちになり、対応方法を調べたのでメモしておきますφ(..)

前提

今回は、下記のようなUserモデルとQuestionモデルとQuestionAnswerモデルがあり、Userモデルに#answerd?というUserがその質問に回答済みかどうかを判定するメソッドを定義しています。
何も対応しないとQuiz一覧画面等で、このメソッドを呼び出すとN+1問題が発生してしまいます。。。

class User < ApplicationRecord
  has_many :question_answers

  def answerd?(question)
    question.question_answers.to_a.select{ |answer| answer.user_id == id }.present?
  end
end

class QuestionAnswer < ApplicationRecord
  belongs_to :user
  belongs_to :question
end

class Question < ApplicationRecord
  has_many :question_answers
end

対応方法

current_userをもとにUserインスタンスを再生成する

controller内でcurrent_userをもとにUserインスタンスを再生成し、その際にincludesで関連モデルをキャッシュするようにします!これにより、#answerd?を呼び出してもキャッシュされた値から処理が行われるので、N+1問題を防ぐことが出来ます🙌

class QuizzesController < ApplicationController
  def index
    @user = User.includes(:question_answers).find(current_user.id)
    @questions = Quiz.includes(:choices)
  end
end

参考

stackoverflow.com

RubyonRails:rails5.2の新機能「ActiveStrage」で画像アップロード処理をお手軽に実装してみる

Rails5.2でActiveStrageというファイルアップロード系の機能が新しく追加されましたね(・∀・)
ちょっと自分が作っているアプリで使ってみたので使い方をφ(..)メモメモ

ActiveStrageとは?

ActiveStrageとは、Rails5.2から実装されたファイルアップロード機能です。
今までは画像等をアップロードするときにはCarrierWaveShrineといったGemを使うことが多かったと思いますが、それがRailsだけで簡単に出来るようになりました🙌

railsguides.jp

ActiveStrageを使ってみた

ソースだけ見たい方はこちら👀

github.com

準備

まずはActiveStrageを使うために下記コマンドを実行します。

$ rails active_storage:install
$ rails db:migrate

そして、ファイルのアップロード先を設定します。アップロード先はconfig.active_storage.serviceに設定されたkeyでstarage.yamlを参照します。

strage.yml

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

development.rb

  # Store uploaded files on the local file system (see config/storage.yml for options)
  config.active_storage.service = :local

ファイルをアップロードしてみる

まずはModelにファイルのプロパティを定義します。1つのファイルをアップロードする場合はhas_one_attached、複数のファイルをアップロードする場合はhas_many_attachedを使用します。

class User
  has_one_attached :avatar
end

次にcontrollerとviewを次のように実装します。
新規登録の場合は、パラメーターにavatorを追加してnewの引数に渡してあげるだけでいいのですが、既存レコードにファイルを設定する場合は.avatar.attachを使用する必要があります。

controller

  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to @user, notice: message('user', 'create')
    else
      render :new
    end
  end

  def update
    @user = User.find(current_user.id)
    @user.avatar.attach(params[:user][:avatar])
    if @user.update(user_params)
      redirect_to @user, notice: message('user', 'update')
    else
      render :edit
    end
  end

  private

  def user_params
    params.require(:user).permit(:email, :name, :password, :password_confirmation, :avatar)
  end

view

= form_for @user do |f|
  %div.form-group
    = f.label :avatar
    = f.file_field :avatar, class: 'file-inputs'
  .actions
    = f.submit t('common.save'), class: 'btn btn-primary'

これで、画面からアップロードが出来るようになりました!!(・∀・)

バリデーションを実装してみる

しかし、このままでは画像以外がアップロードされてしまったり、大きなファイルがアップロードされてしまうといったことが防げません。。。
ActiveStrageは、まだvalidationの機能が無いので、自分で実装する必要があります😥

今回は、サイズのチェックと画像のみをアップロード可能とするようなバリデーションをサンプルとして載せましたので、参考までに。

class User < ApplicationRecord
  validate  :file_validation, if: -> { avatar.attached? }

  private

  def file_validation
    if avatar.blob.byte_size > 1_000_000
      file_raise_error('のファイル容量が大きすぎます')
    elsif !avatar.blob.content_type.starts_with?('image/')
      file_raise_error('は、画像以外はアップロード出来ません')
    end
  end

  def file_raise_error(message)
    avatar.purge
    errors.add(:avatar, message)
  end
end

おわりに

ActiveStrageはシンプルなファイルアップロード機能を実装するのであれば、お手軽に実装出来てよさそうに感じました(・∀・)
しかし、バリデーションを独自で実装する必要があるため、あまり複雑なアップロード処理にはまだ向かなそうですね😥

参考

tech.unifa-e.com

railsguides.jp

stackoverflow.com

関連モデルの関連モデルの読み込み処理を行うN+1問題を解決する。

関連モデルの関連モデルを参照する場合のN+1問題の解消方法で少しハマったのでφ(..)メモメモ

問題

例えば、下記のようなClassがあった場合にtask.user.groupを取得しようとするとGroupの読み込み処理によりN+1問題が発生する。
そのためTaskを取得する際にはTask.all.includes(:user, :group)といった対策が考えられるが、groupはタスクにassociationとして定義されていないので、指定出来ない。。。

class Task < ApplicationRecord
  belongs_to :user, required: false
  belongs_to :owner, class_name: 'User'
end

class User < ApplicationRecord
  has_many :tasks
  has_one :user_group
  has_one :group, through: :user_group
end

class UserGroup < ApplicationRecord
  belongs_to :user
  belongs_to :group
end

class Group < ApplicationRecord
  has_many :user_groups
  has_many :users, through: :user_groups
end

解決策

下記のようにincludes(user: :group)とすることで、関連モデルのassociationを使ってincludesを行うことが出来る🙌

Task.all.includes(user: :group)

参考

qiita.com