主夫ときどきプログラマ

データベース、Webエンジニアリング、コミュニティ、etc

新春bash書き初めをやった

SoftwareDesignの2017/1号の第1特集である新春bash書き初め シェル30本ノックをやった。
結果は 19/30 でまぁまぁの出来だと思う。
コマンドやbashについていろいろと新しい発見があったのでまとめておく。

各問題の自分なりの回答は GitHub においてある。
雑誌はこちら

コマンドについて

sed awk

抽出や変換など知れば知るほどなんでもできそうなコマンド。

  • sed -n '/開始パターン/,/終了パターン/p'
  • sed 'p' | sed '1d;$d' | paste
  • awk '{print}{fflush()}'

while read n; do [command] $n; done

繰り返しのイディオムで頻出。xargs とうまく使い分けよう。

openssl

暗号関連のいろんなことができるコマンド。使うことはほとんどなさそう。

crontab

コマンドというか @reboot コマンド起動時に1度、指定のコマンドを実行するという設定ができることを知った。

grep

grep -q . とすると、マッチの有無で終了ステータスが変わるので処理を分岐できる。
$ (何らかの処理) | grep -q . && (出力がある場合の処理) || (出力がない場合の処理) と出来る。
-q は標準出力に何も出さないオプション。

paste

それぞれのファイルの行を並列に結合する。

$ paste file1 file2
file1の1行目 file2の1行目
file1の2行目 file2の2行目
...

標準入力も扱える。

$ seq 1 10 | paste - -
1   2
3   4
5   6
7   8
9   10

file

ファイルの種類を推定してくれるコマンド。

tee

標準出力に出力しつつ、内容をファイルに保存する。
リダイレクトでは特権(sudo)が必要なファイルは書き込めないためteeを使うとうまくいく。
$ echo 'foo bar' | sudo tee special_file.txt

rev

反転してくれる。

$ seq 1 10 | xargs
1 2 3 4 5 6 7 8 9 10
$ seq 1 10 | xargs | rev
01 9 8 7 6 5 4 3 2 1

