datamapper 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +31 -1
- data/MIT-LICENSE +1 -1
- data/README +9 -1
- data/example.rb +23 -15
- data/lib/data_mapper.rb +5 -0
- data/lib/data_mapper/adapters/abstract_adapter.rb +9 -207
- data/lib/data_mapper/adapters/mysql_adapter.rb +132 -108
- data/lib/data_mapper/adapters/postgresql_adapter.rb +242 -0
- data/lib/data_mapper/adapters/sql/coersion.rb +74 -0
- data/lib/data_mapper/adapters/sql/commands/advanced_load_command.rb +140 -0
- data/lib/data_mapper/adapters/sql/commands/conditions.rb +161 -0
- data/lib/data_mapper/adapters/sql/commands/delete_command.rb +113 -0
- data/lib/data_mapper/adapters/sql/commands/load_command.rb +296 -0
- data/lib/data_mapper/adapters/sql/commands/save_command.rb +141 -0
- data/lib/data_mapper/adapters/sql/commands/table_exists_command.rb +33 -0
- data/lib/data_mapper/adapters/sql/mappings/column.rb +91 -0
- data/lib/data_mapper/adapters/sql/mappings/schema.rb +30 -0
- data/lib/data_mapper/adapters/sql/mappings/table.rb +143 -0
- data/lib/data_mapper/adapters/sql/quoting.rb +38 -0
- data/lib/data_mapper/adapters/sql_adapter.rb +163 -0
- data/lib/data_mapper/adapters/sqlite3_adapter.rb +155 -116
- data/lib/data_mapper/associations.rb +2 -0
- data/lib/data_mapper/associations/advanced_has_many_association.rb +55 -0
- data/lib/data_mapper/associations/belongs_to_association.rb +2 -2
- data/lib/data_mapper/associations/has_many_association.rb +3 -3
- data/lib/data_mapper/associations/has_one_association.rb +2 -2
- data/lib/data_mapper/base.rb +30 -11
- data/lib/data_mapper/callbacks.rb +4 -1
- data/lib/data_mapper/database.rb +8 -41
- data/lib/data_mapper/identity_map.rb +23 -3
- data/lib/data_mapper/session.rb +34 -186
- data/lib/data_mapper/{extensions → support}/active_record_impersonation.rb +16 -12
- data/lib/data_mapper/support/blank.rb +35 -0
- data/lib/data_mapper/support/connection_pool.rb +2 -1
- data/lib/data_mapper/support/string.rb +27 -0
- data/lib/data_mapper/support/struct.rb +26 -0
- data/lib/data_mapper/validations/unique_validator.rb +1 -3
- data/lib/data_mapper/validations/validation_helper.rb +1 -1
- data/performance.rb +24 -7
- data/profile_data_mapper.rb +24 -2
- data/rakefile.rb +2 -2
- data/spec/basic_finder.rb +2 -2
- data/spec/belongs_to.rb +1 -1
- data/spec/delete_command_spec.rb +9 -0
- data/spec/fixtures/zoos.yaml +4 -0
- data/spec/has_many.rb +1 -1
- data/spec/load_command_spec.rb +44 -0
- data/spec/models/zoo.rb +2 -0
- data/spec/save_command_spec.rb +13 -0
- data/spec/spec_helper.rb +10 -1
- data/spec/support/string_spec.rb +7 -0
- data/spec/validates_confirmation_of.rb +1 -1
- data/spec/validates_format_of.rb +1 -1
- data/spec/validates_length_of.rb +1 -1
- data/spec/validations.rb +1 -1
- metadata +23 -20
- data/lib/data_mapper/extensions/callback_helpers.rb +0 -35
- data/lib/data_mapper/loaded_set.rb +0 -45
- data/lib/data_mapper/mappings/column.rb +0 -78
- data/lib/data_mapper/mappings/schema.rb +0 -28
- data/lib/data_mapper/mappings/table.rb +0 -99
- data/lib/data_mapper/queries/conditions.rb +0 -141
- data/lib/data_mapper/queries/connection.rb +0 -34
- data/lib/data_mapper/queries/create_table_statement.rb +0 -38
- data/lib/data_mapper/queries/delete_statement.rb +0 -17
- data/lib/data_mapper/queries/drop_table_statement.rb +0 -17
- data/lib/data_mapper/queries/insert_statement.rb +0 -29
- data/lib/data_mapper/queries/reader.rb +0 -42
- data/lib/data_mapper/queries/result.rb +0 -19
- data/lib/data_mapper/queries/select_statement.rb +0 -103
- data/lib/data_mapper/queries/table_exists_statement.rb +0 -17
- data/lib/data_mapper/queries/truncate_table_statement.rb +0 -17
- data/lib/data_mapper/queries/update_statement.rb +0 -25
@@ -1,5 +1,5 @@
|
|
1
1
|
module DataMapper
|
2
|
-
module
|
2
|
+
module Support
|
3
3
|
|
4
4
|
module ActiveRecordImpersonation
|
5
5
|
|
@@ -12,7 +12,7 @@ module DataMapper
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def reload!
|
15
|
-
session.
|
15
|
+
session.first(self.class, key, :select => session.mappings[self.class].columns.map(&:name), :reload => true)
|
16
16
|
end
|
17
17
|
|
18
18
|
def reload
|
@@ -25,12 +25,16 @@ module DataMapper
|
|
25
25
|
|
26
26
|
module ClassMethods
|
27
27
|
|
28
|
+
def find_or_create(search_attributes, create_attributes = nil)
|
29
|
+
first(search_attributes) || create(search_attributes.merge(create_attributes))
|
30
|
+
end
|
31
|
+
|
28
32
|
def all(options = {}, &b)
|
29
|
-
|
33
|
+
database.all(self, options, &b)
|
30
34
|
end
|
31
35
|
|
32
|
-
def first(
|
33
|
-
|
36
|
+
def first(*args, &b)
|
37
|
+
database.first(self, *args, &b)
|
34
38
|
end
|
35
39
|
|
36
40
|
def delete_all
|
@@ -41,8 +45,12 @@ module DataMapper
|
|
41
45
|
database.truncate(self)
|
42
46
|
end
|
43
47
|
|
44
|
-
def find(
|
45
|
-
|
48
|
+
def find(type_or_id, options = {}, &b)
|
49
|
+
case type_or_id
|
50
|
+
when :first then first(options, &b)
|
51
|
+
when :all then all(options, &b)
|
52
|
+
else first(type_or_id, options, &b)
|
53
|
+
end
|
46
54
|
end
|
47
55
|
|
48
56
|
def find_by_sql(*args)
|
@@ -50,11 +58,7 @@ module DataMapper
|
|
50
58
|
end
|
51
59
|
|
52
60
|
def [](id_or_hash)
|
53
|
-
|
54
|
-
find(:first, id_or_hash)
|
55
|
-
else
|
56
|
-
find(id_or_hash)
|
57
|
-
end
|
61
|
+
first(id_or_hash)
|
58
62
|
end
|
59
63
|
|
60
64
|
def create(attributes)
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class Object
|
2
|
+
def blank?
|
3
|
+
nil? || (respond_to?(:empty?) && empty?)
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
class Fixnum
|
8
|
+
def blank?
|
9
|
+
false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class NilClass
|
14
|
+
def blank?
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class TrueClass
|
20
|
+
def blank?
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class FalseClass
|
26
|
+
def blank?
|
27
|
+
false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class String
|
32
|
+
def blank?
|
33
|
+
empty? || self =~ /^\s*$/
|
34
|
+
end
|
35
|
+
end
|
@@ -2,6 +2,11 @@ module DataMapper
|
|
2
2
|
module Support
|
3
3
|
module String
|
4
4
|
|
5
|
+
# I set the constant on the String itself to avoid inheritance chain lookups.
|
6
|
+
def self.included(base)
|
7
|
+
base.const_set('EMPTY', ''.freeze)
|
8
|
+
end
|
9
|
+
|
5
10
|
def ensure_starts_with(part)
|
6
11
|
[0,1] == part ? self : (part + self)
|
7
12
|
end
|
@@ -14,10 +19,32 @@ module DataMapper
|
|
14
19
|
ensure_starts_with(a).ensure_ends_with(b || a)
|
15
20
|
end
|
16
21
|
|
22
|
+
# Matches any whitespace (including newline) and replaces with a single space
|
23
|
+
# EXAMPLE:
|
24
|
+
# <<QUERY.compress_lines
|
25
|
+
# SELECT name
|
26
|
+
# FROM users
|
27
|
+
# QUERY
|
28
|
+
# => "SELECT name FROM users"
|
29
|
+
def compress_lines
|
30
|
+
gsub(/\s+/, ' ').strip
|
31
|
+
end
|
32
|
+
|
17
33
|
end # module String
|
18
34
|
end # module Support
|
19
35
|
end # module DataMapper
|
20
36
|
|
21
37
|
class String #:nodoc:
|
22
38
|
include DataMapper::Support::String
|
39
|
+
|
40
|
+
def self.fragile_underscore(camel_cased_word)
|
41
|
+
camel_cased_word.gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
|
42
|
+
end
|
43
|
+
|
44
|
+
@underscore_cache = Hash.new { |h,k| h[k.freeze] = fragile_underscore(k) }
|
45
|
+
|
46
|
+
def self.memoized_underscore(camel_cased_word)
|
47
|
+
@underscore_cache[camel_cased_word]
|
48
|
+
end
|
49
|
+
|
23
50
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Support
|
3
|
+
class Struct
|
4
|
+
|
5
|
+
def self.define(raw_fields)
|
6
|
+
|
7
|
+
normalized_fields = raw_fields.map { |field| Inflector.underscore(field).to_sym }
|
8
|
+
|
9
|
+
Class.new(self) do
|
10
|
+
define_method(:fields) do
|
11
|
+
normalized_fields
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(values)
|
17
|
+
@values = values
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing(sym, *args)
|
21
|
+
@values[fields.index(sym)]
|
22
|
+
end
|
23
|
+
|
24
|
+
end # class Struct
|
25
|
+
end # module Support
|
26
|
+
end # module DataMapper
|
@@ -37,9 +37,7 @@ module DataMapper
|
|
37
37
|
|
38
38
|
finder_options.merge!({ target.session.mappings[target.class].key.name.not => target.key }) unless target.new_record?
|
39
39
|
|
40
|
-
|
41
|
-
# found with unless new_record?
|
42
|
-
target.session.find(target.class, :first, finder_options).nil?
|
40
|
+
target.session.first(target.class, finder_options).nil?
|
43
41
|
end
|
44
42
|
|
45
43
|
end
|
data/performance.rb
CHANGED
@@ -14,8 +14,7 @@ class ARPerson < ActiveRecord::Base
|
|
14
14
|
set_table_name 'people'
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
require 'data_mapper'
|
17
|
+
require 'lib/data_mapper'
|
19
18
|
|
20
19
|
log_path = File.dirname(__FILE__) + '/development.log'
|
21
20
|
|
@@ -24,10 +23,8 @@ FileUtils::rm log_path if File.exists?(log_path)
|
|
24
23
|
|
25
24
|
DataMapper::Database.setup do
|
26
25
|
adapter 'mysql'
|
27
|
-
username 'root'
|
28
26
|
database 'data_mapper_1'
|
29
|
-
|
30
|
-
log_level Logger::DEBUG
|
27
|
+
username 'root'
|
31
28
|
end
|
32
29
|
|
33
30
|
class DMAnimal < DataMapper::Base
|
@@ -76,6 +73,14 @@ Benchmark::send(ENV['BM'] || :bmbm, 40) do |x|
|
|
76
73
|
N.times { DMAnimal[1] }
|
77
74
|
end
|
78
75
|
|
76
|
+
x.report('ActiveRecord:all') do
|
77
|
+
N.times { ARAnimal.find(:all) }
|
78
|
+
end
|
79
|
+
|
80
|
+
x.report('DataMapper:all') do
|
81
|
+
N.times { DMAnimal.all }
|
82
|
+
end
|
83
|
+
|
79
84
|
x.report('ActiveRecord:conditions') do
|
80
85
|
N.times { ARZoo.find(:first, :conditions => ['name = ?', 'Galveston']) }
|
81
86
|
end
|
@@ -118,7 +123,7 @@ Benchmark::send(ENV['BM'] || :bmbm, 40) do |x|
|
|
118
123
|
|
119
124
|
x.report('ActiveRecord:update') do
|
120
125
|
N.times do
|
121
|
-
bob = ARAnimal.find(:first, :conditions => ["name = ?", '
|
126
|
+
bob = ARAnimal.find(:first, :conditions => ["name = ?", 'Elephant'])
|
122
127
|
bob.notes = 'Updated by ActiveRecord'
|
123
128
|
bob.save
|
124
129
|
end
|
@@ -126,7 +131,7 @@ Benchmark::send(ENV['BM'] || :bmbm, 40) do |x|
|
|
126
131
|
|
127
132
|
x.report('DataMapper:update') do
|
128
133
|
N.times do
|
129
|
-
bob = DMAnimal.first(:name => '
|
134
|
+
bob = DMAnimal.first(:name => 'Elephant')
|
130
135
|
bob.notes = 'Updated by DataMapper'
|
131
136
|
bob.save
|
132
137
|
end
|
@@ -153,4 +158,16 @@ Benchmark::send(ENV['BM'] || :bmbm, 40) do |x|
|
|
153
158
|
end
|
154
159
|
end
|
155
160
|
end
|
161
|
+
|
162
|
+
x.report('ActiveRecord:find_by_sql') do
|
163
|
+
N.times do
|
164
|
+
ARZoo.find_by_sql("SELECT * FROM zoos")
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
x.report('DataMapper:find_by_sql') do
|
169
|
+
N.times do
|
170
|
+
database.query("SELECT * FROM zoos")
|
171
|
+
end
|
172
|
+
end
|
156
173
|
end
|
data/profile_data_mapper.rb
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
#!/opt/local/bin/ruby
|
2
|
+
|
3
|
+
ENV['LOGGER'] = 'false'
|
4
|
+
|
1
5
|
require 'example'
|
2
6
|
require 'ruby-prof'
|
3
7
|
|
@@ -13,6 +17,24 @@ end
|
|
13
17
|
|
14
18
|
profile do
|
15
19
|
1000.times do
|
16
|
-
Zoo
|
20
|
+
Zoo.all
|
17
21
|
end
|
18
|
-
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# require 'benchmark'
|
25
|
+
#
|
26
|
+
# N = 100_000
|
27
|
+
#
|
28
|
+
# Benchmark::bmbm do |x|
|
29
|
+
# x.report do
|
30
|
+
# N.times do
|
31
|
+
# Inflector.underscore('DataMapper')
|
32
|
+
# end
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# x.report do
|
36
|
+
# N.times do
|
37
|
+
# String::memoized_underscore('DataMapper')
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
# end
|
data/rakefile.rb
CHANGED
@@ -11,7 +11,7 @@ task :default => 'test'
|
|
11
11
|
desc "Run specifications"
|
12
12
|
Spec::Rake::SpecTask.new('test') do |t|
|
13
13
|
t.spec_opts = [ '-rspec/spec_helper' ]
|
14
|
-
t.spec_files = FileList[ENV['FILES'] || 'spec
|
14
|
+
t.spec_files = FileList['spec/*.rb', (ENV['FILES'] || 'spec/**/*_spec.rb')]
|
15
15
|
end
|
16
16
|
|
17
17
|
desc "Run comparison with ActiveRecord"
|
@@ -24,7 +24,7 @@ task :profile do
|
|
24
24
|
load 'profile_data_mapper.rb'
|
25
25
|
end
|
26
26
|
|
27
|
-
PACKAGE_VERSION = '0.1.
|
27
|
+
PACKAGE_VERSION = '0.1.1'
|
28
28
|
|
29
29
|
PACKAGE_FILES = FileList[
|
30
30
|
'README',
|
data/spec/basic_finder.rb
CHANGED
@@ -3,14 +3,14 @@ context 'Finder' do
|
|
3
3
|
specify 'database-specific load should not fail' do
|
4
4
|
|
5
5
|
DataMapper::database do |db|
|
6
|
-
froggy = db.
|
6
|
+
froggy = db.first(Animal, :conditions => ['name = ?', 'Frog'])
|
7
7
|
froggy.name.should == 'Frog'
|
8
8
|
end
|
9
9
|
|
10
10
|
end
|
11
11
|
|
12
12
|
specify 'current-database load should not fail' do
|
13
|
-
froggy = DataMapper::database.
|
13
|
+
froggy = DataMapper::database.first(Animal).name.should == 'Frog'
|
14
14
|
end
|
15
15
|
|
16
16
|
specify 'load through ActiveRecord impersonation should not fail' do
|
data/spec/belongs_to.rb
CHANGED
data/spec/fixtures/zoos.yaml
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
- name: Dallas
|
2
|
+
notes: Keep Dallas Pretentious?
|
2
3
|
- name: San Diego
|
3
4
|
- name: Miami
|
4
5
|
- name: Ft. Lauderdale
|
5
6
|
- name: Ft. Worth
|
6
7
|
- name: New Orleans
|
7
8
|
- name: San Antonio
|
9
|
+
notes: The most sprawling city I've ever seen. It's insane.
|
8
10
|
- name: Galveston
|
11
|
+
notes: A friendly place. It's like Hotel California for Houston's homeless though. (No way out without a car.)
|
9
12
|
- name: Austin
|
13
|
+
notes: People in Austin like to drive around with "Save Mother Earth" stickers on their SUVs and proclaim "Keep Austin Weird" while sipping on Starbucks.
|
10
14
|
- name: Brownsville
|
11
15
|
- name: Los Angeles
|
12
16
|
- name: Phillidelphia
|
data/spec/has_many.rb
CHANGED
@@ -18,7 +18,7 @@ describe DataMapper::Associations::HasManyAssociation do
|
|
18
18
|
it 'should lazily-load the association when Enumerable methods are called' do
|
19
19
|
database do |db|
|
20
20
|
@san_diego.exhibits.size.should == 2
|
21
|
-
@san_diego.exhibits.should include(@san_diego.session.
|
21
|
+
@san_diego.exhibits.should include(@san_diego.session.first(Exhibit, :name => 'Monkey Mayhem'))
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
describe DataMapper::Adapters::Sql::Commands::LoadCommand do
|
2
|
+
|
3
|
+
before(:all) do
|
4
|
+
fixtures(:zoos)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "should return a Struct for custom queries" do
|
8
|
+
results = database.query("SELECT * FROM zoos WHERE name = ?", 'Galveston')
|
9
|
+
zoo = results.first
|
10
|
+
zoo.class.superclass.should == DataMapper::Support::Struct
|
11
|
+
zoo.name.should == "Galveston"
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
describe DataMapper::Adapters::Sql::Commands::AdvancedLoadCommand do
|
17
|
+
|
18
|
+
def loader_for(klass, options = {})
|
19
|
+
session = database
|
20
|
+
DataMapper::Adapters::Sql::Commands::AdvancedLoadCommand.new(session.adapter, session, klass, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return a simple select statement for a given class" do
|
24
|
+
loader_for(Zoo).to_sql.should == 'SELECT `id`, `name` FROM `zoos`'
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should include only the columns specified in the statement" do
|
28
|
+
loader_for(Zoo, :select => [:name]).to_sql.should == 'SELECT `name` FROM `zoos`'
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should optionally include lazy-loaded columns in the statement" do
|
32
|
+
loader_for(Zoo, :include => :notes).to_sql.should == 'SELECT `id`, `name`, `notes` FROM `zoos`'
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should join associations in the statement" do
|
36
|
+
loader_for(Zoo, :include => :exhibits2).to_sql.should == <<-EOS.compress_lines
|
37
|
+
SELECT `zoos`.`id`, `zoos`.`name`,
|
38
|
+
`exhibits`.`id`, `exhibits`.`name`, `exhibits`.`zoo_id`
|
39
|
+
FROM `zoos`
|
40
|
+
JOIN `exhibits` ON `exhibits`.`cow_id` = `zoos`.`id`
|
41
|
+
EOS
|
42
|
+
end
|
43
|
+
|
44
|
+
end if ENV['ADAPTER'].nil? || ENV['ADAPTER'] == 'mysql'
|