• This repository has been archived on 18/Apr/2018
  • Stars
    star
    755
  • Rank 60,102 (Top 2 %)
  • Language
    Ruby
  • License
    MIT License
  • Created almost 16 years ago
  • Updated over 8 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

DataMapper - Core

Why DataMapper?

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 an issue, speak up on our mailing-list, chat with us on irc, write a spec, get it reviewed, ask for commit rights. It's as easy as that to become a contributor.

Identity Map

One row in the data-store 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.

  repository do
    @parent = Tree.first(:name => 'bob')

    @parent.children.each do |child|
      puts @parent.equal?(child.parent)  # => true
    end
  end

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

Dirty Tracking

When you save a model back to your data-store, DataMapper will only write the fields that actually changed. So it plays well with others. You can use it in an Integration data-store without worrying that your application will be a bad actor causing trouble for all of your other processes.

Eager Loading

Ready for something amazing? The following example executes only two queries regardless of how many rows the inner and outer queries return.

  repository do
    Zoo.all.each { |zoo| zoo.exhibits.to_a }
  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.

Laziness Can Be A Virtue

Text fields are expensive in data-stores. 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 data-store server has to hop around all over the place to get what it needs. Since ActiveRecord returns everything by default, adding a text field to a table slows everything down drastically, across the board.

Not so with the DataMapper. Text fields are lazily loaded, meaning they only load when you need them. If you want more control you can enable or disable this feature for any field (not just text-fields) by passing a :lazy option to your field mapping with a value of true or false.

  class Animal
    include DataMapper::Resource

    property :name,        String
    property :description, Text, :lazy => false
  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:

  repository do
    Animal.all.each { |animal| animal.description.to_a }
  end

Did you notice the #to_a call in the above example? That was necessary because even DataMapper collections are lazy. If you don't iterate over them, or in this case ask them to become Arrays, they won't execute until you need them. We needed to call #to_a to force the lazy load because without it, the above example would have only executed one query. This extra bit of laziness can come in very handy, for example:

  animals     = Animal.all
  description = 'foo'

  animals.each do |animal|
    animal.update(:description => description)
  end

In the above example, the Animals won't be retrieved until you actually need them. This comes in handy in cases where you initialize the collection before you know if you need it, like in a web app controller.

Collection Chaining

DataMapper's lazy collections are also handy because you can get the same effect as named scopes, without any special syntax, eg:

  class Animal
    # ... setup ...

    def self.mammals
      all(:mammal => true)
    end

    def self.zoo(zoo)
      all(:zoo => zoo)
    end
  end

  zoo = Zoo.first(:name => 'Greater Vancouver Zoo')

  Animal.mammals.zoo(zoo).to_a  # => executes one query

In the above example, we ask the Animal model for all the mammals, and then all the animals in a specific zoo, and DataMapper will chain the collection queries together and execute a single query to retrieve the matching records. There's no special syntax, and no custom DSLs to learn, it's just plain ruby all the way down.

You can even use this on association collections, eg:

  zoo.animals.mammals.to_a  # => executes one query

Custom Properties

With DataMapper it is possible to create custom properties for your models. Consider this example:

  module DataMapper
    class Property
      class Email < String
        required true
        format   /^([\w\.%\+\-]+)@([\w\-]+\.)+([\w]{2,})$/i
      end
    end
  end

  class User
    include DataMapper::Resource

    property :id,    Serial
    property :email, Email
  end

This way there won't be a need to repeat same property options every time you add an email to a model. In the example above we create an Email property which is just a String with additional pre-configured options: required and format. Please note that it is possible to override these options when declaring a property, like this:

  class Member
    include DataMapper::Resource

    property :id,    Serial
    property :email, Email, :required => false
  end

Plays Well With Others

In ActiveRecord, all your fields 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 field in your data-store, 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 data-store, not the other way around.

Unless of course you want to map to a legacy data-store. 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 data-store you can't afford to change right now? In DataMapper you control the mappings:

  class Fruit
    include DataMapper::Resource

    storage_names[:repo] = 'frt'

    property :name, String, :field => 'col2Name'
  end

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 query, 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:

  # '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)

  # 'not' allows you to match all people without the name "bob"
  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 ])

  # Does a NOT IN () clause for you.
  Person.all(:name.not => [ 'bob', 'rick', 'steve' ])

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...