[

test コマンドの別名。if [ xxxx ]; でよく使われるのでオプションを覚えたほうが良い。

set env

set は現在のシェル変数、 env環境変数のみを出力する。

bashについて

${変数}

変数へ代入する時はスペースを入れてはいけない。
変数を使う時は$をつける。

$ n='hello world'
$ echo ${n}
hello world

|| && 終了ステータス判定

直前に実行したコマンド(処理)の終了ステータス($0)を判定するbash演算子
$ コマンド && コマンドが成功した時の処理 || コマンドが失敗した時の処理

<<< 標準入力へ流し込む

<<< の右側に書いた文字列や変数の値を標準入力に流し込む。

$ cat <<< 'hello world'
hello world
$ cat < 'hello world'
bash: no such file or directory: hello world

$(( 計算式 )) 算術式展開

いろいろな計算ができる。四則演算はもちろんのこと、論理和論理積、ビット演算も可能。

$ echo $(( 2 * 3 + 4 ))
10

$( コマンド ) コマンド置換

コマンドの実行結果を文字列として扱える。

$ for n in $( seq 1 5 ); do echo "羊が$n匹"; done
羊が1匹
羊が2匹
羊が3匹
羊が4匹
羊が5匹

:- 変数展開

難しいのでこちら
デフォルト値への置換ができる。

--

オプションの打ち止め。 $ mkdir -- -RF-RFというディレクトリが作成される。--以降はオプションと認識されなくなる。

$(<ファイル) コマンド置換2

コマンド置換の拡張的な機能で「ファイル」の中身に置き換えられる。

$ echo "$(<ファイル)" > foo$ cp ファイル foo と同じ。

<( コマンド ) プロセス置換

コマンドの入出力をファイルとして扱うことが出来る。

$ cat < <(echo 'hello world')
hello world

さいごに

各コマンドやbashの詳しい使い方や実例は雑誌を読んでくれ。

SoftwareDesign2017.01

2016年をふりかえる

Facebookにポストしてた2016年の目標をブログに移したのでそれをベースに2016年を振り返ります。

masayuki14.hatenablog.com

ひとつひとつ見ていきましょう。さほど意識しないで1年を過ごしてきたわけですが・・・。

2016年にたてた目標

Swift2

swift2を試す。iOsネイティブのアプリを作れるようになろう。年内にクソゲーがリリースできたらバンザイ\(^o^)/

2016年の目標 - masayuki14’s diary

宣言することは大事なもので、2月に参加したスプーキーズ沖縄合宿iOSアプリチームとなりSwiftにチャレンジすることとなりました。
夏頃からアプリ開発の案件にも参加することとなり年間通じて触れる機会に恵まれました。 Swiftの言語仕様はほぼ把握したのでコーディング自体には問題ありませんが UIKit 周りが全然わかってなくて、一人でアプリ開発することはまだまだできない感じです。

Redux

Reactで作ったアプリにReduxを導入する。

2016年の目標 - masayuki14’s diary

React 0.13 で開発したのでバージョンアップにも結構コストかかりそうだったし、サービス自体もあまり変更なく稼働していたのでそのまま。
Redux導入までには至りませんでした。今後も予定はありません。そして1年通じてほとんど React に触れませんでした。

Hubot

なんかつくる。くだらないものからやっていこう。みさわの画像出すとか。すでにありそうだけど。

2016年の目標 - masayuki14’s diary

忘れてください。ごめんなさい。

Meckerel

サーバーにMackerel入れてみる。APIコールして独自のメトリクスとりたい。

2016年の目標 - masayuki14’s diary

ごめんなさい。忘れてください。

ブログ

月に1回はブログを書く。ちゃんとアウトプットしよう。

2016年の目標 - masayuki14’s diary

月1とは言わずとも少しは書きました。せっかくなのでリンクを貼っておこう。

Rubyでメタプログラミング - 主夫ときどきプログラマ

スプーキーズの勉強会イベント一般公開します。 - スプーキーズの中の人。

おじさんの嫉妬は犬も喰わない - スプーキーズの中の人。

RubyKaigi2016 に行ってきました。 - 主夫ときどきプログラマ

暖かい季節にはモチベーションも上がるようです。

IPA試験

IPAの試験を受けてなんか合格したい。DBとネットワークあたりか。春の試験は受付中。

2016年の目標 - masayuki14’s diary

データベーススペシャリスト合格しました!おめでとう!
ネットワークスペシャリストは不合格でした!次回がんばろう!

OSSコミッタ

になろう。規模の大小は問わずGitHubでPullRequestを送ってみる。

2016年の目標 - masayuki14’s diary

https://github.com/ash1day/Copy-link-as-markdown/pull/3

知人のリポジトリですがPullRequest送ったのでこれでいいんだ!はじめの一歩はこんなもんだ!

コミュニティ活動

勉強会とか参加してみよう。

2016年の目標 - masayuki14’s diary

ブログにも書きましたがRubyKaigi2016にHelper Staff として参加しました。
今年1番の大きな出来事だと思っています。今後も京都や関西のコミュニティで活動を継続していきます。

総括

本当にやりたいと思っていることは目標にしてもしなくてもやるし、 そんなに思っても見ないことを目標にしてもあんまり達成しないのかな、という印象です。

とはいえそれほど思っても見ないことを達成するにはより目標とスケジュールを一緒に設定すればいいのかな、とも思います。 2017年もしっかり目標立てて1年後ふりかえれるように頑張ろう。

RubyKaigi2016 に行ってきました。

RubyKaigi2016が京都で開催されました。

RubyKaigiが地元で開催されるのなら行くっきゃない、ということで申し込みました。
早い段階で申し込みをしたこともあり、HelperStaff募集にも応募することができ、 HelperStaffでの初参加と相成りました。

今年はHelperの応募が少なかったとのことなので、スタッフになるとこんなにいいことがあるよ、 ということを紹介したいと思います。
再び関西で開催される時には応募が増えるといいなと思います。

開催中の様子はこちらのブログにすばらしい写真がたくさんあったので御覧ください。 hazi.jp

参加することのメリット

Rubyistの仲間が増える

会場にはほぼRuby関係者が集まりますので、自然とRubyistの仲間が増えます。 熟練のRubyistも数多くいますので、見習うべき先輩達にも出会えます。

モチベーションが上がる

Rubyはもちろんのこと、Rubyで先端技術をどう扱うかといったようなセッションが数多いので自然とモチベーションがあがります。 仕事や技術に対する意識も変わってくることでしょう。

お弁当とTシャツがついてくる

参加費は早割でも10000円しますが、3日間の昼食弁当とTシャツ(数量限定)がついてくることを考えると安いと思います。 たくさんのステッカーもゲットできるのでラップトップに貼っていい感じにしましょう。

Helper Staff のメリット

スタッフTシャツもらえる

スタッフは紺色のTシャツでした。 もちろんお弁当もおなじ。

Ruby界隈のことがわかってくる

OrganizerやStaffにはRubyコミッターの方々がいます。(今回初めて知った。)
もちろん著名なコミッターの方の基調講演やセッションも行われるのでそういった人達を知ることができます。
オフィシャルパーティーや打ち上げで彼らと話す機会もありますし、 Twitterなどをフォローすればさらにいろいろな情報を得られるでしょう。

知らない人に話しかけやすい

オフィシャルパーティーやブースでもスタッフだと話しかけやすいです。
特にパーティーで知らない人と話をするときに
「スタッフなのでぜんぜんセッション聞けてないんで、今日のおすすめあれば教えて下さい。」
というのが常套手段のようで、私もパーティーではこれで話を始めることが多かったです。

変化する運営方法が間近で見られる

発生する課題にその都度対応し、翌日にはより良くなるように皆さん動いていて 変化する運営の中にいられるのはとても刺激的でおもしろい体験でした。
同時通訳レシーバーの貸出を担当しましたが、いい感じで運営できたと思います。
やはりオフラインコミュニケーションの方が改善のスピードは圧倒的に速いかもしれせん。

オフィシャルパーティーに参加できる

定員のあるオフィシャルパーティーにスタッフ枠で参加できますし、 もちろんスタッフの打ち上げにも参加できます。
今回は京都ということもあり最終日に打ち上げを行ったようです。
セミオフィシャルな形でスタッフ以外の参加もありましたが、 東京では後日にスタッフだけで打ち上げするみたいです。

ステージに上がれる

クロージングで壇上にあがりました。
会場とても広かったしこんなところでセッションできる人って やっぱりすごいんだなーと思います。

Helper Staff のデメリット

HelperStaffになるのは基本的にいいことずくめなんですが、 やはり少しだけデメリットもあります。
でも総合的にみたらメリットしかないです。

セッションが聞けない

担当にもよりますが、セッションはあまり聞けません。
特に受付担当になると会場からも遠いのでかなり難しい状況になります。
逆に会場担当だとけっこう聞けると思います。

朝が早い

開場前に集合するので当然朝が早いです。毎日8時頃にはみなさん集まっていました。

立ちっぱなし

立ちっぱなしでいる時間が長いので疲れます。筋トレをしておきましょう。

仕事がたまる

やはり平日開催なので仕事ができません。とうぜん仕事が溜まります。
空いた時間に仕事している方もいました。

交通費・宿泊費がかかる

東京から来ている方は当然それなりの費用がかかります。
このあたりをバックアップいただける企業さんは本当にありがたいと思います。
だいたいスタッフさんはこういった企業で活躍されている方のようでした。

変なテンションになった

仕事したくない
Rubyコード書きたい
のんびりしたい
でもお金欲しい

欲望に素直になりました。
2週間たっても変わらない。

おわりに

公式サイトで各セッションの動画が配信されています。
すばらしいセッションの数々を御覧ください!

おすすめはJustinさんの基調講演で、リファクタリングがしたくなります。
絵文字の使い方がとてもじょうず。

www.youtube.com

RubyKaigi はOrganizerの皆さん、多くのスポンサー企業、そしてこれまでの実績を積み上げてきた 過去に尽力頂いたたくさんの方のおかげで今年も開催できているのだなと感じました。
すべての皆さんに感謝したいと思います。ありがとうございました。

f:id:masayuki14:20160910174716j:plain

また京都で開催されますよーに。

Rubyでメタプログラミング

勉強会でRubyについて話す機会があったのでその資料を公開します。
勉強会についてのエントリはこちら。 スプーキーズの勉強会イベント一般公開します。 - スプーキーズの中の人。

メタプログラミングとは?

コードを記述するコードを記述すること

メタプログラミング (metaprogramming) とはプログラミング技法の一種で、ロジックを直接コーディングするのではなく、あるパターンをもったロジックを生成する高位ロジックによってプログラミングを行う方法、またその高位ロジックを定義する方法のこと。主に対象言語に埋め込まれたマクロ言語によって行われる。

by wikipedia

It's Magic!

Rubyを使ってDBにアクセスするプログラムを書いてみよう。 今回書くのはActiveRecordを使ってmoviesテーブルにアクセスするクラスだ。

class Movie < ActiveRecord::Base
end

Wow, たったこれだけ!

movie = Movie.new
movei.title = '燃えよドラゴンズ!'
movie.title                  # => '燃えよドラゴンズ!'
movie.save

ActiveRecord はテーブルを知ってるの?

知りません

Movie#title()Movie#title=() を呼び出しているが、これらのソースコードはどこにもない。 どこにも定義されてないとするとどうやって存在するんだい?

これこそメタプログラミングがやってくれるんだ!!

実行時にメソッドを定義している

ActiveRecord は実行時にクラス名を調べ簡単な規約を適用します。 例えば Movie クラスであれば movies テーブルにマッピングします。 そしてデータベーススキーマを読み取り title カラムがあることを見つけ、この属性のアクセッサメソッドを定義しています。

プログラムの実行中にこっそりと定義してるのだ!!

メタプログラミングするには

魔法のようなメタプログラミングをするには魔術書が必要!

オープンクラス

文字列からアルファベットと数字以外を除外したい。

def to_alphanumeric(s)
    s.gsub /[^\w\s]/, ''
end

to_alphanumeric("#hello, it's magic number 3*?")     # => "hello its magic number 3"

でもこのメソッドはオブジェクト指向的じゃないよね?

文字列自身に変換してもらおう

class String
  def to_alphanumeric
    gsub /[^\w\s]/, ''
  end
end

"#hello, it's magic number 3*?".to_alphanumeric()    # => "hello its magic number 3"

いつだってクラスを再オープンできる

class A
  def x; 'x'; end
end

class A
  def y; 'y'; end
end

obj = A.new
obj.x       # => 'x'
obj.y       # => 'y'

class キーワードはクラス宣言というよりはスコープ演算子のようなもので、class を使ってクラスコンテキストに行きそこでメソッドを定義する。つまりいつでもクラスを再オープンしてその場で修正ができる。

モンキーパッチ

同じ要領で配列要素を置換するメソッドを定義しよう

def replace(array, from, to)
  array.each_with_index do |e, i|
    array[i] = to if e == from
  end
end
# Arrayクラスに再定義
class Array
  def replace(from, to)
    each_with_index do |e, i|
      self[i] = to if e == from
  end
end

これをプロダクトのコードでやったらおかしなことになってしまうぞ!

既存のメソッドを上書きしてしまった

> [].methods.grep /^re/
 => [:reverse_each, :reverse, :reverse!, :reject, :reject!, :replace, .....

独自の replace() メソッドでうっかり元の replace() メソッドを上書きしてしまった。 こうしたクラスへの安易なパッチに否定的な人には蔑称で モンキーパッチ って言われちゃうぞ!

これが オープンクラスのダークサイドだ。

メソッドを知る

Rubyでは動的なメソッド呼び出しやメソッド定義が可能だ。

Object#send() メソッド

メソッドを呼び出すには通常ドット記法を使う。

class Person
  def saba(my_age)
    my_age - 5
  end
end

obj = Person.new
obj.saba(35)           # => 30

Object#send() メソッドでも呼び出すことができる。

obj.send(:saba, 45)    # => 40

動的ディスパッチ

obj.saba(35)           # => 30
obj.send(:saba, 45)    # => 40

どちらのコードも saba() を呼び出してるが、後者は send() を使っている。これはメソッドで第1引数はメソッド名、その他の引数(とブロック引数)はそのままメソッドに渡される。

つまりメソッドを実行するメソッドというわけだ。

呼び出したいメソッド名が通常の引数になるから実行時に呼び出すメソッドを直前に決められる。これを動的ディスパッチと呼ぶ。

メソッドを動的に定義する

Module#define_method() を使えば、メソッドをその場で定義することができる。メソッド名とブロックを渡す必要があり、ブロックがメソッドの本体となる。

class Spookies
  define_method :muscle do |name|
    "#{name}, さぁ今日も筋トレだ!"
  end
end

spoo = Spookies.new
spoo.muscle("アッシー")        # => アッシー, さぁ今日も筋トレだ!

動的メソッド

通常defキーワードを使って定義するメソッドをModule#define_method()で定義している。これはメソッドを定義するメソッドだ。

define_method()Spookiesのなかで実行され、muscle()Spookiesインスタンスメソッドとして定義される。実行時にメソッドを定義するこのテクニックは動的メソッドと呼ばれる。

エイリアス

エイリアスのメソッドもまたメソッドだ

def deserves_a_look?(book)
  amazon = Amazon.new
  amazon.reviews_of(book).size > 20
end  

例外処理が考慮されてないメソッドをなんとかしたい。 こんな時はこのメソッドをラップして、機能を追加して、全てのクライアントが新しく追加した機能を自動的に使えるようにしたらいい。

メソッドエイリアス

alias キーワードを使えば、 Rubyのメソッドにエイリアス(別名)をつけられる。

class Pizza
  def cheese
    'cheese !'
  end
  alias :formaggio :cheese


  def tomato; 'tomato!'; end
  alias pomodori tomato
end

p = Pizza.new
p.cheese         # => "cheese !"
p.formaggio      # => "cheese !"
p.pomodori       # => "tomato!"
  • aliasはキーワード
  • 新しい名前が前、古い名前が後でカンマ不要
  • シンボルでもいいし素の名前でもいい
  • メソッドではない
  • 同じ動きをするModule#alias_method()メソッド
  • String#size()String#lengthエイリアス

メソッドを alias して再定義すると何が起きるだろうか?

class String
  alias :real_length :length

  def length
    real_length > 5 ? 'long' : 'short'
  end
end

"I'm so hungry".length            # => "long"
"I'm so happy!".real_length       # => 13

このコードはString#length()を再定義している。しかしエイリアスは元のメソッドを参照している。 メソッドの再定義とは、元のメソッドを変更するのではなく、新しいメソッドを定義して、元のメソッドの名前をつけているわけだ。

アラウンドエイリアス

class Fixnum
  alias :old_plus :+

  def +(value)
    self.old_plus(value).old_plus(1)
  end
end

3 + 5        # => 9
4 + 10       # => 15

新しい +()old_plus()の周りをラップしている。 これをアラウンドエイリアスと呼ぶ。

  1. メソッドにエイリアスをつける
  2. 新しいメソッドを定義する
  3. 新しいメソッドから古いメソッドを呼び出す

でもどんなときに使ったらいいの?

事例紹介

HTMLページを量産したい

  • 飲食チェーンの広告ランディングページ
  • 100店舗すべて別ページを作る
  • 基本デザインは同じ
  • 店舗固有のデータ(店名や住所など)はCSVで管理

テンプレートとデータを読み込んで各店舗のページを出力するようなツールが欲しい

イメージはこんな感じ

f:id:masayuki14:20160428121521p:plain

まずはCSVを読み込もう

新米プログラマの例

store_id store_name address
102 おばけベーカリー 新宿店 新宿区新宿2丁目・・・
103 おばけベーカリー 渋谷店 渋谷区宇田川町・・・
110 おばけベーカリー 池袋店 豊島区東池袋1丁目・・・
line_number, rows = [ 0, {} ]
CSV.foreach('data.csv') do |line|
  line_number += 1

  rows << {
    store_id: line[0],
    store_name: line[1],
    address: line[2],
  }
end

これってイケてないよね

  • データの追加や順番が変わるたびにコードの修正が必要
  • store_id などの項目名が生かせてない

こんな感じにしたい!

row = SupponRecord.new(line)

row.store_id       # => 102
row.store_name     # => おばけベーカリー 新宿店
row.address        # => 新宿区新宿2丁目・・・

オープンクラスと動的メソッド

CSVファイルの1行目(項目名)を使ってアクセッサを動的に定義してやればいい!

class SpooRecord

  ### オープンクラスの同類
  # 特定のインスタンスを再オープンして定義を追加します
  # この中で定義されたメソッドはクラスメソッドになります
  class << self

      ### 属性の定義
      # attributes = ['store_id', 'store_name'] の場合
      # store_id, store_id=, store_name, store_name= のアクセッサを定義する
      def define_attributes(attributes)

        attributes.each do |attr|
          attr_accessor attr # attrへのアクセッサを定義
        end
    end
  end
end

irb で実行してみよう

2.2.1 :020 >   SpooRecord.define_attributes( ['id', 'name', 'address'] )
 => ["id", "name", "address"]
2.2.1 :021 > r = SpooRecord.new
 => #<SpooRecord:0x007ffcc1046378>
2.2.1 :022 > r.id = 100
 => 100
2.2.1 :023 > r.name = 'Spookies'
 => "Spookies"
2.2.1 :024 > r.address = '京都市'
 => "京都市"
2.2.1 :025 > p r
#<SpooRecord:0x007ffcc1046378 @id=100, @name="Spookies", @address="京都市">
 => #<SpooRecord:0x007ffcc1046378 @id=100, @name="Spookies", @address="京都市">
2.2.1 :026 >

動的ディスパッチで値を設定する

2行目以降のデータを対応する項目値として設定する

class SpooRecord
  class << self

    # indexと項目名の対応を保持, 1 => store_id
    # クラスにアクセッサが定義される
    # SpooRecord.index_to_attr にアクセスできる
    attr_accessor :index_to_attr 

    def define_attributes(attributes)
      @index_to_attr ||= {}

      attributes.each_with_index do |attr, index|
        attr_accessor attr # attrへのアクセッサ
        @index_to_attr[index] = attr # indexと項目名の対応
      end

    end
  end

  # コンストラクタ
  def initialize(data)
    data.each_with_index do |value, index|
      attr = self.class.index_to_attr[index] # クラスメソッドの `index_to_attr` で読込
      self.send("#{attr}=", value) # 動的ディスパッチ!!
    end
  end
end

irb で実行してみよう

2.2.1 :023 >   SpooRecord.define_attributes( ['id', 'name', 'address'] )
 => ["id", "name", "address"]
2.2.1 :024 > r = SpooRecord.new( [100, 'Spookies', '京都市'] )
 => #<SpooRecord:0x007fd9c10945c8 @id=100, @name="Spookies", @address="京都市">
2.2.1 :025 > p r
#<SpooRecord:0x007fd9c10945c8 @id=100, @name="Spookies", @address="京都市">
 => #<SpooRecord:0x007fd9c10945c8 @id=100, @name="Spookies", @address="京都市">

出力は ERB へバインド

例えばテンプレートがERBだったらそのままバインドしちゃいましょう

<body>
  <ul>
    <li>店舗: <%= store_name %></li>
    <li>住所: <%= address %></li>
  </ul>
</body>
class SpooRecord

  ### テンプレートにデータをバインドしてブロックに返す
  def bind_erb(template_path)
    File.open(template_path, 'r') do |file|
      yield(ERB.new(file.read).result(binding))
    end
  end
end

SpooRecord を使ってみた

line_number = 0

CSV.foreach('data.csv') do |line|

  if line_number == 1
    SupponRecord.define_attributes( line ) 
    next
  end

  row = SupponRecord.new(line)
  row.bind_erb( 'template.erb') do |binded|
    File.open("#{row.store_id}/index.html", 'w') { |file| file.write(binded) }
  end
end

他にもいろいろ機能をついかしていくと。。。

https://gist.github.com/masayuki14/9d1e2aace3b75369c6ec5d15cdd6a642

メタプログラミングRuby

2016年の目標

去年やったことをめずらしく書きだしたのでせっかくだから今年の目標も書いておく

masayuki14.hatenablog.com

今年の目標

Swift2

swift2を試す。iOsネイティブのアプリを作れるようになろう。年内にクソゲーがリリースできたらバンザイ\(^o^)/

Redux

Reactで作ったアプリにReduxを導入する。

Hubot

なんかつくる。くだらないものからやっていこう。みさわの画像出すとか。すでにありそうだけど。

Mackerel

サーバーにMackerel入れてみる。APIコールして独自のメトリクスとりたい。

ブログ

月に1回はブログを書く。ちゃんとアウトプットしよう。

IPA試験

IPAの試験を受けてなんか合格したい。DBとネットワークあたりか。春の試験は受付中。

OSSコミッタ

になろう。規模の大小は問わずGitHubでPullRequestを送ってみる。

コミュニティ活動

勉強会とか参加してみよう。