11月13日

新規ファイルを追加してもアイコンが「?」にならない
EclipseでGitを使用していれば、新規ファイルを追加すればファイルのアイコンに「?」がオーバーレイされるはずなのに、これが表示されないという問題が発生した。プロジェクト内のどこに新規ファイルを作っても、Gitの管理下に追加できない状況になっていた。

調べた結果、プロジェクトを格納しているフォルダの上位フォルダに .gitignore ファイルが作成されていた。
その内容が、

/プロジェクトフォルダ名/

となっていたため、既存ファイル以外のすべての追加が無視されていた。

問題の1行を削除してコミットすることで、新規ファイルに「?」アイコンが表示されるようになった。

同じ問題で困っていた人↓
https://stackoverflow.com/questions/12808656/cant-add-new-file-to-repository-in-egit

10月6日

課題について補足

件名
件名は「何」に対して「どうする」のかを書くこと。ロバストネス図内のView(JSPファイル)・Controller(Javaファイル)・Entity(Javaファイル)や、設定ファイル(XMLファイル)、プロジェクトの内容や詳細を説明するWikiページ、画面に表示する画像ファイル、CSSファイル、JavaScriptファイルなどが「何」にあたる。

詳細
件名だけでわかるようなら詳細は書かなくてもよい。

コメント
Gitにコミット&プッシュする場合は、コミットのコメントに課題IDだけを書く。
自動的に、その課題のコメントに、GitのコミットIDが追加され、どのファイルが変更されたのかがわかるようになる。
Gitにコミット&プッシュしない変更を行った場合は、Wikiページはファイルなどへのリンクをコメントに貼って、その課題による成果がわかるようにすること。

10月2日

これからのチーム開発のやりかた

  1. 毎朝、それぞれが取り組む課題を決定する。
  2. Backlogに課題を登録する。
  3. 課題を「未対応」→「処理中」に変更する。
  4. 課題に取り組む。
  5. ファイルを新規で作った場合はGitに追加する。
  6. 終わったらGitにコミットする(コミットメッセージは課題のIDのみ)
  7. 課題を「処理中」→「処理済み」に変更する。
  8. チームのほかのメンバーに確認してもらう。
  9. 確認したメンバーが課題を「処理済み」→「完了」に変更する。
  10. Backlogのリモートリポジトリにプッシュする。
  11. ほかのメンバーにプッシュしたことを伝え、プルしてもらう。
  12. 1に戻る。

課題の内容は、当日(1~2現の3時間)で完了できる内容にすること。

課題のタイトルは、ロバストネス図に書いた、画面・コントローラ・エンティティなどを書いて、それに対してどのような変更をするのかを記載し、課題で何を解決しようとしているのかが一目でわかるようにすること。

課題の詳細欄には、変更する具体的な内容や、変更するファイル名などを記載する。タイトルだけでわかるのであれば、書かなくてもよい。

9月29日

リーダーが新規でプロジェクトを作成する。
「click to enter」を表示するまでの動作を確認する。
[チーム]-[プロジェクトの共用]を選択する。
リポジトリタイプは「Git」を選択する。
適当な場所にリポジトリを作成して「完了」をクリックする。

プロジェクトホームに「.gitignore」ファイルを作成する。

/target
.settings/
.classpath
/pleiades45

[チーム]-[コミット]でファイルをリポジトリに登録する。
不要なファイルが含まれていないか確認して、含まれていれば「.gitignore」ファイルに設定を追加する。
問題なければコミットを実行する。

[チーム]-[リモート]-[プッシュ]を選択し、BacklogのGitにあるリモートリポジトリにプッシュする。
プッシュ先のURLは、BacklogのGitのURLをコピーして使用する。

他のメンバーは、[新規]-[インポート]で、プロジェクトをBacklogのリモートリポジトリからインポートする。
インポートソースの選択で「Gitからプロジェクト」を選択し「次へ」をクリック。
「URLの複製」を選択して「次へ」をクリック。
BacklogのGitのURLをコピーしてURIにペーストし、BacklogのユーザーIDとパスワードを入力して「次へ」をクリック。
「master」が選択されていることを確認して「次へ」をクリック。
ローカルリポジトリのディレクトリーを指定して「次へ」をクリック。
プロジェクトをインポートするためのウィザードで「Import existing Eclipse project」を選択し「次へ」をクリック。
インポートしたいプロジェクトにチェックが入っていることを確認して「完了」をクリック。

