datamapper 0.2.5 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +5 -1
- data/FAQ +96 -0
- data/QUICKLINKS +12 -0
- data/README +57 -155
- data/environment.rb +61 -43
- data/example.rb +30 -12
- data/lib/data_mapper.rb +6 -1
- data/lib/data_mapper/adapters/abstract_adapter.rb +0 -57
- data/lib/data_mapper/adapters/data_object_adapter.rb +203 -97
- data/lib/data_mapper/adapters/mysql_adapter.rb +4 -0
- data/lib/data_mapper/adapters/postgresql_adapter.rb +7 -1
- data/lib/data_mapper/adapters/sql/coersion.rb +3 -2
- data/lib/data_mapper/adapters/sql/commands/load_command.rb +29 -10
- data/lib/data_mapper/adapters/sql/mappings/associations_set.rb +4 -0
- data/lib/data_mapper/adapters/sql/mappings/column.rb +13 -9
- data/lib/data_mapper/adapters/sql/mappings/conditions.rb +172 -0
- data/lib/data_mapper/adapters/sql/mappings/table.rb +43 -17
- data/lib/data_mapper/adapters/sqlite3_adapter.rb +9 -2
- data/lib/data_mapper/associations.rb +75 -3
- data/lib/data_mapper/associations/belongs_to_association.rb +70 -36
- data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +195 -86
- data/lib/data_mapper/associations/has_many_association.rb +168 -61
- data/lib/data_mapper/associations/has_n_association.rb +23 -3
- data/lib/data_mapper/attributes.rb +73 -0
- data/lib/data_mapper/auto_migrations.rb +2 -6
- data/lib/data_mapper/base.rb +5 -9
- data/lib/data_mapper/database.rb +4 -3
- data/lib/data_mapper/embedded_value.rb +66 -30
- data/lib/data_mapper/identity_map.rb +1 -3
- data/lib/data_mapper/is/tree.rb +121 -0
- data/lib/data_mapper/migration.rb +155 -0
- data/lib/data_mapper/persistence.rb +532 -218
- data/lib/data_mapper/property.rb +306 -0
- data/lib/data_mapper/query.rb +164 -0
- data/lib/data_mapper/support/blank.rb +2 -2
- data/lib/data_mapper/support/connection_pool.rb +5 -6
- data/lib/data_mapper/support/enumerable.rb +3 -3
- data/lib/data_mapper/support/errors.rb +10 -1
- data/lib/data_mapper/support/inflector.rb +174 -238
- data/lib/data_mapper/support/object.rb +54 -0
- data/lib/data_mapper/support/serialization.rb +19 -1
- data/lib/data_mapper/support/string.rb +7 -16
- data/lib/data_mapper/support/symbol.rb +3 -15
- data/lib/data_mapper/support/typed_set.rb +68 -0
- data/lib/data_mapper/types/base.rb +44 -0
- data/lib/data_mapper/types/string.rb +34 -0
- data/lib/data_mapper/validations/number_validator.rb +40 -0
- data/lib/data_mapper/validations/string_validator.rb +20 -0
- data/lib/data_mapper/validations/validator.rb +13 -0
- data/performance.rb +26 -1
- data/profile_data_mapper.rb +1 -1
- data/rakefile.rb +42 -2
- data/spec/acts_as_tree_spec.rb +11 -3
- data/spec/adapters/data_object_adapter_spec.rb +31 -0
- data/spec/associations/belongs_to_association_spec.rb +98 -0
- data/spec/associations/has_and_belongs_to_many_association_spec.rb +377 -0
- data/spec/associations/has_many_association_spec.rb +337 -0
- data/spec/attributes_spec.rb +23 -1
- data/spec/auto_migrations_spec.rb +86 -29
- data/spec/callbacks_spec.rb +107 -0
- data/spec/column_spec.rb +5 -2
- data/spec/count_command_spec.rb +33 -1
- data/spec/database_spec.rb +18 -0
- data/spec/dependency_spec.rb +4 -2
- data/spec/embedded_value_spec.rb +8 -8
- data/spec/fixtures/people.yaml +1 -1
- data/spec/fixtures/projects.yaml +10 -1
- data/spec/fixtures/tasks.yaml +6 -0
- data/spec/fixtures/tasks_tasks.yaml +2 -0
- data/spec/fixtures/tomatoes.yaml +1 -0
- data/spec/is_a_tree_spec.rb +149 -0
- data/spec/load_command_spec.rb +71 -9
- data/spec/magic_columns_spec.rb +17 -2
- data/spec/migration_spec.rb +267 -0
- data/spec/models/animal.rb +1 -1
- data/spec/models/candidate.rb +8 -0
- data/spec/models/career.rb +1 -1
- data/spec/models/chain.rb +8 -0
- data/spec/models/comment.rb +1 -1
- data/spec/models/exhibit.rb +1 -1
- data/spec/models/fence.rb +7 -0
- data/spec/models/fruit.rb +2 -2
- data/spec/models/job.rb +8 -0
- data/spec/models/person.rb +2 -3
- data/spec/models/post.rb +1 -1
- data/spec/models/project.rb +21 -1
- data/spec/models/section.rb +1 -1
- data/spec/models/serializer.rb +1 -1
- data/spec/models/task.rb +9 -0
- data/spec/models/tomato.rb +27 -0
- data/spec/models/user.rb +8 -2
- data/spec/models/zoo.rb +2 -7
- data/spec/paranoia_spec.rb +1 -1
- data/spec/{base_spec.rb → persistence_spec.rb} +207 -18
- data/spec/postgres_spec.rb +48 -6
- data/spec/property_spec.rb +90 -9
- data/spec/query_spec.rb +71 -5
- data/spec/save_command_spec.rb +11 -0
- data/spec/spec_helper.rb +14 -11
- data/spec/support/blank_spec.rb +8 -0
- data/spec/support/inflector_spec.rb +41 -0
- data/spec/support/object_spec.rb +9 -0
- data/spec/{serialization_spec.rb → support/serialization_spec.rb} +1 -1
- data/spec/support/silence_spec.rb +15 -0
- data/spec/{support_spec.rb → support/string_spec.rb} +3 -3
- data/spec/support/struct_spec.rb +12 -0
- data/spec/support/typed_set_spec.rb +66 -0
- data/spec/table_spec.rb +3 -3
- data/spec/types/string.rb +81 -0
- data/spec/validates_uniqueness_of_spec.rb +17 -0
- data/spec/validations/number_validator.rb +59 -0
- data/spec/validations/string_validator.rb +14 -0
- metadata +59 -17
- data/do_performance.rb +0 -153
- data/lib/data_mapper/support/active_record_impersonation.rb +0 -103
- data/lib/data_mapper/support/weak_hash.rb +0 -46
- data/spec/active_record_impersonation_spec.rb +0 -129
- data/spec/associations_spec.rb +0 -232
- data/spec/conditions_spec.rb +0 -49
- data/spec/has_many_association_spec.rb +0 -173
- data/spec/models/animals_exhibit.rb +0 -8
data/CHANGELOG
CHANGED
@@ -138,4 +138,8 @@
|
|
138
138
|
* Added private/protected properties
|
139
139
|
* Remove HasOneAssociation, Make HasManyAssociation impersonate has_one relationships
|
140
140
|
* Added #get method
|
141
|
-
* Persistence module added, inheriting from DataMapper::Base no longer necessary
|
141
|
+
* Persistence module added, inheriting from DataMapper::Base no longer necessary
|
142
|
+
|
143
|
+
-- 0.3.0
|
144
|
+
* HasManyAssociation::Set now has a nil? method, so we can do stuff like cage.animal.nil?
|
145
|
+
|
data/FAQ
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
:include:QUICKLINKS
|
2
|
+
|
3
|
+
= FAQ
|
4
|
+
|
5
|
+
=== I don't want to use :id as a primary key, but I don't see <tt>set_primary_key</tt> anywhere. What do I do?
|
6
|
+
|
7
|
+
If you're working with a table that doesn't have a <tt>:id</tt> column, you can declare your properties as you usually do, and declare one of them as a natural key.
|
8
|
+
|
9
|
+
property :name, :string, :key => true
|
10
|
+
|
11
|
+
You should now be able to do <tt>Class['name_string']</tt> as well. Remember: this column should be unique, so treat it that way. This is the equivalent to using <tt>set_primary_key</tt> in ActiveRecord.
|
12
|
+
|
13
|
+
|
14
|
+
=== How do I make a model paranoid?
|
15
|
+
|
16
|
+
property :deleted_at, :datetime
|
17
|
+
|
18
|
+
If you've got deleted_at, your model is paranoid auto-magically. All of your calls to <tt>##all()</tt>, <tt>##first()</tt>, and <tt>##count()</tt> will be scoped with <tt>where deleted_at is null</tt>. Plus, you won't see deleted objects in your associations.
|
19
|
+
|
20
|
+
=== Does DataMapper support Has Many Through?
|
21
|
+
|
22
|
+
Write me!
|
23
|
+
|
24
|
+
=== What about Self-Referential Has And Belongs to Many?
|
25
|
+
|
26
|
+
Sure does. Here's an example implementation:
|
27
|
+
|
28
|
+
class Task < DataMapper::Base
|
29
|
+
has_and_belongs_to_many :tasks,
|
30
|
+
:join_table => "task_relationships",
|
31
|
+
:left_foreign_key => "parent_id",
|
32
|
+
:right_foreign_key => "child_id"
|
33
|
+
end
|
34
|
+
|
35
|
+
You'll notice that instead of <tt>foreign_key</tt> and <tt>association_foreign_key</tt>, DataMapper uses the "database-y" terms <tt>left_foreign_key</tt>, and <tt>right_foreign_key</tt>.
|
36
|
+
|
37
|
+
=== Does DataMapper do Single Table Inheritance?
|
38
|
+
|
39
|
+
Oh yes, and particularly well too.
|
40
|
+
|
41
|
+
class Person < Datamapper::Base
|
42
|
+
property :type, :class
|
43
|
+
## other shared properties here
|
44
|
+
end
|
45
|
+
|
46
|
+
class Salesperson < Person; end
|
47
|
+
|
48
|
+
You can claim a column to have the type <tt>:class</tt> and DataMapper will automatically drop the class name of the inherited classes into that column of the database.
|
49
|
+
|
50
|
+
=== What about Class Table Inheritance?
|
51
|
+
|
52
|
+
Class Table Inheritance is on the drawing board and everyone's drooling over it. So no, not yet, but soon.
|
53
|
+
|
54
|
+
=== How do I run my own commands?
|
55
|
+
|
56
|
+
You're probably asking for <tt>find_by_sql</tt>, and DataMapper has that in it's ActiveRecordImpersonation, but if you want to go straight-up DataMapper, you'll want to use <tt>database.query</tt>
|
57
|
+
|
58
|
+
database.query("select * from users where clue > 0")
|
59
|
+
|
60
|
+
This does not return any Users (har har), but rather Struct's that will quack like Users. They'll be read-only as well.
|
61
|
+
|
62
|
+
<tt>database.query</tt> shouldn't be used if you aren't expecting a result set back. If you want to just execute something against the database, use <tt>database.execute</tt> instead.
|
63
|
+
|
64
|
+
=== Can I batch-process a ton of records at once?
|
65
|
+
|
66
|
+
User.each(:performance_rating => "low") do |u|
|
67
|
+
u.employment_status = "fired"
|
68
|
+
u.save
|
69
|
+
end
|
70
|
+
|
71
|
+
With ActiveRecord, doing a <tt>User.find(:all).each{}</tt> would execute the find, instantiate an object for EVERY result, THEN apply your transformations to each object in turn. Doesn't sound too horrible unless you have a TON of records; you WILL grind your system to a screeching and bloody halt.
|
72
|
+
|
73
|
+
Datamapper's <tt>#each</tt> works in sets of 500 so the amount of objects instantiated at a time won't make your computer think it's a victim in a Saw movie. Once it's done executing your block on the first set of 500, it moves on to the next.
|
74
|
+
|
75
|
+
What's more is <tt>#each</tt> is secretly a finder too. You can pass it an options hash and it'll only iterate on 500-item sets matching your query. Don't send it <tt>:offset</tt> though, because that's how it pages. You can overload the page size by sending it <tt>:limit</tt>
|
76
|
+
|
77
|
+
=== Can I get an SQL log of what queries DataMapper is issuing?
|
78
|
+
|
79
|
+
Yup, when you issue <tt>Database.setup</tt>, tack on the <tt>log_stream</tt> and <tt>log_level</tt>:
|
80
|
+
|
81
|
+
DataMapper::Database.setup({
|
82
|
+
:adapter => 'mysql',
|
83
|
+
:host => 'localhost',
|
84
|
+
:username => 'root',
|
85
|
+
:password => 'R00tPaswooooord',
|
86
|
+
:database => 'myspiffyblog_development',
|
87
|
+
:log_stream => 'log/sql.log',
|
88
|
+
:log_level => 0
|
89
|
+
})
|
90
|
+
|
91
|
+
By supplying the <tt>log_stream</tt> you're telling DataMapper what file you want to see your sql logs in. <tt>log_level</tt> is the Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/] level of output you want to see there. 0, in this case, says that you want to see all DEBUG level messages (and higher) sent to the logger. For more information on how to work with Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/], hit up http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc/.
|
92
|
+
|
93
|
+
Incidentally, if you'd like to send a message into the Datamapper logger, do:
|
94
|
+
|
95
|
+
database.adapter.logger.info "your message here"
|
96
|
+
|
data/QUICKLINKS
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
= Quick Links
|
2
|
+
|
3
|
+
* Finders and CRUD - DataMapper::Persistence::ConvenienceMethods::ClassMethods
|
4
|
+
* Properties - DataMapper::Property
|
5
|
+
* Validations - Validatable
|
6
|
+
* Migrations
|
7
|
+
* FAQ[link:/files/FAQ.html]
|
8
|
+
* Contact Us
|
9
|
+
* Website - http://www.datamapper.org
|
10
|
+
* Bug Reports - http://wm.lighthouseapp.com/projects/4819-datamapper/overview
|
11
|
+
* IRC Channel - <tt>##datamapper</tt> on irc.freenode.net
|
12
|
+
* Mailing List - http://groups.google.com/group/datamapper/
|
data/README
CHANGED
@@ -1,203 +1,105 @@
|
|
1
|
-
= DataMapper
|
2
1
|
|
3
|
-
|
2
|
+
:include:QUICKLINKS
|
4
3
|
|
5
|
-
|
4
|
+
= Why DataMapper?
|
6
5
|
|
7
|
-
|
6
|
+
== Open Development
|
8
7
|
|
9
|
-
|
10
|
-
creating not only beautiful syntax, but a huge speed up from other popular ORMs.
|
8
|
+
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[http://wm.lighthouseapp.com/projects/4819-datamapper/overview] 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.
|
11
9
|
|
12
|
-
|
10
|
+
== Identity Map
|
13
11
|
|
14
|
-
If
|
12
|
+
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 <tt>false</tt> results. Do the same in DataMapper and it's <tt>true</tt> all the way down.
|
15
13
|
|
16
|
-
If you're looking for cold hard numbers, take a gander at the PERFORMANCE file.
|
17
|
-
You'll find the latest performance stats under "Current Performance".
|
18
14
|
|
15
|
+
@parent = Tree.find(:first, :conditions => ['name = ?', 'bob'])
|
19
16
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
=== Open Development
|
24
|
-
|
25
|
-
Found a bug or want a feature? Tired of dealing with the bureaucracy that other ORM maintainers force upon you?
|
26
|
-
|
27
|
-
The community surrounding DataMapper is open and friendly. Contributions of any kind are welcome,
|
28
|
-
and the founder and maintainer of the project can be easily reached by using the fairly active
|
29
|
-
mailing list (http://groups.google.com/group/datamapper) or by jumping in to the <tt>datamapper</tt>
|
30
|
-
channel on irc.freenode.net.
|
31
|
-
|
32
|
-
=== Thread-Safe
|
33
|
-
|
34
|
-
Thread safety is a critical topic in any production software, and especially so with the DataMapper.
|
35
|
-
The maintainers have dedicated much of their time to ensuring that the DataMapper is thread safe thus far,
|
36
|
-
and will continue to do as much to ensure it stays that way.
|
37
|
-
|
38
|
-
=== Hacker Friendly
|
39
|
-
|
40
|
-
One of the most frustrating problems with other ORMs is the painstaking time it takes
|
41
|
-
to understand and traverse the code-base.
|
42
|
-
|
43
|
-
DataMapper aims to be as lightweight as possible, keeping
|
44
|
-
the code-base clean and uncluttered. Never be afraid to crack open the subversion trunk again!
|
45
|
-
|
46
|
-
|
47
|
-
== Getting Started
|
48
|
-
|
49
|
-
=== Install DataMapper
|
50
|
-
There are two simple options for getting started with DataMapper.
|
51
|
-
|
52
|
-
If you'd like to use SVN and check out the bleeding edge version of the project,
|
53
|
-
execute the following command in your terminal:
|
54
|
-
|
55
|
-
|
56
|
-
svn co http://datamapper.rubyforge.org/svn/trunk/ data_mapper
|
57
|
-
|
58
|
-
After checking out the trunk, point your application to the trunk/lib folder and require it.
|
59
|
-
|
60
|
-
If you'd like to just <i>use</i> datamapper and don't plan on looking at the source code or want bleeding edge features,
|
61
|
-
execute this instead:
|
62
|
-
|
63
|
-
gem install datamapper
|
17
|
+
@parent.children.each do |child|
|
18
|
+
puts @parent.object_id == child.parent.object_id
|
19
|
+
end
|
64
20
|
|
65
|
-
|
66
|
-
If you want to find out how to use DataMapper in your existing application, jump to "Require it in your application".
|
21
|
+
This makes DataMapper faster and allocate less resources to get things done.
|
67
22
|
|
68
|
-
|
69
|
-
create a new database called:
|
70
|
-
data_mapper_1
|
23
|
+
== Don't Do What You Don't Have To
|
71
24
|
|
72
|
-
|
73
|
-
rake
|
74
|
-
|
75
|
-
That will setup your database. From there, execute:
|
76
|
-
ruby example.rb
|
77
|
-
|
78
|
-
This will drop you into an IRB session, where you can further play with the syntax. Try out the intuitive finders:
|
79
|
-
Animal[0]
|
80
|
-
Animal.all
|
81
|
-
Zoo.first(:name => 'Galveston')
|
82
|
-
Zoo.find(:first, :conditions => ['name = ?', 'Galveston'])
|
25
|
+
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.
|
83
26
|
|
84
|
-
|
27
|
+
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.
|
85
28
|
|
86
|
-
|
87
|
-
require 'data_mapper'
|
88
|
-
|
89
|
-
=== Specify a Database Connection
|
29
|
+
== Eager Loading
|
90
30
|
|
91
|
-
|
92
|
-
Ruby on Rails project, and have a database.yml, DataMapper should pick it up automatically.
|
31
|
+
Ready for something amazing? The following example executes only two queries.
|
93
32
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
:host => 'localhost',
|
98
|
-
:username => 'root',
|
99
|
-
:password => 'R00tPaswooooord',
|
100
|
-
:database => 'selecta_development'
|
101
|
-
})
|
102
|
-
|
103
|
-
The currently supported databases are:
|
104
|
-
PostgreSQL:: (postgresql adaptor)
|
105
|
-
MySQL:: (mysql adaptor)
|
106
|
-
SQLite3:: (sqlite3 adaptor)
|
107
|
-
|
108
|
-
=== Define Your Models
|
33
|
+
zoos = Zoo.all
|
34
|
+
first = zoos.first
|
35
|
+
first.exhibits # Loads the exhibits for all the Zoo objects in the zoos variable.
|
109
36
|
|
110
|
-
|
37
|
+
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 <tt>include</tt> option in your finders.
|
111
38
|
|
112
|
-
|
113
|
-
* Define your properties you wish to access from the database
|
114
|
-
* Define any relationships (optional)
|
39
|
+
== Laziness Can Be A Virtue
|
115
40
|
|
116
|
-
|
117
|
-
you'd like to make available to your models as properties. There are a few reasons for this,
|
118
|
-
which are too numerous to go into here.
|
41
|
+
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.
|
119
42
|
|
120
|
-
|
43
|
+
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 <tt>true</tt> or <tt>false</tt>.
|
121
44
|
|
122
|
-
class Animal
|
123
|
-
include DataMapper::Persistence
|
124
45
|
|
46
|
+
class Animal < DataMapper::Base
|
125
47
|
property :name, :string
|
126
|
-
property :notes, :text, :lazy =>
|
127
|
-
|
128
|
-
has_one :favourite_fruit, :class => 'Fruit', :foreign_key => 'devourer_id'
|
129
|
-
has_and_belongs_to_many :exhibits
|
48
|
+
property :notes, :text, :lazy => false
|
130
49
|
end
|
131
|
-
|
132
|
-
==== Lazy Attributes
|
133
50
|
|
134
|
-
|
51
|
+
|
52
|
+
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:
|
135
53
|
|
136
|
-
|
54
|
+
animals = Animal.all
|
55
|
+
animals.each do |pet|
|
56
|
+
pet.notes
|
57
|
+
end
|
137
58
|
|
138
|
-
|
139
|
-
of those records occasionally, your memory signature will essentially stay much smaller, while still
|
140
|
-
easily allowing you to gain access to those attributes when you need them.
|
59
|
+
== Plays Well With Others
|
141
60
|
|
142
|
-
|
143
|
-
of returned data, you'd notice that all of the notes fields were <b>nil</b>. If you were to do something
|
144
|
-
like the following:
|
145
|
-
|
146
|
-
animals = Animals.all
|
147
|
-
animals.inspect
|
61
|
+
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 <tt>property :name, :string</tt> 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.
|
148
62
|
|
149
|
-
|
63
|
+
Unless of course you want to map to a legacy database. Raise your hand if you like seeing a method called <tt>col2Name</tt> 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:
|
150
64
|
|
151
|
-
If you were to do something like the following:
|
152
|
-
animals.first.notes
|
153
|
-
animals.inspect
|
154
65
|
|
155
|
-
|
66
|
+
class Fruit < DataMapper::Base
|
67
|
+
set_table_name 'frt'
|
68
|
+
property :name, :string, :column => 'col2'
|
69
|
+
end
|
156
70
|
|
157
|
-
The astute rubyist will immediately wonder if this is possible on associations, and indeed it is!
|
158
|
-
This inevitably avoids the 1+N query problems that have so plagued us in the past. It allows us to
|
159
|
-
develop in an intuitive manner, so that if we executed a loop:
|
160
71
|
|
161
|
-
|
162
|
-
#The next line crafts a query to fetch ALL of this
|
163
|
-
#set's favorite_fruit attributes
|
164
|
-
puts animal.favorite_fruit
|
165
|
-
end
|
72
|
+
== All Ruby, All The Time
|
166
73
|
|
167
|
-
|
74
|
+
It's great that ActiveRecord allows you to write SQL when you need to, but should we have to so often?
|
168
75
|
|
169
|
-
|
76
|
+
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 <tt>Zoo.all(:name => 'Dallas')</tt> and DataMapper will look for zoos with the name of 'Dallas'.
|
170
77
|
|
171
|
-
|
78
|
+
It's just a little thing, but it's so much nicer than writing <tt>Zoo.find(:all, :conditions => ['name = ?', 'Dallas'])</tt>. What if you need other comparisons though? Try these:
|
172
79
|
|
173
|
-
* belongs_to :model
|
174
|
-
* has_one :model
|
175
|
-
* has_many :model
|
176
|
-
* has_and_belongs_to_many :model
|
177
80
|
|
178
|
-
|
81
|
+
Zoo.first(:name => 'Galveston')
|
179
82
|
|
180
|
-
|
83
|
+
# 'gt' means greater-than. We also do 'lt'.
|
84
|
+
Person.all(:age.gt => 30)
|
181
85
|
|
182
|
-
|
86
|
+
# 'gte' means greather-than-or-equal-to. We also do 'lte'.
|
87
|
+
Person.all(:age.gte => 30)
|
183
88
|
|
184
|
-
|
89
|
+
Person.all(:name.not => 'bob')
|
185
90
|
|
186
|
-
|
187
|
-
|
188
|
-
This will generate the table structure for your model automatically.
|
91
|
+
# If the value of a pair is an Array, we do an IN-clause for you.
|
92
|
+
Person.all(:name.like => 'S%', :id => [1, 2, 3, 4, 5])
|
189
93
|
|
190
|
-
|
94
|
+
# An alias for Zoo.find(11)
|
95
|
+
Zoo[11]
|
191
96
|
|
192
|
-
|
97
|
+
# Does a NOT IN () clause for you.
|
98
|
+
Person.all(:name.not => ['bob','rick','steve'])
|
193
99
|
|
194
|
-
=== NOTE
|
195
|
-
Both of these methods are DESTRUCTIVE. If you are working in anything other than a pre-production environment,
|
196
|
-
stay far away from these methods!
|
197
100
|
|
198
|
-
|
101
|
+
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...
|
199
102
|
|
200
|
-
|
103
|
+
== Better Is Great, But Familiar Is Nice
|
201
104
|
|
202
|
-
DataMapper
|
203
|
-
find on the above link is top-notch.
|
105
|
+
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.
|
data/environment.rb
CHANGED
@@ -1,44 +1,62 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require File.dirname(__FILE__) + '/
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
configuration_options[:username] = ENV['USERNAME'] || 'postgres'
|
26
|
-
when 'mysql' then
|
27
|
-
configuration_options[:username] = 'root'
|
28
|
-
when 'sqlite3' then
|
29
|
-
unless configuration_options[:database] == ':memory:'
|
30
|
-
configuration_options[:database] << '.db'
|
1
|
+
unless defined?(INITIAL_CLASSES)
|
2
|
+
# Require the DataMapper, and a Mock Adapter.
|
3
|
+
require File.dirname(__FILE__) + '/lib/data_mapper'
|
4
|
+
require File.dirname(__FILE__) + '/spec/mock_adapter'
|
5
|
+
|
6
|
+
adapter = ENV['ADAPTER'] || 'sqlite3'
|
7
|
+
|
8
|
+
configuration_options = {
|
9
|
+
:adapter => adapter,
|
10
|
+
:database => (ENV['DATABASE'] || 'data_mapper_1').dup
|
11
|
+
}
|
12
|
+
|
13
|
+
# Prepare the log path, and remove the existing spec.log
|
14
|
+
require 'fileutils'
|
15
|
+
|
16
|
+
if ENV['LOG_NAME']
|
17
|
+
log_path = nil
|
18
|
+
|
19
|
+
if ENV['LOG_NAME'] != 'STDOUT'
|
20
|
+
FileUtils::mkdir_p(File.dirname(__FILE__) + '/log')
|
21
|
+
log_path = File.dirname(__FILE__) + "/log/#{ENV['LOG_NAME']}.log"
|
22
|
+
FileUtils::rm log_path if File.exists?(log_path)
|
23
|
+
else
|
24
|
+
log_path = 'STDOUT'
|
31
25
|
end
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
26
|
+
|
27
|
+
configuration_options.merge!(:log_stream => log_path, :log_level => Logger::DEBUG)
|
28
|
+
end
|
29
|
+
|
30
|
+
case adapter
|
31
|
+
when 'postgresql' then
|
32
|
+
configuration_options[:username] = ENV['USERNAME'] || 'postgres'
|
33
|
+
when 'mysql' then
|
34
|
+
configuration_options[:username] = 'root'
|
35
|
+
when 'sqlite3' then
|
36
|
+
unless configuration_options[:database] == ':memory:'
|
37
|
+
configuration_options[:database] << '.db'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
load_models = lambda do
|
42
|
+
Dir[File.dirname(__FILE__) + '/spec/models/*.rb'].sort.each { |path| load path }
|
43
|
+
end
|
44
|
+
|
45
|
+
secondary_configuration_options = configuration_options.dup
|
46
|
+
secondary_configuration_options.merge!(:database => (adapter == 'sqlite3' ? 'data_mapper_2.db' : 'data_mapper_2'))
|
47
|
+
|
48
|
+
DataMapper::Database.setup(configuration_options)
|
49
|
+
DataMapper::Database.setup(:secondary, secondary_configuration_options)
|
50
|
+
DataMapper::Database.setup(:mock, :adapter => MockAdapter)
|
51
|
+
|
52
|
+
[:default, :secondary, :mock].each { |name| database(name) { load_models.call } }
|
53
|
+
|
54
|
+
# Reset the test database.
|
55
|
+
unless ENV['AUTO_MIGRATE'] == 'false'
|
56
|
+
[:default, :secondary].each { |name| database(name) { DataMapper::Persistence.auto_migrate! } }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Save the initial database layout so we can put everything back together
|
60
|
+
# after auto migrations testing
|
61
|
+
INITIAL_CLASSES = Array.new(DataMapper::Persistence.subclasses.to_a)
|
62
|
+
end
|