投稿者「sasaki」のアーカイブ

[Ruby] モジュール(module) 入門

概要

Rubyのモジュール(module)を何となく使ってきたので、基礎を振り返りつつ、ざっくばらんにコード例を掲載する。詳しい説明は省略するので必要に応じて各自で調べるものとする。

前提

以下環境で動作確認済み

debian 9.3
ruby 2.4.1

moduleとは

Rubyにおけるモジュールは、Moduleクラスのインスタンスでうんたらであるが、ここではクラスと同じように変数やメソッドを持ってる名前空間ぐらいの認識から始める。

module MyModule
end

みたいに定義できる。

モジュールはクラスと違い

  • インスタンスを生成できない
  • 継承ができない

などの特徴がある。インスタンス作れない、継承できないでどうやって使うのよという疑問は後々。

moduleを入れ子にする

module Constant
  module User
    module Status
      INVALID = 0
      VALID   = 1
    end
  end
end

p Constant::User::Status::VALID # 1

のように、moduleを入れ子にすることで、名前空間の階層化ができる。これによって階層的な定数の管理とかがしやすくなる。

モジュールに特異メソッドを実装する

module MyModule
  def self.say_hello
    p 'Hello'
  end
end

MyModule::say_hello # Hello

のように、クラスのクラスメソッドみたいな書き方でメソッドを定義すると、モジュールの特異メソッドとして利用できるようになる。クラスに属さない汎用的なロジックとか定義すると捗る。

特異メソッドはmodule_functionメソッドで定義することも出来る。こっちのほうが一般的かも。

module MyModule
  def say_hello
    p 'Hello'
  end
  module_function :say_hello
end

MyModule::say_hello # Hello

モジュールをクラスにmixinする

モジュールはインスタンスを生成できないけど、クラスにインクルードする(mixinする)ことはできる。

module MyModule
  def say_hello
    p "Hello"
  end
end

class MyClass
  include MyModule
end

MyClass.new.say_hello # Hello

クラスにモジュールをincludeすると、モジュールのメソッドがあたかもクラスのインスタンスメソッドであるかのように利用できる。

Rubyはクラスの多重継承を行うことができないが、includeは複数のモジュールに対して行えるので、一つのクラスに複数の機能を取り込むような使い方ができる。

module FooModule
  def say_foo
    p 'Foo!!'
  end
end

module BarModule
  def say_bar
    p 'Bar!!'
  end
end

class MyClass
  include FooModule
  include BarModule
end

obj = MyClass.new
obj.say_foo # Foo!!
obj.say_bar # Bar!!

逆に、一つのモジュールを複数のクラスでincludeすることもできるので、ある共通の機能を複数のクラスに持たせることができる。

module Printable
  def print_info
    p "this class is #{self.class}"
  end
end

class User
  include Printable
end

class Post
  include Printable
end

User.new.print_info # this class is User
Post.new.print_info # this class is Post

クラスをモジュールでextendする

includeでは、モジュールのメソッドをクラスのインスタンスメソッドにすることができた。

今度はextendを使うことで、モジュールのメソッドをクラスのクラスメソッドにすることができる。

module MyModule
  def say_hello
    p "Hello!!"
  end
end

class MyClass
  extend MyModule
end

MyClass.say_hello # Hello!!

mixin(include,extend)の継承

クラスの継承時に、親クラスのmixin(include,extend)はそのまま引き継がれる

module IncludeMyModule
  def print_class
    p "this class is #{self.class}"
  end
end

module ExtendMyModule
  def say_hello
    p "Hello!!"
  end
end

class ParentClass
  include IncludeMyModule
  extend  ExtendMyModule
end

class ChildClass < ParentClass
end

ParentClass.new.print_class # this class is ParentClass
ChildClass.new.print_class  # this class is ChildClass

ParentClass.say_hello # Hello!!
ChildClass.say_hello  # Hello!!

[Debian] ターミナル越しに日本語入力をできるようにする

概要

Dockerで建てたDebian環境に、日本語入力ができない問題が起こって少しだけハマったので備忘録を残す。

日本語入力が出来ないというのは、以下のようにIMEによる全角文字の入力が消えてしまう問題を指す。

本記事はDebianによる問題解決で、Ubuntuの場合は以下の方法で同様の問題を解決できる。(Dockerで無くとも)
[Docker] Ubuntuのコンテナ内で日本語入力ができない | qs Developers

前準備

この手の作業を行う前はapt-get updateをしておくと良い

$ apt-get update

解決策1 (失敗)

最初はDebian/Ubuntuのデフォルトロケールを変更する – Qiita の、debianの例にそってイケルと思った。

が、環境が悪いのか、

$ apt-get install -y task-japanese
Reading package lists... Done
Building dependency tree
Reading state information... Done
E: Unable to locate package task-japanese

となったり、task-japaneseインストール成功後も、

$ locale-gen
bash: locale-gen: command not found
$ update-locale LANG=ja_JP.UTF-8
bash: update-locale: command not found

と、上手く行かなかったので、色々調べて少し違う方法を模索

解決策2 (成功)

必要なパッケージ入れる

$ apt-get install -y locales locales-all

vimで以下ファイルを開いて

$ vim /etc/locale.gen

以下のコメントアウトを外して保存

