RoadMovie

write down memos or something I found about tech things

Rubyのメソッドの引数あれこれメモ

配列引数

複数の引数を1つの配列として扱うにはアスタリスクをつける

def my_mthod(*args)
  args
end

my_method(1, 'hoge', 'three')
#=> [1, 'hoge', 'three']

キーワード引数

Ruby 2.0からの機能(同じことは1.9系とかでもできるがハッシュを展開しないとダメ)

def my_method(name: 'test user')
  name
end

my_method(name: "test new user")
#=> "test new user"

メソッド定義の際の引数にdef my_method(:name => 'test user')って書き方はできないみたい。 ただ、メソッドを呼び出す際の引数に上記は可能。つまりmy_method(:name => 'test new user')はOK

Rubyist Magazine - Ruby 2.0.0 のキーワード引数

 


ブロック引数、Procの引数に関しては下記のQiitaの記事が非常にわかりやすかったのでぜひご一読下さい。

[Ruby基礎] ブロックとProcをちゃんと理解する - Qiita [キータ]

MySQLのデータを定期実行でバックアップ

サービス運用してるとバックアップ取ると思うのですが、
毎回調べるので自分のブログにメモ。

backup_db.shとかで。 このシェルスクリプトをcronで定期実行するイメージです。

#!/bin/bash

TODAY=`date +%Y%m%d`
BACKUP_DIR=/data/backup/db
BACKUP_INTERVAL='+3'

for database in `mysql -uroot -N -s -e"show databases"`; do
    if [ $database == 'DB名' ]; then
        nice mysqldump -uroot --single-transaction --no-autocommit ${database} | pbzip2 -cv -p4 > ${BACKUP_DIR}/${database}-${TODAY}.sql.bz2
    fi
done

find ${BACKUP_DIR} -type f -name "*.bz2" -mtime ${BACKUP_INTERVAL} | xargs rm -Rf

 

ちなみにcronはこんな感じ

0  4 * * * root sh /path/to/backup_db.sh   > /dev/null 2>&1

Rubyでxls(Excelファイル)を簡単にパースする方法

すごく簡単でドキュメント通りな内容なのですが、非常に便利だったのでメモ。

その名もSpreadsheetというgemを使います。 https://github.com/zdavatz/spreadsheet/blob/master/GUIDE.md

# Gemfile
gem 'spreadsheet'
# vim hoge.rb
Spreadsheet.client_encoding = 'UTF-8'

book = Spreadsheet.open '/path/to/an/excel-file.xls'
sheet1 = book.worksheet 0 # スプレッドシートの1枚目を指定

sheet1.each do |row|
  # do something interesting with a row
end

たったこれだけです!超簡単! 配列でrowの部分にデータが入ってくるので扱いやすくてよいです。

スプレッドシートの指定の仕方はindexで指定しているのですが(なので2枚目だと1を指定すればよいです)、シート名をそのまま書いても大丈夫なようです。(ex. sheet1 = book.worksheet 'Sheet1')

また、Spreadsheet.open部分はRailsの場合だとexpand_pathとか使えなくてめんどくさそうと思ったのですが、普通にアプリルートからのパスで通りました。

なのでbatchスクリプト内で実行する際は

book = Spreadsheet.open 'bin/batch/excel-file.xls'

などと書いておいて、コンソールから

$ rails r bin/batch/hoge.rb

で大丈夫です。

メタプログラミングRubyメモ

Object#extend

module MyModule
  def my_method; 'hello'; end
end

を使いたいときに

class MyClass
  class << self
    include MyModule
    ….
  end
end

class MyClass
  extend MyModule
  ….
end

