Home - mongomapper - GitHub

What is MongoMapper?

MongoMapper is a Ruby wrapper library which aims to make using MongoDB much easier and friendlier than the default Ruby driver provided by XGen. When it makes sense to do so, MongoMapper tries to stick closely with the familiar syntax of ActiveRecord.

Because of the way MongoDB stores data, there are two key concepts when working with MongoMapper: the Document, and the EmbeddedDocument.

The Document is essentially a record with fields, just as you’d expect.

An Embedded Document is exactly like a Document, except that it is injected into a Document and still retains all of its information.

Because of the way MongoMapper works, all of this is transparent to you. With MongoMapper, when you retrieve a record with an embedded document attached to it, the embedded document’s information is retrieved along with it, which is where MongoMapper truly shines. By embedding when it makes sense to do so, you are able to retrieve all of the typical join information in one speedy query.

More reading…

Mongoid

Mongoid [mon-goyd] - A Ruby ODM Framework for MongoDB
1.0.0

Mongoid provides an easy to use API for working with documents in MongoDB.

Features:

  1. - Effortless mapping of Ruby objects to documents and embedded documents.
  2. - Rich criteria API and chained scopes for document retrieval from the database.
  3. - Support for versioning of documents.
  4. - Full callback support on documents and embedded documents.
  5. - Compatibility for most modern Rails targeted frameworks.

Ruby Freaks Lounge:第11回 自由なWebフレームワーク,Ramaze|gihyo.jp … 技術評論社

シンプルなフレームワークといえば,第7回第9回で紹介したSinatraもそうでした。SinatraはDSLを駆使して独自の世界を作っていましたが,RamazeはRubyのやり方をできるだけ踏襲します。実際のアプリケーションの例を見てみましょう。

DataMapper - Why DataMapper?

Why DataMapper?

DataMapper differentiates itself from other Ruby Object/Relational Mappers in a number of ways:

Identity Map

One row in the database should equal one object reference. Pretty simple idea. Pretty profound impact. If you run the following code in ActiveRecord you’ll see all false results. Do the same in DataMapper and it’s true all the way down.

1 @parent = Tree.first(:conditions => { :name => 'bob' })
2 
3 @parent.children.each do |child|
4   puts @parent.object_id == child.parent.object_id
5 end

This makes DataMapper faster and allocate less resources to get things done.

Plays Well With Others

With DataMapper you define your mappings in your model. Your data-store can develop independently of your models using Migrations.

To support data-stores which you don’t have the ability to manage yourself, it’s simply a matter of telling DataMapper where to look.

1 class Fruit
2   include DataMapper::Resource
3 
4   storage_names[:default] = 'frt' # equivalent to set_table_name in AR
5 
6   property :id,   Serial
7   property :name, String, :field => 'col2'
8 end

DataMapper only issues updates or creates for the properties it knows about. So it plays well with others. You can use it in an Integration Database without worrying that your application will be a bad actor causing trouble for all of your other processes.

Laziness Can Be A Virtue

Columns of potentially infinite length, like Text columns, are expensive in data-stores. They’re generally stored in a different place from the rest of your data. So instead of a fast sequential read from your hard-drive, your data-store has to hop around all over the place to get what it needs.

With DataMapper, these fields are treated like in-row associations by default, meaning they are loaded if and only if you access them. If you want more control you can enable or disable this feature for any column (not just text-fields) by passing a lazy option to your column mapping with a value of true or false.

1 class Animal
2   include DataMapper::Resource
3 
4   property :id,    Serial
5   property :name,  String
6   property :notes, Text    # lazy-loads by default
7 end

Plus, lazy-loading of Text property happens automatically and intelligently when working with associations. The following only issues 2 queries to load up all of the notes fields on each animal:

1 animals = Animal.all
2 animals.each do |pet|
3   pet.notes
4 end

Strategic Eager Loading

DataMapper will only issue the very bare minimums of queries to your data-store that it needs to. For example, the following example will only issue 2 queries. Notice how we don’t supply any extra :include information.

1 zoos = Zoo.all
 2 zoos.each do |zoo|
 3   # on first iteration, DM loads up all of the exhibits for all of the items in zoos
 4   # in 1 query to the data-store.
 5 
 6   zoo.exhibits.each do |exhibit|
 7     # n+1 queries in other ORMs, not in DataMapper
 8     puts "Zoo: #{zoo.name}, Exhibit: #{exhibit.name}"
 9   end
10 end

The idea is that you aren’t going to load a set of objects and use only an association in just one of them. This should hold up pretty well against a 99% rule.

When you don’t want it to work like this, just load the item you want in it’s own set. So DataMapper thinks ahead. We like to call it “performant by default”. This feature single-handedly wipes out the “N+1 Query Problem”.

DataMapper also waits until the very last second to actually issue the query to your data-store. For example, zoos = Zoo.all won’t run the query until you start iterating over zoos or call one of the ‘kicker’ methods like #length. If you never do anything with the results of a query, DataMapper won’t incur the latency of talking to your data-store.

All Ruby, All The Time

DataMapper goes further than most Ruby ORMs in letting you avoid writing raw query fragments yourself. It provides more helpers and a unique hash-based conditions syntax to cover more of the use-cases where issuing your own SQL would have been the only way to go.

