PostgreSQL Client

MacPostgreSQL のクライアントは何がよいかなぁ。
勉強用なので、有料はちょっとね。
pgAdmin で良いかと思ったけど、ちょっと無理だった。
β版を使ったから、クエリウィンドウのサイズ変更が出来ないのと、日本語が満足にいれられない。
日本語使わなければいける。

環境は VagrantPostgreSQL に接続。
ポートフォワーディングの設定しとく。

f:id:yossk:20151105235009j:plain

さてどうしようかな。

f:id:yossk:20151105235213j:plain

f:id:yossk:20151105235253j:plain

f:id:yossk:20151105235308j:plain

これからはじめるTDD

tatsu-zine.com

C# で写経してみた。
Testプロジェクトは、頭に Test をつけたけど、後ろにつけるべきだったのかな。
注意点は、プロダクトの方にテストプロジェクトがアクセスするためには、プロダクト側の AssemblyInfo.cs に以下を記述すること。

[assembly: InternalsVisibleTo("TestKorekaraTdd")]

BowlingGame.cs

using System.Collections.Generic;
using System.Linq;

namespace KorekaraTdd
{
    class BowlingGame
    {
        private Frame spareFrame;
        private List<Frame> strikes = new List<Frame> { };
        private IList<Frame> frames = new List<Frame> { new Frame() };

        public int FrameScore(int frameNo)
        {
            return frames[frameNo - 1].Score;
        }

        public void RecordShot(int pins)
        {
            frames.Last().RecordShot(pins);
            CalcSpareBonus(pins);
            CalcStrikeBonus(pins);
            if (frames.Last().IsFinished())
            {
                frames.Add(new Frame());
            }
        }

        public int Score()
        {
            int total = 0;
            foreach(var frame in frames)
            {
                total += frame.Score;
            }
            return total;
        }

        private void AddStrikeBonus(int pins)
        {
            foreach(var strike in strikes)
            {
                if (strike.IsNeedBonus())
                {
                    strike.AddBonus(pins);
                }
            }
        }

        private void CalcSpareBonus(int pins)
        {
            if (spareFrame != null && spareFrame.IsNeedBonus())
            {
                spareFrame.AddBonus(pins);
            }

            if (frames.Last().IsSpare())
            {
               spareFrame = frames.Last();
            }
        }

        private void CalcStrikeBonus(int pins)
        {
            AddStrikeBonus(pins);
            if (frames.Last().IsStrike())
            {
                RecognizeStrikeBonus();
            }
        }

        private void RecognizeStrikeBonus()
        {
            strikes.Add(frames.Last());
        }
    }
}

Frame.cs

using System;

namespace KorekaraTdd
{
    class Frame
    {
        private int score = 0;
        private int bonusCount = 0;

        public int Score {
            get
            {
                return score + Bonus;
            }
            private set
            {
                score = value;
            }
        }

        public int Bonus { get; private set; }

        private int shotCount = 0;

        public Frame()
        {
            Score = 0;
            Bonus = 0;
        }

        public void AddBonus(int bonus)
        {
            Bonus += bonus;
            bonusCount++;
        }

        public bool IsFinished()
        {
            return Score >= 10 || shotCount > 1;
        }

        public bool IsSpare()
        {
            return Score >= 10 && shotCount > 1;
        }

        public bool IsStrike()
        {
            return Score >= 10 && shotCount == 1;
        }

        public bool IsNeedBonus()
        {
            if (IsSpare())
            {
                return bonusCount < 1;
            }

            if (IsStrike())
            {
                return bonusCount < 2;
            }

            return false;
        }

        public void RecordShot(int pins)
        {
            CheckPinsCount(pins);
            Score += pins;
            shotCount++;
        }

        private void CheckPinsCount(int pins)
        {
            if ((pins < 0 || 10 < pins) && Score + pins <= 10)
            {
                throw new ArgumentException();
            }
        }
    }
}

BowlingGameTest.cs

using System;
using KorekaraTdd;

namespace TestKorekaraTdd
{
    using NUnit.Framework;

    [TestFixture]
    public class BowlingGameTest
    {
        BowlingGame game;

        [SetUp]
        public void SetUp()
        {
            game = new BowlingGame();
        }

        [Test]
        public void 全ての投球がガター()
        {
            RecordManyShot(20, 0);
            Assert.That(game.Score, Is.EqualTo(0));
        }

        [Test]
        public void 全ての投球で1ピンだけ倒した()
        {
            RecordManyShot(20, 1);
            Assert.That(game.Score, Is.EqualTo(20));
        }

