datamapper 0.3.2 → 0.9.3
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/History.txt +0 -0
- data/Manifest.txt +5 -0
- data/README.txt +11 -0
- data/Rakefile +70 -0
- data/lib/datamapper.rb +8 -0
- metadata +152 -319
- data/CHANGELOG +0 -145
- data/FAQ +0 -96
- data/MIT-LICENSE +0 -22
- data/QUICKLINKS +0 -12
- data/README +0 -105
- data/environment.rb +0 -62
- data/example.rb +0 -156
- data/lib/data_mapper.rb +0 -88
- data/lib/data_mapper/adapters/abstract_adapter.rb +0 -43
- data/lib/data_mapper/adapters/data_object_adapter.rb +0 -480
- data/lib/data_mapper/adapters/mysql_adapter.rb +0 -72
- data/lib/data_mapper/adapters/postgresql_adapter.rb +0 -258
- data/lib/data_mapper/adapters/sql/coersion.rb +0 -134
- data/lib/data_mapper/adapters/sql/commands/load_command.rb +0 -545
- data/lib/data_mapper/adapters/sql/mappings/associations_set.rb +0 -34
- data/lib/data_mapper/adapters/sql/mappings/column.rb +0 -279
- data/lib/data_mapper/adapters/sql/mappings/conditions.rb +0 -172
- data/lib/data_mapper/adapters/sql/mappings/schema.rb +0 -60
- data/lib/data_mapper/adapters/sql/mappings/table.rb +0 -459
- data/lib/data_mapper/adapters/sql/quoting.rb +0 -24
- data/lib/data_mapper/adapters/sqlite3_adapter.rb +0 -159
- data/lib/data_mapper/associations.rb +0 -106
- data/lib/data_mapper/associations/belongs_to_association.rb +0 -160
- data/lib/data_mapper/associations/has_and_belongs_to_many_association.rb +0 -437
- data/lib/data_mapper/associations/has_many_association.rb +0 -283
- data/lib/data_mapper/associations/has_n_association.rb +0 -143
- data/lib/data_mapper/associations/reference.rb +0 -47
- data/lib/data_mapper/attributes.rb +0 -73
- data/lib/data_mapper/auto_migrations.rb +0 -36
- data/lib/data_mapper/base.rb +0 -17
- data/lib/data_mapper/callbacks.rb +0 -107
- data/lib/data_mapper/context.rb +0 -112
- data/lib/data_mapper/database.rb +0 -234
- data/lib/data_mapper/dependency_queue.rb +0 -28
- data/lib/data_mapper/embedded_value.rb +0 -145
- data/lib/data_mapper/identity_map.rb +0 -47
- data/lib/data_mapper/is/tree.rb +0 -121
- data/lib/data_mapper/migration.rb +0 -155
- data/lib/data_mapper/persistence.rb +0 -852
- data/lib/data_mapper/property.rb +0 -310
- data/lib/data_mapper/query.rb +0 -164
- data/lib/data_mapper/support/blank.rb +0 -35
- data/lib/data_mapper/support/connection_pool.rb +0 -117
- data/lib/data_mapper/support/enumerable.rb +0 -35
- data/lib/data_mapper/support/errors.rb +0 -16
- data/lib/data_mapper/support/inflector.rb +0 -265
- data/lib/data_mapper/support/object.rb +0 -54
- data/lib/data_mapper/support/serialization.rb +0 -96
- data/lib/data_mapper/support/silence.rb +0 -10
- data/lib/data_mapper/support/string.rb +0 -72
- data/lib/data_mapper/support/struct.rb +0 -7
- data/lib/data_mapper/support/symbol.rb +0 -82
- data/lib/data_mapper/support/typed_set.rb +0 -65
- data/lib/data_mapper/types/base.rb +0 -44
- data/lib/data_mapper/types/string.rb +0 -34
- data/lib/data_mapper/validatable_extensions/errors.rb +0 -12
- data/lib/data_mapper/validatable_extensions/macros.rb +0 -7
- data/lib/data_mapper/validatable_extensions/validatable_instance_methods.rb +0 -62
- data/lib/data_mapper/validatable_extensions/validation_base.rb +0 -18
- data/lib/data_mapper/validatable_extensions/validations/formats/email.rb +0 -43
- data/lib/data_mapper/validatable_extensions/validations/validates_acceptance_of.rb +0 -7
- data/lib/data_mapper/validatable_extensions/validations/validates_confirmation_of.rb +0 -7
- data/lib/data_mapper/validatable_extensions/validations/validates_each.rb +0 -7
- data/lib/data_mapper/validatable_extensions/validations/validates_format_of.rb +0 -28
- data/lib/data_mapper/validatable_extensions/validations/validates_length_of.rb +0 -15
- data/lib/data_mapper/validatable_extensions/validations/validates_numericality_of.rb +0 -7
- data/lib/data_mapper/validatable_extensions/validations/validates_presence_of.rb +0 -7
- data/lib/data_mapper/validatable_extensions/validations/validates_true_for.rb +0 -7
- data/lib/data_mapper/validatable_extensions/validations/validates_uniqueness_of.rb +0 -40
- data/lib/data_mapper/validations.rb +0 -20
- data/lib/data_mapper/validations/number_validator.rb +0 -40
- data/lib/data_mapper/validations/string_validator.rb +0 -20
- data/lib/data_mapper/validations/validator.rb +0 -13
- data/performance.rb +0 -307
- data/plugins/can_has_sphinx/LICENSE +0 -23
- data/plugins/can_has_sphinx/README +0 -4
- data/plugins/can_has_sphinx/REVISION +0 -1
- data/plugins/can_has_sphinx/Rakefile +0 -22
- data/plugins/can_has_sphinx/init.rb +0 -1
- data/plugins/can_has_sphinx/install.rb +0 -1
- data/plugins/can_has_sphinx/lib/acts_as_sphinx.rb +0 -123
- data/plugins/can_has_sphinx/lib/sphinx.rb +0 -460
- data/plugins/can_has_sphinx/scripts/sphinx.sh +0 -47
- data/plugins/can_has_sphinx/tasks/acts_as_sphinx_tasks.rake +0 -41
- data/profile_data_mapper.rb +0 -40
- data/rakefile.rb +0 -159
- data/spec/acts_as_tree_spec.rb +0 -67
- data/spec/adapters/data_object_adapter_spec.rb +0 -31
- data/spec/associations/belongs_to_association_spec.rb +0 -98
- data/spec/associations/has_and_belongs_to_many_association_spec.rb +0 -377
- data/spec/associations/has_many_association_spec.rb +0 -337
- data/spec/attributes_spec.rb +0 -52
- data/spec/auto_migrations_spec.rb +0 -101
- data/spec/callbacks_spec.rb +0 -186
- data/spec/can_has_sphinx.rb +0 -5
- data/spec/coersion_spec.rb +0 -41
- data/spec/column_spec.rb +0 -114
- data/spec/count_command_spec.rb +0 -45
- data/spec/database_spec.rb +0 -18
- data/spec/dataobjects_spec.rb +0 -27
- data/spec/delete_command_spec.rb +0 -11
- data/spec/dependency_spec.rb +0 -29
- data/spec/embedded_value_spec.rb +0 -161
- data/spec/fixtures/animals.yaml +0 -33
- data/spec/fixtures/animals_exhibits.yaml +0 -2
- data/spec/fixtures/careers.yaml +0 -5
- data/spec/fixtures/comments.yaml +0 -1
- data/spec/fixtures/exhibits.yaml +0 -90
- data/spec/fixtures/fruit.yaml +0 -6
- data/spec/fixtures/people.yaml +0 -37
- data/spec/fixtures/posts.yaml +0 -3
- data/spec/fixtures/projects.yaml +0 -13
- data/spec/fixtures/sections.yaml +0 -5
- data/spec/fixtures/serializers.yaml +0 -6
- data/spec/fixtures/tasks.yaml +0 -6
- data/spec/fixtures/tasks_tasks.yaml +0 -2
- data/spec/fixtures/tomatoes.yaml +0 -1
- data/spec/fixtures/users.yaml +0 -1
- data/spec/fixtures/zoos.yaml +0 -24
- data/spec/is_a_tree_spec.rb +0 -149
- data/spec/legacy_spec.rb +0 -16
- data/spec/load_command_spec.rb +0 -322
- data/spec/magic_columns_spec.rb +0 -26
- data/spec/migration_spec.rb +0 -267
- data/spec/mock_adapter.rb +0 -20
- data/spec/models/animal.rb +0 -12
- data/spec/models/candidate.rb +0 -8
- data/spec/models/career.rb +0 -7
- data/spec/models/chain.rb +0 -8
- data/spec/models/comment.rb +0 -6
- data/spec/models/exhibit.rb +0 -14
- data/spec/models/fence.rb +0 -7
- data/spec/models/fruit.rb +0 -8
- data/spec/models/job.rb +0 -8
- data/spec/models/person.rb +0 -30
- data/spec/models/post.rb +0 -14
- data/spec/models/project.rb +0 -41
- data/spec/models/sales_person.rb +0 -5
- data/spec/models/section.rb +0 -8
- data/spec/models/serializer.rb +0 -5
- data/spec/models/task.rb +0 -9
- data/spec/models/tomato.rb +0 -27
- data/spec/models/user.rb +0 -12
- data/spec/models/zoo.rb +0 -13
- data/spec/natural_key_spec.rb +0 -36
- data/spec/paranoia_spec.rb +0 -38
- data/spec/persistence_spec.rb +0 -479
- data/spec/postgres_spec.rb +0 -96
- data/spec/property_spec.rb +0 -151
- data/spec/query_spec.rb +0 -77
- data/spec/save_command_spec.rb +0 -94
- data/spec/schema_spec.rb +0 -8
- data/spec/serialize_spec.rb +0 -19
- data/spec/single_table_inheritance_spec.rb +0 -43
- data/spec/spec_helper.rb +0 -45
- data/spec/support/blank_spec.rb +0 -8
- data/spec/support/inflector_spec.rb +0 -41
- data/spec/support/object_spec.rb +0 -9
- data/spec/support/serialization_spec.rb +0 -61
- data/spec/support/silence_spec.rb +0 -15
- data/spec/support/string_spec.rb +0 -7
- data/spec/support/struct_spec.rb +0 -12
- data/spec/support/typed_set_spec.rb +0 -66
- data/spec/symbolic_operators_spec.rb +0 -27
- data/spec/table_spec.rb +0 -79
- data/spec/types/string.rb +0 -81
- data/spec/validates_confirmation_of_spec.rb +0 -55
- data/spec/validates_format_of_spec.rb +0 -78
- data/spec/validates_length_of_spec.rb +0 -117
- data/spec/validates_uniqueness_of_spec.rb +0 -92
- data/spec/validations/number_validator.rb +0 -59
- data/spec/validations/string_validator.rb +0 -14
- data/spec/validations_spec.rb +0 -141
- data/tasks/fixtures.rb +0 -53
data/example.rb
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
ENV['LOG_NAME'] ||= 'example'
|
|
4
|
-
require 'environment'
|
|
5
|
-
|
|
6
|
-
# Define a fixtures helper method to load up our test data.
|
|
7
|
-
def fixtures(name)
|
|
8
|
-
entry = YAML::load_file(File.dirname(__FILE__) + "/spec/fixtures/#{name}.yaml")
|
|
9
|
-
klass = begin
|
|
10
|
-
Kernel::const_get(Inflector.classify(Inflector.singularize(name)))
|
|
11
|
-
rescue
|
|
12
|
-
nil
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
unless klass.nil?
|
|
16
|
-
database.logger.debug { "AUTOMIGRATE: #{klass}" }
|
|
17
|
-
klass.auto_migrate!
|
|
18
|
-
|
|
19
|
-
(entry.kind_of?(Array) ? entry : [entry]).each do |hash|
|
|
20
|
-
if hash['type']
|
|
21
|
-
Object::const_get(hash['type'])::create(hash)
|
|
22
|
-
else
|
|
23
|
-
klass::create(hash)
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
else
|
|
27
|
-
table = database.table(name.to_s)
|
|
28
|
-
table.create! true
|
|
29
|
-
table.activate_associations!
|
|
30
|
-
|
|
31
|
-
#pp database.schema
|
|
32
|
-
|
|
33
|
-
(entry.kind_of?(Array) ? entry : [entry]).each do |hash|
|
|
34
|
-
table.insert(hash)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
# Pre-fill the database so non-destructive tests don't need to reload fixtures.
|
|
41
|
-
Dir[File.dirname(__FILE__) + "/spec/fixtures/*.yaml"].each do |path|
|
|
42
|
-
fixtures(File::basename(path).sub(/\.yaml$/, ''))
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
require 'irb'
|
|
46
|
-
|
|
47
|
-
# database { IRB::start }
|
|
48
|
-
IRB::start
|
|
49
|
-
|
|
50
|
-
if false
|
|
51
|
-
|
|
52
|
-
# Simple example to setup a database:
|
|
53
|
-
DataMapper::Database.setup({
|
|
54
|
-
:adapter => 'mysql',
|
|
55
|
-
:database => 'data_mapper_1',
|
|
56
|
-
:username => 'root'
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
class Animal #:nodoc:
|
|
60
|
-
include DataMapper::Persistence
|
|
61
|
-
|
|
62
|
-
set_table_name 'animals' # Just as an example. Same inflector as Rails,
|
|
63
|
-
# so this really isn't necessary.
|
|
64
|
-
|
|
65
|
-
property :name, :string
|
|
66
|
-
property :notes, :string, :lazy => true
|
|
67
|
-
|
|
68
|
-
has_and_belongs_to_many :exhibits
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
class Exhibit #:nodoc:
|
|
72
|
-
include DataMapper::Persistence
|
|
73
|
-
|
|
74
|
-
property :name, :string
|
|
75
|
-
belongs_to :zoo
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
class Zoo #:nodoc:
|
|
79
|
-
include DataMapper::Persistence
|
|
80
|
-
|
|
81
|
-
property :name, :string
|
|
82
|
-
has_many :exhibits
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
class Person #:nodoc:
|
|
86
|
-
include DataMapper::Persistence
|
|
87
|
-
|
|
88
|
-
property :name, :string
|
|
89
|
-
property :age, :integer
|
|
90
|
-
property :occupation, :string
|
|
91
|
-
property :notes, :text, :lazy => true
|
|
92
|
-
|
|
93
|
-
# Generates Person::Address class:
|
|
94
|
-
embed :address do
|
|
95
|
-
property :street, :string
|
|
96
|
-
property :city, :string
|
|
97
|
-
property :state, :string, :size => 2
|
|
98
|
-
property :postal_code, :string
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
# Compatible with ActiveRecord finder syntax:
|
|
103
|
-
Zoo.find(1)
|
|
104
|
-
Zoo.find(:first, :conditions => ['name = ?', 'Galveston'])
|
|
105
|
-
Zoo.find(:all)
|
|
106
|
-
|
|
107
|
-
# These are options as well:
|
|
108
|
-
Zoo[1]
|
|
109
|
-
Zoo.first(:name => 'Galveston')
|
|
110
|
-
Zoo.all
|
|
111
|
-
|
|
112
|
-
# Or even this as an alias to ::first:
|
|
113
|
-
Zoo[:name => 'Galveston']
|
|
114
|
-
|
|
115
|
-
# EmbeddedValues are just nice sugar to partition
|
|
116
|
-
# denormalized data.
|
|
117
|
-
Person.first.address.city
|
|
118
|
-
|
|
119
|
-
# Remove all data in a table...
|
|
120
|
-
Person.truncate!
|
|
121
|
-
|
|
122
|
-
# Create a new object...
|
|
123
|
-
Person::create(:name => 'Sam', :age => 30, :occupation => 'Software Monkey')
|
|
124
|
-
|
|
125
|
-
# Saving only updates the values that have changed,
|
|
126
|
-
# and is skipped entirely if the object is not dirty.
|
|
127
|
-
dumbo = Animal.first(:name => 'Elephant')
|
|
128
|
-
dumbo.notes = 'He can fly!'
|
|
129
|
-
dumbo.save # returns true
|
|
130
|
-
dumbo.save # The object is no longer dirty, so returns false
|
|
131
|
-
|
|
132
|
-
# DataMapper associations are loaded as sets.
|
|
133
|
-
# Here's the code:
|
|
134
|
-
Zoo.all.each { |zoo| zoo.exhibits.entries }
|
|
135
|
-
# The important bit to understand about the above is that
|
|
136
|
-
# every Zoo that was loaded by Zoo.all has a reference to
|
|
137
|
-
# every other Zoo it was loaded with through Zoo#loaded_set.
|
|
138
|
-
# This is then used to load all other instances in the set
|
|
139
|
-
# when the association of one instance is accessed. So while
|
|
140
|
-
# it looks like we'd run into the dreaded 1+N query problem
|
|
141
|
-
# with the above, we actually avoid it entirely. The above
|
|
142
|
-
# code will only execute two queries. The first to find all
|
|
143
|
-
# zoos, the second to load all exhibits with zoo_id's that
|
|
144
|
-
# are a part of the set of loaded zoos.
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
# Objects within the same session are uniqued, so this is both
|
|
148
|
-
# faster, and fulfills obvious expectations.
|
|
149
|
-
database do
|
|
150
|
-
Zoo.first == Zoo.first
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
# DataMapper find_by_sql equivilent
|
|
154
|
-
database.query("SELECT * FROM zoos")
|
|
155
|
-
|
|
156
|
-
end
|
data/lib/data_mapper.rb
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
# This file begins the loading sequence.
|
|
2
|
-
#
|
|
3
|
-
# Quick Overview:
|
|
4
|
-
# * Requires set, fastthread, support libs, and base
|
|
5
|
-
# * Sets the applications root and environment for compatibility with rails or merb
|
|
6
|
-
# * Checks for the database.yml and loads it if it exists
|
|
7
|
-
# * Sets up the database using the config from the yaml file or from the environment
|
|
8
|
-
# *
|
|
9
|
-
|
|
10
|
-
# This line just let's us require anything in the +lib+ sub-folder
|
|
11
|
-
# without specifying a full path.
|
|
12
|
-
unless defined?(DM_PLUGINS_ROOT)
|
|
13
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
14
|
-
|
|
15
|
-
DM_PLUGINS_ROOT = (File.dirname(__FILE__) + '/../plugins')
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
# Require the basics...
|
|
19
|
-
require 'date'
|
|
20
|
-
require 'time'
|
|
21
|
-
require 'rubygems'
|
|
22
|
-
require 'yaml'
|
|
23
|
-
require 'set'
|
|
24
|
-
require 'fastthread'
|
|
25
|
-
require 'validatable'
|
|
26
|
-
require 'data_mapper/support/object'
|
|
27
|
-
require 'data_mapper/support/blank'
|
|
28
|
-
require 'data_mapper/support/enumerable'
|
|
29
|
-
require 'data_mapper/support/symbol'
|
|
30
|
-
require 'data_mapper/support/string'
|
|
31
|
-
require 'data_mapper/support/silence'
|
|
32
|
-
require 'data_mapper/support/inflector'
|
|
33
|
-
require 'data_mapper/support/errors'
|
|
34
|
-
require 'data_mapper/support/typed_set'
|
|
35
|
-
require 'data_mapper/database'
|
|
36
|
-
require 'data_mapper/persistence'
|
|
37
|
-
require 'data_mapper/base'
|
|
38
|
-
require 'data_mapper/types/string'
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
begin
|
|
42
|
-
# This block of code is for compatibility with Ruby On Rails' or Merb's database.yml
|
|
43
|
-
# file, allowing you to simply require the data_mapper.rb in your
|
|
44
|
-
# Rails application's environment.rb to configure the DataMapper.
|
|
45
|
-
unless defined?(DM_APP_ROOT)
|
|
46
|
-
application_root, application_environment = *if defined?(RAILS_ROOT)
|
|
47
|
-
[RAILS_ROOT, RAILS_ENV]
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
DM_APP_ROOT = application_root || Dir::pwd
|
|
51
|
-
|
|
52
|
-
if application_root && File.exists?(application_root + '/config/database.yml')
|
|
53
|
-
|
|
54
|
-
database_configurations = YAML::load_file(application_root + '/config/database.yml')
|
|
55
|
-
current_database_config = database_configurations[application_environment] || database_configurations[application_environment.to_sym]
|
|
56
|
-
|
|
57
|
-
config = lambda { |key| current_database_config[key.to_s] || current_database_config[key] }
|
|
58
|
-
|
|
59
|
-
default_database_config = {
|
|
60
|
-
:adapter => config[:adapter],
|
|
61
|
-
:host => config[:host],
|
|
62
|
-
:database => config[:database],
|
|
63
|
-
:username => config[:username],
|
|
64
|
-
:password => config[:password],
|
|
65
|
-
:socket => config[:socket]
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
DataMapper::Database.setup(default_database_config)
|
|
69
|
-
|
|
70
|
-
elsif application_root && FileTest.directory?(application_root + '/config')
|
|
71
|
-
|
|
72
|
-
%w(development testing production).map do |environment|
|
|
73
|
-
<<-EOS.margin
|
|
74
|
-
#{environment}:
|
|
75
|
-
adapter: mysql
|
|
76
|
-
username: root
|
|
77
|
-
password:
|
|
78
|
-
host: localhost
|
|
79
|
-
database: #{File.dirname(DM_APP_ROOT).split('/').last}_#{environment}
|
|
80
|
-
EOS
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
#File::open(application_root + '/config/database.yml')
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
rescue Exception
|
|
87
|
-
warn "Could not connect to database specified by database.yml."
|
|
88
|
-
end
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
module DataMapper
|
|
2
|
-
module Adapters
|
|
3
|
-
|
|
4
|
-
class AbstractAdapter
|
|
5
|
-
|
|
6
|
-
# Instantiate an Adapter by passing it a DataMapper::Database
|
|
7
|
-
# object for configuration.
|
|
8
|
-
def initialize(configuration)
|
|
9
|
-
@configuration = configuration
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def index_path
|
|
13
|
-
@configuration.index_path
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def name
|
|
17
|
-
@configuration.name
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def delete(instance_or_klass, options = nil)
|
|
21
|
-
raise NotImplementedError.new
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def save(database_context, instance)
|
|
25
|
-
raise NotImplementedError.new
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def load(database_context, klass, options)
|
|
29
|
-
raise NotImplementedError.new
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def get(database_context, klass, *keys)
|
|
33
|
-
raise NotImplementedError.new
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def logger
|
|
37
|
-
@logger || @logger = @configuration.logger
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
end # class AbstractAdapter
|
|
41
|
-
|
|
42
|
-
end # module Adapters
|
|
43
|
-
end # module DataMapper
|
|
@@ -1,480 +0,0 @@
|
|
|
1
|
-
require 'data_mapper/adapters/abstract_adapter'
|
|
2
|
-
require 'data_mapper/adapters/sql/commands/load_command'
|
|
3
|
-
require 'data_mapper/adapters/sql/coersion'
|
|
4
|
-
require 'data_mapper/adapters/sql/quoting'
|
|
5
|
-
require 'data_mapper/adapters/sql/mappings/schema'
|
|
6
|
-
require 'data_mapper/support/connection_pool'
|
|
7
|
-
require 'data_mapper/query'
|
|
8
|
-
|
|
9
|
-
module DataMapper
|
|
10
|
-
|
|
11
|
-
# An Adapter is really a Factory for three types of object,
|
|
12
|
-
# so they can be selectively sub-classed where needed.
|
|
13
|
-
#
|
|
14
|
-
# The first type is a Query. The Query is an object describing
|
|
15
|
-
# the database-specific operations we wish to perform, in an
|
|
16
|
-
# abstract manner. For example: While most if not all databases
|
|
17
|
-
# support a mechanism for limiting the size of results returned,
|
|
18
|
-
# some use a "LIMIT" keyword, while others use a "TOP" keyword.
|
|
19
|
-
# We can set a SelectStatement#limit field then, and allow
|
|
20
|
-
# the adapter to override the underlying SQL generated.
|
|
21
|
-
# Refer to DataMapper::Queries.
|
|
22
|
-
#
|
|
23
|
-
# The final type provided is a DataMapper::Transaction.
|
|
24
|
-
# Transactions are duck-typed Connections that span multiple queries.
|
|
25
|
-
#
|
|
26
|
-
# Note: It is assumed that the Adapter implements it's own
|
|
27
|
-
# ConnectionPool if any since some libraries implement their own at
|
|
28
|
-
# a low-level, and it wouldn't make sense to pay a performance
|
|
29
|
-
# cost twice by implementing a secondary pool in the DataMapper itself.
|
|
30
|
-
# If the library being adapted does not provide such functionality,
|
|
31
|
-
# DataMapper::Support::ConnectionPool can be used.
|
|
32
|
-
module Adapters
|
|
33
|
-
|
|
34
|
-
# You must inherit from the DoAdapter, and implement the
|
|
35
|
-
# required methods to adapt a database library for use with the DataMapper.
|
|
36
|
-
#
|
|
37
|
-
# NOTE: By inheriting from DoAdapter, you get a copy of all the
|
|
38
|
-
# standard sub-modules (Quoting, Coersion and Queries) in your own Adapter.
|
|
39
|
-
# You can extend and overwrite these copies without affecting the originals.
|
|
40
|
-
class DataObjectAdapter < AbstractAdapter
|
|
41
|
-
|
|
42
|
-
$LOAD_PATH << (DM_PLUGINS_ROOT + '/dataobjects')
|
|
43
|
-
|
|
44
|
-
FIND_OPTIONS = [
|
|
45
|
-
:select, :offset, :limit, :class, :include, :shallow_include, :reload, :conditions, :order, :intercept_load
|
|
46
|
-
]
|
|
47
|
-
|
|
48
|
-
TABLE_QUOTING_CHARACTER = '`'.freeze
|
|
49
|
-
COLUMN_QUOTING_CHARACTER = '`'.freeze
|
|
50
|
-
|
|
51
|
-
SYNTAX = {
|
|
52
|
-
:now => 'NOW()'.freeze
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
def initialize(configuration)
|
|
56
|
-
super
|
|
57
|
-
@connection_pool = Support::ConnectionPool.new { create_connection }
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def activated?
|
|
61
|
-
@activated
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def activate!
|
|
65
|
-
@activated = true
|
|
66
|
-
schema.activate!
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def create_connection
|
|
70
|
-
raise NotImplementedError.new
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def batch_insertable?
|
|
74
|
-
true
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
# Yields an available connection. Flushes the connection-pool and reconnects
|
|
78
|
-
# if the connection returns an error.
|
|
79
|
-
def connection
|
|
80
|
-
begin
|
|
81
|
-
# Yield the appropriate connection
|
|
82
|
-
@connection_pool.hold { |active_connection| yield(active_connection) }
|
|
83
|
-
rescue => execution_error
|
|
84
|
-
# Log error on failure
|
|
85
|
-
logger.error { execution_error }
|
|
86
|
-
|
|
87
|
-
# Close all open connections, assuming that if one
|
|
88
|
-
# had an error, it's likely due to a lost connection,
|
|
89
|
-
# in which case all connections are likely broken.
|
|
90
|
-
flush_connections!
|
|
91
|
-
|
|
92
|
-
raise execution_error
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# Close any open connections.
|
|
97
|
-
def flush_connections!
|
|
98
|
-
@connection_pool.available_connections.each do |active_connection|
|
|
99
|
-
begin
|
|
100
|
-
active_connection.close
|
|
101
|
-
rescue => close_connection_error
|
|
102
|
-
# An error on closing the connection is almost expected
|
|
103
|
-
# if the socket is broken.
|
|
104
|
-
logger.warn { close_connection_error }
|
|
105
|
-
end
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
# Reopen fresh connections.
|
|
109
|
-
@connection_pool.instance_variable_set('@created_count', 0)
|
|
110
|
-
@connection_pool.available_connections.clear
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def transaction(&block)
|
|
114
|
-
raise NotImplementedError.new
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def query(*args)
|
|
118
|
-
db = create_connection
|
|
119
|
-
|
|
120
|
-
command = db.create_command(args.shift)
|
|
121
|
-
|
|
122
|
-
reader = command.execute_reader(*args)
|
|
123
|
-
fields = reader.fields.map { |field| Inflector.underscore(field).to_sym }
|
|
124
|
-
results = []
|
|
125
|
-
|
|
126
|
-
if fields.size > 1
|
|
127
|
-
struct = Struct.new(*fields)
|
|
128
|
-
|
|
129
|
-
reader.each do
|
|
130
|
-
results << struct.new(*reader.current_row)
|
|
131
|
-
end
|
|
132
|
-
else
|
|
133
|
-
reader.each do
|
|
134
|
-
results << reader.item(0)
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
return results
|
|
139
|
-
rescue => e
|
|
140
|
-
logger.error { e }
|
|
141
|
-
raise e
|
|
142
|
-
ensure
|
|
143
|
-
reader.close if reader
|
|
144
|
-
db.close
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
def execute(*args)
|
|
148
|
-
db = create_connection
|
|
149
|
-
command = db.create_command(args.shift)
|
|
150
|
-
return command.execute_non_query(*args)
|
|
151
|
-
rescue => e
|
|
152
|
-
logger.error { e }
|
|
153
|
-
raise e
|
|
154
|
-
ensure
|
|
155
|
-
db.close
|
|
156
|
-
end
|
|
157
|
-
|
|
158
|
-
def handle_error(error)
|
|
159
|
-
raise error
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
def schema
|
|
163
|
-
@schema || ( @schema = self.class::Mappings::Schema.new(self, @configuration.database) )
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def column_exists_for_table?(table_name, column_name)
|
|
167
|
-
connection do |db|
|
|
168
|
-
table = self.table(table_name)
|
|
169
|
-
command = db.create_command(table.to_column_exists_sql)
|
|
170
|
-
command.execute_reader(table.name, column_name, table.schema.name) do |reader|
|
|
171
|
-
reader.any? { reader.item(1) == column_name.to_s }
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def delete(database_context, instance)
|
|
177
|
-
table = self.table(instance)
|
|
178
|
-
|
|
179
|
-
if instance.is_a?(Class)
|
|
180
|
-
table.delete_all!
|
|
181
|
-
else
|
|
182
|
-
callback(instance, :before_destroy)
|
|
183
|
-
|
|
184
|
-
table.associations.each do |association|
|
|
185
|
-
instance.send(association.name).deactivate unless association.is_a?(::DataMapper::Associations::BelongsToAssociation)
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
if table.paranoid?
|
|
189
|
-
instance.instance_variable_set(table.paranoid_column.instance_variable_name, Time::now)
|
|
190
|
-
instance.save
|
|
191
|
-
else
|
|
192
|
-
if connection do |db|
|
|
193
|
-
command = db.create_command("DELETE FROM #{table.to_sql} WHERE #{table.key.to_sql} = ?")
|
|
194
|
-
command.execute_non_query(instance.key).to_i > 0
|
|
195
|
-
end # connection do...end # if continued below:
|
|
196
|
-
instance.instance_variable_set(:@new_record, true)
|
|
197
|
-
instance.database_context = database_context
|
|
198
|
-
instance.original_values.clear
|
|
199
|
-
database_context.identity_map.delete(instance)
|
|
200
|
-
callback(instance, :after_destroy)
|
|
201
|
-
end
|
|
202
|
-
end
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
def save(database_context, instance, validate = true, cleared = Set.new)
|
|
207
|
-
case instance
|
|
208
|
-
when Class then
|
|
209
|
-
table(instance).create!
|
|
210
|
-
table(instance).activate_associations!
|
|
211
|
-
when Mappings::Table then instance.create!
|
|
212
|
-
when DataMapper::Persistence then
|
|
213
|
-
event = instance.new_record? ? :create : :update
|
|
214
|
-
|
|
215
|
-
return false if (validate && !instance.validate_recursively(event, Set.new)) || cleared.include?(instance)
|
|
216
|
-
cleared << instance
|
|
217
|
-
|
|
218
|
-
callback(instance, :before_save)
|
|
219
|
-
|
|
220
|
-
return true unless instance.new_record? || instance.dirty?
|
|
221
|
-
|
|
222
|
-
result = send(event, database_context, instance)
|
|
223
|
-
|
|
224
|
-
instance.database_context = database_context
|
|
225
|
-
instance.attributes.each_pair do |name, value|
|
|
226
|
-
instance.original_values[name] = value
|
|
227
|
-
end
|
|
228
|
-
|
|
229
|
-
instance.loaded_associations.each do |association|
|
|
230
|
-
association.save_without_validation(database_context, cleared) if association.dirty?
|
|
231
|
-
end
|
|
232
|
-
|
|
233
|
-
callback(instance, :after_save)
|
|
234
|
-
result
|
|
235
|
-
end
|
|
236
|
-
rescue => error
|
|
237
|
-
logger.error(error)
|
|
238
|
-
raise error
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
def save_without_validation(database_context, instance, cleared = Set.new)
|
|
242
|
-
save(database_context, instance, false, cleared)
|
|
243
|
-
end
|
|
244
|
-
|
|
245
|
-
def update(database_context, instance)
|
|
246
|
-
callback(instance, :before_update)
|
|
247
|
-
|
|
248
|
-
instance = update_magic_properties(database_context, instance)
|
|
249
|
-
|
|
250
|
-
table = self.table(instance)
|
|
251
|
-
attributes = instance.dirty_attributes
|
|
252
|
-
parameters = []
|
|
253
|
-
|
|
254
|
-
unless attributes.empty?
|
|
255
|
-
sql = "UPDATE " << table.to_sql << " SET "
|
|
256
|
-
|
|
257
|
-
sql << attributes.map do |key, value|
|
|
258
|
-
parameters << value
|
|
259
|
-
"#{table[key].to_sql} = ?"
|
|
260
|
-
end.join(', ')
|
|
261
|
-
|
|
262
|
-
sql << " WHERE #{table.key.to_sql} = ?"
|
|
263
|
-
parameters << instance.key
|
|
264
|
-
|
|
265
|
-
result = connection do |db|
|
|
266
|
-
db.create_command(sql).execute_non_query(*parameters)
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
# BUG: do_mysql returns inaccurate affected row counts for UPDATE statements.
|
|
270
|
-
if true || result.to_i > 0
|
|
271
|
-
callback(instance, :after_update)
|
|
272
|
-
return true
|
|
273
|
-
else
|
|
274
|
-
return false
|
|
275
|
-
end
|
|
276
|
-
else
|
|
277
|
-
true
|
|
278
|
-
end
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
def empty_insert_sql
|
|
282
|
-
"DEFAULT VALUES"
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
def create(database_context, instance)
|
|
286
|
-
callback(instance, :before_create)
|
|
287
|
-
|
|
288
|
-
instance = update_magic_properties(database_context, instance)
|
|
289
|
-
|
|
290
|
-
table = self.table(instance)
|
|
291
|
-
attributes = instance.dirty_attributes
|
|
292
|
-
|
|
293
|
-
if table.multi_class?
|
|
294
|
-
instance.instance_variable_set(
|
|
295
|
-
table[:type].instance_variable_name,
|
|
296
|
-
attributes[:type] = instance.class.name
|
|
297
|
-
)
|
|
298
|
-
end
|
|
299
|
-
|
|
300
|
-
keys = []
|
|
301
|
-
values = []
|
|
302
|
-
attributes.each_pair do |key, value|
|
|
303
|
-
raise ArgumentError.new("#{value.inspect} is not a valid value for #{key.inspect}") if value.is_a?(Array)
|
|
304
|
-
|
|
305
|
-
keys << table[key].to_sql
|
|
306
|
-
values << value
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
sql = if keys.size > 0
|
|
310
|
-
"INSERT INTO #{table.to_sql} (#{keys.join(', ')}) VALUES ?"
|
|
311
|
-
else
|
|
312
|
-
"INSERT INTO #{table.to_sql} #{self.empty_insert_sql}"
|
|
313
|
-
end
|
|
314
|
-
|
|
315
|
-
result = connection do |db|
|
|
316
|
-
db.create_command(sql).execute_non_query(values)
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
if result.to_i > 0
|
|
320
|
-
instance.instance_variable_set(:@new_record, false)
|
|
321
|
-
instance.key = result.last_insert_row if table.key.serial? && !attributes.include?(table.key.name)
|
|
322
|
-
database_context.identity_map.set(instance)
|
|
323
|
-
callback(instance, :after_create)
|
|
324
|
-
return true
|
|
325
|
-
else
|
|
326
|
-
return false
|
|
327
|
-
end
|
|
328
|
-
end
|
|
329
|
-
|
|
330
|
-
MAGIC_PROPERTIES = {
|
|
331
|
-
:updated_at => lambda { self.updated_at = Time::now },
|
|
332
|
-
:updated_on => lambda { self.updated_on = Date::today },
|
|
333
|
-
:created_at => lambda { self.created_at ||= Time::now },
|
|
334
|
-
:created_on => lambda { self.created_on ||= Date::today }
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
def update_magic_properties(database_context, instance)
|
|
338
|
-
instance.class.properties.find_all { |property| MAGIC_PROPERTIES.has_key?(property.name) }.each do |property|
|
|
339
|
-
instance.instance_eval(&MAGIC_PROPERTIES[property.name])
|
|
340
|
-
end
|
|
341
|
-
instance
|
|
342
|
-
end
|
|
343
|
-
|
|
344
|
-
def load(database_context, klass, options)
|
|
345
|
-
self.class::Commands::LoadCommand.new(self, database_context, klass, options).call
|
|
346
|
-
end
|
|
347
|
-
|
|
348
|
-
def get(database_context, klass, keys)
|
|
349
|
-
table = self.table(klass)
|
|
350
|
-
instance_id = table.key.type_cast_value(keys.first)
|
|
351
|
-
instance = database_context.identity_map.get(klass, instance_id)
|
|
352
|
-
|
|
353
|
-
return instance if instance
|
|
354
|
-
|
|
355
|
-
column_indexes = {}
|
|
356
|
-
select_columns = []
|
|
357
|
-
|
|
358
|
-
table.columns.each_with_index do |column, i|
|
|
359
|
-
column_indexes[column] = i
|
|
360
|
-
select_columns << column.to_sql
|
|
361
|
-
end
|
|
362
|
-
|
|
363
|
-
sql = "SELECT #{select_columns.join(', ')} FROM #{table.to_sql} WHERE #{table.keys.map { |key| "#{key.to_sql} = ?" }.join(' AND ')}"
|
|
364
|
-
|
|
365
|
-
connection do |db|
|
|
366
|
-
reader = nil
|
|
367
|
-
begin
|
|
368
|
-
reader = db.create_command(sql).execute_reader(*keys)
|
|
369
|
-
|
|
370
|
-
if reader.has_rows?
|
|
371
|
-
|
|
372
|
-
instance_type = klass
|
|
373
|
-
|
|
374
|
-
if table.multi_class? && table.type_column
|
|
375
|
-
value = reader.item(column_indexes[table.type_column])
|
|
376
|
-
instance_type = table.type_column.type_cast_value(value) unless value.blank?
|
|
377
|
-
end
|
|
378
|
-
|
|
379
|
-
if instance.nil?
|
|
380
|
-
instance = instance_type.allocate()
|
|
381
|
-
instance.instance_variable_set(:@__key, instance_id)
|
|
382
|
-
instance.instance_variable_set(:@new_record, false)
|
|
383
|
-
database_context.identity_map.set(instance)
|
|
384
|
-
elsif instance.new_record?
|
|
385
|
-
instance.instance_variable_set(:@__key, instance_id)
|
|
386
|
-
instance.instance_variable_set(:@new_record, false)
|
|
387
|
-
database_context.identity_map.set(instance)
|
|
388
|
-
end
|
|
389
|
-
|
|
390
|
-
instance.database_context = database_context
|
|
391
|
-
|
|
392
|
-
instance_type.callbacks.execute(:before_materialize, instance)
|
|
393
|
-
|
|
394
|
-
originals = instance.original_values
|
|
395
|
-
|
|
396
|
-
column_indexes.each_pair do |column, i|
|
|
397
|
-
value = column.type_cast_value(reader.item(i))
|
|
398
|
-
instance.instance_variable_set(column.instance_variable_name, value)
|
|
399
|
-
|
|
400
|
-
case value
|
|
401
|
-
when String, Date, Time then originals[column.name] = value.dup
|
|
402
|
-
else originals[column.name] = value
|
|
403
|
-
end
|
|
404
|
-
end
|
|
405
|
-
|
|
406
|
-
instance.loaded_set = [instance]
|
|
407
|
-
|
|
408
|
-
instance_type.callbacks.execute(:after_materialize, instance)
|
|
409
|
-
end # if reader.has_rows?
|
|
410
|
-
ensure
|
|
411
|
-
reader.close if reader && reader.open?
|
|
412
|
-
end
|
|
413
|
-
end # connection
|
|
414
|
-
|
|
415
|
-
return instance
|
|
416
|
-
end
|
|
417
|
-
|
|
418
|
-
def table(instance)
|
|
419
|
-
case instance
|
|
420
|
-
when DataMapper::Adapters::Sql::Mappings::Table then instance
|
|
421
|
-
when DataMapper::Persistence then schema[instance.class]
|
|
422
|
-
when Class, String then schema[instance]
|
|
423
|
-
else raise "Don't know how to map #{instance.inspect} to a table."
|
|
424
|
-
end
|
|
425
|
-
end
|
|
426
|
-
|
|
427
|
-
def callback(instance, callback_name)
|
|
428
|
-
instance.class.callbacks.execute(callback_name, instance)
|
|
429
|
-
end
|
|
430
|
-
|
|
431
|
-
# This callback copies and sub-classes modules and classes
|
|
432
|
-
# in the DoAdapter to the inherited class so you don't
|
|
433
|
-
# have to copy and paste large blocks of code from the
|
|
434
|
-
# DoAdapter.
|
|
435
|
-
#
|
|
436
|
-
# Basically, when inheriting from the DoAdapter, you
|
|
437
|
-
# aren't just inheriting a single class, you're inheriting
|
|
438
|
-
# a whole graph of Types. For convenience.
|
|
439
|
-
def self.inherited(base)
|
|
440
|
-
|
|
441
|
-
commands = base.const_set('Commands', Module.new)
|
|
442
|
-
|
|
443
|
-
Sql::Commands.constants.each do |name|
|
|
444
|
-
commands.const_set(name, Class.new(Sql::Commands.const_get(name)))
|
|
445
|
-
end
|
|
446
|
-
|
|
447
|
-
mappings = base.const_set('Mappings', Module.new)
|
|
448
|
-
|
|
449
|
-
Sql::Mappings.constants.each do |name|
|
|
450
|
-
mappings.const_set(name, Class.new(Sql::Mappings.const_get(name)))
|
|
451
|
-
end
|
|
452
|
-
|
|
453
|
-
base.const_set('TYPES', TYPES.dup)
|
|
454
|
-
base.const_set('FIND_OPTIONS', FIND_OPTIONS.dup)
|
|
455
|
-
base.const_set('SYNTAX', SYNTAX.dup)
|
|
456
|
-
|
|
457
|
-
super
|
|
458
|
-
end
|
|
459
|
-
|
|
460
|
-
TYPES = {
|
|
461
|
-
:integer => 'int'.freeze,
|
|
462
|
-
:string => 'varchar'.freeze,
|
|
463
|
-
:text => 'text'.freeze,
|
|
464
|
-
:class => 'varchar'.freeze,
|
|
465
|
-
:decimal => 'decimal'.freeze,
|
|
466
|
-
:float => 'float'.freeze,
|
|
467
|
-
:datetime => 'datetime'.freeze,
|
|
468
|
-
:date => 'date'.freeze,
|
|
469
|
-
:boolean => 'boolean'.freeze,
|
|
470
|
-
:object => 'text'.freeze
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
include Sql
|
|
474
|
-
include Quoting
|
|
475
|
-
include Coersion
|
|
476
|
-
|
|
477
|
-
end # class DoAdapter
|
|
478
|
-
|
|
479
|
-
end # module Adapters
|
|
480
|
-
end # module DataMapper
|