あとはこれまでと同じように
[Maven]-[プロジェクトの更新]
[実行]-[maven install」
を実行すれば、ローカルでもWebアプリが動作するようになる。

9月25日

Backlogにログインする。
・課題を新規作成する。(Wikiページの作成)
・課題を処理中にする。
・Wikiページを作成する。
・課題を処理済みにする。
・他のメンバーに確認してもらったら、課題を完了にする。

9月11日

Gitの練習

todo.jsp を変更してみる。

<body>
<h1>ToDo項目</h1>

<form method="post" action="input">
<input type="text" name="item">
<input type="submit" value="登録">
</form>

<h3>未完了</h3>
<div id="todo">
<c:if test="${list != null}">
  <form method="post" action="check">
    <c:forEach var="t" items="${list}">
      <c:if test="${t.done == null}">
        <div id="check${t.id}">
      	  <input type="checkbox" name="items" value="${t.id}" />${t.item} <span class="days">${t.days}日前</span>
        </div>
      </c:if>
    </c:forEach>
  </form>
</c:if>
</div>

変更して保存すると、プロジェクトエクスプローラ上のファイル名の左側に「>」が表示される。
これは、リポジトリの最新の状態から変更があったことを示す。

プロジェクトを右クリックして[チーム]-[コミット]を選択すると、todo.jsp がファイルリストに表示される。
todo.jspをダブルクリックすると変更箇所が確認できる。
変更内容が間違いなければ、コミットメッセージを入力してコミットを実行する。

未完了項目の経過日数表示

未完了項目の右側に登録してからの経過日数を表示するようにする。
経過日数はサーバー側で計算した方が実装が簡単そうなので、ToDoエンティティに経過日数を格納する days を追加する。
このデータはデータベースに保存する必要はないので @Transient アノテーションを追加しておく。

package jp.abc;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Transient;

@Entity
public class ToDo {
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column
	private long id;

	@Column
	private String item;

	@Column
	private Date created;

	@Column
	private Date done;

	@Transient
	private int days;

	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getItem() {
		return item;
	}

	public void setItem(String item) {
		this.item = item;
	}

	public Date getCreated() {
		return created;
	}

	public void setCreated(Date created) {
		this.created = created;
	}

	public Date getDone() {
		return done;
	}

	public void setDone(Date done) {
		this.done = done;
	}

	public int getDays() {
		return days;
	}

	public void setDays(int days) {
		this.days = days;
	}
}

コントローラを見ると、あちこちで dao.getAll() を使っているので、コントローラではなくDAO実装内で追加すると一か所の変更で済ませることができる。

	public List<ToDo> getAll() {
		EntityManager manager = factory.createEntityManager();
		List<ToDo> list = null;
		CriteriaBuilder builder = manager.getCriteriaBuilder();
		CriteriaQuery<ToDo> q = builder.createQuery(ToDo.class);
		Root<ToDo> r = q.from(ToDo.class);
		q.select(r);
		list = (List<ToDo>)manager.createQuery(q).getResultList();
		manager.close();
		Date now = new Date();
		for (ToDo t : list) {
			long diff = now.getTime() - t.getCreated().getTime();
			diff = diff / (1000 * 60 * 60 * 24);
			t.setDays((int)diff);
		}
		return list;
	}

todo.jsp の未完了項目の右側に値を追加してみる。

<h3>未完了</h3>
<div id="todo">
<c:if test="${list != null}">
  <form method="post" action="check">
    <c:forEach var="t" items="${list}">
      <c:if test="${t.done == null}">
        <div id="check${t.id}">
      	  <input type="checkbox" name="items" value="${t.id}" />${t.item} ${t.days}日前
        </div>
      </c:if>
    </c:forEach>
  </form>
</c:if>
</div>

「○日前」の文字サイズや色を変更するために span タグで囲む。

      	  <input type="checkbox" name="items" value="${t.id}" />${t.item} <span class="days">${t.days}日前</span>

days クラス用のCSSを用意する。

<title>ToDoアプリ</title>
<style type="text/css">
span.days {
  font-size: 9pt;
  color: gray;
  margin-left: 20px;
}
</style>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>

動作確認したら、プロジェクトを右クリックして[チーム]-[コミット]を選択する。
ToDo.java
ToDoDaoImpl.java
todo.jsp
の3ファイルがリストされる。
それぞれのファイルをダブルクリックして変更箇所を確認し、問題がなければコミットメッセージを入力してコミットを実行する。

完了済みの項目の経過日数

完了済みの項目の経過日数は、完了した日からの経過日数を表示する。

まずは、未完了項目と同じように、登録日からの経過日数を表示してみる。

<h3>完了済み</h3>
<div id="done">
<c:if test="${list != null}">
  <c:forEach var="t" items="${list}">
    <c:if test="${t.done != null}">
      <div id="check${t.id}">
        <input type="checkbox" name="items" value="${t.id}" checked="checked" />${t.item} <span class="days">${t.days}日前</span>
      </div>
    </c:if>
  </c:forEach>
</c:if>
</div>

完了項目に対しての days の値は、完了日からの経過日数となるように、サーバー側の ToDoDaoImpl.java で計算する。

	public List<ToDo> getAll() {
		EntityManager manager = factory.createEntityManager();
		List<ToDo> list = null;
		CriteriaBuilder builder = manager.getCriteriaBuilder();
		CriteriaQuery<ToDo> q = builder.createQuery(ToDo.class);
		Root<ToDo> r = q.from(ToDo.class);
		q.select(r);
		list = (List<ToDo>)manager.createQuery(q).getResultList();
		manager.close();
		Date now = new Date();
		for (ToDo t : list) {
			long from;
			if (t.getDone() == null) {
				from = t.getCreated().getTime();
			} else {
				from = t.getDone().getTime();
			}
			long diff = now.getTime() - from;
			diff = diff / (1000 * 60 * 60 * 24);
			t.setDays((int)diff);
		}
		return list;
	}

新しく項目を追加したり、チェックボックスをチェックすると「○日前」の表示が消えてしまう。
これは、上記操作をしたときに、jQueryでHTML要素を生成しなおしているため。
span要素を追加する処理を、jQueryで記述することで解決する。

function init() {
	$('input[name="items"]').change(function(){
		console.log($(this).prop('checked'));
		var url;
		if ($(this).prop('checked')) {
			url = 'done';
		} else {
			url = 'redo';
		}
		$.post(
			url,
			'id=' + $(this).val(),
			function(data) {
				console.log(data);
				$('#todo form').children().remove();
				$('#done').children().remove();
				for (var i = 0; i < data.length; i++) {
					var div = $('<div>');
					div.attr('id', 'check' + data[i].id);
					var cb = $('<input>')
								.attr('type', 'checkbox')
								.attr('name', 'items')
								.attr('value', data[i].id);
					div.append(cb).append(data[i].item)
					var s = $('<span>');
					s.addClass('days');
					s.text(data[i].days + '日前');
					div.append(s);
					if (data[i].done == null) {
						$('#todo form').append(div);
					} else {
						cb.attr('checked', 'checked');
						$('#done').append(div);
					}
				}
				init();
			});
	});
}

動作確認できたら、[チーム]-[コミット]で変更内容をGitリポジトリに反映させる。

これまでの変更内容は、[チーム]-[ヒストリーに表示]で確認できる。

9月8日

サーバーから受信したデータをもとに画面を更新するようにしたところ、最初のチェックに対してはToDo項目が完了済みに移動したが、2個目からは移動しなくなってしまった。

これは、change()イベントを拾う処理が画面読み込み完了時(load)のみに行われているため。

新しく生成した input[name=”items”] のセレクタで指定した要素にはイベントを拾う処理が登録されていないために動作しない。

この対策を実施する。
まずはload時の処理をfunctionに分割する。

<!DOCTYPE html>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>ToDoアプリ</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
	init();
})

