「Ambitionのすべて」の編集履歴(バックアップ)一覧はこちら

Ambitionのすべて - (2008/05/03 (土) 17:21:38) の1つ前との変更点

追加された行は緑色になります。

削除された行は赤色になります。

このアイデアはIRC、暗い路地、汗まみれのダンスホール、法廷で語りつくされたのを知っています。 こんな風に: SQLを書く代わりにRubyで書いてください。カーテンの後ろでSQLを生成してください。 例えばこれ: User.detect { |u| u.name == 'Jericho' && u.age == 22 } またはこれ: User.select { |u| [1, 2, 3, 4].include? u.id } さもなくばこれ: User.select { |u| u.name =~ 'rick' }.sort_by(&:age) 本当にすごいでしょう? 誤解しないでください。私たちは既にRubyでクエリを書くことが出来ます。 ez-where や Squirrel その他もろもろがありますが。しかしこれらは Ruby ぽい DSLs クエリ で Ruby ではありません。私は単に大好きな Enumerable を使いたいだけです。 古風だと呼んでください。 == Ambitious の仕事 == Erlang の Mnesia データベースは私が欲しいものに似ています。: 素の Erlang で書かれたあなたのクエリは構文木によって Mnesiaクエリ を翻訳されます。 よく出来た仕組みですが、聞いてください。: Ruby にも構文木があり、ParseTree のおかげで簡単に手に入れられます。 それでは、Ambition の始まりです。 $ sudo gem install ambition -y コンソールを使って ActiveRecord のロギングをハックして楽しんでください: $ script/console >> ActiveRecord::Base.logger = Logger.new(STDOUT) => #<Logger:0x2814134 ...> >> require 'ambition' => [] バックグラウンドで実行された SQL によって ActiveRecord モデルが使われるいくつかの例: User.first "SELECT * FROM users LIMIT 1" User.select { |m| m.name != 'macgyver' } "SELECT * FROM users WHERE users.`name` <> 'macgyver'" User.select { |u| u.email =~ /chris/ }.first "SELECT * FROM users WHERE (users.`email` REGEXP 'chris') LIMIT 1" User.select { |u| u.karma > 20 }.sort_by(&:karma).first(5) "SELECT * FROM users WHERE (users.`karma` > 20) ORDER BY users.karma LIMIT 5" User.select { |u| u.email =~ 'ch%' }.size "SELECT count(*) AS count_all FROM users WHERE (users.`email` LIKE 'ch%')" User.sort_by { |u| [ u.email, -u.created_at ] } "SELECT * FROM users ORDER BY users.email,users.created_at DESC" User.detect { |u| u.email =~ 'chris%' && u.profile.blog == 'Err' } "SELECT users.`id` AS t0_r0 ... FROM users LEFT OUTER JOIN profiles ON profiles.user_id = users.id WHERE ((users.`email` LIKE 'chris%' AND profiles.blog = 'Err')) LIMIT 1" など。README にたくさんの例をリストで確認できます。 == Kicking Around Data == A good thing to keep in mind is that queries aren’t actually run until the data they represent is requested. Usually this is done with what I call a kicker method. You can call them that, too. Kicker methods are guys like detect, each, each_with_index, map, and first (with no argument). Methods like select, sort_by, and first (with an argument) are not kicker methods and return a Query object without running any SQL. As such, you can garner some information from a Query object: >> user = User.select { |u| u.name == 'Dio' } => (Query object: call #to_sql or #to_hash to inspect...) >> user.to_sql => "SELECT * FROM users WHERE users.`name` = 'Dio'" >> user.to_hash => {:conditions=>"users.`name` = 'Dio'"} >> user.first # => SQL is run => #<User:0x36896e4 ...> Note the to_hash—Ambition doesn’t actually run any SQL, it just hands this hash to ActiveRecord::Base#find. Anyway, kickers have useful implications for Rails apps. Take this controller: class BandsController < ApplicationController def index @bands = Band.sort_by(&:name) end end Since no kicker method is called, @bands is just a Query object—no SQL run. The SQL is only run once we call each in our view: <h1>Rocktastic Bands<h1> <ul> <% @bands.each do |band| %> <li><%= band %></li> <% end %> </ul> a) キャッシュの断片 と b) クエリの再利用をしたいと言うでしょう。 Standard stuff. Two birds, one stone: <h1>Rocktastic Bands<h1> <% cache do %> <ul> <% @bands.each do |band| %> <li><%= band %></li> <% end %> </ul> <% end %> Rails がキャッシュを見つけたなら、SQLは何も実行されません。 滑らかにしてください。(キャッシュしないでください?) == The Catch == This is pretty new, so watch the sharp edges. While we aren’t good at executing arbitrary Ruby inside the block, we can handle variables. Practically speaking, instead of writing: User.select { |u| u.created_at = 2.days.ago }.first Write: date = 2.days.ago User.select { |u| u.created_at = date }.first Instance variables and simple method calls work fine, too. Expect full Ruby support in a future release. (Expect it sooner if someone sends in a patch!) == Big Dreams == Ideally, this thing could turn into something like Rack for databases. Query DataMapper, Sequel, or ActiveRecord using Ruby’s plain jane Enumerable API. Hey, maybe we can thrown an OODB or two into the mix? The usual suspects: Report feature requests, shortcomings, & bugs at Lighthouse. SVN’s at svn://errtheblog.com/svn/projects/ambition Code at http://projects.require.errtheblog.com/browser/ambition RDoc at Rock I’ve been running this on Cheat for a few days and it’s going real swell. Check the source for awesomeness like @sheets = Sheet.sort_by(&:title). Watch this space as we grow up our little ambition. Update: Oh yeah, KirinDave came up with the name Ambition. Thanks. Update 2: Okay, added some stuff tonight: You can now do cross-table sort_bys: User.sort_by { |u| [ u.profile.name, u.ideas.karma ] } That didn’t work before. Doh. I also added query support for any?, all?, and empty?—they do a COUNT behind the scenes, so feel free to give them crazy conditions. All three are kickers. Also added were the entries and to_a kickers. User.to_a is the same same as User.find(:all). Works as an all-purpose kicker, too—User.select { |u| u.name == ‘kicker’ }.to_a and whatnot. Finally, slice is now an alias for []. You guys can thank PJ for that one. The new gem is out and it’s hot. I added some empty specs for destructive and constructive methods—dangerous thinking, I know. We’ll see.
このアイデアはIRC、暗い路地、汗まみれのダンスホール、法廷で語りつくされたのを知っています。 こんな風に: SQLを書く代わりにRubyで書いてください。カーテンの後ろでSQLを生成してください。 例えばこれ: User.detect { |u| u.name == 'Jericho' && u.age == 22 } またはこれ: User.select { |u| [1, 2, 3, 4].include? u.id } さもなくばこれ: User.select { |u| u.name =~ 'rick' }.sort_by(&:age) 本当にすごいでしょう? 誤解しないでください。私たちは既にRubyでクエリを書くことが出来ます。 ez-where や Squirrel その他もろもろがありますが。しかしこれらは Ruby ぽい DSLs クエリ で Ruby ではありません。私は単に大好きな Enumerable を使いたいだけです。 古風だと呼んでください。 == Ambitious の仕事 == Erlang の Mnesia データベースは私が欲しいものに似ています。: 素の Erlang で書かれたあなたのクエリは構文木によって Mnesiaクエリ を翻訳されます。 よく出来た仕組みですが、聞いてください。: Ruby にも構文木があり、ParseTree のおかげで簡単に手に入れられます。 それでは、Ambition の始まりです。 $ sudo gem install ambition -y コンソールを使って ActiveRecord のロギングをハックして楽しんでください: $ script/console >> ActiveRecord::Base.logger = Logger.new(STDOUT) => #<Logger:0x2814134 ...> >> require 'ambition' => [] バックグラウンドで実行された SQL によって ActiveRecord モデルが使われるいくつかの例: User.first "SELECT * FROM users LIMIT 1" User.select { |m| m.name != 'macgyver' } "SELECT * FROM users WHERE users.`name` <> 'macgyver'" User.select { |u| u.email =~ /chris/ }.first "SELECT * FROM users WHERE (users.`email` REGEXP 'chris') LIMIT 1" User.select { |u| u.karma > 20 }.sort_by(&:karma).first(5) "SELECT * FROM users WHERE (users.`karma` > 20) ORDER BY users.karma LIMIT 5" User.select { |u| u.email =~ 'ch%' }.size "SELECT count(*) AS count_all FROM users WHERE (users.`email` LIKE 'ch%')" User.sort_by { |u| [ u.email, -u.created_at ] } "SELECT * FROM users ORDER BY users.email,users.created_at DESC" User.detect { |u| u.email =~ 'chris%' && u.profile.blog == 'Err' } "SELECT users.`id` AS t0_r0 ... FROM users LEFT OUTER JOIN profiles ON profiles.user_id = users.id WHERE ((users.`email` LIKE 'chris%' AND profiles.blog = 'Err')) LIMIT 1" など。README にたくさんの例をリストで確認できます。 == Kicking Around Data == A good thing to keep in mind is that queries aren’t actually run until the data they represent is requested. Usually this is done with what I call a kicker method. You can call them that, too. Kicker methods are guys like detect, each, each_with_index, map, and first (with no argument). Methods like select, sort_by, and first (with an argument) are not kicker methods and return a Query object without running any SQL. As such, you can garner some information from a Query object: >> user = User.select { |u| u.name == 'Dio' } => (Query object: call #to_sql or #to_hash to inspect...) >> user.to_sql => "SELECT * FROM users WHERE users.`name` = 'Dio'" >> user.to_hash => {:conditions=>"users.`name` = 'Dio'"} >> user.first # => SQL is run => #<User:0x36896e4 ...> Note the to_hash—Ambition doesn’t actually run any SQL, it just hands this hash to ActiveRecord::Base#find. Anyway, kickers have useful implications for Rails apps. Take this controller: class BandsController < ApplicationController def index @bands = Band.sort_by(&:name) end end Since no kicker method is called, @bands is just a Query object—no SQL run. The SQL is only run once we call each in our view: <h1>Rocktastic Bands</h1> <ul> <% @bands.each do |band| %> <li><%= band %></li> <% end %> </ul> a) キャッシュの断片 と b) クエリの再利用をしたいと言うでしょう。 Standard stuff. Two birds, one stone: <h1>Rocktastic Bands<h1> <% cache do %> <ul> <% @bands.each do |band| %> <li><%= band %></li> <% end %> </ul> <% end %> Rails がキャッシュを見つけたなら、SQLは何も実行されません。 滑らかにしてください。(キャッシュしないでください?) == The Catch == This is pretty new, so watch the sharp edges. While we aren’t good at executing arbitrary Ruby inside the block, we can handle variables. Practically speaking, instead of writing: User.select { |u| u.created_at = 2.days.ago }.first Write: date = 2.days.ago User.select { |u| u.created_at = date }.first Instance variables and simple method calls work fine, too. Expect full Ruby support in a future release. (Expect it sooner if someone sends in a patch!) == Big Dreams == Ideally, this thing could turn into something like Rack for databases. Query DataMapper, Sequel, or ActiveRecord using Ruby’s plain jane Enumerable API. Hey, maybe we can thrown an OODB or two into the mix? The usual suspects: Report feature requests, shortcomings, & bugs at Lighthouse. SVN’s at svn://errtheblog.com/svn/projects/ambition Code at http://projects.require.errtheblog.com/browser/ambition RDoc at Rock I’ve been running this on Cheat for a few days and it’s going real swell. Check the source for awesomeness like @sheets = Sheet.sort_by(&:title). Watch this space as we grow up our little ambition. Update: Oh yeah, KirinDave came up with the name Ambition. Thanks. Update 2: Okay, added some stuff tonight: You can now do cross-table sort_bys: User.sort_by { |u| [ u.profile.name, u.ideas.karma ] } That didn’t work before. Doh. I also added query support for any?, all?, and empty?—they do a COUNT behind the scenes, so feel free to give them crazy conditions. All three are kickers. Also added were the entries and to_a kickers. User.to_a is the same same as User.find(:all). Works as an all-purpose kicker, too—User.select { |u| u.name == ‘kicker’ }.to_a and whatnot. Finally, slice is now an alias for []. You guys can thank PJ for that one. The new gem is out and it’s hot. I added some empty specs for destructive and constructive methods—dangerous thinking, I know. We’ll see.

表示オプション

横に並べて表示:
変化行の前後のみ表示:
記事メニュー
人気記事ランキング
目安箱バナー