読者です 読者をやめる 読者になる 読者になる
Nao Minami's Blog

Software Engineer at Wantedly, Inc.

ブログテーマを変えてみました

2016年にもなって心機一転という事で、ブログテーマを更新してみました。

参考にしたのは Svbtle というブログサービスのデザインです。 Swift のテストフレームワーク Quick で有名な modocache さんが Svbtle を使って記事を書いていて、あまりにカッコよかったのでついつい真似してしまいました。ほぼパクりみたいになっちゃったのは反省してます。

Medium だったり Svbtle だったり、最近は顔出しが流行ってるみたいなのでそこも真似してみました。若干の恥ずかしさはありますが、気にしない事にします。

2016年の抱負

2016年の抱負を書きたいと思います。去年は Wantedly に新卒として入社して、目の前の仕事を必死でこなしていたらあっという間に1年が過ぎました。若干の焦りを覚えていて、特にアウトプットがすごく少なかったのが反省点だと思っています(このブログも1回しか更新しませんでした)。

2016年はブログや発表といった形でアウトプットを増やしていけたらと思ってます。

最近のマイブーム

最近は、値段が高くて学生時代には手が出せなかった「古典的名著」を買うのがマイブームになってます。TAPL や ドラゴンブック、赤い悪魔本など、買って満足してどんどん積み上がっていってます。積み上げるだけだと勿体無いので、少しずつでも読んでいこうと思います。

YAPC::Asia 2015に行ってきた!!

8/21, 22の2日間 YAPC::Asia に行ってきた。

YAPC::Asia の存在は前から知っていて、でも Perl よくわからないしお金無いし。。とか言って去年は行かなかったんだけど、今年は働き始めてチケット代くらいは出せるようになったので勢いのままに Early Bird Ticket を買って行ってきた。

丸2日行ったけど、振り返ってみたらなんだかあっという間だった。

自分が聞いたトーク一覧

面白かったトーク

TBD by Matz

このトークについては toggetter を見ると雰囲気伝わると思うけど、とにかく Matz さんの話の面白さとトークの完成度の高さが最高だった。 http://togetter.com/li/863417

Ruby作者だからRubyをdisれる」とか「Rubyの良くないところはPerlの影響を受けたこと」とか問題発言もぶち込みつつ、言語デザイナーとしての考えとかが聞けたのが良かった。

Matz さんは将来的に「ほとんどコードを書く必要が無い未来」が来ると考えてて(1ファイルくらいのスクリプトで考えた事が実現できる未来、おそらく高度に抽象化された基盤の上に乗ってる)、昔は自分もそういった未来が来ると無邪気に考えてたけど今はそう思えなくなってるのを実感した。

自分は基盤となるライブラリやフレームワークが全てをカバーするのは無理なんじゃ無いかと思ってる。例えば Ruby on Rails みたいなフレームワークを使うとちょっとした DSL を書くだけで Web アプリケーションに必要な機能が作れるけど、Railを外れると自分でガリゴリ実装を書く必要がある。Rails ほどの規模であっても全てはカバー出来ない事を考えると、短いコードだけで 全て を実現出来る日は来ないんじゃ無いかと思う( 全て じゃなくて よくあるユースケース を抑えるだけなら出来ると思うし、実際そういった流れはあると思うけど)

とりあえず雑にまとめると、言語デザイナーとしての生き方が楽しそうだった。

実践nginxモジュール開発〜CとLua

cubicdaiya さんのトーク。mosaic の nginx 回を聞いてどんな人か気になってたので、話が聞けて良かった。

ngx_small_light の採用事例として弊社(Wantedly)の nginx-image-server が取り上げられてたのはちょっと嬉しかった。 https://github.com/wantedly/nginx-image-server

トークの前半は nginx のモジュールの作り方の説明で、用意しなきゃいけない struct とかが決まっててお作法は覚えなきゃいけないけど、簡単な処理をするモジュールくらいなら作れそうに感じた。

後半は ngx_lua はパワフルで良いよーってお話しで、提供されてる API に詳しくなれた。ngx_lua は昔一瞬だけ使ったことがあって、その時は外部プロセスを一瞬起動するみたいな簡単な処理をしただけだったけど、それでもすごく使いやすかったのが印象的だった。Webサーバにスクリプティング環境があるのは、夢があると思う。

【特別企画】YAPCあるある(仮)