function init() {
	$('input[name="items"]').change(function(){
		console.log($(this).parent());
		$('#done').append($(this).parent());
		$.post(
			'done',
			'id=' + $(this).val(),
			function(data) {
				console.log(data);
				$('#todo form').children().remove();
				$('#done').children().remove();
				for (var i = 0; i < data.length; i++) {
					var div = $('<div>');
					div.attr('id', 'check' + data[i].id);
					var cb = $('<input>')
								.attr('type', 'checkbox')
								.attr('name', 'items')
								.attr('value', data[i].id);
					div.append(cb).append(data[i].item);
					if (data[i].done == null) {
						$('#todo form').append(div);
					} else {
						cb.attr('checked', 'checked');
						$('#done').append(div);
					}
				}
			});
	});
}
</script>
</head>
<body>
<h1>ToDo項目</h1>

<form method="post" action="input">
<input type="text" name="item">
<input type="submit" value="登録">
</form>

<div id="todo">
<c:if test="${list != null}">
  <form method="post" action="check">
    <c:forEach var="t" items="${list}">
      <c:if test="${t.done == null}">
        <div id="check${t.id}">
      	  <input type="checkbox" name="items" value="${t.id}" />${t.item}
        </div>
      </c:if>
    </c:forEach>
  </form>
</c:if>
</div>