これでもMyClassの特異メソッドとしてmy_methodを呼べる。 ( MyClass.my_method #=> 'hello' )
 

falseの時なにか決まったものを返すとき、三項演算子じゃなくていいよ!的な

class MyClass
  def initialize
    @hoge = false
  end

  def hoge?
    @hoge || false
  end
end

irb(main):009:0> MyClass.new
=> #<MyClass:0x007ff499890fe0 @hoge=false>

irb(main):010:0> a = MyClass.new
=> #<MyClass:0x007ff49987a8d0 @hoge=false>

irb(main):011:0> a.hoge?
=> false

って感じでいける。or演算子やから当たり前やけど、こう書けてなかったな、、っていう自分のレベルの低さを感じたのでメモ
 

alias :m(新しいメソッド名) :my_method(古いメソッド名)

★アラウンドエイリアス

module Kernel
  alias gem_original_require require
  
  def require(path)
    gem_original_require path
  rescue LoadError => load_error
    if load_error.message =~ /#{Regexp.escape path}\z/ and
        spec = Gem.searcher.find(path)  then
      Gem.activate(spec.name, "= #{spec.version}")
      gem_original_require path
    else
      raise load_error
    end
  end

end

エイリアスは元のメソッドを変更するのではなく、新しいメソッドを定義して元のメソッドの名前をつける。 なので元のものをラップして新しいのに飛ばすようなことができてアラウンドエイリアスと呼ぶ。
 

クラスマクロ

クラスマクロはクラス定義の中で使えるクラスメソッド

class Book
  def self.deprecate(old_method, new_method)
    ..
  end

  deprecate :GetTitle, :title
end 

attr_accessorとかがそう。

module CheckedAttributes
  
  def self.included(base)
    base.extend ClassMethods # baseにはincludeしたクラスが入ってくるのでそれにたいしてClassMethodsを渡してる。
                                            # こうすることで渡ってきたクラスのクラスメソッドにClassMethodsを設定できる   
  end

  module ClassMethods
    def attr_checked(attribute, &validation)
      define_method "#{attribute}=" do |value|
        raise 'Invalid attribute' unless validation.call(value)
        instance_variable_set("@#{attribute}", value)
      end
      
      define_method attribute do
        instance_variable_get "@#{attribute}"
      end
    end
  end

end

  これで

class Person
  include CheckedAttributes

  attr_checked :age do |v|
    v >= 18
  end
end

とかできるようになるわけ。

RSpecでsessionを使用したテスト(ログイン機能)

内容としてはajaxのテストを書こうとして、それにはsessionが必要というような場合です。(渡しの場合、ログインしていないとajax通信ができないという仕様でした。)
 

ここけっこう困って、前回こういったsessionを使うテストを実装した際にはgrapeというRails向けのREST APIフレームワークを使っていたのでこれはまた別問題でした。(grapeはrackを使ってsession設定できます)
 


想定したアプローチは2つあって

  • controller specを使ってテスト

  • postで実際にログインさせてからテストを実行

1つ目はやってみたのですがテスト内ではsessionを使えたのですがアプリ内での引き継ぎができなくてうまくいきませんでした、が、ブログ書きながらちょろちょろ調べて見てると出来そうな雰囲気もあるのでまた次回試してみます。。

2つ目が今回の実装でやったのですが、挙動的にはこっちのほうがいいのかなと。

require 'spec_helper'

describe CalendarController do
  before do
    @user = create(:user)
    post '/login', {:username => @user.name, :password => @user.password}
  end

  context 'create' do
    …
    it do
      xhr :post, 'hoge/fuga', {:hoge => 1, :fuga => 'fuga'}
      expect(response.body).to eq({:success => true, :data => {…}.to_json})
      response.code.should == "200"
    end
  end

end 

というような感じです。 実際のログインユーザーが行う処理という流れに沿っている分テスト的には良いかなと思いました。 xhrでajax通信を使ったgetやpostが行えます。その後に中身とステータスコード200をチェックしているというような形です。
 

ただ、普通にsession[:calendar_id]みたいなのを使いたいというような時は1つ目のアプローチが必要と思いますので、また解決し次第ブログに書きたいと思います。

Ruby(Rails)からExcelで文字化けしないCSVファイル作成

CSVファイル作成でハマったのでメモ。

今回作ろう思っていたのはリンクになっているボタンを押したらCSVファイルがダウンロードされるという単純なものです。

 

HTML載せようと思いましたがはてブ内での書き方わからないので省略、、

 

バージョンはRuby2.0, Rails4です。

 

controllerは下記のような感じで処理はmodelに。

# controllers/management_controller.rb

def event_csv
  respond_to do |format|
    format.csv { send_data Event.event_csv(start_date, end_date),
      :filename => "events_#{start_date}-#{end_date}.csv"
    }
  end
end

 もともとはここで:type => ""でcontent-typeをcsvと指定していたのですがそれではうまくいきませんでした。

 

modelは下記

# models/event.rb

class Event < ActiveRecord::Base
  class << self
    
    def event_csv(start_date, end_date)
      data = self.all
      csv_data = CSV.generate do |csv|
        csv << %w(ユーザー 所属 合計) # headerになる部分を先にpush
        data.each do |record|
          csv << [record[:username], record[:belonging], record[:sum]]
        end
      end
      
      csv_data
    end

  end
end    

 ここでもまだ文字コード指定はしていません。

CSV.generate自体ウェブ上にあまり情報がなくて、しかもdocumentを見るとgenerate('', :encoding => 'sjis')みたいな感じで:encodingで指定できるよ!って書いてあるのですが できませんでした。。(文字コードは上記の場合、csv_data.encodeで調べられます。)

 

なのでさらに戻ってcontrollerから最後吐き出すときにafter_filterでnkfを使ってShift_JISエンコードするという方法で解決しました。

 

class ApplicationController < ActionController::Base
  after_filter :cahnge_charset_to_sjis, :if => csv?
  
  ...
  
  private
    def csv?
      return request.original_url.match(/csv/) ? true : flase
    end
    
    def change_charset_to_sjis
      require 'nkf'
      
      response.body = NKF::nkf('-Ws', response.body)
      headers["Content-Type"] = "text/csv; charset=Shift_JIS"
    end    
  end
  
end

 

 パスがcsvだったらsjis文字コードを変えるメソッドを通してるって感じです。ここでheader["Content-Type"]も設定しています。

 

ちなみに念のためconfig/routes.rbにはこんな感じで書いてます。

get 'management/event_csv/(:start_date)/(:end_date)',  :to => 'management#event_csv'

 

controllerの中でrespond_toでcsvを指定しているので上記のパスに.csvでアクセスするとcsvファイルがダウンロードされる、という感じです。(ex. /management/event_csv.csv

 

文字化けは相変わらず辛い・・・。

うまく行かなかったところはもうちょい追求しなければ。

もっと良い実装があればぜひコメント下さい!m(_ _)m

 

プライベートプロジェクトでのリーン・スタートアップ by ffab0

先日、友人と2人で立ち上げたffab0というチームで

最初のサービス"PositionStrategy"をリリースした。

http://position-strategy.ffab-0.com/

 

今回はサービス企画からファーストリリースまでの1ヶ月間をまとめてみた。

仕事をしながらプライベートでサービスを出したい、学生同士で友人となにかサービスを作りたいと考えている方たちの参考に一部でもなれば。それと自分用に備忘録としても。すべて隠さずに書いているつもり。

 

目次はこんな感じ。

  • サービス概要 ~ 宣伝も兼ねて ~
  • 前提 ~ 開発環境と連絡手段などチームマネジメント ~
  • 1週目 ~ 企画、そして挫折。新たなメンバー ~
  • 2週目 ~ 企画MTG。表参道での誓い ~
  • 3週目 ~ 2人で開発することによる成功と失敗。本業による多忙・・ ~
  • 4週目 ~ リリースまでの長い道のり ~

 

■サービス概要

サービスの概要を説明しておくと、最近よく「LINEの売上が前年比◯◯%アップ!」や「グリーがついに赤字転落」など、決算や業績に関するニュースがピックアップされるが、それが実感としてどの程度すごいのか、どの程度良くないことなのかつかみにくいと思う。(少なくともビジネス感覚の低い僕はそうです。。)

 

そこを解決するために、代表的な財務指標をいくつかピックアップし、グラフ化して、前年はどうだった、他企業と比べてどうか、業界的にはどうなのかなどが視覚的に、直感的に理解できてるようにしたツールです。それを見る人の立場、役割によって様々な戦略を立てる手助けになりえるのではないかと考えています。

PositionStrategy

 

■前提 ~ 開発環境と連絡手段などチームマネジメント~

今回のPositionStrategyの使っている技術をざっと上げると

ざっとこんな感じ。コードの管理はgitホスティングサービスのbitbucketを使った。

 

 チームのマネジメントという点では僕は東京で友人Kは大阪だったので基本的には

LINEとTwitterで相談、それでも難しい場合はGoogleハングアウトで会話+画面共有しながら相談という感じ。

また、土日など時間が取れる時に1日ハッカソンみたいな感じでずっとハングアウトつなぎっぱなしで開発をしていた。

ドキュメントはGoogleドライブ。

 

こんな感じで1ヶ月間で企画からリリースまで。

それでは次から1ヶ月間を振り返っていく。文章が稚拙なのでおもしろくないかも。。

 

■1週目 ~ 企画、そして挫折。新たなメンバー ~

このサービスは兼ねてから僕が考えていて勉強も兼ねてひとりで作ろうと思っていた。そこに同期のプログラマーが乗ってきたので2人でやることになったのだが、ここで最初の大きな挫折が。

  • 仕事を言い訳にできてしまう
  • 2人ともデザインのセンスがなかった
  • 同期はユーザビリティよりもシステムに関心がいっている時期だった
  • サービスを創れる状態になかった

 

同期同士なのでお互い忙しいのがわかるのと、仕事内容も把握していて催促できないことで甘えが出てしまっていた。さらにふたりともデザインセンスがほぼ皆無だった(僕はデザインが好きなだけに非常に残念だ笑)。同期はシステムやアクセスをどうさばくかというところに強い関心があったが、2人で創るにはその前の段階でクリアしなければならないことが多すぎた。どっちが悪いでもなかったがサービスを創るチームではなかったのだというのが結論だと感じている。

 

そんな中、救世主の友人Kがジョインしてくれた。どうやって呼び込んだかというと、この挫折しきった状態を大いに包み隠してビジョンだけ情熱的に話して呼び込んだ(笑) もちろんKがこのサービスに共感してくれたのが大きかったのと、高校時代からの友人で彼となら遠隔でも(Kは大阪在住で僕は東京)できるとお互いが思ったことが大きかった。実はKとは前に一度サービスを作ったことがあってある程度次はもっとうまくやれるとお互いに思いがあったこともある。

 

■2週目 ~ 企画MTG。表参道での誓い ~

Kと僕でやることになった時のチームのスペックとしては

  • 僕はコード(主にサーバーサイドだがマークアップも)は多少書ける。ビジネスを考えるのは好き。
  • Kはデザインが得意でマークアップもできる。サーバーサイドはほぼ素人だがターミナルで作業できるのとgitは教えた。ビジネスを考えるのは好き。

この2週目にKがたまたま東京に来る機会があったので、2泊3日の合宿で企画MTGと最初の環境構築をした。ふたりで振り返ってもここが重要なポイントだった。何が今回の成功(?)の鍵だったかというと、初回リリースの機能を洗い出して期限と担当を決めたこと。これは非常に重要なのだが、これをするためにはお互いに厚い信頼がないと仕事でない場面ではうまくいかないと思う。これを2人が守り切ったからこそ1ヶ月でのリリースができた。

 

もともと信頼はあったが、この合宿の最後に表参道でチーム論と人生について2人で話し合った。そしてffab0をスタートすることに決めた。これで2人でやっていく結束がさらに固まったように思う。ffab0がどうなるかは一定の結論が出た段階でまた書きたい。

 

■3週目 ~ 2人で開発することによる成功と失敗。本業による多忙・・ ~

ここは少し技術的な苦労が多かった。。まずKがgitを扱ったことがなかったのと、僕の伝え方がいまいちだったのが相まってコンフリクトからの激しいマージの嵐(笑) JSは最初Kに任せていたが途中から僕が巻き取ったのだがリファクタがかなり大変だった。。ただ、動くところまでやってくれていたので動きを見なくても綺麗に書きなおしてディレクトリ構造を綺麗にすればいい感じだったので助かったが。 あとは途中からKにもサーバーサイドで簡単なところは任せたのだが僕の中でのコーディング規約みたいなものを全く伝えていなかったのでお互いに苦労がうまれた。。ただbitbucketを使ってコミットを追いながらコメントしていたので修正はなんとかできた(それほどお互いに交わり合う実装が無いと思ったのでブランチ切ったりプルリクやったりはしなかった)

 

さらに仕事でのリリースがあってこっちに使える時間があまりなかった。ただそれは先ほど重要といった期限を守るという絶対の約束を守るという気持ちでなんとかした。自分が大変でも相方がちゃんとサービスを前進させてくれているというのはかなり気持ち的にも助けになる。

 

■4週目 ~ リリースまでの長い道のり ~

これは僕がrailsのassets piplineでつまったせいがかなりの比重を占める・・・。。それからどうしてもバグがでまくる。最後はずっとハングアウトで画面共有しながらお互いに指摘・説明・修正を繰り返してリリースまでこぎつけた、という感じだった。1年ほど前にCTOに「サービスは出来たと思ったところでだいたい2割くらい。あと8割残ってる」と言われたことがあるのでが、まさに今回もそんな感じだった・・。ここはちゃんとドキュメント化して無理なくできるようにしたい。

 

----

 

こんな感じの1ヶ月間でした。これからはリーンな感じで徐々にこのPositionStrategyをブラッシュアップしつつ、ffab0は1ヶ月1サービスを目標にしているので来月あたりまた新たなサービスを世に出せればと思っています。(ちなみに今週はこのブログに書いた企画MTGを再び実施予定)

 

...

最後に、シェアの時代なのでKPT法で振り返った僕らの反省会を載せておきます

 

 

KPT:

【K】

役割分担(デザインとサーバーサイド)

マイルストーンの置き方(この日までにこうしようという期日をしっかり定めて守った)

お互いが進めてる感がわかった(ツイッターとか通して)

ハングアウト

合宿形式で一旦認識を深め合ったのが良かった

サーバーサイド担当からのデザインに対する感想・指摘・アドバイスがあったこと

Kがrailsとコンソールを触れるようになったこと

slimいいね

 

【P】

gitのやりとりがうまくいってない(Kがもっと覚えるべき by K)

cssとslimをもっときれいに書く。今後はsassで書く

cssの構造を練ってからとりかかるべきだった

認識のすり合わせができていない(コードレベルでのズレが多くて書きなおすことが多々)

モデルやコントローラーの書き方(K)

本番リリース

いらないgemとか早めに消しとくべき

本番を意識した書き方ができていない&むずい。認識がなかった。

 

【T】

もっとKがサーバーサイド触る

ブランチ切ってプルリクを機能させる

CSSフレームワークを使う

技術をパッケージ化したい

年末年始合宿

ビジネスをもう少し考えてやりたい(マネタイズ、集客など)

⇛ 作るときにどういうビジネスモデルで行くのか、どういう集客方法でやるのかを

  考えてから作る