        [Test]
        public void スペアをとると次の投球のピン数を加算()
        {
            RecordSpare();
            game.RecordShot(4);
            RecordManyShot(17, 0);

            Assert.That(game.Score, Is.EqualTo(18));
            Assert.That(game.FrameScore(1), Is.EqualTo(14));
        }

        [Test]
        public void 直前の投球との合計が10品でもフレーム違いはスペアではない()
        {
            foreach (var i in new int[] { 2, 5, 5, 1 })
            {
                game.RecordShot(i);
            }
            RecordManyShot(16, 0);
            Assert.That(game.Score, Is.EqualTo(13));
        }

        [Test]
        public void ストライクをとると次の2投分のピン数を加算()
        {
            RecordStrike();
            foreach (var i in new int[] { 3, 3, 1 })
            {
                game.RecordShot(i);
            }
            RecordManyShot(15, 0);
            Assert.That(game.Score, Is.EqualTo(23));
            Assert.That(game.FrameScore(1), Is.EqualTo(16));
        }

        [Test]
        public void 連続ストライクすなわちダブル()
        {
            RecordStrike();
            RecordStrike();
            foreach (var i in new int[] { 3, 1 })
            {
                game.RecordShot(i);
            }
            RecordManyShot(14, 0);
            Assert.That(game.Score, Is.EqualTo(41));
            Assert.That(game.FrameScore(1), Is.EqualTo(23));
            Assert.That(game.FrameScore(2), Is.EqualTo(14));
        }

        [Test]
        public void 連続3回ストライクすなわちターキー()
        {
            for (var i = 0; i < 3; i++) { RecordStrike(); }
            foreach (var i in new int[] { 3, 1 })
            {
                game.RecordShot(i);
            }
            RecordManyShot(12, 0);
            Assert.That(game.Score, Is.EqualTo(71));
        }

        [Test]
        public void ストライクの後のスペア()
        {
            RecordStrike();
            RecordSpare();
            game.RecordShot(3);
            RecordManyShot(15, 0);
            Assert.That(game.Score, Is.EqualTo(36));
        }

        [Test]
        public void ダブル後のスペア()
        {
            RecordStrike();
            RecordStrike();
            RecordSpare();
            game.RecordShot(3);
            RecordManyShot(13, 0);
            Assert.That(game.Score, Is.EqualTo(61));
        }

        [Test]
        public void 全ての投球がガターの場合の第1フレームの得点()
        {
            RecordManyShot(20, 0);
            Assert.That(game.FrameScore(1), Is.EqualTo(0));
        }

        [Test]
        public void 全ての投球が1ピンだと全フレーム2点()
        {
            RecordManyShot(20, 1);
            Assert.That(game.FrameScore(1), Is.EqualTo(2));
        }

        private void RecordManyShot(int count, int pins)
        {
            for (var i = 0; i < count; i++)
            {
                game.RecordShot(pins);
            }
        }

        private void RecordStrike()
        {
            game.RecordShot(10);
        }

        private void RecordSpare()
        {
            RecordManyShot(2, 5);
        }
    }
}

FrameTest.cs

using System;
using KorekaraTdd;

namespace TestKorekaraTdd
{
    using NUnit.Framework;

    [TestFixture]
    class FrameTest
    {
        Frame frame;

        [SetUp]
        public void SetUp()
        {
            frame = new Frame();
        }

        [Test]
        public void 全ての投球がガター()
        {
            frame.RecordShot(0);
            frame.RecordShot(0);
            Assert.That(frame.Score, Is.EqualTo(0));
        }

        [Test]
        public void 全ての投球で1ピンだけ倒した()
        {
            frame.RecordShot(1);
            frame.RecordShot(1);
            Assert.That(frame.Score, Is.EqualTo(2));
        }

        [Test]
        public void 二投するとフレームは完了する()
        {
            frame.RecordShot(1);
            Assert.That(frame.IsFinished(), Is.False);
            frame.RecordShot(1);
            Assert.That(frame.IsFinished(), Is.True);
        }

        [Test]
        public void 十ピン倒した時点でフレームは完了する()
        {
            frame.RecordShot(10);
            Assert.That(frame.IsFinished, Is.True);
        }

        [Test]
        public void 二投目で10ピン倒すとスペア()
        {
            frame.RecordShot(5);
            Assert.That(frame.IsSpare(), Is.False);
            frame.RecordShot(5);
            Assert.That(frame.IsSpare(), Is.True);
        }