まさにオールスター企画。
miyagawa さん、takesako さん、maki さん、941 さんみたいなこれまでに YPAC を運営してきた人たちによる YAPC の振り返りトークで、安定して面白かった。

toggeter 見ると盛り上がりがわかりそう http://togetter.com/li/863907

HTTP2 時代の Web

Jxck さんの HTTP2 についてのトーク。ちょうど最近 「ハイパフォーマンスブラウザネットワーキング」を読んでたので自分の中ではタイムリーな話しだった(今年の初めに RFC化されたから世間的にも注目浴びてるけど)。

multiplex で速くなるっていうのは知識としては知ってたけど、画像を使ったデモがすごく分かりやすい & 印象的で良かった。 速い って一口で言ってもいろんな速さがあって、例えば html 降ってくるのが速くなってもフロントで詰まる事は全然あって、大きな画像がページの上部に6枚あればそれで簡単にコネクションが占有されてしまうというのが印象的だった。

HTTP/2 を導入すべきかどうかみたいな話しもあるけど、 rebuild で oku さんも話してたみたいに apache や nginx みたいな Web サーバーがどうせ実装する(&ブラウザは既に実装してる)から、素直にみんな使うようになるんじゃないかと思う。

全体の所感

著名な人が集まってて、オールスター感がすごかった。 特に、 rebuild や mosaic みたいなPodcastを最近は聴きまくってたので、そこに出てた人が喋ってるのを見れたのが嬉しかった。(Jxck さんとか声のイメージしか無かったので、こんな人だったのかーみたいな)

残念だったのは、5トラックあるから休まず出てもトーク全体の 20 % しか聞けない事。これはまあ構造上仕方が無い事ではあるんだけど、面白そうなトークが被りまくってて他の直接聞けないのは悲しいと思った。 直接トーク聞いて感じたのはスライドだけでは伝わらないものがけっこうあるって事で(例えばスライドに載せてないけど話してた重要な事とか会場での盛り上がりとか)、よく発表したスライドが公開されてるけどトークの動画も公開して欲しいと感じた。

全体として、YAPCが開催されて、こうやってYAPCに行けて良かったと思う。 スタッフ、スピーカー、参加者のみなさん、お疲れさま & ありがとうございました。

気がついたら一ヶ月経ってた

前回ブログを書いてから、気がついたら一ヶ月経ってた。びっくり。

続きを読む

webmockすごい

エラー出ても使い方を教えてくれて優しい

$ ruby webmock.rb
/Users/(ユーザー名)/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/webmock-1.20.4/lib/webmock/http_lib_adapters/net_http.rb:114:in `request': Real HTTP connections are disabled. Unregistered request: GET http://www.example.com/ with headers {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'} (WebMock::NetConnectNotAllowedError)

You can stub this request with the following snippet:

stub_request(:get, "http://www.example.com/").
  with(:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'User-Agent'=>'Ruby'}).
  to_return(:status => 200, :body => "", :headers => {})

registered request stubs:

stub_request(:any, "http://www.example.com/").
  with(:body => "abc",
       :headers => {'Content-Length'=>'3'})

============================================================
  from /Users/(ユーザー名)/.rbenv/versions/2.1.5/lib/ruby/2.1.0/net/http.rb:1280:in `request_get'
   from /Users/(ユーザー名)/.rbenv/versions/2.1.5/lib/ruby/2.1.0/net/http.rb:474:in `block in get_response'
  from /Users/(ユーザー名)/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/webmock-1.20.4/lib/webmock/http_lib_adapters/net_http.rb:123:in `start_without_connect'
    from /Users/(ユーザー名)/.rbenv/versions/2.1.5/lib/ruby/gems/2.1.0/gems/webmock-1.20.4/lib/webmock/http_lib_adapters/net_http.rb:150:in `start'
  from /Users/(ユーザー名)/.rbenv/versions/2.1.5/lib/ruby/2.1.0/net/http.rb:473:in `get_response'
   from /Users/(ユーザー名)/.rbenv/versions/2.1.5/lib/ruby/2.1.0/net/http.rb:455:in `get'
  from webmock.rb:7:in `<main>'

Pryではトップレベルで定義したメソッドがObjectクラスのpublicなインスタンスメソッドになる

表題の通り。