# ja_JP.UTF-8 UTF-8

ローケルを設定

$ locale-gen
$ update-locale

bashrcにローケル情報を追記

$ echo "export LANG=ja_JP.UTF-8" >> ~/.bashrc

bashrcを再読込

$ source ~/.bashrc

入力できるようになった

参考

[Rails] rspec + factory_bot + database_cleaner で、APIのテストを書く

概要

Ruby on rails で開発中のAPIサーバのテストを、rspecを用いて行うまでにやったことの備忘録。
本記事では、APIのテストにrspecを、テスト用のデータの生成にfactory_botを、テスト前後でデータベースを初期化する処理をdatabase_cleanerで行う。

前提

以下環境で動作確認

ububntu 16.04.2
ruby 2.4.1
rails 5.1.2
rspec 3.7.1
factory_bot 4.8.2
database_cleaner 1.6.2

以下の状態まで出来上がっていることを想定

  • Railsによる何らかのAPIが存在する
    — 特定のモデルがある
    — GET /api/[モデル名] で、モデルの一覧を取得できる
  • bundleでgemを管理している

Ruby on Railsによる開発環境構築及びチュートリアルについては以下参照

rspecによるテストコード作成については以下参照

本記事の目的

本記事では、前提の条件から発展させて、以下のことをできるようにする

  • rspecを用いて、APIのアクション単位でのテストを書けるようにする
  • テストの対象となるデータは、FactoryBotを用いてテストコード内で動的に作成する
  • テストの対象となるデータは、テスト内だけで使用し、テスト終了後に全て削除するようにする
  • テストの対象となるデータは、開発用とデータベースをわけ、開発用データに影響を与えないようにする

必要なgemのインストール

Gemfileを以下のように編集する。既に何らかのgemが記述済みの場合、消す必要はない。

group :development, :test do
  gem 'rspec-rails'
  gem 'factory_bot_rails'
  gem 'database_cleaner'
end

インストール

$ bundle install

rspecの初期設定

ジェネレータを使ってrspecの初期ファイルを自動生成する

$ bundle exec rails generate rspec:install
      create  .rspec
      create  spec
      create  spec/spec_helper.rb
      create  spec/rails_helper.rb

生成された spec/rails_helper.rbに、以下の一行を追加することで、テストコード中でのFactoryBotの名前空間指定を省略できるようにする

RSpec.configure do |config|
  # 中略
  config.include FactoryBot::Syntax::Methods
end

さらに、 spec/spec_helper.rb に、以下を追加することで、テストごとに生成されたテストデータを自動で削除する仕組みを導入する。

RSpec.configure do |config|
  # 中略
  config.before(:suite) do
    DatabaseCleaner.strategy = :truncation
  end

  config.before(:each) do
    DatabaseCleaner.start
  end

  config.after(:each) do
    DatabaseCleaner.clean
  end
  # 以下略
end

Factoryの作成

テストデータを作成してくれるモジュール(Factory)を作成する。本記事ではFactoryBotを利用する。基本的なFactoryBotの使い方は以下がオススメ

本記事では、名前と誕生日を持ったUserモデルが存在しているという体で、Userモデルを生成してくれるFactoryを作成する。

spec/factoriesディレクトリを作成し、その中にusers.rbを作成する。

$ mkdir factories
$ cd factories
$ vim users.rb

users.rbでは、factory_botを用いて、Userモデルを生成するためのFactoryを定義する。生成されるユーザは、名前が”TEST_NAME_1″,”TEST_NAME_2″…と、生成毎に連番になるようし、誕生日は固定で2017-01-01になるように定義する。

FactoryBot.define do

  factory :user do
    sequence(:name) { |n| "TEST_NAME_#{n}" }
    birthday '2017-01-01'
  end

end

軽く動作確認をすると以下のようになる。

irb(main):013:0* FactoryBot.build(:user).name
=> "TEST_NAME_1"
irb(main):014:0> FactoryBot.build(:user).name
=> "TEST_NAME_2"
irb(main):015:0> FactoryBot.build(:user).name
=> "TEST_NAME_3"
irb(main):016:0> FactoryBot.build(:user).name
=> "TEST_NAME_4"

テスト用データベースの作成

Railsでは、開発用データベースとテスト用データベースと、本番用データベースを分けて使用することができる。プロジェクト名がappであるとき、app_testが、テスト用データベースになる。

以下のコマンドを用いて、テスト用のデータベースを作成してマイグレーションを実行する。シーダが存在する場合は別途シーダも回す。

$ bundle exec rails db:migrate RAILS_ENV=test

テストコードの作成

APIのテストコードは、specディレクトリ内のrequests以下に作成し、さらに今回はモデル単位でディレクトリを分けるので、usersディレクトリもまとめて作成する。usersディレクトリ内で、index_spec.rbを作成し、このファイルにて、usersの一覧取得APIのテストを記述する。

$ mkdir requests/users -p
$ cd requests/users
$ vim index_spec.rb

index_spec.rbでは、テスト実行時に先程作成したFactoryを用いて、ユーザを100人作成してから、APIにて100ユーザを取得できることを検証する。

RSpec.describe 'Users', type: :request do

  FactoryBot.create_list(:user, 100)

  describe "GET /api/users" do
    it '全件取得できる' do
      get api_users_path
      expect(JSON.parse(response.body).count).to eq 100
    end
  end