        [Test]
        public void 一投目で10ピン倒すとストライク()
        {
            Assert.That(frame.IsStrike(), Is.False);
            frame.RecordShot(10);
            Assert.That(frame.IsStrike(), Is.True);
        }

        [Test]
        public void ボーナス点を加算する()
        {
            frame.RecordShot(5);
            frame.RecordShot(5);
            frame.AddBonus(5);
            Assert.That(frame.Score, Is.EqualTo(15));
        }

        [Test]
        public void オープンフレームにはボーナス不要()
        {
            frame.RecordShot(3);
            frame.RecordShot(3);
            Assert.That(frame.IsNeedBonus(), Is.False);
        }

        [Test]
        public void スペアのボーナスは1投分で完了()
        {
            frame.RecordShot(5);
            frame.RecordShot(5);
            Assert.That(frame.IsNeedBonus(), Is.True);
            frame.AddBonus(5);
            Assert.That(frame.IsNeedBonus(), Is.False);
        }

        [Test]
        public void ストライクのボーナスは2投分で完了()
        {
            frame.RecordShot(10);
            frame.AddBonus(5);
            Assert.That(frame.IsNeedBonus(), Is.True);
            frame.AddBonus(5);
            Assert.That(frame.IsNeedBonus(), Is.False);
        }

        [Test]
        public void ピン数は0本以上()
        {
            Assert.Throws<ArgumentException>( delegate { frame.RecordShot(-1); });
        }
    }
}

これからはじめるTDD

tatsu-zine.com

C# で書いてる。
C# はたまに勉強はするけど、使ったりはしないのですぐ忘れちゃう。
今回は書籍通りに進めていく。

今は第5章まで進めた。
NUnit でテストを書いている。
NUnit - Home

NUnit は 3 の RC が出ているようだ。
3 からは 2 までについていた GUI の実行ツールがなくなっている?
Visual Studio に Test Adapter を入れる。
NUnit3 用を入れないといけないので注意!無印を入れるとテストが実行されない。

f:id:yossk:20151103215909p:plain

ふと思ったのだが、ボーリングはほぼしたことなくてルールをあまり知らないが、
最後に3回投げれたりしなかったかな?
この書籍だと何も言われてないが。
ボウリング - Wikipedia
特定条件でのみ投げれるようだ。
話を簡単にするために触れてないのだろう。

これからはじめるTDD

tatsu-zine.com

第2章を終えたところから、これは正解を見ずに書いた方が勉強になるかも、と感じ、
テストの方だけを見て、実装の方はあまり見ずにやってみた。
また、minitest は使ったことなかったので、RSpec + Guard でテストを回していた。

第3章の時点で、これはFrameクラス有った方が良いなと思って追加したので、
最終結果の変数名とか違う部分が出てきた。

最終結果で参考にして直した部分は 3点。

  1. Frame を 最初に MAX フレーム数配列として 10 作っていたが、current を追加していく方式に変更
  2. destribute_bonus のほうは、select を使う発想がなく、ストライクでも MAX 2つ前までなので、2つ前まで見る、としていたのを修正
  3. (0..10).cover?(pins) という書き方

他に苦労した点とは、ボーリングのルールを知らない、という点だった。

コードを見て貰う機会がないので、jnchito さんに添削してもらいたいところ。

bowling_game.rb

require_relative "frame.rb"

class BowlingGame
  def initialize
    @frame_status = [ Frame.new ]
  end

  def frame_score(frame_no)
    @frame_status[frame_no - 1].score
  end

  def record_shot(pins)
    @frame_status.last.add(pins)
    add_bonus(pins)
    return change_frame if @frame_status.last.finished?
  end

  def score
    @frame_status.inject(0) { |sum, frame| sum += frame.score }
  end

  private

  def add_bonus(pins)
    @frame_status[0..-2].select(&:need_bonus?).each do |frame|
      frame.add_bonus(pins)
    end
  end

  def change_frame
    @frame_status << Frame.new
  end
end

bowling_game_spec.rb