今日Pry触ってて気づいたんだけど、Pryのトップレベルで定義したメソッドはObjectクラスのpublicなインスタンスメソッドになるっぽくて、大抵のオブジェクトから呼び出せる様になる。意図した仕様なのかバグなのかは分からないけど、びっくりしたからメモっとく。

ちなみにRubyのversionは2.1.5。Pryは普通にgem installした。irbRubyを普通に起動した場合には挙動が違ってて、privateなインスタンスメソッドになる。

$ ruby -v
ruby 2.1.5p273 (2014-11-13 revision 48405) [x86_64-darwin14.0]
$ pry -v
Pry version 0.10.1 on Ruby 2.1.5

何に驚いたのか?

以下にコードで例を示す。

[1] pry(main)> def my_method
[1] pry(main)*   p 'my_method'
[1] pry(main)* end
=> :my_method
[2] pry(main)> [].my_method // arrayオブジェクトのメソッドとしてmy_methodが呼び出せる!!
"my_method"
=> "my_method"
[3] pry(main)> [].methods.include?(:my_method) // arrayオブジェクトの呼び出し可能なメソッドにmy_methodが含まれてる!!
=> true

トップレベルで定義したmy_methodメソッドが、空のarrayオブジェクトである[]から呼び出せている。割と驚きの挙動。

なぜこうなるのか?

表題にも書いたけど、Objectクラスのインスタンスメソッドとしてmy_methodが定義されてるのが原因。Objectクラスはほとんどのオブジェクトの上位クラスに位置しているので、大抵のオブジェクトからmy_methodが呼び出せるようになる。

[4] pry(main)> Object.instance_methods(false).include?(:my_method) // instance_methods(false)で、そのクラスで定義されたinstance_methodsが取得できる
=> true
[5] pry(main)> [].class
=> Array
[6] pry(main)> [].class.superclass // Arrayの上位クラスにObjectが位置している
=> Object

自分で適当に定義したクラスもObjectクラスを継承した状態になるので、そこから生成したオブジェクトもmy_methodを呼び出せる。

[7] pry(main)> class MyClass; end
=> nil
[8] pry(main)> my_obj = MyClass.new
=> #<MyClass:0x007fbf6972ce20>
[9] pry(main)> my_obj.my_method
"my_method"
=> "my_method"

Objectよりもさらに上位のBaicObjectを継承すれば、こうはならない。

[10] pry(main)> class MyBasicClass < BasicObject; end
=> nil
[11] pry(main)> my_basic_obj = MyBasicClass.new
=> #<MyBasicClass:0x3fdfb62dab38>
[12] pry(main)> my_basic_obj.my_method
NoMethodError: undefined method `my_method' for #<MyBasicClass:0x007fbf6c5b5670>
from (pry):12:in `__pry__'

ここまでがPryでの話。

irbRubyコマンドで起動した場合はどうなるか

Objectのprivateなインスタンスメソッドとして定義されるので、外部から呼び出しは出来ない。

irb(main):001:0> def my_method
irb(main):002:1>   p 'my_method'
irb(main):003:1> end
=> :my_method
irb(main):004:0> [].my_method
NoMethodError: private method `my_method' called for []:Array
  from (irb):4
  from /Users/(ユーザ名)//.rbenv/versions/2.1.5/bin/irb:11:in `<main>'

じゃあ何でこんな挙動(わざわざObjectクラスにprivateなインスタンスメソッドとして定義)になってるかと言うと、オブジェクトのメソッドを定義する時なんかにトップレベルで定義されたメソッドを使えるようにする為。

irb(main):005:0> class MyClass
irb(main):006:1>   def try_2_my_method
irb(main):007:2>     my_method           // my_methodが使える!!
irb(main):008:2>     my_method           // my_methodが使える!!
irb(main):009:2>   end
irb(main):010:1> end
=> :try_2_my_method
irb(main):011:0> my_obj = MyClass.new
=> #<MyClass:0x007fefdb285938>
irb(main):012:0> my_obj.try_2_my_method
"my_method"
"my_method"
=> "my_method"

要は、Rubyではいわゆるグローバルスコープ的な機能を提供する為にObjectクラスを利用してるっぽい。

この事は、BaiscObjectを継承するクラスの中でインスタンスメソッドを定義する時にはトップレベルで定義したメソッドが使えない事からも確認できる。