end

テストの実行

テストは、Railsのルートディレクトリに戻って、以下のコマンドで実行できる。(-f d は出力フォーマットの指定。無くてもテストは実行できる)

$ bundle exec rspec -f d

Users
  GET /api/users
    全件取得できる

Finished in 0.40263 seconds (files took 1.92 seconds to load)
1 example, 0 failures

テストでは、ユーザが100人居ることを検証したが、開発用DB及びテスト用DBには影響が残っていないことが確認できる。

mysql> use app_development;
Database changed
mysql> select count(*) from users;
+----------+
| count(*) |
+----------+
|       27 |
+----------+
1 row in set (0.00 sec)

mysql> use app_test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> select count(*) from users;
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (0.00 sec)

もし実行時に以下のようなエラーが出た場合

$ bundle exec rspec -f d

An error occurred while loading ./spec/requests/users/index_spec.rb.
Failure/Error: FactoryBot.create_list(:user, 100)

NameError:
  uninitialized constant FactoryBot
# ./spec/requests/users/index_spec.rb:3:in `block in <top (required)>'
# ./spec/requests/users/index_spec.rb:1:in `<top (required)>'
No examples found.
$ bundle exec rspec -f d
bundler: failed to load command: rspec (/root/appname/vendor/bundle/ruby/2.4.0/bin/rspec)
NameError: uninitialized constant Rails

spec_helper.rbに、以下のような記述を追加すると解決するかも

ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'

RSpec.configure do |config|
  # 中略
  config.include Rails.application.routes.url_helpers
end

参考

3年目プログラマが参考にしたQiita記事まとめ⑮

概要

特にカテゴリもレベルも偏らずにざっくばらんに、参考になったQiita記事を一言添えて列挙するシリーズ14本目

Qiitaまとめシリーズ

PHPを使いもせずDISってる君達へ

PHPの擁護記事かと思ったらボロクソ行ってて笑えた。と同時に、PHPのドハマリしそうなポイントを抑えることができた。

なぜ仮想DOMという概念が俺達の魂を震えさせるのか

Reactを必死に勉強してた頃に目を通して意味わからなかったけど、今ならそれなりに理解できる。何でReactがここまで成り上がったか、やっぱり仮想DOMの影響が大きいんだと言うことがわかった。今はReactよりVueのほうが使ってるけど、Vueも仮想DOMなのでこの記事の内容はそのまま使うことができる。

ハッシュをソートしてハッシュで返す(Ruby)

痒いところに手が届いた。ハッシュは本来順番を持たない概念だからこういうことをやろうとするのがミスだと思うけど、今のRubyのハッシュは順序をしっかり持ってるので捗る。

フロントエンドエンジニアが暇なときにやると良いかもしれないこと

情報収集が受け見ガチなのでより能動的に情報拾いに行きたいね。少なくとも言われたから調べるとかは辞めたい。

カオスになりがちなCSSと向き合ってみた

あるある。スタイルをABC順で統一するの良いなぁ。自動化できると尚良いけど、組織レベルでやるとしたら何らかの気風が必要そう。ビルドツールでまとめて行えるのが一番いいけど。

最近Webサイトやアプリを使ってイラッとしたUI/UX

SPAはこれが難しいなぁ。しっかりエラー対応付けないと、ブラウザレベルでユーザにエラーの報告してくれないからわけわかんない感じになる。

pry-byebugでrubyをデバッグする

この記事読んでpryデビューした。ずっと素直にirb使ってきたけど、pryは素で便利。もっと早く乗り換えておけばよかった。

[Vue.js] フォームの多重送信を防止する

防止用のボタンをコンポーネントで切り出す考えは良いな。これは率先して使っていきたい。

一つ上のチームメンバーのそだてかた

コーチングは将来役立つものだろうし覚えておくとして、エンジニアのレベルの評価方法は参考になった。紙に書いた適当なスキルシートとか、面談通して口頭でエンジニアのレベルを測れるはずがない。やっぱりエンジニアに求められるのは何よりアウトプット。

テストがなかった無法地帯にテストを導入して開発速度を1.7倍にした話

テストもだけどレビューに関しても結構振れられててソッチのほうが参考になったりした。

今までのレビュー目的は「バグを減らすため」でしたが、再整理の結果、「知識共有と可読性の検査」へと変わりました。

人のコードを積極的に読まないと知識は偏るし、人に読ませるようにコードを書かないと保守性の悪いコードが生まれる。互いにもっとコードを読み合うような仕組みが合ってしかるべきなんだけど。

今回は、自動テストの狙いを「開発速度を上げること」に絞り、手動動作確認よりも自動テストの方が素早く確認できる箇所にのみ自動テストを導入することに決めました。

誤解されがちだけど、自動テストはコードの品質を高めるとかじゃなく(それもあるけど) 最終的な開発速度を上げることが目的。長期的な視点の場合、テストを書くことで工数が増えるというよりはテストを書くことで工数が減るはず。まともな設計がある前提だろうけど。

まぁ自動テストにしろコードレビューにしろ、コードは疎結合に書こうねというのは共通してる。

[JavaScript] moment.jsで日付時刻操作を行うTips

