Permalink
Browse files

priming the waters

  • Loading branch information...
1 parent 9dc88a7 commit e1366e49996010b6b6e4316ce74a5a4c322854e0 Adam French committed Apr 6, 2008
Showing with 2,324 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +24 −0 Rakefile
  3. +25 −0 TODO
  4. +22 −0 content/articles/gem_030_released.txt
  5. +75 −0 content/articles/spotlight_on_cpk.txt
  6. +133 −0 content/articles/spotlight_on_laziness.txt
  7. +80 −0 content/articles/the_great_refactoring.txt
  8. +120 −0 content/community.txt
  9. +47 −0 content/contribute.txt
  10. +27 −0 content/css/coderay.css
  11. +7 −0 content/css/ie_hacks.css
  12. +298 −0 content/css/site.css
  13. +16 −0 content/development/index.txt
  14. +77 −0 content/docs/associations.txt
  15. +73 −0 content/docs/callbacks.txt
  16. +69 −0 content/docs/create_and_destroy.txt
  17. +98 −0 content/docs/find.txt
  18. +64 −0 content/docs/index.txt
  19. +44 −0 content/docs/install.txt
  20. +98 −0 content/docs/misc.txt
  21. +86 −0 content/docs/properties.txt
  22. +25 −0 content/get.txt
  23. +120 −0 content/getting_started.txt
  24. BIN content/images/bug.gif
  25. BIN content/images/content_bkg.gif
  26. BIN content/images/download_button.gif
  27. BIN content/images/footer_bkg.gif
  28. BIN content/images/get_button.gif
  29. BIN content/images/header_bkg.gif
  30. BIN content/images/home_bkg.gif
  31. BIN content/images/logo.gif
  32. BIN content/images/martini.gif
  33. BIN content/images/new_release_bkg.gif
  34. BIN content/images/page_bkg.gif
  35. BIN content/images/people/.DS_Store
  36. BIN content/images/people/ben_burket.png
  37. BIN content/images/people/guy_v.png
  38. BIN content/images/people/heimidal.jpg
  39. BIN content/images/people/ior3k.jpg
  40. BIN content/images/people/rando.jpg
  41. BIN content/images/people/reinh.jpg
  42. BIN content/images/people/yehuda.jpg
  43. BIN content/images/puff_bkg.gif
  44. BIN content/images/waiter.gif
  45. +58 −0 content/index.txt
  46. +11 −0 content/js/jquery-1.2.3.pack.js
  47. +34 −0 content/news.rss
  48. +28 −0 content/news.txt
  49. +153 −0 content/using-git.rhtml
  50. +115 −0 content/why.txt
  51. BIN image_sources/logo_large.png
  52. BIN image_sources/martini.png
  53. +55 −0 layouts/default.rhtml
  54. +28 −0 lib/breadcrumbs.rb
  55. +6 −0 tasks/create.rake
  56. +22 −0 tasks/deploy.rake
  57. +12 −0 tasks/growl.rake
  58. +28 −0 tasks/heel.rake
  59. +24 −0 tasks/setup.rb
  60. +19 −0 tasks/validate.rake
  61. +10 −0 templates/_partial.erb
  62. +34 −0 templates/atom_feed.erb
  63. +17 −0 templates/news.erb
  64. +18 −0 templates/page.erb
  65. +22 −0 templates/spotlight.erb
