new wave of database programming with ruby 1.9 on rails 2.1

79
New Wave of Database Programming with Ruby 1.9 on Rails 2.1 松田 明 @ RubyKaigi 2008 LT 1

Upload: akira-matsuda

Post on 28-May-2015

4.751 views

Category:

Technology


3 download

DESCRIPTION

LT Given at RubyKaigi 2008, describing the history of DB programming style, AR, named_scope and named_scope in Ruby 1.9 syntax.

TRANSCRIPT

Page 1: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

New Wave of Database Programming with

Ruby 1.9 on Rails 2.1

松田 明 @ RubyKaigi 2008 LT

1

Page 2: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

begin

2

Page 3: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

自己紹介:名前 => 松田 明

:職業 => フリーランスの Ruby/Rails プログラマ

:仕事 => Rails

:趣味 => Rails

:仕事で書いているもの => 業務アプリが多い

3

Page 4: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

業務アプリと言えば、DB。

4

Page 5: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

人類のDBプログラミングの進化の歴史を振り返る

5

Page 6: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

WEB + DBプログラミングにおけるパラダイムの遷移

古代 → 近代 → 現代

2回ぐらいの大きなパラダイムの転換

6

Page 7: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

history

古代 → 近代 → 現代

7

Page 8: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

古代言語e.g.

P H P8

Page 9: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

:logic => 生SQLを文字列で 組み立てて発行

:view => 結果セットをぐるぐるループして HTML文字列を生成。

9

Page 10: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

サンプルコード

10

Page 11: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

省略。11

Page 12: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

古代言語の衰退に伴って 絶滅。

12

Page 13: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

history

古代 → 近代 → 現代

13

Page 14: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

大きなパラダイムの転換

14

Page 15: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

DBフレームワークの登場

• DBアクセス手段の共通化

• 設定の一元管理/コネクションのハンドリング

• RDBMS間の方言を吸収

• いわゆる O/Rマッパー

15

Page 16: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

O/Rマッパーがもたらしたもの

RDBMSと

オブジェクト指向の

幸福な出会い

16

Page 17: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

人間らしいDBプログラミング

17

Page 18: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

サンプルコード

18

Page 19: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

よくあるO/Rマッパーを使ったちょっとした業務アプリ

public interface ProductDAO { public Product find_by_id(int id); public Product find_by_name(String name); public List<Product> find_by_maker(Maker maker); public List<Product> find_by_category(Category category); public List<Product> find_by_maker_and_category(Maker maker, Category category);}

19

Page 20: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

よくあるO/Rマッパーを使ったちょっとした業務アプリ

public interface ProductDAO { public Product find_by_id(int id); public Product find_by_name(String name); public List<Product> find_by_maker(Maker maker); public List<Product> find_by_category(Category category); public List<Product> find_by_maker_and_category(Maker maker, Category category);}

Remote InterfaceImpl

EntityBeanmock Home Interface

Serializable

CORBA

RMISetter

Getter

Compile

Deploy

DBUnit

19

Page 21: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

よくあるO/Rマッパーを使ったちょっとした業務アプリ

public interface ProductDAO { public Product find_by_id(int id); public Product find_by_name(String name); public List<Product> find_by_maker(Maker maker); public List<Product> find_by_category(Category category); public List<Product> find_by_maker_and_category(Maker maker, Category category);}

Remote InterfaceImpl

EntityBeanDAO

Annotation

Dependency Injection

mock

DTODXO

Home Interface

Lazy LoadingOpen Session in View

Serializable

CORBA

RMISetter

Getter

Compile

Deploy

DBUnit

Bytecode Enhancement

19

Page 22: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

よくあるO/Rマッパーを使ったちょっとした業務アプリ

public interface ProductDAO { public Product find_by_id(int id); public Product find_by_name(String name); public List<Product> find_by_maker(Maker maker); public List<Product> find_by_category(Category category); public List<Product> find_by_maker_and_category(Maker maker, Category category);}

Remote InterfaceImpl

EntityBeanDAO

Annotation

Dependency Injection

mock

DTODXO

Home Interface

Lazy LoadingOpen Session in View

Serializable

CORBA

RMISetter

Getter

Compile

Deploy

流れるようなインターフェース(笑)

DBUnit

Bytecode Enhancement

19

Page 23: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

よくあるO/Rマッパーを使ったちょっとした業務アプリ

public interface ProductDAO { public Product find_by_id(int id); public Product find_by_name(String name); public List<Product> find_by_maker(Maker maker); public List<Product> find_by_category(Category category); public List<Product> find_by_maker_and_category(Maker maker, Category category);}

Remote InterfaceImpl

EntityBeanDAO

Annotation

Dependency Injection

mock

DTODXO

Home Interface