For example, any finder option that are non-standard is considered a condition. So you can write Zoo.all(:name => 'Dallas') and DataMapper will look for zoos with the name of ‘Dallas’.

It’s just a little thing, but it’s so much nicer than writing Zoo.find(:all, :conditions => [ 'name = ?', 'Dallas' ]) and won’t incur the Ruby overhead of Zoo.find_by_name('Dallas'), nor is it more difficult to understand once the number of parameters increases.

What if you need other comparisons though? Try these:

1 Zoo.first(:name => 'Galveston')
 2 
 3 # 'gt' means greater-than. 'lt' is less-than.
 4 Person.all(:age.gt => 30)
 5 
 6 # 'gte' means greather-than-or-equal-to. 'lte' is also available
 7 Person.all(:age.gte => 30)
 8 
 9 Person.all(:name.not => 'bob')
10 
11 # If the value of a pair is an Array, we do an IN-clause for you.
12 Person.all(:name.like => 'S%', :id => [ 1, 2, 3, 4, 5 ])
13 
14 # Does a NOT IN () clause for you.
15 Person.all(:name.not => [ 'bob', 'rick', 'steve' ])
16 
17 # Ordering
18 Person.all(:order => [ :age.desc ])
19 # .asc is the default

To query a model by it’s associations, you can use a QueryPath:

1   Person.all(:links => [ :pets ], Person.pets.name => 'Pixel')

You can even chain calls to all or first to continue refining your query or search within a scope. See Finders for more information.

Open Development

DataMapper sports a very accessible code-base and a welcoming community. Outside contributions and feedback are welcome and encouraged, especially constructive criticism. Go ahead, fork DataMapper, we’d love to see what you come up with!

Make your voice heard! Submit a ticket or patch, speak up on our mailing-list, chat with us on irc, write a spec, get it reviewed, ask for commit rights. It’s as easy as that to become a contributor.

Copyright Dan Kubb, Sam Smoot 2009

Web Design by Luke Matthew Sutton - Community Maintained

2009-10-04 - make for h @ppy_things;

Web側

全部Rubyで書いてあります. コントローラSinatra, ビューにHamlを使っています. データベースへのアクセスSQLを直接書いているので, 特にどうこうはありません. あとはグラフを描画するためにGruffを使っています.

サーバーURLからもわかりますが, さくらさんです. データベースMySQL. 最初はcoreserverの方でやっていましたけど, Gruffが利用するRMagickをインストールするコトができなかったので, しぶしぶ乗り換えた次第. ご迷惑をおかけしております.

バックグラウンド

バックグラウンドはすべてErlangで書いています. 動作自体は各ユーザーさんのフォロワーを取得して, 例のスパムがいたらブロックするだけです.

ちなみにボクのマシンで動作させています. 当然Web側のデータベースともやり取りを行わなければいけない(ユーザーさんの取得, ブロック情報の更新)ので, その間はSSHを介してごちゃごちゃやっています*

ruby:MemCache

hdknr@debsq:~/tmp/cached_mem$ more config/environment.rb

memcache_options = {
:c_threshold => 10_000,
:compression => false,
:debug => false,
:namespace => 'vis_mod',
:readonly => false,
:urlencode => false
}
CACHE = MemCache.new memcache_options
CACHE.servers = 'localhost:11211'

hdknr@debsq:~/tmp/cached_mem$ ruby script/console
Loading development environment (Rails 2.3.4)
@@@ config/environtment.rb
>> sql ="SELECT * FROM `categories` WHERE (`categories`.`id` = 1)"
=> "SELECT * FROM `categories` WHERE (`categories`.`id` = 1)"
>> d = Digest::MD5.hexdigest(sql)
=> "1b837a35dbfd6bf42f95e110df28c9e5"
>> Category.find_by_sql(sql)
=> [#]
>> CACHE.set(d,Category.find_by_sql(sql))
=> "STORED\r\n"
>> CACHE.get(d)
=> [#]

memcachedログ。

32: Client using the ascii protocol
>32 STORED
>32 sending key vis_mod:1b837a35dbfd6bf42f95e110df28c9e5C 0 247
Category:@attributes_cache{
>32 END

Ruby on RailsでRuby-GetText-Packageを使う (Rails-2.1.x以前) - よたらぼ 保管庫

ローカライズドテンプレートの使用

foo_ja.hml.erb, foo_ja_JP.html.erbと言う風にテンプレート自体を英語の元ファイルから分離させてしまうこともできます。例えば以下のようにファイルを配置します。

/app/views/blog/list.html.erb
/app/views/blog/list_ja.html.erb

このケースでは日本語(jaロケール)の場合のみ、2番目のファイルが呼び出され、それ以外の時は1番目のファイルが呼び出されます。 render_partialの場合は、_foo.html.erb, _foo_ja.html.erbとすると、日本語の場合のみ_foo_ja.html.erbが呼び出されます。

Note メンテナンス性を考えるとこの機能はなるべく使わないことを推奨します。 こちらにその理由がありますのでご参照ください。なお、この日記のエントリに書かれているソースコードはすでにRuby-GetText-Package本体に取り込まれていますので無視してください。

使わない方がいいのか。