describe BowlingGame do
  describe "#record_short" do
    before do
      @game = BowlingGame.new
    end

    subject do
      throw_count.times.each { @game.record_shot(pins) }
      @game.score
    end

    context "全ての投球がガターの場合" do
      let(:throw_count) { 20 }
      let(:pins) { 0 }
      it { expect(subject).to eq 0 }
    end

    context "全ての投球で1ピンだけ倒した" do
      let(:throw_count) { 20 }
      let(:pins) { 1 }
      it { expect(subject).to eq 20 }
    end

    context "スペアをとると次の投球のピン数を加算" do
      before do
        [3, 7, 4].each { |n| @game.record_shot(n) }
      end
      let(:throw_count) { 17 }
      let(:pins) { 0 }
      it { expect(subject).to eq 18 }
      it "一フレームの得点が加算されていること" do
        subject
        expect(@game.frame_score(1)).to eq 14
      end
    end

    context "直前の投球と合計が10ピンでもフレーム違いはspareではない" do
      before do
        [2, 5, 5, 1].each { |n| @game.record_shot(n) }
      end
      let(:throw_count) { 16 }
      let(:pins) { 0 }
      it { expect(subject).to eq 13 }
    end

    context "ストライクをとると次の2投分のピン数を加算" do
      before do
        [10, 3, 3, 1].each { |n| @game.record_shot(n) }
      end
      let(:throw_count) { 15 }
      let(:pins) { 0 }
      it { expect(subject).to eq 23 }
      it "一フレームの得点が加算されていること" do
        subject
        expect(@game.frame_score(1)).to eq 16
      end
    end

    context "連続ストライクすなわちダブル" do
      before do
        [10, 10, 3, 1].each { |n| @game.record_shot(n) }
      end
      let(:throw_count) { 14 }
      let(:pins) { 0 }
      it { expect(subject).to eq 41 }
      it "一フレームの得点が加算されていること" do
        subject
        expect(@game.frame_score(1)).to eq 23
      end
      it "二フレームの得点が加算されていること" do
        subject
        expect(@game.frame_score(2)).to eq 14
      end
    end

    context "3連続ストライクすなわちターキー" do
      before do
        [10, 10, 10, 3, 1].each { |n| @game.record_shot(n) }
      end
      let(:throw_count) { 12 }
      let(:pins) { 0 }
      it { expect(subject).to eq 71 }
    end

    context "ストライク後のスペア" do
      before do
        [10, 5, 5, 3].each { |n| @game.record_shot(n) }
      end
      let(:throw_count) { 15 }
      let(:pins) { 0 }
      it { expect(subject).to eq 36 }
    end

    context "ダブル後のスペア" do
      before do
        [10, 10, 5, 5, 3].each { |n| @game.record_shot(n) }
      end
      let(:throw_count) { 13 }
      let(:pins) { 0 }
      it { expect(subject).to eq 61 }
    end

    context "全ての投球が1ピンの場合" do
      let(:throw_count) { 20 }
      let(:pins) { 1 }

      it "全フレーム2点であること" do
        subject
        10.times.with_index(1) do |_, i|
          expect(@game.frame_score(i)).to eq 2
        end
      end
    end
  end
end

frame.rb

class Frame
  PITCH_MAX = 2
  SPARE_POWER = 1
  STRIKE_POWER = 2
  MAX_PINS = 10

  def initialize
    @pitches = []
    @bonus_count = 0
    @bonus = 0
  end

  def add(pins)
    check_pins(pins)
    @pitches << pins
    return @bonus_count = STRIKE_POWER if strike?
    @bonus_count = SPARE_POWER if spare?
  end

  def add_bonus(pins)
    return unless need_bonus?
    @bonus += pins
    @bonus_count -= 1
  end

  def finished?
    pitch_score == Frame::MAX_PINS || @pitches.count == PITCH_MAX
  end

  def need_bonus?
    @bonus_count > 0
  end

  def pitch_score
    @pitches.count == 0 ? 0 : @pitches.inject(&:+)
  end

  def score
     pitch_score + @bonus
  end

  def spare?
    @pitches.count == PITCH_MAX && @pitches.inject(&:+) == MAX_PINS
  end

  def strike?
    @pitches.count == 1 && @pitches.first == MAX_PINS
  end

  private

  def check_pins(pins)
    unless (0..10).cover?(pins) && pitch_score + pins <= MAX_PINS
      raise ArgumentError.new("bad num of pins: #{pins}")
    end
  end
end

frame_spec.rb