概要

JavaScriptのdateオブジェクトは中々に使いづらい。moment.jsはそれを非常に使いやすい形にラッピングしたモジュールである。本記事では、私はmoment.jsを使って業務で役立った使い方を備忘録代わりにまとめる。

前提

以下環境で動作確認済み

node 5.12.0
npm 3.8.6
moment.js 2.19.4
Chrome 63.0

moment.jsのインストール

本記事ではnpmを使う

$ npm install --save moment

moment.jsを使う

const moment = require('moment')

あるいは

import moment from 'moment'

現在時刻のmomentオブジェクトを生成

momentオブジェクトはjavaScriptのdateオブジェクトをラッピングしたオブジェクト

moment()
// moment("2018-02-04T14:06:32.809")

時刻文字列からmomentオブジェクトを生成

moment('1992-05-29')
// moment('1992-05-29 13:10:15')
moment('1992-05-29 13:10:15')
// moment("1992-05-29T13:10:15.000")

dateオブジェクトからmomentオブジェクトを生成

moment(new Date())
// moment("2018-02-04T14:17:30.672")

momentオブジェクトからdateオブジェクトを生成

moment().toDate()
// Sun Feb 04 2018 14:17:13 GMT+0000 (UTC)

年月日や時刻を取得

moment()
// moment("2018-02-04T14:12:57.506")
moment().year()
// 2018
moment().month()
// 1 ← 1月が0なので注意
moment().date()
// 4 ← dayだと曜日番号になるので注意
moment().day()
// 0 ← 日曜日が0

年月日や時刻を設定

moment()
// moment("2018-02-04T14:14:05.528")
moment().year(1992).month(4).date(29).hour(13).minutes(10).second(20)
// moment("1992-05-29T13:10:20.900") ← 月が1ずれるので注意

相対日付を取得

現在時刻から見た1年後の3日前を取得する。
durationメソッドで「期間」を用意しておき、momentオブジェクトに対してaddメソッド、subtractメソッドを用いて相対的なmomentオブジェクトを取得できる。

const oneYear = moment.duration(1, 'years')
const threeDay = moment.duration(3, 'days')
moment()
// moment("2018-02-04T14:10:48.701")
moment().add(oneYear).subtract(threeDay)
// moment("2019-02-01T14:47:10.419")

日付と日付の差分を取得する

momentオブジェクトのdiffメソッドで、対象となるmomentオブジェクトを渡してあげると、その差を秒で取得することができる。

const from = moment('1992-05-29')
const to = moment()
to.diff(from)
// 810658205231

diffメソッドに第二引数を指定すると、形式を指定できる。

to.diff(from, 'year')
// 25 ← 差が25年
to.diff(from, 'day')
// 9382 ← 差が9382日

フォーマットを指定して文字列を取得

moment().format('YYYY-MM-DD')
// '2018-02-04'
moment().format('hh:mm:ss')
// '02:07:14'

[vue] vue-chartjsのreactiveDataを用いてリアクティブにグラフを描画する

概要

グラフ描画ライブラリのJavaScriptライブラリであるChart.jsを、Vueから使いやすくしたラッパーライブラリであるvue-chartjsを用いて、グラフをリアクティブに描画するサンプル。

前提

debian 8.6
node 5.12.0
vue 2.4.1
chart.js 2.7.1
vue-chartjs 3.1
  • webpackを用いた、単一ファイルコンポーネントによるVueの利用を前提とする
  • vue及びchartjsに関する解説は割愛する

目的

以下のGif動画のように、スライダによるデータの変更に応じて、リアクティブに棒グラフを描画する。

グラフ描画用のデータは、下記のようにコンポーネントへのプロパティに、データ本体のみを指定して使えるようにする。

<graph-sample :data1="40" :data2="30"/>

モジュールのインストール

グラフ描画用のchart.jsと、それをVueで利用するvue-chartjsをnpmでインストールする

$ npm install --save vue-chartjs
$ npm install --save chart.js

※ vue-chart.jsだけインストールすればchart.jsへの依存を解決してくれるハズだが、どうにも手元の環境で上手く解決できなかったのでchart.jsも別途入れるように対応した

親コンポーネントの実装

親コンポーネントは、2種類のスライダと、そのスライダの値をプロパティにしたグラフ用コンポーネントを描画する。

なお、本記事ではvue用のUIライブラリであるelement-uiのスライダコンポーネントを用いているが、本記事の本筋とは関係ないため詳細は割愛する。

また、グラフ描画用のコンポーネントは、同ディレクトリのGraphSample.vueに存在するものとする。

<template>
  <div>
    <el-slider v-model="data1" :min="0" :max="100" />
    <el-slider v-model="data2" :min="0" :max="100" />
    <graph-sample
      :data1="data1"
      :data2="data2"
    />
  </div>
</template>
<script>
  export default {
    data: function() {
      return {
        data1: 40,
        data2: 80,
      }
    },
    components: {
      GraphSample: require('./GraphSample')
    }
  }
</script>

