{"id":13484299,"url":"https://github.com/datamapper/dm-core","last_synced_at":"2025-03-27T16:30:40.200Z","repository":{"id":488644,"uuid":"114791","full_name":"datamapper/dm-core","owner":"datamapper","description":"DataMapper - Core","archived":true,"fork":false,"pushed_at":"2016-05-26T20:37:01.000Z","size":14912,"stargazers_count":753,"open_issues_count":101,"forks_count":158,"subscribers_count":27,"default_branch":"master","last_synced_at":"2024-05-17T17:02:43.379Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://datamapper.org/","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/datamapper.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2009-01-26T06:42:36.000Z","updated_at":"2024-03-13T03:45:28.000Z","dependencies_parsed_at":"2022-07-07T14:03:35.683Z","dependency_job_id":null,"html_url":"https://github.com/datamapper/dm-core","commit_stats":null,"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datamapper%2Fdm-core","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datamapper%2Fdm-core/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datamapper%2Fdm-core/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datamapper%2Fdm-core/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/datamapper","download_url":"https://codeload.github.com/datamapper/dm-core/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":238461503,"owners_count":19476341,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T17:01:22.064Z","updated_at":"2025-03-27T16:30:39.166Z","avatar_url":"https://github.com/datamapper.png","language":"Ruby","readme":"Why DataMapper?\n===============\n\nOpen Development\n----------------\n\nDataMapper sports a very accessible code-base and a welcoming community.\nOutside contributions and feedback are welcome and encouraged, especially\nconstructive criticism. Make your voice heard! [Submit an issue](https://github.com/datamapper/dm-core/issues),\nspeak up on our [mailing-list](http://groups.google.com/group/datamapper/),\nchat with us on [irc](irc://irc.freenode.net/#datamapper), write a spec, get it\nreviewed, ask for commit rights. It's as easy as that to become a contributor.\n\nIdentity Map\n------------\n\nOne row in the data-store should equal one object reference. Pretty simple idea.\nPretty profound impact. If you run the following code in ActiveRecord you'll\nsee all `false` results. Do the same in DataMapper and it's\n`true` all the way down.\n\n``` ruby\n  repository do\n    @parent = Tree.first(:name =\u003e 'bob')\n\n    @parent.children.each do |child|\n      puts @parent.equal?(child.parent)  # =\u003e true\n    end\n  end\n```\n\nThis makes DataMapper faster and allocate less resources to get things done.\n\nDirty Tracking\n--------------\n\nWhen you save a model back to your data-store, DataMapper will only write\nthe fields that actually changed. So it plays well with others. You can\nuse it in an Integration data-store without worrying that your application will\nbe a bad actor causing trouble for all of your other processes.\n\nEager Loading\n-------------\n\nReady for something amazing? The following example executes only two queries\nregardless of how many rows the inner and outer queries return.\n\n``` ruby\n  repository do\n    Zoo.all.each { |zoo| zoo.exhibits.to_a }\n  end\n```\n\nPretty impressive huh? The idea is that you aren't going to load a set of\nobjects and use only an association in just one of them. This should hold up\npretty well against a 99% rule. When you don't want it to work like this, just\nload the item you want in it's own set. So the DataMapper thinks ahead. We\nlike to call it \"performant by default\". This feature single-handedly wipes\nout the \"N+1 Query Problem\". No need to specify an `:include` option in\nyour finders.\n\nLaziness Can Be A Virtue\n------------------------\n\nText fields are expensive in data-stores. They're generally stored in a\ndifferent place than the rest of your data. So instead of a fast sequential\nread from your hard-drive, your data-store server has to hop around all over the\nplace to get what it needs. Since ActiveRecord returns everything by default,\nadding a text field to a table slows everything down drastically, across the\nboard.\n\nNot so with the DataMapper. Text fields are lazily loaded, meaning they\nonly load when you need them. If you want more control you can enable or\ndisable this feature for any field (not just text-fields) by passing a\n`:lazy` option to your field mapping with a value of `true` or\n`false`.\n\n``` ruby\n  class Animal\n    include DataMapper::Resource\n\n    property :name,        String\n    property :description, Text, :lazy =\u003e false\n  end\n```\n\nPlus, lazy-loading of Text fields happens automatically and intelligently when\nworking with associations.  The following only issues 2 queries to load up all\nof the notes fields on each animal:\n\n``` ruby\n  repository do\n    Animal.all.each { |animal| animal.description.to_a }\n  end\n```\n\nDid you notice the `#to_a` call in the above example?  That\nwas necessary because even DataMapper collections are lazy.  If you don't\niterate over them, or in this case ask them to become Arrays, they won't\nexecute until you need them.  We needed to call `#to_a` to force\nthe lazy load because without it, the above example would have only\nexecuted one query.  This extra bit of laziness can come in very handy,\nfor example:\n\n``` ruby\n  animals     = Animal.all\n  description = 'foo'\n\n  animals.each do |animal|\n    animal.update(:description =\u003e description)\n  end\n```\n\nIn the above example, the Animals won't be retrieved until you actually\nneed them.  This comes in handy in cases where you initialize the\ncollection before you know if you need it, like in a web app controller.\n\nCollection Chaining\n-------------------\n\nDataMapper's lazy collections are also handy because you can get the\nsame effect as named scopes, without any special syntax, eg:\n\n``` ruby\n  class Animal\n    # ... setup ...\n\n    def self.mammals\n      all(:mammal =\u003e true)\n    end\n\n    def self.zoo(zoo)\n      all(:zoo =\u003e zoo)\n    end\n  end\n\n  zoo = Zoo.first(:name =\u003e 'Greater Vancouver Zoo')\n\n  Animal.mammals.zoo(zoo).to_a  # =\u003e executes one query\n```\n\nIn the above example, we ask the Animal model for all the mammals,\nand then all the animals in a specific zoo, and DataMapper will chain\nthe collection queries together and execute a single query to retrieve\nthe matching records.  There's no special syntax, and no custom DSLs\nto learn, it's just plain ruby all the way down.\n\nYou can even use this on association collections, eg:\n\n``` ruby\n  zoo.animals.mammals.to_a  # =\u003e executes one query\n```\n\nCustom Properties\n-----------------\n\nWith DataMapper it is possible to create custom properties for your models.\nConsider this example:\n\n``` ruby\n  module DataMapper\n    class Property\n      class Email \u003c String\n        required true\n        format   /^([\\w\\.%\\+\\-]+)@([\\w\\-]+\\.)+([\\w]{2,})$/i\n      end\n    end\n  end\n\n  class User\n    include DataMapper::Resource\n\n    property :id,    Serial\n    property :email, Email\n  end\n```\n\nThis way there won't be a need to repeat same property options every time you\nadd an email to a model. In the example above we create an Email property which\nis just a String with additional pre-configured options: `required` and\n`format`. Please note that it is possible to override these options when\ndeclaring a property, like this:\n\n``` ruby\n  class Member\n    include DataMapper::Resource\n\n    property :id,    Serial\n    property :email, Email, :required =\u003e false\n  end\n```\n\nPlays Well With Others\n----------------------\n\nIn ActiveRecord, all your fields are mapped, whether you want them or not.\nThis slows things down. In the DataMapper you define your mappings in your\nmodel. So instead of an _ALTER TABLE ADD field_ in your data-store, you simply\nadd a `property :name, String` to your model. DRY. No schema.rb. No\nmigration files to conflict or die without reverting changes. Your model\ndrives the data-store, not the other way around.\n\nUnless of course you want to map to a legacy data-store. Raise your hand if you\nlike seeing a method called `col2Name` on your model just because\nthat's what it's called in an old data-store you can't afford to change right\nnow? In DataMapper you control the mappings:\n\n``` ruby\n  class Fruit\n    include DataMapper::Resource\n\n    storage_names[:repo] = 'frt'\n\n    property :name, String, :field =\u003e 'col2Name'\n  end\n```\n\nAll Ruby, All The Time\n----------------------\n\nIt's great that ActiveRecord allows you to write SQL when you need to, but\nshould we have to so often?\n\nDataMapper supports issuing your own query, but it also provides more helpers\nand a unique hash-based condition syntax to cover more of the use-cases where\nissuing your own SQL would have been the only way to go. For example, any\nfinder option that's non-standard is considered a condition. So you can write\n`Zoo.all(:name =\u003e 'Dallas')` and DataMapper will look for zoos with the\nname of 'Dallas'.\n\nIt's just a little thing, but it's so much nicer than writing\n`Zoo.find(:all, :conditions =\u003e ['name = ?', 'Dallas'])`. What if you\nneed other comparisons though? Try these:\n\n``` ruby\n  # 'gt' means greater-than. We also do 'lt'.\n  Person.all(:age.gt =\u003e 30)\n\n  # 'gte' means greather-than-or-equal-to. We also do 'lte'.\n  Person.all(:age.gte =\u003e 30)\n\n  # 'not' allows you to match all people without the name \"bob\"\n  Person.all(:name.not =\u003e 'bob')\n\n  # If the value of a pair is an Array, we do an IN-clause for you.\n  Person.all(:name.like =\u003e 'S%', :id =\u003e [ 1, 2, 3, 4, 5 ])\n\n  # Does a NOT IN () clause for you.\n  Person.all(:name.not =\u003e [ 'bob', 'rick', 'steve' ])\n```\n\nSee? Fewer SQL fragments dirtying your Ruby code. And that's just a few of the\nnice syntax tweaks DataMapper delivers out of the box...\n\nNote on Patches/Pull Requests\n-----------------------------\n\n* Fork the project.\n* Make your feature addition or bug fix.\n* Add tests for it. This is important so I don't break it in a\n  future version unintentionally.\n* Commit, do not mess with rakefile, version, or history.\n  (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)\n* Send me a pull request. Bonus points for topic branches.\n\nCopyright\n---------\n\nCopyright (c) 2012 Dan Kubb. See LICENSE for details.\n","funding_links":[],"categories":["ORM/ODM"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatamapper%2Fdm-core","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdatamapper%2Fdm-core","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatamapper%2Fdm-core/lists"}