sam-dm-core 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.autotest +26 -0
- data/CONTRIBUTING +51 -0
- data/FAQ +92 -0
- data/History.txt +145 -0
- data/MIT-LICENSE +22 -0
- data/Manifest.txt +125 -0
- data/QUICKLINKS +12 -0
- data/README.txt +143 -0
- data/Rakefile +30 -0
- data/SPECS +63 -0
- data/TODO +1 -0
- data/lib/dm-core.rb +224 -0
- data/lib/dm-core/adapters.rb +4 -0
- data/lib/dm-core/adapters/abstract_adapter.rb +202 -0
- data/lib/dm-core/adapters/data_objects_adapter.rb +707 -0
- data/lib/dm-core/adapters/mysql_adapter.rb +136 -0
- data/lib/dm-core/adapters/postgres_adapter.rb +188 -0
- data/lib/dm-core/adapters/sqlite3_adapter.rb +105 -0
- data/lib/dm-core/associations.rb +199 -0
- data/lib/dm-core/associations/many_to_many.rb +147 -0
- data/lib/dm-core/associations/many_to_one.rb +107 -0
- data/lib/dm-core/associations/one_to_many.rb +309 -0
- data/lib/dm-core/associations/one_to_one.rb +61 -0
- data/lib/dm-core/associations/relationship.rb +218 -0
- data/lib/dm-core/associations/relationship_chain.rb +81 -0
- data/lib/dm-core/auto_migrations.rb +113 -0
- data/lib/dm-core/collection.rb +638 -0
- data/lib/dm-core/dependency_queue.rb +31 -0
- data/lib/dm-core/hook.rb +11 -0
- data/lib/dm-core/identity_map.rb +45 -0
- data/lib/dm-core/is.rb +16 -0
- data/lib/dm-core/logger.rb +232 -0
- data/lib/dm-core/migrations/destructive_migrations.rb +17 -0
- data/lib/dm-core/migrator.rb +29 -0
- data/lib/dm-core/model.rb +471 -0
- data/lib/dm-core/naming_conventions.rb +84 -0
- data/lib/dm-core/property.rb +673 -0
- data/lib/dm-core/property_set.rb +162 -0
- data/lib/dm-core/query.rb +625 -0
- data/lib/dm-core/repository.rb +159 -0
- data/lib/dm-core/resource.rb +637 -0
- data/lib/dm-core/scope.rb +58 -0
- data/lib/dm-core/support.rb +7 -0
- data/lib/dm-core/support/array.rb +13 -0
- data/lib/dm-core/support/assertions.rb +8 -0
- data/lib/dm-core/support/errors.rb +23 -0
- data/lib/dm-core/support/kernel.rb +7 -0
- data/lib/dm-core/support/symbol.rb +41 -0
- data/lib/dm-core/transaction.rb +267 -0
- data/lib/dm-core/type.rb +160 -0
- data/lib/dm-core/type_map.rb +80 -0
- data/lib/dm-core/types.rb +19 -0
- data/lib/dm-core/types/boolean.rb +7 -0
- data/lib/dm-core/types/discriminator.rb +34 -0
- data/lib/dm-core/types/object.rb +24 -0
- data/lib/dm-core/types/paranoid_boolean.rb +34 -0
- data/lib/dm-core/types/paranoid_datetime.rb +33 -0
- data/lib/dm-core/types/serial.rb +9 -0
- data/lib/dm-core/types/text.rb +10 -0
- data/lib/dm-core/version.rb +3 -0
- data/script/all +5 -0
- data/script/performance.rb +203 -0
- data/script/profile.rb +87 -0
- data/spec/integration/association_spec.rb +1371 -0
- data/spec/integration/association_through_spec.rb +203 -0
- data/spec/integration/associations/many_to_many_spec.rb +449 -0
- data/spec/integration/associations/many_to_one_spec.rb +163 -0
- data/spec/integration/associations/one_to_many_spec.rb +151 -0
- data/spec/integration/auto_migrations_spec.rb +398 -0
- data/spec/integration/collection_spec.rb +1069 -0
- data/spec/integration/data_objects_adapter_spec.rb +32 -0
- data/spec/integration/dependency_queue_spec.rb +58 -0
- data/spec/integration/model_spec.rb +127 -0
- data/spec/integration/mysql_adapter_spec.rb +85 -0
- data/spec/integration/postgres_adapter_spec.rb +731 -0
- data/spec/integration/property_spec.rb +233 -0
- data/spec/integration/query_spec.rb +506 -0
- data/spec/integration/repository_spec.rb +57 -0
- data/spec/integration/resource_spec.rb +475 -0
- data/spec/integration/sqlite3_adapter_spec.rb +352 -0
- data/spec/integration/sti_spec.rb +208 -0
- data/spec/integration/strategic_eager_loading_spec.rb +138 -0
- data/spec/integration/transaction_spec.rb +75 -0
- data/spec/integration/type_spec.rb +271 -0
- data/spec/lib/logging_helper.rb +18 -0
- data/spec/lib/mock_adapter.rb +27 -0
- data/spec/lib/model_loader.rb +91 -0
- data/spec/lib/publicize_methods.rb +28 -0
- data/spec/models/vehicles.rb +34 -0
- data/spec/models/zoo.rb +47 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +86 -0
- data/spec/unit/adapters/abstract_adapter_spec.rb +133 -0
- data/spec/unit/adapters/adapter_shared_spec.rb +15 -0
- data/spec/unit/adapters/data_objects_adapter_spec.rb +628 -0
- data/spec/unit/adapters/postgres_adapter_spec.rb +133 -0
- data/spec/unit/associations/many_to_many_spec.rb +17 -0
- data/spec/unit/associations/many_to_one_spec.rb +152 -0
- data/spec/unit/associations/one_to_many_spec.rb +393 -0
- data/spec/unit/associations/one_to_one_spec.rb +7 -0
- data/spec/unit/associations/relationship_spec.rb +71 -0
- data/spec/unit/associations_spec.rb +242 -0
- data/spec/unit/auto_migrations_spec.rb +111 -0
- data/spec/unit/collection_spec.rb +182 -0
- data/spec/unit/data_mapper_spec.rb +35 -0
- data/spec/unit/identity_map_spec.rb +126 -0
- data/spec/unit/is_spec.rb +80 -0
- data/spec/unit/migrator_spec.rb +33 -0
- data/spec/unit/model_spec.rb +339 -0
- data/spec/unit/naming_conventions_spec.rb +36 -0
- data/spec/unit/property_set_spec.rb +83 -0
- data/spec/unit/property_spec.rb +753 -0
- data/spec/unit/query_spec.rb +530 -0
- data/spec/unit/repository_spec.rb +93 -0
- data/spec/unit/resource_spec.rb +626 -0
- data/spec/unit/scope_spec.rb +142 -0
- data/spec/unit/transaction_spec.rb +493 -0
- data/spec/unit/type_map_spec.rb +114 -0
- data/spec/unit/type_spec.rb +119 -0
- data/tasks/ci.rb +68 -0
- data/tasks/dm.rb +63 -0
- data/tasks/doc.rb +20 -0
- data/tasks/gemspec.rb +23 -0
- data/tasks/hoe.rb +46 -0
- data/tasks/install.rb +20 -0
- metadata +216 -0
data/QUICKLINKS
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
= Quick Links
|
2
|
+
|
3
|
+
* Setup and Configuration - DataMapper
|
4
|
+
* Finders and CRUD -
|
5
|
+
* Properties - DataMapper::Property
|
6
|
+
* FAQ[link:/files/FAQ.html]
|
7
|
+
* Contact Us
|
8
|
+
* Website - http://www.datamapper.org
|
9
|
+
* Bug Reports - http://wm.lighthouseapp.com/projects/4819-datamapper/overview
|
10
|
+
* IRC Channel - <tt>##datamapper</tt> on irc.freenode.net
|
11
|
+
* Mailing List - http://groups.google.com/group/datamapper/
|
12
|
+
|
data/README.txt
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
|
2
|
+
:include:QUICKLINKS
|
3
|
+
|
4
|
+
= Why DataMapper?
|
5
|
+
|
6
|
+
== Open Development
|
7
|
+
|
8
|
+
DataMapper sports a very accessible code-base and a welcoming community.
|
9
|
+
Outside contributions and feedback are welcome and encouraged, especially
|
10
|
+
constructive criticism. Make your voice heard! Submit a
|
11
|
+
ticket[http://wm.lighthouseapp.com/projects/4819-datamapper/overview] or
|
12
|
+
patch[http://wm.lighthouseapp.com/projects/4819-datamapper/overview], speak up
|
13
|
+
on our mailing-list[http://groups.google.com/group/datamapper/], chat with us
|
14
|
+
on irc[irc://irc.freenode.net/#datamapper], write a spec, get it reviewed, ask
|
15
|
+
for commit rights. It's as easy as that to become a contributor.
|
16
|
+
|
17
|
+
== Identity Map
|
18
|
+
|
19
|
+
One row in the data-store should equal one object reference. Pretty simple idea.
|
20
|
+
Pretty profound impact. If you run the following code in ActiveRecord you'll
|
21
|
+
see all <tt>false</tt> results. Do the same in DataMapper and it's
|
22
|
+
<tt>true</tt> all the way down.
|
23
|
+
|
24
|
+
@parent = Tree.find(:first, :conditions => ['name = ?', 'bob'])
|
25
|
+
|
26
|
+
@parent.children.each do |child|
|
27
|
+
puts @parent.object_id == child.parent.object_id
|
28
|
+
end
|
29
|
+
|
30
|
+
This makes DataMapper faster and allocate less resources to get things done.
|
31
|
+
|
32
|
+
== Dirty Tracking
|
33
|
+
|
34
|
+
When you save a model back to your data-store, DataMapper will only write
|
35
|
+
the fields that actually changed. So it plays well with others. You can
|
36
|
+
use it in an Integration data-store without worrying that your application will
|
37
|
+
be a bad actor causing trouble for all of your other processes.
|
38
|
+
|
39
|
+
You can also configure which strategy you'd like to use to track dirtiness.
|
40
|
+
|
41
|
+
== Eager Loading
|
42
|
+
|
43
|
+
Ready for something amazing? The following example executes only two queries.
|
44
|
+
|
45
|
+
zoos = Zoo.all
|
46
|
+
first = zoos.first
|
47
|
+
first.exhibits # Loads the exhibits for all the Zoo objects in the zoos variable.
|
48
|
+
|
49
|
+
Pretty impressive huh? The idea is that you aren't going to load a set of
|
50
|
+
objects and use only an association in just one of them. This should hold up
|
51
|
+
pretty well against a 99% rule. When you don't want it to work like this, just
|
52
|
+
load the item you want in it's own set. So the DataMapper thinks ahead. We
|
53
|
+
like to call it "performant by default". This feature single-handedly wipes
|
54
|
+
out the "N+1 Query Problem". No need to specify an <tt>include</tt> option in
|
55
|
+
your finders.
|
56
|
+
|
57
|
+
== Laziness Can Be A Virtue
|
58
|
+
|
59
|
+
Text fields are expensive in data-stores. They're generally stored in a
|
60
|
+
different place than the rest of your data. So instead of a fast sequential
|
61
|
+
read from your hard-drive, your data-store server has to hop around all over the
|
62
|
+
place to get what it needs. Since ActiveRecord returns everything by default,
|
63
|
+
adding a text field to a table slows everything down drastically, across the
|
64
|
+
board.
|
65
|
+
|
66
|
+
Not so with the DataMapper. Text fields are treated like in-row associations
|
67
|
+
by default, meaning they only load when you need them. If you want more
|
68
|
+
control you can enable or disable this feature for any field (not just
|
69
|
+
text-fields) by passing a @lazy@ option to your field mapping with a value of
|
70
|
+
<tt>true</tt> or <tt>false</tt>.
|
71
|
+
|
72
|
+
class Animal
|
73
|
+
include DataMapper::Resource
|
74
|
+
property :name, String
|
75
|
+
property :notes, Text, :lazy => false
|
76
|
+
end
|
77
|
+
|
78
|
+
Plus, lazy-loading of text fields happens automatically and intelligently when
|
79
|
+
working with associations. The following only issues 2 queries to load up all
|
80
|
+
of the notes fields on each animal:
|
81
|
+
|
82
|
+
animals = Animal.all
|
83
|
+
animals.each do |pet|
|
84
|
+
pet.notes
|
85
|
+
end
|
86
|
+
|
87
|
+
== Plays Well With Others
|
88
|
+
|
89
|
+
In ActiveRecord, all your fields are mapped, whether you want them or not.
|
90
|
+
This slows things down. In the DataMapper you define your mappings in your
|
91
|
+
model. So instead of an _ALTER TABLE ADD field_ in your data-store, you simply
|
92
|
+
add a <tt>property :name, :string</tt> to your model. DRY. No schema.rb. No
|
93
|
+
migration files to conflict or die without reverting changes. Your model
|
94
|
+
drives the data-store, not the other way around.
|
95
|
+
|
96
|
+
Unless of course you want to map to a legacy data-store. Raise your hand if you
|
97
|
+
like seeing a method called <tt>col2Name</tt> on your model just because
|
98
|
+
that's what it's called in an old data-store you can't afford to change right
|
99
|
+
now? In DataMapper you control the mappings:
|
100
|
+
|
101
|
+
class Fruit
|
102
|
+
include DataMapper::Resource
|
103
|
+
storage_names[:repo] = 'frt'
|
104
|
+
property :name, String, :field => 'col2Name'
|
105
|
+
end
|
106
|
+
|
107
|
+
== All Ruby, All The Time
|
108
|
+
|
109
|
+
It's great that ActiveRecord allows you to write SQL when you need to, but
|
110
|
+
should we have to so often?
|
111
|
+
|
112
|
+
DataMapper supports issuing your own query, but it also provides more helpers
|
113
|
+
and a unique hash-based condition syntax to cover more of the use-cases where
|
114
|
+
issuing your own SQL would have been the only way to go. For example, any
|
115
|
+
finder option that's non-standard is considered a condition. So you can write
|
116
|
+
<tt>Zoo.all(:name => 'Dallas')</tt> and DataMapper will look for zoos with the
|
117
|
+
name of 'Dallas'.
|
118
|
+
|
119
|
+
It's just a little thing, but it's so much nicer than writing
|
120
|
+
<tt>Zoo.find(:all, :conditions => ['name = ?', 'Dallas'])</tt>. What if you
|
121
|
+
need other comparisons though? Try these:
|
122
|
+
|
123
|
+
Zoo.first(:name => 'Galveston')
|
124
|
+
|
125
|
+
# 'gt' means greater-than. We also do 'lt'.
|
126
|
+
Person.all(:age.gt => 30)
|
127
|
+
|
128
|
+
# 'gte' means greather-than-or-equal-to. We also do 'lte'.
|
129
|
+
Person.all(:age.gte => 30)
|
130
|
+
|
131
|
+
Person.all(:name.not => 'bob')
|
132
|
+
|
133
|
+
# If the value of a pair is an Array, we do an IN-clause for you.
|
134
|
+
Person.all(:name.like => 'S%', :id => [1, 2, 3, 4, 5])
|
135
|
+
|
136
|
+
# An alias for Zoo.find(11)
|
137
|
+
Zoo[11]
|
138
|
+
|
139
|
+
# Does a NOT IN () clause for you.
|
140
|
+
Person.all(:name.not => ['bob','rick','steve'])
|
141
|
+
|
142
|
+
See? Fewer SQL fragments dirtying your Ruby code. And that's just a few of the
|
143
|
+
nice syntax tweaks DataMapper delivers out of the box...
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'pathname'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake'
|
5
|
+
require 'rake/rdoctask'
|
6
|
+
require 'spec/rake/spectask'
|
7
|
+
|
8
|
+
require 'lib/dm-core/version'
|
9
|
+
|
10
|
+
ROOT = Pathname(__FILE__).dirname.expand_path
|
11
|
+
|
12
|
+
AUTHOR = "Sam Smoot"
|
13
|
+
EMAIL = "ssmoot@gmail.com"
|
14
|
+
GEM_NAME = "dm-core"
|
15
|
+
GEM_VERSION = DataMapper::VERSION
|
16
|
+
GEM_DEPENDENCIES = ["data_objects", ">=0.9.5"], ["extlib", ">=0.9.5"],
|
17
|
+
["rspec", ">=1.1.3"], ["addressable", ">=1.0.4"]
|
18
|
+
|
19
|
+
|
20
|
+
PROJECT_NAME = "datamapper"
|
21
|
+
PROJECT_DESCRIPTION = "Faster, Better, Simpler."
|
22
|
+
PROJECT_SUMMARY = "An Object/Relational Mapper for Ruby"
|
23
|
+
PROJECT_URL = "http://datamapper.org"
|
24
|
+
|
25
|
+
require ROOT + 'tasks/hoe'
|
26
|
+
require ROOT + 'tasks/gemspec'
|
27
|
+
require ROOT + 'tasks/install'
|
28
|
+
require ROOT + 'tasks/dm'
|
29
|
+
require ROOT + 'tasks/doc'
|
30
|
+
require ROOT + 'tasks/ci'
|
data/SPECS
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
Reading Specs
|
2
|
+
=============
|
3
|
+
|
4
|
+
Blah blah blah...
|
5
|
+
|
6
|
+
Writing Specs
|
7
|
+
=============
|
8
|
+
|
9
|
+
Here are some general dos and don'ts
|
10
|
+
|
11
|
+
= DO:
|
12
|
+
|
13
|
+
* Write more specs for error conditions than clean conditions.
|
14
|
+
* Write specs with readability in mind. Somebody knew to DataMapper should be
|
15
|
+
able to read specs to learn how something works.
|
16
|
+
* Use existing models that are part of a metaphor.
|
17
|
+
* Nest describe blocks (2 or 3 levels deep is probably fine).
|
18
|
+
* Limit a describe block to 10 - 15 examples.
|
19
|
+
* Group specs by method being tested. (See the 'Ordering Specs' section)
|
20
|
+
* Use custom matchers.
|
21
|
+
|
22
|
+
= DON'T:
|
23
|
+
|
24
|
+
* Spec more than one unit of functionality in an example. An example should be
|
25
|
+
as short as possible (while still remaining readable).
|
26
|
+
* Spec implementation. Refactoring code should not break specs.
|
27
|
+
* Declare models in the spec file.
|
28
|
+
|
29
|
+
And a final do: Do go against the guidelines if your best judgement tells you
|
30
|
+
to. These are just guidelines and are obviously not fast rules.
|
31
|
+
|
32
|
+
Models
|
33
|
+
======
|
34
|
+
|
35
|
+
Models are declared in separate files as opposed to individual spec files for
|
36
|
+
two reasons. The first is to improve readability. By creating as few models
|
37
|
+
as possible and sharing these models throughout the specs, a reader can
|
38
|
+
become familiar with the models being used quicker. Models also follow a
|
39
|
+
few simple metaphors, such as a zoo, a blog implementation, etc... Following
|
40
|
+
metaphors makes it easier for a reader to guess what is going on with respect
|
41
|
+
to the models.
|
42
|
+
|
43
|
+
The second reason is to allow the spec environment to be as pristine as
|
44
|
+
possible going into an example. Models being loaded from the model directory
|
45
|
+
are tracked and reloaded before each example. Any changes that might be made
|
46
|
+
to the model are reset at the end.
|
47
|
+
|
48
|
+
Mocks and Stubs
|
49
|
+
===============
|
50
|
+
|
51
|
+
Obviously, mocks and stubs are a powerful feature when it comes to BDD;
|
52
|
+
however, remember that you are writing specs for behavior and NOT
|
53
|
+
implementation.
|
54
|
+
|
55
|
+
Ordering Specs
|
56
|
+
==============
|
57
|
+
|
58
|
+
Specs aren't much use if nobody can find where anything is, so keeping specs
|
59
|
+
well organized is critical. Currently, we are trying out the following
|
60
|
+
structure:
|
61
|
+
|
62
|
+
* List guidelines here...
|
63
|
+
|
data/lib/dm-core.rb
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
# This file begins the loading sequence.
|
2
|
+
#
|
3
|
+
# Quick Overview:
|
4
|
+
# * Requires fastthread, support libs, and base.
|
5
|
+
# * Sets the application root and environment for compatibility with frameworks
|
6
|
+
# such as Rails or Merb.
|
7
|
+
# * Checks for the database.yml and loads it if it exists.
|
8
|
+
# * Sets up the database using the config from the Yaml file or from the
|
9
|
+
# environment.
|
10
|
+
#
|
11
|
+
|
12
|
+
require 'date'
|
13
|
+
require 'pathname'
|
14
|
+
require 'set'
|
15
|
+
require 'time'
|
16
|
+
require 'yaml'
|
17
|
+
|
18
|
+
require 'rubygems'
|
19
|
+
|
20
|
+
gem 'addressable', '>=1.0.4'
|
21
|
+
require 'addressable/uri'
|
22
|
+
|
23
|
+
gem 'extlib', '>=0.9.5'
|
24
|
+
require 'extlib'
|
25
|
+
require "extlib/inflection"
|
26
|
+
|
27
|
+
begin
|
28
|
+
require 'fastthread'
|
29
|
+
rescue LoadError
|
30
|
+
# fastthread not installed
|
31
|
+
end
|
32
|
+
|
33
|
+
dir = Pathname(__FILE__).dirname.expand_path / 'dm-core'
|
34
|
+
|
35
|
+
require dir / 'support'
|
36
|
+
require dir / 'resource'
|
37
|
+
require dir / 'model'
|
38
|
+
|
39
|
+
require dir / 'dependency_queue'
|
40
|
+
require dir / 'type'
|
41
|
+
require dir / 'type_map'
|
42
|
+
require dir / 'types'
|
43
|
+
require dir / 'hook'
|
44
|
+
require dir / 'associations'
|
45
|
+
require dir / 'auto_migrations'
|
46
|
+
require dir / 'identity_map'
|
47
|
+
require dir / 'logger'
|
48
|
+
require dir / 'migrator'
|
49
|
+
require dir / 'naming_conventions'
|
50
|
+
require dir / 'property_set'
|
51
|
+
require dir / 'query'
|
52
|
+
require dir / 'transaction'
|
53
|
+
require dir / 'repository'
|
54
|
+
require dir / 'scope'
|
55
|
+
require dir / 'property'
|
56
|
+
require dir / 'adapters'
|
57
|
+
require dir / 'collection'
|
58
|
+
require dir / 'is'
|
59
|
+
|
60
|
+
# == Setup and Configuration
|
61
|
+
# DataMapper uses URIs or a connection hash to connect to your data-store.
|
62
|
+
# URI connections takes the form of:
|
63
|
+
# DataMapper.setup(:default, 'protocol://username:password@localhost:port/path/to/repo')
|
64
|
+
#
|
65
|
+
# Breaking this down, the first argument is the name you wish to give this
|
66
|
+
# connection. If you do not specify one, it will be assigned :default. If you
|
67
|
+
# would like to connect to more than one data-store, simply issue this command
|
68
|
+
# again, but with a different name specified.
|
69
|
+
#
|
70
|
+
# In order to issue ORM commands without specifying the repository context, you
|
71
|
+
# must define the :default database. Otherwise, you'll need to wrap your ORM
|
72
|
+
# calls in <tt>repository(:name) { }</tt>.
|
73
|
+
#
|
74
|
+
# Second, the URI breaks down into the access protocol, the username, the
|
75
|
+
# server, the password, and whatever path information is needed to properly
|
76
|
+
# address the data-store on the server.
|
77
|
+
#
|
78
|
+
# Here's some examples
|
79
|
+
# DataMapper.setup(:default, "sqlite3://path/to/your/project/db/development.db")
|
80
|
+
# DataMapper.setup(:default, "mysql://localhost/dm_core_test")
|
81
|
+
# # no auth-info
|
82
|
+
# DataMapper.setup(:default, "postgres://root:supahsekret@127.0.0.1/dm_core_test")
|
83
|
+
# # with auth-info
|
84
|
+
#
|
85
|
+
#
|
86
|
+
# Alternatively, you can supply a hash as the second parameter, which would
|
87
|
+
# take the form:
|
88
|
+
#
|
89
|
+
# DataMapper.setup(:default, {
|
90
|
+
# :adapter => 'adapter_name_here',
|
91
|
+
# :database => "path/to/repo",
|
92
|
+
# :username => 'username',
|
93
|
+
# :password => 'password',
|
94
|
+
# :host => 'hostname'
|
95
|
+
# })
|
96
|
+
#
|
97
|
+
# === Logging
|
98
|
+
# To turn on error logging to STDOUT, issue:
|
99
|
+
#
|
100
|
+
# DataMapper::Logger.new(STDOUT, 0)
|
101
|
+
#
|
102
|
+
# You can pass a file location ("/path/to/log/file.log") in place of STDOUT.
|
103
|
+
# see DataMapper::Logger for more information.
|
104
|
+
#
|
105
|
+
module DataMapper
|
106
|
+
extend Assertions
|
107
|
+
|
108
|
+
def self.root
|
109
|
+
@root ||= Pathname(__FILE__).dirname.parent.expand_path
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
# Setups up a connection to a data-store
|
114
|
+
#
|
115
|
+
# @param Symbol name a name for the context, defaults to :default
|
116
|
+
# @param [Hash{Symbol => String}, Addressable::URI, String] uri_or_options
|
117
|
+
# connection information
|
118
|
+
#
|
119
|
+
# @return Repository the resulting setup repository
|
120
|
+
#
|
121
|
+
# @raise ArgumentError "+name+ must be a Symbol, but was..." indicates that
|
122
|
+
# an invalid argument was passed for name[Symbol]
|
123
|
+
# @raise [ArgumentError] "+uri_or_options+ must be a Hash, URI or String,
|
124
|
+
# but was..." indicates that connection information could not be gleaned
|
125
|
+
# from the given uri_or_options<Hash, Addressable::URI, String>
|
126
|
+
#
|
127
|
+
# -
|
128
|
+
# @api public
|
129
|
+
def self.setup(name, uri_or_options)
|
130
|
+
assert_kind_of 'name', name, Symbol
|
131
|
+
assert_kind_of 'uri_or_options', uri_or_options, Addressable::URI, Hash, String
|
132
|
+
|
133
|
+
case uri_or_options
|
134
|
+
when Hash
|
135
|
+
adapter_name = uri_or_options[:adapter].to_s
|
136
|
+
when String, Addressable::URI
|
137
|
+
uri_or_options = Addressable::URI.parse(uri_or_options) if uri_or_options.kind_of?(String)
|
138
|
+
adapter_name = uri_or_options.scheme
|
139
|
+
end
|
140
|
+
|
141
|
+
class_name = Extlib::Inflection.classify(adapter_name) + 'Adapter'
|
142
|
+
|
143
|
+
unless Adapters::const_defined?(class_name)
|
144
|
+
lib_name = "#{Extlib::Inflection.underscore(adapter_name)}_adapter"
|
145
|
+
begin
|
146
|
+
require root / 'lib' / 'dm-core' / 'adapters' / lib_name
|
147
|
+
rescue LoadError => e
|
148
|
+
begin
|
149
|
+
require lib_name
|
150
|
+
rescue Exception
|
151
|
+
# library not found, raise the original error
|
152
|
+
raise e
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
Repository.adapters[name] = Adapters::const_get(class_name).new(name, uri_or_options)
|
158
|
+
end
|
159
|
+
|
160
|
+
##
|
161
|
+
# Block Syntax
|
162
|
+
# Pushes the named repository onto the context-stack,
|
163
|
+
# yields a new session, and pops the context-stack.
|
164
|
+
#
|
165
|
+
# Non-Block Syntax
|
166
|
+
# Returns the current session, or if there is none,
|
167
|
+
# a new Session.
|
168
|
+
#
|
169
|
+
# @param [Symbol] args the name of a repository to act within or return, :default is default
|
170
|
+
# @yield [Proc] (optional) block to execute within the context of the named repository
|
171
|
+
# @demo spec/integration/repository_spec.rb
|
172
|
+
def self.repository(*args, &block) # :yields: current_context
|
173
|
+
if args.size > 1
|
174
|
+
raise ArgumentError, "Can only pass in one optional argument, but passed in #{args.size} arguments", caller
|
175
|
+
end
|
176
|
+
|
177
|
+
if args.any? && !args.first.kind_of?(Symbol)
|
178
|
+
raise ArgumentError, "First optional argument must be a Symbol, but was #{args.first.inspect}", caller
|
179
|
+
end
|
180
|
+
|
181
|
+
name = args.first
|
182
|
+
|
183
|
+
current_repository = if name
|
184
|
+
Repository.context.detect { |r| r.name == name } || Repository.new(name)
|
185
|
+
else
|
186
|
+
Repository.context.last || Repository.new(Repository.default_name)
|
187
|
+
end
|
188
|
+
|
189
|
+
return current_repository unless block_given?
|
190
|
+
|
191
|
+
current_repository.scope(&block)
|
192
|
+
end
|
193
|
+
|
194
|
+
# A logger should always be present. Lets be consistent with DO
|
195
|
+
Logger.new(nil, :off)
|
196
|
+
|
197
|
+
##
|
198
|
+
# destructively migrates the repository upwards to match model definitions
|
199
|
+
#
|
200
|
+
# @param [Symbol] name repository to act on, :default is the default
|
201
|
+
def self.migrate!(name = Repository.default_name)
|
202
|
+
repository(name).migrate!
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# drops and recreates the repository upwards to match model definitions
|
207
|
+
#
|
208
|
+
# @param [Symbol] name repository to act on, :default is the default
|
209
|
+
def self.auto_migrate!(repository_name = nil)
|
210
|
+
AutoMigrator.auto_migrate(repository_name)
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.auto_upgrade!(repository_name = nil)
|
214
|
+
AutoMigrator.auto_upgrade(repository_name)
|
215
|
+
end
|
216
|
+
|
217
|
+
def self.prepare(*args, &blk)
|
218
|
+
yield repository(*args)
|
219
|
+
end
|
220
|
+
|
221
|
+
def self.dependency_queue
|
222
|
+
@dependency_queue ||= DependencyQueue.new
|
223
|
+
end
|
224
|
+
end
|