Lazy LoadingOpen Session in View

Serializable

CORBA

RMISetter

Getter

Compile

Deploy

流れるようなインターフェース(笑)

DBUnit

Bytecode Enhancement

XML

XML

XML

XMLXML

XML

XMLXML

XMLXML

XMLXML

XML

XMLXMLXML

XML

XML

XML

XML

19

Page 24: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

カオス(笑)

20

Page 25: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

人々がまだJavaを喋っていた時代の懐かしいお話

21

Page 26: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

history

古代 → 近代 → 現代

22

Page 27: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

次のパラダイム転換のきっかけ

23

Page 28: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

我らが ActiveRecordの登場

24

Page 29: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

•CoC をフル活用。「XML hell」と決別。

• modelクラスのアクセッサメソッドや単純な検索メソッドは DBのスキーマ定義から動的に生成。

•REST思想と見事な統合を遂げる。model == リソース。

• 柔軟なプラグイン機構

特徴

25

Page 30: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

• 言語内DSLを生かした極めて可読性の高いバリデーション

• マイグレーションも Rubyスクリプトで。

• YAML形式でさくさくテストデータを記述。テストは専用DBで。

• フィルタのようなイメージで条件を重ね合わせていくwith_scope という機能=> 「Activerecord を詳しく」by 舞波氏 参照

機能

26

Page 31: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

現代人のニーズとセンスに完璧にマッチした、変化に強い、エレガントなフレームワーク

27

Page 32: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

DB層フレームワークの決定版

28

Page 33: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

scaffoldで作る CRUDアプリ• 一覧

@models = Model.find(:all)

• 詳細@model = Model.find(params[:id])a

• 登録@model = Model.new(params[:model])@model.save