describe Frame do
  before do
    @frame = Frame.new
  end

  describe "#add" do
    context "一投分を追加した場合" do
      it "一投分が保存されていること" do
        pins = 6
        @frame.add(pins)
        expect(@frame.instance_variable_get("@pitches")).to match_array([pins])
      end
    end

    context "二投分を追加した場合" do
      it "二投分が保存されていること" do
        pins_arr = [7, 2]
        pins_arr.each { |pins| @frame.add(pins) }
        expect(
          @frame.instance_variable_get("@pitches")
        ).to match_array(pins_arr)
      end
    end

    context "10ピン倒した状態になった場合" do
      it "スペアポイントが設定されていること" do
        pins_arr = [4, 6]
        pins_arr.each { |pins| @frame.add(pins) }
        expect(
          @frame.instance_variable_get("@bonus_count")
        ).to eq Frame::SPARE_POWER
      end
    end


    shared_examples "Raise ArgumentError" do
      it "ArgumentError 例外が発生すること" do
        expect { @frame.add(pins) }.to raise_error(ArgumentError)
      end
    end

    context "ピン数が0未満の場合" do
      let(:pins) { -1 }
      include_examples "Raise ArgumentError"
    end

    context "ピン数が11以上の場合" do
      let(:pins) { 11 }
      include_examples "Raise ArgumentError"
    end

    context "フレームの合計ピン数が11以上の場合" do
      before do
        @frame.add(5)
      end
      let(:pins) { 6 }
      include_examples "Raise ArgumentError"
    end
  end

  describe "#add_bonus" do
    subject { @frame.add_bonus(pins) }
    let(:pins) { 7 }

    context "ボーナスカウントがない場合" do
      it "ボーナスポイントは 0 のままであること" do
        subject
        expect(@frame.instance_variable_get("@bonus")).to eq 0
      end
    end

    context "スペア分のカウントがある場合" do
      before do
        @frame.instance_variable_set("@bonus_count", Frame::SPARE_POWER)
      end

      it "ボーナスポイントがつくこと" do
        subject
        expect(@frame.instance_variable_get("@bonus")).to eq pins
      end

      it "ボーナスカウントが減ること" do
        subject
        expect(@frame.instance_variable_get("@bonus_count")).to eq 0
      end
    end
  end

  describe "#finished?" do
    subject { @frame.finished? }

    context "一投終えた場合" do
      before do
        @frame.instance_variable_set("@pitches", [2])
      end

      it { expect(subject).to be_falsey }
    end

    context "二投終えた場合" do
      before do
        @frame.instance_variable_set("@pitches", [1, 2])
      end

      it { expect(subject).to be_truthy }
    end

    context "一投で10ピン倒した場合" do
      before do
        @frame.instance_variable_set("@pitches", [10])
      end

      it { expect(subject).to be_truthy }
    end
  end

  describe "#need_bonus?" do
    subject { @frame.need_bonus? }

    context "オープンフレームの場合" do
      before do
        [5, 3].each { |pins| @frame.add(pins) }
      end

      it "ボーナスは不要" do
        expect(subject).to be_falsey
      end
    end

    context "スペアのボーナスは1投分で完了" do
      before do
        [5, 5].each { |pins| @frame.add(pins) }
      end

      it "ボーナス付与前はボーナスが必要" do
        expect(subject).to be_truthy
      end
    end
  end

  describe "#score" do
    subject { @frame.score }

    context "投球が一回もない場合" do
      it { expect(subject).to eq 0 }
    end

    context "投球が二回あった場合" do
      before do
        @frame.instance_variable_set("@pitches", [2, 3])
      end

      it "投球の合計が返ること" do
        expect(subject).to eq 5
      end
    end

    context "ボーナスポイントがある場合" do
      before do
        @frame.instance_variable_set("@pitches", [3, 4])
        @frame.instance_variable_set("@bonus", 9)
      end

      it "投球の合計 + ボーナスポイントが返ること" do
        expect(subject).to eq 16
      end
    end
  end

  describe "#spare?" do
    subject { @frame.spare? }

    context "投球してない場合" do
      it { expect(subject).to be_falsey }
    end

    context "一投の場合" do
      before do
        @frame.instance_variable_set("@pitches", [5])
      end

      it { expect(subject).to be_falsey }
    end

    context "一投ので10ピン倒した場合" do
      before do
        @frame.instance_variable_set("@pitches", [10])
      end

      it { expect(subject).to be_falsey }
    end

    context "二投の場合" do
      before do
        @frame.instance_variable_set("@pitches", [7, 2])
      end

      it { expect(subject).to be_falsey }
    end

    context "二投で10ピン倒した場合" do
      before do
        @frame.instance_variable_set("@pitches", [7, 3])
      end

      it { expect(subject).to be_truthy }
    end
  end

  describe "#strike?" do
    subject { @frame.strike? }

    context "投球してない場合" do
      it { expect(subject).to be_falsey }
    end

    context "一投の場合" do
      before do
        @frame.instance_variable_set("@pitches", [5])
      end

      it { expect(subject).to be_falsey }
    end

    context "一投ので10ピン倒した場合" do
      before do
        @frame.instance_variable_set("@pitches", [10])
      end

      it { expect(subject).to be_truthy }
    end

    context "二投の場合" do
      before do
        @frame.instance_variable_set("@pitches", [7, 2])
      end

      it { expect(subject).to be_falsey }
    end

    context "二投で10ピン倒した場合" do
      before do
        @frame.instance_variable_set("@pitches", [7, 3])
      end

      it { expect(subject).to be_falsey }
    end
  end