グラフ描画用コンポーネントの実装

  • vue-chartjsは、そのコンポーネント自体をグラフコンポーネントにしてくれるので、templateタグを記述する必要はない
  • vue-chartjsから、Barオブジェクトをimportし、それをミックスインすることで棒グラフを利用できる
  • vue-chartjsから、reactiveDataオブジェクトをimportし、それをミックスインすることでdataを監視してリアクティブにグラフを再描画できるようになる
  • reactiveDataをミックスインすると、chartDataというグラフ描画用データが定義され、それを書き換えることでグラフも再描画される
  • mountedで最初にグラフ描画用データのベースを定義する
  • 親コンポーネントから、スライダの変更に応じてプロパティが書き換わるので、それをwatchして、変更をchartDataに反映させる
<script>
  import VueCharts from 'vue-chartjs'
  import {Bar, mixins} from 'vue-chartjs'
  export default {
    mixins: [Bar, mixins.reactiveData],
    data: function() {
      return {
        options: {
          scales: {
            yAxes: [
              {
                ticks: {
                  min: 0,
                  max: 100,
                }
              },
            ]
          },
        },
      }
    },
    props: {
      data1: {
        type: Number,
        required: true,
      },
      data2: {
        type: Number,
        required: true,
      },
    },
    watch: {
      data1: function() {
        this.updateChartData()
      },
      data2: function() {
        this.updateChartData()
      }
    },
    methods: {
      updateChartData() {
        const newChartData = Object.assign({}, this.chartData)
        newChartData.datasets[0].data = [this.data1]
        newChartData.datasets[1].data = [this.data2]
        this.chartData = newChartData
      },
    },
    mounted: function() {
      this.chartData = {
        datasets: [
          {
            label: 'data1',
            data: [this.data1],
          },
          {
            label: 'data2',
            data: [this.data2],
          },
        ],
      }
    }
  }
</script>

デモ(再掲)

備考

  • reactivePropを用いたリアクティブなグラフ描画の方法はググると沢山出てくるのに、reactiveDataを用いたものが余り無かったので、ソースコードを読んで試してみた。

  • reactivePropを用いた場合、プロパティにグラフ描画用データ全体を指定する必要があり、今回の用に棒グラフの値本体のみをプロパティで指定する仕組みにしたい時に使いづらかったので、reactiveDataを使う方法を採用した

  • かなり我流を含んでいる気がする。もっと楽な方法でリアクティブな描画ができるかも。

VimGolfでvimの練習をしてvim力を向上させる

概要

vimを用いてお題に沿ったテキスト編集を、どれほど少ないキーストロークで実現できるかを競うWebサービス及びツールであるVimGolfに初挑戦する。本記事では、アカウントを登録して、適当な問題を1問回答後、結果をアップロードするまでの流れを確認する。

vimGolf(vimゴルフ) とは

vimGolfは、お題として指定されたテキスト編集を、どれほど少ないキーストローク数で実現できるかを競うWebサービス及びツールである。

vimはカーソル移動やテキスト編集を効率的に行う術が豊富であるため、それらを巧みに使いこなすことで、複雑なテキスト編集も非常に少ないキーストローク数(タイプ数)で実現することができる。vimGolfでは、誰が最も少ないキーストローク数でテキスト編集を行えるかを競う。

問題を自分の力で創意工夫して解いても良いし、人間を辞めてそうなvimmerがアップロードしたハイセンスなキーストロークの記録を見て勉強するのも良い。とにかくvim力を向上させて日頃のテキスト編集スピードを高めたい人にはピッタリのサービスだ。

アカウントを登録する

vimGolfに参加するためには、Twitterでの認証が必須(?)となる。
vimGolfにアクセスして、画面右側のplease sign inをクリックすることで、Twitterアカウントを用いたアカウント登録ができる。

登録が完了すると、以下のようにvimGolfKeyが取得できるので、それを控えておく。

vimGolfのインストール

vimGolfは、前述のアカウントの登録と、vimgolfコマンドのインストールを行うことで参加できる。vimgolfコマンドはgemで配布されているので、gemでインストールする。

$ sudo gem install vimgolf

vimgolfコマンドがインストールできたら、以下のようにsetupを行う。先程取得したvimGolfKeyをここで入力することで、vimGolfへの参加準備が完了する。

$ vimgolf setup

Let's setup your VimGolf key...
1) Open vimgolf.com in your browser.
2) Click "Sign in with Twitter".
3) Once signed in, copy your key (black box, top right).

Paste your VimGolf key: 0e2a216f02chogehoge90e841aca47
Saved. Happy golfing!

問題を解いてみる

今回は以下の問題を解いてみる。
Just the middle

vimGolfでは、問題毎に「元となるテキスト」と、「編集後のテキスト」が提示される。

上記の問題で言うと、「元となるテキスト」が

Leave only the
numbered lines.
LINE 1
LINE 2
LINE 3
That's all.
Thank you
very much.

で、「編集後のテキスト」が

LINE 1
LINE 2
LINE 3

である。要は真ん中の3行以外を削除しようというシンプルな問題だ。

上記URLの画面右側に書かれている、以下のコマンドを実行するとvimgolfが始まる。

$ vimgolf put 54862fbb3f90ac0002904cf5
Downloading Vimgolf challenge: 54862fbb3f90ac0002904cf5
Launching VimGolf session for challenge: 54862fbb3f90ac0002904cf5