irb(main):013:0> class MyBasicClass < BasicObject
irb(main):014:1>   def try_3_my_method
irb(main):015:2>     my_method
irb(main):016:2>     my_method
irb(main):017:2>     my_method
irb(main):018:2>   end
irb(main):019:1> end
=> :try_3_my_method
irb(main):020:0> my_basic_obj = MyBasicClass.new
(Object doesn't support #inspect)
=>
irb(main):021:0> my_basic_obj.try_3_my_method
NameError: undefined local variable or method `my_method' for #<MyBasicClass:0x007fefdc86dc50>
    from (irb):15:in `try_3_my_method'
  from (irb):21
  from /Users/(ユーザ名)/.rbenv/versions/2.1.5/bin/irb:11:in `<main>'

try_3_my_methodメソッドの定義の中でmy_methodを呼び出そうとしてエラーが出てる事が分かる。my_methodはグローバルに定義されてる訳では無くて、あくまでObjectクラスのインスタンスメソッドとして定義されてる事が確認出来たと言える。

まとめ

Pryの驚きの挙動から始めて、Rubyメソッドスコープの仕組みについての知見を得た。あとどうでも良いかもだけど、methodsとかinstance_methodsとかprivate_instance_methodsとかsingleton_methodsみたいな定義されたメソッドをarrayで取得する系のメソッドが今回かなり役立ったので、使っていくと良いと思う。特に、falseを引数として渡すと上位クラス由来のものは取り除いてくれるので、どこでメソッドが定義されてるかが特定しやすかった。

7つのデータベース7つの世界を読んだ

タイトルは半分嘘で、全部じゃなくて途中まで読んだ。具体的には、第2章のPostgreSQL、第3章のRiak、第4章のHBase(Hadoop)の1,2日目、第5章のMongoDB、第8章のRedisの1,2日目。CouchDBとNeo4jはイントロだけ。

PostgreSQL

postgresqlは一応慣れ親しんでる(?)はずだけど、知らない事・使った事の無い機能がいっぱい載ってた。というか、普段はActiveRecord越しでイジるかデータの確認にシェルでselectするかぐらいでしか使ってなかったので、実質的には慣れ親しんで無かったんだと思う。

2日目で言うと、ストアドプロシージャ、トリガー、ビュー、クロス集計は使った事が無い機能だった。


ストアドプロシージャとは

DB側である程度まとまった処理を行いたい時に、プロシージャとして名前をつけて処理を保存しておける機能。「7つのデータベース 7つの世界」では、例としてテーブルA上に特定のレコードaがあるかを確認して、無ければaを作成してからテーブルB上にaと関連づけられたレコードbを追加するというストアドプロシージャのコードが載っていた。レコードの「確認(select)」と「追加(insert)」を1回でまとめて行えるので、パフォーマンスも上がるらしい。

ちょっと自分でもコードを書いてみようと思う。

データベースを作る。

いろいろ試すために、まずは練習用のデータベースを作成する。psqlコマンドでシェルを立ち上げて、以下のコマンドを実行。seven_databaseテーブルが作られる。

user=# create database seven_database; // terminalでcreatedbでも良い
CREATE DATABASE
user=# \c seven_database
You are now connected to database "seven_database" as user "(user_name)".

これでOK。次に、contribパッケージとしてcube等が必要になるので、インストールする。一番楽なのはCREATE EXTENSION (パッケージ名)でインストールする事らしいので、tablefunc, dict_xsyn, fuzzystrmatch, pg_trgm, cubeについて順に実行してインストール。

現在のデータベースにインストールされたパッケージは、pg_extensionビューで確認できる。

seven_database=# select extname from pg_catalog.pg_extension ;
    extname
---------------
 plpgsql
 tablefunc
 dict_xsyn
 fuzzystrmatch
 pg_trgm
 cube
(6 rows)

ちゃんとインストールされている事が分かる。

次に、テーブルとして7つのデータエース 7つの世界に載ってたgenres, movies, actors, movie_actorsテーブルを作る。

このページにおいてあるコードをダウンロードしてpsql -d seven_database < create_movies.sqlで実行するか、シェルから以下のSQLを実行。

CREATE TABLE genres (
    name text UNIQUE,
    position integer
);
CREATE TABLE movies (
    movie_id SERIAL PRIMARY KEY,
    title text,
    genre cube
);
CREATE TABLE actors (
    actor_id SERIAL PRIMARY KEY,
    name text
);

CREATE TABLE movies_actors (
    movie_id integer REFERENCES movies NOT NULL,
    actor_id integer REFERENCES actors NOT NULL,
    UNIQUE (movie_id, actor_id)
);
CREATE INDEX movies_actors_movie_id ON movies_actors (movie_id);
CREATE INDEX movies_actors_actor_id ON movies_actors (actor_id);
CREATE INDEX movies_genres_cube ON movies USING gist (genre);

これでOK。

ストアドプロシージャを作ってみる

とりあえず書籍をパクって、moviesテーブルにレコードがあるかを確認して、無ければ作成してからそれに関連付けられたレコードをactorsテーブルにinsertしてみる。

  1 CREATE OR REPLACE FUNCTION add_actor_with_movie( actor_name text, movie_title text, movie_genre cube )
  2 RETURNS boolean AS $$
  3 DECLARE
  4   did_insert   boolean := false;
  5   found_count  integer;
  6   the_movie_id integer;
  7   the_actor_id integer;
  8 BEGIN
  9   SELECT movie_id INTO the_movie_id
 10   FROM movies m
 11   WHERE m.title=movie_title AND m.genre=movie_genre
 12   LIMIT 1;
 13
 14   IF the_movie_id IS NULL THEN
 15     INSERT INTO movies (title, genre)
 16     VALUES (movie_title, movie_genre)
 17     RETURNING movie_id INTO the_movie_id;
 18
 19     did_insert := true;
 20   END IF;
 21
 22   -- Note: not an “error”, as in some programming languages
 23   RAISE NOTICE 'Venue found %', the_movie_id;
 24
 25   INSERT INTO actors (name)
 26   VALUES (actor_name)
 27   RETURNING actor_id INTO the_actor_id;
 28
 29   INSERT INTO movies_actors(movie_id, actor_id)
 30   VALUES (the_movie_id, the_actor_id);
 31
 32   RETURN did_insert;
 33 END;
 34 $$ LANGUAGE plpgsql;

とりあえずこんな感じで書いたらちゃんと動いた。32行目でRETURN did_insertしてて、実際にmovieのinsertを行った場合はtが返ってきてる。

seven_database=# SELECT add_actor_with_movie('Taro', 'Star Wars','(0,7,0,0,0,0,0,0,0,7,0,0,0,0,10,0,0,0)');
NOTICE:  Venue found 2
 add_actor_with_movie
----------------------
 t
(1 row)

'Star Wars'に違うactorを紐づけて実行すると、ちゃんとactorはinsertされるけど返り値はfになる。

seven_database=# SELECT add_actor_with_movie('Jiro', 'Star Wars','(0,7,0,0,0,0,0,0,0,7,0,0,0,0,10,0,0,0)');
NOTICE:  Venue found 2
 add_actor_with_movie
----------------------
 f
(1 row)

とりあえず想定通りの挙動はしてる。

ハマりポイント

ストアドプロシージャの引数名がtableのカラム名と一致してるとエラーが出た。下の実行例は、ストアドプロシージャの引数名としてtitleを使ってたら、movie tableのtitleカラムと同じって事でエラーが出てる。

seven_database=# SELECT add_actor_with_movie('Saburo', 'Star Wars','(0,7,0,0,0,0,0,0,0,7,0,0,0,0,10,0,0,0)');
ERROR:  column reference "title" is ambiguous
LINE 2:   WHERE m.title=title AND m.genre=movie_genre
                        ^
DETAIL:  It could refer to either a PL/pgSQL variable or a table column.
QUERY:  SELECT movie_id                     FROM movies m
  WHERE m.title=title AND m.genre=movie_genre
  LIMIT 1
CONTEXT:  PL/pgSQL function add_actor_with_movie(text,text,cube) line 8 at SQL statement

これをErrorにするのは許せない。。。

あと、途中でいくつか作り替えながら試してみたら、同じadd_actor_with_movieという名前のストアドプロシージャを複数作ってしまって削除の必要が生じた。どうやら、引数の型が違うと同じ名前でも別のプロシージャとして保存されるっぽい。

このページいわく

drop function プロシージャ名([引数の型])

で削除できたので、drop function add_actor_with_movie(text, text, cube);で削除した。あと、引数の型は変えずに名前だけ変えようとすると既存のプロシージャを消してくれってエラーが出た。

seven_database=# \i my_code/add_actor_with_movie.sql
psql:my_code/add_actor_with_movie.sql:34: ERROR:  cannot change name of input parameter "title"
HINT:  Use DROP FUNCTION add_actor_with_movie(text,text,cube) first.

いったんまとめ

学んだ機能を順に説明してくつもりがストアドプロシージャだけでそこそこ時間たっちゃったので、今日はここまで。ストアドプロシージャは強力だけどデータベースにロジックが激しく依存してしまうので、使いどころは難しそう。覚悟の上で使うなら良いと思う。

SQLアンチパターンを読んだ

3ヶ月くらい積読されてたのを引っ張り出して読んだ。例によって、読み始めるとめちゃめちゃ面白くて何故こんな素晴らしいものを積読してたんだ...って感じだった。

ざっくりとした感想

けっこう色んな人が読んだ感想とか内容を簡単にまとめたものとかを書いてるので、ぶっちゃけ今更な気もするけど簡単にまとめとく。

この本ではSQL(というかRDB)のスキーマ設計とかクエリの投げ方とかで陥りがちなアンチパターンを25個に絞って順に解説していってる(1章で1つのテーマを扱うので、ちょうど25章ある)。25章とか多すぎるんじゃってビビってたけど、むしろ1章1章のボリュームは抑え気味で簡潔にまとまってて読みやすかった。

アンチパターンって言っても頭ごなしに否定する訳じゃなくて、どういう意図があってそのアンチパターンを採用してしまうのかとか、より良い解決策は何なのかとか、そういった事が順に説明されてく構成だった。「何を避けるべきか」だけで無く、「どう進むべきか」の指針も打ち出してくれてて、嬉しかった。

自分にとってはⅠ部の「データベース論理設計のアンチパターン」で扱われてた5~8章が印象的で、特に「データのメタデータへの混入」や「メターデータのデータへの混入」を避けよう、みたいな特定のパターンに依存しない心がけを知れたのが良かった。8章の「メタデータトリブル」で扱われてたケース(データであるはずの「年代」をメタデータであるテーブル名として採用してしまう)はけっこうやってしまいそうな気がしたので、まずいって感覚を掴めたのは良かったと思う。

24章. マジックビーンズについて

24章はモデルをアクティブレコードそのものとして扱うことへの弊害について書かれていた。この辺のこと(特にRailsにおいてActiveRecord::Baseを継承したオブジェクトがmodelとして扱われること)はこれまでに多くの人がいろんな議論をしていて、自分の中でもいろいろ思うところがあったので、ちょっとまとめておく。

まずは用語の定義から。マーティン・ファウラーがPoEAAで取り上げた「アクティブレコードパターン」はモデルオブジェクトのクラスをテーブルと対応付け、1つのオブジェクトをテーブルの1行と、オブジェクトの1つのフィールドをテーブルの1つの列と対応付けるというもの。アクティブレコードは基本的なCRUD操作をサポートする。実は今挙げた点だけだと「Row Data Gatewayパターン」(これもPoEAAで取り上げられてるもの)と全然違いはなくて、ただアクティブレコードは domain logicを含む という点でRow Data Gatewayとは違う。

よく問題だと言われるのが、アクティブレコードをデータアクセスオブジェクト(DAO)としてしか使わず、Controllerにロジックを書いてしまうというもの。正直、これに関してはModelやControllerの役割を理解してれば絶対にしない事だからあまり問題じゃ無いと思う。

また、別の点でよく突っ込まれるのは

  • アクティブレコードはモデルをデータベーススキーマに強く依存させてしまう
  • アクティブレコードを用いると1つのクラスで扱う仕事が多くなり過ぎる

みたいな点で、これはまあその通りかなと思う。というか、アクティブレコードだけを用いてモデル層を記述しようとすれば、そりゃこういった問題は出て来るだろうという感じ。

PoEAAの中でマーティン・ファウラーも言ってるんだけど、そもそもアクティブレコードはロジックの薄いモデルを扱うときに便利なパターンで、複雑なロジックを記述するには向いていない。だからまあ複雑になってきたらDAOとしての機能は切り出して別のモデルクラスにロジックは書くとか、そういった感じで対処すれば良いしそれが望まれてるんだと思う。

この辺は既に経験豊かな優秀な人たちがたくさん議論してて、Web上にもいっぱい情報が落ちてるのでそういった情報を参考にしながら適宜対処していけば良さそう。

まとめ

まとめとくと、SQLアンチパターンは良い本なので、読むと良い。と思う。