<hr />
<h3>完了済み</h3>
<div id="done">
<c:if test="${list != null}">
  <c:forEach var="t" items="${list}">
    <c:if test="${t.done != null}">
      <div id="check${t.id}">
        <input type="checkbox" name="items" value="${t.id}" checked="checked" />${t.item}
      </div>
    </c:if>
  </c:forEach>
</c:if>
</div>



</body>
</html>

この変更ではいままでどおりの動作となる。

さらに、サーバーからデータを受信して、新規に要素を生成して画面上に追加した処理のあとに init() を呼ぶようにすることで、チェックボックスのイベントに対しての処理を行うようにする。

<!DOCTYPE html>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>ToDoアプリ</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
	init();
})

function init() {
	$('input[name="items"]').change(function(){
		console.log($(this).parent());
		$('#done').append($(this).parent());
		$.post(
			'done',
			'id=' + $(this).val(),
			function(data) {
				console.log(data);
				$('#todo form').children().remove();
				$('#done').children().remove();
				for (var i = 0; i < data.length; i++) {
					var div = $('<div>');
					div.attr('id', 'check' + data[i].id);
					var cb = $('<input>')
								.attr('type', 'checkbox')
								.attr('name', 'items')
								.attr('value', data[i].id);
					div.append(cb).append(data[i].item);
					if (data[i].done == null) {
						$('#todo form').append(div);
					} else {
						cb.attr('checked', 'checked');
						$('#done').append(div);
					}
				}
				init();
			});
	});
}
</script>
</head>
<body>
<h1>ToDo項目</h1>

<form method="post" action="input">
<input type="text" name="item">
<input type="submit" value="登録">
</form>

<div id="todo">
<c:if test="${list != null}">
  <form method="post" action="check">
    <c:forEach var="t" items="${list}">
      <c:if test="${t.done == null}">
        <div id="check${t.id}">
      	  <input type="checkbox" name="items" value="${t.id}" />${t.item}
        </div>
      </c:if>
    </c:forEach>
  </form>
</c:if>
</div>

<hr />
<h3>完了済み</h3>
<div id="done">
<c:if test="${list != null}">
  <c:forEach var="t" items="${list}">
    <c:if test="${t.done != null}">
      <div id="check${t.id}">
        <input type="checkbox" name="items" value="${t.id}" checked="checked" />${t.item}
      </div>
    </c:if>
  </c:forEach>
</c:if>
</div>



</body>
</html>

チェックボックスのチェックを外された時は、ToDo項目を完了済みから未完了に移動させるようにする。