vimgolfを開始すると、vimで「元となるテキスト」が開かれるので、自分の出来る限りの最小のキーストロークで「編集後のテキスト」を作成する。

作成完了後にファイルを保存すると、自信が行ったキーストロークの内容や、正解不正解、キーストローク数が確認できる。今回は”:v/N/d:wq“というストロークで、スコアは11だった。

結果をアップロードするのか、再挑戦するのかなどを選べるので、今回はアップロードすることにした。

Here are your keystrokes:
:v/N/d<CR>:wq<CR>

Success! Your output matches. Your score: 11
[w] Upload result and retry the challenge
[x] Upload result and quit
[r] Do not upload result and retry the challenge
[q] Do not upload result and quit
Choice> x
Uploading to VimGolf...
Uploaded entry.
View the leaderboard: http://www.vimgolf.com/challenges/54862fbb3f90ac0002904cf5

Thanks for playing!

アップロードが完了すると、該当の問題ページに、以下のように結果が公開される。

上位を確認してみると、理論上の最高スコアは9点。
どうやら:wqで保存するよりも、ZZで保存したほうがストローク数は少ないようだ。

[PHP] 配列の内部ポインタ(カーソル)の操作

概要

PHPの配列は内部ポインタ(カーソル)を持っており、配列内で現在参照している要素をシフトすることができるらしい。適当にコードを書きながら色々試してみたので整理する。

current関数

current関数は、指定した配列の内部ポインタが指している要素を戻す。ポインタ自体は動かさないので連続で呼び出しても結果は変わらない。最初は先頭の要素を示している。

$array = ['a', 'b', 'c'];
echo current($array); // a
echo current($array); // a
echo current($array); // a

next関数

next関数は、内部ポインタを一つ進めて、進めたあとに指し示している要素を戻す。これ以上進めない(=末尾要素を指している)場合はfalseを戻す。

$array = ['a', 'b', 'c'];
echo next($array); // b
echo next($array); // c
echo next($array); // false

prev関数

prev関数は、next関数の逆で、ポインタを一つ前に移動する。これ以上戻れない(=戦闘要素を指している)場合はfalseを戻す

$array = ['a', 'b', 'c'];
echo next($array); // b
echo next($array); // c

echo prev($array); // b
echo prev($array); // a
echo prev($array); // false

reset関数

内部ポインタを先頭に移動し、先頭要素を戻す

$array = ['a', 'b', 'c'];
echo next($array); // b
echo next($array); // c

echo reset($array); // a

end関数

内部ポインタを末尾に移動し、末尾要素を戻す

$array = ['a', 'b', 'c'];
echo current($array); // a
echo end($array);     // c

each関数

each関数は、現在内部ポインタが指し示している要素のキーと値のペアを戻し、内部ポインタを次に進める。
キーは[0]または[‘key’]で、値は[1]または[‘key’]で参照できる。

$array = ['a', 'b', 'c'];

var_dump(each($array)); // [0 => 0, 1 => 'a', 'key' => 0, 'value' => 'a']
var_dump(each($brrby)); // [0 => 1, 1 => 'b', 'key' => 1, 'vblue' => 'b']
var_dump(each($crrcy)); // [0 => 2, 1 => 'c', 'key' => 2, 'vclue' => 'c']
var_dump(each($array)); // false

この関数は PHP 7.2.0 で 非推奨になります。この関数に頼らないことを強く推奨します。

具体例

全然使いみちが思いつかないけど強引に使ってみた。

$user_age = [
  'Taro'   => 25,
  'Jiro'   => 19,
  'Hanako' => 14
];

while ($user = each($user_age)) {
  echo $user['key'] . 'is' . $user['value'] . "\n";
}
$ php hoge.php
Tarois25
Jirois19
Hanakois14

言うまでもなく、上記コードはforeachとかで書き直せる。

所感

直接使うことはまず無いけど、PHPの配列が内部的にこうやって動いてるということを想像するとどこかで役立つかも?

3年目プログラマが参考にしたQiita記事まとめ⑭

概要

特にカテゴリもレベルも偏らずにざっくばらんに、参考になったQiita記事を一言添えて列挙するシリーズ14本目

Qiitaまとめシリーズ

有名私立大学のホームページを計測してみた

龍谷大学の描画速度がスゴイ。それなりにコンテンツが充実しててリッチなのにこの早さはかなり工夫されてる。対して学習院は同じぐらいの情報量なのに描画に時間がかかりすぎてる。フロントエンジニアの技術力の差が出てる感じがする。

削除したツイートだけ取ってくるシステムを作ってみた

こういうの何度か作ってみようとか思ったけど、どうやら削除済みツイートを意図的に見ようとすることが規約違反になるみたい。そりゃないよ。

白っぽい文字(白,臼,日,曰,目,且,旦,亘)で動くプログラミング言語を作った

一度BrainFuckライク言語を作ってみたい。最小限のプログラミング言語の動きが学べるし、C言語で十数行で実装できるみたいだし。

(Twitterとかに)zipファイルを(画像ファイルに変換して)アップロードできるツールをつくりました

なかなかに興味深かった。デジタルだとどうしてもソレが画像であることを証明できないから必ずこういう抜け道はあるよね。

【Git】オレならこう説明する!Git初心者への用語説明

