自作のなんちゃってフレームワークでブログを作りました
タイトルの通り、自作のなんちゃってフレームワーク(言語はJava)でブログを作りました。
実際に動かしているブログはこちら。
kmaebashi.com
実装については以下の記事に書いています。
リンク先の記事にもあるとおり、このブログを動かしている「なんちゃってフレームワーク」は、ブラウザからのリクエストをServletで受けてRouterでルーティングしてControllerに処理を渡し、その後、Serviceがビジネスロジックを、DbAccessがDBへのアクセスを提供します。DbAccess層はSQLをガリガリ書きますし(結果のクラスへのマッピングだけはやる)、ControllerもServiceもDbAccessも全部staticメソッドです。SQLおじさんやstaticおじさんでも大丈夫! というよりは、実のところSQLおじさんとstaticおじさんの方が正しかったのではあるまいか。
他の特長としては、デザイナとの協業を重視した、という点があります。このフレームワークでは、デザイナが上げてきた*1HTMLを「そのまま」使います。今回のブログでは、雛型となるHTMLテンプレートは以下にも上げてありますが、
このフレームワークでは、このとおりのHTMLを読み込んで、jsoupでDOMをいじってHTMLを生成しています。実のところHTMLではないHTMLテンプレートよりも、デザイナとは共有しやすいだろう、と思っています。
ここしばらくは自作のK.Maebashi's blogの方にブログを書いていて、こっちのはてなブログはすっかりご無沙汰しておりました。
さて、今後はどっちに書くべきか。もちろん、自作のブログなんかよりはてなブログの方が高機能ではありますが、自転車でどっか行った記事なんかだと写真を大量に貼りたいことがあって、はてなブログだと写真を一度に1枚しか上げられないんですよね。そういう細かい点を自分好みにできるなら、自作ブログも悪くない、と思っていたりします。
*1:いやこのブログでは私がせこせこ書いてますけど
昔JavaScriptで作ったテトリス風ゲームに解説記事を追加しました
だいぶ前、記録によると2019年の5/19に、JavaScriptでテトリス風ゲームを作りました。
はてなブログにも記事を上げてた。
この時は、ソースを貼ったWebページこそ作ったものの、ソースの説明は一言二言ぐらいしか書いてなかったのですが、どうもこのテトリス、検索順位が高いせいか割と多くの人に見ていただいているようで、それならとソースの説明を大幅に増やしました。以下のページです。
kmaebashi.comJavaScriptでテトリスを作っているページは他にもたくさんあるかと思いますが、おそらく他と比べても飾りっ気のないシンプルなプログラムになっていると思いますので、興味のある方は読んでみてください。
テトリスはまだ難しい、という初心者の方は、以下のページをどうぞ。
たくさんの画像をまとめて縮小するプログラム「ImageShrinker」を作った
タイトルの通りですが、「旅行に行ってスマホで撮ってきたたくさんの写真を、Webページに上げて友達に見せたい」といった用途に使える画像縮小プログラムを作りました。
ソースはGitHubにあります。
画面はこんな感じです。
「旅行に行ってスマホで撮ってきたたくさんの写真を、Webページに上げて友達に見せたい」といった用途を想定したツールなので、ただ画像を縮小するのではなく、Webページに貼るためのHTML生成機能も付いています。
いまどき、「旅行に行ってスマホで撮ってきたたくさんの写真を友達に見せたい」というときにHTMLを自分で書いてWebサーバに上げる、という人はそうそういない気がしますし、そういうことが盛んだった頃に作られた同種のツールもいろいろある気がしますが(調べてない)、私自身が使いたいという理由で作りました。
ソースからコンパイルする手段を持たない人も多いでしょうから、ビルド済みのEXEも配布しています。「誰かが作った謎のEXE」を実行するのは当然リスクが伴いますので、ご理解の上ご利用ください(kmaebashi.comはSSL化しているので、私が作ったものであることは、まあ保証されていると思いますが)。
JavaScriptでカレンダーを作った話
いやさ、こんなのごちゃごちゃいうほどのもんでもない。
kmaebashi.comJavaScriptでカレンダーを作るのは別に難しい話でもないんですが、どうもカレンダーとかWebページに貼るとなると、すぐこうあれやこれやと外部ライブラリを使おうという話が出てくる。外部ライブラリを拾ってきたって使い方を覚えたり評価したりカスタマイズしたりが必要なので、こんなの自分で作る方がたぶん誰にとっても早いですよ。20年近く前、仕事でカレンダーパーツを作る必要があって、ASP.NETにカレンダーのパーツがあったので若いのがそれ使って作ろうとして、要件の一部が実装できなくて苦労していたので「そんなもん自分で作れ」と言ったらあっさりできた、ということがあったのを思い出した。
ライセンスは私の知る限りもっとも緩いライセンスであるNYSL(煮るなり焼くなり好きにしろライセンス)なので、よければ使ってやってくださいませ。
JSONパーサにクラスとの相互変換機能を付けた
ここで作ったJSONパーサに、通常のクラスのインスタンスとの相互変換機能を追加しました。
kmaebashi.hatenablog.com
上記の時点では、JSONのテキストをJsonElementという独自クラス(インタフェース)にパースし、そのサブインタフェースのJsonElementやJsonObjectやJsonValueから値を取り出す、というところまでしか実装していませんでした。この実装でもまあ役には立つでしょうし、「任意のJSONをパースしなければいけない」という状況では特に便利でしょうが、たいていのケースではJSONで送ったり受け取ったりするデータ形式は決まっているので、それ用のクラスを作ってそこにマッピングしてくれた方が便利です。元の実装では、JsonObjectはMapを保持しているので、オブジェクトのプロパティの値を取得するためにはキーを文字列で指定するようになっていたのですが、これだとキーの名前をミスタイプしてもコンパイルエラーになってくれません。私は「動的型付け言語という間違ったアイディアでこの業界は20年遠回りした」と思っている人間なので、すべてのバグはできるだけ早く、可能ならコンパイルの段階で機械に見つけてほしいと思っています。その意味でも、静的型付け言語(Java)のクラスにマッピングするほうが仕様として「正しい」と思います。
修正版のプログラムは例によってGitHubにも上げています。
github.com
具体的な使い方としては、たとえば以下のようなクラスHogeがあったとして、
public class Hoge { public int intValue; private int privateInt; // privateフィールドは対象外 @JsonIgnore public int intValueIgnore; // @JsonIgnoreを付けるとJSON化されない public Integer intObj; // Wrapperオブジェクトも対象。nullも使える。 public float floatValue; public double doubleValue; public boolean booleanValue; public String stringValue; public int[] intArray; public Integer[] intObjArray; public Hoge[] hogeArray; public List<Hoge> hogeList; public void setPrivateInt(int value) { this.privateInt = value; } }
このクラスのインスタンスをJSONに変換するには、ClassMapper.toJson()メソッドを使います。
Hoge hoge = new Hoge(); hoge.intValue = 1; hoge.setPrivateInt(999); hoge.intValueIgnore = 9999; hoge.intObj = 2; hoge.floatValue = 3.1f; hoge.doubleValue = 4.1; hoge.booleanValue = true; hoge.stringValue = "abc"; hoge.intArray = new int[] {10, 20, 30}; hoge.intObjArray = new Integer[] {11, 21, null}; hoge.hogeArray = new Hoge[2]; hoge.hogeArray[0] = new Hoge(); hoge.hogeList = new ArrayList<>(); hoge.hogeList.add(new Hoge()); hoge.hogeList.add(new Hoge()); System.out.println("json.." + ClassMapper.toJson(hoge));
結果はこうなります。floatに誤差が出るのは、まあしょうがない*1。
json..{ "intValue":1, "intObj":2, "floatValue":3.0999999046325684, "doubleValue":4.1, "booleanValue":true, "stringValue":"abc", "intArray":[ 10, 20, 30 ], "intObjArray":[ 11, 21, null ], "hogeArray":[ { "intValue":0, "intObj":null, "floatValue":0.0, "doubleValue":0.0, "booleanValue":false, "stringValue":null, "intArray":null, "intObjArray":null, "hogeArray":null, "hogeList":null }, null ], "hogeList":[ { "intValue":0, "intObj":null, "floatValue":0.0, "doubleValue":0.0, "booleanValue":false, "stringValue":null, "intArray":null, "intObjArray":null, "hogeArray":null, "hogeList":null }, { "intValue":0, "intObj":null, "floatValue":0.0, "doubleValue":0.0, "booleanValue":false, "stringValue":null, "intArray":null, "intObjArray":null, "hogeArray":null, "hogeList":null } ] }
逆にこのJSONをHogeクラスに変換もできるのですが、制限として、JSONからクラスに変換するほうではListは使えません。JavaではGenericコレクションクラスを使っても実行時には要素のクラスが特定できないためです。だからJavaのGenericsってのは……(以下自粛)
上のJSONから、hogeListに相当する部分を削除すれば、以下のようにClassMapper.toObject()でJSONからクラスのオブジェクトに変換できます。ClassMapper.toObject()の第2引数は、変換対象のクラスのClassです。
String jsonStr = """ { "intValue":1, "intObj":2, "floatValue":3.0999999046325684, "doubleValue":4.1, "booleanValue":true, "stringValue":"abc", "intArray":[ 10, 20, 30 ], "intObjArray":[ 11, 21, null ], "hogeArray":[ { "intValue":0, "intObj":null, "floatValue":0.0, "doubleValue":0.0, "booleanValue":false, "stringValue":null, "intArray":null, "intObjArray":null, "hogeArray":null, "hogeList":null }, null ] } """; Hoge hoge = ClassMapper.toObject(jsonStr, Hoge.class);
JSONでは、「ただのintの文字列("5"とか)」も立派なJSONなので、そのあたりの変換にも対応しています。
Integer int1 = ClassMapper.toObject("5", int.class); assertEquals(5, int1.intValue());
ライセンスは私の知る限りもっとも緩いライセンスであるNYSL(煮るなり焼くなり好きにしろライセンス)なので、よければ使ってやってくださいませ。
*1:Float.toString()を使うとfloatの値をきれいに文字列に変換できるようですが、このtoJson()は内部的にJsonElementを経由しているので、いったんdoubleに変換されます。
正月休みの最終日、JSONパーサを作りました
「コピペで使おう! ライブラリ」とか始めたものの現在内容がしょぼいので、正月休みの最終日である今日、JSONパーサを作りました。
朝から(といっても11:00頃ですが)からかかって20:00頃まで、途中昼食を作って食べたりおやつを食べたりとかはしたものの、割とがっつりコード書きして、現状時間切れでテストが足りないところはちょっとあるものの、以下くらいのJSONは読み込めるようになりました。
{ "array": [1, 2.0, 123, 123.456, -1, -2.0], "array2": [3e5, 3E5, 3.1e5, 3.1E5, 3.1e+5, 3.1e-5, true, false, null], "string": "abcあいうえお", "string2": "\"\\\/\t", "string3": "\u3042", "string4": "\u3042\u3044\u3046", "string5": "\uD867\uDE3D", "string6": "\uD867\uDE3D\r\n", "string7": "\uD867\uDE3D\u3042\u3044\u3046", "objSub": { "sub1": 1, "sub2": "abc" }, "objInArray": [ { "objInArray1": 1, "objInArray2": "abc" } ], "arrayInArray": [ [ 1, true, { "arrayInArray1": 1, "arrayInArray2": "abc" } ] ] }
コードは例によってGitHubに上げてあります。
github.com
もうちょっとちゃんとテストしたら、「コピペで使おう! ライブラリ」にも上げます。
以前、同じような休日コーディング(Twitter実況付き。ほんのちょっとですが)で、samplanというプログラミング言語を作ったのですが、
kmaebashi.hatenablog.com
プログラミング言語といえば当然パーサを含むので、今回作ったものもまあ似たようなプログラムではあります。ただ、ちゃんとJSONに対応しようとすると「1.2e3」のような指数表記やら「\uXXXX」のようなunicodeエンコードやらにも対応しなければいけなくて、その辺は結構面倒でした……
今回のJSONパーサは、JSONを読み込んでJsonElementというインタフェースを返します。これのサブインタフェースにJsonObject、JsonArray, JsonValueがあり、それぞれがJSONのオブジェクト、配列、値を表します。つまり、これは「任意のJSONを読み込むためのJSONパーサ」であり、「あらかじめ決められた形式のJSONを読み込むためのJSONパーサ」ではありません。もちろんあらかじめ決められた形式のJSONを読んでもよいのですが、どうせ形式が決まっているなら、クラスにマッピングするところまでやりたいですよね。その辺はまた後日。
今回、1日でテストコード除いて785行(空行込み、コメントなぞ書いてない)ばかり書いてなかなかに疲れましたが、やっぱりコードを書くのは楽しいです。コードを書き始める前に、ティーポット1杯(カップ3杯分)の紅茶を淹れたのに1杯だけ飲んで残りは忘れてしまったりとか、トイレに行く間も惜しんでコード書いてたのでトイレ行くのはいつもぎりぎりだったりとか、ひさびさに集中したコード書きができました。
しかし疲れた。明日も休みになんないかな。
「コピペで使おう! ライブラリ」なるページを作ってみました
私のWebサイトkmaebashi.comに、「コピペで使おう! ライブラリ」というページを作ってみました。
能書きはリンク先に散々書いてあるので、ここではあまり繰り返しませんが(つまり一部は繰り返すのですが)、プログラマのお仕事が、コードを読んだり書いたりではなく、「ググってライブラリを見つけ、ググって使い方を調べ、エラーが出たらググって解決策を探す」になってしまっているのは悲しいと私は思っています。
世間に山ほどあるプログラムでも、たとえ車輪の再発明でも、コードを書くのは楽しいですし、本人の力になると思っています。私は趣味で書いたプログラム(のうち他人でも使いそうなもの)をここに公開していきますので、読んだ方は、コピペして使うもよしいじりたおすもよし、あるいはこれを参考にまったく別のプログラムを作るもよし、ご自由にお使いください。
まあなんだ、既存のフレームワークやライブラリをぺたぺた組み合わせて動かすパズラーのような仕事じゃなくて、ガリガリとスクラッチからコードを書こうぜみんな。
現状の内容は以下の通り。
Java編
- CSVパーサ
シンプルなCSVパーサです。 - JDBCのPreparedStatementに名前付きパラメタ
JDBCのPreparedStatementでは、名前付きパラメタが使えず「?」しか指定できないので、何番目の?かを数えなければいけません。名前付きのパラメタが使えたら、ソースが読みやすくなります。
Spring FrameworkならNamedParameterJdbcTemplateなんてのが使えたりしますが、何もそれを使わなくても自作できますよ。 - JDBCのResultSetからクラスにマッピング
JDBCでDBを検索した検索結果ResultSetの内容をDTOとなるクラスに転記する機能です。DTOクラスの方には、DBの列と対応付けるためのアノテーションを付けておきます。
- ※にマウスオーバーで脚注を表示する
文中の「※2」とかにマウスオーバーすると、対応する脚注が読めるという機能です。はてなブログには最初からある機能ですが。そう、こんなの*1。 - <pre>要素内のコードブロックに行番号を表示する
以前はkmaebashi.comではソースを貼るときは事前に(大昔に書いた)Cのプログラムで行番号を付与していましたが、その方法だとコピペ時に行番号が付いてきてしまうので。
*1:ほら、こんなの