サーバー側では、URL /redo を用意する。処理の内容は、done()とほぼ同じで、ToDoエンティティのcreatedの値を現在時刻にして、doneの値をnullに戻すだけの違い。

 	@RequestMapping(value = "/redo", method = RequestMethod.POST)
	@ResponseBody
	@ResponseStatus(value = HttpStatus.OK)
	public List<ToDo> redo(@RequestParam(value = "id") String id, Model model) {
		ToDoDao<ToDo> dao = new ToDoDaoImpl();
		ToDo todo = dao.findById(Long.parseLong(id));
		todo.setCreated(new Date());
		todo.setDone(null);
		dao.update(todo);
		List<ToDo> list = dao.getAll();
		return list;
	}

ブラウザ側では、完了済みにあるチェックボックスのチェックが外されたときは、redo に POST 送信するように、JavaScriptコードを書き換えればよい。

<!DOCTYPE html>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>ToDoアプリ</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script type="text/javascript">
$(function(){
	init();
})

function init() {
	$('input[name="items"]').change(function(){
		console.log($(this).prop('checked'));
		var url;
		if ($(this).prop('checked')) {
			url = 'done';
		} else {
			url = 'redo';
		}
		$.post(
			url,
			'id=' + $(this).val(),
			function(data) {
				console.log(data);
				$('#todo form').children().remove();
				$('#done').children().remove();
				for (var i = 0; i < data.length; i++) {
					var div = $('<div>');
					div.attr('id', 'check' + data[i].id);
					var cb = $('<input>')
								.attr('type', 'checkbox')
								.attr('name', 'items')
								.attr('value', data[i].id);
					div.append(cb).append(data[i].item);
					if (data[i].done == null) {
						$('#todo form').append(div);
					} else {
						cb.attr('checked', 'checked');
						$('#done').append(div);
					}
				}
				init();
			});
	});
}
</script>
</head>
<body>
<h1>ToDo項目</h1>

<form method="post" action="input">
<input type="text" name="item">
<input type="submit" value="登録">
</form>

<div id="todo">
<c:if test="${list != null}">
  <form method="post" action="check">
    <c:forEach var="t" items="${list}">
      <c:if test="${t.done == null}">
        <div id="check${t.id}">
      	  <input type="checkbox" name="items" value="${t.id}" />${t.item}
        </div>
      </c:if>
    </c:forEach>
  </form>
</c:if>
</div>

<hr />
<h3>完了済み</h3>
<div id="done">
<c:if test="${list != null}">
  <c:forEach var="t" items="${list}">
    <c:if test="${t.done != null}">
      <div id="check${t.id}">
        <input type="checkbox" name="items" value="${t.id}" checked="checked" />${t.item}
      </div>
    </c:if>
  </c:forEach>
</c:if>
</div>



</body>
</html>

Gitによるバージョン管理

Eclipseでプロジェクトを右クリックし、[チーム]-[プロジェクトの共用]を選択する。
リポジトリタイプは「Git」を選択し「次へ」をクリックする。
リポジトリの「作成」をクリックし、リポジトリディレクトリに「C:\Users\name\git」が指定されている。
「完了」ボタンが押せない場合は、フォルダ名を適当に変更して「完了」ボタンが有効になるようにし、「完了」をクリックする。
「完了」すると、プロジェクト名の右側に「NO-HEAD」の表示が出る。

プロジェクトを右クリックし、[チーム]-[コミット]を選択すると、画面の下にコミット対象となるファイルの一覧が表示される。余計なファイルがたくさん含まれているので、これを除外する設定を行う。
リポジトリ内にあるプロジェクトのフォルダ内に「.gitignore」ファイルを作成する。
エクスプローラでは新規ファイルでテキストファイル「gitignore.txt」を作成する。
コマンドプロンプトを起動してファイル名を変更する。

> ren gitignore.txt .gitignore

.gitignore ファイル内に、除外するファイルやフォルダ名を記載する。

/target
.settings/
.classpath
.project
/pleiades45

.gitignore ファイルを保存したら、Eclipseでプロジェクトを右クリックし「リフレッシュ」を選択する。
プロジェクトを右クリックし[チーム]-[コミット]を選択する。
コミット対象のファイル一覧に余計なファイルが含まれていないことを確認したら、コミットメッセージに変更理由(今回は最初なので「新規作成」でもよい)などを記載して「コミット」をクリックする。

コミットが成功したら、プロジェクト名の右側が「master」に変化する。