わかりやすいっちゃわかりやすいけど正確さに欠ける。まぁ最初は正確さより雰囲気に慣れることが大事だと思うけど。関係ないけどQiitaでソースツリー使ってる記事初めてみたかも。個人的には早い段階でソースツリーは脱却して欲しい。

受託でやるのに程よいベストなGitのフローを考える

とりあえずmasterを自由に更新できるのは辞めたい。masterは常にそれを本番に適用しても構わないという前提に、レビュー環境に適用するためのreleseブランチと、問題解決ごとのトピックブランチを切るようにしたい。と書いてて気づいたけどこれほぼgit-flowだ。

CSSだけでaタグのリンク先プレビューを表示する

なるほどなぁと唸る感じ。シンプルながらかなり強力そう。というかcontent属性で直接リソース指定できるのがビビる。文字列入れてそれ表示する程度のものだと思ってた。

リモートワークを始めて1年が経過し、幸福だったこと・苦痛だったこと・より幸福になるために

一長一短あるのはよくわかるけど、それでも自分はリモートワークに適してると思った。
記事中の各デメリットについて

  • 人間関係及びコミュニケーションの問題
    — 表情が見えない問題についてはSkypeなりでビデオ会議すればいい話
  • 生産性の問題
    — 日頃からやってるBacklogの自動化による実働管理を続ければ実体は変わらないと思うし
  • 精神的な問題
    — 出勤しててもそんな変わらない
  • 稼働時間の問題
    — Backlogの自動化で解決できる
  • 人生設計の問題
    — 既に嫁が居て外出も多いから問題ない。

デメリット全部問題なかった。

有志の社内勉強会を1年で50回くらいやった結果 伝えたいこと

小規模、低レベルな勉強会を運営する側に回るっててもあるのか。
何となく勉強会って講義形式か、ハンズオン形式だと思ってたからディスカッション形式なるものがあるのが意外。というか記事主的にはそれがほとんどらしい。あんまりイメージできないな。「タスク管理について」でディスカッションとか無事に進行できるのか。

人は自分が知らない物を激しく批判し、排除しようとする

極論気味だけど言いたいことはわかる。けど遠回しにこの記事もPHP批判してるようにみえるぞ。

[Ruby] RuboCopをインストールしてvimで実用化するまで

概要

Rubyの静的解析ツールであるRuboCopをインストールし、基本的な使い方と、vimからの利用方法を確認し、Rubyコーディング時のコード品質を高めるためにやったことの記録。

前提

以下環境で動作確認済み

debian 8.6
ruby 2.2.2
rubocop 0.52.1
vim 7.4

RuboCopとは

Rubyのソースコードを静的解析してくれるツール。インストール直後から特に設定などを行うこと無く、Rubyスタイルガイドを概ね踏襲したコーディング規約を強制できるようになる。

例えば以下のRubyコードに対してRuboCopを実行すると

def badName
  if something
    test
    end
end
$ rubocop
Inspecting 1 file
W

Offenses:

01.rb:1:5: C: Naming/MethodName: Use snake_case for method names.
def badName
    ^^^^^^^
01.rb:2:3: C: Style/GuardClause: Use a guard clause instead of wrapping the code inside a conditional expression.
  if something
  ^^
01.rb:2:3: C: Style/IfUnlessModifier: Favor modifier if usage when having a single-line body. Another good alternative is the usage of control flow &&/||.
  if something
  ^^
01.rb:4:5: W: Lint/EndAlignment: end at 4, 4 is not aligned with if at 2, 2.
    end
    ^^^

1 file inspected, 4 offenses detected
  • メソッド名はスネークケースにしようね
  • if文が一行の場合は後置ifを使おうね
  • endがインデントずれてるよ

と教えてくれる。

さらに、

$ rubocop --auto-correct

のように、auto-correctオプションをつけると、その一部を自動で修正してくれる。自動修正後のコードは以下の通り。

def badName
  test if something
end

見ての通り、メソッド名をスネークケースにするべきところは治ってないので、自動修正は一部にしか適用されない。また、この機能はまだ実験段階なので注意して使うようにと公式ドキュメントにも書かれている。

とにかく、Rubyのコード品質を高めたり、チーム内でのコーディングルールを強制したりするために活用することができる。

ちなみにJavaScriptにも同じコンセプトのESLintというツールがあるが、それについては以下記事で触れている。
ESLintによるJavaScriptコーディングルールの強制 | qs Developers

RuboCopのインストール

Rubyのライブラリなので当然gemでインストール可能。もちろんbundler経由でインストールもできるが、今回はグローバルインストールする。

$ gem install rubocop

インストール後、rubocopコマンドですぐに使用可能。

$ rubocop -v
0.52.1

Cop/Departmentについて

RuboCopは、Copというコーディングルールの集合で構成されている。例えば前述の例で言う「メソッド名はスネークケース」「1行のif文は後置ifを使う」「ブロックのインデントを揃える」といったのがCopである。(正確にはCopはもう少し細かいルールだがここではわかりやすく簡略化している)

個々のCopは、コーディングルールのカテゴリであるDepartmentに属している。

Departmentの一覧は以下の通り

Department Bundler

Bundlerに関する規約。「同じgemが被ってるよ」や「gemはアルファベット順に書こうね」など

Department Gemspec