View
2 .gitignore
@@ -0,0 +1,2 @@
+output/*
+.DS_Store
View
24 Rakefile
@@ -0,0 +1,24 @@
+#
+# datamapper.org is built with Webby 0.8.2
+#
+# Gem webby-0.8.2
+# directory_watcher (>= 1.1.1)
+# heel (>= 0.6.0)
+# hpricot (>= 0.6)
+# logging (>= 0.7.1)
+# rake (>= 0.8.1)
+# rspec (>= 1.1.3)
+#
+# Please do not deploy without proofreading and the
+# approval of a maintainer
+#
+# $Id$
+
+load 'tasks/setup.rb'
+
+task :default => :rebuild
+
+desc 'deploy the site to the webserver'
+task :deploy => [:rebuild, 'deploy:rsync']
+
+# EOF
View
25 TODO
@@ -0,0 +1,25 @@
+Since DM is going through a Great Refactoring, the website may need to change a little to reflect the new stuff
+
+New site structure'll probably look something like this:
+
+ .
+ |-- community.txt
+ |-- development
+ | |-- index.txt
+ | |-- plans.txt
+ | |-- contributing.txt
+ | |-- using_git.rhtml
+ | `-- docs
+ | |-- associations.txt
+ | |-- callbacks.txt
+ | |-- create_and_destroy.txt
+ | |-- find.txt
+ | |-- index.txt
+ | |-- install.txt
+ | `-- properties.txt
+ |-- css/
+ |-- js/
+ |-- getting_started.txt
+ |-- images/
+ |-- index.txt
+ `-- why.txt
View
22 content/articles/gem_030_released.txt
@@ -0,0 +1,22 @@
+---
+body_id: news
+title: 0.3.0 Gem Released!
+created_at: 2008-03-18T15:55:50-05:00
+summary: DataMapper 0.3.0 has been released.
+release_type: important
+author: ssmoot
+filter:
+ - erb
+ - textile
+---
+
+h1. <%= @page.title %>
+
+0.3.0 is a maintenance release, bringing minor bug-fixes and minimal new features to DataMapper 0.2.5. This is the latest stable gem using database terminology and metaphors.
+
+This changeset includes:
+
+* Updated documentation, now with quick links to commonly used sections and FAQs
+* Fixed bugs related to @loaded_associations@
+* Improved support for @:dependant => :destroy@ and @:dependent => :protect@
+* Other updates, fixes, and patches from bugs reports
View
75 content/articles/spotlight_on_cpk.txt
@@ -0,0 +1,75 @@
+---
+body_id: news
+title: Spotlight on... Composite Keys
+created_at: 2008-03-29T19:09:07-05:00
+summary: When a Primary Key Just Isn't Enough
+release_type: blog
+author: afrench and ssmoot
+filter:
+ - erb
+ - textile
+---
+
+h1. <%= @page.title %>
+
+For those of us who have taken a course on database design in college or university, you may have run across a concept called 'Composite Primary Keys' (or sometimes 'Compound Keys' or 'Concatenated Keys', and abbreviated CPK(Composite Primary Keys)s). It's usually right before you tackle JOINs and right after you fight with the "surrogate key" or "primary key" concept.
+
+Boiling CPK(Composite Primary Keys)s down, they're just a way of identifying a row by multiple keys rather than one. So instead of an auto_incrementing "serial" primary key (as in @id@), you'd have a combination of @some_column@ and @some_other_column@ that would uniquely identify a row.
+
+CPK(Composite Primary Keys)s aren't as prevalent in the Rails world as Serial Keys (such as the auto-incrementing @:id@ column), but if you're going to support legacy, integration or reporting databases or just de-normalized schemas for performance reasons, they can be invaluable. So sure, Surrogate Keys are a great convenience, but sometimes they just aren't an option.
+
+Let's briefly take a look at how a few ruby ORMs support Composite Primary Keys and then we'll talk about DataMapper's support for CPK(Composite Primary Keys)s.
+
+h2. ActiveRecord
+
+In short, ActiveRecord doesn't support CPK(Composite Primary Keys)s without the help of an external library. "Dr. Nic Williams":http://drnicwilliams.com/about/ "Composite Keys":http://compositekeys.rubyforge.org/ is an effort to overcome this limitation.
+
+h2. Sequel
+
+Unlike ActiveRecord, Sequel supports CPK(Composite Primary Keys)s natively:
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post < Sequel::Model
+ set_primary_key [:category, :title]
+end
+
+post = Post['ruby', 'hello world']
+post.pk # => ['ruby', 'hello world']
+<% end %>
+
+p(attribution). example compiled from "http://code.google.com/p/ruby-sequel/wiki/SequelModels":http://code.google.com/p/ruby-sequel/wiki/SequelModels
+
+h2. DataMapper
+
+The latest DataMapper was designed from the ground up to support CPK(Composite Primary Keys)s:
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Pig
+ include DataMapper::Resource
+ property :id, Fixnum, :key => true
+ property :slug, String, :key => true
+ property :name, String
+end
+
+Pig.get(1, 'Porky')
+Pig[2, 'Piglet']
+
+pig.key # => [1, 'Wilbur']
+<% end %>
+
+We declared our keys by adding the @:key => true@ to the appropriate properties. The order is important as it will determine the order keys are addressed throughout the system.
+
+Next, we mixed and matched the keys' types. @:id@ is a Fixnum, but @:slug@ is a String. DataMapper didn't flinch when we defined a key column as a String because it supports "Natural Keys":http://en.wikipedia.org/wiki/Natural_key as well.
+
+Lastly, when retrieving rows via @get@ and @[]@ with a CPK(Composite Primary Keys), we supplied the keys in the order they were defined within our model. For example, we defined @:id@ first, then @:slug@ second; later, we retrieved Porky by specifying his @:id@ and @:slug@ in the same order. Additionally, when we asked Wilbur for his keys, he handed us an array in the order the keys were defined.
+
+We didn't need to mix in an external library to get support for CPK(Composite Primary Keys)s, nor did we need to call a @set_primary_key@ method and then supply more than one key to it. DataMapper supports Composite Primary Keys intuitively and without compromise!
+
+In later "Spotlight On..." articles, we'll examine and demonstrate other DataMapper features or persistence concepts as well as compare similar features with other ORMs or libraries.
+
+h2(newRelease). Contribute a "Spotlight On..." Article
+
+p(newRelease). Got something important to say? Want something explained a little<br>
+better or demonstrated? Contribute or request a "Spotlight On..." <br> article!
+Email the "DataMapper Mailing List":http://groups.google.com/group/datamapper with the request or <br>
+contribution and we'll post it here.
View
133 content/articles/spotlight_on_laziness.txt
@@ -0,0 +1,133 @@
+---
+body_id: news
+title: Spotlight on... Laziness
+created_at: 2008-04-03T23:08:12-05:00
+summary: Don't Get Stuff Done Until You Gotta Get Stuff Done
+release_type: blog
+author: afrench
+filter:
+ - erb
+ - textile
+---
+
+h1. <%= @page.title %>
+
+Laziness. It means "an unwillingness to work or use energy" and typically indicates that the dishes don't get washed after lunch, the bath tub doesn't get cleaned, and the trash sits around an extra few days and stinks up the place.
+
+But that very same definition in software takes on a whole new meaning: To avoid doing work you don't have to do for as long as you can avoid it; sometimes never doing it at all. It's a good thing. It means that expensive and slow tasks can be put off until the very last cycle possible and thus only incur their cost when it really is worth it.
+
+Maybe you put off running a specific subroutine because it's slow, or because it locks a file that might be needed elsewhere, or because instantiating the resulting object eats up RAM. Either way, deferring execution of a block of code until the very last possible moment can be the difference between a snappy application that rarely slows down and a slow application that rarely speeds up.
+
+But laziness isn't without its hidden costs. If you put off everything to the very last cycle, you don't get anything done. You might even run into concurrency problems and race conditions when things do finally get executed.
+
+So where's the balance?
+
+I don't know. Where is the balance for your specific application? Ultimately, it's up to you. And having the tools that offer you the flexibility you need to design your application ought to be one of the most important requirements. Every system, after all, is unique and breaks the mold of systems before it.
+
+This brings us to DataMapper.
+
+h2. Lazy-loading attributes
+
+You likely already know that DataMapper supports lazy properties.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post
+ include DataMapper::Resource
+ property :id, Fixnum, :serial => true # auto_incrementing primary key
+ property :title, String, :lazy => true # intentionally lazy
+ property :body, Text # lazy by default
+end
+<% end %>
+
+In this case, we're intentionally marking this Post's @:title@ property as lazy, as well as letting the @:body@ be lazy by default. If we go and inspect our query log for the retrieval of a post with the ID of 1, we see
+
+<% coderay(:lang => "sql") do -%>
+SELECT `id` FROM `posts` WHERE `id` IN (1)
+<% end %>
+
+DataMapper didn't request the two lazy columns. But when we call @.title@ off of our post, we suddenly see
+
+<% coderay(:lang => "sql") do -%>
+SELECT `title` FROM `posts` WHERE `id` IN (1)
+<% end %>
+
+This is the very definition of a lazy-loaded property; The lazy column didn't get requested from our data store until we actually needed it, and no sooner.
+
+But this is just for one individual instance of a post. How does this behave when we have a collection of posts and iteratively call the @.title@ method?
+
+<% coderay(:lang => "sql") do -%>
+SELECT `title` FROM `posts` WHERE `id` IN (1, 2, 3, 4, 5)
+<% end %>
+
+DataMapper loaded up the title for all of the posts in our collection in one query. It didn't issue the lazy-load retrieval from above over and over for each individual post, nor did it chicken out and issue the lazy-load retrieval for ALL of the posts in the database.
+
+When you retrieve a set of results using DataMapper's @.all@, each instance it returns knows about the others in the result set, which makes it brutally simple to issue just one lazy-load retrieval of @:title@, and thus solving the n+1 query problem.
+
+h2. Contextual Lazy-loading
+
+With a recent commit by "Guy van den Berg":http://www.guyvdb.info/ruby/lazy-loading-properties-in-datamapper/, DataMapper just got a whole lot more flexible.
+
+Most applications have only a few main views of a resource: a brief summary view used in listing results, a complete representation that might appear on a show page and a comprehensive view for when someone is editing something and needs access to metadata. Wouldn't it be nice to lump all of the lazy-load retrieval queries into one query which loads up multiple lazy properties, rather than query after query for each lazy property as you call them?
+
+DataMapper now does this!
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post
+ include DataMapper::Resource
+
+ property :id, Fixnum, :serial => true
+ property :title, String, :lazy => [:summary, :brief]
+ property :body, Text, :lazy => [:summary]
+end
+<% end %>
+
+So now, when you load an attribute and it has the @:summary@ context, DataMapper will load up all of the other lazy-loaded properties marked @:summary@ in one query to the data store.
+
+In your query log, you'll see:
+
+<% coderay(:lang => "sql", :line_numbers => "inline") do -%>
+-- initial load
+SELECT `id` FROM `posts`
+
+-- lazy-loading of multiple properties in a given context in one query
+SELECT `title`, `body` FROM `posts`
+<% end %>
+
+If you use this wisely, it would mean that DataMapper will never load more than it needs nor will it ever fire off more than the absolutely necessary amount of queries to get the job done.
+
+It's lazy ;-)
+
+h2. Strategic Eager Loading
+
+Well, not for everything.
+
+Returning for a little bit to our "loaded set" discussion from above, every item you pull out of the data store is aware of any other item that got pulled along with it. This is a very powerful feature which lets DataMapper defeat n+1 query problems when dealing with associations as well as lazy-loading of properties.
+
+For example, this is a severe "no no" in ActiveRecord:
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+ Zoo.find(:all).each do |zoo|
+ zoo.animals
+ end
+<% end %>
+
+This is a very bad idea because the ORM must query the "animals" table over and over again to load the association for each iteration. It's far better to use @Zoo.find(:all, :include => [:animals]).each {}@ because a JOIN occurs and everything is retrieved in 1 query.
+
+But the same issue doesn't exist in DataMapper. Each instance is aware of the other instances it was retrieved with. The same iterator example from above only fires off 2 queries as you're iterating and calling the association inside the @each@. You don't have to specify the @:include@ directive if you don't want to; when you don't, DataMapper behaves sanely.
+
+"Yehuda Katz":http://www.yehudakatz.com/ has aptly named this 'Strategic Eager Loading'.
+
+h2. Getting Around to It
+
+A conclusion for our talk about laziness will be written whenever I get around to it.
+
+For now, just remember that DataMapper embraces lazy-loading, yet isn't overly zealous when the lazy properties are finally retrieved. It also fills associations strategically, and assumes you're going to iterate over the set of results. You don't have to catch yourself when you write an iterator because DataMapper loads associations for all of your items in the set, rather than on a one-by-one basis.
+
+And, most importantly, you can avoid doing work you don't have to do for as long as you can avoid it.
+
+h2(newRelease). Contribute a "Spotlight On..." Article
+
+p(newRelease). Got something important to say? Want something explained a little<br>
+better or demonstrated? Contribute or request a "Spotlight On..." <br> article!
+Email the "DataMapper Mailing List":http://groups.google.com/group/datamapper with the request or <br>
+contribution and we'll post it here.
View
80 content/articles/the_great_refactoring.txt
@@ -0,0 +1,80 @@
+---
+body_id: news
+title: The Great Refactoring
+created_at: 2008-03-22T15:55:50-05:00
+summary: Change is afoot
+release_type: blog
+author: afrench and ssmoot
+filter:
+ - erb
+ - textile
+---
+
+h1. <%= @page.title %>
+
+"Tip" DataMapper (hosted on "github":http://github.com/sam/dm-core) is going through a dramatic re-factor. Here's a quick summary of the anticipated NEW public API.
+
+h2. Not Just for Databases Anymore
+
+DataMapper's class terminology will change and 'de-couple' itself from database-specific terminology. Gone are "database", "table", "column" and "join". Say hello to "Repository", "Resource", "Property", and "Link".
+
+"Why would you want to do that?", you ask. Ultimately it's because DataMapper will soon support different types of persistence layers, not just databases. It'll talk to all sorts of things like web services (REST and such), XML files, YAML files, non-relational databases, even custom file-types or services of your own design. Just implement an Adapter that conforms to a certain API and DataMapper could support any type of data store. No need for a completely separate library or anything.
+
+As an added benefit, DataMapper become more "RESTful". "Ryan Tomayko":http://tomayko.com/writings/rest-to-my-wife has a very good explanation of REST that all should read entitled "How I Explained REST to My Wife":http://tomayko.com/writings/rest-to-my-wife.
+
+h2. Model Definitions Are a Little Different
+
+Since we're changing up the terminology, model definitions are going to change up a little bit. Here's what a Planet model would look like using the new API:
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Planet
+
+ include DataMapper::Resource
+
+ resource_names[:legacy] = "dying_planets"
+
+ property :name, String
+ property :age, Fixnum
+ property :core, String, :private => true
+
+end
+<% end %>
+
+A couple of things are going on here. First, DataMapper::Base and DataMapper::Persistence are gone and replaced with DataMapper::Resource. Next @set_table_name@ has been replaced with @resource_names@ hash where you specify which arena play occurs in. After that we have a couple of Property definitions that look a little different.
+
+First off, Properties will no longer take @:symbols@ for their types and instead take real constants like String, Fixnum, DateTime. Also on the docket are the ability to define your own custom types.
+
+Think about that for a minute. If developers are able to define their own custom types with their own materialization and serialization methods, DataMapper will be able to support all kinds of wild data-types like GIS information, network information, marshaled objects, JSON...pretty much anything a developer might need, or want.
+
+h2. A Command-Line Interface
+
+Taking a lesson from web frameworks, DataMapper will sport an interactive command-line so that you can browse your resources without the need to load up the entire environment of your application. Here's an example session:
+
+<% coderay(:lang => "bash", :line_numbers => "inline") do -%>
+$ dm mysql://root@localhost/great_musicians # connecting to a repository
+<% end %>
+
+An IRB session boots up...
+
+<% coderay(:lang => "ruby") do -%>
+>> the_king = Person.first(:name => 'elvis')
+>> the_king.alive? # => maybe
+<% end %>
+
+This is very similar to @script/console@ in Rails or @merb -i@ in Merb, only it won't load up the entire environment of your application, just your DataMapper resources and their associations, methods, and such. If you prefer "fat models", this will constitute the core of your application.
+
+h2. How This All Comes Together
+
+This is the coolest new feature of DataMapper: we're skipping all the way from 0.3.0 to 0.9! Get excited, contact the press, fire up the blogosphere! Its a huge jump and we're honestly concerned that people may not be able to handle it.
+
+Alright, so it's not _that_ big of a deal, but we're confident that all of this will get DataMapper so close to going 1.0 that we'll be able to taste it. To get there, DataMapper's more advanced features like single table inheritance, paranoia, and chained associations will be re-implimented to use all this new stuff, and then we're sure 0.9 will need a touch up or two.
+
+So close....so very very close...
+
+Stay tuned in to the "mailing list":http://groups.google.com/group/datamapper, check up on the "wiki":http://wiki.datamapper.org/doku.php, chat it up in "#datamapper":irc://irc.freenode.net/#datamapper and watch "github commit messages":http://github.com/sam/dm-core/commits/master for updates.
+
+
+
+
+
+
View
120 content/community.txt
@@ -0,0 +1,120 @@
+---
+title: DataMapper's Community
+created_at: Sun Mar 16 23:55:11 -0500 2008
+filter:
+ - erb
+ - textile
+---
+h2. <%= @page.title %>
+
+Like all open-source projects, DataMapper has a burgeoning community.
+
+h3. Links, Blogs, Wiki's, and Stuff
+
+<dl>
+ <dt><a href="/news.html">News and Notes</a></dt>
+ <dd>Official News and other contributed articles</dd>
+ <dt><a href="http://wiki.datamapper.org/doku.php">Wiki</a></dt>
+ <dd>Official Wiki</dd>
+ <dt><a href="http://github.com/sam/dm-core/tree/master">DM on GitHub</a></dt>
+ <dd>Source Repository</dd>
+ <dt><a href="http://datamapper.rubyforge.org/">API on RubyForge</a></dt>
+ <dd>Documentation</dd>
+ <dt><a href="irc://irc.freenode.net/#datamapper"><code>#datamapper</code> on irc.Freenode.net</a></dt>
+ <dd>Official IRC Channel</dd>
+ <dt><a href="http://groups.google.com/group/datamapper"> Google Groups</a></dt>
+ <dd>Official Mailing List</dd>
+
+
+</dl>
+
+
+h3. The People Behind DataMapper
+
+<div class="yearbook">
+ <div class="thumb">
+ <img src="http://www.gravatar.com/avatar/41c597a48c80e37ba68d1adc7095ea0e?s=80" title="Sam Smoot" alt="Sam Smoot" />
+ <p>Sam Smoot (Lead)</p>
+ </div>
+
+ <div class="thumb">
+ <img src="/images/people/yehuda.jpg" title="Yehuda Katz" alt="Yehuda Katz" />
+ <p><a href="http://www.yehudakatz.com/">Yehuda Katz</a></p>
+ </div>
+
+ <div class="thumb">
+ <img src="/images/people/rando.jpg" title="Paul Sadauskas" alt="Paul Sadauskas" />
+ <p><a href="http://www.theamazingrando.com/">Paul Sadauskas</a></p>
+ </div>
+
+ <div class="thumb">
+ <img src="http://gravatar.com/avatar/089d4a0c54ac9eee0950444826ed20f0?s=80" title="Adam French" alt="Adam French" />
+ <p><a href="http://adam.speaksoutofturn.com">Adam French</a></p>
+ </div>
+
+ <div class="thumb">
+ <img src="/images/people/ben_burket.png" title="Ben Burket" alt="Ben Burket" />
+ <p><a href="http://benburkert.com">Ben Burket</a></p>
+ </div>
+
+ <div class="thumb">
+ <img src="http://www.gravatar.com/avatar.php?gravatar_id=9d1f5d2d9de70bd9a934f557dc95a406" title="Daniel Neighman" alt="Daniel Neighman" />
+ <p><a href="http://hassox.blogspot.com">Daniel Neighman</a></p>
+ </div>
+
+ <div class="thumb">
+ <img src="http://en.gravatar.com/avatar/b9b5ff40232c1dfd61238c2a90467f84?s=80&r=any" title="Wayne E. Seguin" alt="Wayne E. Seguin"/>
+ <p><a href="http://wayneseguin.us">Wayne E Seguin</a></p>
+ </div>
+
+ <div class="thumb">
+ <img src="/images/people/reinh.jpg" title="Rein Henrichs" alt="Rein Henrichs"/>
+ <p><a href="http://reinh.com">Rein Henrichs</a></p>
+ </div>
+
+ <div class="thumb">
+ <img src="/images/people/heimidal.jpg" title="Brian Rose" alt="Brian Rose"/>
+ <p><a href="http://heimidal.net">Brian Rose</a></p>
+ </div>
+
+ <div class="thumb">
+ <img src="http://en.gravatar.com/avatar/ee5c1f36549c4ddca2189f9c4cf36f2c?s=80" title="Scott Bauer" alt="Scott Bauer" />
+ <p><a href="http://railsaddict.com">Scott Bauer</a></p>
+ </div>
+
+ <div class="thumb">
+ <img src="/images/people/guy_v.png" title="Guy van den Berg" alt="Guy van den Berg"/>
+ <p><a href="http://www.guyvdb.info">Guy van den Berg</a></p>
+ </div>
+
+ <div class="thumb">
+ <img src="/images/people/ior3k.jpg" title="David Leal" alt="David Leal"/>
+ <p>David Leal</p>
+ </div>
+
+ <div class="thumb">
+ <img src="http://www.gravatar.com/avatar/ea627ef000ec92c6cdd5a4c14075e740" title="Daniel Kubb" alt="Daniel Kubb" />
+ <p>Dan Kubb</p>
+ </div>
+
+ <div class="thumb">
+ <img src="https://secure.gravatar.com/avatar/37872f2e08a213f07e49cf5eabfedc61?s=80" title="Luke Matthew Sutton" />
+ <p><a href="http://www.mr-eel.com">Luke Matthew Sutton</a></p>
+ </div>
+
+ <div class="thumb">
+ <img src="http://en.gravatar.com/avatar/8e3cc061dec070b2f4d1c78c13ade6ec?s=80&r=any" title="Mayo Jordanov" />
+ <p><a href="http://oyam.ca/">Mayo Jordanov</a></p>
+ </div>
+
+ <div class="thumb">
+ <img src="http://en.gravatar.com/avatar/949106533af33ece50adda643c34bc08?s=80" title="Robert Evans" />
+ <p><a href="http://robertrevans.com/">Robert Evans</a></p>
+ </div>
+
+ <div class="thumb">
+ <img src="/images/martini.gif" title="you" alt="you could be here" />
+ <p>More to come!</p>
+ </div>
+
+</div>
View
47 content/contribute.txt
@@ -0,0 +1,47 @@
+---
+title: Contribute to DataMapper
+created_at: Wed Aug 29 20:37:00 +0930 2007
+filter:
+ - erb
+ - textile
+---
+h1. <%= @page.title %>
+
+DataMapper is always looking for more contributers. When you've got an itch to scratch, jump in and contribute! Write a few specs showing us how your code works, create a patch and "submit it":http://wm.lighthouseapp.com/projects/4819-datamapper/overview as a new ticket or a fix for an existing one. After a few patches and many thanks, you'll get commit access.
+
+We benchmark all but the most trivial of patches, because we care about performance and you should too!
+
+h2. What we need
+
+Currently DataMapper needs help in a few particular areas:
+
+* API Documentation (using the "Merb documentation style":http://yehudakatz.com/2008/01/14/merbnext/)
+* Tutorials
+* Code contributions
+* Bug Reports
+
+h2. Git - Edge DataMapper
+
+DataMapper development has switched to the "Git":http://git.or.cz SCM. Please see "using git":using-git.html to learn how to contribute.
+
+<div class="CodeRay">
+ <pre>$ git clone git://github.com/sam/dm-core.git</pre>
+</div>
+
+h2. SVN - Bug Fixes for 0.2.5 (gem before The Great Refactor)
+
+SVN development is for 0.2.5 (Stable) bug-fixes only. Edge DataMapper has moved to "Git":http://github.com/sam/dm-core
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+$ svn co http://datamapper.rubyforge.org/svn/trunk/ data_mapper
+<% end %>
+
+h2. Code Style Guidelines
+
+When contributing any code to DataMapper, please follow these guidelines.
+
+# Spec first. Spec thoroughly. (DataMapper is written with "Rspec":http://rspec.info/)
+# Parentheses around parameter lists for methods
+# Two space indent - not tabs!
+# Documentation is required (use the "Merb documentation style":http://yehudakatz.com/2008/01/14/merbnext/)
+
View
27 content/css/coderay.css
@@ -0,0 +1,27 @@
+/*
+ Taken from http://railscasts.com/stylesheets/coderay.css
+ Thanks! :)
+*/
+.CodeRay pre {
+ margin: 0px;
+ padding: 0px;
+}
+
+.CodeRay .an { color:#E7BE69 } /* html attribute */
+.CodeRay .c { color:#BC9358; font-style: italic; } /* comment */
+.CodeRay .ch { color:#509E4F } /* escaped character */
+.CodeRay .cl { color:#FFF } /* class */
+.CodeRay .co { color:#FFF } /* constant */
+.CodeRay .fl { color:#A4C260 } /* float */
+.CodeRay .fu { color:#FFC56D } /* function */
+.CodeRay .gv { color:#D0CFFE } /* global variable */
+.CodeRay .i { color:#A4C260 } /* integer */
+.CodeRay .il { background:#151515 } /* inline code */
+.CodeRay .iv { color:#D0CFFE } /* instance variable */
+.CodeRay .pp { color:#E7BE69 } /* doctype */
+.CodeRay .r { color:#CB7832 } /* keyword */
+.CodeRay .rx { color:#A4C260 } /* regex */
+.CodeRay .s { color:#A4C260 } /* string */
+.CodeRay .sy { color:#6C9CBD } /* symbol */
+.CodeRay .ta { color:#E7BE69 } /* html tag */
+.CodeRay .pc { color:#6C9CBD } /* boolean */
View
7 content/css/ie_hacks.css
@@ -0,0 +1,7 @@
+#header ul {
+ left:-30px;
+}
+/* Margin doubling on floated elements */
+p#download {
+ display:inline;
+}
View
298 content/css/site.css
@@ -0,0 +1,298 @@
+* {
+ padding:0;
+ margin:0;
+ font-weight:normal;
+ font-size:1em;
+}
+body {
+ background:center top url(../images/page_bkg.gif);
+ color:#ffffff;
+ font-family: "Lucida Grande", "Lucida Sans", Helvetica, Verdana, sans-serif;
+ font-size:82%;
+ margin:29px 0;
+}
+#content {
+ background:left top repeat-y url(../images/content_bkg.gif);
+ width:870px;
+ margin:0 auto;
+}
+#header {
+ background:center top repeat-y url(../images/header_bkg.gif);
+ position:relative;
+ padding:4em 0 1.5em 10px;
+ margin-bottom:1.5em;
+}
+#header h1,
+#header h1 a {
+ height:85px;
+ width:340px;
+ margin:0 !important;
+ padding:0;
+ display:block;
+}
+#header h1 a {
+ background: left top no-repeat url(../images/logo.gif);
+ text-indent:-1000em; /* Hack to hide the text */
+}
+#header ul {
+ border-bottom:1px solid #152C4C;
+ width:851px;
+ padding:0 0 0 19px;
+ margin:0 !important;
+ position:absolute;
+ left:0;
+ top:0;
+}
+#header li {
+ display:inline;
+ list-style-type:none;
+}
+#header a {
+ color:#B7BAC0 !important;
+ text-decoration:none !important;
+ font-weight:bold;
+ padding:1em 10px 0.7em 10px;
+ float:left;
+}
+#getDataMapper {
+ background:left center no-repeat url(../images/get_button.gif);
+ width:158px;
+ text-indent:-1000em;
+ float:right !important;
+}
+#footer {
+ background:left bottom no-repeat url(../images/footer_bkg.gif);
+ color:#4676C0;
+ margin:3em 0 0 0;
+ padding:0.7em 0;
+ clear:both;
+}
+#footer p {
+ font-size:0.85em;
+ margin-bottom:0;
+}
+#footer a {
+ color:#ffffff !important;
+}
+
+/* TEXT */
+h1,
+h2,
+h3,
+p,
+.CodeRay,
+.puff,
+ul,
+dl,
+ol {
+ margin:0 29px 0 30px;
+}
+h1 {
+ font-size:2em;
+ padding-bottom:0.2em;
+}
+h2 {
+ color:#6680B2;
+ font-size:1.5em;
+ padding-bottom:0.1em;
+}
+h3 {
+ font-size:1.2em;
+ font-weight:bold;
+}
+p,
+ul,
+ol {
+ margin-bottom:1.5em;
+ line-height:1.5;
+}
+a:link {
+ color:#66FF33;
+}
+a:visited {
+ color:#cccccc;
+}
+.CodeRay {
+ background:#0C182A;
+ border:1px solid #26518A;
+ padding:1em 15px;
+ line-height:1.5;
+ margin-bottom:2em;
+ font-family: 'Courier New', 'Terminal', monospace;
+ color: #E6E0DB;
+ overflow: auto;
+ font-size: 12px;
+}
+ul,
+ol {
+ padding:0 0 0 2em;
+}
+
+/* HOME PAGE */
+#home #content {
+ background-image:url(../images/home_bkg.gif);
+}
+#home h1,
+#home h2,
+#home h3,
+#home p,
+#home .CodeRay,
+#home .puff {
+ margin-right:240px;
+}
+#help,
+#bugs {
+ background:#11233D left 0.5em no-repeat;
+ padding:1em 20px 1em 60px;
+ margin-bottom:1.5em;
+ height:10em;
+ display:block;
+ text-decoration:none !important;
+ color:#ffffff !important;
+}
+#help h2,
+#bugs h2 {
+ margin-right:0 !important;
+}
+#help p,
+#bugs p {
+ font-size:0.85em;
+ margin-right:auto;
+}
+#help a,
+#bugs a {
+ font-size:1.2em;
+}
+#help {
+ background-image:url(/images/waiter.gif);
+ margin-left:30px;
+ width:220px;
+ float:left;
+}
+#bugs {
+ background-image:url(/images/bug.gif);
+ margin-right:240px;
+ width:219px;
+ float:right;
+}
+#home h1 {
+ clear:both;
+}
+/* PUFFS */
+div.puff,
+h2.latestRelease {
+ background:no-repeat url(/images/puff_bkg.gif);
+}
+div.puff,
+.latestRelease {
+ background-color:#11233D !important;
+}
+div.puff {
+ margin-bottom:1.5em;
+}
+div.puff h2 {
+ color:#ffffff;
+}
+/* LATEST RELEASE */
+h2.latestRelease,
+p.latestRelease {
+ padding-left:30px;
+ padding-right:30px;
+}
+h2.latestRelease {
+ color:#ffffff;
+ padding-top:20px;
+}
+p.latestRelease {
+ padding-bottom:20px;
+ margin-bottom:1px;
+}
+/* NEW RELEASE */
+h2.newRelease,
+p.newRelease {
+ background:no-repeat url(../images/new_release_bkg.gif);
+ padding-left:30px;
+ padding-right:30px;
+}
+h2.newRelease {
+ background-position:left top;
+ color:#ffffff;
+ padding-top:20px;
+ padding-bottom:0;
+}
+p.newRelease {
+ background-position:left bottom;
+ padding-bottom:20px;
+}
+p#download {
+ margin-top:-6em;
+ margin-bottom:5em;
+ margin-right:290px;
+ float:right;
+}
+p#download a {
+ background:center center no-repeat url(../images/download_button.gif);
+ text-indent:-1000em;
+ width:147px;
+ height:31px;
+ display:block;
+}
+p.attribution {
+ font-size:0.8em;
+ margin-top:-2em;
+}
+/* DOCUMENTATION */
+dl {
+ margin-top:1em;
+ margin-bottom:2em;
+ border-bottom:1px dotted #ffffff;
+ float:left;
+}
+dt,
+dd {
+ border-top:1px dotted #ffffff;
+ padding:1.2em 0;
+}
+dt {
+ width:240px;
+ float:left;
+ clear:left;
+}
+dd {
+ width:570px;
+ float:left;
+ clear:right;
+}
+dt a {
+ color:#66FF33 !important;
+ text-decoration:none !important;
+ font-weight:bold;
+ display:block;
+}
+dd {
+ clear:right;
+}
+
+div.yearbook {
+ margin:1em;
+}
+div.yearbook div.thumb {
+ display:block;
+ float:left;
+ width:120px;
+ height:120px;
+ margin:0.5em;
+ text-align:center;
+}
+
+div.yearbook div.thumb p {
+ margin:auto -0.5em;
+}
+small {
+ font-size:.8em;
+}
+
+blockquote {
+ margin: auto 1em;
+ font-style: italic;
+}
View
16 content/development/index.txt
@@ -0,0 +1,16 @@
+---
+title: Development
+created_at: Mon Mar 17 01:37:12 -0500 2008
+filter:
+ - erb
+ - textile
+---
+h2. <%= @page.title %>
+
+Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Nunc congue ipsum vestibulum libero. Aenean vitae justo. Nam eget tellus. Etiam convallis, est eu lobortis mattis, lectus tellus tempus felis, a ultricies erat ipsum at metus.
+
+Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Morbi et risus. Aliquam nisl. Nulla facilisi. Cras accumsan vestibulum ante. Vestibulum sed tortor. Praesent tempus fringilla elit. Ut elit diam, sagittis in, nonummy in, gravida non, nunc. Ut orci. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam egestas, orci eu imperdiet malesuada, nisl purus fringilla odio, quis commodo est orci vitae justo. Aliquam placerat odio tincidunt nulla. Cras in libero. Aenean rutrum, magna non tristique posuere, erat odio eleifend nisl, non convallis est tortor blandit ligula. Nulla id augue.
+
+bq. Nullam mattis, odio ut tempus facilisis, metus nisl facilisis metus, auctor consectetuer felis ligula nec mauris. Vestibulum odio erat, fermentum at, commodo vitae, ultrices et, urna. Mauris vulputate, mi pulvinar sagittis condimentum, sem nulla aliquam velit, sed imperdiet mi purus eu magna. Nulla varius metus ut eros. Aenean aliquet magna eget orci. Class aptent taciti sociosqu ad litora.
+
+Vivamus euismod. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse vel nibh ut turpis dictum sagittis. Aliquam vel velit a elit auctor sollicitudin. Nam vel dui vel neque lacinia pretium. Quisque nunc erat, venenatis id, volutpat ut, scelerisque sed, diam. Mauris ante. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec mattis. Morbi dignissim sollicitudin libero. Nulla lorem.
View
77 content/docs/associations.txt
@@ -0,0 +1,77 @@
+---
+title: Associations
+body_id: docs
+created_at: Tue Dec 04 14:46:32 +1030 2007
+filter:
+ - erb
+ - textile
+---
+
+h1. <%= @page.title %>
+
+Associations are a way of declaring relationships between models, for example a blog Post "has many" Comments, or a Post belongs to an Author. They add a series of methods to your models which allow you to create relationships and retrieve related models along with a few other useful features. Which records are related to which are determined by their foreign keys.
+
+The types of associations currently in DataMapper are:
+
+<ul>
+ <li>Has Many</li>
+ <li>Has One</li>
+ <li>Belongs To</li>
+ <li>Has and Belongs to Many</li>
+ <li>Self-Referential Has And Belongs To Many</li>
+ <li>Has Many Through (done manually - see below)</li>
+</ul>
+
+h2. Declaring Associations
+
+This is done via declarations inside your model class. The class name of the related model is determined by the symbol you pass in. For illustration, we'll add an association of each type. Pay attention to the pluralisation or the related model's name.
+
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post < DataMapper::Base
+ has_many :comments
+ has_and_belongs_to_many :categories
+ belongs_to :author
+end
+<% end %>
+
+
+h2. Customising Associations
+
+The association declarations make certain assumptions about which classes are being related and the names of foreign keys based on some simple conventions. In some situations you may need to tweak them a little. The association declarations accept additional options to allow you to customise them as you need
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post < DataMapper::Base
+ belongs_to :author, :class => 'User', :foreign_key => 'user_id'
+end
+<% end %>
+
+h2. Creating New Records via Associations
+
+You can create an instance, update it and save or you can create and save an associated record in a single step.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+post = Post[1]
+# Creates a new instance of Comment and sets it's foreign key to point to the post
+comment = post.comments.build
+# Then we update and save it
+comment.attributes = {:name => 'King Rat', :body => 'London will be mine!'}
+comment.save
+# Alternately we can create a new instance and save it in one step
+post.comments.create(:name => 'Anansi', :body => 'I would lose a leg before I let you!')
+<% end %>
+
+h2. Has Many Through
+
+Has Many Through is a special kind of Has Many relationship which, rather than using an 'automatic' @table_table@ join table, uses another model's table. Though at present DataMapper does no have a special syntax for defining an HMT relationship, it's very easily done manually with the following:
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post < DataMapper::Base
+ has_many :categories,
+ :join_table => "categorizations",
+ :left_foreign_key => "post_id",
+ :right_foreign_key => "category_id",
+ :class => "Category"
+end
+<% end %>
+
View
73 content/docs/callbacks.txt
@@ -0,0 +1,73 @@
+---
+title: Callbacks
+body_id: docs
+created_at: Fri Nov 30 15:29:01 +1030 2007
+filter:
+ - erb
+ - textile
+---
+
+h1. <%= @page.title %>
+
+DataMapper implements a series of before and after callback hooks for you to use to call your own code whenever you create, save, update or destroy a record. You might use this to sanitize certain values, or generate values that you don't want users to directly access -- say a salt to mix in with an encrypted password.
+
+Before and after callbacks are defined for:
+
+* Materialize (pulling a record from the DB)
+* Create
+* Save
+* Validation
+* Update
+* Destroy
+
+h2. Adding a Callback
+
+Callbacks are declared inside your model. There are multiple ways of adding a callback. Each behaves slightly differently.
+
+h3. Block or Procs
+
+Blocks and Procs are not evaluated inside the model instance. Instead they are passed a reference to the model. You should be aware of this if you need to access private methods, in which case you need to set the callbacks using a symbol or string.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post < DataMapper::Base
+ before_save do |post|
+ logger.info("We are saving the post")
+ true
+ end
+
+ before_destroy Proc.new {|post| logger.info("We are destroying the post #{post.name}"); true}
+end
+<% end %>
+
+h3. Symbol or String
+
+Symbols and Strings are treated as a reference to a method inside your model.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post < DataMapper::Base
+ after_validation :log_success
+
+ def log_success
+ logger.info("YAY!") if errors.empty?
+ true
+ end
+end
+<% end %>
+
+h2. True to Go, False to Stop
+
+Callbacks have to return true else the relevant action will be halted. For example if you use a before_save callback and fail to return true, the record will not save.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post < DataMapper::Base
+ # This record will save properly
+ before_save do |post|
+ true
+ end
+
+ # But it will not be destroyed
+ before_destroy do |post|
+ false
+ end
+end
+<% end %>
View
69 content/docs/create_and_destroy.txt
@@ -0,0 +1,69 @@
+---
+title: Create, Update, Save and Destroy
+body_id: docs
+created_at: Tue Dec 04 14:46:32 +1030 2007
+filter:
+ - erb
+ - textile
+---
+
+h1. <%= @page.title %>
+
+To illustrate the various methods used in manipulating records, we'll create, save and destroy a record - ignoring "validations":/docs/validations.html for now.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Zoo < DataMapper::Base
+ property :name, :string
+ property :description, :text
+ property :inception, :datetime
+end
+<% end %>
+
+h2. Creating
+
+Let's create a new instance of the model, update it's properties and save it to the database. Save will return true if the save succeeds, or false when something went wrong.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+zoo = Zoo.new
+zoo.attributes = {:name => 'The Glue Factory', :inception => Time.now}
+zoo.save
+<% end %>
+
+Pretty straight forward. In this example we've updated the attributes using the @#attributes=@ method, but there are multiple ways of setting the values of a model's properties.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+zoo = Zoo.new(:name => 'Awesome Town Zoo') # Pass in a hash to the new method
+zoo.name = 'Dodgy Town Zoo' # Set individual property
+zoo.attributes = {:name => 'No Fun Zoo', :open => false} # Set multiple properties at once
+<% end %>
+
+You can also update a model's properties and save it with one method call. @#update_attributes@ will return true if the record saves, false if the save fails, exactly like the @#save@ method.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+zoo.update_attributes(:name => 'Funky Town Municipal Zoo')
+<% end %>
+
+
+h2. Destroy
+
+To destroy a record, you simply call it's @#destroy!@ method. It will return true or false depending if the record is successfully deleted. Here is an example of finding an existing record then destroying it.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+zoo = Zoo[5]
+zoo.destroy! #=> true
+<% end %>
+
+h2. Other Things
+
+If you're feeling particularly zealous, you can also destroy every record in a table.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+Zoo.delete_all # iterates over each, deleting them as they go
+Zoo.truncate! # issues a TRUNCATE
+<% end %>
+
+You can use @find_or_create@ to find an object, or create the object if the it's not there. @find_or_create@ takes 2 hashes as it's arguments: @search_conditions@ and @create_conditions@, which are reverse-merged together when the object needs to be created. Searching only happens using the @search_conditions@ though.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+Zoo.find_or_create {:name => 'Zoo'}, {:inception => Time.now}
+<% end %>
View
98 content/docs/find.txt
@@ -0,0 +1,98 @@
+---
+title: Finding Records
+body_id: docs
+created_at: Tue Dec 04 14:46:32 +1030 2007
+filter:
+ - erb
+ - textile
+---
+
+h1. <%= @page.title %>
+
+The finder methods for DataMapper objects are defined in DataMapper::Persistence. They include @[]@, @all()@, @first()@
+
+h2. Finder Methods
+
+DataMapper has methods which allow you to grab a single record by primary key, the first match to a set of conditions, or a collection of records matching conditions.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+zoo = Zoo[1] # get the zoo with primary key of 1
+zoo = Zoo['DFW'] # wow, support for natural primary keys
+zoo = Zoo.first(:name => 'Luke') # first matching record with the name 'Luke'
+zoos = Zoo.all # all zoos
+zoos = Zoo.all(:open => true) # all zoos that are open
+<% end %>
+
+h2. Conditions
+
+Now, this is where we get really clever. Rather than defining conditions using SQL fragments, we can actually specify conditions using a hash.
+
+The examples above are pretty simple, but you might be wondering how we can specify conditions beyond equality without resorting to SQL. Well, thanks to some clever additions to the Symbol class it's easy!
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+exhibitions = Exibition.all(:run_time.gt => 2, :run_time.lt => 5)
+# => SQL conditions: 'run_time > 1 AND run_time < 5'
+<% end %>
+
+Valid symbol operators for the conditions are:
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+gt # greater than
+lt # less than
+gte # greater than or equal
+lte # less than or equal
+not # not equal
+like # like
+in # in - will be used automatically when an array is passed in as an argument
+<% end %>
+
+h3. Compatibility
+
+DataMapper supports other conditions syntaxes as well:
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+zoos = Zoo.all(:conditions => {:id => 34})
+zoos = Zoo.all(:conditions => ["id = ?", 34])
+
+# even mix and match
+zoos = Zoo.all(:conditions => {:id => 34}, :name.like => '%foo%')
+<% end %>
+
+h2. Find By SQL
+
+Sometimes you may find that you need to tweak a query using your own SQL.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+zoos = repository.query('SELECT name, open FROM zoos WHERE open = 1')
+# Zoo.find_by_sql() would have worked here, too
+<% end %>
+
+@zoos@ will be full of Struct objects with @name@, and @open@ attributes, rather than instances of the Zoo class. They'll also be read-only. You can still use the interpolated array condition syntax as well:
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+zoos = repository.query('SELECT name, open FROM zoos WHERE name = ?', "Awesome Zoo")
+<% end %>
+
+h2. Counting
+
+The count method returns an integer of the number of records matching the every condition you pass in.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+count = Zoo.count(:age.gt => 200) #=> 2
+<% end %>
+
+h2. Each
+
+DataMapper sports an incredibly powerful @each@ method with safely iterates over large quantities of rows in a table performing the block you pass in. Think of it like a @Animal.find(:all).each {}@ block, but so much easier on your resources. Rather than instantiating all objects and then iterating over them, @each@ works in batches and only instantiates a batch of results at a time, then iterates over items in the batch. Notice how you can pass it @options@ just like a finder.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+Animal.each(:species => 'Mammal').each do |a|
+ a.reproduce!
+end
+<% end %>
+
+
+
+
+
+
View
64 content/docs/index.txt
@@ -0,0 +1,64 @@
+---
+title: Documentation
+body_id: docs
+created_at: Fri Nov 30 15:29:01 +1030 2007
+filter:
+ - erb
+ - textile
+---
+
+h1. <%= @page.title %>
+
+<dl>
+ <dt><a href="http://datamapper.rubyforge.org/">API</a></dt>
+ <dd>The API for the current gem release.</dd>
+ <dt><a href="/why.html">Why DataMapper?</a></dt>
+ <dd>If you haven't read this yet, you should, right now!</dd>
+ <dt><a href="/getting_started.html">Getting Started</a></dt>
+ <dd>A whirlwind tour of DM. This is the place to start if you haven't used the library before.</dd>
+ <dt><a href="/docs/install.html">Common installation issues</a></dt>
+ <dd>Troubleshooting installation, with instructions for specific platforms.</dd>
+ <dt><a href="/docs/properties.html">Properties</a></dt>
+ <dd>Properties declared in your model map to the fields in the database.</dd>
+ <dt><a href="/docs/create_and_destroy.html">Creating, Saving and Destroying Records</a></dt>
+ <dd>Obviously you're going to be doing a lot of this :)</dd>
+ <dt><a href="/docs/find.html">Finding and Counting Records</a></dt>
+ <dd>There are lots of nice ways to find records, including some found in the ActiveRecord impersonation module.</dd>
+ <dt><a href="/docs/associations.html">Associations</a></dt>
+ <dd>Models can be associated to each other in various ways -- has_many, belongs_to, has_and_belongs_to_many</dd>
+ <dt><a href="/docs/callbacks.html">Callbacks</a></dt>
+ <dd>Callbacks allow you to call your own code each time a record is created, validated and saved if you need.</dd>
+ <dt><a href="/docs/misc.html">Misc. Features</a></dt>
+ <dd>Paranoia, Single Table Inheritance, Multiple Database Connections, and Chained Associations</dd>
+
+</dl>
+
+
+h1. Development
+
+DataMapper development has switched to the "Git":http://git.or.cz SCM. Please see "using git":/using-git.html to learn how to contribute. To check out "tip" DataMapper anonymously:
+
+<% coderay(:lang => "bash", :line_numbers => "inline") do -%>
+git clone git://github.com/sam/dm-core.git
+<% end %>
+
+If you have a "github":http://www.github.com account, log in, and "fork" "Sam's Repo":http://github.com/sam/dm-core/tree/master. When you think you're ready, send Sam a "pull request".
+
+h2. SVN - Bug Fixes for 0.3.0 (gem before The Great Refactor)
+
+SVN development is for 0.3.0 (Stable) bug-fixes only. "Tip" DataMapper has moved to "Git":http://github.com/sam/dm-core.
+
+<% coderay(:lang => "bash", :line_numbers => "inline") do -%>
+svn co http://datamapper.rubyforge.org/svn/trunk/ data_mapper
+<% end %>
+
+h2. Coding Conventions and Adminstriva
+
+When contributing any code to DataMapper, please follow these guidelines.
+
+# Spec first. Spec thoroughly. (DataMapper is written with "Rspec":http://rspec.info/)
+# Parentheses around parameter lists for methods
+# Two space indent - not tabs!
+# Documentation is required (use the "Merb documentation style":http://yehudakatz.com/2008/01/14/merbnext/)
+
+Consider following the "code optimization tips":http://wiki.datamapper.org/doku.php?id=codeoptimizationtips page on the wiki.
View
44 content/docs/install.txt
@@ -0,0 +1,44 @@
+---
+title: Installation Issues
+body_id: docs
+created_at: Tue Dec 04 13:20:00 +1030 2007
+filter:
+ - erb
+ - textile
+---
+
+h1. <%= @page.title %>
+
+If you've followed the "install instructions":/getting_started.html but run into problems, you can find some tips below. At the moment DataMapper is still quite young, so the install process isn't 100% bullet proof... yet!
+
+h2(newRelease). Windows Users
+
+p(newRelease). At present, "DataObjects":http://dataobjects.devjavu.com/ does not run well on Windows natively <br>and will require you to install cygwin or another linux-like <br>environment. People have been able to get it installed and running <br>on Windows but with severe drops in performance. This is a known <br>issue and we're working on it.
+
+h2. Dependencies
+
+First port of call if you're having issues with an installation is to make sure you have all the dependencies installed. Rubygems should take care of this for you, but just in case, make sure you have the following gems as well:
+
+* fastthread
+* json
+* validatable
+* rspec - for running specs on DataMapper itself
+
+h2(#trunk). Using Trunk
+
+You will also need to install the DataObject gem and the adaptor for your platform
+
+<% coderay(:lang => "bash", :line_numbers => "inline") do -%>
+sudo gem install data_objects
+sudo gem install do_mysql
+<% end %>
+
+The current database adaptors are:
+
+* do_mysql
+* do_sqlite3
+* do_postgres
+
+h2. Getting Help
+
+If you still have issues, we suggest getting onto the "mailing list":http://groups.google.com/group/datamapper or the "IRC channel":irc://irc.freenode.net/#datamapper and asking around. There's friendly people there to help you out.
View
98 content/docs/misc.txt
@@ -0,0 +1,98 @@
+---
+title: Miscellaneous Features
+created_at: Thu Mar 20 23:26:54 -0500 2008
+filter:
+ - erb
+ - textile
+---
+h1. <%= @page.title %>
+
+DataMapper comes loaded features, many of which other ORMs require external libraries for.
+
+h2. Single Table Inheritance
+
+Many ORMs support Single Table Inheritance and DataMapper is no different. In order to declare a model for Single Table Inheritance, define a property with the data-type of @:class@
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Person < DataMapper::Base
+ property :name, :string
+ property :job, :string, :length => 255
+ property :type, :class
+ ...
+end
+
+class Geek < Person; end
+class Nerd < Person; end
+
+<% end %>
+
+When DataMapper sees your @type@ column declared as type @:class@, it will automatically insert the class name of the object you've created and later instantiate that row as that class.
+
+h2. Paranoia
+
+Sometimes...most times...you don't _really_ want to destroy a row in the database, you just want to mark it as deleted so that you can restore it later if need be. This is aptly-named Paranoia and DataMapper has basic support for this baked right in. When you declare a @deleted_at@ or @deleted_on@ (datetime) property, DataMapper will pick up on it and assume they are for storing when an object was deleted.
+
+Once these columns exist, DataMapper will also tack on @deleted_[at|on] IS NULL@ to generated queries in order to avoid retrieving rows marked as deleted.
+
+To subvert paranoia and pull back previously deleted rows, simply use manual querying:
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+dead_people = database.query('SELECT id, name, job FROM people WHERE deleted_at IS NOT NULL')
+<% end %>
+
+For now, you'll get back Structs which will "quack like" a Person object, but won't have any of the decorations you defined in the Person class. This is plenty to resurrect the row from it's deleted-ness.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+dead_people.each do |zombie|
+ Person.new(zombie.attributes.reject{ |k, v| k == :deleted_at} )
+end # dead_people will no longer contain zombies, but real live people
+
+<% end %>
+
+h2. Multiple Database Connections
+
+DataMapper sports a concept called a Context which encapsulates the database context in which you want operations to occur. For example, when you setup a database connection in "getting_started":/getting_started.html, you were defining a context known as @:default@
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+DataMapper::Database.setup({ # :default is the default context name
+ :adapter => 'mysql',
+ :host => 'localhost',
+ :username => 'root',
+ :password => 'R00tPaswooooord',
+ :database => 'myspiffyblog_development'
+})
+<% end %>
+
+But if you supply a context name, you will now have 2 database contexts with their own unique loggers, connection pool, identity map....one default context and one named context.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+DataMapper::Database.setup(:external, { # :external is the context name
+ :adapter => 'mysql',
+ :host => 'otherhost',
+ :username => 'root',
+ :password => 'R00tPaswooooord2',
+ :database => 'someother_development'
+})
+<% end %>
+
+To use one context rather than another, simply wrap your code block inside a @database@ call. It will return whatever your block of code returns.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+database(:external) { Person.first }
+# hits up your :external database and retrieves the first Person
+<% end %>
+
+This will use your connection to the @:external@ database and the first Person it finds. Later, when you call @.save@ on that person, it'll get saved back to the @:external@ database; An object is aware of what context it came from and should be saved back to.
+
+h2. Chained Associations
+
+Say you want to find all of the animals in a zoo, but Animal belongs to Exhibit which belongs to Zoo. Other ORMs solve this problem by providing a means to describe the double JOINs into the retrieval call for Animals. ActiveRecord specifically will let you specify JOINs in a hash-of-hashes syntax which will make most developers throw up a little in their mouths.
+
+DataMapper's solution is to let you chain association calls:
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+zoo = Zoo.first
+zoo.exhibits.animals # retrieves all animals for all exhibits for that zoo
+<% end %>
+
+This has great potential for browsing collections of content, like browsing all blog posts' comments by category or tag. At present, chaining beyond 2 associations is still experimental.
View
86 content/docs/properties.txt
@@ -0,0 +1,86 @@
+---
+title: Properties
+body_id: docs
+created_at: Tue Dec 04 13:27:16 +1030 2007
+filter:
+ - erb
+ - textile
+---
+
+h1. <%= @page.title %>
+
+A model's properties are not introspected from the fields in the database; In fact the reverse happens. You declare the properties for a model inside it's class definition, which is then used to generate the fields in the database.
+
+This has a few advantages. First it means that a model's properties are documented in the model itself, not a migration or XML file. If you've ever been annoyed at having to look in a schema file to see the list of properties and types for a model, you'll find this particularly useful.
+
+Second, it lets you limit access to properties using Ruby's access semantics. Properties can be declared public, private or protected. They are public by default.
+
+Finally, since DataMapper only cares about properties explicitly defined in your models, DataMapper plays well with legacy databases and shares databases easily with other applications.
+
+h2. Declaring Properties
+
+Inside your class, call the property method for each property you want to add. The only two required arguments are the name and type, everything else is optional.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post < DataMapper::Base
+ property :title, :string, :nullable => false # Cannot be null
+ property :publish, :boolean, :default => false # Default value for new records is false
+end
+<% end %>
+
+h2. Limiting Access
+
+Access for properties is defined using the same semantics as Ruby. Accessors are public by default, but you can declare them as private or protected if you need. You can set access using the @:accessor@ option.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post < DataMapper::Base
+ property :title, :string, :accessor => :private # Both reader and writer are private
+ property :body, :text, :accessor => :protected # Both reader and writer are protected
+end
+<% end %>
+
+You also have more fine grained control over how you declare access. You can, for example, have a public reader and private writer by using the @:writer@ and @:reader@ options. (Remember, the default is Public)
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post < DataMapper::Base
+ property :title, :string, :writer => :private # Only writer is private
+ property :tags, :string, :reader => :protected # Only reader is protected
+end
+<% end %>
+
+h2. Over-riding Accessors
+
+When a property has declared accessors for getting and setting, it's values are added to the model. Just like using @attr_accessor@, you can over-ride these with your own custom accessors. It's a simple matter of adding an accessor after the property declaration.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post < DataMapper::Base
+ property :title, :string
+
+ def title=(new_title)
+ raise ArgumentError if new_title != 'DataMapper is Awesome'
+ @title = new_title
+ end
+end
+<% end %>
+
+h2. Lazy Loading
+
+Properties can be configured to be lazy loading. A lazily loaded property is not requested from the database by default. Instead it is only loaded when it's accessor is called for the first time. This means you can stop default queries from being greedy, a particular problem with text fields. Text fields are lazily loaded by default, which you can over-ride if you need to.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post < DataMapper::Base
+ property :title, :string
+ property :body, :text # Is lazily loaded by default
+ property :notes, :text, :lazy => false # Isn't lazily loaded
+end
+<% end %>
+
+h2. Primary and Natural Keys
+
+To set your own primary key, and even use a string column as a primary 'natural' key, pass @:key => true@ as an option.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post < DataMapper::Base
+ property :slug, :string, :key => true
+end
+<% end %>
View
25 content/get.txt
@@ -0,0 +1,25 @@
+---
+title: Get DataMapper
+created_at: Wed Aug 29 20:37:05 +0930 2007
+filter:
+ - erb
+ - textile
+---
+h1. <%= @page.title %>
+
+To get the 0.2.4 release of DataMapper you can download it here, or install it via RubyGems
+
+<% coderay(:lang => "bash", :line_numbers => "inline") do -%>
+gem install datamapper
+<% end %>
+
+
+h1. Get the source
+
+If you feel like contributing - yes please! - you can check out the code via Subversion.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+svn co http://datamapper.rubyforge.org/svn/trunk/ data_mapper
+<% end %>
+
+You will also need to "install the DataObjects plugin":/docs/install.html#trunk to use the trunk version of DataMapper.
View
120 content/getting_started.txt
@@ -0,0 +1,120 @@
+---
+page_id: gettingStarted
+title: Getting started with DataMapper 0.3.0
+created_at: Wed Aug 29 20:36:53 +0930 2007
+filter:
+ - erb
+ - textile
+---
+
+h1. <%= @page.title %>
+
+First, if you think you might need some help, there's an active community supporting DataMapper through "the mailing list":http://groups.google.com/group/datamapper and the @#datamapper@ IRC channel on irc.freenode.net.
+
+So lets imagine we're setting up some models for a blogging app. We'll keep it nice and simple. The first thing to decide on is what models we want. Post is a given. So is Comment. But let's mix it up and do Category too.
+
+h2. Install DataMapper
+
+If you have RubyGems installed, pop open your console and install DataMapper.
+
+<% coderay(:lang => "bash", :line_numbers => "inline") do -%>
+gem install datamapper
+<% end %>
+
+h2. Install the "DataObjects.rb":http://dataobjects.devjavu.com drivers.
+
+Issue the following command to install your preferred flavor of DataObjects. This is the equivalent to @gem install mysql@ when you installed ActiveRecord.
+
+<% coderay(:lang => "bash", :line_numbers => "inline") do -%>
+gem install do_mysql # or do_sqlite3 or do_postgres
+<% end %>
+
+h2. Require it in your application
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+require 'rubygems'
+require 'data_mapper'
+<% end %>
+
+h2. Specify your database connection
+
+You need make sure this is set before you define your models.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+DataMapper::Database.setup({
+ :adapter => 'mysql',
+ :host => 'localhost',
+ :username => 'root',
+ :password => 'R00tPaswooooord',
+ :database => 'myspiffyblog_development'
+})
+<% end %>
+
+h2. Define your models
+
+The Post model should inherit from DataMapper::Base. The convention with model names is to use the singular, not plural version.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post < DataMapper::Base
+ property :title, :string
+ property :body, :text
+ property :created_at, :datetime
+end
+<% end %>
+
+You can also mix-in DataMapper through an @include@.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Post
+ include DataMapper::Persistence
+
+ property :title, :string
+ property :body, :text
+ property :created_at, :datetime
+end
+<% end %>
+
+h3. Associations
+
+We want to associate the posts with the categories and comments.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Category < DataMapper::Base
+ property :name, :string
+
+ has_many :posts
+end
+
+class Comment < DataMapper::Base
+ property :posted_by, :string
+ property :email, :string
+ property :url, :string
+ property :body, :text
+
+ belongs_to :post
+end
+
+# Now we re-open our Post class to define the associations.
+# This would be best included in with the original definition
+# of the class, but for the purposes of our demo, this'll do.
+class Post
+ has_many :comments
+ belongs_to :category
+end
+<% end %>
+
+h2. Set up your database tables
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+Post.table.create!
+Category.table.create!
+Comment.table.create!
+<% end %>
+
+This will issue the necessary CREATE statements to define each table according to their properties.
+
+You could also do:
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+DataMapper::Persistence.auto_migrate!
+<% end %>
View
BIN content/images/bug.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/content_bkg.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/download_button.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/footer_bkg.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/get_button.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/header_bkg.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/home_bkg.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/logo.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/martini.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/new_release_bkg.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/page_bkg.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/people/.DS_Store
Binary file not shown.
View
BIN content/images/people/ben_burket.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/people/guy_v.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/people/heimidal.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/people/ior3k.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/people/rando.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/people/reinh.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/people/yehuda.jpg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/puff_bkg.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN content/images/waiter.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
58 content/index.txt
@@ -0,0 +1,58 @@
+---
+body_id: home
+title: DataMapper
+filter:
+ - erb
+ - textile
+---
+
+DataMapper is a "Object Relational Mapper":http://en.wikipedia.org/wiki/Object-relational_mapping written in "Ruby.":http://ruby-lang.org/ The goal is to create an ORM which is fast, thread-safe and feature rich.
+
+To learn a little more about this project and why you should be interested,<br> read the "Why Datamapper?":/why.html page.
+
+<% update = @pages.find(1, :in_directory => "articles", :sort_by => "created_at", :reverse => true, :release_type => 'important')
+%>
+
+h2(latestRelease). Recent News
+
+p(latestRelease). <%= update.title %> <br> <%= update.summary %> <a href="<%= update.url %>" class="read_more">Read More</a>
+
+<div id="help">
+
+h2. Help!
+
+If you're having trouble, don't forget to check the documentation, which has both references and step by step tutorials.
+
+"Read documentation":/docs
+
+</div>
+
+<div id="bugs">
+
+h2. Issues
+
+If you're still having trouble, or you think you came across something you think might be a bug, let us know.
+
+"Log a ticket":http://wm.lighthouseapp.com/projects/4819-datamapper/overview
+
+</div>
+
+h1. Examples using 0.3.0
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Animal < DataMapper::Base
+ property :name, :string
+ property :notes, :text, :lazy => true
+
+ has_one :favorite_fruit, :class => 'Fruit'
+ has_and_belongs_to_many :exhibits
+end
+
+<% end %>
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+monkey = Animal.first(:name => 'Monkey')
+monkey.favorite_fruit.name # => 'Bananas'
+<% end %>
+</coderay>
+</notextile>
View
11 content/js/jquery-1.2.3.pack.js
@@ -0,0 +1,11 @@
+/*
+ * jQuery 1.2.3 - New Wave Javascript
+ *
+ * Copyright (c) 2008 John Resig (jquery.com)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * $Date: 2008-02-06 00:21:25 -0500 (Wed, 06 Feb 2008) $
+ * $Rev: 4663 $
+ */
+eval(function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(J(){7(1e.3N)L w=1e.3N;L E=1e.3N=J(a,b){K 1B E.2l.4T(a,b)};7(1e.$)L D=1e.$;1e.$=E;L u=/^[^<]*(<(.|\\s)+>)[^>]*$|^#(\\w+)$/;L G=/^.[^:#\\[\\.]*$/;E.1n=E.2l={4T:J(d,b){d=d||T;7(d.15){6[0]=d;6.M=1;K 6}N 7(1o d=="25"){L c=u.2O(d);7(c&&(c[1]||!b)){7(c[1])d=E.4a([c[1]],b);N{L a=T.5J(c[3]);7(a)7(a.2w!=c[3])K E().2s(d);N{6[0]=a;6.M=1;K 6}N d=[]}}N K 1B E(b).2s(d)}N 7(E.1q(d))K 1B E(T)[E.1n.21?"21":"3U"](d);K 6.6E(d.1k==1M&&d||(d.5h||d.M&&d!=1e&&!d.15&&d[0]!=10&&d[0].15)&&E.2I(d)||[d])},5h:"1.2.3",87:J(){K 6.M},M:0,22:J(a){K a==10?E.2I(6):6[a]},2F:J(b){L a=E(b);a.54=6;K a},6E:J(a){6.M=0;1M.2l.1g.1i(6,a);K 6},R:J(a,b){K E.R(6,a,b)},4X:J(b){L a=-1;6.R(J(i){7(6==b)a=i});K a},1J:J(c,a,b){L d=c;7(c.1k==4e)7(a==10)K 6.M&&E[b||"1J"](6[0],c)||10;N{d={};d[c]=a}K 6.R(J(i){Q(c 1p d)E.1J(b?6.W:6,c,E.1l(6,d[c],b,i,c))})},1j:J(b,a){7((b==\'27\'||b==\'1R\')&&2M(a)<0)a=10;K 6.1J(b,a,"2o")},1u:J(b){7(1o b!="3V"&&b!=V)K 6.4x().3t((6[0]&&6[0].2i||T).5r(b));L a="";E.R(b||6,J(){E.R(6.3p,J(){7(6.15!=8)a+=6.15!=1?6.6K:E.1n.1u([6])})});K a},5m:J(b){7(6[0])E(b,6[0].2i).5k().3o(6[0]).2c(J(){L a=6;2b(a.1C)a=a.1C;K a}).3t(6);K 6},8w:J(a){K 6.R(J(){E(6).6z().5m(a)})},8p:J(a){K 6.R(J(){E(6).5m(a)})},3t:J(){K 6.3O(18,P,S,J(a){7(6.15==1)6.38(a)})},6q:J(){K 6.3O(18,P,P,J(a){7(6.15==1)6.3o(a,6.1C)})},6o:J(){K 6.3O(18,S,S,J(a){6.1a.3o(a,6)})},5a:J(){K 6.3O(18,S,P,J(a){6.1a.3o(a,6.2B)})},3h:J(){K 6.54||E([])},2s:J(b){L c=E.2c(6,J(a){K E.2s(b,a)});K 6.2F(/[^+>] [^+>]/.17(b)||b.1f("..")>-1?E.57(c):c)},5k:J(e){L f=6.2c(J(){7(E.14.1d&&!E.3E(6)){L a=6.69(P),4Y=T.3s("1x");4Y.38(a);K E.4a([4Y.3d])[0]}N K 6.69(P)});L d=f.2s("*").4R().R(J(){7(6[F]!=10)6[F]=V});7(e===P)6.2s("*").4R().R(J(i){7(6.15==3)K;L c=E.O(6,"2R");Q(L a 1p c)Q(L b 1p c[a])E.16.1b(d[i],a,c[a][b],c[a][b].O)});K f},1E:J(b){K 6.2F(E.1q(b)&&E.3y(6,J(a,i){K b.1P(a,i)})||E.3e(b,6))},56:J(b){7(b.1k==4e)7(G.17(b))K 6.2F(E.3e(b,6,P));N b=E.3e(b,6);L a=b.M&&b[b.M-1]!==10&&!b.15;K 6.1E(J(){K a?E.33(6,b)<0:6!=b})},1b:J(a){K!a?6:6.2F(E.37(6.22(),a.1k==4e?E(a).22():a.M!=10&&(!a.12||E.12(a,"3u"))?a:[a]))},3H:J(a){K a?E.3e(a,6).M>0:S},7j:J(a){K 6.3H("."+a)},5O:J(b){7(b==10){7(6.M){L c=6[0];7(E.12(c,"2k")){L e=c.3T,5I=[],11=c.11,2X=c.U=="2k-2X";7(e<0)K V;Q(L i=2X?e:0,2f=2X?e+1:11.M;i<2f;i++){L d=11[i];7(d.2p){b=E.14.1d&&!d.9J.1A.9y?d.1u:d.1A;7(2X)K b;5I.1g(b)}}K 5I}N K(6[0].1A||"").1r(/\\r/g,"")}K 10}K 6.R(J(){7(6.15!=1)K;7(b.1k==1M&&/5u|5t/.17(6.U))6.3k=(E.33(6.1A,b)>=0||E.33(6.31,b)>=0);N 7(E.12(6,"2k")){L a=b.1k==1M?b:[b];E("98",6).R(J(){6.2p=(E.33(6.1A,a)>=0||E.33(6.1u,a)>=0)});7(!a.M)6.3T=-1}N 6.1A=b})},3q:J(a){K a==10?(6.M?6[0].3d:V):6.4x().3t(a)},6S:J(a){K 6.5a(a).1V()},6Z:J(i){K 6.2K(i,i+1)},2K:J(){K 6.2F(1M.2l.2K.1i(6,18))},2c:J(b){K 6.2F(E.2c(6,J(a,i){K b.1P(a,i,a)}))},4R:J(){K 6.1b(6.54)},O:J(d,b){L a=d.23(".");a[1]=a[1]?"."+a[1]:"";7(b==V){L c=6.5n("8P"+a[1]+"!",[a[0]]);7(c==10&&6.M)c=E.O(6[0],d);K c==V&&a[1]?6.O(a[0]):c}N K 6.1N("8K"+a[1]+"!",[a[0],b]).R(J(){E.O(6,d,b)})},35:J(a){K 6.R(J(){E.35(6,a)})},3O:J(g,f,h,d){L e=6.M>1,3n;K 6.R(J(){7(!3n){3n=E.4a(g,6.2i);7(h)3n.8D()}L b=6;7(f&&E.12(6,"1O")&&E.12(3n[0],"4v"))b=6.3S("1U")[0]||6.38(6.2i.3s("1U"));L c=E([]);E.R(3n,J(){L a=e?E(6).5k(P)[0]:6;7(E.12(a,"1m")){c=c.1b(a)}N{7(a.15==1)c=c.1b(E("1m",a).1V());d.1P(b,a)}});c.R(6A)})}};E.2l.4T.2l=E.2l;J 6A(i,a){7(a.3Q)E.3P({1c:a.3Q,3l:S,1H:"1m"});N E.5g(a.1u||a.6x||a.3d||"");7(a.1a)a.1a.34(a)}E.1s=E.1n.1s=J(){L b=18[0]||{},i=1,M=18.M,5c=S,11;7(b.1k==8d){5c=b;b=18[1]||{};i=2}7(1o b!="3V"&&1o b!="J")b={};7(M==1){b=6;i=0}Q(;i<M;i++)7((11=18[i])!=V)Q(L a 1p 11){7(b===11[a])6w;7(5c&&11[a]&&1o 11[a]=="3V"&&b[a]&&!11[a].15)b[a]=E.1s(b[a],11[a]);N 7(11[a]!=10)b[a]=11[a]}K b};L F="3N"+(1B 3v()).3L(),6t=0,5b={};L H=/z-?4X|86-?84|1w|6k|7Z-?1R/i;E.1s({7Y:J(a){1e.$=D;7(a)1e.3N=w;K E},1q:J(a){K!!a&&1o a!="25"&&!a.12&&a.1k!=1M&&/J/i.17(a+"")},3E:J(a){K a.1F&&!a.1h||a.28&&a.2i&&!a.2i.1h},5g:J(a){a=E.3g(a);7(a){L b=T.3S("6f")[0]||T.1F,1m=T.3s("1m");1m.U="1u/4m";7(E.14.1d)1m.1u=a;N 1m.38(T.5r(a));b.38(1m);b.34(1m)}},12:J(b,a){K b.12&&b.12.2E()==a.2E()},1T:{},O:J(c,d,b){c=c==1e?5b:c;L a=c[F];7(!a)a=c[F]=++6t;7(d&&!E.1T[a])E.1T[a]={};7(b!=10)E.1T[a][d]=b;K d?E.1T[a][d]:a},35:J(c,b){c=c==1e?5b:c;L a=c[F];7(b){7(E.1T[a]){2V E.1T[a][b];b="";Q(b 1p E.1T[a])1Q;7(!b)E.35(c)}}N{1S{2V c[F]}1X(e){7(c.52)c.52(F)}2V E.1T[a]}},R:J(c,a,b){7(b){7(c.M==10){Q(L d 1p c)7(a.1i(c[d],b)===S)1Q}N Q(L i=0,M=c.M;i<M;i++)7(a.1i(c[i],b)===S)1Q}N{7(c.M==10){Q(L d 1p c)7(a.1P(c[d],d,c[d])===S)1Q}N Q(L i=0,M=c.M,1A=c[0];i<M&&a.1P(1A,i,1A)!==S;1A=c[++i]){}}K c},1l:J(b,a,c,i,d){7(E.1q(a))a=a.1P(b,i);K a&&a.1k==51&&c=="2o"&&!H.17(d)?a+"2S":a},1t:{1b:J(c,b){E.R((b||"").23(/\\s+/),J(i,a){7(c.15==1&&!E.1t.3Y(c.1t,a))c.1t+=(c.1t?" ":"")+a})},1V:J(c,b){7(c.15==1)c.1t=b!=10?E.3y(c.1t.23(/\\s+/),J(a){K!E.1t.3Y(b,a)}).6a(" "):""},3Y:J(b,a){K E.33(a,(b.1t||b).3X().23(/\\s+/))>-1}},68:J(b,c,a){L e={};Q(L d 1p c){e[d]=b.W[d];b.W[d]=c[d]}a.1P(b);Q(L d 1p c)b.W[d]=e[d]},1j:J(d,e,c){7(e=="27"||e=="1R"){L b,46={43:"4W",4U:"1Z",19:"3D"},3c=e=="27"?["7O","7M"]:["7J","7I"];J 5E(){b=e=="27"?d.7H:d.7F;L a=0,2N=0;E.R(3c,J(){a+=2M(E.2o(d,"7E"+6,P))||0;2N+=2M(E.2o(d,"2N"+6+"5X",P))||0});b-=24.7C(a+2N)}7(E(d).3H(":4d"))5E();N E.68(d,46,5E);K 24.2f(0,b)}K E.2o(d,e,c)},2o:J(e,k,j){L d;J 3x(b){7(!E.14.2d)K S;L a=T.4c.4K(b,V);K!a||a.4M("3x")==""}7(k=="1w"&&E.14.1d){d=E.1J(e.W,"1w");K d==""?"1":d}7(E.14.2z&&k=="19"){L c=e.W.50;e.W.50="0 7r 7o";e.W.50=c}7(k.1D(/4g/i))k=y;7(!j&&e.W&&e.W[k])d=e.W[k];N 7(T.4c&&T.4c.4K){7(k.1D(/4g/i))k="4g";k=k.1r(/([A-Z])/g,"-$1").2h();L h=T.4c.4K(e,V);7(h&&!3x(e))d=h.4M(k);N{L f=[],2C=[];Q(L a=e;a&&3x(a);a=a.1a)2C.4J(a);Q(L i=0;i<2C.M;i++)7(3x(2C[i])){f[i]=2C[i].W.19;2C[i].W.19="3D"}d=k=="19"&&f[2C.M-1]!=V?"2H":(h&&h.4M(k))||"";Q(L i=0;i<f.M;i++)7(f[i]!=V)2C[i].W.19=f[i]}7(k=="1w"&&d=="")d="1"}N 7(e.4n){L g=k.1r(/\\-(\\w)/g,J(a,b){K b.2E()});d=e.4n[k]||e.4n[g];7(!/^\\d+(2S)?$/i.17(d)&&/^\\d/.17(d)){L l=e.W.26,3K=e.3K.26;e.3K.26=e.4n.26;e.W.26=d||0;d=e.W.7f+"2S";e.W.26=l;e.3K.26=3K}}K d},4a:J(l,h){L k=[];h=h||T;7(1o h.3s==\'10\')h=h.2i||h[0]&&h[0].2i||T;E.R(l,J(i,d){7(!d)K;7(d.1k==51)d=d.3X();7(1o d=="25"){d=d.1r(/(<(\\w+)[^>]*?)\\/>/g,J(b,a,c){K c.1D(/^(aa|a6|7e|a5|4D|7a|a0|3m|9W|9U|9S)$/i)?b:a+"></"+c+">"});L f=E.3g(d).2h(),1x=h.3s("1x");L e=!f.1f("<9P")&&[1,"<2k 74=\'74\'>","</2k>"]||!f.1f("<9M")&&[1,"<73>","</73>"]||f.1D(/^<(9G|1U|9E|9B|9x)/)&&[1,"<1O>","</1O>"]||!f.1f("<4v")&&[2,"<1O><1U>","</1U></1O>"]||(!f.1f("<9w")||!f.1f("<9v"))&&[3,"<1O><1U><4v>","</4v></1U></1O>"]||!f.1f("<7e")&&[2,"<1O><1U></1U><6V>","</6V></1O>"]||E.14.1d&&[1,"1x<1x>","</1x>"]||[0,"",""];1x.3d=e[1]+d+e[2];2b(e[0]--)1x=1x.5o;7(E.14.1d){L g=!f.1f("<1O")&&f.1f("<1U")<0?1x.1C&&1x.1C.3p:e[1]=="<1O>"&&f.1f("<1U")<0?1x.3p:[];Q(L j=g.M-1;j>=0;--j)7(E.12(g[j],"1U")&&!g[j].3p.M)g[j].1a.34(g[j]);7(/^\\s/.17(d))1x.3o(h.5r(d.1D(/^\\s*/)[0]),1x.1C)}d=E.2I(1x.3p)}7(d.M===0&&(!E.12(d,"3u")&&!E.12(d,"2k")))K;7(d[0]==10||E.12(d,"3u")||d.11)k.1g(d);N k=E.37(k,d)});K k},1J:J(d,e,c){7(!d||d.15==3||d.15==8)K 10;L f=E.3E(d)?{}:E.46;7(e=="2p"&&E.14.2d)d.1a.3T;7(f[e]){7(c!=10)d[f[e]]=c;K d[f[e]]}N 7(E.14.1d&&e=="W")K E.1J(d.W,"9u",c);N 7(c==10&&E.14.1d&&E.12(d,"3u")&&(e=="9r"||e=="9o"))K d.9m(e).6K;N 7(d.28){7(c!=10){7(e=="U"&&E.12(d,"4D")&&d.1a)6Q"U 9i 9h\'t 9g 9e";d.9b(e,""+c)}7(E.14.1d&&/6O|3Q/.17(e)&&!E.3E(d))K d.4z(e,2);K d.4z(e)}N{7(e=="1w"&&E.14.1d){7(c!=10){d.6k=1;d.1E=(d.1E||"").1r(/6M\\([^)]*\\)/,"")+(2M(c).3X()=="96"?"":"6M(1w="+c*6L+")")}K d.1E&&d.1E.1f("1w=")>=0?(2M(d.1E.1D(/1w=([^)]*)/)[1])/6L).3X():""}e=e.1r(/-([a-z])/95,J(a,b){K b.2E()});7(c!=10)d[e]=c;K d[e]}},3g:J(a){K(a||"").1r(/^\\s+|\\s+$/g,"")},2I:J(b){L a=[];7(1o b!="93")Q(L i=0,M=b.M;i<M;i++)a.1g(b[i]);N a=b.2K(0);K a},33:J(b,a){Q(L i=0,M=a.M;i<M;i++)7(a[i]==b)K i;K-1},37:J(a,b){7(E.14.1d){Q(L i=0;b[i];i++)7(b[i].15!=8)a.1g(b[i])}N Q(L i=0;b[i];i++)a.1g(b[i]);K a},57:J(a){L c=[],2r={};1S{Q(L i=0,M=a.M;i<M;i++){L b=E.O(a[i]);7(!2r[b]){2r[b]=P;c.1g(a[i])}}}1X(e){c=a}K c},3y:J(c,a,d){L b=[];Q(L i=0,M=c.M;i<M;i++)7(!d&&a(c[i],i)||d&&!a(c[i],i))b.1g(c[i]);K b},2c:J(d,a){L c=[];Q(L i=0,M=d.M;i<M;i++){L b=a(d[i],i);7(b!==V&&b!=10){7(b.1k!=1M)b=[b];c=c.71(b)}}K c}});L v=8Y.8W.2h();E.14={5K:(v.1D(/.+(?:8T|8S|8R|8O)[\\/: ]([\\d.]+)/)||[])[1],2d:/77/.17(v),2z:/2z/.17(v),1d:/1d/.17(v)&&!/2z/.17(v),48:/48/.17(v)&&!/(8L|77)/.17(v)};L y=E.14.1d?"6H":"75";E.1s({8I:!E.14.1d||T.6F=="79",46:{"Q":"8F","8E":"1t","4g":y,75:y,6H:y,3d:"3d",1t:"1t",1A:"1A",2Y:"2Y",3k:"3k",8C:"8B",2p:"2p",8A:"8z",3T:"3T",6C:"6C",28:"28",12:"12"}});E.R({6B:J(a){K a.1a},8y:J(a){K E.4u(a,"1a")},8x:J(a){K E.2Z(a,2,"2B")},8v:J(a){K E.2Z(a,2,"4t")},8u:J(a){K E.4u(a,"2B")},8t:J(a){K E.4u(a,"4t")},8s:J(a){K E.5i(a.1a.1C,a)},8r:J(a){K E.5i(a.1C)},6z:J(a){K E.12(a,"8q")?a.8o||a.8n.T:E.2I(a.3p)}},J(c,d){E.1n[c]=J(b){L a=E.2c(6,d);7(b&&1o b=="25")a=E.3e(b,a);K 6.2F(E.57(a))}});E.R({6y:"3t",8m:"6q",3o:"6o",8l:"5a",8k:"6S"},J(c,b){E.1n[c]=J(){L a=18;K 6.R(J(){Q(L i=0,M=a.M;i<M;i++)E(a[i])[b](6)})}});E.R({8j:J(a){E.1J(6,a,"");7(6.15==1)6.52(a)},8i:J(a){E.1t.1b(6,a)},8h:J(a){E.1t.1V(6,a)},8g:J(a){E.1t[E.1t.3Y(6,a)?"1V":"1b"](6,a)},1V:J(a){7(!a||E.1E(a,[6]).r.M){E("*",6).1b(6).R(J(){E.16.1V(6);E.35(6)});7(6.1a)6.1a.34(6)}},4x:J(){E(">*",6).1V();2b(6.1C)6.34(6.1C)}},J(a,b){E.1n[a]=J(){K 6.R(b,18)}});E.R(["8f","5X"],J(i,c){L b=c.2h();E.1n[b]=J(a){K 6[0]==1e?E.14.2z&&T.1h["5e"+c]||E.14.2d&&1e["8e"+c]||T.6F=="79"&&T.1F["5e"+c]||T.1h["5e"+c]:6[0]==T?24.2f(24.2f(T.1h["5d"+c],T.1F["5d"+c]),24.2f(T.1h["5L"+c],T.1F["5L"+c])):a==10?(6.M?E.1j(6[0],b):V):6.1j(b,a.1k==4e?a:a+"2S")}});L C=E.14.2d&&4s(E.14.5K)<8c?"(?:[\\\\w*4r-]|\\\\\\\\.)":"(?:[\\\\w\\8b-\\8a*4r-]|\\\\\\\\.)",6v=1B 4q("^>\\\\s*("+C+"+)"),6u=1B 4q("^("+C+"+)(#)("+C+"+)"),6s=1B 4q("^([#.]?)("+C+"*)");E.1s({6r:{"":J(a,i,m){K m[2]=="*"||E.12(a,m[2])},"#":J(a,i,m){K a.4z("2w")==m[2]},":":{89:J(a,i,m){K i<m[3]-0},88:J(a,i,m){K i>m[3]-0},2Z:J(a,i,m){K m[3]-0==i},6Z:J(a,i,m){K m[3]-0==i},3j:J(a,i){K i==0},3J:J(a,i,m,r){K i==r.M-1},6n:J(a,i){K i%2==0},6l:J(a,i){K i%2},"3j-4p":J(a){K a.1a.3S("*")[0]==a},"3J-4p":J(a){K E.2Z(a.1a.5o,1,"4t")==a},"83-4p":J(a){K!E.2Z(a.1a.5o,2,"4t")},6B:J(a){K a.1C},4x:J(a){K!a.1C},82:J(a,i,m){K(a.6x||a.81||E(a).1u()||"").1f(m[3])>=0},4d:J(a){K"1Z"!=a.U&&E.1j(a,"19")!="2H"&&E.1j(a,"4U")!="1Z"},1Z:J(a){K"1Z"==a.U||E.1j(a,"19")=="2H"||E.1j(a,"4U")=="1Z"},80:J(a){K!a.2Y},2Y:J(a){K a.2Y},3k:J(a){K a.3k},2p:J(a){K a.2p||E.1J(a,"2p")},1u:J(a){K"1u"==a.U},5u:J(a){K"5u"==a.U},5t:J(a){K"5t"==a.U},59:J(a){K"59"==a.U},3I:J(a){K"3I"==a.U},58:J(a){K"58"==a.U},6j:J(a){K"6j"==a.U},6i:J(a){K"6i"==a.U},2G:J(a){K"2G"==a.U||E.12(a,"2G")},4D:J(a){K/4D|2k|6h|2G/i.17(a.12)},3Y:J(a,i,m){K E.2s(m[3],a).M},7X:J(a){K/h\\d/i.17(a.12)},7W:J(a){K E.3y(E.3G,J(b){K a==b.Y}).M}}},6g:[/^(\\[) *@?([\\w-]+) *([!*$^~=]*) *(\'?"?)(.*?)\\4 *\\]/,/^(:)([\\w-]+)\\("?\'?(.*?(\\(.*?\\))?[^(]*?)"?\'?\\)/,1B 4q("^([:.#]*)("+C+"+)")],3e:J(a,c,b){L d,2m=[];2b(a&&a!=d){d=a;L f=E.1E(a,c,b);a=f.t.1r(/^\\s*,\\s*/,"");2m=b?c=f.r:E.37(2m,f.r)}K 2m},2s:J(t,p){7(1o t!="25")K[t];7(p&&p.15!=1&&p.15!=9)K[];p=p||T;L d=[p],2r=[],3J,12;2b(t&&3J!=t){L r=[];3J=t;t=E.3g(t);L o=S;L g=6v;L m=g.2O(t);7(m){12=m[1].2E();Q(L i=0;d[i];i++)Q(L c=d[i].1C;c;c=c.2B)7(c.15==1&&(12=="*"||c.12.2E()==12))r.1g(c);d=r;t=t.1r(g,"");7(t.1f(" ")==0)6w;o=P}N{g=/^([>+~])\\s*(\\w*)/i;7((m=g.2O(t))!=V){r=[];L l={};12=m[2].2E();m=m[1];Q(L j=0,3f=d.M;j<3f;j++){L n=m=="~"||m=="+"?d[j].2B:d[j].1C;Q(;n;n=n.2B)7(n.15==1){L h=E.O(n);7(m=="~"&&l[h])1Q;7(!12||n.12.2E()==12){7(m=="~")l[h]=P;r.1g(n)}7(m=="+")1Q}}d=r;t=E.3g(t.1r(g,""));o=P}}7(t&&!o){7(!t.1f(",")){7(p==d[0])d.4l();2r=E.37(2r,d);r=d=[p];t=" "+t.6e(1,t.M)}N{L k=6u;L m=k.2O(t);7(m){m=[0,m[2],m[3],m[1]]}N{k=6s;m=k.2O(t)}m[2]=m[2].1r(/\\\\/g,"");L f=d[d.M-1];7(m[1]=="#"&&f&&f.5J&&!E.3E(f)){L q=f.5J(m[2]);7((E.14.1d||E.14.2z)&&q&&1o q.2w=="25"&&q.2w!=m[2])q=E(\'[@2w="\'+m[2]+\'"]\',f)[0];d=r=q&&(!m[3]||E.12(q,m[3]))?[q]:[]}N{Q(L i=0;d[i];i++){L a=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];7(a=="*"&&d[i].12.2h()=="3V")a="3m";r=E.37(r,d[i].3S(a))}7(m[1]==".")r=E.55(r,m[2]);7(m[1]=="#"){L e=[];Q(L i=0;r[i];i++)7(r[i].4z("2w")==m[2]){e=[r[i]];1Q}r=e}d=r}t=t.1r(k,"")}}7(t){L b=E.1E(t,r);d=r=b.r;t=E.3g(b.t)}}7(t)d=[];7(d&&p==d[0])d.4l();2r=E.37(2r,d);K 2r},55:J(r,m,a){m=" "+m+" ";L c=[];Q(L i=0;r[i];i++){L b=(" "+r[i].1t+" ").1f(m)>=0;7(!a&&b||a&&!b)c.1g(r[i])}K c},1E:J(t,r,h){L d;2b(t&&t!=d){d=t;L p=E.6g,m;Q(L i=0;p[i];i++){m=p[i].2O(t);7(m){t=t.7V(m[0].M);m[2]=m[2].1r(/\\\\/g,"");1Q}}7(!m)1Q;7(m[1]==":"&&m[2]=="56")r=G.17(m[3])?E.1E(m[3],r,P).r:E(r).56(m[3]);N 7(m[1]==".")r=E.55(r,m[2],h);N 7(m[1]=="["){L g=[],U=m[3];Q(L i=0,3f=r.M;i<3f;i++){L a=r[i],z=a[E.46[m[2]]||m[2]];7(z==V||/6O|3Q|2p/.17(m[2]))z=E.1J(a,m[2])||\'\';7((U==""&&!!z||U=="="&&z==m[5]||U=="!="&&z!=m[5]||U=="^="&&z&&!z.1f(m[5])||U=="$="&&z.6e(z.M-m[5].M)==m[5]||(U=="*="||U=="~=")&&z.1f(m[5])>=0)^h)g.1g(a)}r=g}N 7(m[1]==":"&&m[2]=="2Z-4p"){L e={},g=[],17=/(-?)(\\d*)n((?:\\+|-)?\\d*)/.2O(m[3]=="6n"&&"2n"||m[3]=="6l"&&"2n+1"||!/\\D/.17(m[3])&&"7U+"+m[3]||m[3]),3j=(17[1]+(17[2]||1))-0,d=17[3]-0;Q(L i=0,3f=r.M;i<3f;i++){L j=r[i],1a=j.1a,2w=E.O(1a);7(!e[2w]){L c=1;Q(L n=1a.1C;n;n=n.2B)7(n.15==1)n.4k=c++;e[2w]=P}L b=S;7(3j==0){7(j.4k==d)b=P}N 7((j.4k-d)%3j==0&&(j.4k-d)/3j>=0)b=P;7(b^h)g.1g(j)}r=g}N{L f=E.6r[m[1]];7(1o f=="3V")f=f[m[2]];7(1o f=="25")f=6c("S||J(a,i){K "+f+";}");r=E.3y(r,J(a,i){K f(a,i,m,r)},h)}}K{r:r,t:t}},4u:J(b,c){L d=[];L a=b[c];2b(a&&a!=T){7(a.15==1)d.1g(a);a=a[c]}K d},2Z:J(a,e,c,b){e=e||1;L d=0;Q(;a;a=a[c])7(a.15==1&&++d==e)1Q;K a},5i:J(n,a){L r=[];Q(;n;n=n.2B){7(n.15==1&&(!a||n!=a))r.1g(n)}K r}});E.16={1b:J(f,i,g,e){7(f.15==3||f.15==8)K;7(E.14.1d&&f.53!=10)f=1e;7(!g.2D)g.2D=6.2D++;7(e!=10){L h=g;g=J(){K h.1i(6,18)};g.O=e;g.2D=h.2D}L j=E.O(f,"2R")||E.O(f,"2R",{}),1v=E.O(f,"1v")||E.O(f,"1v",J(){L a;7(1o E=="10"||E.16.5f)K a;a=E.16.1v.1i(18.3R.Y,18);K a});1v.Y=f;E.R(i.23(/\\s+/),J(c,b){L a=b.23(".");b=a[0];g.U=a[1];L d=j[b];7(!d){d=j[b]={};7(!E.16.2y[b]||E.16.2y[b].4j.1P(f)===S){7(f.3F)f.3F(b,1v,S);N 7(f.6b)f.6b("4i"+b,1v)}}d[g.2D]=g;E.16.2a[b]=P});f=V},2D:1,2a:{},1V:J(e,h,f){7(e.15==3||e.15==8)K;L i=E.O(e,"2R"),29,4X;7(i){7(h==10||(1o h=="25"&&h.7T(0)=="."))Q(L g 1p i)6.1V(e,g+(h||""));N{7(h.U){f=h.2q;h=h.U}E.R(h.23(/\\s+/),J(b,a){L c=a.23(".");a=c[0];7(i[a]){7(f)2V i[a][f.2D];N Q(f 1p i[a])7(!c[1]||i[a][f].U==c[1])2V i[a][f];Q(29 1p i[a])1Q;7(!29){7(!E.16.2y[a]||E.16.2y[a].4h.1P(e)===S){7(e.67)e.67(a,E.O(e,"1v"),S);N 7(e.66)e.66("4i"+a,E.O(e,"1v"))}29=V;2V i[a]}}})}Q(29 1p i)1Q;7(!29){L d=E.O(e,"1v");7(d)d.Y=V;E.35(e,"2R");E.35(e,"1v")}}},1N:J(g,c,d,f,h){c=E.2I(c||[]);7(g.1f("!")>=0){g=g.2K(0,-1);L a=P}7(!d){7(6.2a[g])E("*").1b([1e,T]).1N(g,c)}N{7(d.15==3||d.15==8)K 10;L b,29,1n=E.1q(d[g]||V),16=!c[0]||!c[0].36;7(16)c.4J(6.4Z({U:g,2L:d}));c[0].U=g;7(a)c[0].65=P;7(E.1q(E.O(d,"1v")))b=E.O(d,"1v").1i(d,c);7(!1n&&d["4i"+g]&&d["4i"+g].1i(d,c)===S)b=S;7(16)c.4l();7(h&&E.1q(h)){29=h.1i(d,b==V?c:c.71(b));7(29!==10)b=29}7(1n&&f!==S&&b!==S&&!(E.12(d,\'a\')&&g=="4V")){6.5f=P;1S{d[g]()}1X(e){}}6.5f=S}K b},1v:J(c){L a;c=E.16.4Z(c||1e.16||{});L b=c.U.23(".");c.U=b[0];L f=E.O(6,"2R")&&E.O(6,"2R")[c.U],42=1M.2l.2K.1P(18,1);42.4J(c);Q(L j 1p f){L d=f[j];42[0].2q=d;42[0].O=d.O;7(!b[1]&&!c.65||d.U==b[1]){L e=d.1i(6,42);7(a!==S)a=e;7(e===S){c.36();c.44()}}}7(E.14.1d)c.2L=c.36=c.44=c.2q=c.O=V;K a},4Z:J(c){L a=c;c=E.1s({},a);c.36=J(){7(a.36)a.36();a.7S=S};c.44=J(){7(a.44)a.44();a.7R=P};7(!c.2L)c.2L=c.7Q||T;7(c.2L.15==3)c.2L=a.2L.1a;7(!c.4S&&c.5w)c.4S=c.5w==c.2L?c.7P:c.5w;7(c.64==V&&c.63!=V){L b=T.1F,1h=T.1h;c.64=c.63+(b&&b.2v||1h&&1h.2v||0)-(b.62||0);c.7N=c.7L+(b&&b.2x||1h&&1h.2x||0)-(b.60||0)}7(!c.3c&&((c.4f||c.4f===0)?c.4f:c.5Z))c.3c=c.4f||c.5Z;7(!c.7b&&c.5Y)c.7b=c.5Y;7(!c.3c&&c.2G)c.3c=(c.2G&1?1:(c.2G&2?3:(c.2G&4?2:0)));K c},2y:{21:{4j:J(){5M();K},4h:J(){K}},3C:{4j:J(){7(E.14.1d)K S;E(6).2j("4P",E.16.2y.3C.2q);K P},4h:J(){7(E.14.1d)K S;E(6).3w("4P",E.16.2y.3C.2q);K P},2q:J(a){7(I(a,6))K P;18[0].U="3C";K E.16.1v.1i(6,18)}},3B:{4j:J(){7(E.14.1d)K S;E(6).2j("4O",E.16.2y.3B.2q);K P},4h:J(){7(E.14.1d)K S;E(6).3w("4O",E.16.2y.3B.2q);K P},2q:J(a){7(I(a,6))K P;18[0].U="3B";K E.16.1v.1i(6,18)}}}};E.1n.1s({2j:J(c,a,b){K c=="4H"?6.2X(c,a,b):6.R(J(){E.16.1b(6,c,b||a,b&&a)})},2X:J(d,b,c){K 6.R(J(){E.16.1b(6,d,J(a){E(6).3w(a);K(c||b).1i(6,18)},c&&b)})},3w:J(a,b){K 6.R(J(){E.16.1V(6,a,b)})},1N:J(c,a,b){K 6.R(J(){E.16.1N(c,a,6,P,b)})},5n:J(c,a,b){7(6[0])K E.16.1N(c,a,6[0],S,b);K 10},2g:J(){L b=18;K 6.4V(J(a){6.4N=0==6.4N?1:0;a.36();K b[6.4N].1i(6,18)||S})},7D:J(a,b){K 6.2j(\'3C\',a).2j(\'3B\',b)},21:J(a){5M();7(E.2Q)a.1P(T,E);N E.3A.1g(J(){K a.1P(6,E)});K 6}});E.1s({2Q:S,3A:[],21:J(){7(!E.2Q){E.2Q=P;7(E.3A){E.R(E.3A,J(){6.1i(T)});E.3A=V}E(T).5n("21")}}});L x=S;J 5M(){7(x)K;x=P;7(T.3F&&!E.14.2z)T.3F("5W",E.21,S);7(E.14.1d&&1e==3b)(J(){7(E.2Q)K;1S{T.1F.7B("26")}1X(3a){3z(18.3R,0);K}E.21()})();7(E.14.2z)T.3F("5W",J(){7(E.2Q)K;Q(L i=0;i<T.4L.M;i++)7(T.4L[i].2Y){3z(18.3R,0);K}E.21()},S);7(E.14.2d){L a;(J(){7(E.2Q)K;7(T.39!="5V"&&T.39!="1y"){3z(18.3R,0);K}7(a===10)a=E("W, 7a[7A=7z]").M;7(T.4L.M!=a){3z(18.3R,0);K}E.21()})()}E.16.1b(1e,"3U",E.21)}E.R(("7y,7x,3U,7w,5d,4H,4V,7v,"+"7G,7u,7t,4P,4O,7s,2k,"+"58,7K,7q,7p,3a").23(","),J(i,b){E.1n[b]=J(a){K a?6.2j(b,a):6.1N(b)}});L I=J(a,c){L b=a.4S;2b(b&&b!=c)1S{b=b.1a}1X(3a){b=c}K b==c};E(1e).2j("4H",J(){E("*").1b(T).3w()});E.1n.1s({3U:J(g,d,c){7(E.1q(g))K 6.2j("3U",g);L e=g.1f(" ");7(e>=0){L i=g.2K(e,g.M);g=g.2K(0,e)}c=c||J(){};L f="4Q";7(d)7(E.1q(d)){c=d;d=V}N{d=E.3m(d);f="61"}L h=6;E.3P({1c:g,U:f,1H:"3q",O:d,1y:J(a,b){7(b=="1W"||b=="5U")h.3q(i?E("<1x/>").3t(a.4b.1r(/<1m(.|\\s)*?\\/1m>/g,"")).2s(i):a.4b);h.R(c,[a.4b,b,a])}});K 6},7n:J(){K E.3m(6.5T())},5T:J(){K 6.2c(J(){K E.12(6,"3u")?E.2I(6.7m):6}).1E(J(){K 6.31&&!6.2Y&&(6.3k||/2k|6h/i.17(6.12)||/1u|1Z|3I/i.17(6.U))}).2c(J(i,c){L b=E(6).5O();K b==V?V:b.1k==1M?E.2c(b,J(a,i){K{31:c.31,1A:a}}):{31:c.31,1A:b}}).22()}});E.R("5S,6d,5R,6D,5Q,6m".23(","),J(i,o){E.1n[o]=J(f){K 6.2j(o,f)}});L B=(1B 3v).3L();E.1s({22:J(d,b,a,c){7(E.1q(b)){a=b;b=V}K E.3P({U:"4Q",1c:d,O:b,1W:a,1H:c})},7l:J(b,a){K E.22(b,V,a,"1m")},7k:J(c,b,a){K E.22(c,b,a,"3i")},7i:J(d,b,a,c){7(E.1q(b)){a=b;b={}}K E.3P({U:"61",1c:d,O:b,1W:a,1H:c})},85:J(a){E.1s(E.4I,a)},4I:{2a:P,U:"4Q",2U:0,5P:"4o/x-7h-3u-7g",5N:P,3l:P,O:V,6p:V,3I:V,49:{3M:"4o/3M, 1u/3M",3q:"1u/3q",1m:"1u/4m, 4o/4m",3i:"4o/3i, 1u/4m",1u:"1u/a7",4G:"*/*"}},4F:{},3P:J(s){L f,2W=/=\\?(&|$)/g,1z,O;s=E.1s(P,s,E.1s(P,{},E.4I,s));7(s.O&&s.5N&&1o s.O!="25")s.O=E.3m(s.O);7(s.1H=="4E"){7(s.U.2h()=="22"){7(!s.1c.1D(2W))s.1c+=(s.1c.1D(/\\?/)?"&":"?")+(s.4E||"7d")+"=?"}N 7(!s.O||!s.O.1D(2W))s.O=(s.O?s.O+"&":"")+(s.4E||"7d")+"=?";s.1H="3i"}7(s.1H=="3i"&&(s.O&&s.O.1D(2W)||s.1c.1D(2W))){f="4E"+B++;7(s.O)s.O=(s.O+"").1r(2W,"="+f+"$1");s.1c=s.1c.1r(2W,"="+f+"$1");s.1H="1m";1e[f]=J(a){O=a;1W();1y();1e[f]=10;1S{2V 1e[f]}1X(e){}7(h)h.34(g)}}7(s.1H=="1m"&&s.1T==V)s.1T=S;7(s.1T===S&&s.U.2h()=="22"){L i=(1B 3v()).3L();L j=s.1c.1r(/(\\?|&)4r=.*?(&|$)/,"$a4="+i+"$2");s.1c=j+((j==s.1c)?(s.1c.1D(/\\?/)?"&":"?")+"4r="+i:"")}7(s.O&&s.U.2h()=="22"){s.1c+=(s.1c.1D(/\\?/)?"&":"?")+s.O;s.O=V}7(s.2a&&!E.5H++)E.16.1N("5S");7((!s.1c.1f("a3")||!s.1c.1f("//"))&&s.1H=="1m"&&s.U.2h()=="22"){L h=T.3S("6f")[0];L g=T.3s("1m");g.3Q=s.1c;7(s.7c)g.a2=s.7c;7(!f){L l=S;g.9Z=g.9Y=J(){7(!l&&(!6.39||6.39=="5V"||6.39=="1y")){l=P;1W();1y();h.34(g)}}}h.38(g);K 10}L m=S;L k=1e.78?1B 78("9X.9V"):1B 76();k.9T(s.U,s.1c,s.3l,s.6p,s.3I);1S{7(s.O)k.4C("9R-9Q",s.5P);7(s.5C)k.4C("9O-5A-9N",E.4F[s.1c]||"9L, 9K 9I 9H 5z:5z:5z 9F");k.4C("X-9C-9A","76");k.4C("9z",s.1H&&s.49[s.1H]?s.49[s.1H]+", */*":s.49.4G)}1X(e){}7(s.6Y)s.6Y(k);7(s.2a)E.16.1N("6m",[k,s]);L c=J(a){7(!m&&k&&(k.39==4||a=="2U")){m=P;7(d){6I(d);d=V}1z=a=="2U"&&"2U"||!E.6X(k)&&"3a"||s.5C&&E.6J(k,s.1c)&&"5U"||"1W";7(1z=="1W"){1S{O=E.6W(k,s.1H)}1X(e){1z="5x"}}7(1z=="1W"){L b;1S{b=k.5q("6U-5A")}1X(e){}7(s.5C&&b)E.4F[s.1c]=b;7(!f)1W()}N E.5v(s,k,1z);1y();7(s.3l)k=V}};7(s.3l){L d=53(c,13);7(s.2U>0)3z(J(){7(k){k.9t();7(!m)c("2U")}},s.2U)}1S{k.9s(s.O)}1X(e){E.5v(s,k,V,e)}7(!s.3l)c();J 1W(){7(s.1W)s.1W(O,1z);7(s.2a)E.16.1N("5Q",[k,s])}J 1y(){7(s.1y)s.1y(k,1z);7(s.2a)E.16.1N("5R",[k,s]);7(s.2a&&!--E.5H)E.16.1N("6d")}K k},5v:J(s,a,b,e){7(s.3a)s.3a(a,b,e);7(s.2a)E.16.1N("6D",[a,s,e])},5H:0,6X:J(r){1S{K!r.1z&&9q.9p=="59:"||(r.1z>=6T&&r.1z<9n)||r.1z==6R||r.1z==9l||E.14.2d&&r.1z==10}1X(e){}K S},6J:J(a,c){1S{L b=a.5q("6U-5A");K a.1z==6R||b==E.4F[c]||E.14.2d&&a.1z==10}1X(e){}K S},6W:J(r,b){L c=r.5q("9k-U");L d=b=="3M"||!b&&c&&c.1f("3M")>=0;L a=d?r.9j:r.4b;7(d&&a.1F.28=="5x")6Q"5x";7(b=="1m")E.5g(a);7(b=="3i")a=6c("("+a+")");K a},3m:J(a){L s=[];7(a.1k==1M||a.5h)E.R(a,J(){s.1g(3r(6.31)+"="+3r(6.1A))});N Q(L j 1p a)7(a[j]&&a[j].1k==1M)E.R(a[j],J(){s.1g(3r(j)+"="+3r(6))});N s.1g(3r(j)+"="+3r(a[j]));K s.6a("&").1r(/%20/g,"+")}});E.1n.1s({1G:J(c,b){K c?6.2e({1R:"1G",27:"1G",1w:"1G"},c,b):6.1E(":1Z").R(J(){6.W.19=6.5s||"";7(E.1j(6,"19")=="2H"){L a=E("<"+6.28+" />").6y("1h");6.W.19=a.1j("19");7(6.W.19=="2H")6.W.19="3D";a.1V()}}).3h()},1I:J(b,a){K b?6.2e({1R:"1I",27:"1I",1w:"1I"},b,a):6.1E(":4d").R(J(){6.5s=6.5s||E.1j(6,"19");6.W.19="2H"}).3h()},6N:E.1n.2g,2g:J(a,b){K E.1q(a)&&E.1q(b)?6.6N(a,b):a?6.2e({1R:"2g",27:"2g",1w:"2g"},a,b):6.R(J(){E(6)[E(6).3H(":1Z")?"1G":"1I"]()})},9f:J(b,a){K 6.2e({1R:"1G"},b,a)},9d:J(b,a){K 6.2e({1R:"1I"},b,a)},9c:J(b,a){K 6.2e({1R:"2g"},b,a)},9a:J(b,a){K 6.2e({1w:"1G"},b,a)},99:J(b,a){K 6.2e({1w:"1I"},b,a)},97:J(c,a,b){K 6.2e({1w:a},c,b)},2e:J(l,k,j,h){L i=E.6P(k,j,h);K 6[i.2P===S?"R":"2P"](J(){7(6.15!=1)K S;L g=E.1s({},i);L f=E(6).3H(":1Z"),4A=6;Q(L p 1p l){7(l[p]=="1I"&&f||l[p]=="1G"&&!f)K E.1q(g.1y)&&g.1y.1i(6);7(p=="1R"||p=="27"){g.19=E.1j(6,"19");g.32=6.W.32}}7(g.32!=V)6.W.32="1Z";g.40=E.1s({},l);E.R(l,J(c,a){L e=1B E.2t(4A,g,c);7(/2g|1G|1I/.17(a))e[a=="2g"?f?"1G":"1I":a](l);N{L b=a.3X().1D(/^([+-]=)?([\\d+-.]+)(.*)$/),1Y=e.2m(P)||0;7(b){L d=2M(b[2]),2A=b[3]||"2S";7(2A!="2S"){4A.W[c]=(d||1)+2A;1Y=((d||1)/e.2m(P))*1Y;4A.W[c]=1Y+2A}7(b[1])d=((b[1]=="-="?-1:1)*d)+1Y;e.45(1Y,d,2A)}N e.45(1Y,a,"")}});K P})},2P:J(a,b){7(E.1q(a)||(a&&a.1k==1M)){b=a;a="2t"}7(!a||(1o a=="25"&&!b))K A(6[0],a);K 6.R(J(){7(b.1k==1M)A(6,a,b);N{A(6,a).1g(b);7(A(6,a).M==1)b.1i(6)}})},94:J(b,c){L a=E.3G;7(b)6.2P([]);6.R(J(){Q(L i=a.M-1;i>=0;i--)7(a[i].Y==6){7(c)a[i](P);a.72(i,1)}});7(!c)6.5p();K 6}});L A=J(b,c,a){7(!b)K 10;c=c||"2t";L q=E.O(b,c+"2P");7(!q||a)q=E.O(b,c+"2P",a?E.2I(a):[]);K q};E.1n.5p=J(a){a=a||"2t";K 6.R(J(){L q=A(6,a);q.4l();7(q.M)q[0].1i(6)})};E.1s({6P:J(b,a,c){L d=b&&b.1k==92?b:{1y:c||!c&&a||E.1q(b)&&b,2u:b,3Z:c&&a||a&&a.1k!=91&&a};d.2u=(d.2u&&d.2u.1k==51?d.2u:{90:8Z,9D:6T}[d.2u])||8X;d.5y=d.1y;d.1y=J(){7(d.2P!==S)E(6).5p();7(E.1q(d.5y))d.5y.1i(6)};K d},3Z:{70:J(p,n,b,a){K b+a*p},5j:J(p,n,b,a){K((-24.8V(p*24.8U)/2)+0.5)*a+b}},3G:[],3W:V,2t:J(b,c,a){6.11=c;6.Y=b;6.1l=a;7(!c.47)c.47={}}});E.2t.2l={4y:J(){7(6.11.30)6.11.30.1i(6.Y,[6.2J,6]);(E.2t.30[6.1l]||E.2t.30.4G)(6);7(6.1l=="1R"||6.1l=="27")6.Y.W.19="3D"},2m:J(a){7(6.Y[6.1l]!=V&&6.Y.W[6.1l]==V)K 6.Y[6.1l];L r=2M(E.1j(6.Y,6.1l,a));K r&&r>-8Q?r:2M(E.2o(6.Y,6.1l))||0},45:J(c,b,d){6.5B=(1B 3v()).3L();6.1Y=c;6.3h=b;6.2A=d||6.2A||"2S";6.2J=6.1Y;6.4B=6.4w=0;6.4y();L e=6;J t(a){K e.30(a)}t.Y=6.Y;E.3G.1g(t);7(E.3W==V){E.3W=53(J(){L a=E.3G;Q(L i=0;i<a.M;i++)7(!a[i]())a.72(i--,1);7(!a.M){6I(E.3W);E.3W=V}},13)}},1G:J(){6.11.47[6.1l]=E.1J(6.Y.W,6.1l);6.11.1G=P;6.45(0,6.2m());7(6.1l=="27"||6.1l=="1R")6.Y.W[6.1l]="8N";E(6.Y).1G()},1I:J(){6.11.47[6.1l]=E.1J(6.Y.W,6.1l);6.11.1I=P;6.45(6.2m(),0)},30:J(a){L t=(1B 3v()).3L();7(a||t>6.11.2u+6.5B){6.2J=6.3h;6.4B=6.4w=1;6.4y();6.11.40[6.1l]=P;L b=P;Q(L i 1p 6.11.40)7(6.11.40[i]!==P)b=S;7(b){7(6.11.19!=V){6.Y.W.32=6.11.32;6.Y.W.19=6.11.19;7(E.1j(6.Y,"19")=="2H")6.Y.W.19="3D"}7(6.11.1I)6.Y.W.19="2H";7(6.11.1I||6.11.1G)Q(L p 1p 6.11.40)E.1J(6.Y.W,p,6.11.47[p])}7(b&&E.1q(6.11.1y))6.11.1y.1i(6.Y);K S}N{L n=t-6.5B;6.4w=n/6.11.2u;6.4B=E.3Z[6.11.3Z||(E.3Z.5j?"5j":"70")](6.4w,n,0,1,6.11.2u);6.2J=6.1Y+((6.3h-6.1Y)*6.4B);6.4y()}K P}};E.2t.30={2v:J(a){a.Y.2v=a.2J},2x:J(a){a.Y.2x=a.2J},1w:J(a){E.1J(a.Y.W,"1w",a.2J)},4G:J(a){a.Y.W[a.1l]=a.2J+a.2A}};E.1n.5L=J(){L b=0,3b=0,Y=6[0],5l;7(Y)8M(E.14){L d=Y.1a,41=Y,1K=Y.1K,1L=Y.2i,5D=2d&&4s(5K)<8J&&!/a1/i.17(v),2T=E.1j(Y,"43")=="2T";7(Y.6G){L c=Y.6G();1b(c.26+24.2f(1L.1F.2v,1L.1h.2v),c.3b+24.2f(1L.1F.2x,1L.1h.2x));1b(-1L.1F.62,-1L.1F.60)}N{1b(Y.5G,Y.5F);2b(1K){1b(1K.5G,1K.5F);7(48&&!/^t(8H|d|h)$/i.17(1K.28)||2d&&!5D)2N(1K);7(!2T&&E.1j(1K,"43")=="2T")2T=P;41=/^1h$/i.17(1K.28)?41:1K;1K=1K.1K}2b(d&&d.28&&!/^1h|3q$/i.17(d.28)){7(!/^8G|1O.*$/i.17(E.1j(d,"19")))1b(-d.2v,-d.2x);7(48&&E.1j(d,"32")!="4d")2N(d);d=d.1a}7((5D&&(2T||E.1j(41,"43")=="4W"))||(48&&E.1j(41,"43")!="4W"))1b(-1L.1h.5G,-1L.1h.5F);7(2T)1b(24.2f(1L.1F.2v,1L.1h.2v),24.2f(1L.1F.2x,1L.1h.2x))}5l={3b:3b,26:b}}J 2N(a){1b(E.2o(a,"a8",P),E.2o(a,"a9",P))}J 1b(l,t){b+=4s(l)||0;3b+=4s(t)||0}K 5l}})();',62,631,'||||||this|if||||||||||||||||||||||||||||||||||||||function|return|var|length|else|data|true|for|each|false|document|type|null|style||elem||undefined|options|nodeName||browser|nodeType|event|test|arguments|display|parentNode|add|url|msie|window|indexOf|push|body|apply|css|constructor|prop|script|fn|typeof|in|isFunction|replace|extend|className|text|handle|opacity|div|complete|status|value|new|firstChild|match|filter|documentElement|show|dataType|hide|attr|offsetParent|doc|Array|trigger|table|call|break|height|try|cache|tbody|remove|success|catch|start|hidden||ready|get|split|Math|string|left|width|tagName|ret|global|while|map|safari|animate|max|toggle|toLowerCase|ownerDocument|bind|select|prototype|cur||curCSS|selected|handler|done|find|fx|duration|scrollLeft|id|scrollTop|special|opera|unit|nextSibling|stack|guid|toUpperCase|pushStack|button|none|makeArray|now|slice|target|parseFloat|border|exec|queue|isReady|events|px|fixed|timeout|delete|jsre|one|disabled|nth|step|name|overflow|inArray|removeChild|removeData|preventDefault|merge|appendChild|readyState|error|top|which|innerHTML|multiFilter|rl|trim|end|json|first|checked|async|param|elems|insertBefore|childNodes|html|encodeURIComponent|createElement|append|form|Date|unbind|color|grep|setTimeout|readyList|mouseleave|mouseenter|block|isXMLDoc|addEventListener|timers|is|password|last|runtimeStyle|getTime|xml|jQuery|domManip|ajax|src|callee|getElementsByTagName|selectedIndex|load|object|timerId|toString|has|easing|curAnim|offsetChild|args|position|stopPropagation|custom|props|orig|mozilla|accepts|clean|responseText|defaultView|visible|String|charCode|float|teardown|on|setup|nodeIndex|shift|javascript|currentStyle|application|child|RegExp|_|parseInt|previousSibling|dir|tr|state|empty|update|getAttribute|self|pos|setRequestHeader|input|jsonp|lastModified|_default|unload|ajaxSettings|unshift|getComputedStyle|styleSheets|getPropertyValue|lastToggle|mouseout|mouseover|GET|andSelf|relatedTarget|init|visibility|click|absolute|index|container|fix|outline|Number|removeAttribute|setInterval|prevObject|classFilter|not|unique|submit|file|after|windowData|deep|scroll|client|triggered|globalEval|jquery|sibling|swing|clone|results|wrapAll|triggerHandler|lastChild|dequeue|getResponseHeader|createTextNode|oldblock|checkbox|radio|handleError|fromElement|parsererror|old|00|Modified|startTime|ifModified|safari2|getWH|offsetTop|offsetLeft|active|values|getElementById|version|offset|bindReady|processData|val|contentType|ajaxSuccess|ajaxComplete|ajaxStart|serializeArray|notmodified|loaded|DOMContentLoaded|Width|ctrlKey|keyCode|clientTop|POST|clientLeft|clientX|pageX|exclusive|detachEvent|removeEventListener|swap|cloneNode|join|attachEvent|eval|ajaxStop|substr|head|parse|textarea|reset|image|zoom|odd|ajaxSend|even|before|username|prepend|expr|quickClass|uuid|quickID|quickChild|continue|textContent|appendTo|contents|evalScript|parent|defaultValue|ajaxError|setArray|compatMode|getBoundingClientRect|styleFloat|clearInterval|httpNotModified|nodeValue|100|alpha|_toggle|href|speed|throw|304|replaceWith|200|Last|colgroup|httpData|httpSuccess|beforeSend|eq|linear|concat|splice|fieldset|multiple|cssFloat|XMLHttpRequest|webkit|ActiveXObject|CSS1Compat|link|metaKey|scriptCharset|callback|col|pixelLeft|urlencoded|www|post|hasClass|getJSON|getScript|elements|serialize|black|keyup|keypress|solid|change|mousemove|mouseup|dblclick|resize|focus|blur|stylesheet|rel|doScroll|round|hover|padding|offsetHeight|mousedown|offsetWidth|Bottom|Top|keydown|clientY|Right|pageY|Left|toElement|srcElement|cancelBubble|returnValue|charAt|0n|substring|animated|header|noConflict|line|enabled|innerText|contains|only|weight|ajaxSetup|font|size|gt|lt|uFFFF|u0128|417|Boolean|inner|Height|toggleClass|removeClass|addClass|removeAttr|replaceAll|insertAfter|prependTo|contentWindow|contentDocument|wrap|iframe|children|siblings|prevAll|nextAll|prev|wrapInner|next|parents|maxLength|maxlength|readOnly|readonly|reverse|class|htmlFor|inline|able|boxModel|522|setData|compatible|with|1px|ie|getData|10000|ra|it|rv|PI|cos|userAgent|400|navigator|600|slow|Function|Object|array|stop|ig|NaN|fadeTo|option|fadeOut|fadeIn|setAttribute|slideToggle|slideUp|changed|slideDown|be|can|property|responseXML|content|1223|getAttributeNode|300|method|protocol|location|action|send|abort|cssText|th|td|cap|specified|Accept|With|colg|Requested|fast|tfoot|GMT|thead|1970|Jan|attributes|01|Thu|leg|Since|If|opt|Type|Content|embed|open|area|XMLHTTP|hr|Microsoft|onreadystatechange|onload|meta|adobeair|charset|http|1_|img|br|plain|borderLeftWidth|borderTopWidth|abbr'.split('|'),0,{}))
View
34 content/news.rss
@@ -0,0 +1,34 @@
+---
+extension: rss
+layout: nil
+dirty: true
+filter:
+- erb
+---
+<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom">
+
+ <title>Datamapper.org News and Notes</title>
+ <subtitle>Ruby Object Relational Mapper</subtitle>
+ <link href="http://www.datamapper.org/news.rss" rel="self"/>
+ <link href="http://www.datamapper.org/"/>
+ <updated><%= Time.now.xmlschema %></updated>
+ <author>
+ <name>Maintained By the Community</name>
+ <email>datamapper@googlegroups.com</email>
+ </author>
+ <id>http://www.datamapper.org</id>
+ <% @pages.find(:limit => 10,
+ :in_directory => 'articles',
+ :recursive => true,
+ :sort_by => 'created_at',
+ :reverse => true).each do |article| %>
+ <entry>
+ <title><%= h(article.title) %></title>
+ <link href="<%= article.url %>"/>
+ <id>tag:datamapper.org,<%= article.created_at.strftime('%Y-%m-%d') %>:<%= article.created_at.to_i %></id>
+ <updated><%= article.created_at.xmlschema %></updated>
+ <content type="html"><%= h(article.render) %></content>
+ </entry>
+ <% end %>
+</feed>
View
28 content/news.txt
@@ -0,0 +1,28 @@
+---
+body_id: news
+title: DataMapper News and Notes
+filter:
+ - erb
+ - textile
+---
+
+h1. <%= @page.title %>
+
+<dl>
+<%
+ articles = @pages.find(:limit => :all, :in_directory => 'articles') {|a| a.release_type != 'draft'}.sort{|a, b| b.created_at <=> a.created_at}
+
+ paginate(articles, 20) do |page|
+%>
+
+ <dt>
+ <a href="<%= page.url %>"> <%= page.title %></a>
+ <br>
+ <small><%= page.created_at.strftime(" Posted On %m/%d/%Y") %> by <%= page.author %></small>
+ </dt>
+ <dd><%= page.summary %></dd>
+
+<% end %>
+</dl>
+
+<p><%= link_to("Prev", @pager.prev) if @pager.prev? %> <%= link_to("Next", @pager.next) if @pager.next? %></p>
View
153 content/using-git.rhtml
@@ -0,0 +1,153 @@
+---
+title: Using Git
+created_at: Mon Feb 18 16:18:12 -0600 2008
+filter:
+ - erb
+---
+<h2><%= @page.title %></h2>
+<p>The DataMapper project uses the Git <span class="caps">SCM</span>. Committers need to use git to commit their code directly to the main repository.</p>
+
+<p>This page contains information on getting Git installed, getting source code with Git, and steps for working with Git.</p>
+
+<p>Also, see these references: <a href="http://git.or.cz/course/svn.html">Git &#8211; <span class="caps">SVN</span> Crash Course</a> and <a href="http://www.kernel.org/pub/software/scm/git/docs/everyday.html">Everyday <span class="caps">GIT</span> With 20 Commands Or So</a></p>
+
+<h2>Getting Git for Your System</h2>
+
+<p>You can use an earlier version, but 1.5.x is definitely recommended.</p>
+
+<ul>
+ <li>MacPorts has &#8216;git-core&#8217;</li>
+ <li>Debian has &#8216;git-core&#8217; (If you&#8217;re using Etch, you can get a recent Git version from Backports <a href="http://backports.org/dokuwiki/doku.php?id=instructions">http://backports.org/dokuwiki/do&#8230;</a>)</li>
+ <li>Get the source at <a href="http://git.or.cz/">http://git.or.cz/</a></li>
+</ul>
+
+<h2>Setup</h2>
+
+<p>Configure Git with your proper name and email. This will display when you submit changes to the DataMapper repository.</p>
+
+<div class="CodeRay"><pre><code>git config --global user.name "My Name"
+git config --global user.email "my@email"</code></pre>
+</div>
+<p>If you prefer to use different credentials for different projects, you can also configure the above for a single repository only. See the git documentation.</p>
+
+<h2>Formatting Git Commit Messages</h2>
+
+<p>In general, use an editor to create your commit messages rather than passing them on the command line. The format should be:</p>
+
+<ul>
+<li>A hard wrap at 72 characters</li>
+ <li>A single, short, summary of the commit</li>
+ <li>Followed by a single blank line</li>
+ <li>Followed by supporting details</li>
+</ul>
+
+<p>The supporting details could be a bulleted enumeration or an explanatory paragraph. The single summary line helps folks reviewing commits. An example commit:</p>
+
+<% coderay(:lang => "bash") do -%>
+Fixes for Module#make_my_day return values.
+
+* Return nil when passed ':('
+* Return true when passed ':)'
+* Updated specs for #make_my_day for nil argument case
+* Updated CI excludes.
+<% end %>
+
+<h2>Getting the Code</h2>
+
+<p>DataMapper is hosted at <a href="http://github.com">GitHub</a>. Getting the code is easy once you have git installed but is slightly different depending on your access. In both cases that exact command will put the repository in a local directory called dm. You can give it a different name just by appending it to the command.</p>
+
+<h3>New Users and Developers</h3>
+
+<% coderay(:lang => "bash", :line_numbers => "inline") do -%>
+git clone git://github.com/sam/dm.git
+<% end %>
+
+<h3>Committers with Commit Bit</h3>
+
+<% coderay(:lang => "bash", :line_numbers => "inline") do -%>
+git clone git@github.com/sam/dm.git
+<% end %>
+
+
+<h2>Commit Rights</h2>
+
+<p>If you are a committer, either give Sam your github account name or paste your ssh public key (~/.ssh/id_rsa.pub) into one of the paste sites and give Sam the <span class="caps">URL</span>.</p>
+
+<h2>Git Workflow</h2>
+
+<p>Working with Git is significantly different than working with <span class="caps">SVN</span>. In particular, although similar, git pull is not svn update, git push is not svn commit, and git add is not svn add. If you are a <span class="caps">SVN</span> user, be sure to read the man pages for the different git commands.</p>
+
+<p>The following workflow is recommended by Rein and is the guideline for contributing code to DataMapper.</p>
+
+<ol>
+<li><p>Create a local working copy of the source code (we did this earlier.)</p>
+<div class="CodeRay"><pre></code># See above for the exact invocation</code></pre></div>
+</li>
+
+<li><p>Change to the newly created directory that contains the local working copy. (Substitute the directory if you created it with a different name, obviously.)</p>
+<div class="CodeRay"><code><pre>cd dm</pre></code></div>
+</li>
+
+<li><p>Create a branch for your work. It is important that you do your work in a local branch, rather than master.</p>
+<div class="CodeRay"><code><pre>git checkout -b new_feature</pre></code></div>
+</li>
+
+<li><p>Edit the code and test your changes. Then commit to your local working copy.</p>
+<div class="CodeRay"><code><pre>git add .
+git commit</pre></code></div>
+</li>
+
+<li><p>When you are ready to send your local changes back to the DataMapper repository, you first need to ensure that your local copy is up-to-date. First, ensure you have committed your local changes. Then switch from your topic branch to the master branch.</p>
+<div class="CodeRay"><code><pre>git checkout master</pre></code></div>
+</li>
+
+<li><p>Update your local copy with changes from the DataMapper repository</p>
+<div class="CodeRay"><code><pre>git pull origin master</pre></code></div>
+</li>
+
+<li>
+<p>Switch back to your topic branch and integrate any new changes. The git rebase command will save your changes away, update the topic branch, and then reapply them.</p>
+<div class="CodeRay"><code><pre>git checkout new_feature
+git rebase master</pre></code></div>
+<p>Warning! If you have shared the topic branch publicly, you must use</p>
+
+<div class="CodeRay"><code><pre>git merge master</pre></code></div>
+<p>Rebase causes the commit layout to change and will confuse anyone you&#8217;ve shared this branch with.</p>
+</li>
+
+<li><p>If there are conflicts applying your changes during the git rebase command, fix them and use the following to finish applying them</p>
+<div class="CodeRay"><code><pre>git rebase --continue</pre></code></div>
+</li>
+
+<li><p>Now, switch back to the master branch and merge your changes from the topic branch</p>
+<div class="CodeRay"><code><pre>git checkout master
+git merge new_feature</pre></code></div>
+</li>
+
+<li><p>You might want to check that your commits ended up as you intended. To do so, you can have a look at the log</p>
+<div class="CodeRay"><code><pre>git log</pre></code></div>
+</li>
+
+<li><p>Get your changes in the main repository. If you have commit rights, you can just use the git push command. Otherwise, see the section below for information on creating a set of patches to send.</p>
+<div class="CodeRay"><code><pre>git push origin master</pre></code></div>
+</li>
+
+<li><p>At this point, you can delete the branch if you like.</p>
+<div class="CodeRay"><code><pre>git branch -d new_feature</pre></code></div>
+</li>
+
+</ol>
+
+<h2>Patches: git-format-patch</h2>
+
+<p>If you are a new committer (or want to create a patch instead of directly pushing the code for some other reason) you should create a patch file for your commits. The patch file should be then attached to a ticket on Lighthouse. You can also send the patch to the mailing list but please use the ticket tracker if at all possible. Either way, the patch file(s) should be created using Git.</p>
+
+<p>First, make your changes as detailed below and then use the git format-patch command to create the patch files. Usually using the command is as simple as specifying the commits you want to create patches for, and that is done in one of two ways: by giving a range of commits or a starting point.</p>
+
+<p>For our purposes, the simplest way to create a patch is to begin at the end of step 8 above (after you have rebased your branch) and then, instead of merging:</p>
+
+<div class="CodeRay"><pre><code>git format-patch master..</code></pre></div>
+
+<p>This will create a separate patch file for each commit in your working branch that is not in master, named [number]-[first line of commit message].patch. You can then attach these to a ticket (or e-mail them).</p>
+
+<p>You can also inspect your changes using <code>git log master..</code> or <code>git diff master..</code> to ensure that the patches will be generated correctly if you are uncertain.</p>
View
115 content/why.txt
@@ -0,0 +1,115 @@
+---
+body_id: why
+title: Why DataMapper?
+filter:
+ - erb
+ - textile
+---
+
+h1. Why DataMapper?
+
+You might be wondering "Why Yet Another ORM?"
+
+h2. 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.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+@parent = Tree.find(:first, :conditions => ['name = ?', 'bob'])
+
+@parent.children.each do |child|
+ puts @parent.object_id == child.parent.object_id
+end
+<% end %>
+
+This makes DataMapper faster and allocate less resources to get things done.
+
+h2. Plays Well With Others
+
+In ActiveRecord, all your columns are mapped, whether you want them or not. This slows things down. In the DataMapper you define your mappings in your model. So instead of an @ALTER TABLE ADD COLUMN@ in your Database, you simply add a @property :name, :string@ to your model. DRY. No schema.rb. No migration files to conflict or die without reverting changes. Your model drives the database, not the other way around.
+
+Unless of course you want to map to a legacy database. Raise your hand if you like seeing a method called @col2Name@ on your model just because that's what it's called in an old database you can't afford to change right now? In DataMapper you control the mappings:
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Fruit < DataMapper::Base
+ set_table_name 'frt'
+ property :name, :string, :column => 'col2'
+end
+<% end %>
+
+Additionally, ActiveRecord updates every column in a row during a save whether that column changed or not. So it performs work it doesn't really need to making it much slower, and more likely to eat data during concurrent access if you don't go around adding locking support to everything.
+
+DataMapper only does what it needs to. 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.
+
+h2. Laziness Can Be A Virtue
+
+Text columns are expensive in databases. They're generally stored in a different place than the rest of your data. So instead of a fast sequential read from your hard-drive, your database server has to hop around all over the place to get what it needs. Since ActiveRecord returns everything by default, adding a text column to a table slows everything down drastically, across the board.
+
+Not so with the DataMapper. Text fields are treated like in-row associations by default, meaning they only load when you need 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@.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+class Animal < DataMapper::Base
+ property :name, :string
+ property :notes, :text, :lazy => false
+end
+<% end %>
+
+Plus, lazy-loading of text fields 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:
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+animals = Animal.all
+animals.each do |pet|
+ pet.notes
+end
+<% end %>
+
+h2. Strategic Eager Loading
+
+Ready for something amazing? The following example executes only two queries.
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+zoos = Zoo.all
+first = zoos.first
+first.exhibits # Loads the exhibits for all the Zoo objects in the zoos variable.
+<% end %>
+
+Pretty impressive huh? 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 the DataMapper thinks ahead. We like to call it "performant by default". This feature single-handedly wipes out the "N+1 Query Problem". No need to specify an @include@ option in your finders.
+
+h2. All Ruby, All The Time
+
+It's great that ActiveRecord allows you to write SQL when you need to, but should we have to so often?
+
+DataMapper supports issuing your own SQL, but it also provides more helpers and a unique hash-based condition 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's 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'])@. What if you need other comparisons though? Try these:
+
+<% coderay(:lang => "ruby", :line_numbers => "inline") do -%>
+Zoo.first(:name => 'Galveston')
+
+# 'gt' means greater-than. We also do 'lt'.
+Person.all(:age.gt => 30)
+
+# 'gte' means greather-than-or-equal-to. We also do 'lte'.
+Person.all(:age.gte => 30)
+
+Person.all(:name.not => 'bob')
+
+# If the value of a pair is an Array, we do an IN-clause for you.
+Person.all(:name.like => 'S%', :id => [1, 2, 3, 4, 5])
+
+# An alias for Zoo.get(11)
+Zoo[11]
+
+# Does a NOT IN () clause for you.
+Person.all(:name.not => ['bob','rick','steve'])
+<% end %>
+
+See? Fewer SQL fragments dirtying your Ruby code. And that's just a few of the nice syntax tweaks DataMapper delivers out of the box...
+
+h2. Better Is Great, But Familiar Is Nice
+
+The DataMapper also supports a lot of old-fashioned ActiveRecord syntax. We want to make it easy for you to get started, so aside from mapping your columns and changing the base-class your models inherit from, much of AR syntax for finders are supported as well, making your transition easy.
+
+h2. Open Development
+
+DataMapper sports a very accessible code-base and a welcoming community. Outside contributions and feedback are welcome and encouraged, especially constructive criticism. Make your voice heard! "Submit a ticket or patch":http://wm.lighthouseapp.com/projects/4819-datamapper/overview, speak up on our "mailing-list":http://groups.google.com/group/datamapper/, chat with us on "irc":irc://irc.freenode.net/#datamapper, write a spec, get it reviewed, ask for commit rights. It's as easy as that to become a contributor.
View
BIN image_sources/logo_large.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN image_sources/martini.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
55 layouts/default.rhtml
@@ -0,0 +1,55 @@
+---
+extension: html
+filter: erb
+---
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us">
+
+ <head>
+ <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
+ <title>DataMapper - <%= @page.title %></title>
+ <link rel="stylesheet" href="/css/site.css" type="text/css" media="screen, projection" />
+ <link rel="stylesheet" href="/css/coderay.css" type="text/css" media="screen, projection" />
+ <!--[if lt IE 7]> <link rel="stylesheet" href="css/ie_hacks.css" type="text/css" media="screen, projection" /> <![endif]-->
+ <script src="/js/jquery-1.2.3.pack.js" type="text/javascript" charset="utf-8"></script>
+ <link rel="alternate" type="application/rss+xml"
+ title="News and Notes feed"
+ href="/news.rss" />
+ </head>
+
+ <body id="<%= @page.body_id %>">
+ <div id="content">
+ <div id="header">
+ <h1><a href="/">DataMapper</a></h1>
+
+ <ul>
+ <li><a href="/why.html">Why DataMapper?</a></li>
+ <li><a href="/community.html">Community</a></li>
+ <li><a href="/docs">Docs &amp; Dev</a></li>
+ <li><a href="</