end

Eclispe で クラス図を描く

tatsu-zine.com

この書籍をちょっとやってみよう(目的は C# でやりたい、なんだけど)としたが、
何かやろうとすると別のことが気になってすすまない現象に。

クラス図、以前は Astah Community を使わせて頂いていたが、7.0 になってライセンスが厳しくなってしまったので、ちょっとやめとこうと。

結論としては、AmaterasUML を使わせて頂きました。
AmaterasUML - Project Amateras

GEF をインストールしないといけないらしい。www.eclipse.org

以下を追加した。

http://download.eclipse.org/tools/gef/gef4/updates/releases

「GEF」ってそのものはないので、Commonだけ入れてみた。
f:id:yossk:20151031123738j:plain

AmaterasUML のファイルをダウンロードしてきて、Mac 上の Eclipse ファイルを右クリックコンテキストメニューからパッケージの内容を表示で、dropins に配置。
f:id:yossk:20151031124239j:plain

動いたよ!
f:id:yossk:20151031124257j:plain

Itamae で Redmine 環境

とりあえずできた。
参考にしたものをつなぎ合わせただけだけど。
また、itamae の便利な plugin は基本使わないことにして、コードをコピっただけにした。
後で見てわけわかんなくなりそうだったので。

追記: postgesql の起動タイミングを間違っていたので修正。
再追記: さらに間違えてたので修正。また、Postfixipv6 無効対応をした。他にもあるかもしれないし、そもそも無効にしなくてもいいのでは・・・と思ってきた。

参考

rbenv

qiita.com
ほぼ丸パクリさせていただいた

SELINUX 無効

github.com
disabled のところ丸パクリさせていただいた

IPv6 無効

FAQ/CentOS7 - CentOS Wiki
IPv6 は使わないが、敢えて無効にする必要ないと思っていたが、
Redmine から PostgreSQL に接続するさい、IPv6 で接続しにいってしまったので

PostgreSQL

yuumi3.hatenablog.com
PostgreSQL のインストール部分をパクらせていただいた

Redmine の 追加 Plugin と Theme について

Issue Template を入れてと要望があったのでwww.r-labs.org

Theme は常にこれgithub.com

Vagrant の設定

Box自体は前に作ったものを使う。また、Vagrantfile の以下部分のコメント外しておいた。

  • Vagrantfile
  config.vm.network "private_network", ip: "192.168.33.10"

Itamae ファイル

今回関係ないパッケージも別件で使うので入れてしまってる
IPv6 を無効にしているのは、RedminePostgreSQLに対して IPv6 で接続しに移行としてた。
pg_hba.conf で IPv6コメントアウトしてたので。

  • recipes/base.rb
# SELinux disabled
execute 'setenforce 0' do
  not_if 'getenforce | grep Disabled'
end

file '/etc/selinux/config' do
  action :edit
  block do |content|
    next if content =~ /^SELINUX=disabled/
    content.gsub!(/^SELINUX=.*/, "SELINUX=disabled")
  end
end

# update
execute "update yum repo" do
  command "yum -y update"
end

# EPEL
package "epel-release"

# Base Library
package "asciidoc"
package "compat-libcap1"
package "compat-libstdc++-33"
package "gcc"
package "gcc-c++"
package "git"
package "java-1.8.0-openjdk"
package "java-1.8.0-openjdk-devel"
package "libaio-devel"
package "libcurl-devel"
package "libstdc++"
package "libstdc++-devel"
package "libxml2-devel"
package "libxslt-devel"
package "libyaml-devel"
package "openssl-devel"
package "readline-devel"
package "tmux"
package "vim"
package "zlib-devel"
package "ipa-gothic-fonts"
package "ipa-pgothic-fonts"
package "ipa-mincho-fonts"
package "ipa-pmincho-fonts"

# IPv6 Disable
file "/etc/sysctl.conf" do
  user "root"
  action :edit
  block do |content|
    unless content =~ /ipv6/
      content.concat <<-CONF
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
      CONF
    end
  end
end

execute "IPv6 Disable" do
  command <<-CMD
sysctl -w net.ipv6.conf.all.disable_ipv6=1
sysctl -w net.ipv6.conf.default.disable_ipv6=1
  CMD
end

# Postfix
file "/etc/sysctl.conf" do
  user "root"
  action :edit
  block do |content|
    unless content =~ /inet_protocols = all/
      content.gsub!("all", "ipv4")
    end
  end
end
  • recipes/rbenv.rb
RBENV_DIR = "/usr/local/rbenv"
RBENV_SCRIPT = "/etc/profile.d/rbenv.sh"

remote_file RBENV_SCRIPT do
  owner "root"
  group "root"
  source "remote_files/rbenv.sh"
end

execute "rbenv install" do
  user "root"
  command <<-CMD
git clone git://github.com/sstephenson/rbenv.git #{RBENV_DIR}
git clone git://github.com/sstephenson/ruby-build.git #{RBENV_DIR}/plugins/ruby-build
  CMD
  not_if "test -e #{RBENV_DIR}"
end

node["rbenv"]["versions"].each do |version|
  execute "install ruby #{version}" do
    user "root"
    command "source #{RBENV_SCRIPT}; rbenv install #{version}"
    not_if "source #{RBENV_SCRIPT}; rbenv versions | grep #{version}"
  end
end

execute "set global ruby #{node["rbenv"]["global"]}" do
  command "source #{RBENV_SCRIPT}; rbenv global #{node["rbenv"]["global"]}; rbenv rehash"
  not_if "source #{RBENV_SCRIPT}; rbenv global | grep #{node["rbenv"]["global"]}"
end

node["rbenv"]["gems"].each do |gem|
  execute "gem install #{gem}" do
    user "root"
    command "source #{RBENV_SCRIPT}; gem install #{gem}; rbenv rehash"
    not_if "source #{RBENV_SCRIPT}; gem list | grep #{gem}"
  end
end

execute "update gem" do
  command "source #{RBENV_SCRIPT}; gem update -- --system; rbenv rehash"
end
# PostgreSQL Install
ver = node[:postgresql][:ver]
short_ver = node[:postgresql][:short_ver]

package node[:postgresql][:pgdg] do
  not_if "rpm -q #{File.basename(node[:postgresql][:pgdg], ".rpm")}"
end

package "postgresql#{short_ver}"
package "postgresql#{short_ver}-server"
package "postgresql#{short_ver}-contrib"
package "postgresql#{short_ver}-devel"
package "postgresql#{short_ver}-libs"

execute "initdb" do
  command "PGSETUP_INITDB_OPTIONS='--encoding UTF8 --no-locale' /usr/pgsql-#{ver}/bin/postgresql#{short_ver}-setup initdb"
  not_if "test -e /var/lib/pgsql/#{ver}/data/postgresql.conf"
end

%w(pg_hba.conf postgresql.conf).each do |file|
  remote_file "/var/lib/pgsql/#{ver}/data/#{file}" do
    source "remote_files/#{file}"
    owner "postgres"
    group "postgres"
    mode "0600"
  end
end

[:enable, :restart].each do |act|
  service "postgresql-#{ver}" do
    action act
  end
end

# Firewall
execute "firewall port open" do
  command "firewall-cmd --add-port=5432/tcp --zone=public --permanent"
  not_if "grep -c 5432 /etc/firewalld/zones/public.xml"
end

execute "Firewall reload" do
  command "firewall-cmd --reload"
end
package "httpd"
package "httpd-devel"
package "httpd-tools"
package "ImageMagick"
package "ImageMagick-devel"
package "subversion"
package "mercurial"

include_recipe "postgresql.rb"

RBENV_SCRIPT = "/etc/profile.d/rbenv.sh"
PSQL = "/usr/bin/psql"
BASE_DIR = node[:redmine][:base_dir]

remote_file RBENV_SCRIPT do
  source "remote_files/rbenv.sh"
end

execute "passenger install" do
  user "root"
  command "source #{RBENV_SCRIPT}; gem i passenger -v #{node[:passenger][:ver]}; rbenv rehash"
  not_if "source #{RBENV_SCRIPT}; gem list | grep -c passenger"
end

execute "passenger-install-apache2-module" do
  user "root"
  command "source #{RBENV_SCRIPT}; passenger-install-apache2-module --auto"
  not_if "test -e #{node[:passenger][:so]}"
end

remote_file "/etc/httpd/conf.d/redmine.conf" do
  owner "apache"
  group "apache"
  source "remote_files/redmine.conf"
end

execute "install redmine" do
  command "svn co  #{node[:redmine][:svn]} #{BASE_DIR}"
  not_if "test -e #{BASE_DIR}"
end

execute "chown redmine directory" do
  command "chown -R apache:apache #{BASE_DIR}"
end

execute "create redmine role" do
  user "postgres"
  command <<-CMD
#{PSQL} -c "CREATE ROLE redmine LOGIN ENCRYPTED PASSWORD 'pass'
    NOINHERIT VALID UNTIL 'infinity'"
  CMD

  not_if <<-CMD
#{PSQL} -c "SELECT rolname FROM pg_roles WHERE rolname = 'redmine'" | grep -c redmine
  CMD
end

execute "create redmine database" do
  user "postgres"
  command <<-CMD
#{PSQL} -c "CREATE DATABASE redmine WITH ENCODING='UTF8' OWNER=redmine"
  CMD

  not_if <<-CMD
#{PSQL} -l | grep -c redmine
  CMD
end

remote_file "#{BASE_DIR}/config/database.yml" do
  owner "apache"
  group "apache"
  source "remote_files/database.yml"
end

# Issue Template
execute "Issue Template" do
  cwd "#{BASE_DIR}/plugins"
  command "hg clone https://bitbucket.org/akiko_pusu/redmine_issue_templates"
  not_if "test -e redmine_issue_templates"
end

# Farend Fancy Theme
git "#{BASE_DIR}/public/themes/farend_fancy" do
  repository "git://github.com/farend/redmine_theme_farend_fancy.git"
end

execute "bundle install" do
  cwd BASE_DIR
  command <<-CMD
source #{RBENV_SCRIPT}
PATH=/usr/pgsql-#{node[:postgresql][:ver]}/bin:$PATH gem i pg
bundle install --without development test
bundle exec rake generate_secret_token
bundle exec rake db:migrate RAILS_ENV=production
bundle exec rake redmine:plugins:migrate RAILS_ENV=production
  CMD
end

# httpd
file "/etc/httpd/conf/httpd.conf" do
  action :edit
  block do |content|
    next if content =~ /redmine/
    content.gsub!(/^DocumentRoot "\/var\/www\/html"/, %Q(DocumentRoot "#{BASE_DIR}/public"))
  end
end

[:enable, :restart].each do |act|
  service "httpd" do
    action act
  end
end

# Firewall
execute "firewall port open" do
  command "firewall-cmd --add-service=http --zone=public --permanent"
  not_if "grep -c http /etc/firewalld/zones/public.xml"
end

execute "Firewall reload" do
  command "firewall-cmd --reload"
end
  • node.yml
rbenv:
  global:
    2.2.3
  versions:
    - 2.2.3
  gems: [bundler]
  rbenv_root: /usr/local/rbenv

passenger:
  ver: 5.0.21
  so: /usr/local/rbenv/versions/2.2.3/lib/ruby/gems/2.2.0/gems/passenger-5.0.21/buildout/apache2/mod_passenger.so

postgresql:
  pgdg: http://yum.postgresql.org/9.5/redhat/rhel-7-x86_64/pgdg-centos95-9.5-2.noarch.rpm
  ver: 9.5
  short_ver: 95

redmine:
  base_dir: /var/lib/redmine
  svn: http://svn.redmine.org/redmine/branches/3.1-stable
  • run.rb

上記を実行するスクリプト

include_recipe "recipes/base.rb"
include_recipe "recipes/rbenv.rb"
include_recipe "recipes/redmine.rb"
  • 実行
% itamae ssh --host localhost -p 2222 -u vagrant ./run.rb -y node.yml

http://192.168.33.10/ にアクセスするとRedmineが動作してるはず
f:id:yossk:20151031004848j:plain
f:id:yossk:20151031004857j:plain

Itamae で Redmine環境

Itamae で Redmine 環境を構築したい。してみたい。
以下の記事で Ruby は設定。
gem で設定するものもあるけど、動きが分からなくなるので使わない。

qiita.com

Redmine の設定自体は RedmineJP様の記事で。
Redmine 3.1をCentOS 7.1にインストールする手順 | Redmine.JP Blog

ここで、PostgreSQL の設定で躓いてしまった。
pg_hba_conf の設定。
作った redmine ユーザで接続できないので、明日はPostgreSQL を調査する。