Gemspecに関する規約。「アルファベット順に書こうね」など

Department Style

Rubyスタイルガイドに基づいた一貫性のあるスタイルに関する規約。「for文とかやめてeach使お」「空のelseとか辞めよう」「引数のないメソッドは括弧を省略しよう」など

Department Layout

インデントや空白、整形などの規約。「ブロックの開始と終了のインデントを揃えようね」「範囲式でスペース入れるのやめようね」「メソッドチェインを複数行で書く時は縦を揃えようね」など

Department Lint

バグの原因になりえるコードに関する規約。「ハッシュリテラルでキーが重複してるよ」「シングルコーテーションで変数展開しようとしてるよ」「無意味なアクセス修飾子ついてるよ」など

Department Metrics

ブロックの行数、1行の文字数などに関する規約。「ブロックの行数はN行まで」「一行の長さはN文字まで」「ブロックの入れ子はN段まで」など

Department Naming

命名に関する規約。「変数名はスネークケースにしてね」「メソッド名はスネークケースにしてね」「クラス名やモジュール名はキャメルケースにしてね」など

Department Performance

性能劣化になりえるコードに関する規約。「この場合sortメソッド使うよりsort_byメソッド使ったほうが早いよ」「その処理、このメソッド使ったほうが早いよ」など

Department Rails

Ruby on railsのコードに関する規約。「nil? || empty? ← blank?メソッドで済むよ」「whereつかうよりfind_byで済むよ」など

Department Security

セキュリティリスクのあるコードに関する規約。「evalとか使わないほうがいいよ」「JSON.loadよりJSON.parse使おう」など

.rubocop.ymlにRuboCopの設定を記述する

前述の通り、RuboCopには様々なCopがあり、デフォルトでもrubocopコマンドだけで多くの恩恵が受けられる。

しかし、Copの中には明示的に設定しないと有効にならないCopがあったり、デフォルトで有効だけど使いたくないCopがあったり、有効にはしたいけど一部制限したいCopなどもある。

そういった場合にRuboCopの設定ファイルになる、.rubocop.ymlを記述することで、事細かにRuboCopを制御することができる。通常は、rubocopコマンドを実行するディレクトリに配置されている.rubocop.ymlが参照される。

例えば以下のコードでそのままRuboCopを実行すると

# 標準出力するメソッド
def say
  puts '01234567890123456789012345678901234567890'
end
$ rubocop
Inspecting 1 file
C

Offenses:

01.rb:1:3: C: Style/AsciiComments: Use only ascii symbols in comments.
# 標準出力するメソッド
  ^^^^^^^^^^

1 file inspected, 1 offense detected

となり、コメントにマルチバイト文字を使っていることが指摘される。

コメントに日本語は普通に使いたいので、同ディレクトリに以下の.rubocop.ymlを作成する。

# コメントに日本語を書きたいので無効にする
Style/AsciiComments:
  Enabled: false

# 1行は40文字までにする
Metrics/LineLength:
  Max: 40

日本語コメントが使えない原因のCopを無効にして、ついでにデフォルトでは無効の1行の最大文字数Copを40文字設定で有効にする。

これでもう一度rubocopを実行すると、今度は日本語コメントに関する通知が無くなり、1行の文字数に関する通知が出るようになった。

$ rubocop
Inspecting 1 file
C

Offenses:

01.rb:3:41: C: Metrics/LineLength: Line is too long. [50/40]
  puts '01234567890123456789012345678901234567890'
                                        ^^^^^^^^^^

1 file inspected, 1 offense detected
vagrant$
vagrant$ rubocop
Inspecting 1 file
C

Offenses:

01.rb:3:41: C: Metrics/LineLength: Line is too long. [50/40]
  puts '01234567890123456789012345678901234567890'
                                        ^^^^^^^^^^

1 file inspected, 1 offense detected

vimから自動でRuboCopを実行できるようにする

ここまで、rubocopコマンドを用いて静的解析を実行していたが、どうにも一々コマンドを実行するのが面倒くさい。vimでコード編集中にリアルタイムで教えてほしい。

ということで、vimからrubocopを使うためのvimプラグインである、ngmy/vim-rubocopを導入した。

インストールはNeoBundleで行うので、vimrcに以下を追加。

NeoBundle 'ngmy/vim-rubocop'

:RuboCop コマンドで、RuboCopの実行結果をVim上に表示することができる。

一々コマンドを打つのも面倒なので、適当なキーバインドを設定するとよい。

これだけでも充分使えるが、私が普段から愛用していた汎用構文チェック用のvimプラグインであるvim-syntastic/syntastic と組み合わせることでさらに協力になる。

vimrcを以下のようにすると

NeoBundle 'ngmy/vim-rubocop'
NeoBundle 'scrooloose/syntastic.git'
set statusline+=%#warningmsg#
set statusline+=%{SyntasticStatuslineFlag()}
set statusline+=%*
let g:syntastic_always_populate_loc_list = 1
let g:syntastic_auto_loc_list = 1
let g:syntastic_check_on_open = 1
let g:syntastic_check_on_wq = 0
let g:syntastic_ruby_checkers=['rubocop', 'mri']

ファイルの保存をフックに、どの行でどの問題が起こっているかを可視化してくれる

参考