Note on Patches/Pull Requests

  • Fork the project.
  • Make your feature addition or bug fix.
  • Add tests for it. This is important so I don't break it in a future version unintentionally.
  • Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
  • Send me a pull request. Bonus points for topic branches.

Copyright

Copyright (c) 2012 Dan Kubb. See LICENSE for details.

More Repositories

1

dm-rails

Integrate DataMapper with Rails 3
Ruby
175
star
2

do

DataObjects
Ruby
147
star
3

dm-more

Deprecated Extras for DataMapper (See ReadMe) including bridges to DataObjects::Migrations and Merb::DataMapper
Ruby
143
star
4

extlib

General Ruby extensions for Merb
Ruby
70
star
5

dm-migrations

DataMapper plugin for writing and speccing migrations
Ruby
66
star
6

dm-types

DataMapper plugin providing extra data types
Ruby
55
star
7

dm-validations

Library for performing validations on DM models and pure Ruby object
Ruby
50
star
8

datamapper.github.com

The DataMapper Website (was sam/dm-www)
CSS
48
star
9

dm-serializer

DataMapper plugin for serializing Resources and Collections
Ruby
40
star
10

dm-rest-adapter

REST Adapter for DataMapper
Ruby
40
star
11

dm-sweatshop

DataMapper plugin for building pseudo random models
Ruby
35
star
12

data_mapper

DataMapper meta-gem distributing the most common gems
Ruby
33
star
13

dm-is-versioned

DataMapper plugin enabling simple versioning of models
Ruby
32
star
14

dm-do-adapter

A Data Objects Adapter for DataMapper
Ruby
23
star
15

dm-transactions

Adds support for transaction to datamapper
Ruby
22
star
16

dm-is-tree

DataMapper plugin allowing the creation of tree structures from data models
Ruby
21
star
17

dm-constraints

DataMapper plugin constraining relationships
Ruby
21
star
18

dm-tags

This package brings tagging to DataMapper. It is inspired by Acts As Taggable On by Michael Bleigh, github's mbleigh. Props to him for the contextual tagging based on Acts As Taggable on Steroids.
Ruby
20
star
19

dm-active_model

A plugin to make datamapper active_model compliant and thus compatible with rails3
Ruby
20
star
20

dm-yaml-adapter

A YAML Adapter for DataMapper
Ruby
18
star
21

dm-oracle-adapter

Oracle Adapter for DataMapper
Ruby
18
star
22

dm-aggregates

DataMapper plugin providing support for aggregates on collections
Ruby
16
star
23

dm-is-list

DataMapper plugin for creating and organizing lists
Ruby
14
star
24

dm-postgres-adapter

A postgresql Adapter for DataMapper
Ruby
14
star
25

dm-is-state_machine

DataMapper plugin for creating state machines
Ruby
13
star
26

dm-is-remixable

dm-is-remixable allow you to create reusable data functionality
Ruby
13
star
27

dm-sqlite-adapter

A sqlite Adapter for DataMapper
Ruby
13
star
28

dm-ar-finders

DataMapper plugin providing ActiveRecord-style finders
Ruby
12
star
29

dm-sqlserver-adapter

A SQLServer Adapter for DataMapper
Ruby
11
star
30

dm-is-nested_set

DataMapper plugin allowing the creation of nested sets from data models
Ruby
11
star
31

dm-mysql-adapter

A mysql Adapter for DataMapper
Ruby
10
star
32

dm-is-searchable

A DataMapper plugin for searching
Ruby
10
star
33

dm-observer

DataMapper plugin for observing Resources
Ruby
10
star
34

dm-dev

Thor tasks helping with DM development and contribution
Ruby
9
star
35

dm-timestamps

DataMapper plugin for magical timestamps
Ruby
9
star
36

dm-adjust

DataMapper plugin providing methods to increment and decrement properties
Ruby
8
star
37

dm-cli

DataMapper plugin allowing interaction with models through a CLI
Ruby
7
star
38

dm-models

Reusable standard models.
Ruby
7
star
39

rails_datamapper

Rails Plugin for DataMapper
Ruby
4
star
40

dm-ferret-adapter

Ferret Adapter for DataMapper
Ruby
3
star
41

dm-relation-graph

Relation graph for DataMapper 2
Ruby
2
star