• 更新@model = Model.find(params[:id])@model.update_attributes(params[:model]

• 削除@model.destroy

29

Page 34: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

コマンド一発で自動生成

30

Page 35: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

誰でも書ける ActiveRecord

31

Page 36: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

みんなの ActiveRecord

32

Page 37: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

ご清聴ありが(ry

33

Page 38: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

「15分で作るブログ」みたいなアプリ

ならね。

34

Page 39: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

user_values = [] user_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? if project user_values += project.users.sort.collect{|s| [s.name, s.id.to_s] } else # members of the user's projects user_values += User.current.projects.collect(&:users).flatten.uniq.sort.collect{|s| [s.name, s.id.to_s] } end @available_filters["assigned_to_id"] = { :type => :list_optional, :order => 4, :values => user_values } unless user_values.empty? @available_filters["author_id"] = { :type => :list, :order => 5, :values => user_values } unless user_values.empty?

if project # project specific filters @available_filters["category_id"] = { :type => :list_optional, :order => 6, :values => @project.issue_categories.collect{|s| [s.name, s.id.to_s] } } @available_filters["fixed_version_id"] = { :type => :list_optional, :order => 7, :values => @project.versions.sort.collect{|s| [s.name, s.id.to_s] } } unless @project.active_children.empty? @available_filters["subproject_id"] = { :type => :list_subprojects, :order => 13, :values => @project.active_children.collect{|s| [s.name, s.id.to_s] } } end @project.all_custom_fields.select(&:is_filter?).each do |field| case field.field_format when "text" options = { :type => :text, :order => 20 } when "list" options = { :type => :list_optional, :values => field.possible_values, :order => 20} when "date" options = { :type => :date, :order => 20 } when "bool" options = { :type => :list, :values => [[l(:general_text_yes), "1"], [l(:general_text_no), "0"]], :order => 20 } else options = { :type => :string, :order => 20 } end @available_filters["cf_#{field.id}"] = options.merge({ :name => field.name }) end # remove category filter if no category defined @available_filters.delete "category_id" if @available_filters["category_id"][:values].empty? end

でも実際の業務アプリになると…

35

Page 40: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

やっぱりカオス・・・

36

Page 41: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

実は DBアクセスは Railsの弱点

• 結局、SQL文字列を組み立てる処理を常に意識

•CoCではどうにもならない泥臭いところ

• 複雑になればなるほど肥大化

37

Page 42: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

• 「SQLの文字列を組み立てる」

という昔ながらのダサい処理

•Ruby的オブジェクト指向

というハイセンスで華やかな世界観

この問題の根本原因

38

Page 43: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

• 「SQLの文字列を組み立てる」

という昔ながらのダサい処理

•Ruby的オブジェクト指向

というハイセンスで華やかな世界観

この問題の根本原因

大きなギャップ

38

Page 44: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

RDBMS操作 == SQL文を組み立てて発行することという先入観から脱却できていないから。

why?

39

Page 45: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

RDBMS操作 != SQL文を組み立てて発行すること

actually,

40

Page 46: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

DB操作の本質は集合演算。

41

Page 47: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

そこで、

42

Page 48: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

history

古代 → 近代 → 現代 → 2008年~

43

Page 49: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

named_scope

44

Page 50: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

named_scope とは?

• もともとは 昨秋ごろに彗星のように登場した has_finderという名前の野良 Railsプラグイン

• 今年の3月ごろ、Rails 2.1系 から正式に

Rails本体に取り込まれた

45

Page 51: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

named_scopeの機能• 問い合わせを固まりではなく断片でとらえる

「scope」

• 「scope」に名前を付けて、DSL的にあらかじめ定義

• 複雑な条件クエリの組み立ては、定義済みの「scope」を組み合わせて動的に作ることで表現できる

46

Page 52: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

example• modelnamed_scope :of_the_month, :conditions => {:reported_at => Date.today.beginning_of_month..Date.today.end_of_month}

• 呼び出し側@reports = Report.of_the_month

• 発行されるSQLSELECT * FROM "reports" WHERE ("reports"."reported_at" BETWEEN '2008-06-01' AND '2008-06-30')

47

Page 53: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

『DAO』と何が違うの?

48

Page 54: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

example(2)• modelnamed_scope :of_the_month, :conditions => {:reported_at => Date.today.beginning_of_month..Date.today.end_of_month} named_scope :written_by, Proc.new {|u| {:conditions => {:user_id => u}}}

• 呼び出し側@reports = Report.of_the_month.written_by(@current_user)

• 発行されるSQLSELECT * FROM "reports" WHERE (("reports"."user_id" = 1) AND ("reports"."reported_at" BETWEEN '2008-06-01' AND '2008-06-30'))

49

Page 55: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

カスケードして呼び出してもSQLは1回

50

Page 56: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

可読性高すぎ。

51

Page 57: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

イメージ図

52

Page 58: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

イメージ図of_the_month

52

Page 59: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

イメージ図of_the_month written_by

52

Page 60: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

イメージ図of_the_month written_by

これを select。

52

Page 61: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

まさに集合演算。

53

Page 62: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

集合がDSLで!

54

Page 63: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

named_scopeがもたらしたものRDBMS

オブジェクト指向と

集合演算の

幸福な出会い55

Page 64: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

これを実現している技術

• scopeの実体は Procのインスタンス

• チェインされた Procたちをまとめて評価 -> 実行

• Rubyの豊かな表現力を生かした、Rubyならではの実装

56

Page 65: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

単体テストも楽々(RSpecの場合)• scopeそのものの条件テスト

describe 'written_by' do it 'Userのインスタンスを受け取って user_id で検索を実行すること' do Report.written_by(@user).proxy_options.should == {:conditions => {:user_id => 1}} end

• ロジック側から然るべきscopeが正しく呼ばれていることのテスト it ‘named_scope :written_by を「ログインユーザ」を引数にして呼び出していること’ do @written_by_scope.should_receive(:call).with(Report, users(:ito)).and_return(Report) do_get end

57

Page 66: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

Rails 2.2に向けてますます機能拡張中!

• !付きメソッド

• 検索以外でも使えるように

• 詳細は

Rails勉強会@東京ブースにて配布中のペーパー参照。

58

Page 67: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

Life Changing!

59

Page 68: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

まとめ

60

Page 69: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

RubyプログラマはRubyでものを考える

61

Page 70: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

named_scopeは、泥臭い DB操作をキレイな Ruby語に翻訳するデバイス

62

Page 71: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

でも、キレイな Ruby語とか言うわりには、

• デフォルト引数を与えたい場合、こう書きたいけど書けないnamed_scope :recent, Proc.new {|time || 2.weeks.ago| {:conditions => ['reported_at > ?', time]} }

• ので、こんなふうにでも書いて逃げるしか。named_scope :recent, Proc.new {|*args| {:conditions => ['reported_at > ?', (args.first || 2.weeks.ago)]} }

63

Page 72: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

これはちょっとイケてないかも?

64

Page 73: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

そこで、Ruby 1.9。

65

Page 75: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

Ruby 1.9なら

•新しい lambda記法 -> ができた

•procにデフォルト引数がとれるようになった

67

Page 76: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

Ruby 1.9 なら• さっきのこれが、

named_scope :recent, Proc.new {|*args| {:conditions => ['reported_at > ?', (args.first || 2.weeks.ago)]} }

• こうなる。named_scope :recent, ->(time = 2.weeks.ago) { {:conditions => ["reported_at > ?", time]} }

68

Page 77: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

Ruby 1.9 is for you, Railers!

69

Page 78: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

end

70

Page 79: New Wave of Database Programming  with Ruby 1.9 on Rails 2.1

続きは WEBで!http://blog.dio.jp

71