sam-dm-core 0.9.6
Sign up to get free protection for your applications and to get access to all the features.
- 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
|